Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 69f0340a

History | View | Annotate | Download (71.6 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 4b97f902 Apollon Oikonomopoulos
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Block device abstraction"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import re
25 a8083063 Iustin Pop
import time
26 a8083063 Iustin Pop
import errno
27 b6135bbc Apollon Oikonomopoulos
import stat
28 a2cfdea2 Iustin Pop
import pyparsing as pyp
29 6f695a2e Manuel Franceschini
import os
30 468c5f77 Iustin Pop
import logging
31 a8083063 Iustin Pop
32 a8083063 Iustin Pop
from ganeti import utils
33 a8083063 Iustin Pop
from ganeti import errors
34 fe96220b Iustin Pop
from ganeti import constants
35 96acbc09 Michael Hanselmann
from ganeti import objects
36 cea881e5 Michael Hanselmann
from ganeti import compat
37 a744b676 Manuel Franceschini
from ganeti import netutils
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
40 310fbb64 Iustin Pop
# Size of reads in _CanReadDevice
41 310fbb64 Iustin Pop
_DEVICE_READ_SIZE = 128 * 1024
42 310fbb64 Iustin Pop
43 310fbb64 Iustin Pop
44 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
45 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
46 82463074 Iustin Pop

47 82463074 Iustin Pop
  This is used in order to simplify the execution of cleanup or
48 82463074 Iustin Pop
  rollback functions.
49 82463074 Iustin Pop

50 82463074 Iustin Pop
  @rtype: boolean
51 82463074 Iustin Pop
  @return: True when fn didn't raise an exception, False otherwise
52 82463074 Iustin Pop

53 82463074 Iustin Pop
  """
54 82463074 Iustin Pop
  try:
55 82463074 Iustin Pop
    fn(*args, **kwargs)
56 82463074 Iustin Pop
    return True
57 82463074 Iustin Pop
  except errors.BlockDeviceError, err:
58 099c52ad Iustin Pop
    logging.warning("Caught BlockDeviceError but ignoring: %s", str(err))
59 82463074 Iustin Pop
    return False
60 82463074 Iustin Pop
61 82463074 Iustin Pop
62 82463074 Iustin Pop
def _ThrowError(msg, *args):
63 82463074 Iustin Pop
  """Log an error to the node daemon and the raise an exception.
64 82463074 Iustin Pop

65 82463074 Iustin Pop
  @type msg: string
66 82463074 Iustin Pop
  @param msg: the text of the exception
67 82463074 Iustin Pop
  @raise errors.BlockDeviceError
68 82463074 Iustin Pop

69 82463074 Iustin Pop
  """
70 82463074 Iustin Pop
  if args:
71 82463074 Iustin Pop
    msg = msg % args
72 82463074 Iustin Pop
  logging.error(msg)
73 82463074 Iustin Pop
  raise errors.BlockDeviceError(msg)
74 82463074 Iustin Pop
75 82463074 Iustin Pop
76 310fbb64 Iustin Pop
def _CanReadDevice(path):
77 310fbb64 Iustin Pop
  """Check if we can read from the given device.
78 310fbb64 Iustin Pop

79 310fbb64 Iustin Pop
  This tries to read the first 128k of the device.
80 310fbb64 Iustin Pop

81 310fbb64 Iustin Pop
  """
82 310fbb64 Iustin Pop
  try:
83 310fbb64 Iustin Pop
    utils.ReadFile(path, size=_DEVICE_READ_SIZE)
84 310fbb64 Iustin Pop
    return True
85 1122eb25 Iustin Pop
  except EnvironmentError:
86 310fbb64 Iustin Pop
    logging.warning("Can't read from device %s", path, exc_info=True)
87 310fbb64 Iustin Pop
    return False
88 310fbb64 Iustin Pop
89 310fbb64 Iustin Pop
90 a8083063 Iustin Pop
class BlockDev(object):
91 a8083063 Iustin Pop
  """Block device abstract class.
92 a8083063 Iustin Pop

93 a8083063 Iustin Pop
  A block device can be in the following states:
94 a8083063 Iustin Pop
    - not existing on the system, and by `Create()` it goes into:
95 a8083063 Iustin Pop
    - existing but not setup/not active, and by `Assemble()` goes into:
96 a8083063 Iustin Pop
    - active read-write and by `Open()` it goes into
97 a8083063 Iustin Pop
    - online (=used, or ready for use)
98 a8083063 Iustin Pop

99 a8083063 Iustin Pop
  A device can also be online but read-only, however we are not using
100 abdf0113 Iustin Pop
  the readonly state (LV has it, if needed in the future) and we are
101 abdf0113 Iustin Pop
  usually looking at this like at a stack, so it's easier to
102 abdf0113 Iustin Pop
  conceptualise the transition from not-existing to online and back
103 a8083063 Iustin Pop
  like a linear one.
104 a8083063 Iustin Pop

105 a8083063 Iustin Pop
  The many different states of the device are due to the fact that we
106 a8083063 Iustin Pop
  need to cover many device types:
107 a8083063 Iustin Pop
    - logical volumes are created, lvchange -a y $lv, and used
108 a8083063 Iustin Pop
    - drbd devices are attached to a local disk/remote peer and made primary
109 a8083063 Iustin Pop

110 a8083063 Iustin Pop
  A block device is identified by three items:
111 a8083063 Iustin Pop
    - the /dev path of the device (dynamic)
112 a8083063 Iustin Pop
    - a unique ID of the device (static)
113 a8083063 Iustin Pop
    - it's major/minor pair (dynamic)
114 a8083063 Iustin Pop

115 a8083063 Iustin Pop
  Not all devices implement both the first two as distinct items. LVM
116 a8083063 Iustin Pop
  logical volumes have their unique ID (the pair volume group, logical
117 abdf0113 Iustin Pop
  volume name) in a 1-to-1 relation to the dev path. For DRBD devices,
118 abdf0113 Iustin Pop
  the /dev path is again dynamic and the unique id is the pair (host1,
119 abdf0113 Iustin Pop
  dev1), (host2, dev2).
120 a8083063 Iustin Pop

121 a8083063 Iustin Pop
  You can get to a device in two ways:
122 a8083063 Iustin Pop
    - creating the (real) device, which returns you
123 abdf0113 Iustin Pop
      an attached instance (lvcreate)
124 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
125 a8083063 Iustin Pop

126 a8083063 Iustin Pop
  The second point, the attachement to a device, is different
127 a8083063 Iustin Pop
  depending on whether the device is assembled or not. At init() time,
128 a8083063 Iustin Pop
  we search for a device with the same unique_id as us. If found,
129 a8083063 Iustin Pop
  good. It also means that the device is already assembled. If not,
130 a8083063 Iustin Pop
  after assembly we'll have our correct major/minor.
131 a8083063 Iustin Pop

132 a8083063 Iustin Pop
  """
133 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
134 a8083063 Iustin Pop
    self._children = children
135 a8083063 Iustin Pop
    self.dev_path = None
136 a8083063 Iustin Pop
    self.unique_id = unique_id
137 a8083063 Iustin Pop
    self.major = None
138 a8083063 Iustin Pop
    self.minor = None
139 cb999543 Iustin Pop
    self.attached = False
140 464f8daf Iustin Pop
    self.size = size
141 a8083063 Iustin Pop
142 a8083063 Iustin Pop
  def Assemble(self):
143 a8083063 Iustin Pop
    """Assemble the device from its components.
144 a8083063 Iustin Pop

145 f87548b5 Iustin Pop
    Implementations of this method by child classes must ensure that:
146 f87548b5 Iustin Pop
      - after the device has been assembled, it knows its major/minor
147 f87548b5 Iustin Pop
        numbers; this allows other devices (usually parents) to probe
148 f87548b5 Iustin Pop
        correctly for their children
149 f87548b5 Iustin Pop
      - calling this method on an existing, in-use device is safe
150 f87548b5 Iustin Pop
      - if the device is already configured (and in an OK state),
151 f87548b5 Iustin Pop
        this method is idempotent
152 a8083063 Iustin Pop

153 a8083063 Iustin Pop
    """
154 1063abd1 Iustin Pop
    pass
155 a8083063 Iustin Pop
156 a8083063 Iustin Pop
  def Attach(self):
157 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
158 a8083063 Iustin Pop

159 a8083063 Iustin Pop
    """
160 a8083063 Iustin Pop
    raise NotImplementedError
161 a8083063 Iustin Pop
162 a8083063 Iustin Pop
  def Close(self):
163 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
164 a8083063 Iustin Pop

165 a8083063 Iustin Pop
    """
166 a8083063 Iustin Pop
    raise NotImplementedError
167 a8083063 Iustin Pop
168 a8083063 Iustin Pop
  @classmethod
169 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
170 a8083063 Iustin Pop
    """Create the device.
171 a8083063 Iustin Pop

172 a8083063 Iustin Pop
    If the device cannot be created, it will return None
173 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
174 a8083063 Iustin Pop

175 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
176 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
177 a8083063 Iustin Pop
    enough for both creation and assembly (later).
178 a8083063 Iustin Pop

179 a8083063 Iustin Pop
    """
180 a8083063 Iustin Pop
    raise NotImplementedError
181 a8083063 Iustin Pop
182 a8083063 Iustin Pop
  def Remove(self):
183 a8083063 Iustin Pop
    """Remove this device.
184 a8083063 Iustin Pop

185 abdf0113 Iustin Pop
    This makes sense only for some of the device types: LV and file
186 5bbd3f7f Michael Hanselmann
    storage. Also note that if the device can't attach, the removal
187 abdf0113 Iustin Pop
    can't be completed.
188 a8083063 Iustin Pop

189 a8083063 Iustin Pop
    """
190 a8083063 Iustin Pop
    raise NotImplementedError
191 a8083063 Iustin Pop
192 f3e513ad Iustin Pop
  def Rename(self, new_id):
193 f3e513ad Iustin Pop
    """Rename this device.
194 f3e513ad Iustin Pop

195 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
196 f3e513ad Iustin Pop

197 f3e513ad Iustin Pop
    """
198 f3e513ad Iustin Pop
    raise NotImplementedError
199 f3e513ad Iustin Pop
200 a8083063 Iustin Pop
  def Open(self, force=False):
201 a8083063 Iustin Pop
    """Make the device ready for use.
202 a8083063 Iustin Pop

203 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
204 a8083063 Iustin Pop
    devices need this.
205 a8083063 Iustin Pop

206 a8083063 Iustin Pop
    The force parameter signifies that if the device has any kind of
207 a8083063 Iustin Pop
    --force thing, it should be used, we know what we are doing.
208 a8083063 Iustin Pop

209 a8083063 Iustin Pop
    """
210 a8083063 Iustin Pop
    raise NotImplementedError
211 a8083063 Iustin Pop
212 a8083063 Iustin Pop
  def Shutdown(self):
213 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
214 a8083063 Iustin Pop

215 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
216 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
217 a8083063 Iustin Pop
    assembled after this call.
218 a8083063 Iustin Pop

219 a8083063 Iustin Pop
    """
220 a8083063 Iustin Pop
    raise NotImplementedError
221 a8083063 Iustin Pop
222 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
223 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
224 a8083063 Iustin Pop

225 a8083063 Iustin Pop
    In case this is not a mirroring device, this is no-op.
226 a8083063 Iustin Pop

227 a8083063 Iustin Pop
    """
228 a8083063 Iustin Pop
    result = True
229 a8083063 Iustin Pop
    if self._children:
230 a8083063 Iustin Pop
      for child in self._children:
231 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
232 a8083063 Iustin Pop
    return result
233 a8083063 Iustin Pop
234 a3fffcc6 Renรฉ Nussbaumer
  def PauseResumeSync(self, pause):
235 a3fffcc6 Renรฉ Nussbaumer
    """Pause/Resume the sync of the mirror.
236 a3fffcc6 Renรฉ Nussbaumer

237 a3fffcc6 Renรฉ Nussbaumer
    In case this is not a mirroring device, this is no-op.
238 a3fffcc6 Renรฉ Nussbaumer

239 a3fffcc6 Renรฉ Nussbaumer
    @param pause: Wheater to pause or resume
240 a3fffcc6 Renรฉ Nussbaumer

241 a3fffcc6 Renรฉ Nussbaumer
    """
242 a3fffcc6 Renรฉ Nussbaumer
    result = True
243 a3fffcc6 Renรฉ Nussbaumer
    if self._children:
244 a3fffcc6 Renรฉ Nussbaumer
      for child in self._children:
245 a3fffcc6 Renรฉ Nussbaumer
        result = result and child.PauseResumeSync(pause)
246 a3fffcc6 Renรฉ Nussbaumer
    return result
247 a3fffcc6 Renรฉ Nussbaumer
248 a8083063 Iustin Pop
  def GetSyncStatus(self):
249 a8083063 Iustin Pop
    """Returns the sync status of the device.
250 a8083063 Iustin Pop

251 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
252 a8083063 Iustin Pop
    status of the mirror.
253 a8083063 Iustin Pop

254 0834c866 Iustin Pop
    If sync_percent is None, it means the device is not syncing.
255 a8083063 Iustin Pop

256 a8083063 Iustin Pop
    If estimated_time is None, it means we can't estimate
257 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
258 0834c866 Iustin Pop

259 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
260 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
261 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
262 a8083063 Iustin Pop

263 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
264 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
265 0834c866 Iustin Pop
    return False (not degraded).
266 0834c866 Iustin Pop

267 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
268 c41eea6e Iustin Pop

269 a8083063 Iustin Pop
    """
270 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
271 96acbc09 Michael Hanselmann
                                  major=self.major,
272 96acbc09 Michael Hanselmann
                                  minor=self.minor,
273 96acbc09 Michael Hanselmann
                                  sync_percent=None,
274 96acbc09 Michael Hanselmann
                                  estimated_time=None,
275 96acbc09 Michael Hanselmann
                                  is_degraded=False,
276 f208978a Michael Hanselmann
                                  ldisk_status=constants.LDS_OKAY)
277 a8083063 Iustin Pop
278 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
279 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
280 a8083063 Iustin Pop

281 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
282 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
283 a8083063 Iustin Pop
    children.
284 a8083063 Iustin Pop

285 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
286 96acbc09 Michael Hanselmann

287 a8083063 Iustin Pop
    """
288 96acbc09 Michael Hanselmann
    status = self.GetSyncStatus()
289 96acbc09 Michael Hanselmann
290 96acbc09 Michael Hanselmann
    min_percent = status.sync_percent
291 96acbc09 Michael Hanselmann
    max_time = status.estimated_time
292 96acbc09 Michael Hanselmann
    is_degraded = status.is_degraded
293 f208978a Michael Hanselmann
    ldisk_status = status.ldisk_status
294 96acbc09 Michael Hanselmann
295 a8083063 Iustin Pop
    if self._children:
296 a8083063 Iustin Pop
      for child in self._children:
297 96acbc09 Michael Hanselmann
        child_status = child.GetSyncStatus()
298 96acbc09 Michael Hanselmann
299 a8083063 Iustin Pop
        if min_percent is None:
300 96acbc09 Michael Hanselmann
          min_percent = child_status.sync_percent
301 96acbc09 Michael Hanselmann
        elif child_status.sync_percent is not None:
302 96acbc09 Michael Hanselmann
          min_percent = min(min_percent, child_status.sync_percent)
303 96acbc09 Michael Hanselmann
304 a8083063 Iustin Pop
        if max_time is None:
305 96acbc09 Michael Hanselmann
          max_time = child_status.estimated_time
306 96acbc09 Michael Hanselmann
        elif child_status.estimated_time is not None:
307 96acbc09 Michael Hanselmann
          max_time = max(max_time, child_status.estimated_time)
308 96acbc09 Michael Hanselmann
309 96acbc09 Michael Hanselmann
        is_degraded = is_degraded or child_status.is_degraded
310 f208978a Michael Hanselmann
311 f208978a Michael Hanselmann
        if ldisk_status is None:
312 f208978a Michael Hanselmann
          ldisk_status = child_status.ldisk_status
313 f208978a Michael Hanselmann
        elif child_status.ldisk_status is not None:
314 f208978a Michael Hanselmann
          ldisk_status = max(ldisk_status, child_status.ldisk_status)
315 96acbc09 Michael Hanselmann
316 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
317 96acbc09 Michael Hanselmann
                                  major=self.major,
318 96acbc09 Michael Hanselmann
                                  minor=self.minor,
319 96acbc09 Michael Hanselmann
                                  sync_percent=min_percent,
320 96acbc09 Michael Hanselmann
                                  estimated_time=max_time,
321 96acbc09 Michael Hanselmann
                                  is_degraded=is_degraded,
322 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
323 a8083063 Iustin Pop
324 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
325 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
326 a0c3fea1 Michael Hanselmann

327 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
328 a0c3fea1 Michael Hanselmann

329 a0c3fea1 Michael Hanselmann
    """
330 a0c3fea1 Michael Hanselmann
    for child in self._children:
331 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
332 a0c3fea1 Michael Hanselmann
333 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
334 1005d816 Iustin Pop
    """Grow the block device.
335 1005d816 Iustin Pop

336 7fe23d47 Iustin Pop
    @type amount: integer
337 c41eea6e Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
338 7fe23d47 Iustin Pop
    @type dryrun: boolean
339 7fe23d47 Iustin Pop
    @param dryrun: whether to execute the operation in simulation mode
340 7fe23d47 Iustin Pop
        only, without actually increasing the size
341 1005d816 Iustin Pop

342 1005d816 Iustin Pop
    """
343 1005d816 Iustin Pop
    raise NotImplementedError
344 a0c3fea1 Michael Hanselmann
345 fcff3897 Iustin Pop
  def GetActualSize(self):
346 fcff3897 Iustin Pop
    """Return the actual disk size.
347 fcff3897 Iustin Pop

348 fcff3897 Iustin Pop
    @note: the device needs to be active when this is called
349 fcff3897 Iustin Pop

350 fcff3897 Iustin Pop
    """
351 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
352 fcff3897 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize64", self.dev_path])
353 fcff3897 Iustin Pop
    if result.failed:
354 fcff3897 Iustin Pop
      _ThrowError("blockdev failed (%s): %s",
355 fcff3897 Iustin Pop
                  result.fail_reason, result.output)
356 fcff3897 Iustin Pop
    try:
357 fcff3897 Iustin Pop
      sz = int(result.output.strip())
358 fcff3897 Iustin Pop
    except (ValueError, TypeError), err:
359 fcff3897 Iustin Pop
      _ThrowError("Failed to parse blockdev output: %s", str(err))
360 fcff3897 Iustin Pop
    return sz
361 fcff3897 Iustin Pop
362 a8083063 Iustin Pop
  def __repr__(self):
363 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
364 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
365 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
366 a8083063 Iustin Pop
367 a8083063 Iustin Pop
368 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
369 a8083063 Iustin Pop
  """Logical Volume block device.
370 a8083063 Iustin Pop

371 a8083063 Iustin Pop
  """
372 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
373 6136f8f0 Iustin Pop
  _INVALID_NAMES = frozenset([".", "..", "snapshot", "pvmove"])
374 6136f8f0 Iustin Pop
  _INVALID_SUBSTRINGS = frozenset(["_mlog", "_mimage"])
375 6136f8f0 Iustin Pop
376 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
377 a8083063 Iustin Pop
    """Attaches to a LV device.
378 a8083063 Iustin Pop

379 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
380 a8083063 Iustin Pop

381 a8083063 Iustin Pop
    """
382 464f8daf Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children, size)
383 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
384 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
385 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
386 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
387 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
388 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
389 99e8295c Iustin Pop
    self._degraded = True
390 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
391 a8083063 Iustin Pop
    self.Attach()
392 a8083063 Iustin Pop
393 a8083063 Iustin Pop
  @classmethod
394 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
395 a8083063 Iustin Pop
    """Create a new logical volume.
396 a8083063 Iustin Pop

397 a8083063 Iustin Pop
    """
398 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
399 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
400 6c626518 Iustin Pop
                                   str(unique_id))
401 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
402 6136f8f0 Iustin Pop
    cls._ValidateName(vg_name)
403 6136f8f0 Iustin Pop
    cls._ValidateName(lv_name)
404 2070598f Iustin Pop
    pvs_info = cls.GetPVInfo([vg_name])
405 a8083063 Iustin Pop
    if not pvs_info:
406 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", vg_name)
407 a8083063 Iustin Pop
    pvs_info.sort()
408 a8083063 Iustin Pop
    pvs_info.reverse()
409 5b7b5d49 Guido Trotter
410 e687ec01 Michael Hanselmann
    pvlist = [pv[1] for pv in pvs_info]
411 403f5172 Guido Trotter
    if compat.any(":" in v for v in pvlist):
412 01b6558a Iustin Pop
      _ThrowError("Some of your PVs have the invalid character ':' in their"
413 01b6558a Iustin Pop
                  " name, this is not supported - please filter them out"
414 01b6558a Iustin Pop
                  " in lvm.conf using either 'filter' or 'preferred_names'")
415 e687ec01 Michael Hanselmann
    free_size = sum([pv[0] for pv in pvs_info])
416 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
417 fecbe9d5 Iustin Pop
    stripes = min(current_pvs, constants.LVM_STRIPECOUNT)
418 5b7b5d49 Guido Trotter
419 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
420 5b7b5d49 Guido Trotter
    # calling the create function.
421 a8083063 Iustin Pop
    if free_size < size:
422 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
423 82463074 Iustin Pop
                  " available %s", size, free_size)
424 fecbe9d5 Iustin Pop
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
425 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
426 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
427 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
428 fecbe9d5 Iustin Pop
    # stripes
429 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
430 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
431 fecbe9d5 Iustin Pop
      if not result.failed:
432 fecbe9d5 Iustin Pop
        break
433 a8083063 Iustin Pop
    if result.failed:
434 82463074 Iustin Pop
      _ThrowError("LV create failed (%s): %s",
435 82463074 Iustin Pop
                  result.fail_reason, result.output)
436 464f8daf Iustin Pop
    return LogicalVolume(unique_id, children, size)
437 a8083063 Iustin Pop
438 a8083063 Iustin Pop
  @staticmethod
439 197478f2 Renรฉ Nussbaumer
  def _GetVolumeInfo(lvm_cmd, fields):
440 197478f2 Renรฉ Nussbaumer
    """Returns LVM Volumen infos using lvm_cmd
441 197478f2 Renรฉ Nussbaumer

442 197478f2 Renรฉ Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
443 197478f2 Renรฉ Nussbaumer
    @param fields: Fields to return
444 197478f2 Renรฉ Nussbaumer
    @return: A list of dicts each with the parsed fields
445 197478f2 Renรฉ Nussbaumer

446 197478f2 Renรฉ Nussbaumer
    """
447 197478f2 Renรฉ Nussbaumer
    if not fields:
448 197478f2 Renรฉ Nussbaumer
      raise errors.ProgrammerError("No fields specified")
449 197478f2 Renรฉ Nussbaumer
450 197478f2 Renรฉ Nussbaumer
    sep = "|"
451 197478f2 Renรฉ Nussbaumer
    cmd = [lvm_cmd, "--noheadings", "--nosuffix", "--units=m", "--unbuffered",
452 197478f2 Renรฉ Nussbaumer
           "--separator=%s" % sep, "-o%s" % ",".join(fields)]
453 197478f2 Renรฉ Nussbaumer
454 197478f2 Renรฉ Nussbaumer
    result = utils.RunCmd(cmd)
455 197478f2 Renรฉ Nussbaumer
    if result.failed:
456 197478f2 Renรฉ Nussbaumer
      raise errors.CommandError("Can't get the volume information: %s - %s" %
457 197478f2 Renรฉ Nussbaumer
                                (result.fail_reason, result.output))
458 197478f2 Renรฉ Nussbaumer
459 197478f2 Renรฉ Nussbaumer
    data = []
460 197478f2 Renรฉ Nussbaumer
    for line in result.stdout.splitlines():
461 197478f2 Renรฉ Nussbaumer
      splitted_fields = line.strip().split(sep)
462 197478f2 Renรฉ Nussbaumer
463 197478f2 Renรฉ Nussbaumer
      if len(fields) != len(splitted_fields):
464 197478f2 Renรฉ Nussbaumer
        raise errors.CommandError("Can't parse %s output: line '%s'" %
465 197478f2 Renรฉ Nussbaumer
                                  (lvm_cmd, line))
466 197478f2 Renรฉ Nussbaumer
467 197478f2 Renรฉ Nussbaumer
      data.append(splitted_fields)
468 197478f2 Renรฉ Nussbaumer
469 197478f2 Renรฉ Nussbaumer
    return data
470 197478f2 Renรฉ Nussbaumer
471 197478f2 Renรฉ Nussbaumer
  @classmethod
472 197478f2 Renรฉ Nussbaumer
  def GetPVInfo(cls, vg_names, filter_allocatable=True):
473 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
474 a8083063 Iustin Pop

475 2070598f Iustin Pop
    @param vg_names: list of volume group names, if empty all will be returned
476 2070598f Iustin Pop
    @param filter_allocatable: whether to skip over unallocatable PVs
477 a8083063 Iustin Pop

478 c41eea6e Iustin Pop
    @rtype: list
479 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
480 098c0958 Michael Hanselmann

481 a8083063 Iustin Pop
    """
482 197478f2 Renรฉ Nussbaumer
    try:
483 197478f2 Renรฉ Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
484 197478f2 Renรฉ Nussbaumer
                                        "pv_attr"])
