Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 26d3fd2f

History | View | Annotate | Download (66.7 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 fcee765d Manuel Franceschini
# Copyright (C) 2006, 2007, 2010 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 a2cfdea2 Iustin Pop
import pyparsing as pyp
28 6f695a2e Manuel Franceschini
import os
29 468c5f77 Iustin Pop
import logging
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
from ganeti import utils
32 a8083063 Iustin Pop
from ganeti import errors
33 fe96220b Iustin Pop
from ganeti import constants
34 96acbc09 Michael Hanselmann
from ganeti import objects
35 cea881e5 Michael Hanselmann
from ganeti import compat
36 a744b676 Manuel Franceschini
from ganeti import netutils
37 a8083063 Iustin Pop
38 a8083063 Iustin Pop
39 310fbb64 Iustin Pop
# Size of reads in _CanReadDevice
40 310fbb64 Iustin Pop
_DEVICE_READ_SIZE = 128 * 1024
41 310fbb64 Iustin Pop
42 310fbb64 Iustin Pop
43 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
44 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
45 82463074 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

226 a8083063 Iustin Pop
    """
227 a8083063 Iustin Pop
    result = True
228 a8083063 Iustin Pop
    if self._children:
229 a8083063 Iustin Pop
      for child in self._children:
230 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
231 a8083063 Iustin Pop
    return result
232 a8083063 Iustin Pop
233 a8083063 Iustin Pop
  def GetSyncStatus(self):
234 a8083063 Iustin Pop
    """Returns the sync status of the device.
235 a8083063 Iustin Pop

236 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
237 a8083063 Iustin Pop
    status of the mirror.
238 a8083063 Iustin Pop

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

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

244 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
245 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
246 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
247 a8083063 Iustin Pop

248 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
249 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
250 0834c866 Iustin Pop
    return False (not degraded).
251 0834c866 Iustin Pop

252 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
253 c41eea6e Iustin Pop

254 a8083063 Iustin Pop
    """
255 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
256 96acbc09 Michael Hanselmann
                                  major=self.major,
257 96acbc09 Michael Hanselmann
                                  minor=self.minor,
258 96acbc09 Michael Hanselmann
                                  sync_percent=None,
259 96acbc09 Michael Hanselmann
                                  estimated_time=None,
260 96acbc09 Michael Hanselmann
                                  is_degraded=False,
261 f208978a Michael Hanselmann
                                  ldisk_status=constants.LDS_OKAY)
262 a8083063 Iustin Pop
263 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
264 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
265 a8083063 Iustin Pop

266 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
267 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
268 a8083063 Iustin Pop
    children.
269 a8083063 Iustin Pop

270 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
271 96acbc09 Michael Hanselmann

272 a8083063 Iustin Pop
    """
273 96acbc09 Michael Hanselmann
    status = self.GetSyncStatus()
274 96acbc09 Michael Hanselmann
275 96acbc09 Michael Hanselmann
    min_percent = status.sync_percent
276 96acbc09 Michael Hanselmann
    max_time = status.estimated_time
277 96acbc09 Michael Hanselmann
    is_degraded = status.is_degraded
278 f208978a Michael Hanselmann
    ldisk_status = status.ldisk_status
279 96acbc09 Michael Hanselmann
280 a8083063 Iustin Pop
    if self._children:
281 a8083063 Iustin Pop
      for child in self._children:
282 96acbc09 Michael Hanselmann
        child_status = child.GetSyncStatus()
283 96acbc09 Michael Hanselmann
284 a8083063 Iustin Pop
        if min_percent is None:
285 96acbc09 Michael Hanselmann
          min_percent = child_status.sync_percent
286 96acbc09 Michael Hanselmann
        elif child_status.sync_percent is not None:
287 96acbc09 Michael Hanselmann
          min_percent = min(min_percent, child_status.sync_percent)
288 96acbc09 Michael Hanselmann
289 a8083063 Iustin Pop
        if max_time is None:
290 96acbc09 Michael Hanselmann
          max_time = child_status.estimated_time
291 96acbc09 Michael Hanselmann
        elif child_status.estimated_time is not None:
292 96acbc09 Michael Hanselmann
          max_time = max(max_time, child_status.estimated_time)
293 96acbc09 Michael Hanselmann
294 96acbc09 Michael Hanselmann
        is_degraded = is_degraded or child_status.is_degraded
295 f208978a Michael Hanselmann
296 f208978a Michael Hanselmann
        if ldisk_status is None:
297 f208978a Michael Hanselmann
          ldisk_status = child_status.ldisk_status
298 f208978a Michael Hanselmann
        elif child_status.ldisk_status is not None:
299 f208978a Michael Hanselmann
          ldisk_status = max(ldisk_status, child_status.ldisk_status)
300 96acbc09 Michael Hanselmann
301 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
302 96acbc09 Michael Hanselmann
                                  major=self.major,
303 96acbc09 Michael Hanselmann
                                  minor=self.minor,
304 96acbc09 Michael Hanselmann
                                  sync_percent=min_percent,
305 96acbc09 Michael Hanselmann
                                  estimated_time=max_time,
306 96acbc09 Michael Hanselmann
                                  is_degraded=is_degraded,
307 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
308 a8083063 Iustin Pop
309 a8083063 Iustin Pop
310 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
311 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
312 a0c3fea1 Michael Hanselmann

313 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
314 a0c3fea1 Michael Hanselmann

315 a0c3fea1 Michael Hanselmann
    """
316 a0c3fea1 Michael Hanselmann
    for child in self._children:
317 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
318 a0c3fea1 Michael Hanselmann
319 1005d816 Iustin Pop
  def Grow(self, amount):
320 1005d816 Iustin Pop
    """Grow the block device.
321 1005d816 Iustin Pop

322 c41eea6e Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
323 1005d816 Iustin Pop

324 1005d816 Iustin Pop
    """
325 1005d816 Iustin Pop
    raise NotImplementedError
326 a0c3fea1 Michael Hanselmann
327 fcff3897 Iustin Pop
  def GetActualSize(self):
328 fcff3897 Iustin Pop
    """Return the actual disk size.
329 fcff3897 Iustin Pop

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

332 fcff3897 Iustin Pop
    """
333 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
334 fcff3897 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize64", self.dev_path])
335 fcff3897 Iustin Pop
    if result.failed:
336 fcff3897 Iustin Pop
      _ThrowError("blockdev failed (%s): %s",
337 fcff3897 Iustin Pop
                  result.fail_reason, result.output)
338 fcff3897 Iustin Pop
    try:
339 fcff3897 Iustin Pop
      sz = int(result.output.strip())
340 fcff3897 Iustin Pop
    except (ValueError, TypeError), err:
341 fcff3897 Iustin Pop
      _ThrowError("Failed to parse blockdev output: %s", str(err))
342 fcff3897 Iustin Pop
    return sz
343 fcff3897 Iustin Pop
344 a8083063 Iustin Pop
  def __repr__(self):
345 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
346 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
347 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
348 a8083063 Iustin Pop
349 a8083063 Iustin Pop
350 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
351 a8083063 Iustin Pop
  """Logical Volume block device.
352 a8083063 Iustin Pop

353 a8083063 Iustin Pop
  """
354 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
355 6136f8f0 Iustin Pop
  _INVALID_NAMES = frozenset([".", "..", "snapshot", "pvmove"])
356 6136f8f0 Iustin Pop
  _INVALID_SUBSTRINGS = frozenset(["_mlog", "_mimage"])
357 6136f8f0 Iustin Pop
358 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
359 a8083063 Iustin Pop
    """Attaches to a LV device.
360 a8083063 Iustin Pop

361 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
362 a8083063 Iustin Pop

363 a8083063 Iustin Pop
    """
364 464f8daf Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children, size)
365 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
366 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
367 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
368 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
369 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
370 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
371 99e8295c Iustin Pop
    self._degraded = True
372 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
373 a8083063 Iustin Pop
    self.Attach()
374 a8083063 Iustin Pop
375 a8083063 Iustin Pop
  @classmethod
376 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
377 a8083063 Iustin Pop
    """Create a new logical volume.
378 a8083063 Iustin Pop

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

424 197478f2 Renรฉ Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
425 197478f2 Renรฉ Nussbaumer
    @param fields: Fields to return
426 197478f2 Renรฉ Nussbaumer
    @return: A list of dicts each with the parsed fields
427 197478f2 Renรฉ Nussbaumer

428 197478f2 Renรฉ Nussbaumer
    """
429 197478f2 Renรฉ Nussbaumer
    if not fields:
430 197478f2 Renรฉ Nussbaumer
      raise errors.ProgrammerError("No fields specified")
431 197478f2 Renรฉ Nussbaumer
432 197478f2 Renรฉ Nussbaumer
    sep = "|"
433 197478f2 Renรฉ Nussbaumer
    cmd = [lvm_cmd, "--noheadings", "--nosuffix", "--units=m", "--unbuffered",
434 197478f2 Renรฉ Nussbaumer
           "--separator=%s" % sep, "-o%s" % ",".join(fields)]
435 197478f2 Renรฉ Nussbaumer
436 197478f2 Renรฉ Nussbaumer
    result = utils.RunCmd(cmd)
437 197478f2 Renรฉ Nussbaumer
    if result.failed:
438 197478f2 Renรฉ Nussbaumer
      raise errors.CommandError("Can't get the volume information: %s - %s" %
439 197478f2 Renรฉ Nussbaumer
                                (result.fail_reason, result.output))
440 197478f2 Renรฉ Nussbaumer
441 197478f2 Renรฉ Nussbaumer
    data = []
442 197478f2 Renรฉ Nussbaumer
    for line in result.stdout.splitlines():
443 197478f2 Renรฉ Nussbaumer
      splitted_fields = line.strip().split(sep)
444 197478f2 Renรฉ Nussbaumer
445 197478f2 Renรฉ Nussbaumer
      if len(fields) != len(splitted_fields):
446 197478f2 Renรฉ Nussbaumer
        raise errors.CommandError("Can't parse %s output: line '%s'" %
447 197478f2 Renรฉ Nussbaumer
                                  (lvm_cmd, line))
448 197478f2 Renรฉ Nussbaumer
449 197478f2 Renรฉ Nussbaumer
      data.append(splitted_fields)
450 197478f2 Renรฉ Nussbaumer
451 197478f2 Renรฉ Nussbaumer
    return data
452 197478f2 Renรฉ Nussbaumer
453 197478f2 Renรฉ Nussbaumer
  @classmethod
454 197478f2 Renรฉ Nussbaumer
  def GetPVInfo(cls, vg_names, filter_allocatable=True):
455 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
456 a8083063 Iustin Pop

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

460 c41eea6e Iustin Pop
    @rtype: list
461 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
462 098c0958 Michael Hanselmann

463 a8083063 Iustin Pop
    """
464 197478f2 Renรฉ Nussbaumer
    try:
465 197478f2 Renรฉ Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
466 197478f2 Renรฉ Nussbaumer
                                        "pv_attr"])
