Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 66e884e1

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 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
698 800ac399 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1011 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1012 a8083063 Iustin Pop

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

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

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

1053 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1054 a8083063 Iustin Pop

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

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

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

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

1110 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1111 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1459 7d585316 Iustin Pop
    This is the low-level implementation.
1460 7d585316 Iustin Pop

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

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

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

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

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

1505 a2cfdea2 Iustin Pop

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

1510 0834c866 Iustin Pop

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

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

1517 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1518 c41eea6e Iustin Pop

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

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

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

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

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

1577 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1578 cf8df3f3 Iustin Pop

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

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

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

1641 cf8df3f3 Iustin Pop
    Args:
1642 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1643 cf8df3f3 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

1809 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1810 f3e513ad Iustin Pop

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

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

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

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

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

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

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

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

1893 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1894 6f695a2e Manuel Franceschini

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

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

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

1914 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1915 6f695a2e Manuel Franceschini

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

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

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

1932 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1933 6f695a2e Manuel Franceschini

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

1940 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1941 6f695a2e Manuel Franceschini

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

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

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

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

1968 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
1969 bbe4cc16 Iustin Pop

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

1986 6f695a2e Manuel Franceschini
    Check if this file already exists.
1987 6f695a2e Manuel Franceschini

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

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

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

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

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

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

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

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

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

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

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

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