485 197478f2 Renรฉ Nussbaumer
    except errors.GenericError, err:
486 197478f2 Renรฉ Nussbaumer
      logging.error("Can't get PV information: %s", err)
487 a8083063 Iustin Pop
      return None
488 197478f2 Renรฉ Nussbaumer
489 a8083063 Iustin Pop
    data = []
490 197478f2 Renรฉ Nussbaumer
    for pv_name, vg_name, pv_free, pv_attr in info:
491 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
492 197478f2 Renรฉ Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
493 a8083063 Iustin Pop
        continue
494 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
495 197478f2 Renรฉ Nussbaumer
      if vg_names and vg_name not in vg_names:
496 2070598f Iustin Pop
        continue
497 197478f2 Renรฉ Nussbaumer
      data.append((float(pv_free), pv_name, vg_name))
498 197478f2 Renรฉ Nussbaumer
499 197478f2 Renรฉ Nussbaumer
    return data
500 197478f2 Renรฉ Nussbaumer
501 197478f2 Renรฉ Nussbaumer
  @classmethod
502 197478f2 Renรฉ Nussbaumer
  def GetVGInfo(cls, vg_names, filter_readonly=True):
503 197478f2 Renรฉ Nussbaumer
    """Get the free space info for specific VGs.
504 197478f2 Renรฉ Nussbaumer

505 197478f2 Renรฉ Nussbaumer
    @param vg_names: list of volume group names, if empty all will be returned
506 197478f2 Renรฉ Nussbaumer
    @param filter_readonly: whether to skip over readonly VGs
507 197478f2 Renรฉ Nussbaumer

508 197478f2 Renรฉ Nussbaumer
    @rtype: list
509 673cd9c4 Renรฉ Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
510 673cd9c4 Renรฉ Nussbaumer
             MiB
511 197478f2 Renรฉ Nussbaumer

512 197478f2 Renรฉ Nussbaumer
    """