467 197478f2 Renรฉ Nussbaumer
    except errors.GenericError, err:
468 197478f2 Renรฉ Nussbaumer
      logging.error("Can't get PV information: %s", err)
469 a8083063 Iustin Pop
      return None
470 197478f2 Renรฉ Nussbaumer
471 a8083063 Iustin Pop
    data = []
472 197478f2 Renรฉ Nussbaumer
    for pv_name, vg_name, pv_free, pv_attr in info:
473 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
474 197478f2 Renรฉ Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
475 a8083063 Iustin Pop
        continue
476 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
477 197478f2 Renรฉ Nussbaumer
      if vg_names and vg_name not in vg_names:
478 2070598f Iustin Pop
        continue
479 197478f2 Renรฉ Nussbaumer
      data.append((float(pv_free), pv_name, vg_name))
480 197478f2 Renรฉ Nussbaumer
481 197478f2 Renรฉ Nussbaumer
    return data
482 197478f2 Renรฉ Nussbaumer
483 197478f2 Renรฉ Nussbaumer
  @classmethod
484 197478f2 Renรฉ Nussbaumer
  def GetVGInfo(cls, vg_names, filter_readonly=True):
485 197478f2 Renรฉ Nussbaumer
    """Get the free space info for specific VGs.
486 197478f2 Renรฉ Nussbaumer

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

490 197478f2 Renรฉ Nussbaumer
    @rtype: list
491 673cd9c4 Renรฉ Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
492 673cd9c4 Renรฉ Nussbaumer
             MiB
493 197478f2 Renรฉ Nussbaumer

494 197478f2 Renรฉ Nussbaumer
    """
495 197478f2 Renรฉ Nussbaumer
    try:
496 673cd9c4 Renรฉ Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
497 673cd9c4 Renรฉ Nussbaumer
                                        "vg_size"])
498 197478f2 Renรฉ Nussbaumer
    except errors.GenericError, err:
499 197478f2 Renรฉ Nussbaumer
      logging.error("Can't get VG information: %s", err)
500 197478f2 Renรฉ Nussbaumer
      return None
501 197478f2 Renรฉ Nussbaumer
502 197478f2 Renรฉ Nussbaumer
    data = []
503 673cd9c4 Renรฉ Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
504 197478f2 Renรฉ Nussbaumer
      # (possibly) skip over vgs which are not writable
505 197478f2 Renรฉ Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
506 197478f2 Renรฉ Nussbaumer
        continue
507 197478f2 Renรฉ Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
508 197478f2 Renรฉ Nussbaumer
      if vg_names and vg_name not in vg_names:
509 197478f2 Renรฉ Nussbaumer
        continue
510 673cd9c4 Renรฉ Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
511 a8083063 Iustin Pop
512 a8083063 Iustin Pop
    return data
513 a8083063 Iustin Pop
514 6136f8f0 Iustin Pop
  @classmethod
515 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
516 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
517 6136f8f0 Iustin Pop

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

522 6136f8f0 Iustin Pop
    """
523 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
524 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
525 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
526 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
527 6136f8f0 Iustin Pop
528 a8083063 Iustin Pop
  def Remove(self):
529 a8083063 Iustin Pop
    """Remove this logical volume.
530 a8083063 Iustin Pop

531 a8083063 Iustin Pop
    """
532 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
533 a8083063 Iustin Pop
      # the LV does not exist
534 0c6c04ec Iustin Pop
      return
535 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
536 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
537 a8083063 Iustin Pop
    if result.failed:
538 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
539 a8083063 Iustin Pop
540 f3e513ad Iustin Pop
  def Rename(self, new_id):
541 f3e513ad Iustin Pop
    """Rename this logical volume.
542 f3e513ad Iustin Pop

543 f3e513ad Iustin Pop
    """
544 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
545 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
546 f3e513ad Iustin Pop
    new_vg, new_name = new_id
547 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
548 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
549 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
550 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
551 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
552 f3e513ad Iustin Pop
    if result.failed:
553 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
554 be345db0 Iustin Pop
    self._lv_name = new_name
555 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
556 be345db0 Iustin Pop
557 a8083063 Iustin Pop
  def Attach(self):
558 a8083063 Iustin Pop
    """Attach to an existing LV.
559 a8083063 Iustin Pop

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

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

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

629 a8083063 Iustin Pop
    """
630 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
631 5574047a Iustin Pop
    if result.failed:
632 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
633 a8083063 Iustin Pop
634 a8083063 Iustin Pop
  def Shutdown(self):
635 a8083063 Iustin Pop
    """Shutdown the device.
636 a8083063 Iustin Pop

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

640 a8083063 Iustin Pop
    """
641 746f7476 Iustin Pop
    pass
642 a8083063 Iustin Pop
643 9db6dbce Iustin Pop
  def GetSyncStatus(self):
644 9db6dbce Iustin Pop
    """Returns the sync status of the device.
645 9db6dbce Iustin Pop

646 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
647 9db6dbce Iustin Pop
    status of the mirror.
