Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 7c4d6c7b

History | View | Annotate | Download (55.6 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 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 a8083063 Iustin Pop
35 a8083063 Iustin Pop
36 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
37 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
38 82463074 Iustin Pop

39 82463074 Iustin Pop
  This is used in order to simplify the execution of cleanup or
40 82463074 Iustin Pop
  rollback functions.
41 82463074 Iustin Pop

42 82463074 Iustin Pop
  @rtype: boolean
43 82463074 Iustin Pop
  @return: True when fn didn't raise an exception, False otherwise
44 82463074 Iustin Pop

45 82463074 Iustin Pop
  """
46 82463074 Iustin Pop
  try:
47 82463074 Iustin Pop
    fn(*args, **kwargs)
48 82463074 Iustin Pop
    return True
49 82463074 Iustin Pop
  except errors.BlockDeviceError, err:
50 82463074 Iustin Pop
    logging.warning("Caught BlockDeviceError but ignoring: %s" % str(err))
51 82463074 Iustin Pop
    return False
52 82463074 Iustin Pop
53 82463074 Iustin Pop
54 82463074 Iustin Pop
def _ThrowError(msg, *args):
55 82463074 Iustin Pop
  """Log an error to the node daemon and the raise an exception.
56 82463074 Iustin Pop

57 82463074 Iustin Pop
  @type msg: string
58 82463074 Iustin Pop
  @param msg: the text of the exception
59 82463074 Iustin Pop
  @raise errors.BlockDeviceError
60 82463074 Iustin Pop

61 82463074 Iustin Pop
  """
62 82463074 Iustin Pop
  if args:
63 82463074 Iustin Pop
    msg = msg % args
64 82463074 Iustin Pop
  logging.error(msg)
65 82463074 Iustin Pop
  raise errors.BlockDeviceError(msg)
66 82463074 Iustin Pop
67 82463074 Iustin Pop
68 a8083063 Iustin Pop
class BlockDev(object):
69 a8083063 Iustin Pop
  """Block device abstract class.
70 a8083063 Iustin Pop

71 a8083063 Iustin Pop
  A block device can be in the following states:
72 a8083063 Iustin Pop
    - not existing on the system, and by `Create()` it goes into:
73 a8083063 Iustin Pop
    - existing but not setup/not active, and by `Assemble()` goes into:
74 a8083063 Iustin Pop
    - active read-write and by `Open()` it goes into
75 a8083063 Iustin Pop
    - online (=used, or ready for use)
76 a8083063 Iustin Pop

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

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

88 a8083063 Iustin Pop
  A block device is identified by three items:
89 a8083063 Iustin Pop
    - the /dev path of the device (dynamic)
90 a8083063 Iustin Pop
    - a unique ID of the device (static)
91 a8083063 Iustin Pop
    - it's major/minor pair (dynamic)
92 a8083063 Iustin Pop

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

99 a8083063 Iustin Pop
  You can get to a device in two ways:
100 a8083063 Iustin Pop
    - creating the (real) device, which returns you
101 abdf0113 Iustin Pop
      an attached instance (lvcreate)
102 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
103 a8083063 Iustin Pop

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

110 a8083063 Iustin Pop
  """
111 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
112 a8083063 Iustin Pop
    self._children = children
113 a8083063 Iustin Pop
    self.dev_path = None
114 a8083063 Iustin Pop
    self.unique_id = unique_id
115 a8083063 Iustin Pop
    self.major = None
116 a8083063 Iustin Pop
    self.minor = None
117 cb999543 Iustin Pop
    self.attached = False
118 464f8daf Iustin Pop
    self.size = size
119 a8083063 Iustin Pop
120 a8083063 Iustin Pop
  def Assemble(self):
121 a8083063 Iustin Pop
    """Assemble the device from its components.
122 a8083063 Iustin Pop

123 f87548b5 Iustin Pop
    Implementations of this method by child classes must ensure that:
124 f87548b5 Iustin Pop
      - after the device has been assembled, it knows its major/minor
125 f87548b5 Iustin Pop
        numbers; this allows other devices (usually parents) to probe
126 f87548b5 Iustin Pop
        correctly for their children
127 f87548b5 Iustin Pop
      - calling this method on an existing, in-use device is safe
128 f87548b5 Iustin Pop
      - if the device is already configured (and in an OK state),
129 f87548b5 Iustin Pop
        this method is idempotent
130 a8083063 Iustin Pop

131 a8083063 Iustin Pop
    """
132 1063abd1 Iustin Pop
    pass
133 a8083063 Iustin Pop
134 a8083063 Iustin Pop
  def Attach(self):
135 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
136 a8083063 Iustin Pop

137 a8083063 Iustin Pop
    """
138 a8083063 Iustin Pop
    raise NotImplementedError
139 a8083063 Iustin Pop
140 a8083063 Iustin Pop
  def Close(self):
141 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
142 a8083063 Iustin Pop

143 a8083063 Iustin Pop
    """
144 a8083063 Iustin Pop
    raise NotImplementedError
145 a8083063 Iustin Pop
146 a8083063 Iustin Pop
  @classmethod
147 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
148 a8083063 Iustin Pop
    """Create the device.
149 a8083063 Iustin Pop

150 a8083063 Iustin Pop
    If the device cannot be created, it will return None
151 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
152 a8083063 Iustin Pop

153 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
154 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
155 a8083063 Iustin Pop
    enough for both creation and assembly (later).
156 a8083063 Iustin Pop

157 a8083063 Iustin Pop
    """
158 a8083063 Iustin Pop
    raise NotImplementedError
159 a8083063 Iustin Pop
160 a8083063 Iustin Pop
  def Remove(self):
161 a8083063 Iustin Pop
    """Remove this device.
162 a8083063 Iustin Pop

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

167 a8083063 Iustin Pop
    """
168 a8083063 Iustin Pop
    raise NotImplementedError
169 a8083063 Iustin Pop
170 f3e513ad Iustin Pop
  def Rename(self, new_id):
171 f3e513ad Iustin Pop
    """Rename this device.
172 f3e513ad Iustin Pop

173 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
174 f3e513ad Iustin Pop

175 f3e513ad Iustin Pop
    """
176 f3e513ad Iustin Pop
    raise NotImplementedError
177 f3e513ad Iustin Pop
178 a8083063 Iustin Pop
  def Open(self, force=False):
179 a8083063 Iustin Pop
    """Make the device ready for use.
180 a8083063 Iustin Pop

181 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
182 a8083063 Iustin Pop
    devices need this.
183 a8083063 Iustin Pop

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

187 a8083063 Iustin Pop
    """
188 a8083063 Iustin Pop
    raise NotImplementedError
189 a8083063 Iustin Pop
190 a8083063 Iustin Pop
  def Shutdown(self):
191 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
192 a8083063 Iustin Pop

193 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
194 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
195 a8083063 Iustin Pop
    assembled after this call.
196 a8083063 Iustin Pop

197 a8083063 Iustin Pop
    """
198 a8083063 Iustin Pop
    raise NotImplementedError
199 a8083063 Iustin Pop
200 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
201 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
202 a8083063 Iustin Pop

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

205 a8083063 Iustin Pop
    """
206 a8083063 Iustin Pop
    result = True
207 a8083063 Iustin Pop
    if self._children:
208 a8083063 Iustin Pop
      for child in self._children:
209 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
210 a8083063 Iustin Pop
    return result
211 a8083063 Iustin Pop
212 a8083063 Iustin Pop
  def GetSyncStatus(self):
213 a8083063 Iustin Pop
    """Returns the sync status of the device.
214 a8083063 Iustin Pop

215 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
216 a8083063 Iustin Pop
    status of the mirror.
217 a8083063 Iustin Pop

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

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

223 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
224 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
225 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
226 a8083063 Iustin Pop

227 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
228 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
229 0834c866 Iustin Pop
    return False (not degraded).
230 0834c866 Iustin Pop

231 c41eea6e Iustin Pop
    @rtype: tuple
232 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
233 c41eea6e Iustin Pop

234 a8083063 Iustin Pop
    """
235 0834c866 Iustin Pop
    return None, None, False, False
236 a8083063 Iustin Pop
237 a8083063 Iustin Pop
238 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
239 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
240 a8083063 Iustin Pop

241 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
242 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
243 a8083063 Iustin Pop
    children.
244 a8083063 Iustin Pop

245 a8083063 Iustin Pop
    """
246 0834c866 Iustin Pop
    min_percent, max_time, is_degraded, ldisk = self.GetSyncStatus()
247 a8083063 Iustin Pop
    if self._children:
248 a8083063 Iustin Pop
      for child in self._children:
249 0834c866 Iustin Pop
        c_percent, c_time, c_degraded, c_ldisk = child.GetSyncStatus()
250 a8083063 Iustin Pop
        if min_percent is None:
251 a8083063 Iustin Pop
          min_percent = c_percent
252 a8083063 Iustin Pop
        elif c_percent is not None:
253 a8083063 Iustin Pop
          min_percent = min(min_percent, c_percent)
254 a8083063 Iustin Pop
        if max_time is None:
255 a8083063 Iustin Pop
          max_time = c_time
256 a8083063 Iustin Pop
        elif c_time is not None:
257 a8083063 Iustin Pop
          max_time = max(max_time, c_time)
258 a8083063 Iustin Pop
        is_degraded = is_degraded or c_degraded
259 0834c866 Iustin Pop
        ldisk = ldisk or c_ldisk
260 0834c866 Iustin Pop
    return min_percent, max_time, is_degraded, ldisk
261 a8083063 Iustin Pop
262 a8083063 Iustin Pop
263 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
264 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
265 a0c3fea1 Michael Hanselmann

266 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
267 a0c3fea1 Michael Hanselmann

268 a0c3fea1 Michael Hanselmann
    """
269 a0c3fea1 Michael Hanselmann
    for child in self._children:
270 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
271 a0c3fea1 Michael Hanselmann
272 1005d816 Iustin Pop
  def Grow(self, amount):
273 1005d816 Iustin Pop
    """Grow the block device.
274 1005d816 Iustin Pop

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

277 1005d816 Iustin Pop
    """
278 1005d816 Iustin Pop
    raise NotImplementedError
279 a0c3fea1 Michael Hanselmann
280 a8083063 Iustin Pop
  def __repr__(self):
281 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
282 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
283 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
284 a8083063 Iustin Pop
285 a8083063 Iustin Pop
286 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
287 a8083063 Iustin Pop
  """Logical Volume block device.
288 a8083063 Iustin Pop

289 a8083063 Iustin Pop
  """
290 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
291 a8083063 Iustin Pop
    """Attaches to a LV device.
292 a8083063 Iustin Pop

293 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
294 a8083063 Iustin Pop

295 a8083063 Iustin Pop
    """
296 464f8daf Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children, size)
297 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
298 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
299 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
300 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
301 99e8295c Iustin Pop
    self._degraded = True
302 99e8295c Iustin Pop
    self.major = self.minor = None
303 a8083063 Iustin Pop
    self.Attach()
304 a8083063 Iustin Pop
305 a8083063 Iustin Pop
  @classmethod
306 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
307 a8083063 Iustin Pop
    """Create a new logical volume.
308 a8083063 Iustin Pop

309 a8083063 Iustin Pop
    """
310 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
311 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
312 6c626518 Iustin Pop
                                   str(unique_id))
313 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
314 a8083063 Iustin Pop
    pvs_info = cls.GetPVInfo(vg_name)
315 a8083063 Iustin Pop
    if not pvs_info:
316 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", vg_name)
317 a8083063 Iustin Pop
    pvs_info.sort()
318 a8083063 Iustin Pop
    pvs_info.reverse()
319 5b7b5d49 Guido Trotter
320 5b7b5d49 Guido Trotter
    pvlist = [ pv[1] for pv in pvs_info ]
321 5b7b5d49 Guido Trotter
    free_size = sum([ pv[0] for pv in pvs_info ])
322 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
323 fecbe9d5 Iustin Pop
    stripes = min(current_pvs, constants.LVM_STRIPECOUNT)
324 5b7b5d49 Guido Trotter
325 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
326 5b7b5d49 Guido Trotter
    # calling the create function.
327 a8083063 Iustin Pop
    if free_size < size:
328 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
329 82463074 Iustin Pop
                  " available %s", size, free_size)
330 fecbe9d5 Iustin Pop
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
331 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
332 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
333 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
334 fecbe9d5 Iustin Pop
    # stripes
335 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
336 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
337 fecbe9d5 Iustin Pop
      if not result.failed:
338 fecbe9d5 Iustin Pop
        break
339 a8083063 Iustin Pop
    if result.failed:
340 82463074 Iustin Pop
      _ThrowError("LV create failed (%s): %s",
341 82463074 Iustin Pop
                  result.fail_reason, result.output)
342 464f8daf Iustin Pop
    return LogicalVolume(unique_id, children, size)
343 a8083063 Iustin Pop
344 a8083063 Iustin Pop
  @staticmethod
345 a8083063 Iustin Pop
  def GetPVInfo(vg_name):
346 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
347 a8083063 Iustin Pop

348 c41eea6e Iustin Pop
    @param vg_name: the volume group name
349 a8083063 Iustin Pop

350 c41eea6e Iustin Pop
    @rtype: list
351 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
352 098c0958 Michael Hanselmann

353 a8083063 Iustin Pop
    """
354 a8083063 Iustin Pop
    command = ["pvs", "--noheadings", "--nosuffix", "--units=m",
355 a8083063 Iustin Pop
               "-opv_name,vg_name,pv_free,pv_attr", "--unbuffered",
356 a8083063 Iustin Pop
               "--separator=:"]
357 a8083063 Iustin Pop
    result = utils.RunCmd(command)
358 a8083063 Iustin Pop
    if result.failed:
359 468c5f77 Iustin Pop
      logging.error("Can't get the PV information: %s - %s",
360 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
361 a8083063 Iustin Pop
      return None
362 a8083063 Iustin Pop
    data = []
363 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
364 a8083063 Iustin Pop
      fields = line.strip().split(':')
365 a8083063 Iustin Pop
      if len(fields) != 4:
366 468c5f77 Iustin Pop
        logging.error("Can't parse pvs output: line '%s'", line)
367 a8083063 Iustin Pop
        return None
368 a8083063 Iustin Pop
      # skip over pvs from another vg or ones which are not allocatable
369 a8083063 Iustin Pop
      if fields[1] != vg_name or fields[3][0] != 'a':
370 a8083063 Iustin Pop
        continue
371 a8083063 Iustin Pop
      data.append((float(fields[2]), fields[0]))
372 a8083063 Iustin Pop
373 a8083063 Iustin Pop
    return data
374 a8083063 Iustin Pop
375 a8083063 Iustin Pop
  def Remove(self):
376 a8083063 Iustin Pop
    """Remove this logical volume.
377 a8083063 Iustin Pop

378 a8083063 Iustin Pop
    """
379 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
380 a8083063 Iustin Pop
      # the LV does not exist
381 0c6c04ec Iustin Pop
      return
382 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
383 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
384 a8083063 Iustin Pop
    if result.failed:
385 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
386 a8083063 Iustin Pop
387 f3e513ad Iustin Pop
  def Rename(self, new_id):
388 f3e513ad Iustin Pop
    """Rename this logical volume.
389 f3e513ad Iustin Pop

390 f3e513ad Iustin Pop
    """
391 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
392 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
393 f3e513ad Iustin Pop
    new_vg, new_name = new_id
394 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
395 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
396 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
397 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
398 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
399 f3e513ad Iustin Pop
    if result.failed:
400 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
401 be345db0 Iustin Pop
    self._lv_name = new_name
402 be345db0 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
403 be345db0 Iustin Pop
404 a8083063 Iustin Pop
  def Attach(self):
405 a8083063 Iustin Pop
    """Attach to an existing LV.
406 a8083063 Iustin Pop

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

411 a8083063 Iustin Pop
    """
412 cb999543 Iustin Pop
    self.attached = False
413 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
414 99e8295c Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor",
415 99e8295c Iustin Pop
                           self.dev_path])