513 197478f2 Renรฉ Nussbaumer
    try:
514 673cd9c4 Renรฉ Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
515 673cd9c4 Renรฉ Nussbaumer
                                        "vg_size"])
516 197478f2 Renรฉ Nussbaumer
    except errors.GenericError, err:
517 197478f2 Renรฉ Nussbaumer
      logging.error("Can't get VG information: %s", err)
518 197478f2 Renรฉ Nussbaumer
      return None
519 197478f2 Renรฉ Nussbaumer
520 197478f2 Renรฉ Nussbaumer
    data = []
521 673cd9c4 Renรฉ Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
522 197478f2 Renรฉ Nussbaumer
      # (possibly) skip over vgs which are not writable
523 197478f2 Renรฉ Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
524 197478f2 Renรฉ Nussbaumer
        continue
525 197478f2 Renรฉ Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
526 197478f2 Renรฉ Nussbaumer
      if vg_names and vg_name not in vg_names:
527 197478f2 Renรฉ Nussbaumer
        continue
528 673cd9c4 Renรฉ Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
529 a8083063 Iustin Pop
530 a8083063 Iustin Pop
    return data
531 a8083063 Iustin Pop
532 6136f8f0 Iustin Pop
  @classmethod
533 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
534 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
535 6136f8f0 Iustin Pop

536 6136f8f0 Iustin Pop
    The list of valid characters and restricted names is taken out of
537 6136f8f0 Iustin Pop
    the lvm(8) manpage, with the simplification that we enforce both
538 6136f8f0 Iustin Pop
    VG and LV restrictions on the names.
539 6136f8f0 Iustin Pop

540 6136f8f0 Iustin Pop
    """
541 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
542 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
543 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
544 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
545 6136f8f0 Iustin Pop
546 a8083063 Iustin Pop
  def Remove(self):
547 a8083063 Iustin Pop
    """Remove this logical volume.
548 a8083063 Iustin Pop

549 a8083063 Iustin Pop
    """
550 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
551 a8083063 Iustin Pop
      # the LV does not exist
552 0c6c04ec Iustin Pop
      return
553 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
554 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
555 a8083063 Iustin Pop
    if result.failed:
556 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
557 a8083063 Iustin Pop
558 f3e513ad Iustin Pop
  def Rename(self, new_id):
559 f3e513ad Iustin Pop
    """Rename this logical volume.
560 f3e513ad Iustin Pop

561 f3e513ad Iustin Pop
    """
562 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
563 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
564 f3e513ad Iustin Pop
    new_vg, new_name = new_id
565 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
566 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
567 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
568 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
569 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
570 f3e513ad Iustin Pop
    if result.failed:
571 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
572 be345db0 Iustin Pop
    self._lv_name = new_name
573 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
574 be345db0 Iustin Pop
575 a8083063 Iustin Pop
  def Attach(self):
576 a8083063 Iustin Pop
    """Attach to an existing LV.
577 a8083063 Iustin Pop

578 a8083063 Iustin Pop
    This method will try to see if an existing and active LV exists
579 c99a3cc0 Manuel Franceschini
    which matches our name. If so, its major/minor will be
580 a8083063 Iustin Pop
    recorded.
581 a8083063 Iustin Pop

582 a8083063 Iustin Pop
    """
583 cb999543 Iustin Pop
    self.attached = False
584 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
585 38256320 Iustin Pop
                           "--units=m", "--nosuffix",
586 38256320 Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
587 38256320 Iustin Pop
                           "vg_extent_size,stripes", self.dev_path])
588 a8083063 Iustin Pop
    if result.failed:
589 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
590 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
591 a8083063 Iustin Pop
      return False
592 38256320 Iustin Pop
    # the output can (and will) have multiple lines for multi-segment
593 38256320 Iustin Pop
    # LVs, as the 'stripes' parameter is a segment one, so we take
594 38256320 Iustin Pop
    # only the last entry, which is the one we're interested in; note
595 38256320 Iustin Pop
    # that with LVM2 anyway the 'stripes' value must be constant
596 38256320 Iustin Pop
    # across segments, so this is a no-op actually
597 38256320 Iustin Pop
    out = result.stdout.splitlines()
598 38256320 Iustin Pop
    if not out: # totally empty result? splitlines() returns at least
599 38256320 Iustin Pop
                # one line for any non-empty string
600 38256320 Iustin Pop
      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
601 38256320 Iustin Pop
      return False
602 d0c8c01d Iustin Pop
    out = out[-1].strip().rstrip(",")
603 99e8295c Iustin Pop
    out = out.split(",")
604 38256320 Iustin Pop
    if len(out) != 5:
605 38256320 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
606 99e8295c Iustin Pop
      return False
607 99e8295c Iustin Pop
608 38256320 Iustin Pop
    status, major, minor, pe_size, stripes = out
609 99e8295c Iustin Pop
    if len(status) != 6:
610 468c5f77 Iustin Pop
      logging.error("lvs lv_attr is not 6 characters (%s)", status)
611 99e8295c Iustin Pop
      return False
612 99e8295c Iustin Pop
613 99e8295c Iustin Pop
    try:
614 99e8295c Iustin Pop
      major = int(major)
615 99e8295c Iustin Pop
      minor = int(minor)
616 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
617 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
618 99e8295c Iustin Pop
619 38256320 Iustin Pop
    try:
620 38256320 Iustin Pop
      pe_size = int(float(pe_size))
621 38256320 Iustin Pop
    except (TypeError, ValueError), err:
622 38256320 Iustin Pop
      logging.error("Can't parse vg extent size: %s", err)
623 38256320 Iustin Pop
      return False
624 38256320 Iustin Pop
625 38256320 Iustin Pop
    try:
626 38256320 Iustin Pop
      stripes = int(stripes)
627 38256320 Iustin Pop
    except (TypeError, ValueError), err:
628 38256320 Iustin Pop
      logging.error("Can't parse the number of stripes: %s", err)
629 38256320 Iustin Pop
      return False
630 38256320 Iustin Pop
631 99e8295c Iustin Pop
    self.major = major
632 99e8295c Iustin Pop
    self.minor = minor
633 38256320 Iustin Pop
    self.pe_size = pe_size
634 38256320 Iustin Pop
    self.stripe_count = stripes
635 d0c8c01d Iustin Pop
    self._degraded = status[0] == "v" # virtual volume, i.e. doesn't backing
636 99e8295c Iustin Pop
                                      # storage
637 cb999543 Iustin Pop
    self.attached = True
638 99e8295c Iustin Pop
    return True
639 a8083063 Iustin Pop
640 a8083063 Iustin Pop
  def Assemble(self):
641 a8083063 Iustin Pop
    """Assemble the device.
642 a8083063 Iustin Pop

643 5bbd3f7f Michael Hanselmann
    We always run `lvchange -ay` on the LV to ensure it's active before
644 5574047a Iustin Pop
    use, as there were cases when xenvg was not active after boot
645 5574047a Iustin Pop
    (also possibly after disk issues).
646 a8083063 Iustin Pop

647 a8083063 Iustin Pop
    """
648 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
649 5574047a Iustin Pop
    if result.failed:
650 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
651 a8083063 Iustin Pop
652 a8083063 Iustin Pop
  def Shutdown(self):
653 a8083063 Iustin Pop
    """Shutdown the device.
654 a8083063 Iustin Pop

655 a8083063 Iustin Pop
    This is a no-op for the LV device type, as we don't deactivate the
656 a8083063 Iustin Pop
    volumes on shutdown.
657 a8083063 Iustin Pop

658 a8083063 Iustin Pop
    """
659 746f7476 Iustin Pop
    pass
660 a8083063 Iustin Pop
661 9db6dbce Iustin Pop
  def GetSyncStatus(self):
662 9db6dbce Iustin Pop
    """Returns the sync status of the device.
663 9db6dbce Iustin Pop

664 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
665 9db6dbce Iustin Pop
    status of the mirror.
666 9db6dbce Iustin Pop

667 9db6dbce Iustin Pop
    For logical volumes, sync_percent and estimated_time are always
668 9db6dbce Iustin Pop
    None (no recovery in progress, as we don't handle the mirrored LV
669 0834c866 Iustin Pop
    case). The is_degraded parameter is the inverse of the ldisk
670 0834c866 Iustin Pop
    parameter.
671 9db6dbce Iustin Pop

672 0834c866 Iustin Pop
    For the ldisk parameter, we check if the logical volume has the
673 0834c866 Iustin Pop
    'virtual' type, which means it's not backed by existing storage
674 0834c866 Iustin Pop
    anymore (read from it return I/O error). This happens after a
675 0834c866 Iustin Pop
    physical disk failure and subsequent 'vgreduce --removemissing' on
676 0834c866 Iustin Pop
    the volume group.
677 9db6dbce Iustin Pop

678 99e8295c Iustin Pop
    The status was already read in Attach, so we just return it.
679 99e8295c Iustin Pop

680 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
681 c41eea6e Iustin Pop

682 9db6dbce Iustin Pop
    """
683 f208978a Michael Hanselmann
    if self._degraded:
684 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
685 f208978a Michael Hanselmann
    else:
686 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
687 f208978a Michael Hanselmann
688 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
689 96acbc09 Michael Hanselmann
                                  major=self.major,
690 96acbc09 Michael Hanselmann
                                  minor=self.minor,
691 96acbc09 Michael Hanselmann
                                  sync_percent=None,
692 96acbc09 Michael Hanselmann
                                  estimated_time=None,
693 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
694 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
695 9db6dbce Iustin Pop
696 a8083063 Iustin Pop
  def Open(self, force=False):
697 a8083063 Iustin Pop
    """Make the device ready for I/O.
698 a8083063 Iustin Pop

699 a8083063 Iustin Pop
    This is a no-op for the LV device type.
700 a8083063 Iustin Pop

701 a8083063 Iustin Pop
    """
702 fdbd668d Iustin Pop
    pass
703 a8083063 Iustin Pop
704 a8083063 Iustin Pop
  def Close(self):
705 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
706 a8083063 Iustin Pop

707 a8083063 Iustin Pop
    This is a no-op for the LV device type.
708 a8083063 Iustin Pop

709 a8083063 Iustin Pop
    """
710 fdbd668d Iustin Pop
    pass
711 a8083063 Iustin Pop
712 a8083063 Iustin Pop
  def Snapshot(self, size):
713 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
714 a8083063 Iustin Pop

715 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
716 800ac399 Iustin Pop

717 a8083063 Iustin Pop
    """
718 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
719 a8083063 Iustin Pop
720 a8083063 Iustin Pop
    # remove existing snapshot if found
721 464f8daf Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None, size)
722 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
723 a8083063 Iustin Pop
724 197478f2 Renรฉ Nussbaumer
    vg_info = self.GetVGInfo([self._vg_name])
725 197478f2 Renรฉ Nussbaumer
    if not vg_info:
726 197478f2 Renรฉ Nussbaumer
      _ThrowError("Can't compute VG info for vg %s", self._vg_name)
727 673cd9c4 Renรฉ Nussbaumer
    free_size, _, _ = vg_info[0]
728 a8083063 Iustin Pop
    if free_size < size:
729 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
730 82463074 Iustin Pop
                  " available %s", size, free_size)
731 a8083063 Iustin Pop
732 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
733 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
734 a8083063 Iustin Pop
    if result.failed:
735 82463074 Iustin Pop
      _ThrowError("command: %s error: %s - %s",
736 82463074 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
737 a8083063 Iustin Pop
738 800ac399 Iustin Pop
    return (self._vg_name, snap_name)
739 a8083063 Iustin Pop
740 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
741 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
742 a0c3fea1 Michael Hanselmann

743 a0c3fea1 Michael Hanselmann
    """