648 9db6dbce Iustin Pop

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

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

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

662 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
663 c41eea6e Iustin Pop

664 9db6dbce Iustin Pop
    """
665 f208978a Michael Hanselmann
    if self._degraded:
666 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
667 f208978a Michael Hanselmann
    else:
668 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
669 f208978a Michael Hanselmann
670 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
671 96acbc09 Michael Hanselmann
                                  major=self.major,
672 96acbc09 Michael Hanselmann
                                  minor=self.minor,
673 96acbc09 Michael Hanselmann
                                  sync_percent=None,
674 96acbc09 Michael Hanselmann
                                  estimated_time=None,
675 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
676 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
677 9db6dbce Iustin Pop
678 a8083063 Iustin Pop
  def Open(self, force=False):
679 a8083063 Iustin Pop
    """Make the device ready for I/O.
680 a8083063 Iustin Pop

681 a8083063 Iustin Pop
    This is a no-op for the LV device type.
682 a8083063 Iustin Pop

683 a8083063 Iustin Pop
    """
684 fdbd668d Iustin Pop
    pass
685 a8083063 Iustin Pop
686 a8083063 Iustin Pop
  def Close(self):
687 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
688 a8083063 Iustin Pop

689 a8083063 Iustin Pop
    This is a no-op for the LV device type.
690 a8083063 Iustin Pop

691 a8083063 Iustin Pop
    """
692 fdbd668d Iustin Pop
    pass
693 a8083063 Iustin Pop
694 a8083063 Iustin Pop
  def Snapshot(self, size):
695 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
696 a8083063 Iustin Pop

697 a8083063 Iustin Pop
    """
698 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
699 a8083063 Iustin Pop
700 a8083063 Iustin Pop
    # remove existing snapshot if found
701 464f8daf Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None, size)
702 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
703 a8083063 Iustin Pop
704 197478f2 Renรฉ Nussbaumer
    vg_info = self.GetVGInfo([self._vg_name])
705 197478f2 Renรฉ Nussbaumer
    if not vg_info:
706 197478f2 Renรฉ Nussbaumer
      _ThrowError("Can't compute VG info for vg %s", self._vg_name)
707 673cd9c4 Renรฉ Nussbaumer
    free_size, _, _ = vg_info[0]
708 a8083063 Iustin Pop
    if free_size < size:
709 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
710 82463074 Iustin Pop
                  " available %s", size, free_size)
711 a8083063 Iustin Pop
712 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
713 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
714 a8083063 Iustin Pop
    if result.failed:
715 82463074 Iustin Pop
      _ThrowError("command: %s error: %s - %s",
716 82463074 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
717 a8083063 Iustin Pop
718 a8083063 Iustin Pop
    return snap_name
719 a8083063 Iustin Pop
720 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
721 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
722 a0c3fea1 Michael Hanselmann

723 a0c3fea1 Michael Hanselmann
    """
724 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
725 a0c3fea1 Michael Hanselmann
726 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
727 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
728 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
729 a0c3fea1 Michael Hanselmann
730 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
731 a0c3fea1 Michael Hanselmann
    text = text[:128]
732 a0c3fea1 Michael Hanselmann
733 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
734 a0c3fea1 Michael Hanselmann
                           self.dev_path])
735 a0c3fea1 Michael Hanselmann
    if result.failed:
736 82463074 Iustin Pop
      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
737 82463074 Iustin Pop
                  result.output)
738 82463074 Iustin Pop
739 1005d816 Iustin Pop
  def Grow(self, amount):
740 1005d816 Iustin Pop
    """Grow the logical volume.
741 1005d816 Iustin Pop

742 1005d816 Iustin Pop
    """
743 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
744 38256320 Iustin Pop
      if not self.Attach():
745 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
746 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
747 38256320 Iustin Pop
    rest = amount % full_stripe_size
748 38256320 Iustin Pop
    if rest != 0:
749 38256320 Iustin Pop
      amount += full_stripe_size - rest
750 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
751 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
752 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
753 1005d816 Iustin Pop
    # supports 'cling'
754 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
755 1005d816 Iustin Pop
      result = utils.RunCmd(["lvextend", "--alloc", alloc_policy,
756 1005d816 Iustin Pop
                             "-L", "+%dm" % amount, self.dev_path])
757 1005d816 Iustin Pop
      if not result.failed:
758 1005d816 Iustin Pop
        return
759 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
760 a0c3fea1 Michael Hanselmann
761 a0c3fea1 Michael Hanselmann
762 6b90c22e Iustin Pop
class DRBD8Status(object):
763 6b90c22e Iustin Pop
  """A DRBD status representation class.
764 6b90c22e Iustin Pop

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

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

870 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
871 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
872 0f7f32d9 Iustin Pop

873 abdf0113 Iustin Pop
  """
874 fcee765d Manuel Franceschini
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)(?:\.\d+)?"
875 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
876 9122e60a Iustin Pop
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
877 9122e60a Iustin Pop
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
878 a8083063 Iustin Pop
879 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
880 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
881 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
882 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
883 a8083063 Iustin Pop
884 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
885 549071a0 Luca Bigliardi
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
886 6b90c22e Iustin Pop
887 abdf0113 Iustin Pop
  @staticmethod
888 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
889 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
890 a8083063 Iustin Pop

891 a8083063 Iustin Pop
    """
892 abdf0113 Iustin Pop
    try:
893 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
894 f6eaed12 Iustin Pop
    except EnvironmentError, err:
895 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
896 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
897 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
898 f6eaed12 Iustin Pop
      else:
899 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
900 abdf0113 Iustin Pop
    if not data:
901 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
902 abdf0113 Iustin Pop
    return data
903 a8083063 Iustin Pop
904 9122e60a Iustin Pop
  @classmethod
905 9122e60a Iustin Pop
  def _MassageProcData(cls, data):
906 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
907 a8083063 Iustin Pop

908 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
909 c41eea6e Iustin Pop
        for that minor
910 a8083063 Iustin Pop

911 a8083063 Iustin Pop
    """
912 abdf0113 Iustin Pop
    results = {}
913 abdf0113 Iustin Pop
    old_minor = old_line = None
914 abdf0113 Iustin Pop
    for line in data:
915 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
916 67d101d4 Iustin Pop
        continue
917 9122e60a Iustin Pop
      lresult = cls._VALID_LINE_RE.match(line)
918 abdf0113 Iustin Pop
      if lresult is not None:
919 abdf0113 Iustin Pop
        if old_minor is not None:
920 abdf0113 Iustin Pop
          results[old_minor] = old_line
921 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
922 abdf0113 Iustin Pop
        old_line = line
923 abdf0113 Iustin Pop
      else:
924 abdf0113 Iustin Pop
        if old_minor is not None:
925 abdf0113 Iustin Pop
          old_line += " " + line.strip()
926 abdf0113 Iustin Pop
    # add last line
927 abdf0113 Iustin Pop
    if old_minor is not None:
928 abdf0113 Iustin Pop
      results[old_minor] = old_line
929 abdf0113 Iustin Pop
    return results
930 a8083063 Iustin Pop
931 abdf0113 Iustin Pop
  @classmethod
932 fcee765d Manuel Franceschini
  def _GetVersion(cls, proc_data):
933 abdf0113 Iustin Pop
    """Return the DRBD version.
934 a8083063 Iustin Pop

935 abdf0113 Iustin Pop
    This will return a dict with keys:
936 c41eea6e Iustin Pop
      - k_major
937 c41eea6e Iustin Pop
      - k_minor
938 c41eea6e Iustin Pop
      - k_point
939 c41eea6e Iustin Pop
      - api
940 c41eea6e Iustin Pop
      - proto
941 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
942 a8083063 Iustin Pop

943 a8083063 Iustin Pop
    """
944 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
945 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
946 abdf0113 Iustin Pop
    if not version:
947 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
948 abdf0113 Iustin Pop
                                    first_line)
949 a8083063 Iustin Pop
950 abdf0113 Iustin Pop
    values = version.groups()