416 a8083063 Iustin Pop
    if result.failed:
417 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
418 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
419 a8083063 Iustin Pop
      return False
420 99e8295c Iustin Pop
    out = result.stdout.strip().rstrip(',')
421 99e8295c Iustin Pop
    out = out.split(",")
422 99e8295c Iustin Pop
    if len(out) != 3:
423 468c5f77 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 3", str(out))
424 99e8295c Iustin Pop
      return False
425 99e8295c Iustin Pop
426 99e8295c Iustin Pop
    status, major, minor = out[:3]
427 99e8295c Iustin Pop
    if len(status) != 6:
428 468c5f77 Iustin Pop
      logging.error("lvs lv_attr is not 6 characters (%s)", status)
429 99e8295c Iustin Pop
      return False
430 99e8295c Iustin Pop
431 99e8295c Iustin Pop
    try:
432 99e8295c Iustin Pop
      major = int(major)
433 99e8295c Iustin Pop
      minor = int(minor)
434 99e8295c Iustin Pop
    except ValueError, err:
435 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
436 99e8295c Iustin Pop
437 99e8295c Iustin Pop
    self.major = major
438 99e8295c Iustin Pop
    self.minor = minor
439 99e8295c Iustin Pop
    self._degraded = status[0] == 'v' # virtual volume, i.e. doesn't backing