744 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
745 a0c3fea1 Michael Hanselmann
746 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
747 d0c8c01d Iustin Pop
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
748 d0c8c01d Iustin Pop
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
749 a0c3fea1 Michael Hanselmann
750 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
751 a0c3fea1 Michael Hanselmann
    text = text[:128]
752 a0c3fea1 Michael Hanselmann
753 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
754 a0c3fea1 Michael Hanselmann
                           self.dev_path])
755 a0c3fea1 Michael Hanselmann
    if result.failed:
756 82463074 Iustin Pop
      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
757 82463074 Iustin Pop
                  result.output)
758 82463074 Iustin Pop
759 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
760 1005d816 Iustin Pop
    """Grow the logical volume.
761 1005d816 Iustin Pop

762 1005d816 Iustin Pop
    """
763 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
764 38256320 Iustin Pop
      if not self.Attach():
765 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
766 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
767 38256320 Iustin Pop
    rest = amount % full_stripe_size
768 38256320 Iustin Pop
    if rest != 0:
769 38256320 Iustin Pop
      amount += full_stripe_size - rest
770 7fe23d47 Iustin Pop
    cmd = ["lvextend", "-L", "+%dm" % amount]
771 7fe23d47 Iustin Pop
    if dryrun:
772 7fe23d47 Iustin Pop
      cmd.append("--test")
773 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
774 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
775 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
776 1005d816 Iustin Pop
    # supports 'cling'
777 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
778 7fe23d47 Iustin Pop
      result = utils.RunCmd(cmd + ["--alloc", alloc_policy, self.dev_path])
779 1005d816 Iustin Pop
      if not result.failed:
780 1005d816 Iustin Pop
        return
781 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
782 a0c3fea1 Michael Hanselmann
783 a0c3fea1 Michael Hanselmann
784 6b90c22e Iustin Pop
class DRBD8Status(object):
785 6b90c22e Iustin Pop
  """A DRBD status representation class.
786 6b90c22e Iustin Pop

787 6b90c22e Iustin Pop
  Note that this doesn't support unconfigured devices (cs:Unconfigured).
788 6b90c22e Iustin Pop

789 6b90c22e Iustin Pop
  """
790 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
791 01e2ce3a Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
792 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
793 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
794 7a380ddf Renรฉ Nussbaumer
                       # Due to a bug in drbd in the kernel, introduced in
795 7a380ddf Renรฉ Nussbaumer
                       # commit 4b0715f096 (still unfixed as of 2011-08-22)
796 7a380ddf Renรฉ Nussbaumer
                       "(?:\s|M)"
797 7a380ddf Renรฉ Nussbaumer
                       "finish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
798 6b90c22e Iustin Pop
799 3c003d9d Iustin Pop
  CS_UNCONFIGURED = "Unconfigured"
800 3c003d9d Iustin Pop
  CS_STANDALONE = "StandAlone"
801 3c003d9d Iustin Pop
  CS_WFCONNECTION = "WFConnection"
802 3c003d9d Iustin Pop
  CS_WFREPORTPARAMS = "WFReportParams"
803 3c003d9d Iustin Pop
  CS_CONNECTED = "Connected"
804 3c003d9d Iustin Pop
  CS_STARTINGSYNCS = "StartingSyncS"
805 3c003d9d Iustin Pop
  CS_STARTINGSYNCT = "StartingSyncT"
806 3c003d9d Iustin Pop
  CS_WFBITMAPS = "WFBitMapS"
807 3c003d9d Iustin Pop
  CS_WFBITMAPT = "WFBitMapT"
808 3c003d9d Iustin Pop
  CS_WFSYNCUUID = "WFSyncUUID"
809 3c003d9d Iustin Pop
  CS_SYNCSOURCE = "SyncSource"
810 3c003d9d Iustin Pop
  CS_SYNCTARGET = "SyncTarget"
811 3c003d9d Iustin Pop
  CS_PAUSEDSYNCS = "PausedSyncS"
812 3c003d9d Iustin Pop
  CS_PAUSEDSYNCT = "PausedSyncT"
813 3c003d9d Iustin Pop
  CSET_SYNC = frozenset([
814 3c003d9d Iustin Pop
    CS_WFREPORTPARAMS,
815 3c003d9d Iustin Pop
    CS_STARTINGSYNCS,
816 3c003d9d Iustin Pop
    CS_STARTINGSYNCT,
817 3c003d9d Iustin Pop
    CS_WFBITMAPS,
818 3c003d9d Iustin Pop
    CS_WFBITMAPT,
819 3c003d9d Iustin Pop
    CS_WFSYNCUUID,
820 3c003d9d Iustin Pop
    CS_SYNCSOURCE,
821 3c003d9d Iustin Pop
    CS_SYNCTARGET,
822 3c003d9d Iustin Pop
    CS_PAUSEDSYNCS,
823 3c003d9d Iustin Pop
    CS_PAUSEDSYNCT,
824 3c003d9d Iustin Pop
    ])
825 3c003d9d Iustin Pop
826 3c003d9d Iustin Pop
  DS_DISKLESS = "Diskless"
827 3c003d9d Iustin Pop
  DS_ATTACHING = "Attaching" # transient state
828 3c003d9d Iustin Pop
  DS_FAILED = "Failed" # transient state, next: diskless
829 3c003d9d Iustin Pop
  DS_NEGOTIATING = "Negotiating" # transient state
830 3c003d9d Iustin Pop
  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
831 3c003d9d Iustin Pop
  DS_OUTDATED = "Outdated"
832 3c003d9d Iustin Pop
  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
833 3c003d9d Iustin Pop
  DS_CONSISTENT = "Consistent"
834 3c003d9d Iustin Pop
  DS_UPTODATE = "UpToDate" # normal state
835 3c003d9d Iustin Pop
836 3c003d9d Iustin Pop
  RO_PRIMARY = "Primary"
837 3c003d9d Iustin Pop
  RO_SECONDARY = "Secondary"
838 3c003d9d Iustin Pop
  RO_UNKNOWN = "Unknown"
839 3c003d9d Iustin Pop
840 6b90c22e Iustin Pop
  def __init__(self, procline):
841 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
842 767d52d3 Iustin Pop
    if u:
843 3c003d9d Iustin Pop
      self.cstatus = self.CS_UNCONFIGURED
844 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
845 767d52d3 Iustin Pop
    else:
846 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
847 767d52d3 Iustin Pop
      if not m:
848 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
849 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
850 767d52d3 Iustin Pop
      self.lrole = m.group(2)
851 767d52d3 Iustin Pop
      self.rrole = m.group(3)
852 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
853 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
854 767d52d3 Iustin Pop
855 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
856 6b90c22e Iustin Pop
857 3c003d9d Iustin Pop
    self.is_standalone = self.cstatus == self.CS_STANDALONE
858 3c003d9d Iustin Pop
    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
859 3c003d9d Iustin Pop
    self.is_connected = self.cstatus == self.CS_CONNECTED
860 3c003d9d Iustin Pop
    self.is_primary = self.lrole == self.RO_PRIMARY
861 3c003d9d Iustin Pop
    self.is_secondary = self.lrole == self.RO_SECONDARY
862 3c003d9d Iustin Pop
    self.peer_primary = self.rrole == self.RO_PRIMARY
863 3c003d9d Iustin Pop
    self.peer_secondary = self.rrole == self.RO_SECONDARY
864 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
865 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
866 6b90c22e Iustin Pop
867 3c003d9d Iustin Pop
    self.is_diskless = self.ldisk == self.DS_DISKLESS
868 3c003d9d Iustin Pop
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
869 6b90c22e Iustin Pop
870 3c003d9d Iustin Pop
    self.is_in_resync = self.cstatus in self.CSET_SYNC
871 3c003d9d Iustin Pop
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
872 6b93ec9d Iustin Pop
873 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
874 6b90c22e Iustin Pop
    if m:
875 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
876 6b90c22e Iustin Pop
      hours = int(m.group(2))
877 6b90c22e Iustin Pop
      minutes = int(m.group(3))
878 6b90c22e Iustin Pop
      seconds = int(m.group(4))
879 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
880 6b90c22e Iustin Pop
    else:
881 3c003d9d Iustin Pop
      # we have (in this if branch) no percent information, but if
882 3c003d9d Iustin Pop
      # we're resyncing we need to 'fake' a sync percent information,
883 3c003d9d Iustin Pop
      # as this is how cmdlib determines if it makes sense to wait for
884 3c003d9d Iustin Pop
      # resyncing or not
885 3c003d9d Iustin Pop
      if self.is_in_resync:
886 3c003d9d Iustin Pop
        self.sync_percent = 0
887 3c003d9d Iustin Pop
      else:
888 3c003d9d Iustin Pop
        self.sync_percent = None
889 6b90c22e Iustin Pop
      self.est_time = None
890 6b90c22e Iustin Pop
891 6b90c22e Iustin Pop
892 b459a848 Andrea Spadaccini
class BaseDRBD(BlockDev): # pylint: disable=W0223
893 0f7f32d9 Iustin Pop
  """Base DRBD class.
894 a8083063 Iustin Pop

895 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
896 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
897 0f7f32d9 Iustin Pop

898 abdf0113 Iustin Pop
  """
899 fcee765d Manuel Franceschini
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)(?:\.\d+)?"
900 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
901 9122e60a Iustin Pop
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
902 9122e60a Iustin Pop
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
903 a8083063 Iustin Pop
904 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
905 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
906 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
907 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
908 a8083063 Iustin Pop
909 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
910 549071a0 Luca Bigliardi
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
911 6b90c22e Iustin Pop
912 abdf0113 Iustin Pop
  @staticmethod
913 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
914 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
915 a8083063 Iustin Pop

916 a8083063 Iustin Pop
    """
917 abdf0113 Iustin Pop
    try:
918 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
919 f6eaed12 Iustin Pop
    except EnvironmentError, err:
920 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
921 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
922 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
923 f6eaed12 Iustin Pop
      else:
924 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
925 abdf0113 Iustin Pop
    if not data:
926 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
927 abdf0113 Iustin Pop
    return data
928 a8083063 Iustin Pop
929 9122e60a Iustin Pop
  @classmethod
930 9122e60a Iustin Pop
  def _MassageProcData(cls, data):
931 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
932 a8083063 Iustin Pop

933 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
934 c41eea6e Iustin Pop
        for that minor
935 a8083063 Iustin Pop

936 a8083063 Iustin Pop
    """
937 abdf0113 Iustin Pop
    results = {}
938 abdf0113 Iustin Pop
    old_minor = old_line = None
939 abdf0113 Iustin Pop
    for line in data:
940 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
941 67d101d4 Iustin Pop
        continue
942 9122e60a Iustin Pop
      lresult = cls._VALID_LINE_RE.match(line)
943 abdf0113 Iustin Pop
      if lresult is not None:
944 abdf0113 Iustin Pop
        if old_minor is not None:
945 abdf0113 Iustin Pop
          results[old_minor] = old_line
946 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
947 abdf0113 Iustin Pop
        old_line = line
948 abdf0113 Iustin Pop
      else:
949 abdf0113 Iustin Pop
        if old_minor is not None:
950 abdf0113 Iustin Pop
          old_line += " " + line.strip()
951 abdf0113 Iustin Pop
    # add last line
952 abdf0113 Iustin Pop
    if old_minor is not None:
953 abdf0113 Iustin Pop
      results[old_minor] = old_line
954 abdf0113 Iustin Pop
    return results
955 a8083063 Iustin Pop
956 abdf0113 Iustin Pop
  @classmethod
957 fcee765d Manuel Franceschini
  def _GetVersion(cls, proc_data):
958 abdf0113 Iustin Pop
    """Return the DRBD version.
959 a8083063 Iustin Pop

960 abdf0113 Iustin Pop
    This will return a dict with keys:
961 c41eea6e Iustin Pop
      - k_major
962 c41eea6e Iustin Pop
      - k_minor
963 c41eea6e Iustin Pop
      - k_point
964 c41eea6e Iustin Pop
      - api
965 c41eea6e Iustin Pop
      - proto
966 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
967 a8083063 Iustin Pop

968 a8083063 Iustin Pop
    """
969 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
970 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
971 abdf0113 Iustin Pop
    if not version:
972 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
973 abdf0113 Iustin Pop
                                    first_line)
974 a8083063 Iustin Pop
975 abdf0113 Iustin Pop
    values = version.groups()
976 d0c8c01d Iustin Pop
    retval = {"k_major": int(values[0]),
977 d0c8c01d Iustin Pop
              "k_minor": int(values[1]),
978 d0c8c01d Iustin Pop
              "k_point": int(values[2]),
979 d0c8c01d Iustin Pop
              "api": int(values[3]),
980 d0c8c01d Iustin Pop
              "proto": int(values[4]),
981 abdf0113 Iustin Pop
             }
982 abdf0113 Iustin Pop
    if values[5] is not None:
983 d0c8c01d Iustin Pop
      retval["proto2"] = values[5]
984 a8083063 Iustin Pop
985 abdf0113 Iustin Pop
    return retval
986 abdf0113 Iustin Pop
987 abdf0113 Iustin Pop
  @staticmethod