951 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
952 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
953 abdf0113 Iustin Pop
              'k_point': int(values[2]),
954 abdf0113 Iustin Pop
              'api': int(values[3]),
955 abdf0113 Iustin Pop
              'proto': int(values[4]),
956 abdf0113 Iustin Pop
             }
957 abdf0113 Iustin Pop
    if values[5] is not None:
958 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
959 a8083063 Iustin Pop
960 abdf0113 Iustin Pop
    return retval
961 abdf0113 Iustin Pop
962 abdf0113 Iustin Pop
  @staticmethod
963 549071a0 Luca Bigliardi
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
964 549071a0 Luca Bigliardi
    """Returns DRBD usermode_helper currently set.
965 549071a0 Luca Bigliardi

966 549071a0 Luca Bigliardi
    """
967 549071a0 Luca Bigliardi
    try:
968 549071a0 Luca Bigliardi
      helper = utils.ReadFile(filename).splitlines()[0]
969 549071a0 Luca Bigliardi
    except EnvironmentError, err:
970 549071a0 Luca Bigliardi
      if err.errno == errno.ENOENT:
971 549071a0 Luca Bigliardi
        _ThrowError("The file %s cannot be opened, check if the module"
972 549071a0 Luca Bigliardi
                    " is loaded (%s)", filename, str(err))
973 549071a0 Luca Bigliardi
      else:
974 549071a0 Luca Bigliardi
        _ThrowError("Can't read DRBD helper file %s: %s", filename, str(err))
975 549071a0 Luca Bigliardi
    if not helper:
976 549071a0 Luca Bigliardi
      _ThrowError("Can't read any data from %s", filename)
977 549071a0 Luca Bigliardi
    return helper
978 549071a0 Luca Bigliardi
979 549071a0 Luca Bigliardi
  @staticmethod
980 abdf0113 Iustin Pop
  def _DevPath(minor):
981 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
982 a8083063 Iustin Pop

983 a8083063 Iustin Pop
    """
984 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
985 a8083063 Iustin Pop
986 abdf0113 Iustin Pop
  @classmethod
987 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
988 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
989 a8083063 Iustin Pop

990 a8083063 Iustin Pop
    """
991 abdf0113 Iustin Pop
    data = cls._GetProcData()
992 a8083063 Iustin Pop
993 abdf0113 Iustin Pop
    used_devs = {}
994 abdf0113 Iustin Pop
    for line in data:
995 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
996 abdf0113 Iustin Pop
      if not match:
997 abdf0113 Iustin Pop
        continue
998 abdf0113 Iustin Pop
      minor = int(match.group(1))
999 abdf0113 Iustin Pop
      state = match.group(2)
1000 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1001 abdf0113 Iustin Pop
        continue
1002 abdf0113 Iustin Pop
      used_devs[minor] = state, line
1003 a8083063 Iustin Pop
1004 abdf0113 Iustin Pop
    return used_devs
1005 a8083063 Iustin Pop
1006 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
1007 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
1008 0834c866 Iustin Pop

1009 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1010 a8083063 Iustin Pop

1011 a8083063 Iustin Pop
    """
1012 abdf0113 Iustin Pop
    if minor is None:
1013 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
1014 cb999543 Iustin Pop
      self.attached = False
1015 a8083063 Iustin Pop
    else:
1016 abdf0113 Iustin Pop
      self.minor = minor
1017 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
1018 cb999543 Iustin Pop
      self.attached = True
1019 a8083063 Iustin Pop
1020 a8083063 Iustin Pop
  @staticmethod
1021 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
1022 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
1023 a8083063 Iustin Pop

1024 abdf0113 Iustin Pop
    This currently only check the size, which must be around
1025 abdf0113 Iustin Pop
    128MiB.
1026 a8083063 Iustin Pop

1027 a8083063 Iustin Pop
    """
1028 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1029 abdf0113 Iustin Pop
    if result.failed:
1030 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
1031 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
1032 a8083063 Iustin Pop
    try:
1033 abdf0113 Iustin Pop
      sectors = int(result.stdout)
1034 691744c4 Iustin Pop
    except (TypeError, ValueError):
1035 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
1036 c04bc777 Iustin Pop
    num_bytes = sectors * 512
1037 c04bc777 Iustin Pop
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
1038 c04bc777 Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (num_bytes / 1024 / 1024))
1039 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
1040 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
1041 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
1042 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
1043 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
1044 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
1045 c04bc777 Iustin Pop
    if num_bytes > 1024 * 1024 * 1024:
1046 c04bc777 Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (num_bytes / 1024 / 1024))
1047 a8083063 Iustin Pop
1048 abdf0113 Iustin Pop
  def Rename(self, new_id):
1049 abdf0113 Iustin Pop
    """Rename a device.
1050 a8083063 Iustin Pop

1051 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1052 a8083063 Iustin Pop

1053 a8083063 Iustin Pop
    """
1054 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1055 a8083063 Iustin Pop
1056 f3e513ad Iustin Pop
1057 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1058 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1059 a2cfdea2 Iustin Pop

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

1064 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1065 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1066 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1067 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1068 a2cfdea2 Iustin Pop

1069 a2cfdea2 Iustin Pop
  """
1070 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1071 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1072 a2cfdea2 Iustin Pop
1073 cf8df3f3 Iustin Pop
  # timeout constants
1074 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
1075 cf8df3f3 Iustin Pop
1076 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1077 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1078 fc1dc9d7 Iustin Pop
      children = []
1079 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
1080 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1081 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
1082 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1083 310fbb64 Iustin Pop
    (self._lhost, self._lport,
1084 310fbb64 Iustin Pop
     self._rhost, self._rport,
1085 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
1086 310fbb64 Iustin Pop
    if children:
1087 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
1088 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
1089 310fbb64 Iustin Pop
        children = []
1090 464f8daf Iustin Pop
    super(DRBD8, self).__init__(unique_id, children, size)
1091 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1092 fcee765d Manuel Franceschini
    version = self._GetVersion(self._GetProcData())
1093 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
1094 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
1095 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
1096 82463074 Iustin Pop
                  version['k_major'], version['k_minor'])
1097 a2cfdea2 Iustin Pop
1098 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1099 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1100 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1101 ffa1c0dc Iustin Pop
                       (unique_id,))
1102 a2cfdea2 Iustin Pop
    self.Attach()
1103 a2cfdea2 Iustin Pop
1104 a2cfdea2 Iustin Pop
  @classmethod
1105 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1106 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1107 a2cfdea2 Iustin Pop

1108 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1109 a2cfdea2 Iustin Pop

1110 a2cfdea2 Iustin Pop
    """
1111 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1112 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1113 a2cfdea2 Iustin Pop
    if result.failed:
1114 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1115 a2cfdea2 Iustin Pop
1116 a2cfdea2 Iustin Pop
  @classmethod
1117 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1118 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1119 a2cfdea2 Iustin Pop

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

1123 a2cfdea2 Iustin Pop
    """
1124 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1125 a2cfdea2 Iustin Pop
1126 a2cfdea2 Iustin Pop
    highest = None
1127 a2cfdea2 Iustin Pop
    for line in data:
1128 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1129 a2cfdea2 Iustin Pop
      if match:
1130 a2cfdea2 Iustin Pop
        return int(match.group(1))
1131 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1132 a2cfdea2 Iustin Pop
      if match:
1133 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1134 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1135 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1136 a2cfdea2 Iustin Pop
      return 0
1137 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1138 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1139 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1140 a2cfdea2 Iustin Pop
    return highest + 1
1141 a2cfdea2 Iustin Pop
1142 a2cfdea2 Iustin Pop
  @classmethod
1143 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1144 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1145 a2cfdea2 Iustin Pop

1146 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1147 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1148 a2cfdea2 Iustin Pop

1149 a2cfdea2 Iustin Pop
    """
1150 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1151 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1152 a2cfdea2 Iustin Pop
1153 a2cfdea2 Iustin Pop
    # pyparsing setup