440 99e8295c Iustin Pop
                                      # storage
441 cb999543 Iustin Pop
    self.attached = True
442 99e8295c Iustin Pop
    return True
443 a8083063 Iustin Pop
444 a8083063 Iustin Pop
  def Assemble(self):
445 a8083063 Iustin Pop
    """Assemble the device.
446 a8083063 Iustin Pop

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

451 a8083063 Iustin Pop
    """
452 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
453 5574047a Iustin Pop
    if result.failed:
454 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
455 a8083063 Iustin Pop
456 a8083063 Iustin Pop
  def Shutdown(self):
457 a8083063 Iustin Pop
    """Shutdown the device.
458 a8083063 Iustin Pop

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

462 a8083063 Iustin Pop
    """
463 746f7476 Iustin Pop
    pass
464 a8083063 Iustin Pop
465 9db6dbce Iustin Pop
  def GetSyncStatus(self):
466 9db6dbce Iustin Pop
    """Returns the sync status of the device.
467 9db6dbce Iustin Pop

468 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
469 9db6dbce Iustin Pop
    status of the mirror.
470 9db6dbce Iustin Pop

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

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

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

484 c41eea6e Iustin Pop
    @rtype: tuple
485 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
486 c41eea6e Iustin Pop

487 9db6dbce Iustin Pop
    """
488 99e8295c Iustin Pop
    return None, None, self._degraded, self._degraded
489 9db6dbce Iustin Pop
490 a8083063 Iustin Pop
  def Open(self, force=False):
491 a8083063 Iustin Pop
    """Make the device ready for I/O.
492 a8083063 Iustin Pop

493 a8083063 Iustin Pop
    This is a no-op for the LV device type.
494 a8083063 Iustin Pop

495 a8083063 Iustin Pop
    """
496 fdbd668d Iustin Pop
    pass
497 a8083063 Iustin Pop
498 a8083063 Iustin Pop
  def Close(self):
499 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
500 a8083063 Iustin Pop

501 a8083063 Iustin Pop
    This is a no-op for the LV device type.
502 a8083063 Iustin Pop

503 a8083063 Iustin Pop
    """
504 fdbd668d Iustin Pop
    pass
505 a8083063 Iustin Pop
506 a8083063 Iustin Pop
  def Snapshot(self, size):
507 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
508 a8083063 Iustin Pop

509 a8083063 Iustin Pop
    """
510 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
511 a8083063 Iustin Pop
512 a8083063 Iustin Pop
    # remove existing snapshot if found
513 464f8daf Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None, size)
514 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
515 a8083063 Iustin Pop
516 a8083063 Iustin Pop
    pvs_info = self.GetPVInfo(self._vg_name)
517 a8083063 Iustin Pop
    if not pvs_info:
518 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", self._vg_name)
519 a8083063 Iustin Pop
    pvs_info.sort()
520 a8083063 Iustin Pop
    pvs_info.reverse()
521 a8083063 Iustin Pop
    free_size, pv_name = pvs_info[0]
522 a8083063 Iustin Pop
    if free_size < size:
523 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
524 82463074 Iustin Pop
                  " available %s", size, free_size)
525 a8083063 Iustin Pop
526 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
527 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
528 a8083063 Iustin Pop
    if result.failed:
529 82463074 Iustin Pop
      _ThrowError("command: %s error: %s - %s",
530 82463074 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
531 a8083063 Iustin Pop
532 a8083063 Iustin Pop
    return snap_name
533 a8083063 Iustin Pop
534 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
535 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
536 a0c3fea1 Michael Hanselmann

537 a0c3fea1 Michael Hanselmann
    """
538 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
539 a0c3fea1 Michael Hanselmann
540 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
541 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
542 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
543 a0c3fea1 Michael Hanselmann
544 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
545 a0c3fea1 Michael Hanselmann
    text = text[:128]
546 a0c3fea1 Michael Hanselmann
547 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
548 a0c3fea1 Michael Hanselmann
                           self.dev_path])
549 a0c3fea1 Michael Hanselmann
    if result.failed:
550 82463074 Iustin Pop
      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
551 82463074 Iustin Pop
                  result.output)
552 82463074 Iustin Pop
553 1005d816 Iustin Pop
  def Grow(self, amount):
554 1005d816 Iustin Pop
    """Grow the logical volume.
555 1005d816 Iustin Pop

556 1005d816 Iustin Pop
    """
557 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
558 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
559 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
560 1005d816 Iustin Pop
    # supports 'cling'
561 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
562 1005d816 Iustin Pop
      result = utils.RunCmd(["lvextend", "--alloc", alloc_policy,
563 1005d816 Iustin Pop
                             "-L", "+%dm" % amount, self.dev_path])
564 1005d816 Iustin Pop
      if not result.failed:
565 1005d816 Iustin Pop
        return
566 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
567 a0c3fea1 Michael Hanselmann
568 a0c3fea1 Michael Hanselmann
569 6b90c22e Iustin Pop
class DRBD8Status(object):
570 6b90c22e Iustin Pop
  """A DRBD status representation class.
571 6b90c22e Iustin Pop

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

574 6b90c22e Iustin Pop
  """
575 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
576 01e2ce3a Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
577 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
578 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
579 6b90c22e Iustin Pop
                       "\sfinish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
580 6b90c22e Iustin Pop
581 3c003d9d Iustin Pop
  CS_UNCONFIGURED = "Unconfigured"
582 3c003d9d Iustin Pop
  CS_STANDALONE = "StandAlone"
583 3c003d9d Iustin Pop
  CS_WFCONNECTION = "WFConnection"
584 3c003d9d Iustin Pop
  CS_WFREPORTPARAMS = "WFReportParams"
585 3c003d9d Iustin Pop
  CS_CONNECTED = "Connected"
586 3c003d9d Iustin Pop
  CS_STARTINGSYNCS = "StartingSyncS"
587 3c003d9d Iustin Pop
  CS_STARTINGSYNCT = "StartingSyncT"
588 3c003d9d Iustin Pop
  CS_WFBITMAPS = "WFBitMapS"
589 3c003d9d Iustin Pop
  CS_WFBITMAPT = "WFBitMapT"
590 3c003d9d Iustin Pop
  CS_WFSYNCUUID = "WFSyncUUID"
591 3c003d9d Iustin Pop
  CS_SYNCSOURCE = "SyncSource"
592 3c003d9d Iustin Pop
  CS_SYNCTARGET = "SyncTarget"
593 3c003d9d Iustin Pop
  CS_PAUSEDSYNCS = "PausedSyncS"
594 3c003d9d Iustin Pop
  CS_PAUSEDSYNCT = "PausedSyncT"
595 3c003d9d Iustin Pop
  CSET_SYNC = frozenset([
596 3c003d9d Iustin Pop
    CS_WFREPORTPARAMS,
597 3c003d9d Iustin Pop
    CS_STARTINGSYNCS,
598 3c003d9d Iustin Pop
    CS_STARTINGSYNCT,
599 3c003d9d Iustin Pop
    CS_WFBITMAPS,
600 3c003d9d Iustin Pop
    CS_WFBITMAPT,
601 3c003d9d Iustin Pop
    CS_WFSYNCUUID,
602 3c003d9d Iustin Pop
    CS_SYNCSOURCE,
603 3c003d9d Iustin Pop
    CS_SYNCTARGET,
604 3c003d9d Iustin Pop
    CS_PAUSEDSYNCS,
605 3c003d9d Iustin Pop
    CS_PAUSEDSYNCT,
606 3c003d9d Iustin Pop
    ])
607 3c003d9d Iustin Pop
608 3c003d9d Iustin Pop
  DS_DISKLESS = "Diskless"
609 3c003d9d Iustin Pop
  DS_ATTACHING = "Attaching" # transient state
610 3c003d9d Iustin Pop
  DS_FAILED = "Failed" # transient state, next: diskless
611 3c003d9d Iustin Pop
  DS_NEGOTIATING = "Negotiating" # transient state
612 3c003d9d Iustin Pop
  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
613 3c003d9d Iustin Pop
  DS_OUTDATED = "Outdated"
614 3c003d9d Iustin Pop
  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
615 3c003d9d Iustin Pop
  DS_CONSISTENT = "Consistent"
616 3c003d9d Iustin Pop
  DS_UPTODATE = "UpToDate" # normal state
617 3c003d9d Iustin Pop
618 3c003d9d Iustin Pop
  RO_PRIMARY = "Primary"
619 3c003d9d Iustin Pop
  RO_SECONDARY = "Secondary"
620 3c003d9d Iustin Pop
  RO_UNKNOWN = "Unknown"
621 3c003d9d Iustin Pop
622 6b90c22e Iustin Pop
  def __init__(self, procline):
623 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
624 767d52d3 Iustin Pop
    if u:
625 3c003d9d Iustin Pop
      self.cstatus = self.CS_UNCONFIGURED
626 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
627 767d52d3 Iustin Pop
    else:
628 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
629 767d52d3 Iustin Pop
      if not m:
630 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
631 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
632 767d52d3 Iustin Pop
      self.lrole = m.group(2)
633 767d52d3 Iustin Pop
      self.rrole = m.group(3)
634 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
635 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
636 767d52d3 Iustin Pop
637 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
638 6b90c22e Iustin Pop
639 3c003d9d Iustin Pop
    self.is_standalone = self.cstatus == self.CS_STANDALONE
640 3c003d9d Iustin Pop
    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
641 3c003d9d Iustin Pop
    self.is_connected = self.cstatus == self.CS_CONNECTED
642 3c003d9d Iustin Pop
    self.is_primary = self.lrole == self.RO_PRIMARY
643 3c003d9d Iustin Pop
    self.is_secondary = self.lrole == self.RO_SECONDARY
644 3c003d9d Iustin Pop
    self.peer_primary = self.rrole == self.RO_PRIMARY
645 3c003d9d Iustin Pop
    self.peer_secondary = self.rrole == self.RO_SECONDARY
646 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
647 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
648 6b90c22e Iustin Pop
649 3c003d9d Iustin Pop
    self.is_diskless = self.ldisk == self.DS_DISKLESS
650 3c003d9d Iustin Pop
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
651 6b90c22e Iustin Pop
652 3c003d9d Iustin Pop
    self.is_in_resync = self.cstatus in self.CSET_SYNC
653 3c003d9d Iustin Pop
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
654 6b93ec9d Iustin Pop
655 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
656 6b90c22e Iustin Pop
    if m:
657 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
658 6b90c22e Iustin Pop
      hours = int(m.group(2))
659 6b90c22e Iustin Pop
      minutes = int(m.group(3))
660 6b90c22e Iustin Pop
      seconds = int(m.group(4))
661 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
662 6b90c22e Iustin Pop
    else:
663 3c003d9d Iustin Pop
      # we have (in this if branch) no percent information, but if
664 3c003d9d Iustin Pop
      # we're resyncing we need to 'fake' a sync percent information,
665 3c003d9d Iustin Pop
      # as this is how cmdlib determines if it makes sense to wait for
666 3c003d9d Iustin Pop
      # resyncing or not
667 3c003d9d Iustin Pop
      if self.is_in_resync:
668 3c003d9d Iustin Pop
        self.sync_percent = 0
669 3c003d9d Iustin Pop
      else:
670 3c003d9d Iustin Pop
        self.sync_percent = None
671 6b90c22e Iustin Pop
      self.est_time = None
672 6b90c22e Iustin Pop
673 6b90c22e Iustin Pop
674 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
675 0f7f32d9 Iustin Pop
  """Base DRBD class.
676 a8083063 Iustin Pop

677 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
678 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
679 0f7f32d9 Iustin Pop

680 abdf0113 Iustin Pop
  """
681 abdf0113 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
682 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
683 a8083063 Iustin Pop
684 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
685 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
686 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
687 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
688 a8083063 Iustin Pop
689 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
690 6b90c22e Iustin Pop
691 abdf0113 Iustin Pop
  @staticmethod
692 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
693 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
694 a8083063 Iustin Pop

695 a8083063 Iustin Pop
    """
696 abdf0113 Iustin Pop
    try:
697 f6eaed12 Iustin Pop
      stat = open(filename, "r")
698 f6eaed12 Iustin Pop
      try:
699 f6eaed12 Iustin Pop
        data = stat.read().splitlines()
700 f6eaed12 Iustin Pop
      finally:
701 f6eaed12 Iustin Pop
        stat.close()
702 f6eaed12 Iustin Pop
    except EnvironmentError, err:
703 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
704 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
705 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
706 f6eaed12 Iustin Pop
      else:
707 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
708 abdf0113 Iustin Pop
    if not data:
709 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
710 abdf0113 Iustin Pop
    return data
711 a8083063 Iustin Pop
712 abdf0113 Iustin Pop
  @staticmethod
713 abdf0113 Iustin Pop
  def _MassageProcData(data):
714 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
715 a8083063 Iustin Pop

716 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
717 c41eea6e Iustin Pop
        for that minor
718 a8083063 Iustin Pop

719 a8083063 Iustin Pop
    """
720 abdf0113 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
721 abdf0113 Iustin Pop
    results = {}
722 abdf0113 Iustin Pop
    old_minor = old_line = None
723 abdf0113 Iustin Pop
    for line in data:
724 abdf0113 Iustin Pop
      lresult = lmatch.match(line)
725 abdf0113 Iustin Pop
      if lresult is not None:
726 abdf0113 Iustin Pop
        if old_minor is not None:
727 abdf0113 Iustin Pop
          results[old_minor] = old_line
728 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
729 abdf0113 Iustin Pop
        old_line = line
730 abdf0113 Iustin Pop
      else:
731 abdf0113 Iustin Pop
        if old_minor is not None:
732 abdf0113 Iustin Pop
          old_line += " " + line.strip()
733 abdf0113 Iustin Pop
    # add last line
734 abdf0113 Iustin Pop
    if old_minor is not None:
735 abdf0113 Iustin Pop
      results[old_minor] = old_line
736 abdf0113 Iustin Pop
    return results
737 a8083063 Iustin Pop
738 abdf0113 Iustin Pop
  @classmethod
739 abdf0113 Iustin Pop
  def _GetVersion(cls):
740 abdf0113 Iustin Pop
    """Return the DRBD version.
741 a8083063 Iustin Pop

742 abdf0113 Iustin Pop
    This will return a dict with keys:
743 c41eea6e Iustin Pop
      - k_major
744 c41eea6e Iustin Pop
      - k_minor
745 c41eea6e Iustin Pop
      - k_point
746 c41eea6e Iustin Pop
      - api
747 c41eea6e Iustin Pop
      - proto
748 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
749 a8083063 Iustin Pop

750 a8083063 Iustin Pop
    """
751 abdf0113 Iustin Pop
    proc_data = cls._GetProcData()
752 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
753 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
754 abdf0113 Iustin Pop
    if not version:
755 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
756 abdf0113 Iustin Pop
                                    first_line)
757 a8083063 Iustin Pop
758 abdf0113 Iustin Pop
    values = version.groups()
759 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
760 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
761 abdf0113 Iustin Pop
              'k_point': int(values[2]),
762 abdf0113 Iustin Pop
              'api': int(values[3]),
763 abdf0113 Iustin Pop
              'proto': int(values[4]),
764 abdf0113 Iustin Pop
             }
765 abdf0113 Iustin Pop
    if values[5] is not None:
766 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
767 a8083063 Iustin Pop
768 abdf0113 Iustin Pop
    return retval
769 abdf0113 Iustin Pop
770 abdf0113 Iustin Pop
  @staticmethod
771 abdf0113 Iustin Pop
  def _DevPath(minor):
772 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
773 a8083063 Iustin Pop

774 a8083063 Iustin Pop
    """
775 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
776 a8083063 Iustin Pop
777 abdf0113 Iustin Pop
  @classmethod
778 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
779 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
780 a8083063 Iustin Pop

781 a8083063 Iustin Pop
    """
782 abdf0113 Iustin Pop
    data = cls._GetProcData()
783 a8083063 Iustin Pop
784 abdf0113 Iustin Pop
    used_devs = {}
785 abdf0113 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
786 abdf0113 Iustin Pop
    for line in data:
787 abdf0113 Iustin Pop
      match = valid_line.match(line)
788 abdf0113 Iustin Pop
      if not match:
789 abdf0113 Iustin Pop
        continue
790 abdf0113 Iustin Pop
      minor = int(match.group(1))
791 abdf0113 Iustin Pop
      state = match.group(2)
792 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
793 abdf0113 Iustin Pop
        continue
794 abdf0113 Iustin Pop
      used_devs[minor] = state, line
795 a8083063 Iustin Pop
796 abdf0113 Iustin Pop
    return used_devs
797 a8083063 Iustin Pop
798 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
799 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
800 0834c866 Iustin Pop

801 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
802 a8083063 Iustin Pop

803 a8083063 Iustin Pop
    """
804 abdf0113 Iustin Pop
    if minor is None:
805 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
806 cb999543 Iustin Pop
      self.attached = False
807 a8083063 Iustin Pop
    else:
808 abdf0113 Iustin Pop
      self.minor = minor
809 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
810 cb999543 Iustin Pop
      self.attached = True
811 a8083063 Iustin Pop
812 a8083063 Iustin Pop
  @staticmethod
813 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
814 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
815 a8083063 Iustin Pop

816 abdf0113 Iustin Pop
    This currently only check the size, which must be around
817 abdf0113 Iustin Pop
    128MiB.
818 a8083063 Iustin Pop

819 a8083063 Iustin Pop
    """
820 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
821 abdf0113 Iustin Pop
    if result.failed:
822 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
823 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
824 a8083063 Iustin Pop
    try:
825 abdf0113 Iustin Pop
      sectors = int(result.stdout)
826 abdf0113 Iustin Pop
    except ValueError:
827 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
828 abdf0113 Iustin Pop
    bytes = sectors * 512
829 abdf0113 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
830 9c793cfb Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
831 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
832 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
833 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
834 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
835 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
836 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
837 1dc10972 Iustin Pop
    if bytes > 1024 * 1024 * 1024:
838 9c793cfb Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
839 a8083063 Iustin Pop
840 abdf0113 Iustin Pop
  def Rename(self, new_id):
841 abdf0113 Iustin Pop
    """Rename a device.
842 a8083063 Iustin Pop

843 abdf0113 Iustin Pop
    This is not supported for drbd devices.
844 a8083063 Iustin Pop

845 a8083063 Iustin Pop
    """
846 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
847 a8083063 Iustin Pop
848 f3e513ad Iustin Pop
849 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
850 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
851 a2cfdea2 Iustin Pop

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

856 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
857 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
858 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
859 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
860 a2cfdea2 Iustin Pop

861 a2cfdea2 Iustin Pop
  """
862 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
863 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
864 a2cfdea2 Iustin Pop
865 cf8df3f3 Iustin Pop
  # timeout constants
866 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
867 cf8df3f3 Iustin Pop
868 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
869 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
870 fc1dc9d7 Iustin Pop
      children = []
871 464f8daf Iustin Pop
    super(DRBD8, self).__init__(unique_id, children, size)
872 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
873 c3f9340c Guido Trotter
    version = self._GetVersion()
874 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
875 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
876 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
877 82463074 Iustin Pop
                  version['k_major'], version['k_minor'])