988 549071a0 Luca Bigliardi
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
989 549071a0 Luca Bigliardi
    """Returns DRBD usermode_helper currently set.
990 549071a0 Luca Bigliardi

991 549071a0 Luca Bigliardi
    """
992 549071a0 Luca Bigliardi
    try:
993 549071a0 Luca Bigliardi
      helper = utils.ReadFile(filename).splitlines()[0]
994 549071a0 Luca Bigliardi
    except EnvironmentError, err:
995 549071a0 Luca Bigliardi
      if err.errno == errno.ENOENT:
996 549071a0 Luca Bigliardi
        _ThrowError("The file %s cannot be opened, check if the module"
997 549071a0 Luca Bigliardi
                    " is loaded (%s)", filename, str(err))
998 549071a0 Luca Bigliardi
      else:
999 549071a0 Luca Bigliardi
        _ThrowError("Can't read DRBD helper file %s: %s", filename, str(err))
1000 549071a0 Luca Bigliardi
    if not helper:
1001 549071a0 Luca Bigliardi
      _ThrowError("Can't read any data from %s", filename)
1002 549071a0 Luca Bigliardi
    return helper
1003 549071a0 Luca Bigliardi
1004 549071a0 Luca Bigliardi
  @staticmethod
1005 abdf0113 Iustin Pop
  def _DevPath(minor):
1006 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
1007 a8083063 Iustin Pop

1008 a8083063 Iustin Pop
    """
1009 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
1010 a8083063 Iustin Pop
1011 abdf0113 Iustin Pop
  @classmethod
1012 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
1013 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
1014 a8083063 Iustin Pop

1015 a8083063 Iustin Pop
    """
1016 abdf0113 Iustin Pop
    data = cls._GetProcData()
1017 a8083063 Iustin Pop
1018 abdf0113 Iustin Pop
    used_devs = {}
1019 abdf0113 Iustin Pop
    for line in data:
1020 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1021 abdf0113 Iustin Pop
      if not match:
1022 abdf0113 Iustin Pop
        continue
1023 abdf0113 Iustin Pop
      minor = int(match.group(1))
1024 abdf0113 Iustin Pop
      state = match.group(2)
1025 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1026 abdf0113 Iustin Pop
        continue
1027 abdf0113 Iustin Pop
      used_devs[minor] = state, line
1028 a8083063 Iustin Pop
1029 abdf0113 Iustin Pop
    return used_devs
1030 a8083063 Iustin Pop
1031 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
1032 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
1033 0834c866 Iustin Pop

1034 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1035 a8083063 Iustin Pop

1036 a8083063 Iustin Pop
    """
1037 abdf0113 Iustin Pop
    if minor is None:
1038 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
1039 cb999543 Iustin Pop
      self.attached = False
1040 a8083063 Iustin Pop
    else:
1041 abdf0113 Iustin Pop
      self.minor = minor
1042 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
1043 cb999543 Iustin Pop
      self.attached = True
1044 a8083063 Iustin Pop
1045 a8083063 Iustin Pop
  @staticmethod
1046 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
1047 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
1048 a8083063 Iustin Pop

1049 abdf0113 Iustin Pop
    This currently only check the size, which must be around
1050 abdf0113 Iustin Pop
    128MiB.
1051 a8083063 Iustin Pop

1052 a8083063 Iustin Pop
    """
1053 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1054 abdf0113 Iustin Pop
    if result.failed:
1055 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
1056 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
1057 a8083063 Iustin Pop
    try:
1058 abdf0113 Iustin Pop
      sectors = int(result.stdout)
1059 691744c4 Iustin Pop
    except (TypeError, ValueError):
1060 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
1061 c04bc777 Iustin Pop
    num_bytes = sectors * 512
1062 c04bc777 Iustin Pop
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
1063 c04bc777 Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (num_bytes / 1024 / 1024))
1064 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
1065 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
1066 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
1067 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
1068 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
1069 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
1070 c04bc777 Iustin Pop
    if num_bytes > 1024 * 1024 * 1024:
1071 c04bc777 Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (num_bytes / 1024 / 1024))
1072 a8083063 Iustin Pop
1073 abdf0113 Iustin Pop
  def Rename(self, new_id):
1074 abdf0113 Iustin Pop
    """Rename a device.
1075 a8083063 Iustin Pop

1076 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1077 a8083063 Iustin Pop

1078 a8083063 Iustin Pop
    """
1079 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1080 a8083063 Iustin Pop
1081 f3e513ad Iustin Pop
1082 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1083 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1084 a2cfdea2 Iustin Pop

1085 a2cfdea2 Iustin Pop
  This implements the local host part of the DRBD device, i.e. it
1086 a2cfdea2 Iustin Pop
  doesn't do anything to the supposed peer. If you need a fully
1087 a2cfdea2 Iustin Pop
  connected DRBD pair, you need to use this class on both hosts.
1088 a2cfdea2 Iustin Pop

1089 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1090 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1091 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1092 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1093 a2cfdea2 Iustin Pop

1094 a2cfdea2 Iustin Pop
  """
1095 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1096 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1097 a2cfdea2 Iustin Pop
1098 cf8df3f3 Iustin Pop
  # timeout constants
1099 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
1100 cf8df3f3 Iustin Pop
1101 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1102 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1103 fc1dc9d7 Iustin Pop
      children = []
1104 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
1105 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1106 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
1107 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1108 310fbb64 Iustin Pop
    (self._lhost, self._lport,
1109 310fbb64 Iustin Pop
     self._rhost, self._rport,
1110 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
1111 310fbb64 Iustin Pop
    if children:
1112 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
1113 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
1114 310fbb64 Iustin Pop
        children = []
1115 464f8daf Iustin Pop
    super(DRBD8, self).__init__(unique_id, children, size)
1116 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1117 fcee765d Manuel Franceschini
    version = self._GetVersion(self._GetProcData())
1118 e687ec01 Michael Hanselmann
    if version["k_major"] != 8:
1119 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
1120 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
1121 d0c8c01d Iustin Pop
                  version["k_major"], version["k_minor"])
1122 a2cfdea2 Iustin Pop
1123 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1124 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1125 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1126 ffa1c0dc Iustin Pop
                       (unique_id,))
1127 a2cfdea2 Iustin Pop
    self.Attach()
1128 a2cfdea2 Iustin Pop
1129 a2cfdea2 Iustin Pop
  @classmethod
1130 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1131 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1132 a2cfdea2 Iustin Pop

1133 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1134 a2cfdea2 Iustin Pop

1135 a2cfdea2 Iustin Pop
    """
1136 18e4dee6 Iustin Pop
    # Zero the metadata first, in order to make sure drbdmeta doesn't
1137 18e4dee6 Iustin Pop
    # try to auto-detect existing filesystems or similar (see
1138 18e4dee6 Iustin Pop
    # http://code.google.com/p/ganeti/issues/detail?id=182); we only
1139 18e4dee6 Iustin Pop
    # care about the first 128MB of data in the device, even though it
1140 18e4dee6 Iustin Pop
    # can be bigger
1141 18e4dee6 Iustin Pop
    result = utils.RunCmd([constants.DD_CMD,
1142 18e4dee6 Iustin Pop
                           "if=/dev/zero", "of=%s" % dev_path,
1143 18e4dee6 Iustin Pop
                           "bs=1048576", "count=128", "oflag=direct"])
1144 18e4dee6 Iustin Pop
    if result.failed:
1145 18e4dee6 Iustin Pop
      _ThrowError("Can't wipe the meta device: %s", result.output)
1146 18e4dee6 Iustin Pop
1147 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1148 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1149 a2cfdea2 Iustin Pop
    if result.failed:
1150 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1151 a2cfdea2 Iustin Pop
1152 a2cfdea2 Iustin Pop
  @classmethod
1153 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1154 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1155 a2cfdea2 Iustin Pop

1156 a2cfdea2 Iustin Pop
    This is specific to 8.x as the minors are allocated dynamically,
1157 a2cfdea2 Iustin Pop
    so non-existing numbers up to a max minor count are actually free.
1158 a2cfdea2 Iustin Pop

1159 a2cfdea2 Iustin Pop
    """
1160 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1161 a2cfdea2 Iustin Pop
1162 a2cfdea2 Iustin Pop
    highest = None
1163 a2cfdea2 Iustin Pop
    for line in data:
1164 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1165 a2cfdea2 Iustin Pop
      if match:
1166 a2cfdea2 Iustin Pop
        return int(match.group(1))
1167 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1168 a2cfdea2 Iustin Pop
      if match:
1169 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1170 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1171 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1172 a2cfdea2 Iustin Pop
      return 0
1173 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1174 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1175 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1176 a2cfdea2 Iustin Pop
    return highest + 1
1177 a2cfdea2 Iustin Pop
1178 a2cfdea2 Iustin Pop
  @classmethod
1179 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1180 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1181 a2cfdea2 Iustin Pop

1182 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1183 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1184 a2cfdea2 Iustin Pop

1185 a2cfdea2 Iustin Pop
    """
1186 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1187 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1188 a2cfdea2 Iustin Pop
1189 a2cfdea2 Iustin Pop
    # pyparsing setup
1190 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1191 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1192 5a672c30 Manuel Franceschini
    lbracket = pyp.Literal("[").suppress()
1193 5a672c30 Manuel Franceschini
    rbracket = pyp.Literal("]").suppress()
1194 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1195 5a672c30 Manuel Franceschini
    colon = pyp.Literal(":").suppress()
1196 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1197 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1198 a2cfdea2 Iustin Pop
1199 e687ec01 Michael Hanselmann
    comment = pyp.Literal("#") + pyp.Optional(pyp.restOfLine)
1200 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1201 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1202 a2cfdea2 Iustin Pop
1203 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1204 a2cfdea2 Iustin Pop
1205 a2cfdea2 Iustin Pop
    # value types
1206 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1207 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1208 5a672c30 Manuel Franceschini
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1209 5a672c30 Manuel Franceschini
                 pyp.Word(pyp.nums + ".") + colon + number)
1210 5a672c30 Manuel Franceschini
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
1211 5a672c30 Manuel Franceschini
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
1212 5a672c30 Manuel Franceschini
                 pyp.Optional(rbracket) + colon + number)
1213 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1214 5a672c30 Manuel Franceschini
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
1215 01e2ce3a Iustin Pop
    # device name, extended syntax
1216 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1217 a2cfdea2 Iustin Pop
1218 a2cfdea2 Iustin Pop
    # a statement
1219 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1220 5a672c30 Manuel Franceschini
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
1221 01e2ce3a Iustin Pop
                         device_value) +
1222 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1223 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1224 a2cfdea2 Iustin Pop
1225 a2cfdea2 Iustin Pop
    # an entire section
1226 d0c8c01d Iustin Pop
    section_name = pyp.Word(pyp.alphas + "_")
1227 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1228 a2cfdea2 Iustin Pop
1229 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1230 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1231 a2cfdea2 Iustin Pop
1232 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1233 a2cfdea2 Iustin Pop
1234 a2cfdea2 Iustin Pop
    return bnf
1235 a2cfdea2 Iustin Pop
1236 a2cfdea2 Iustin Pop
  @classmethod
1237 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1238 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1239 a2cfdea2 Iustin Pop

1240 a2cfdea2 Iustin Pop
    """
1241 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1242 a2cfdea2 Iustin Pop
    if result.failed:
1243 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1244 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1245 3840729d Iustin Pop
      return None
1246 3840729d Iustin Pop
    return result.stdout
1247 3840729d Iustin Pop
1248 3840729d Iustin Pop
  @classmethod
1249 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1250 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1251 3840729d Iustin Pop

1252 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1253 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1254 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1255 3840729d Iustin Pop
    by _GetShowData.
1256 3840729d Iustin Pop

1257 3840729d Iustin Pop
    """
1258 3840729d Iustin Pop
    data = {}
1259 a2cfdea2 Iustin Pop
    if not out:
1260 a2cfdea2 Iustin Pop
      return data
1261 a2cfdea2 Iustin Pop
1262 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1263 a2cfdea2 Iustin Pop
    # run pyparse
1264 a2cfdea2 Iustin Pop
1265 a2cfdea2 Iustin Pop
    try:
1266 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1267 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1268 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1269 a2cfdea2 Iustin Pop
1270 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1271 a2cfdea2 Iustin Pop
    for section in results:
1272 a2cfdea2 Iustin Pop
      sname = section[0]
1273 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1274 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1275 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1276 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1277 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1278 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1279 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1280 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1281 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1282 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1283 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1284 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1285 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1286 a2cfdea2 Iustin Pop
    return data
1287 a2cfdea2 Iustin Pop
1288 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1289 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1290 a2cfdea2 Iustin Pop

1291 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1292 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1293 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1294 a2cfdea2 Iustin Pop
    device.
1295 a2cfdea2 Iustin Pop