1154 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1155 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1156 5a672c30 Manuel Franceschini
    lbracket = pyp.Literal("[").suppress()
1157 5a672c30 Manuel Franceschini
    rbracket = pyp.Literal("]").suppress()
1158 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1159 5a672c30 Manuel Franceschini
    colon = pyp.Literal(":").suppress()
1160 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1161 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1162 a2cfdea2 Iustin Pop
1163 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1164 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1165 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1166 a2cfdea2 Iustin Pop
1167 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1168 a2cfdea2 Iustin Pop
1169 a2cfdea2 Iustin Pop
    # value types
1170 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1171 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1172 5a672c30 Manuel Franceschini
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1173 5a672c30 Manuel Franceschini
                 pyp.Word(pyp.nums + ".") + colon + number)
1174 5a672c30 Manuel Franceschini
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
1175 5a672c30 Manuel Franceschini
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
1176 5a672c30 Manuel Franceschini
                 pyp.Optional(rbracket) + colon + number)
1177 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1178 5a672c30 Manuel Franceschini
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
1179 01e2ce3a Iustin Pop
    # device name, extended syntax
1180 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1181 a2cfdea2 Iustin Pop
1182 a2cfdea2 Iustin Pop
    # a statement
1183 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1184 5a672c30 Manuel Franceschini
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
1185 01e2ce3a Iustin Pop
                         device_value) +
1186 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1187 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1188 a2cfdea2 Iustin Pop
1189 a2cfdea2 Iustin Pop
    # an entire section
1190 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1191 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1192 a2cfdea2 Iustin Pop
1193 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1194 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1195 a2cfdea2 Iustin Pop
1196 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1197 a2cfdea2 Iustin Pop
1198 a2cfdea2 Iustin Pop
    return bnf
1199 a2cfdea2 Iustin Pop
1200 a2cfdea2 Iustin Pop
  @classmethod
1201 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1202 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1203 a2cfdea2 Iustin Pop

1204 a2cfdea2 Iustin Pop
    """
1205 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1206 a2cfdea2 Iustin Pop
    if result.failed:
1207 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1208 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1209 3840729d Iustin Pop
      return None
1210 3840729d Iustin Pop
    return result.stdout
1211 3840729d Iustin Pop
1212 3840729d Iustin Pop
  @classmethod
1213 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1214 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1215 3840729d Iustin Pop

1216 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1217 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1218 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1219 3840729d Iustin Pop
    by _GetShowData.
1220 3840729d Iustin Pop

1221 3840729d Iustin Pop
    """
1222 3840729d Iustin Pop
    data = {}
1223 a2cfdea2 Iustin Pop
    if not out:
1224 a2cfdea2 Iustin Pop
      return data
1225 a2cfdea2 Iustin Pop
1226 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1227 a2cfdea2 Iustin Pop
    # run pyparse
1228 a2cfdea2 Iustin Pop
1229 a2cfdea2 Iustin Pop
    try:
1230 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1231 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1232 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1233 a2cfdea2 Iustin Pop
1234 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1235 a2cfdea2 Iustin Pop
    for section in results:
1236 a2cfdea2 Iustin Pop
      sname = section[0]
1237 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1238 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1239 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1240 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1241 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1242 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1243 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1244 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1245 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1246 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1247 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1248 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1249 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1250 a2cfdea2 Iustin Pop
    return data
1251 a2cfdea2 Iustin Pop
1252 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1253 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1254 a2cfdea2 Iustin Pop

1255 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1256 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1257 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1258 a2cfdea2 Iustin Pop
    device.
1259 a2cfdea2 Iustin Pop

1260 a2cfdea2 Iustin Pop
    """
1261 b00b95dd Iustin Pop
    if self._children:
1262 b00b95dd Iustin Pop
      backend, meta = self._children
1263 b00b95dd Iustin Pop
    else:
1264 b00b95dd Iustin Pop
      backend = meta = None
1265 b00b95dd Iustin Pop
1266 a2cfdea2 Iustin Pop
    if backend is not None:
1267 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1268 a2cfdea2 Iustin Pop
    else:
1269 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1270 b00b95dd Iustin Pop
1271 a2cfdea2 Iustin Pop
    if meta is not None:
1272 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1273 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1274 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1275 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1276 a2cfdea2 Iustin Pop
    else:
1277 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1278 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1279 a2cfdea2 Iustin Pop
    return retval
1280 a2cfdea2 Iustin Pop
1281 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1282 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1283 a2cfdea2 Iustin Pop

1284 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1285 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1286 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1287 a2cfdea2 Iustin Pop
    device.
1288 a2cfdea2 Iustin Pop

1289 a2cfdea2 Iustin Pop
    """
1290 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1291 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1292 a2cfdea2 Iustin Pop
      return True
1293 a2cfdea2 Iustin Pop
1294 a2cfdea2 Iustin Pop
    if self._lhost is None:
1295 a2cfdea2 Iustin Pop
      return False
1296 a2cfdea2 Iustin Pop
1297 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1298 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1299 a2cfdea2 Iustin Pop
      return False
1300 a2cfdea2 Iustin Pop
1301 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1302 a2cfdea2 Iustin Pop
    retval = (retval and
1303 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1304 a2cfdea2 Iustin Pop
    return retval
1305 a2cfdea2 Iustin Pop
1306 a2cfdea2 Iustin Pop
  @classmethod
1307 f069addf Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta, size):
1308 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1309 a2cfdea2 Iustin Pop

1310 a2cfdea2 Iustin Pop
    """
1311 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1312 f069addf Iustin Pop
            backend, meta, "0",
1313 f069addf Iustin Pop
            "-e", "detach",
1314 f069addf Iustin Pop
            "--create-device"]
1315 60bca04a Iustin Pop
    if size:
1316 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1317 89b70f39 Iustin Pop
    if not constants.DRBD_BARRIERS: # disable barriers, if configured so
1318 fcee765d Manuel Franceschini
      version = cls._GetVersion(cls._GetProcData())
1319 89b70f39 Iustin Pop
      # various DRBD versions support different disk barrier options;
1320 89b70f39 Iustin Pop
      # what we aim here is to revert back to the 'drain' method of
1321 89b70f39 Iustin Pop
      # disk flushes and to disable metadata barriers, in effect going
1322 89b70f39 Iustin Pop
      # back to pre-8.0.7 behaviour
1323 89b70f39 Iustin Pop
      vmaj = version['k_major']
1324 89b70f39 Iustin Pop
      vmin = version['k_minor']
1325 89b70f39 Iustin Pop
      vrel = version['k_point']
1326 89b70f39 Iustin Pop
      assert vmaj == 8
1327 89b70f39 Iustin Pop
      if vmin == 0: # 8.0.x
1328 89b70f39 Iustin Pop
        if vrel >= 12:
1329 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1330 89b70f39 Iustin Pop
      elif vmin == 2: # 8.2.x
1331 89b70f39 Iustin Pop
        if vrel >= 7:
1332 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1333 89b70f39 Iustin Pop
      elif vmaj >= 3: # 8.3.x or newer
1334 89b70f39 Iustin Pop
        args.extend(['-i', '-a', 'm'])
1335 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1336 a2cfdea2 Iustin Pop
    if result.failed:
1337 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1338 a2cfdea2 Iustin Pop
1339 a2cfdea2 Iustin Pop
  @classmethod
1340 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1341 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1342 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1343 a2cfdea2 Iustin Pop

1344 a2cfdea2 Iustin Pop
    """
1345 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1346 52857176 Iustin Pop
    if None in net_info:
1347 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1348 52857176 Iustin Pop
      # sure its shutdown
1349 1063abd1 Iustin Pop
      cls._ShutdownNet(minor)
1350 1063abd1 Iustin Pop
      return
1351 52857176 Iustin Pop
1352 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1353 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1354 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1355 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1356 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1357 7d585316 Iustin Pop
    # about its peer.
1358 7d585316 Iustin Pop
    cls._SetMinorSyncSpeed(minor, constants.SYNC_SPEED)