878 a2cfdea2 Iustin Pop
879 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
880 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
881 f9518d38 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
882 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
883 ffa1c0dc Iustin Pop
    (self._lhost, self._lport,
884 ffa1c0dc Iustin Pop
     self._rhost, self._rport,
885 f9518d38 Iustin Pop
     self._aminor, self._secret) = unique_id
886 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
887 ffa1c0dc Iustin Pop
        self._lport == self._rport):
888 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
889 ffa1c0dc Iustin Pop
                       (unique_id,))
890 a2cfdea2 Iustin Pop
    self.Attach()
891 a2cfdea2 Iustin Pop
892 a2cfdea2 Iustin Pop
  @classmethod
893 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
894 a2cfdea2 Iustin Pop
    """Initialize a meta device.
895 a2cfdea2 Iustin Pop

896 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
897 a2cfdea2 Iustin Pop

898 a2cfdea2 Iustin Pop
    """
899 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
900 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
901 a2cfdea2 Iustin Pop
    if result.failed:
902 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
903 a2cfdea2 Iustin Pop
904 a2cfdea2 Iustin Pop
  @classmethod
905 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
906 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
907 a2cfdea2 Iustin Pop

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

911 a2cfdea2 Iustin Pop
    """
912 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
913 a2cfdea2 Iustin Pop
914 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
915 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
916 a2cfdea2 Iustin Pop
    highest = None
917 a2cfdea2 Iustin Pop
    for line in data:
918 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
919 a2cfdea2 Iustin Pop
      if match:
920 a2cfdea2 Iustin Pop
        return int(match.group(1))
921 a2cfdea2 Iustin Pop
      match = used_line.match(line)
922 a2cfdea2 Iustin Pop
      if match:
923 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
924 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
925 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
926 a2cfdea2 Iustin Pop
      return 0
927 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
928 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
929 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
930 a2cfdea2 Iustin Pop
    return highest + 1
931 a2cfdea2 Iustin Pop
932 a2cfdea2 Iustin Pop
  @classmethod
933 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
934 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
935 a2cfdea2 Iustin Pop

936 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
937 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
938 a2cfdea2 Iustin Pop

939 a2cfdea2 Iustin Pop
    """
940 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
941 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
942 a2cfdea2 Iustin Pop
943 a2cfdea2 Iustin Pop
    # pyparsing setup
944 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
945 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
946 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
947 a2cfdea2 Iustin Pop
    # this also converts the value to an int
948 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
949 a2cfdea2 Iustin Pop
950 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
951 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
952 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
953 a2cfdea2 Iustin Pop
954 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
955 a2cfdea2 Iustin Pop
956 a2cfdea2 Iustin Pop
    # value types
957 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
958 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
959 34e71fea Karsten Keil
    addr_type = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
960 34e71fea Karsten Keil
                 pyp.Optional(pyp.Literal("ipv6")).suppress())
961 34e71fea Karsten Keil
    addr_port = (addr_type + pyp.Word(pyp.nums + '.') +
962 34e71fea Karsten Keil
                 pyp.Literal(':').suppress() + number)
963 a2cfdea2 Iustin Pop
    # meta device, extended syntax
964 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
965 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
966 01e2ce3a Iustin Pop
    # device name, extended syntax
967 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
968 a2cfdea2 Iustin Pop
969 a2cfdea2 Iustin Pop
    # a statement
970 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
971 01e2ce3a Iustin Pop
            pyp.Optional(addr_port ^ value ^ quoted ^ meta_value ^
972 01e2ce3a Iustin Pop
                         device_value) +
973 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
974 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
975 a2cfdea2 Iustin Pop
976 a2cfdea2 Iustin Pop
    # an entire section
977 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
978 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
979 a2cfdea2 Iustin Pop
980 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
981 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
982 a2cfdea2 Iustin Pop
983 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
984 a2cfdea2 Iustin Pop
985 a2cfdea2 Iustin Pop
    return bnf
986 a2cfdea2 Iustin Pop
987 a2cfdea2 Iustin Pop
  @classmethod
988 3840729d Iustin Pop
  def _GetShowData(cls, minor):
989 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
990 a2cfdea2 Iustin Pop

991 a2cfdea2 Iustin Pop
    """
992 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
993 a2cfdea2 Iustin Pop
    if result.failed:
994 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
995 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
996 3840729d Iustin Pop
      return None
997 3840729d Iustin Pop
    return result.stdout
998 3840729d Iustin Pop
999 3840729d Iustin Pop
  @classmethod
1000 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1001 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1002 3840729d Iustin Pop

1003 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1004 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1005 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1006 3840729d Iustin Pop
    by _GetShowData.
1007 3840729d Iustin Pop

1008 3840729d Iustin Pop
    """
1009 3840729d Iustin Pop
    data = {}
1010 a2cfdea2 Iustin Pop
    if not out:
1011 a2cfdea2 Iustin Pop
      return data
1012 a2cfdea2 Iustin Pop
1013 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1014 a2cfdea2 Iustin Pop
    # run pyparse
1015 a2cfdea2 Iustin Pop
1016 a2cfdea2 Iustin Pop
    try:
1017 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1018 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1019 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1020 a2cfdea2 Iustin Pop
1021 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1022 a2cfdea2 Iustin Pop
    for section in results:
1023 a2cfdea2 Iustin Pop
      sname = section[0]
1024 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1025 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1026 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1027 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1028 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1029 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1030 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1031 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1032 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1033 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1034 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1035 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1036 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1037 a2cfdea2 Iustin Pop
    return data
1038 a2cfdea2 Iustin Pop
1039 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1040 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1041 a2cfdea2 Iustin Pop

1042 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1043 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1044 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1045 a2cfdea2 Iustin Pop
    device.
1046 a2cfdea2 Iustin Pop