1296 a2cfdea2 Iustin Pop
    """
1297 b00b95dd Iustin Pop
    if self._children:
1298 b00b95dd Iustin Pop
      backend, meta = self._children
1299 b00b95dd Iustin Pop
    else:
1300 b00b95dd Iustin Pop
      backend = meta = None
1301 b00b95dd Iustin Pop
1302 a2cfdea2 Iustin Pop
    if backend is not None:
1303 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1304 a2cfdea2 Iustin Pop
    else:
1305 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1306 b00b95dd Iustin Pop
1307 a2cfdea2 Iustin Pop
    if meta is not None:
1308 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1309 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1310 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1311 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1312 a2cfdea2 Iustin Pop
    else:
1313 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1314 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1315 a2cfdea2 Iustin Pop
    return retval
1316 a2cfdea2 Iustin Pop
1317 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1318 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1319 a2cfdea2 Iustin Pop

1320 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1321 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1322 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1323 a2cfdea2 Iustin Pop
    device.
1324 a2cfdea2 Iustin Pop

1325 a2cfdea2 Iustin Pop
    """
1326 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1327 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1328 a2cfdea2 Iustin Pop
      return True
1329 a2cfdea2 Iustin Pop
1330 a2cfdea2 Iustin Pop
    if self._lhost is None:
1331 a2cfdea2 Iustin Pop
      return False
1332 a2cfdea2 Iustin Pop
1333 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1334 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1335 a2cfdea2 Iustin Pop
      return False
1336 a2cfdea2 Iustin Pop
1337 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1338 a2cfdea2 Iustin Pop
    retval = (retval and
1339 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1340 a2cfdea2 Iustin Pop
    return retval
1341 a2cfdea2 Iustin Pop
1342 a2cfdea2 Iustin Pop
  @classmethod
1343 f069addf Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta, size):
1344 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1345 a2cfdea2 Iustin Pop

1346 a2cfdea2 Iustin Pop
    """
1347 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1348 f069addf Iustin Pop
            backend, meta, "0",
1349 f069addf Iustin Pop
            "-e", "detach",
1350 f069addf Iustin Pop
            "--create-device"]
1351 60bca04a Iustin Pop
    if size:
1352 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1353 89b70f39 Iustin Pop
    if not constants.DRBD_BARRIERS: # disable barriers, if configured so
1354 fcee765d Manuel Franceschini
      version = cls._GetVersion(cls._GetProcData())
1355 89b70f39 Iustin Pop
      # various DRBD versions support different disk barrier options;
1356 89b70f39 Iustin Pop
      # what we aim here is to revert back to the 'drain' method of
1357 89b70f39 Iustin Pop
      # disk flushes and to disable metadata barriers, in effect going
1358 89b70f39 Iustin Pop
      # back to pre-8.0.7 behaviour
1359 d0c8c01d Iustin Pop
      vmaj = version["k_major"]
1360 d0c8c01d Iustin Pop
      vmin = version["k_minor"]
1361 d0c8c01d Iustin Pop
      vrel = version["k_point"]
1362 89b70f39 Iustin Pop
      assert vmaj == 8
1363 89b70f39 Iustin Pop
      if vmin == 0: # 8.0.x
1364 89b70f39 Iustin Pop
        if vrel >= 12:
1365 d0c8c01d Iustin Pop
          args.extend(["-i", "-m"])
1366 89b70f39 Iustin Pop
      elif vmin == 2: # 8.2.x
1367 89b70f39 Iustin Pop
        if vrel >= 7:
1368 d0c8c01d Iustin Pop
          args.extend(["-i", "-m"])
1369 89b70f39 Iustin Pop
      elif vmaj >= 3: # 8.3.x or newer
1370 d0c8c01d Iustin Pop
        args.extend(["-i", "-a", "m"])
1371 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1372 a2cfdea2 Iustin Pop
    if result.failed:
1373 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1374 a2cfdea2 Iustin Pop
1375 a2cfdea2 Iustin Pop
  @classmethod
1376 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1377 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1378 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1379 a2cfdea2 Iustin Pop

1380 a2cfdea2 Iustin Pop
    """
1381 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1382 52857176 Iustin Pop
    if None in net_info:
1383 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1384 52857176 Iustin Pop
      # sure its shutdown
1385 1063abd1 Iustin Pop
      cls._ShutdownNet(minor)
1386 1063abd1 Iustin Pop
      return
1387 52857176 Iustin Pop
1388 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1389 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1390 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1391 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1392 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1393 7d585316 Iustin Pop
    # about its peer.
1394 7d585316 Iustin Pop
    cls._SetMinorSyncSpeed(minor, constants.SYNC_SPEED)
1395 7d585316 Iustin Pop
1396 8b312c1d Manuel Franceschini
    if netutils.IP6Address.IsValid(lhost):
1397 8b312c1d Manuel Franceschini
      if not netutils.IP6Address.IsValid(rhost):
1398 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1399 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1400 5a672c30 Manuel Franceschini
      family = "ipv6"
1401 8b312c1d Manuel Franceschini
    elif netutils.IP4Address.IsValid(lhost):
1402 8b312c1d Manuel Franceschini
      if not netutils.IP4Address.IsValid(rhost):
1403 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1404 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1405 5a672c30 Manuel Franceschini
      family = "ipv4"
1406 5a672c30 Manuel Franceschini
    else:
1407 5a672c30 Manuel Franceschini
      _ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
1408 5a672c30 Manuel Franceschini
1409 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1410 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, lhost, lport),
1411 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, rhost, rport), protocol,
1412 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1413 f38478b2 Iustin Pop
            "-B", "consensus",
1414 ab6cc81c Iustin Pop
            "--create-device",
1415 f38478b2 Iustin Pop
            ]
1416 a2cfdea2 Iustin Pop
    if dual_pri:
1417 a2cfdea2 Iustin Pop
      args.append("-m")
1418 a2cfdea2 Iustin Pop
    if hmac and secret:
1419 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1420 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1421 a2cfdea2 Iustin Pop
    if result.failed:
1422 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1423 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1424 a2cfdea2 Iustin Pop
1425 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1426 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1427 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1428 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1429 def8e2f6 Michael Hanselmann
1430 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1431 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1432 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1433 def8e2f6 Michael Hanselmann
1434 def8e2f6 Michael Hanselmann
    try:
1435 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1436 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1437 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1438 a2cfdea2 Iustin Pop
1439 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1440 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1441 b00b95dd Iustin Pop

1442 b00b95dd Iustin Pop
    """
1443 b00b95dd Iustin Pop
    if self.minor is None:
1444 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1445 82463074 Iustin Pop
                  self._aminor)
1446 b00b95dd Iustin Pop
    if len(devices) != 2:
1447 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1448 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1449 03ece5f3 Iustin Pop
    if "local_dev" in info:
1450 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1451 b00b95dd Iustin Pop
    backend, meta = devices
1452 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1453 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1454 b00b95dd Iustin Pop
    backend.Open()
1455 b00b95dd Iustin Pop
    meta.Open()
1456 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1457 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1458 b00b95dd Iustin Pop
1459 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1460 b00b95dd Iustin Pop
    self._children = devices
1461 b00b95dd Iustin Pop
1462 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1463 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1464 b00b95dd Iustin Pop

1465 b00b95dd Iustin Pop
    """
1466 b00b95dd Iustin Pop
    if self.minor is None:
1467 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1468 82463074 Iustin Pop
                  self._aminor)
1469 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1470 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1471 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1472 03ece5f3 Iustin Pop
      return
1473 b00b95dd Iustin Pop
    if len(self._children) != 2:
1474 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1475 82463074 Iustin Pop
                  self._children)
1476 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1477 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1478 e739bd57 Iustin Pop
      return
1479 b00b95dd Iustin Pop
    if len(devices) != 2:
1480 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1481 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1482 e739bd57 Iustin Pop
      if dev != child.dev_path:
1483 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1484 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1485 b00b95dd Iustin Pop
1486 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1487 b00b95dd Iustin Pop
    self._children = []
1488 b00b95dd Iustin Pop
1489 7d585316 Iustin Pop
  @classmethod
1490 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1491 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1492 a2cfdea2 Iustin Pop

1493 7d585316 Iustin Pop
    This is the low-level implementation.
1494 7d585316 Iustin Pop

1495 7d585316 Iustin Pop
    @type minor: int
1496 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1497 7d585316 Iustin Pop
    @type kbytes: int
1498 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1499 7d585316 Iustin Pop
    @rtype: boolean
1500 7d585316 Iustin Pop
    @return: the success of the operation
1501 7d585316 Iustin Pop

1502 a2cfdea2 Iustin Pop
    """
1503 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1504 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1505 a2cfdea2 Iustin Pop
    if result.failed:
1506 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1507 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1508 7d585316 Iustin Pop
    return not result.failed
1509 7d585316 Iustin Pop
1510 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1511 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1512 7d585316 Iustin Pop

1513 7d585316 Iustin Pop
    @type kbytes: int
1514 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1515 7d585316 Iustin Pop
    @rtype: boolean
1516 7d585316 Iustin Pop
    @return: the success of the operation
1517 7d585316 Iustin Pop

1518 7d585316 Iustin Pop
    """
1519 7d585316 Iustin Pop
    if self.minor is None:
1520 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1521 7d585316 Iustin Pop
      return False
1522 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1523 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1524 a2cfdea2 Iustin Pop
1525 a3fffcc6 Renรฉ Nussbaumer
  def PauseResumeSync(self, pause):
1526 a3fffcc6 Renรฉ Nussbaumer
    """Pauses or resumes the sync of a DRBD device.
1527 a3fffcc6 Renรฉ Nussbaumer

1528 a3fffcc6 Renรฉ Nussbaumer
    @param pause: Wether to pause or resume
1529 a3fffcc6 Renรฉ Nussbaumer
    @return: the success of the operation
1530 a3fffcc6 Renรฉ Nussbaumer

1531 a3fffcc6 Renรฉ Nussbaumer
    """
1532 a3fffcc6 Renรฉ Nussbaumer
    if self.minor is None:
1533 a3fffcc6 Renรฉ Nussbaumer
      logging.info("Not attached during PauseSync")
1534 a3fffcc6 Renรฉ Nussbaumer
      return False
1535 a3fffcc6 Renรฉ Nussbaumer
1536 a3fffcc6 Renรฉ Nussbaumer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
1537 a3fffcc6 Renรฉ Nussbaumer
1538 a3fffcc6 Renรฉ Nussbaumer
    if pause:
1539 a3fffcc6 Renรฉ Nussbaumer
      cmd = "pause-sync"
1540 a3fffcc6 Renรฉ Nussbaumer
    else:
1541 a3fffcc6 Renรฉ Nussbaumer
      cmd = "resume-sync"
1542 a3fffcc6 Renรฉ Nussbaumer
1543 a3fffcc6 Renรฉ Nussbaumer
    result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
1544 a3fffcc6 Renรฉ Nussbaumer
    if result.failed:
1545 a3fffcc6 Renรฉ Nussbaumer
      logging.error("Can't %s: %s - %s", cmd,
1546 a3fffcc6 Renรฉ Nussbaumer
                    result.fail_reason, result.output)
1547 a3fffcc6 Renรฉ Nussbaumer
    return not result.failed and children_result
1548 a3fffcc6 Renรฉ Nussbaumer
1549 6b90c22e Iustin Pop
  def GetProcStatus(self):
1550 6b90c22e Iustin Pop
    """Return device data from /proc.
1551 6b90c22e Iustin Pop

1552 6b90c22e Iustin Pop
    """
1553 6b90c22e Iustin Pop
    if self.minor is None:
1554 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1555 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1556 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1557 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1558 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1559 6b90c22e Iustin Pop
1560 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1561 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1562 a2cfdea2 Iustin Pop

1563 a2cfdea2 Iustin Pop

1564 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1565 5bbd3f7f Michael Hanselmann
    If estimated_time is None, it means we can't estimate
1566 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1567 0834c866 Iustin Pop

1568 0834c866 Iustin Pop

1569 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1570 0834c866 Iustin Pop
    network not connected or local disk missing.
1571 0834c866 Iustin Pop

1572 5bbd3f7f Michael Hanselmann
    We compute the ldisk parameter based on whether we have a local
1573 0834c866 Iustin Pop
    disk or not.
1574 a2cfdea2 Iustin Pop

1575 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1576 c41eea6e Iustin Pop

1577 a2cfdea2 Iustin Pop
    """
1578 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1579 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1580 96acbc09 Michael Hanselmann
1581 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1582 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1583 f208978a Michael Hanselmann
1584 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1585 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1586 f208978a Michael Hanselmann
    elif stats.is_diskless:
1587 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1588 f208978a Michael Hanselmann
    else:
1589 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1590 96acbc09 Michael Hanselmann
1591 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1592 96acbc09 Michael Hanselmann
                                  major=self.major,
1593 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1594 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1595 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1596 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1597 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1598 a2cfdea2 Iustin Pop
1599 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1600 a2cfdea2 Iustin Pop
    """Make the local state primary.
1601 a2cfdea2 Iustin Pop

1602 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1603 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1604 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1605 f860ff4e Guido Trotter
    is mandatory.
1606 a2cfdea2 Iustin Pop