1359 7d585316 Iustin Pop
1360 8b312c1d Manuel Franceschini
    if netutils.IP6Address.IsValid(lhost):
1361 8b312c1d Manuel Franceschini
      if not netutils.IP6Address.IsValid(rhost):
1362 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1363 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1364 5a672c30 Manuel Franceschini
      family = "ipv6"
1365 8b312c1d Manuel Franceschini
    elif netutils.IP4Address.IsValid(lhost):
1366 8b312c1d Manuel Franceschini
      if not netutils.IP4Address.IsValid(rhost):
1367 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1368 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1369 5a672c30 Manuel Franceschini
      family = "ipv4"
1370 5a672c30 Manuel Franceschini
    else:
1371 5a672c30 Manuel Franceschini
      _ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
1372 5a672c30 Manuel Franceschini
1373 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1374 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, lhost, lport),
1375 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, rhost, rport), protocol,
1376 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1377 f38478b2 Iustin Pop
            "-B", "consensus",
1378 ab6cc81c Iustin Pop
            "--create-device",
1379 f38478b2 Iustin Pop
            ]
1380 a2cfdea2 Iustin Pop
    if dual_pri:
1381 a2cfdea2 Iustin Pop
      args.append("-m")
1382 a2cfdea2 Iustin Pop
    if hmac and secret:
1383 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1384 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1385 a2cfdea2 Iustin Pop
    if result.failed:
1386 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1387 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1388 a2cfdea2 Iustin Pop
1389 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1390 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1391 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1392 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1393 def8e2f6 Michael Hanselmann
1394 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1395 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1396 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1397 def8e2f6 Michael Hanselmann
1398 def8e2f6 Michael Hanselmann
    try:
1399 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1400 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1401 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1402 a2cfdea2 Iustin Pop
1403 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1404 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1405 b00b95dd Iustin Pop

1406 b00b95dd Iustin Pop
    """
1407 b00b95dd Iustin Pop
    if self.minor is None:
1408 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1409 82463074 Iustin Pop
                  self._aminor)
1410 b00b95dd Iustin Pop
    if len(devices) != 2:
1411 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1412 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1413 03ece5f3 Iustin Pop
    if "local_dev" in info:
1414 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1415 b00b95dd Iustin Pop
    backend, meta = devices
1416 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1417 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1418 b00b95dd Iustin Pop
    backend.Open()
1419 b00b95dd Iustin Pop
    meta.Open()
1420 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1421 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1422 b00b95dd Iustin Pop
1423 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1424 b00b95dd Iustin Pop
    self._children = devices
1425 b00b95dd Iustin Pop
1426 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1427 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1428 b00b95dd Iustin Pop

1429 b00b95dd Iustin Pop
    """
1430 b00b95dd Iustin Pop
    if self.minor is None:
1431 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1432 82463074 Iustin Pop
                  self._aminor)
1433 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1434 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1435 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1436 03ece5f3 Iustin Pop
      return
1437 b00b95dd Iustin Pop
    if len(self._children) != 2:
1438 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1439 82463074 Iustin Pop
                  self._children)
1440 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1441 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1442 e739bd57 Iustin Pop
      return
1443 b00b95dd Iustin Pop
    if len(devices) != 2:
1444 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1445 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1446 e739bd57 Iustin Pop
      if dev != child.dev_path:
1447 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1448 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1449 b00b95dd Iustin Pop
1450 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1451 b00b95dd Iustin Pop
    self._children = []
1452 b00b95dd Iustin Pop
1453 7d585316 Iustin Pop
  @classmethod
1454 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1455 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1456 a2cfdea2 Iustin Pop

1457 7d585316 Iustin Pop
    This is the low-level implementation.
1458 7d585316 Iustin Pop

1459 7d585316 Iustin Pop
    @type minor: int
1460 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1461 7d585316 Iustin Pop
    @type kbytes: int
1462 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1463 7d585316 Iustin Pop
    @rtype: boolean
1464 7d585316 Iustin Pop
    @return: the success of the operation
1465 7d585316 Iustin Pop

1466 a2cfdea2 Iustin Pop
    """
1467 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1468 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1469 a2cfdea2 Iustin Pop
    if result.failed:
1470 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1471 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1472 7d585316 Iustin Pop
    return not result.failed
1473 7d585316 Iustin Pop
1474 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1475 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1476 7d585316 Iustin Pop

1477 7d585316 Iustin Pop
    @type kbytes: int
1478 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1479 7d585316 Iustin Pop
    @rtype: boolean
1480 7d585316 Iustin Pop
    @return: the success of the operation
1481 7d585316 Iustin Pop

1482 7d585316 Iustin Pop
    """
1483 7d585316 Iustin Pop
    if self.minor is None:
1484 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1485 7d585316 Iustin Pop
      return False
1486 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1487 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1488 a2cfdea2 Iustin Pop
1489 6b90c22e Iustin Pop
  def GetProcStatus(self):
1490 6b90c22e Iustin Pop
    """Return device data from /proc.
1491 6b90c22e Iustin Pop

1492 6b90c22e Iustin Pop
    """
1493 6b90c22e Iustin Pop
    if self.minor is None:
1494 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1495 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1496 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1497 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1498 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1499 6b90c22e Iustin Pop
1500 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1501 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1502 a2cfdea2 Iustin Pop

1503 a2cfdea2 Iustin Pop

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

1508 0834c866 Iustin Pop

1509 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1510 0834c866 Iustin Pop
    network not connected or local disk missing.
1511 0834c866 Iustin Pop

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

1515 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1516 c41eea6e Iustin Pop

1517 a2cfdea2 Iustin Pop
    """
1518 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1519 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1520 96acbc09 Michael Hanselmann
1521 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1522 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1523 f208978a Michael Hanselmann
1524 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1525 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1526 f208978a Michael Hanselmann
    elif stats.is_diskless:
1527 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1528 f208978a Michael Hanselmann
    else:
1529 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1530 96acbc09 Michael Hanselmann
1531 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1532 96acbc09 Michael Hanselmann
                                  major=self.major,
1533 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1534 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1535 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1536 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1537 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1538 a2cfdea2 Iustin Pop
1539 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1540 a2cfdea2 Iustin Pop
    """Make the local state primary.
1541 a2cfdea2 Iustin Pop

1542 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1543 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1544 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1545 f860ff4e Guido Trotter
    is mandatory.
1546 a2cfdea2 Iustin Pop

1547 a2cfdea2 Iustin Pop
    """
1548 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1549 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1550 a2cfdea2 Iustin Pop
      return False
1551 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1552 a2cfdea2 Iustin Pop
    if force:
1553 a2cfdea2 Iustin Pop
      cmd.append("-o")
1554 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1555 a2cfdea2 Iustin Pop
    if result.failed:
1556 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1557 82463074 Iustin Pop
                  result.output)
1558 a2cfdea2 Iustin Pop
1559 a2cfdea2 Iustin Pop
  def Close(self):
1560 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1561 a2cfdea2 Iustin Pop

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

1564 a2cfdea2 Iustin Pop
    """
1565 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1566 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1567 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1568 a2cfdea2 Iustin Pop
    if result.failed:
1569 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1570 82463074 Iustin Pop
                  self.minor, result.output)
1571 a2cfdea2 Iustin Pop
1572 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1573 cf8df3f3 Iustin Pop
    """Removes network configuration.
1574 cf8df3f3 Iustin Pop

1575 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1576 cf8df3f3 Iustin Pop

1577 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1578 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1579 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1580 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1581 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1582 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1583 cf8df3f3 Iustin Pop
    exception.
1584 cf8df3f3 Iustin Pop

1585 cf8df3f3 Iustin Pop
    """
1586 cf8df3f3 Iustin Pop
    if self.minor is None:
1587 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1588 cf8df3f3 Iustin Pop
1589 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1590 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1591 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1592 cf8df3f3 Iustin Pop
1593 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1594 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1595 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1596 cf8df3f3 Iustin Pop
1597 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1598 def8e2f6 Michael Hanselmann
1599 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1600 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1601 def8e2f6 Michael Hanselmann
        return