1047 a2cfdea2 Iustin Pop
    """
1048 b00b95dd Iustin Pop
    if self._children:
1049 b00b95dd Iustin Pop
      backend, meta = self._children
1050 b00b95dd Iustin Pop
    else:
1051 b00b95dd Iustin Pop
      backend = meta = None
1052 b00b95dd Iustin Pop
1053 a2cfdea2 Iustin Pop
    if backend is not None:
1054 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1055 a2cfdea2 Iustin Pop
    else:
1056 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1057 b00b95dd Iustin Pop
1058 a2cfdea2 Iustin Pop
    if meta is not None:
1059 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1060 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1061 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1062 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1063 a2cfdea2 Iustin Pop
    else:
1064 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1065 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1066 a2cfdea2 Iustin Pop
    return retval
1067 a2cfdea2 Iustin Pop
1068 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1069 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1070 a2cfdea2 Iustin Pop

1071 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1072 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1073 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1074 a2cfdea2 Iustin Pop
    device.
1075 a2cfdea2 Iustin Pop

1076 a2cfdea2 Iustin Pop
    """
1077 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1078 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1079 a2cfdea2 Iustin Pop
      return True
1080 a2cfdea2 Iustin Pop
1081 a2cfdea2 Iustin Pop
    if self._lhost is None:
1082 a2cfdea2 Iustin Pop
      return False
1083 a2cfdea2 Iustin Pop
1084 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1085 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1086 a2cfdea2 Iustin Pop
      return False
1087 a2cfdea2 Iustin Pop
1088 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1089 a2cfdea2 Iustin Pop
    retval = (retval and
1090 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1091 a2cfdea2 Iustin Pop
    return retval
1092 a2cfdea2 Iustin Pop
1093 a2cfdea2 Iustin Pop
  @classmethod
1094 f069addf Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta, size):
1095 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1096 a2cfdea2 Iustin Pop

1097 a2cfdea2 Iustin Pop
    """
1098 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1099 f069addf Iustin Pop
            backend, meta, "0",
1100 f069addf Iustin Pop
            "-d", "%sm" % size,
1101 f069addf Iustin Pop
            "-e", "detach",
1102 f069addf Iustin Pop
            "--create-device"]
1103 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1104 a2cfdea2 Iustin Pop
    if result.failed:
1105 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1106 a2cfdea2 Iustin Pop
1107 a2cfdea2 Iustin Pop
  @classmethod
1108 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1109 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1110 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1111 a2cfdea2 Iustin Pop

1112 a2cfdea2 Iustin Pop
    """
1113 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1114 52857176 Iustin Pop
    if None in net_info:
1115 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1116 52857176 Iustin Pop
      # sure its shutdown
1117 1063abd1 Iustin Pop
      cls._ShutdownNet(minor)
1118 1063abd1 Iustin Pop
      return
1119 52857176 Iustin Pop
1120 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1121 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1122 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1123 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1124 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1125 7d585316 Iustin Pop
    # about its peer.
1126 7d585316 Iustin Pop
    cls._SetMinorSyncSpeed(minor, constants.SYNC_SPEED)
1127 7d585316 Iustin Pop
1128 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1129 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1130 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1131 f38478b2 Iustin Pop
            "-B", "consensus",
1132 ab6cc81c Iustin Pop
            "--create-device",
1133 f38478b2 Iustin Pop
            ]
1134 a2cfdea2 Iustin Pop
    if dual_pri:
1135 a2cfdea2 Iustin Pop
      args.append("-m")
1136 a2cfdea2 Iustin Pop
    if hmac and secret:
1137 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1138 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1139 a2cfdea2 Iustin Pop
    if result.failed:
1140 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1141 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1142 a2cfdea2 Iustin Pop
1143 a2cfdea2 Iustin Pop
    timeout = time.time() + 10
1144 a2cfdea2 Iustin Pop
    ok = False
1145 a2cfdea2 Iustin Pop
    while time.time() < timeout:
1146 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1147 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1148 a2cfdea2 Iustin Pop
        time.sleep(1)
1149 a2cfdea2 Iustin Pop
        continue
1150 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1151 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1152 a2cfdea2 Iustin Pop
        time.sleep(1)
1153 a2cfdea2 Iustin Pop
        continue
1154 a2cfdea2 Iustin Pop
      ok = True
1155 a2cfdea2 Iustin Pop
      break
1156 a2cfdea2 Iustin Pop
    if not ok:
1157 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1158 a2cfdea2 Iustin Pop
1159 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1160 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1161 b00b95dd Iustin Pop

1162 b00b95dd Iustin Pop
    """
1163 b00b95dd Iustin Pop
    if self.minor is None:
1164 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1165 82463074 Iustin Pop
                  self._aminor)
1166 b00b95dd Iustin Pop
    if len(devices) != 2:
1167 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1168 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1169 03ece5f3 Iustin Pop
    if "local_dev" in info:
1170 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1171 b00b95dd Iustin Pop
    backend, meta = devices
1172 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1173 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1174 b00b95dd Iustin Pop
    backend.Open()
1175 b00b95dd Iustin Pop
    meta.Open()
1176 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1177 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1178 b00b95dd Iustin Pop
1179 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1180 b00b95dd Iustin Pop
    self._children = devices
1181 b00b95dd Iustin Pop
1182 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1183 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1184 b00b95dd Iustin Pop

1185 b00b95dd Iustin Pop
    """
1186 b00b95dd Iustin Pop
    if self.minor is None:
1187 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1188 82463074 Iustin Pop
                  self._aminor)
1189 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1190 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1191 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1192 03ece5f3 Iustin Pop
      return
1193 b00b95dd Iustin Pop
    if len(self._children) != 2:
1194 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1195 82463074 Iustin Pop
                  self._children)
1196 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1197 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1198 e739bd57 Iustin Pop
      return
1199 b00b95dd Iustin Pop
    if len(devices) != 2:
1200 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1201 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1202 e739bd57 Iustin Pop
      if dev != child.dev_path:
1203 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1204 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1205 b00b95dd Iustin Pop
1206 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1207 b00b95dd Iustin Pop
    self._children = []
1208 b00b95dd Iustin Pop
1209 7d585316 Iustin Pop
  @classmethod
1210 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1211 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1212 a2cfdea2 Iustin Pop

1213 7d585316 Iustin Pop
    This is the low-level implementation.
1214 7d585316 Iustin Pop

1215 7d585316 Iustin Pop
    @type minor: int
1216 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1217 7d585316 Iustin Pop
    @type kbytes: int
1218 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1219 7d585316 Iustin Pop
    @rtype: boolean
1220 7d585316 Iustin Pop
    @return: the success of the operation
1221 7d585316 Iustin Pop

1222 a2cfdea2 Iustin Pop
    """
1223 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1224 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1225 a2cfdea2 Iustin Pop
    if result.failed:
1226 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1227 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1228 7d585316 Iustin Pop
    return not result.failed
1229 7d585316 Iustin Pop
1230 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1231 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1232 7d585316 Iustin Pop

1233 7d585316 Iustin Pop
    @type kbytes: int
1234 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1235 7d585316 Iustin Pop
    @rtype: boolean
1236 7d585316 Iustin Pop
    @return: the success of the operation
1237 7d585316 Iustin Pop

1238 7d585316 Iustin Pop
    """
1239 7d585316 Iustin Pop
    if self.minor is None:
1240 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1241 7d585316 Iustin Pop
      return False
1242 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1243 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1244 a2cfdea2 Iustin Pop
1245 6b90c22e Iustin Pop
  def GetProcStatus(self):
1246 6b90c22e Iustin Pop
    """Return device data from /proc.
1247 6b90c22e Iustin Pop

1248 6b90c22e Iustin Pop
    """
1249 6b90c22e Iustin Pop
    if self.minor is None:
1250 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1251 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1252 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1253 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1254 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1255 6b90c22e Iustin Pop
1256 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1257 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1258 a2cfdea2 Iustin Pop

1259 a2cfdea2 Iustin Pop

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

1264 0834c866 Iustin Pop

1265 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1266 0834c866 Iustin Pop
    network not connected or local disk missing.
1267 0834c866 Iustin Pop

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

1271 c41eea6e Iustin Pop
    @rtype: tuple
1272 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
1273 c41eea6e Iustin Pop

1274 a2cfdea2 Iustin Pop
    """
1275 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1276 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1277 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1278 6b90c22e Iustin Pop
    ldisk = not stats.is_disk_uptodate
1279 6b90c22e Iustin Pop
    is_degraded = not stats.is_connected
1280 6b90c22e Iustin Pop
    return stats.sync_percent, stats.est_time, is_degraded or ldisk, ldisk
1281 a2cfdea2 Iustin Pop
1282 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1283 a2cfdea2 Iustin Pop
    """Make the local state primary.