1607 a2cfdea2 Iustin Pop
    """
1608 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1609 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1610 a2cfdea2 Iustin Pop
      return False
1611 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1612 a2cfdea2 Iustin Pop
    if force:
1613 a2cfdea2 Iustin Pop
      cmd.append("-o")
1614 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1615 a2cfdea2 Iustin Pop
    if result.failed:
1616 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1617 82463074 Iustin Pop
                  result.output)
1618 a2cfdea2 Iustin Pop
1619 a2cfdea2 Iustin Pop
  def Close(self):
1620 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1621 a2cfdea2 Iustin Pop

1622 a2cfdea2 Iustin Pop
    This will, of course, fail if the device is in use.
1623 a2cfdea2 Iustin Pop

1624 a2cfdea2 Iustin Pop
    """
1625 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1626 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1627 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1628 a2cfdea2 Iustin Pop
    if result.failed:
1629 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1630 82463074 Iustin Pop
                  self.minor, result.output)
1631 a2cfdea2 Iustin Pop
1632 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1633 cf8df3f3 Iustin Pop
    """Removes network configuration.
1634 cf8df3f3 Iustin Pop

1635 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1636 cf8df3f3 Iustin Pop

1637 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1638 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1639 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1640 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1641 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1642 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1643 cf8df3f3 Iustin Pop
    exception.
1644 cf8df3f3 Iustin Pop

1645 cf8df3f3 Iustin Pop
    """
1646 cf8df3f3 Iustin Pop
    if self.minor is None:
1647 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1648 cf8df3f3 Iustin Pop
1649 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1650 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1651 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1652 cf8df3f3 Iustin Pop
1653 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1654 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1655 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1656 cf8df3f3 Iustin Pop
1657 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1658 def8e2f6 Michael Hanselmann
1659 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1660 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1661 def8e2f6 Michael Hanselmann
        return
1662 def8e2f6 Michael Hanselmann
1663 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1664 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1665 def8e2f6 Michael Hanselmann
      # forgotten
1666 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1667 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1668 def8e2f6 Michael Hanselmann
1669 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1670 def8e2f6 Michael Hanselmann
1671 def8e2f6 Michael Hanselmann
    # Keep start time
1672 def8e2f6 Michael Hanselmann
    start_time = time.time()
1673 def8e2f6 Michael Hanselmann
1674 def8e2f6 Michael Hanselmann
    try:
1675 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1676 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1677 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1678 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1679 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1680 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1681 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1682 cf8df3f3 Iustin Pop
      else:
1683 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1684 def8e2f6 Michael Hanselmann
1685 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1686 cf8df3f3 Iustin Pop
1687 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1688 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1689 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1690 82463074 Iustin Pop
                   self.minor, reconfig_time)
1691 cf8df3f3 Iustin Pop
1692 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1693 cf8df3f3 Iustin Pop
    """Reconnects the network.
1694 cf8df3f3 Iustin Pop

1695 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1696 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1697 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1698 cf8df3f3 Iustin Pop

1699 cf8df3f3 Iustin Pop
    Args:
1700 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1701 cf8df3f3 Iustin Pop

1702 cf8df3f3 Iustin Pop
    """
1703 cf8df3f3 Iustin Pop
    if self.minor is None:
1704 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1705 cf8df3f3 Iustin Pop
1706 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1707 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1708 cf8df3f3 Iustin Pop
1709 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1710 cf8df3f3 Iustin Pop
1711 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1712 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1713 cf8df3f3 Iustin Pop
1714 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1715 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1716 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1717 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1718 cf8df3f3 Iustin Pop
1719 a2cfdea2 Iustin Pop
  def Attach(self):
1720 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1721 2d0c8319 Iustin Pop

1722 2d0c8319 Iustin Pop
    This doesn't do any device configurations - it only checks if the
1723 2d0c8319 Iustin Pop
    minor is in a state different from Unconfigured.
1724 2d0c8319 Iustin Pop

1725 2d0c8319 Iustin Pop
    Note that this function will not change the state of the system in
1726 2d0c8319 Iustin Pop
    any way (except in case of side-effects caused by reading from
1727 2d0c8319 Iustin Pop
    /proc).
1728 2d0c8319 Iustin Pop

1729 2d0c8319 Iustin Pop
    """
1730 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1731 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1732 2d0c8319 Iustin Pop
      minor = self._aminor
1733 2d0c8319 Iustin Pop
    else:
1734 2d0c8319 Iustin Pop
      minor = None
1735 2d0c8319 Iustin Pop
1736 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1737 2d0c8319 Iustin Pop
    return minor is not None
1738 2d0c8319 Iustin Pop
1739 2d0c8319 Iustin Pop
  def Assemble(self):
1740 2d0c8319 Iustin Pop
    """Assemble the drbd.
1741 2d0c8319 Iustin Pop

1742 2d0c8319 Iustin Pop
    Method:
1743 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1744 2d0c8319 Iustin Pop
        our config
1745 2d0c8319 Iustin Pop
      - if not, we create it from zero
1746 2d0c8319 Iustin Pop

1747 2d0c8319 Iustin Pop
    """
1748 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1749 2d0c8319 Iustin Pop
1750 2d0c8319 Iustin Pop
    self.Attach()
1751 2d0c8319 Iustin Pop
    if self.minor is None:
1752 2d0c8319 Iustin Pop
      # local device completely unconfigured
1753 1063abd1 Iustin Pop
      self._FastAssemble()
1754 2d0c8319 Iustin Pop
    else:
1755 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1756 2d0c8319 Iustin Pop
      # the device
1757 1063abd1 Iustin Pop
      self._SlowAssemble()
1758 2d0c8319 Iustin Pop
1759 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1760 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1761 a2cfdea2 Iustin Pop

1762 a2cfdea2 Iustin Pop
    In case of partially attached (local device matches but no network
1763 a2cfdea2 Iustin Pop
    setup), we perform the network attach. If successful, we re-test
1764 a2cfdea2 Iustin Pop
    the attach if can return success.
1765 a2cfdea2 Iustin Pop

1766 a2cfdea2 Iustin Pop
    """
1767 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
1768 b459a848 Andrea Spadaccini
    # pylint: disable=W0631
1769 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1770 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1771 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1772 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1773 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1774 1063abd1 Iustin Pop
1775 a2cfdea2 Iustin Pop
      if match_l and match_r:
1776 1063abd1 Iustin Pop
        # everything matches
1777 a2cfdea2 Iustin Pop
        break
1778 1063abd1 Iustin Pop
1779 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1780 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1781 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1782 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1783 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1784 1063abd1 Iustin Pop
          break
1785 1063abd1 Iustin Pop
        else:
1786 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1787 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1788 1063abd1 Iustin Pop
1789 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1790 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1791 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1792 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1793 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1794 1063abd1 Iustin Pop
          break
1795 1063abd1 Iustin Pop
        else:
1796 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1797 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1798 bf25af3b Iustin Pop
1799 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1800 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1801 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1802 bf25af3b Iustin Pop
      # configuration
1803 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1804 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1805 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1806 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1807 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1808 9cdbe77f Iustin Pop
        # reconnect to our correct one
1809 1063abd1 Iustin Pop
        try:
1810 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1811 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1812 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1813 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1814 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1815 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1816 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1817 9cdbe77f Iustin Pop
        # None)
1818 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1819 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1820 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1821 9cdbe77f Iustin Pop
          break
1822 1063abd1 Iustin Pop
        else:
1823 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1824 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1825 9cdbe77f Iustin Pop
1826 a2cfdea2 Iustin Pop
    else:
1827 a2cfdea2 Iustin Pop
      minor = None
1828 a2cfdea2 Iustin Pop
1829 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1830 1063abd1 Iustin Pop
    if minor is None:
1831 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1832 1063abd1 Iustin Pop
                  self._aminor)
1833 a2cfdea2 Iustin Pop
1834 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1835 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1836 a2cfdea2 Iustin Pop

1837 2d0c8319 Iustin Pop
    This is run when in Assemble we detect our minor is unused.
1838 a2cfdea2 Iustin Pop

1839 a2cfdea2 Iustin Pop
    """
1840 a1578d63 Iustin Pop
    minor = self._aminor
1841 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1842 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1843 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1844 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1845 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1846 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1847 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1848 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1849 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1850 a2cfdea2 Iustin Pop
1851 a2cfdea2 Iustin Pop
  @classmethod
1852 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1853 b00b95dd Iustin Pop
    """Detach from the local device.
1854 b00b95dd Iustin Pop

1855 b00b95dd Iustin Pop
    I/Os will continue to be served from the remote device. If we
1856 b00b95dd Iustin Pop
    don't have a remote device, this operation will fail.
1857 b00b95dd Iustin Pop

1858 b00b95dd Iustin Pop
    """
1859 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1860 b00b95dd Iustin Pop
    if result.failed:
1861 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1862 b00b95dd Iustin Pop
1863 b00b95dd Iustin Pop
  @classmethod
1864 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1865 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1866 f3e513ad Iustin Pop

1867 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1868 f3e513ad Iustin Pop

1869 f3e513ad Iustin Pop
    """
1870 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1871 a8459f1c Iustin Pop
    if result.failed:
1872 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1873 f3e513ad Iustin Pop
1874 f3e513ad Iustin Pop
  @classmethod
1875 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1876 a2cfdea2 Iustin Pop
    """Deactivate the device.
1877 a2cfdea2 Iustin Pop

1878 a2cfdea2 Iustin Pop
    This will, of course, fail if the device is in use.
1879 a2cfdea2 Iustin Pop

1880 a2cfdea2 Iustin Pop
    """
1881 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1882 a2cfdea2 Iustin Pop
    if result.failed:
1883 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1884 33bc6f01 Iustin Pop
                  minor, result.output)
1885 a2cfdea2 Iustin Pop
1886 a2cfdea2 Iustin Pop
  def Shutdown(self):
1887 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1888 a2cfdea2 Iustin Pop

1889 a2cfdea2 Iustin Pop
    """
1890 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1891 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1892 746f7476 Iustin Pop
      return
1893 746f7476 Iustin Pop
    minor = self.minor
1894 a2cfdea2 Iustin Pop
    self.minor = None
1895 a2cfdea2 Iustin Pop
    self.dev_path = None
1896 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1897 a2cfdea2 Iustin Pop
1898 a2cfdea2 Iustin Pop
  def Remove(self):
1899 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1900 a2cfdea2 Iustin Pop

1901 a2cfdea2 Iustin Pop
    """
1902 0c6c04ec Iustin Pop
    self.Shutdown()
1903 a2cfdea2 Iustin Pop
1904 a2cfdea2 Iustin Pop
  @classmethod
1905 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1906 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1907 a2cfdea2 Iustin Pop

1908 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1909 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1910 a2cfdea2 Iustin Pop

1911 a2cfdea2 Iustin Pop
    """
1912 a2cfdea2 Iustin Pop
    if len(children) != 2:
1913 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1914 767d52d3 Iustin Pop
    # check that the minor is unused
1915 767d52d3 Iustin Pop
    aminor = unique_id[4]
1916 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1917 767d52d3 Iustin Pop
    if aminor in proc_info:
1918 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1919 767d52d3 Iustin Pop
      in_use = status.is_in_use
1920 767d52d3 Iustin Pop
    else:
1921 767d52d3 Iustin Pop
      in_use = False
1922 767d52d3 Iustin Pop
    if in_use:
1923 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1924 a2cfdea2 Iustin Pop
    meta = children[1]
1925 a2cfdea2 Iustin Pop
    meta.Assemble()
1926 a2cfdea2 Iustin Pop
    if not meta.Attach():
1927 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1928 33bc6f01 Iustin Pop
                  aminor, meta)
1929 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1930 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1931 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1932 a2cfdea2 Iustin Pop
1933 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
1934 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1935 1005d816 Iustin Pop

1936 1005d816 Iustin Pop
    """
1937 1005d816 Iustin Pop
    if self.minor is None:
1938 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1939 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1940 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1941 7fe23d47 Iustin Pop
    self._children[0].Grow(amount, dryrun)
1942 7fe23d47 Iustin Pop
    if dryrun:
1943 7fe23d47 Iustin Pop
      # DRBD does not support dry-run mode, so we'll return here
1944 7fe23d47 Iustin Pop
      return
1945 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1946 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1947 1005d816 Iustin Pop
    if result.failed:
1948 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1949 1005d816 Iustin Pop
1950 a8083063 Iustin Pop
1951 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1952 6f695a2e Manuel Franceschini
  """File device.
1953 abdf0113 Iustin Pop

1954 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1955 6f695a2e Manuel Franceschini

1956 6f695a2e Manuel Franceschini
  The unique_id for the file device is a (file_driver, file_path) tuple.
1957 abdf0113 Iustin Pop

1958 6f695a2e Manuel Franceschini
  """
1959 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1960 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1961 6f695a2e Manuel Franceschini

1962 6f695a2e Manuel Franceschini
    """
1963 6f695a2e Manuel Franceschini
    if children:
1964 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1965 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1966 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1967 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1968 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1969 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1970 ecb091e3 Iustin Pop
    self.Attach()
1971 6f695a2e Manuel Franceschini
1972 6f695a2e Manuel Franceschini
  def Assemble(self):
1973 6f695a2e Manuel Franceschini
    """Assemble the device.
1974 6f695a2e Manuel Franceschini