1602 def8e2f6 Michael Hanselmann
1603 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1604 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1605 def8e2f6 Michael Hanselmann
      # forgotten
1606 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1607 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1608 def8e2f6 Michael Hanselmann
1609 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1610 def8e2f6 Michael Hanselmann
1611 def8e2f6 Michael Hanselmann
    # Keep start time
1612 def8e2f6 Michael Hanselmann
    start_time = time.time()
1613 def8e2f6 Michael Hanselmann
1614 def8e2f6 Michael Hanselmann
    try:
1615 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1616 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1617 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1618 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1619 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1620 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1621 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1622 cf8df3f3 Iustin Pop
      else:
1623 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1624 def8e2f6 Michael Hanselmann
1625 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1626 cf8df3f3 Iustin Pop
1627 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1628 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1629 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1630 82463074 Iustin Pop
                   self.minor, reconfig_time)
1631 cf8df3f3 Iustin Pop
1632 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1633 cf8df3f3 Iustin Pop
    """Reconnects the network.
1634 cf8df3f3 Iustin Pop

1635 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1636 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1637 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1638 cf8df3f3 Iustin Pop

1639 cf8df3f3 Iustin Pop
    Args:
1640 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1641 cf8df3f3 Iustin Pop

1642 cf8df3f3 Iustin Pop
    """
1643 cf8df3f3 Iustin Pop
    if self.minor is None:
1644 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1645 cf8df3f3 Iustin Pop
1646 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1647 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1648 cf8df3f3 Iustin Pop
1649 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1650 cf8df3f3 Iustin Pop
1651 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1652 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1653 cf8df3f3 Iustin Pop
1654 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1655 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1656 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1657 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1658 cf8df3f3 Iustin Pop
1659 a2cfdea2 Iustin Pop
  def Attach(self):
1660 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1661 2d0c8319 Iustin Pop

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

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

1669 2d0c8319 Iustin Pop
    """
1670 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1671 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1672 2d0c8319 Iustin Pop
      minor = self._aminor
1673 2d0c8319 Iustin Pop
    else:
1674 2d0c8319 Iustin Pop
      minor = None
1675 2d0c8319 Iustin Pop
1676 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1677 2d0c8319 Iustin Pop
    return minor is not None
1678 2d0c8319 Iustin Pop
1679 2d0c8319 Iustin Pop
  def Assemble(self):
1680 2d0c8319 Iustin Pop
    """Assemble the drbd.
1681 2d0c8319 Iustin Pop

1682 2d0c8319 Iustin Pop
    Method:
1683 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1684 2d0c8319 Iustin Pop
        our config
1685 2d0c8319 Iustin Pop
      - if not, we create it from zero
1686 2d0c8319 Iustin Pop

1687 2d0c8319 Iustin Pop
    """
1688 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1689 2d0c8319 Iustin Pop
1690 2d0c8319 Iustin Pop
    self.Attach()
1691 2d0c8319 Iustin Pop
    if self.minor is None:
1692 2d0c8319 Iustin Pop
      # local device completely unconfigured
1693 1063abd1 Iustin Pop
      self._FastAssemble()
1694 2d0c8319 Iustin Pop
    else:
1695 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1696 2d0c8319 Iustin Pop
      # the device
1697 1063abd1 Iustin Pop
      self._SlowAssemble()
1698 2d0c8319 Iustin Pop
1699 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1700 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1701 a2cfdea2 Iustin Pop

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

1706 a2cfdea2 Iustin Pop
    """
1707 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
1708 527a15ac Iustin Pop
    # pylint: disable-msg=W0631
1709 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1710 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1711 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1712 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1713 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1714 1063abd1 Iustin Pop
1715 a2cfdea2 Iustin Pop
      if match_l and match_r:
1716 1063abd1 Iustin Pop
        # everything matches
1717 a2cfdea2 Iustin Pop
        break
1718 1063abd1 Iustin Pop
1719 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1720 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1721 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1722 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1723 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1724 1063abd1 Iustin Pop
          break
1725 1063abd1 Iustin Pop
        else:
1726 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1727 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1728 1063abd1 Iustin Pop
1729 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1730 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1731 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1732 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1733 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1734 1063abd1 Iustin Pop
          break
1735 1063abd1 Iustin Pop
        else:
1736 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1737 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1738 bf25af3b Iustin Pop
1739 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1740 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1741 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1742 bf25af3b Iustin Pop
      # configuration
1743 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1744 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1745 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1746 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1747 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1748 9cdbe77f Iustin Pop
        # reconnect to our correct one
1749 1063abd1 Iustin Pop
        try:
1750 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1751 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1752 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1753 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1754 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1755 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1756 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1757 9cdbe77f Iustin Pop
        # None)
1758 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1759 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1760 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1761 9cdbe77f Iustin Pop
          break
1762 1063abd1 Iustin Pop
        else:
1763 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1764 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1765 9cdbe77f Iustin Pop
1766 a2cfdea2 Iustin Pop
    else:
1767 a2cfdea2 Iustin Pop
      minor = None
1768 a2cfdea2 Iustin Pop
1769 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1770 1063abd1 Iustin Pop
    if minor is None:
1771 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1772 1063abd1 Iustin Pop
                  self._aminor)
1773 a2cfdea2 Iustin Pop
1774 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1775 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1776 a2cfdea2 Iustin Pop

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

1779 a2cfdea2 Iustin Pop
    """
1780 a1578d63 Iustin Pop
    minor = self._aminor
1781 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1782 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1783 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1784 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1785 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1786 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1787 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1788 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1789 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1790 a2cfdea2 Iustin Pop
1791 a2cfdea2 Iustin Pop
  @classmethod
1792 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1793 b00b95dd Iustin Pop
    """Detach from the local device.
1794 b00b95dd Iustin Pop

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

1798 b00b95dd Iustin Pop
    """
1799 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1800 b00b95dd Iustin Pop
    if result.failed:
1801 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1802 b00b95dd Iustin Pop
1803 b00b95dd Iustin Pop
  @classmethod
1804 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1805 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1806 f3e513ad Iustin Pop

1807 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1808 f3e513ad Iustin Pop

1809 f3e513ad Iustin Pop
    """
1810 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1811 a8459f1c Iustin Pop
    if result.failed:
1812 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1813 f3e513ad Iustin Pop
1814 f3e513ad Iustin Pop
  @classmethod
1815 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1816 a2cfdea2 Iustin Pop
    """Deactivate the device.
1817 a2cfdea2 Iustin Pop

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

1820 a2cfdea2 Iustin Pop
    """
1821 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1822 a2cfdea2 Iustin Pop
    if result.failed:
1823 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1824 33bc6f01 Iustin Pop
                  minor, result.output)
1825 a2cfdea2 Iustin Pop
1826 a2cfdea2 Iustin Pop
  def Shutdown(self):
1827 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1828 a2cfdea2 Iustin Pop

1829 a2cfdea2 Iustin Pop
    """
1830 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1831 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1832 746f7476 Iustin Pop
      return
1833 746f7476 Iustin Pop
    minor = self.minor
1834 a2cfdea2 Iustin Pop
    self.minor = None
1835 a2cfdea2 Iustin Pop
    self.dev_path = None
1836 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1837 a2cfdea2 Iustin Pop
1838 a2cfdea2 Iustin Pop
  def Remove(self):
1839 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1840 a2cfdea2 Iustin Pop

1841 a2cfdea2 Iustin Pop
    """
1842 0c6c04ec Iustin Pop
    self.Shutdown()
1843 a2cfdea2 Iustin Pop
1844 a2cfdea2 Iustin Pop
  @classmethod
1845 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1846 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1847 a2cfdea2 Iustin Pop

1848 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1849 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1850 a2cfdea2 Iustin Pop