1284 a2cfdea2 Iustin Pop

1285 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1286 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1287 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1288 f860ff4e Guido Trotter
    is mandatory.
1289 a2cfdea2 Iustin Pop

1290 a2cfdea2 Iustin Pop
    """
1291 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1292 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1293 a2cfdea2 Iustin Pop
      return False
1294 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1295 a2cfdea2 Iustin Pop
    if force:
1296 a2cfdea2 Iustin Pop
      cmd.append("-o")
1297 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1298 a2cfdea2 Iustin Pop
    if result.failed:
1299 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1300 82463074 Iustin Pop
                  result.output)
1301 a2cfdea2 Iustin Pop
1302 a2cfdea2 Iustin Pop
  def Close(self):
1303 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1304 a2cfdea2 Iustin Pop

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

1307 a2cfdea2 Iustin Pop
    """
1308 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1309 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1310 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1311 a2cfdea2 Iustin Pop
    if result.failed:
1312 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1313 82463074 Iustin Pop
                  self.minor, result.output)
1314 a2cfdea2 Iustin Pop
1315 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1316 cf8df3f3 Iustin Pop
    """Removes network configuration.
1317 cf8df3f3 Iustin Pop

1318 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1319 cf8df3f3 Iustin Pop

1320 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1321 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1322 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1323 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1324 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1325 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1326 cf8df3f3 Iustin Pop
    exception.
1327 cf8df3f3 Iustin Pop

1328 cf8df3f3 Iustin Pop
    """
1329 cf8df3f3 Iustin Pop
    if self.minor is None:
1330 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1331 cf8df3f3 Iustin Pop
1332 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1333 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1334 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1335 cf8df3f3 Iustin Pop
1336 1063abd1 Iustin Pop
    ever_disconnected = _IgnoreError(self._ShutdownNet, self.minor)
1337 cf8df3f3 Iustin Pop
    timeout_limit = time.time() + self._NET_RECONFIG_TIMEOUT
1338 5bbd3f7f Michael Hanselmann
    sleep_time = 0.100 # we start the retry time at 100 milliseconds
1339 cf8df3f3 Iustin Pop
    while time.time() < timeout_limit:
1340 cf8df3f3 Iustin Pop
      status = self.GetProcStatus()
1341 cf8df3f3 Iustin Pop
      if status.is_standalone:
1342 cf8df3f3 Iustin Pop
        break
1343 cf8df3f3 Iustin Pop
      # retry the disconnect, it seems possible that due to a
1344 cf8df3f3 Iustin Pop
      # well-time disconnect on the peer, my disconnect command might
1345 5bbd3f7f Michael Hanselmann
      # be ignored and forgotten
1346 1063abd1 Iustin Pop
      ever_disconnected = _IgnoreError(self._ShutdownNet, self.minor) or \
1347 1063abd1 Iustin Pop
                          ever_disconnected
1348 cf8df3f3 Iustin Pop
      time.sleep(sleep_time)
1349 cf8df3f3 Iustin Pop
      sleep_time = min(2, sleep_time * 1.5)
1350 cf8df3f3 Iustin Pop
1351 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1352 cf8df3f3 Iustin Pop
      if ever_disconnected:
1353 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1354 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1355 cf8df3f3 Iustin Pop
      else:
1356 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1357 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1358 cf8df3f3 Iustin Pop
1359 cf8df3f3 Iustin Pop
    reconfig_time = time.time() - timeout_limit + self._NET_RECONFIG_TIMEOUT
1360 cf8df3f3 Iustin Pop
    if reconfig_time > 15: # hardcoded alert limit
1361 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1362 82463074 Iustin Pop
                   self.minor, reconfig_time)
1363 cf8df3f3 Iustin Pop
1364 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1365 cf8df3f3 Iustin Pop
    """Reconnects the network.
1366 cf8df3f3 Iustin Pop

1367 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1368 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1369 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1370 cf8df3f3 Iustin Pop

1371 cf8df3f3 Iustin Pop
    Args:
1372 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1373 cf8df3f3 Iustin Pop

1374 cf8df3f3 Iustin Pop
    """
1375 cf8df3f3 Iustin Pop
    if self.minor is None:
1376 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1377 cf8df3f3 Iustin Pop
1378 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1379 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1380 cf8df3f3 Iustin Pop
1381 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1382 cf8df3f3 Iustin Pop
1383 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1384 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1385 cf8df3f3 Iustin Pop
1386 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1387 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1388 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1389 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1390 cf8df3f3 Iustin Pop
1391 a2cfdea2 Iustin Pop
  def Attach(self):
1392 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1393 2d0c8319 Iustin Pop

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

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

1401 2d0c8319 Iustin Pop
    """
1402 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1403 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1404 2d0c8319 Iustin Pop
      minor = self._aminor
1405 2d0c8319 Iustin Pop
    else:
1406 2d0c8319 Iustin Pop
      minor = None
1407 2d0c8319 Iustin Pop
1408 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1409 2d0c8319 Iustin Pop
    return minor is not None
1410 2d0c8319 Iustin Pop
1411 2d0c8319 Iustin Pop
  def Assemble(self):
1412 2d0c8319 Iustin Pop
    """Assemble the drbd.
1413 2d0c8319 Iustin Pop

1414 2d0c8319 Iustin Pop
    Method:
1415 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1416 2d0c8319 Iustin Pop
        our config
1417 2d0c8319 Iustin Pop
      - if not, we create it from zero
1418 2d0c8319 Iustin Pop

1419 2d0c8319 Iustin Pop
    """
1420 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1421 2d0c8319 Iustin Pop
1422 2d0c8319 Iustin Pop
    self.Attach()
1423 2d0c8319 Iustin Pop
    if self.minor is None:
1424 2d0c8319 Iustin Pop
      # local device completely unconfigured
1425 1063abd1 Iustin Pop
      self._FastAssemble()
1426 2d0c8319 Iustin Pop
    else:
1427 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1428 2d0c8319 Iustin Pop
      # the device
1429 1063abd1 Iustin Pop
      self._SlowAssemble()
1430 2d0c8319 Iustin Pop
1431 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1432 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1433 a2cfdea2 Iustin Pop

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

1438 a2cfdea2 Iustin Pop
    """
1439 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1440 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1441 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1442 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1443 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1444 1063abd1 Iustin Pop
1445 a2cfdea2 Iustin Pop
      if match_l and match_r:
1446 1063abd1 Iustin Pop
        # everything matches
1447 a2cfdea2 Iustin Pop
        break
1448 1063abd1 Iustin Pop
1449 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1450 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1451 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1452 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1453 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1454 1063abd1 Iustin Pop
          break
1455 1063abd1 Iustin Pop
        else:
1456 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1457 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1458 1063abd1 Iustin Pop
1459 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1460 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1461 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1462 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1463 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1464 1063abd1 Iustin Pop
          break
1465 1063abd1 Iustin Pop
        else:
1466 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1467 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1468 bf25af3b Iustin Pop
1469 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1470 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1471 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1472 bf25af3b Iustin Pop
      # configuration
1473 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1474 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1475 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1476 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1477 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1478 9cdbe77f Iustin Pop
        # reconnect to our correct one
1479 1063abd1 Iustin Pop
        try:
1480 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1481 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1482 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1483 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1484 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1485 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1486 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1487 9cdbe77f Iustin Pop
        # None)
1488 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1489 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1490 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1491 9cdbe77f Iustin Pop
          break
1492 1063abd1 Iustin Pop
        else:
1493 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1494 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1495 9cdbe77f Iustin Pop
1496 a2cfdea2 Iustin Pop
    else:
1497 a2cfdea2 Iustin Pop
      minor = None
1498 a2cfdea2 Iustin Pop
1499 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1500 1063abd1 Iustin Pop
    if minor is None:
1501 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1502 1063abd1 Iustin Pop
                  self._aminor)
1503 a2cfdea2 Iustin Pop
1504 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1505 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1506 a2cfdea2 Iustin Pop

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

1509 a2cfdea2 Iustin Pop
    """
1510 a1578d63 Iustin Pop
    minor = self._aminor
1511 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1512 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1513 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1514 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1515 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1516 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1517 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1518 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1519 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1520 a2cfdea2 Iustin Pop
1521 a2cfdea2 Iustin Pop
  @classmethod
1522 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1523 b00b95dd Iustin Pop
    """Detach from the local device.
1524 b00b95dd Iustin Pop

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