1975 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1976 6f695a2e Manuel Franceschini

1977 6f695a2e Manuel Franceschini
    """
1978 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1979 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1980 6f695a2e Manuel Franceschini
1981 6f695a2e Manuel Franceschini
  def Shutdown(self):
1982 6f695a2e Manuel Franceschini
    """Shutdown the device.
1983 6f695a2e Manuel Franceschini

1984 5bbd3f7f Michael Hanselmann
    This is a no-op for the file type, as we don't deactivate
1985 6f695a2e Manuel Franceschini
    the file on shutdown.
1986 6f695a2e Manuel Franceschini

1987 6f695a2e Manuel Franceschini
    """
1988 746f7476 Iustin Pop
    pass
1989 6f695a2e Manuel Franceschini
1990 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1991 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1992 6f695a2e Manuel Franceschini

1993 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1994 6f695a2e Manuel Franceschini

1995 6f695a2e Manuel Franceschini
    """
1996 6f695a2e Manuel Franceschini
    pass
1997 6f695a2e Manuel Franceschini
1998 6f695a2e Manuel Franceschini
  def Close(self):
1999 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
2000 6f695a2e Manuel Franceschini

2001 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2002 6f695a2e Manuel Franceschini

2003 6f695a2e Manuel Franceschini
    """
2004 6f695a2e Manuel Franceschini
    pass
2005 6f695a2e Manuel Franceschini
2006 6f695a2e Manuel Franceschini
  def Remove(self):
2007 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
2008 6f695a2e Manuel Franceschini

2009 c41eea6e Iustin Pop
    @rtype: boolean
2010 c41eea6e Iustin Pop
    @return: True if the removal was successful
2011 6f695a2e Manuel Franceschini

2012 6f695a2e Manuel Franceschini
    """
2013 6f695a2e Manuel Franceschini
    try:
2014 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
2015 6f695a2e Manuel Franceschini
    except OSError, err:
2016 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
2017 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
2018 6f695a2e Manuel Franceschini
2019 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
2020 bbe4cc16 Iustin Pop
    """Renames the file.
2021 bbe4cc16 Iustin Pop

2022 bbe4cc16 Iustin Pop
    """
2023 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
2024 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
2025 bbe4cc16 Iustin Pop
2026 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
2027 bbe4cc16 Iustin Pop
    """Grow the file
2028 bbe4cc16 Iustin Pop

2029 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2030 bbe4cc16 Iustin Pop

2031 bbe4cc16 Iustin Pop
    """
2032 91e2d9ec Guido Trotter
    # Check that the file exists
2033 91e2d9ec Guido Trotter
    self.Assemble()
2034 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
2035 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
2036 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
2037 7fe23d47 Iustin Pop
    # We can't really simulate the growth
2038 7fe23d47 Iustin Pop
    if dryrun:
2039 7fe23d47 Iustin Pop
      return
2040 91e2d9ec Guido Trotter
    try:
2041 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
2042 91e2d9ec Guido Trotter
      f.truncate(new_size)
2043 91e2d9ec Guido Trotter
      f.close()
2044 91e2d9ec Guido Trotter
    except EnvironmentError, err:
2045 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
2046 bbe4cc16 Iustin Pop
2047 6f695a2e Manuel Franceschini
  def Attach(self):
2048 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2049 6f695a2e Manuel Franceschini

2050 6f695a2e Manuel Franceschini
    Check if this file already exists.
2051 6f695a2e Manuel Franceschini

2052 c41eea6e Iustin Pop
    @rtype: boolean
2053 c41eea6e Iustin Pop
    @return: True if file exists
2054 6f695a2e Manuel Franceschini

2055 6f695a2e Manuel Franceschini
    """
2056 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
2057 ecb091e3 Iustin Pop
    return self.attached
2058 6f695a2e Manuel Franceschini
2059 fcff3897 Iustin Pop
  def GetActualSize(self):
2060 fcff3897 Iustin Pop
    """Return the actual disk size.
2061 fcff3897 Iustin Pop

2062 fcff3897 Iustin Pop
    @note: the device needs to be active when this is called
2063 fcff3897 Iustin Pop

2064 fcff3897 Iustin Pop
    """
2065 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2066 fcff3897 Iustin Pop
    try:
2067 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2068 fcff3897 Iustin Pop
      return st.st_size
2069 fcff3897 Iustin Pop
    except OSError, err:
2070 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2071 fcff3897 Iustin Pop
2072 6f695a2e Manuel Franceschini
  @classmethod
2073 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
2074 6f695a2e Manuel Franceschini
    """Create a new file.
2075 6f695a2e Manuel Franceschini

2076 c41eea6e Iustin Pop
    @param size: the size of file in MiB
2077 6f695a2e Manuel Franceschini

2078 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2079 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2080 6f695a2e Manuel Franceschini

2081 6f695a2e Manuel Franceschini
    """
2082 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2083 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2084 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2085 6f695a2e Manuel Franceschini
    try:
2086 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
2087 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
2088 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2089 6f695a2e Manuel Franceschini
      f.close()
2090 cdeefd9b Guido Trotter
    except EnvironmentError, err:
2091 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
2092 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
2093 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
2094 6f695a2e Manuel Franceschini
2095 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
2096 6f695a2e Manuel Franceschini
2097 6f695a2e Manuel Franceschini
2098 b6135bbc Apollon Oikonomopoulos
class PersistentBlockDevice(BlockDev):
2099 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
2100 b6135bbc Apollon Oikonomopoulos

2101 b6135bbc Apollon Oikonomopoulos
  May be either directly attached, or exposed through DM (e.g. dm-multipath).
2102 b6135bbc Apollon Oikonomopoulos
  udev helpers are probably required to give persistent, human-friendly
2103 b6135bbc Apollon Oikonomopoulos
  names.
2104 b6135bbc Apollon Oikonomopoulos

2105 b6135bbc Apollon Oikonomopoulos
  For the time being, pathnames are required to lie under /dev.
2106 b6135bbc Apollon Oikonomopoulos

2107 b6135bbc Apollon Oikonomopoulos
  """
2108 b6135bbc Apollon Oikonomopoulos
  def __init__(self, unique_id, children, size):
2109 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
2110 b6135bbc Apollon Oikonomopoulos

2111 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2112 b6135bbc Apollon Oikonomopoulos

2113 b6135bbc Apollon Oikonomopoulos
    """
2114 b6135bbc Apollon Oikonomopoulos
    super(PersistentBlockDevice, self).__init__(unique_id, children, size)
2115 b6135bbc Apollon Oikonomopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2116 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2117 b6135bbc Apollon Oikonomopoulos
    self.dev_path = unique_id[1]
2118 d0c8c01d Iustin Pop
    if not os.path.realpath(self.dev_path).startswith("/dev/"):
2119 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Full path '%s' lies outside /dev" %
2120 b6135bbc Apollon Oikonomopoulos
                              os.path.realpath(self.dev_path))
2121 b6135bbc Apollon Oikonomopoulos
    # TODO: this is just a safety guard checking that we only deal with devices
2122 b6135bbc Apollon Oikonomopoulos
    # we know how to handle. In the future this will be integrated with
2123 b6135bbc Apollon Oikonomopoulos
    # external storage backends and possible values will probably be collected
2124 b6135bbc Apollon Oikonomopoulos
    # from the cluster configuration.
2125 b6135bbc Apollon Oikonomopoulos
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
2126 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Got persistent block device of invalid type: %s" %
2127 b6135bbc Apollon Oikonomopoulos
                       unique_id[0])
2128 b6135bbc Apollon Oikonomopoulos
2129 b6135bbc Apollon Oikonomopoulos
    self.major = self.minor = None
2130 b6135bbc Apollon Oikonomopoulos
    self.Attach()
2131 b6135bbc Apollon Oikonomopoulos
2132 b6135bbc Apollon Oikonomopoulos
  @classmethod
2133 b6135bbc Apollon Oikonomopoulos
  def Create(cls, unique_id, children, size):
2134 b6135bbc Apollon Oikonomopoulos
    """Create a new device
2135 b6135bbc Apollon Oikonomopoulos

2136 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2137 b6135bbc Apollon Oikonomopoulos

2138 b6135bbc Apollon Oikonomopoulos
    """
2139 b6135bbc Apollon Oikonomopoulos
    return PersistentBlockDevice(unique_id, children, 0)
2140 b6135bbc Apollon Oikonomopoulos
2141 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
2142 b6135bbc Apollon Oikonomopoulos
    """Remove a device
2143 b6135bbc Apollon Oikonomopoulos

2144 b6135bbc Apollon Oikonomopoulos
    This is a noop
2145 b6135bbc Apollon Oikonomopoulos

2146 b6135bbc Apollon Oikonomopoulos
    """
2147 b6135bbc Apollon Oikonomopoulos
    pass
2148 b6135bbc Apollon Oikonomopoulos
2149 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
2150 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
2151 b6135bbc Apollon Oikonomopoulos

2152 b6135bbc Apollon Oikonomopoulos
    """
2153 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Rename is not supported for PersistentBlockDev storage")
2154 b6135bbc Apollon Oikonomopoulos
2155 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
2156 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
2157 b6135bbc Apollon Oikonomopoulos

2158 b6135bbc Apollon Oikonomopoulos

2159 b6135bbc Apollon Oikonomopoulos
    """
2160 b6135bbc Apollon Oikonomopoulos
    self.attached = False
2161 b6135bbc Apollon Oikonomopoulos
    try:
2162 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
2163 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
2164 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2165 b6135bbc Apollon Oikonomopoulos
      return False
2166 b6135bbc Apollon Oikonomopoulos
2167 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
2168 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
2169 b6135bbc Apollon Oikonomopoulos
      return False
2170 b6135bbc Apollon Oikonomopoulos
2171 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
2172 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
2173 b6135bbc Apollon Oikonomopoulos
    self.attached = True
2174 b6135bbc Apollon Oikonomopoulos
2175 b6135bbc Apollon Oikonomopoulos
    return True
2176 b6135bbc Apollon Oikonomopoulos
2177 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
2178 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
2179 b6135bbc Apollon Oikonomopoulos

2180 b6135bbc Apollon Oikonomopoulos
    """
2181 b6135bbc Apollon Oikonomopoulos
    pass
2182 b6135bbc Apollon Oikonomopoulos
2183 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
2184 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
2185 b6135bbc Apollon Oikonomopoulos

2186 b6135bbc Apollon Oikonomopoulos
    """
2187 b6135bbc Apollon Oikonomopoulos
    pass
2188 b6135bbc Apollon Oikonomopoulos
2189 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
2190 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
2191 b6135bbc Apollon Oikonomopoulos

2192 b6135bbc Apollon Oikonomopoulos
    """
2193 b6135bbc Apollon Oikonomopoulos
    pass
2194 b6135bbc Apollon Oikonomopoulos
2195 b6135bbc Apollon Oikonomopoulos
  def Close(self):
2196 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
2197 b6135bbc Apollon Oikonomopoulos

2198 b6135bbc Apollon Oikonomopoulos
    """
2199 b6135bbc Apollon Oikonomopoulos
    pass
2200 b6135bbc Apollon Oikonomopoulos
2201 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
2202 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
2203 b6135bbc Apollon Oikonomopoulos

2204 b6135bbc Apollon Oikonomopoulos
    """
2205 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2206 b6135bbc Apollon Oikonomopoulos
2207 b6135bbc Apollon Oikonomopoulos
2208 a8083063 Iustin Pop
DEV_MAP = {
2209 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2210 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2211 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
2212 a8083063 Iustin Pop
  }
2213 a8083063 Iustin Pop
2214 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
2215 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
2216 cb7c0198 Iustin Pop
2217 a8083063 Iustin Pop
2218 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
2219 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2220 a8083063 Iustin Pop

2221 a8083063 Iustin Pop
  This will succeed only if the device exists and is assembled, but it
2222 a8083063 Iustin Pop
  does not do any actions in order to activate the device.
2223 a8083063 Iustin Pop

2224 a8083063 Iustin Pop
  """
2225 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2226 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2227 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
2228 cb999543 Iustin Pop
  if not device.attached:
2229 a8083063 Iustin Pop
    return None
2230 ecb091e3 Iustin Pop
  return device
2231 a8083063 Iustin Pop
2232 a8083063 Iustin Pop
2233 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
2234 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2235 a8083063 Iustin Pop

2236 f96e3c4f Iustin Pop
  This will attach to assemble the device, as needed, to bring it
2237 f96e3c4f Iustin Pop
  fully up. It must be safe to run on already-assembled devices.
2238 a8083063 Iustin Pop

2239 a8083063 Iustin Pop
  """
2240 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2241 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2242 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
2243 1063abd1 Iustin Pop
  device.Assemble()
2244 a8083063 Iustin Pop
  return device
2245 a8083063 Iustin Pop
2246 a8083063 Iustin Pop
2247 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2248 a8083063 Iustin Pop
  """Create a device.
2249 a8083063 Iustin Pop

2250 a8083063 Iustin Pop
  """
2251 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2252 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2253 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2254 a8083063 Iustin Pop
  return device