1851 a2cfdea2 Iustin Pop
    """
1852 a2cfdea2 Iustin Pop
    if len(children) != 2:
1853 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1854 767d52d3 Iustin Pop
    # check that the minor is unused
1855 767d52d3 Iustin Pop
    aminor = unique_id[4]
1856 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1857 767d52d3 Iustin Pop
    if aminor in proc_info:
1858 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1859 767d52d3 Iustin Pop
      in_use = status.is_in_use
1860 767d52d3 Iustin Pop
    else:
1861 767d52d3 Iustin Pop
      in_use = False
1862 767d52d3 Iustin Pop
    if in_use:
1863 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1864 a2cfdea2 Iustin Pop
    meta = children[1]
1865 a2cfdea2 Iustin Pop
    meta.Assemble()
1866 a2cfdea2 Iustin Pop
    if not meta.Attach():
1867 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1868 33bc6f01 Iustin Pop
                  aminor, meta)
1869 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1870 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1871 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1872 a2cfdea2 Iustin Pop
1873 1005d816 Iustin Pop
  def Grow(self, amount):
1874 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1875 1005d816 Iustin Pop

1876 1005d816 Iustin Pop
    """
1877 1005d816 Iustin Pop
    if self.minor is None:
1878 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1879 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1880 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1881 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1882 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1883 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1884 1005d816 Iustin Pop
    if result.failed:
1885 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1886 1005d816 Iustin Pop
1887 a8083063 Iustin Pop
1888 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1889 6f695a2e Manuel Franceschini
  """File device.
1890 abdf0113 Iustin Pop

1891 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1892 6f695a2e Manuel Franceschini

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

1895 6f695a2e Manuel Franceschini
  """
1896 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1897 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1898 6f695a2e Manuel Franceschini

1899 6f695a2e Manuel Franceschini
    """
1900 6f695a2e Manuel Franceschini
    if children:
1901 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1902 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1903 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1904 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1905 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1906 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1907 ecb091e3 Iustin Pop
    self.Attach()
1908 6f695a2e Manuel Franceschini
1909 6f695a2e Manuel Franceschini
  def Assemble(self):
1910 6f695a2e Manuel Franceschini
    """Assemble the device.
1911 6f695a2e Manuel Franceschini

1912 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1913 6f695a2e Manuel Franceschini

1914 6f695a2e Manuel Franceschini
    """
1915 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1916 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1917 6f695a2e Manuel Franceschini
1918 6f695a2e Manuel Franceschini
  def Shutdown(self):
1919 6f695a2e Manuel Franceschini
    """Shutdown the device.
1920 6f695a2e Manuel Franceschini

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

1924 6f695a2e Manuel Franceschini
    """
1925 746f7476 Iustin Pop
    pass
1926 6f695a2e Manuel Franceschini
1927 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1928 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1929 6f695a2e Manuel Franceschini

1930 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1931 6f695a2e Manuel Franceschini

1932 6f695a2e Manuel Franceschini
    """
1933 6f695a2e Manuel Franceschini
    pass
1934 6f695a2e Manuel Franceschini
1935 6f695a2e Manuel Franceschini
  def Close(self):
1936 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1937 6f695a2e Manuel Franceschini

1938 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1939 6f695a2e Manuel Franceschini

1940 6f695a2e Manuel Franceschini
    """
1941 6f695a2e Manuel Franceschini
    pass
1942 6f695a2e Manuel Franceschini
1943 6f695a2e Manuel Franceschini
  def Remove(self):
1944 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1945 6f695a2e Manuel Franceschini

1946 c41eea6e Iustin Pop
    @rtype: boolean
1947 c41eea6e Iustin Pop
    @return: True if the removal was successful
1948 6f695a2e Manuel Franceschini

1949 6f695a2e Manuel Franceschini
    """
1950 6f695a2e Manuel Franceschini
    try:
1951 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1952 6f695a2e Manuel Franceschini
    except OSError, err:
1953 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1954 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1955 6f695a2e Manuel Franceschini
1956 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
1957 bbe4cc16 Iustin Pop
    """Renames the file.
1958 bbe4cc16 Iustin Pop

1959 bbe4cc16 Iustin Pop
    """
1960 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
1961 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
1962 bbe4cc16 Iustin Pop
1963 bbe4cc16 Iustin Pop
  def Grow(self, amount):
1964 bbe4cc16 Iustin Pop
    """Grow the file
1965 bbe4cc16 Iustin Pop

1966 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
1967 bbe4cc16 Iustin Pop

1968 bbe4cc16 Iustin Pop
    """
1969 91e2d9ec Guido Trotter
    # Check that the file exists
1970 91e2d9ec Guido Trotter
    self.Assemble()
1971 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
1972 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
1973 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
1974 91e2d9ec Guido Trotter
    try:
1975 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
1976 91e2d9ec Guido Trotter
      f.truncate(new_size)
1977 91e2d9ec Guido Trotter
      f.close()
1978 91e2d9ec Guido Trotter
    except EnvironmentError, err:
1979 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
1980 bbe4cc16 Iustin Pop
1981 6f695a2e Manuel Franceschini
  def Attach(self):
1982 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1983 6f695a2e Manuel Franceschini

1984 6f695a2e Manuel Franceschini
    Check if this file already exists.
1985 6f695a2e Manuel Franceschini

1986 c41eea6e Iustin Pop
    @rtype: boolean
1987 c41eea6e Iustin Pop
    @return: True if file exists
1988 6f695a2e Manuel Franceschini

1989 6f695a2e Manuel Franceschini
    """
1990 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1991 ecb091e3 Iustin Pop
    return self.attached
1992 6f695a2e Manuel Franceschini
1993 fcff3897 Iustin Pop
  def GetActualSize(self):
1994 fcff3897 Iustin Pop
    """Return the actual disk size.
1995 fcff3897 Iustin Pop

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

1998 fcff3897 Iustin Pop
    """
1999 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2000 fcff3897 Iustin Pop
    try:
2001 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2002 fcff3897 Iustin Pop
      return st.st_size
2003 fcff3897 Iustin Pop
    except OSError, err:
2004 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2005 fcff3897 Iustin Pop
2006 6f695a2e Manuel Franceschini
  @classmethod
2007 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
2008 6f695a2e Manuel Franceschini
    """Create a new file.
2009 6f695a2e Manuel Franceschini

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

2012 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2013 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2014 6f695a2e Manuel Franceschini

2015 6f695a2e Manuel Franceschini
    """
2016 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2017 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2018 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2019 6f695a2e Manuel Franceschini
    try:
2020 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
2021 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
2022 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2023 6f695a2e Manuel Franceschini
      f.close()
2024 cdeefd9b Guido Trotter
    except EnvironmentError, err:
2025 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
2026 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
2027 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
2028 6f695a2e Manuel Franceschini
2029 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
2030 6f695a2e Manuel Franceschini
2031 6f695a2e Manuel Franceschini
2032 a8083063 Iustin Pop
DEV_MAP = {
2033 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2034 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2035 a8083063 Iustin Pop
  }
2036 a8083063 Iustin Pop
2037 cb7c0198 Iustin Pop
if constants.ENABLE_FILE_STORAGE:
2038 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
2039 cb7c0198 Iustin Pop
2040 a8083063 Iustin Pop
2041 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
2042 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2043 a8083063 Iustin Pop

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

2047 a8083063 Iustin Pop
  """
2048 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2049 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2050 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
2051 cb999543 Iustin Pop
  if not device.attached:
2052 a8083063 Iustin Pop
    return None
2053 ecb091e3 Iustin Pop
  return device
2054 a8083063 Iustin Pop
2055 a8083063 Iustin Pop
2056 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
2057 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2058 a8083063 Iustin Pop

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

2062 a8083063 Iustin Pop
  """
2063 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2064 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2065 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
2066 1063abd1 Iustin Pop
  device.Assemble()
2067 a8083063 Iustin Pop
  return device
2068 a8083063 Iustin Pop
2069 a8083063 Iustin Pop
2070 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2071 a8083063 Iustin Pop
  """Create a device.
2072 a8083063 Iustin Pop

2073 a8083063 Iustin Pop
  """
2074 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2075 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2076 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2077 a8083063 Iustin Pop
  return device