1528 b00b95dd Iustin Pop
    """
1529 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1530 b00b95dd Iustin Pop
    if result.failed:
1531 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1532 b00b95dd Iustin Pop
1533 b00b95dd Iustin Pop
  @classmethod
1534 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1535 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1536 f3e513ad Iustin Pop

1537 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1538 f3e513ad Iustin Pop

1539 f3e513ad Iustin Pop
    """
1540 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1541 a8459f1c Iustin Pop
    if result.failed:
1542 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1543 f3e513ad Iustin Pop
1544 f3e513ad Iustin Pop
  @classmethod
1545 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1546 a2cfdea2 Iustin Pop
    """Deactivate the device.
1547 a2cfdea2 Iustin Pop

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

1550 a2cfdea2 Iustin Pop
    """
1551 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1552 a2cfdea2 Iustin Pop
    if result.failed:
1553 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1554 33bc6f01 Iustin Pop
                  minor, result.output)
1555 a2cfdea2 Iustin Pop
1556 a2cfdea2 Iustin Pop
  def Shutdown(self):
1557 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1558 a2cfdea2 Iustin Pop

1559 a2cfdea2 Iustin Pop
    """
1560 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1561 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1562 746f7476 Iustin Pop
      return
1563 746f7476 Iustin Pop
    minor = self.minor
1564 a2cfdea2 Iustin Pop
    self.minor = None
1565 a2cfdea2 Iustin Pop
    self.dev_path = None
1566 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1567 a2cfdea2 Iustin Pop
1568 a2cfdea2 Iustin Pop
  def Remove(self):
1569 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1570 a2cfdea2 Iustin Pop

1571 a2cfdea2 Iustin Pop
    """
1572 0c6c04ec Iustin Pop
    self.Shutdown()
1573 a2cfdea2 Iustin Pop
1574 a2cfdea2 Iustin Pop
  @classmethod
1575 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1576 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1577 a2cfdea2 Iustin Pop

1578 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1579 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1580 a2cfdea2 Iustin Pop

1581 a2cfdea2 Iustin Pop
    """
1582 a2cfdea2 Iustin Pop
    if len(children) != 2:
1583 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1584 767d52d3 Iustin Pop
    # check that the minor is unused
1585 767d52d3 Iustin Pop
    aminor = unique_id[4]
1586 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1587 767d52d3 Iustin Pop
    if aminor in proc_info:
1588 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1589 767d52d3 Iustin Pop
      in_use = status.is_in_use
1590 767d52d3 Iustin Pop
    else:
1591 767d52d3 Iustin Pop
      in_use = False
1592 767d52d3 Iustin Pop
    if in_use:
1593 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1594 a2cfdea2 Iustin Pop
    meta = children[1]
1595 a2cfdea2 Iustin Pop
    meta.Assemble()
1596 a2cfdea2 Iustin Pop
    if not meta.Attach():
1597 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1598 33bc6f01 Iustin Pop
                  aminor, meta)
1599 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1600 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1601 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1602 a2cfdea2 Iustin Pop
1603 1005d816 Iustin Pop
  def Grow(self, amount):
1604 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1605 1005d816 Iustin Pop

1606 1005d816 Iustin Pop
    """
1607 1005d816 Iustin Pop
    if self.minor is None:
1608 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1609 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1610 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1611 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1612 1005d816 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize"])
1613 1005d816 Iustin Pop
    if result.failed:
1614 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1615 1005d816 Iustin Pop
1616 a8083063 Iustin Pop
1617 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1618 6f695a2e Manuel Franceschini
  """File device.
1619 abdf0113 Iustin Pop

1620 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1621 6f695a2e Manuel Franceschini

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

1624 6f695a2e Manuel Franceschini
  """
1625 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1626 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1627 6f695a2e Manuel Franceschini

1628 6f695a2e Manuel Franceschini
    """
1629 6f695a2e Manuel Franceschini
    if children:
1630 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1631 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1632 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1633 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1634 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1635 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1636 ecb091e3 Iustin Pop
    self.Attach()
1637 6f695a2e Manuel Franceschini
1638 6f695a2e Manuel Franceschini
  def Assemble(self):
1639 6f695a2e Manuel Franceschini
    """Assemble the device.
1640 6f695a2e Manuel Franceschini

1641 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1642 6f695a2e Manuel Franceschini

1643 6f695a2e Manuel Franceschini
    """
1644 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1645 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1646 6f695a2e Manuel Franceschini
1647 6f695a2e Manuel Franceschini
  def Shutdown(self):
1648 6f695a2e Manuel Franceschini
    """Shutdown the device.
1649 6f695a2e Manuel Franceschini

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

1653 6f695a2e Manuel Franceschini
    """
1654 746f7476 Iustin Pop
    pass
1655 6f695a2e Manuel Franceschini
1656 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1657 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1658 6f695a2e Manuel Franceschini

1659 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1660 6f695a2e Manuel Franceschini

1661 6f695a2e Manuel Franceschini
    """
1662 6f695a2e Manuel Franceschini
    pass
1663 6f695a2e Manuel Franceschini
1664 6f695a2e Manuel Franceschini
  def Close(self):
1665 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1666 6f695a2e Manuel Franceschini

1667 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1668 6f695a2e Manuel Franceschini

1669 6f695a2e Manuel Franceschini
    """
1670 6f695a2e Manuel Franceschini
    pass
1671 6f695a2e Manuel Franceschini
1672 6f695a2e Manuel Franceschini
  def Remove(self):
1673 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1674 6f695a2e Manuel Franceschini

1675 c41eea6e Iustin Pop
    @rtype: boolean
1676 c41eea6e Iustin Pop
    @return: True if the removal was successful
1677 6f695a2e Manuel Franceschini

1678 6f695a2e Manuel Franceschini
    """
1679 6f695a2e Manuel Franceschini
    try:
1680 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1681 6f695a2e Manuel Franceschini
    except OSError, err:
1682 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1683 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1684 6f695a2e Manuel Franceschini
1685 6f695a2e Manuel Franceschini
  def Attach(self):
1686 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1687 6f695a2e Manuel Franceschini

1688 6f695a2e Manuel Franceschini
    Check if this file already exists.
1689 6f695a2e Manuel Franceschini

1690 c41eea6e Iustin Pop
    @rtype: boolean
1691 c41eea6e Iustin Pop
    @return: True if file exists
1692 6f695a2e Manuel Franceschini

1693 6f695a2e Manuel Franceschini
    """
1694 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1695 ecb091e3 Iustin Pop
    return self.attached
1696 6f695a2e Manuel Franceschini
1697 6f695a2e Manuel Franceschini
  @classmethod
1698 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1699 6f695a2e Manuel Franceschini
    """Create a new file.
1700 6f695a2e Manuel Franceschini

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

1703 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
1704 c41eea6e Iustin Pop
    @return: an instance of FileStorage
1705 6f695a2e Manuel Franceschini

1706 6f695a2e Manuel Franceschini
    """
1707 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1708 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1709 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1710 aed77cea Guido Trotter
    if os.path.exists(dev_path):
1711 aed77cea Guido Trotter
      _ThrowError("File already existing: %s", dev_path)
1712 6f695a2e Manuel Franceschini
    try:
1713 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
1714 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1715 6f695a2e Manuel Franceschini
      f.close()
1716 6c626518 Iustin Pop
    except IOError, err:
1717 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
1718 6f695a2e Manuel Franceschini
1719 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
1720 6f695a2e Manuel Franceschini
1721 6f695a2e Manuel Franceschini
1722 a8083063 Iustin Pop
DEV_MAP = {
1723 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1724 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1725 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
1726 a8083063 Iustin Pop
  }
1727 a8083063 Iustin Pop
1728 a8083063 Iustin Pop
1729 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
1730 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1731 a8083063 Iustin Pop

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

1735 a8083063 Iustin Pop
  """
1736 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1737 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1738 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1739 cb999543 Iustin Pop
  if not device.attached:
1740 a8083063 Iustin Pop
    return None
1741 ecb091e3 Iustin Pop
  return device
1742 a8083063 Iustin Pop
1743 a8083063 Iustin Pop
1744 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
1745 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1746 a8083063 Iustin Pop

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

1750 a8083063 Iustin Pop
  """
1751 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1752 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1753 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1754 1063abd1 Iustin Pop
  device.Assemble()
1755 a8083063 Iustin Pop
  return device
1756 a8083063 Iustin Pop
1757 a8083063 Iustin Pop
1758 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1759 a8083063 Iustin Pop
  """Create a device.
1760 a8083063 Iustin Pop

1761 a8083063 Iustin Pop
  """
1762 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1763 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1764 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1765 a8083063 Iustin Pop
  return device