Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ fe96220b

History | View | Annotate | Download (45.8 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 a8083063 Iustin Pop
28 a8083063 Iustin Pop
from ganeti import utils
29 a8083063 Iustin Pop
from ganeti import logger
30 a8083063 Iustin Pop
from ganeti import errors
31 fe96220b Iustin Pop
from ganeti import constants
32 a8083063 Iustin Pop
33 a8083063 Iustin Pop
34 a8083063 Iustin Pop
class BlockDev(object):
35 a8083063 Iustin Pop
  """Block device abstract class.
36 a8083063 Iustin Pop

37 a8083063 Iustin Pop
  A block device can be in the following states:
38 a8083063 Iustin Pop
    - not existing on the system, and by `Create()` it goes into:
39 a8083063 Iustin Pop
    - existing but not setup/not active, and by `Assemble()` goes into:
40 a8083063 Iustin Pop
    - active read-write and by `Open()` it goes into
41 a8083063 Iustin Pop
    - online (=used, or ready for use)
42 a8083063 Iustin Pop

43 a8083063 Iustin Pop
  A device can also be online but read-only, however we are not using
44 a8083063 Iustin Pop
  the readonly state (MD and LV have it, if needed in the future)
45 a8083063 Iustin Pop
  and we are usually looking at this like at a stack, so it's easier
46 a8083063 Iustin Pop
  to conceptualise the transition from not-existing to online and back
47 a8083063 Iustin Pop
  like a linear one.
48 a8083063 Iustin Pop

49 a8083063 Iustin Pop
  The many different states of the device are due to the fact that we
50 a8083063 Iustin Pop
  need to cover many device types:
51 a8083063 Iustin Pop
    - logical volumes are created, lvchange -a y $lv, and used
52 a8083063 Iustin Pop
    - md arrays are created or assembled and used
53 a8083063 Iustin Pop
    - drbd devices are attached to a local disk/remote peer and made primary
54 a8083063 Iustin Pop

55 a8083063 Iustin Pop
  The status of the device can be examined by `GetStatus()`, which
56 a8083063 Iustin Pop
  returns a numerical value, depending on the position in the
57 a8083063 Iustin Pop
  transition stack of the device.
58 a8083063 Iustin Pop

59 a8083063 Iustin Pop
  A block device is identified by three items:
60 a8083063 Iustin Pop
    - the /dev path of the device (dynamic)
61 a8083063 Iustin Pop
    - a unique ID of the device (static)
62 a8083063 Iustin Pop
    - it's major/minor pair (dynamic)
63 a8083063 Iustin Pop

64 a8083063 Iustin Pop
  Not all devices implement both the first two as distinct items. LVM
65 a8083063 Iustin Pop
  logical volumes have their unique ID (the pair volume group, logical
66 a8083063 Iustin Pop
  volume name) in a 1-to-1 relation to the dev path. For MD devices,
67 a8083063 Iustin Pop
  the /dev path is dynamic and the unique ID is the UUID generated at
68 a8083063 Iustin Pop
  array creation plus the slave list. For DRBD devices, the /dev path
69 a8083063 Iustin Pop
  is again dynamic and the unique id is the pair (host1, dev1),
70 a8083063 Iustin Pop
  (host2, dev2).
71 a8083063 Iustin Pop

72 a8083063 Iustin Pop
  You can get to a device in two ways:
73 a8083063 Iustin Pop
    - creating the (real) device, which returns you
74 a8083063 Iustin Pop
      an attached instance (lvcreate, mdadm --create)
75 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
76 a8083063 Iustin Pop

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

83 a8083063 Iustin Pop
  """
84 a8083063 Iustin Pop
  STATUS_UNKNOWN = 0
85 a8083063 Iustin Pop
  STATUS_EXISTING = 1
86 a8083063 Iustin Pop
  STATUS_STANDBY = 2
87 a8083063 Iustin Pop
  STATUS_ONLINE = 3
88 a8083063 Iustin Pop
89 a8083063 Iustin Pop
  STATUS_MAP = {
90 a8083063 Iustin Pop
    STATUS_UNKNOWN: "unknown",
91 a8083063 Iustin Pop
    STATUS_EXISTING: "existing",
92 a8083063 Iustin Pop
    STATUS_STANDBY: "ready for use",
93 a8083063 Iustin Pop
    STATUS_ONLINE: "online",
94 a8083063 Iustin Pop
    }
95 a8083063 Iustin Pop
96 a8083063 Iustin Pop
97 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
98 a8083063 Iustin Pop
    self._children = children
99 a8083063 Iustin Pop
    self.dev_path = None
100 a8083063 Iustin Pop
    self.unique_id = unique_id
101 a8083063 Iustin Pop
    self.major = None
102 a8083063 Iustin Pop
    self.minor = None
103 a8083063 Iustin Pop
104 a8083063 Iustin Pop
105 a8083063 Iustin Pop
  def Assemble(self):
106 a8083063 Iustin Pop
    """Assemble the device from its components.
107 a8083063 Iustin Pop

108 a8083063 Iustin Pop
    If this is a plain block device (e.g. LVM) than assemble does
109 a8083063 Iustin Pop
    nothing, as the LVM has no children and we don't put logical
110 a8083063 Iustin Pop
    volumes offline.
111 a8083063 Iustin Pop

112 a8083063 Iustin Pop
    One guarantee is that after the device has been assembled, it
113 a8083063 Iustin Pop
    knows its major/minor numbers. This allows other devices (usually
114 a8083063 Iustin Pop
    parents) to probe correctly for their children.
115 a8083063 Iustin Pop

116 a8083063 Iustin Pop
    """
117 a8083063 Iustin Pop
    status = True
118 a8083063 Iustin Pop
    for child in self._children:
119 a8083063 Iustin Pop
      if not isinstance(child, BlockDev):
120 a8083063 Iustin Pop
        raise TypeError("Invalid child passed of type '%s'" % type(child))
121 a8083063 Iustin Pop
      if not status:
122 a8083063 Iustin Pop
        break
123 a8083063 Iustin Pop
      status = status and child.Assemble()
124 a8083063 Iustin Pop
      if not status:
125 a8083063 Iustin Pop
        break
126 a8083063 Iustin Pop
      status = status and child.Open()
127 a8083063 Iustin Pop
128 a8083063 Iustin Pop
    if not status:
129 a8083063 Iustin Pop
      for child in self._children:
130 a8083063 Iustin Pop
        child.Shutdown()
131 a8083063 Iustin Pop
    return status
132 a8083063 Iustin Pop
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
141 a8083063 Iustin Pop
  def Close(self):
142 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
143 a8083063 Iustin Pop

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

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

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

159 a8083063 Iustin Pop
    """
160 a8083063 Iustin Pop
    raise NotImplementedError
161 a8083063 Iustin Pop
162 a8083063 Iustin Pop
163 a8083063 Iustin Pop
  def Remove(self):
164 a8083063 Iustin Pop
    """Remove this device.
165 a8083063 Iustin Pop

166 a8083063 Iustin Pop
    This makes sense only for some of the device types: LV and to a
167 a8083063 Iustin Pop
    lesser degree, md devices. Also note that if the device can't
168 a8083063 Iustin Pop
    attach, the removal can't be completed.
169 a8083063 Iustin Pop

170 a8083063 Iustin Pop
    """
171 a8083063 Iustin Pop
    raise NotImplementedError
172 a8083063 Iustin Pop
173 a8083063 Iustin Pop
174 a8083063 Iustin Pop
  def GetStatus(self):
175 a8083063 Iustin Pop
    """Return the status of the device.
176 a8083063 Iustin Pop

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

184 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
185 a8083063 Iustin Pop
    devices need this.
186 a8083063 Iustin Pop

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

190 a8083063 Iustin Pop
    """
191 a8083063 Iustin Pop
    raise NotImplementedError
192 a8083063 Iustin Pop
193 a8083063 Iustin Pop
194 a8083063 Iustin Pop
  def Shutdown(self):
195 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
196 a8083063 Iustin Pop

197 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
198 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
199 a8083063 Iustin Pop
    assembled after this call.
200 a8083063 Iustin Pop

201 a8083063 Iustin Pop
    """
202 a8083063 Iustin Pop
    raise NotImplementedError
203 a8083063 Iustin Pop
204 a8083063 Iustin Pop
205 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
206 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
207 a8083063 Iustin Pop

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

210 a8083063 Iustin Pop
    """
211 a8083063 Iustin Pop
    result = True
212 a8083063 Iustin Pop
    if self._children:
213 a8083063 Iustin Pop
      for child in self._children:
214 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
215 a8083063 Iustin Pop
    return result
216 a8083063 Iustin Pop
217 a8083063 Iustin Pop
218 a8083063 Iustin Pop
  def GetSyncStatus(self):
219 a8083063 Iustin Pop
    """Returns the sync status of the device.
220 a8083063 Iustin Pop

221 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
222 a8083063 Iustin Pop
    status of the mirror.
223 a8083063 Iustin Pop

224 a8083063 Iustin Pop
    Returns:
225 a8083063 Iustin Pop
     (sync_percent, estimated_time, is_degraded)
226 a8083063 Iustin Pop

227 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
228 a8083063 Iustin Pop
    If estimated_time is None, it means we can't estimate
229 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
230 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
231 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
232 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
233 a8083063 Iustin Pop

234 a8083063 Iustin Pop
    """
235 a8083063 Iustin Pop
    return None, None, 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 a8083063 Iustin Pop
    min_percent, max_time, is_degraded = self.GetSyncStatus()
247 a8083063 Iustin Pop
    if self._children:
248 a8083063 Iustin Pop
      for child in self._children:
249 a8083063 Iustin Pop
        c_percent, c_time, c_degraded = 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 a8083063 Iustin Pop
    return min_percent, max_time, is_degraded
260 a8083063 Iustin Pop
261 a8083063 Iustin Pop
262 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
263 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
264 a0c3fea1 Michael Hanselmann

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

267 a0c3fea1 Michael Hanselmann
    """
268 a0c3fea1 Michael Hanselmann
    for child in self._children:
269 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
270 a0c3fea1 Michael Hanselmann
271 a0c3fea1 Michael Hanselmann
272 a8083063 Iustin Pop
  def __repr__(self):
273 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
274 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
275 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
276 a8083063 Iustin Pop
277 a8083063 Iustin Pop
278 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
279 a8083063 Iustin Pop
  """Logical Volume block device.
280 a8083063 Iustin Pop

281 a8083063 Iustin Pop
  """
282 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
283 a8083063 Iustin Pop
    """Attaches to a LV device.
284 a8083063 Iustin Pop

285 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
286 a8083063 Iustin Pop

287 a8083063 Iustin Pop
    """
288 a8083063 Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children)
289 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
290 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
291 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
292 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
293 a8083063 Iustin Pop
    self.Attach()
294 a8083063 Iustin Pop
295 a8083063 Iustin Pop
296 a8083063 Iustin Pop
  @classmethod
297 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
298 a8083063 Iustin Pop
    """Create a new logical volume.
299 a8083063 Iustin Pop

300 a8083063 Iustin Pop
    """
301 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
302 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
303 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
304 a8083063 Iustin Pop
    pvs_info = cls.GetPVInfo(vg_name)
305 a8083063 Iustin Pop
    if not pvs_info:
306 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
307 3ecf6786 Iustin Pop
                                    vg_name)
308 a8083063 Iustin Pop
    pvs_info.sort()
309 a8083063 Iustin Pop
    pvs_info.reverse()
310 5b7b5d49 Guido Trotter
311 5b7b5d49 Guido Trotter
    pvlist = [ pv[1] for pv in pvs_info ]
312 5b7b5d49 Guido Trotter
    free_size = sum([ pv[0] for pv in pvs_info ])
313 5b7b5d49 Guido Trotter
314 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
315 5b7b5d49 Guido Trotter
    # calling the create function.
316 a8083063 Iustin Pop
    if free_size < size:
317 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Not enough free space: required %s,"
318 3ecf6786 Iustin Pop
                                    " available %s" % (size, free_size))
319 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-n%s" % lv_name,
320 5b7b5d49 Guido Trotter
                           vg_name] + pvlist)
321 a8083063 Iustin Pop
    if result.failed:
322 a8083063 Iustin Pop
      raise errors.BlockDeviceError(result.fail_reason)
323 a8083063 Iustin Pop
    return LogicalVolume(unique_id, children)
324 a8083063 Iustin Pop
325 a8083063 Iustin Pop
  @staticmethod
326 a8083063 Iustin Pop
  def GetPVInfo(vg_name):
327 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
328 a8083063 Iustin Pop

329 a8083063 Iustin Pop
    Args:
330 a8083063 Iustin Pop
      vg_name: the volume group name
331 a8083063 Iustin Pop

332 a8083063 Iustin Pop
    Returns:
333 a8083063 Iustin Pop
      list of (free_space, name) with free_space in mebibytes
334 098c0958 Michael Hanselmann

335 a8083063 Iustin Pop
    """
336 a8083063 Iustin Pop
    command = ["pvs", "--noheadings", "--nosuffix", "--units=m",
337 a8083063 Iustin Pop
               "-opv_name,vg_name,pv_free,pv_attr", "--unbuffered",
338 a8083063 Iustin Pop
               "--separator=:"]
339 a8083063 Iustin Pop
    result = utils.RunCmd(command)
340 a8083063 Iustin Pop
    if result.failed:
341 a8083063 Iustin Pop
      logger.Error("Can't get the PV information: %s" % result.fail_reason)
342 a8083063 Iustin Pop
      return None
343 a8083063 Iustin Pop
    data = []
344 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
345 a8083063 Iustin Pop
      fields = line.strip().split(':')
346 a8083063 Iustin Pop
      if len(fields) != 4:
347 a8083063 Iustin Pop
        logger.Error("Can't parse pvs output: line '%s'" % line)
348 a8083063 Iustin Pop
        return None
349 a8083063 Iustin Pop
      # skip over pvs from another vg or ones which are not allocatable
350 a8083063 Iustin Pop
      if fields[1] != vg_name or fields[3][0] != 'a':
351 a8083063 Iustin Pop
        continue
352 a8083063 Iustin Pop
      data.append((float(fields[2]), fields[0]))
353 a8083063 Iustin Pop
354 a8083063 Iustin Pop
    return data
355 a8083063 Iustin Pop
356 a8083063 Iustin Pop
  def Remove(self):
357 a8083063 Iustin Pop
    """Remove this logical volume.
358 a8083063 Iustin Pop

359 a8083063 Iustin Pop
    """
360 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
361 a8083063 Iustin Pop
      # the LV does not exist
362 a8083063 Iustin Pop
      return True
363 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
364 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
365 a8083063 Iustin Pop
    if result.failed:
366 a8083063 Iustin Pop
      logger.Error("Can't lvremove: %s" % result.fail_reason)
367 a8083063 Iustin Pop
368 a8083063 Iustin Pop
    return not result.failed
369 a8083063 Iustin Pop
370 a8083063 Iustin Pop
371 a8083063 Iustin Pop
  def Attach(self):
372 a8083063 Iustin Pop
    """Attach to an existing LV.
373 a8083063 Iustin Pop

374 a8083063 Iustin Pop
    This method will try to see if an existing and active LV exists
375 a8083063 Iustin Pop
    which matches the our name. If so, its major/minor will be
376 a8083063 Iustin Pop
    recorded.
377 a8083063 Iustin Pop

378 a8083063 Iustin Pop
    """
379 a8083063 Iustin Pop
    result = utils.RunCmd(["lvdisplay", self.dev_path])
380 a8083063 Iustin Pop
    if result.failed:
381 a8083063 Iustin Pop
      logger.Error("Can't find LV %s: %s" %
382 a8083063 Iustin Pop
                   (self.dev_path, result.fail_reason))
383 a8083063 Iustin Pop
      return False
384 a8083063 Iustin Pop
    match = re.compile("^ *Block device *([0-9]+):([0-9]+).*$")
385 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
386 a8083063 Iustin Pop
      match_result = match.match(line)
387 a8083063 Iustin Pop
      if match_result:
388 a8083063 Iustin Pop
        self.major = int(match_result.group(1))
389 a8083063 Iustin Pop
        self.minor = int(match_result.group(2))
390 a8083063 Iustin Pop
        return True
391 a8083063 Iustin Pop
    return False
392 a8083063 Iustin Pop
393 a8083063 Iustin Pop
394 a8083063 Iustin Pop
  def Assemble(self):
395 a8083063 Iustin Pop
    """Assemble the device.
396 a8083063 Iustin Pop

397 a8083063 Iustin Pop
    This is a no-op for the LV device type. Eventually, we could
398 a8083063 Iustin Pop
    lvchange -ay here if we see that the LV is not active.
399 a8083063 Iustin Pop

400 a8083063 Iustin Pop
    """
401 a8083063 Iustin Pop
    return True
402 a8083063 Iustin Pop
403 a8083063 Iustin Pop
404 a8083063 Iustin Pop
  def Shutdown(self):
405 a8083063 Iustin Pop
    """Shutdown the device.
406 a8083063 Iustin Pop

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

410 a8083063 Iustin Pop
    """
411 a8083063 Iustin Pop
    return True
412 a8083063 Iustin Pop
413 a8083063 Iustin Pop
414 a8083063 Iustin Pop
  def GetStatus(self):
415 a8083063 Iustin Pop
    """Return the status of the device.
416 a8083063 Iustin Pop

417 a8083063 Iustin Pop
    Logical volumes will can be in all four states, although we don't
418 a8083063 Iustin Pop
    deactivate (lvchange -an) them when shutdown, so STATUS_EXISTING
419 a8083063 Iustin Pop
    should not be seen for our devices.
420 a8083063 Iustin Pop

421 a8083063 Iustin Pop
    """
422 a8083063 Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
423 a8083063 Iustin Pop
    if result.failed:
424 a8083063 Iustin Pop
      logger.Error("Can't display lv: %s" % result.fail_reason)
425 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
426 a8083063 Iustin Pop
    out = result.stdout.strip()
427 a8083063 Iustin Pop
    # format: type/permissions/alloc/fixed_minor/state/open
428 a8083063 Iustin Pop
    if len(out) != 6:
429 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
430 a8083063 Iustin Pop
    #writable = (out[1] == "w")
431 a8083063 Iustin Pop
    active = (out[4] == "a")
432 a8083063 Iustin Pop
    online = (out[5] == "o")
433 a8083063 Iustin Pop
    if online:
434 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
435 a8083063 Iustin Pop
    elif active:
436 a8083063 Iustin Pop
      retval = self.STATUS_STANDBY
437 a8083063 Iustin Pop
    else:
438 a8083063 Iustin Pop
      retval = self.STATUS_EXISTING
439 a8083063 Iustin Pop
440 a8083063 Iustin Pop
    return retval
441 a8083063 Iustin Pop
442 a8083063 Iustin Pop
443 a8083063 Iustin Pop
  def Open(self, force=False):
444 a8083063 Iustin Pop
    """Make the device ready for I/O.
445 a8083063 Iustin Pop

446 a8083063 Iustin Pop
    This is a no-op for the LV device type.
447 a8083063 Iustin Pop

448 a8083063 Iustin Pop
    """
449 a8083063 Iustin Pop
    return True
450 a8083063 Iustin Pop
451 a8083063 Iustin Pop
452 a8083063 Iustin Pop
  def Close(self):
453 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
454 a8083063 Iustin Pop

455 a8083063 Iustin Pop
    This is a no-op for the LV device type.
456 a8083063 Iustin Pop

457 a8083063 Iustin Pop
    """
458 a8083063 Iustin Pop
    return True
459 a8083063 Iustin Pop
460 a8083063 Iustin Pop
461 a8083063 Iustin Pop
  def Snapshot(self, size):
462 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
463 a8083063 Iustin Pop

464 a8083063 Iustin Pop
    """
465 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
466 a8083063 Iustin Pop
467 a8083063 Iustin Pop
    # remove existing snapshot if found
468 a8083063 Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None)
469 a8083063 Iustin Pop
    snap.Remove()
470 a8083063 Iustin Pop
471 a8083063 Iustin Pop
    pvs_info = self.GetPVInfo(self._vg_name)
472 a8083063 Iustin Pop
    if not pvs_info:
473 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
474 3ecf6786 Iustin Pop
                                    self._vg_name)
475 a8083063 Iustin Pop
    pvs_info.sort()
476 a8083063 Iustin Pop
    pvs_info.reverse()
477 a8083063 Iustin Pop
    free_size, pv_name = pvs_info[0]
478 a8083063 Iustin Pop
    if free_size < size:
479 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Not enough free space: required %s,"
480 3ecf6786 Iustin Pop
                                    " available %s" % (size, free_size))
481 a8083063 Iustin Pop
482 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
483 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
484 a8083063 Iustin Pop
    if result.failed:
485 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("command: %s error: %s" %
486 3ecf6786 Iustin Pop
                                    (result.cmd, result.fail_reason))
487 a8083063 Iustin Pop
488 a8083063 Iustin Pop
    return snap_name
489 a8083063 Iustin Pop
490 a8083063 Iustin Pop
491 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
492 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
493 a0c3fea1 Michael Hanselmann

494 a0c3fea1 Michael Hanselmann
    """
495 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
496 a0c3fea1 Michael Hanselmann
497 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
498 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
499 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
500 a0c3fea1 Michael Hanselmann
501 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
502 a0c3fea1 Michael Hanselmann
    text = text[:128]
503 a0c3fea1 Michael Hanselmann
504 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
505 a0c3fea1 Michael Hanselmann
                           self.dev_path])
506 a0c3fea1 Michael Hanselmann
    if result.failed:
507 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Command: %s error: %s" %
508 3ecf6786 Iustin Pop
                                    (result.cmd, result.fail_reason))
509 a0c3fea1 Michael Hanselmann
510 a0c3fea1 Michael Hanselmann
511 a8083063 Iustin Pop
class MDRaid1(BlockDev):
512 a8083063 Iustin Pop
  """raid1 device implemented via md.
513 a8083063 Iustin Pop

514 a8083063 Iustin Pop
  """
515 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
516 a8083063 Iustin Pop
    super(MDRaid1, self).__init__(unique_id, children)
517 a8083063 Iustin Pop
    self.major = 9
518 a8083063 Iustin Pop
    self.Attach()
519 a8083063 Iustin Pop
520 a8083063 Iustin Pop
521 a8083063 Iustin Pop
  def Attach(self):
522 a8083063 Iustin Pop
    """Find an array which matches our config and attach to it.
523 a8083063 Iustin Pop

524 a8083063 Iustin Pop
    This tries to find a MD array which has the same UUID as our own.
525 a8083063 Iustin Pop

526 a8083063 Iustin Pop
    """
527 a8083063 Iustin Pop
    minor = self._FindMDByUUID(self.unique_id)
528 a8083063 Iustin Pop
    if minor is not None:
529 a8083063 Iustin Pop
      self._SetFromMinor(minor)
530 a8083063 Iustin Pop
    else:
531 a8083063 Iustin Pop
      self.minor = None
532 a8083063 Iustin Pop
      self.dev_path = None
533 a8083063 Iustin Pop
534 a8083063 Iustin Pop
    return (minor is not None)
535 a8083063 Iustin Pop
536 a8083063 Iustin Pop
537 a8083063 Iustin Pop
  @staticmethod
538 a8083063 Iustin Pop
  def _GetUsedDevs():
539 a8083063 Iustin Pop
    """Compute the list of in-use MD devices.
540 a8083063 Iustin Pop

541 a8083063 Iustin Pop
    It doesn't matter if the used device have other raid level, just
542 a8083063 Iustin Pop
    that they are in use.
543 a8083063 Iustin Pop

544 a8083063 Iustin Pop
    """
545 a8083063 Iustin Pop
    mdstat = open("/proc/mdstat", "r")
546 a8083063 Iustin Pop
    data = mdstat.readlines()
547 a8083063 Iustin Pop
    mdstat.close()
548 a8083063 Iustin Pop
549 a8083063 Iustin Pop
    used_md = {}
550 a8083063 Iustin Pop
    valid_line = re.compile("^md([0-9]+) : .*$")
551 a8083063 Iustin Pop
    for line in data:
552 a8083063 Iustin Pop
      match = valid_line.match(line)
553 a8083063 Iustin Pop
      if match:
554 a8083063 Iustin Pop
        md_no = int(match.group(1))
555 a8083063 Iustin Pop
        used_md[md_no] = line
556 a8083063 Iustin Pop
557 a8083063 Iustin Pop
    return used_md
558 a8083063 Iustin Pop
559 a8083063 Iustin Pop
560 a8083063 Iustin Pop
  @staticmethod
561 a8083063 Iustin Pop
  def _GetDevInfo(minor):
562 a8083063 Iustin Pop
    """Get info about a MD device.
563 a8083063 Iustin Pop

564 a8083063 Iustin Pop
    Currently only uuid is returned.
565 a8083063 Iustin Pop

566 a8083063 Iustin Pop
    """
567 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-D", "/dev/md%d" % minor])
568 a8083063 Iustin Pop
    if result.failed:
569 a8083063 Iustin Pop
      logger.Error("Can't display md: %s" % result.fail_reason)
570 a8083063 Iustin Pop
      return None
571 a8083063 Iustin Pop
    retval = {}
572 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
573 a8083063 Iustin Pop
      line = line.strip()
574 a8083063 Iustin Pop
      kv = line.split(" : ", 1)
575 a8083063 Iustin Pop
      if kv:
576 a8083063 Iustin Pop
        if kv[0] == "UUID":
577 8d519422 Iustin Pop
          retval["uuid"] = kv[1].split()[0]
578 a8083063 Iustin Pop
        elif kv[0] == "State":
579 a8083063 Iustin Pop
          retval["state"] = kv[1].split(", ")
580 a8083063 Iustin Pop
    return retval
581 a8083063 Iustin Pop
582 a8083063 Iustin Pop
583 a8083063 Iustin Pop
  @staticmethod
584 a8083063 Iustin Pop
  def _FindUnusedMinor():
585 a8083063 Iustin Pop
    """Compute an unused MD minor.
586 a8083063 Iustin Pop

587 a8083063 Iustin Pop
    This code assumes that there are 256 minors only.
588 a8083063 Iustin Pop

589 a8083063 Iustin Pop
    """
590 a8083063 Iustin Pop
    used_md = MDRaid1._GetUsedDevs()
591 a8083063 Iustin Pop
    i = 0
592 a8083063 Iustin Pop
    while i < 256:
593 a8083063 Iustin Pop
      if i not in used_md:
594 a8083063 Iustin Pop
        break
595 a8083063 Iustin Pop
      i += 1
596 a8083063 Iustin Pop
    if i == 256:
597 a8083063 Iustin Pop
      logger.Error("Critical: Out of md minor numbers.")
598 a8083063 Iustin Pop
      return None
599 a8083063 Iustin Pop
    return i
600 a8083063 Iustin Pop
601 a8083063 Iustin Pop
602 a8083063 Iustin Pop
  @classmethod
603 a8083063 Iustin Pop
  def _FindMDByUUID(cls, uuid):
604 a8083063 Iustin Pop
    """Find the minor of an MD array with a given UUID.
605 a8083063 Iustin Pop

606 a8083063 Iustin Pop
    """
607 a8083063 Iustin Pop
    md_list = cls._GetUsedDevs()
608 a8083063 Iustin Pop
    for minor in md_list:
609 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
610 a8083063 Iustin Pop
      if info and info["uuid"] == uuid:
611 a8083063 Iustin Pop
        return minor
612 a8083063 Iustin Pop
    return None
613 a8083063 Iustin Pop
614 a8083063 Iustin Pop
615 1a87dca7 Iustin Pop
  @staticmethod
616 1a87dca7 Iustin Pop
  def _ZeroSuperblock(dev_path):
617 1a87dca7 Iustin Pop
    """Zero the possible locations for an MD superblock.
618 1a87dca7 Iustin Pop

619 1a87dca7 Iustin Pop
    The zero-ing can't be done via ``mdadm --zero-superblock`` as that
620 1a87dca7 Iustin Pop
    fails in versions 2.x with the same error code as non-writable
621 1a87dca7 Iustin Pop
    device.
622 1a87dca7 Iustin Pop

623 1a87dca7 Iustin Pop
    The superblocks are located at (negative values are relative to
624 1a87dca7 Iustin Pop
    the end of the block device):
625 1a87dca7 Iustin Pop
      - -128k to end for version 0.90 superblock
626 1a87dca7 Iustin Pop
      - -8k to -12k for version 1.0 superblock (included in the above)
627 1a87dca7 Iustin Pop
      - 0k to 4k for version 1.1 superblock
628 1a87dca7 Iustin Pop
      - 4k to 8k for version 1.2 superblock
629 1a87dca7 Iustin Pop

630 1a87dca7 Iustin Pop
    To cover all situations, the zero-ing will be:
631 1a87dca7 Iustin Pop
      - 0k to 128k
632 1a87dca7 Iustin Pop
      - -128k to end
633 1a87dca7 Iustin Pop

634 1a87dca7 Iustin Pop
    As such, the minimum device size must be 128k, otherwise we'll get
635 1a87dca7 Iustin Pop
    I/O errors.
636 1a87dca7 Iustin Pop

637 1a87dca7 Iustin Pop
    Note that this function depends on the fact that one can open,
638 1a87dca7 Iustin Pop
    read and write block devices normally.
639 1a87dca7 Iustin Pop

640 1a87dca7 Iustin Pop
    """
641 1a87dca7 Iustin Pop
    overwrite_size = 128 * 1024
642 1a87dca7 Iustin Pop
    empty_buf = '\0' * overwrite_size
643 1a87dca7 Iustin Pop
    fd = open(dev_path, "r+")
644 1a87dca7 Iustin Pop
    try:
645 1a87dca7 Iustin Pop
      fd.seek(0, 0)
646 1a87dca7 Iustin Pop
      p1 = fd.tell()
647 1a87dca7 Iustin Pop
      fd.write(empty_buf)
648 1a87dca7 Iustin Pop
      p2 = fd.tell()
649 1a87dca7 Iustin Pop
      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
650 1a87dca7 Iustin Pop
      fd.seek(-overwrite_size, 2)
651 1a87dca7 Iustin Pop
      p1 = fd.tell()
652 1a87dca7 Iustin Pop
      fd.write(empty_buf)
653 1a87dca7 Iustin Pop
      p2 = fd.tell()
654 1a87dca7 Iustin Pop
      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
655 1a87dca7 Iustin Pop
    finally:
656 1a87dca7 Iustin Pop
      fd.close()
657 1a87dca7 Iustin Pop
658 a8083063 Iustin Pop
  @classmethod
659 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
660 a8083063 Iustin Pop
    """Create a new MD raid1 array.
661 a8083063 Iustin Pop

662 a8083063 Iustin Pop
    """
663 a8083063 Iustin Pop
    if not isinstance(children, (tuple, list)):
664 a8083063 Iustin Pop
      raise ValueError("Invalid setup data for MDRaid1 dev: %s" %
665 a8083063 Iustin Pop
                       str(children))
666 a8083063 Iustin Pop
    for i in children:
667 a8083063 Iustin Pop
      if not isinstance(i, BlockDev):
668 a8083063 Iustin Pop
        raise ValueError("Invalid member in MDRaid1 dev: %s" % type(i))
669 a8083063 Iustin Pop
    for i in children:
670 1a87dca7 Iustin Pop
      try:
671 1a87dca7 Iustin Pop
        cls._ZeroSuperblock(i.dev_path)
672 1a87dca7 Iustin Pop
      except EnvironmentError, err:
673 1a87dca7 Iustin Pop
        logger.Error("Can't zero superblock for %s: %s" %
674 1a87dca7 Iustin Pop
                     (i.dev_path, str(err)))
675 a8083063 Iustin Pop
        return None
676 a8083063 Iustin Pop
    minor = cls._FindUnusedMinor()
677 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--create", "/dev/md%d" % minor,
678 a8083063 Iustin Pop
                           "--auto=yes", "--force", "-l1",
679 a8083063 Iustin Pop
                           "-n%d" % len(children)] +
680 a8083063 Iustin Pop
                          [dev.dev_path for dev in children])
681 a8083063 Iustin Pop
682 a8083063 Iustin Pop
    if result.failed:
683 1a87dca7 Iustin Pop
      logger.Error("Can't create md: %s: %s" % (result.fail_reason,
684 1a87dca7 Iustin Pop
                                                result.output))
685 a8083063 Iustin Pop
      return None
686 a8083063 Iustin Pop
    info = cls._GetDevInfo(minor)
687 a8083063 Iustin Pop
    if not info or not "uuid" in info:
688 a8083063 Iustin Pop
      logger.Error("Wrong information returned from mdadm -D: %s" % str(info))
689 a8083063 Iustin Pop
      return None
690 a8083063 Iustin Pop
    return MDRaid1(info["uuid"], children)
691 a8083063 Iustin Pop
692 a8083063 Iustin Pop
693 a8083063 Iustin Pop
  def Remove(self):
694 a8083063 Iustin Pop
    """Stub remove function for MD RAID 1 arrays.
695 a8083063 Iustin Pop

696 a8083063 Iustin Pop
    We don't remove the superblock right now. Mark a to do.
697 a8083063 Iustin Pop

698 a8083063 Iustin Pop
    """
699 a8083063 Iustin Pop
    #TODO: maybe zero superblock on child devices?
700 a8083063 Iustin Pop
    return self.Shutdown()
701 a8083063 Iustin Pop
702 a8083063 Iustin Pop
703 a8083063 Iustin Pop
  def AddChild(self, device):
704 a8083063 Iustin Pop
    """Add a new member to the md raid1.
705 a8083063 Iustin Pop

706 a8083063 Iustin Pop
    """
707 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
708 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device")
709 a8083063 Iustin Pop
    if device.dev_path is None:
710 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("New child is not initialised")
711 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-a", self.dev_path, device.dev_path])
712 a8083063 Iustin Pop
    if result.failed:
713 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Failed to add new device to array: %s" %
714 3ecf6786 Iustin Pop
                                    result.output)
715 a8083063 Iustin Pop
    new_len = len(self._children) + 1
716 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", self.dev_path, "-n", new_len])
717 a8083063 Iustin Pop
    if result.failed:
718 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't grow md array: %s" %
719 3ecf6786 Iustin Pop
                                    result.output)
720 a8083063 Iustin Pop
    self._children.append(device)
721 a8083063 Iustin Pop
722 a8083063 Iustin Pop
723 a8083063 Iustin Pop
  def RemoveChild(self, dev_path):
724 a8083063 Iustin Pop
    """Remove member from the md raid1.
725 a8083063 Iustin Pop

726 a8083063 Iustin Pop
    """
727 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
728 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device")
729 a8083063 Iustin Pop
    if len(self._children) == 1:
730 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't reduce member when only one"
731 3ecf6786 Iustin Pop
                                    " child left")
732 a8083063 Iustin Pop
    for device in self._children:
733 a8083063 Iustin Pop
      if device.dev_path == dev_path:
734 a8083063 Iustin Pop
        break
735 a8083063 Iustin Pop
    else:
736 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't find child with this path")
737 a8083063 Iustin Pop
    new_len = len(self._children) - 1
738 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-f", self.dev_path, dev_path])
739 a8083063 Iustin Pop
    if result.failed:
740 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Failed to mark device as failed: %s" %
741 3ecf6786 Iustin Pop
                                    result.output)
742 a8083063 Iustin Pop
743 a8083063 Iustin Pop
    # it seems here we need a short delay for MD to update its
744 a8083063 Iustin Pop
    # superblocks
745 a8083063 Iustin Pop
    time.sleep(0.5)
746 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-r", self.dev_path, dev_path])
747 a8083063 Iustin Pop
    if result.failed:
748 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Failed to remove device from array:"
749 3ecf6786 Iustin Pop
                                        " %s" % result.output)
750 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", "--force", self.dev_path,
751 a8083063 Iustin Pop
                           "-n", new_len])
752 a8083063 Iustin Pop
    if result.failed:
753 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't shrink md array: %s" %
754 3ecf6786 Iustin Pop
                                    result.output)
755 a8083063 Iustin Pop
    self._children.remove(device)
756 a8083063 Iustin Pop
757 a8083063 Iustin Pop
758 a8083063 Iustin Pop
  def GetStatus(self):
759 a8083063 Iustin Pop
    """Return the status of the device.
760 a8083063 Iustin Pop

761 a8083063 Iustin Pop
    """
762 a8083063 Iustin Pop
    self.Attach()
763 a8083063 Iustin Pop
    if self.minor is None:
764 a8083063 Iustin Pop
      retval = self.STATUS_UNKNOWN
765 a8083063 Iustin Pop
    else:
766 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
767 a8083063 Iustin Pop
    return retval
768 a8083063 Iustin Pop
769 a8083063 Iustin Pop
770 a8083063 Iustin Pop
  def _SetFromMinor(self, minor):
771 a8083063 Iustin Pop
    """Set our parameters based on the given minor.
772 a8083063 Iustin Pop

773 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
774 a8083063 Iustin Pop

775 a8083063 Iustin Pop
    """
776 a8083063 Iustin Pop
    self.minor = minor
777 a8083063 Iustin Pop
    self.dev_path = "/dev/md%d" % minor
778 a8083063 Iustin Pop
779 a8083063 Iustin Pop
780 a8083063 Iustin Pop
  def Assemble(self):
781 a8083063 Iustin Pop
    """Assemble the MD device.
782 a8083063 Iustin Pop

783 a8083063 Iustin Pop
    At this point we should have:
784 a8083063 Iustin Pop
      - list of children devices
785 a8083063 Iustin Pop
      - uuid
786 a8083063 Iustin Pop

787 a8083063 Iustin Pop
    """
788 a8083063 Iustin Pop
    result = super(MDRaid1, self).Assemble()
789 a8083063 Iustin Pop
    if not result:
790 a8083063 Iustin Pop
      return result
791 a8083063 Iustin Pop
    md_list = self._GetUsedDevs()
792 a8083063 Iustin Pop
    for minor in md_list:
793 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
794 a8083063 Iustin Pop
      if info and info["uuid"] == self.unique_id:
795 a8083063 Iustin Pop
        self._SetFromMinor(minor)
796 a8083063 Iustin Pop
        logger.Info("MD array %s already started" % str(self))
797 a8083063 Iustin Pop
        return True
798 a8083063 Iustin Pop
    free_minor = self._FindUnusedMinor()
799 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-A", "--auto=yes", "--uuid",
800 a8083063 Iustin Pop
                           self.unique_id, "/dev/md%d" % free_minor] +
801 a8083063 Iustin Pop
                          [bdev.dev_path for bdev in self._children])
802 a8083063 Iustin Pop
    if result.failed:
803 8d519422 Iustin Pop
      logger.Error("Can't assemble MD array: %s: %s" %
804 8d519422 Iustin Pop
                   (result.fail_reason, result.output))
805 a8083063 Iustin Pop
      self.minor = None
806 a8083063 Iustin Pop
    else:
807 a8083063 Iustin Pop
      self.minor = free_minor
808 a8083063 Iustin Pop
    return not result.failed
809 a8083063 Iustin Pop
810 a8083063 Iustin Pop
811 a8083063 Iustin Pop
  def Shutdown(self):
812 a8083063 Iustin Pop
    """Tear down the MD array.
813 a8083063 Iustin Pop

814 a8083063 Iustin Pop
    This does a 'mdadm --stop' so after this command, the array is no
815 a8083063 Iustin Pop
    longer available.
816 a8083063 Iustin Pop

817 a8083063 Iustin Pop
    """
818 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
819 a8083063 Iustin Pop
      logger.Info("MD object not attached to a device")
820 a8083063 Iustin Pop
      return True
821 a8083063 Iustin Pop
822 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--stop", "/dev/md%d" % self.minor])
823 a8083063 Iustin Pop
    if result.failed:
824 a8083063 Iustin Pop
      logger.Error("Can't stop MD array: %s" % result.fail_reason)
825 a8083063 Iustin Pop
      return False
826 a8083063 Iustin Pop
    self.minor = None
827 a8083063 Iustin Pop
    self.dev_path = None
828 a8083063 Iustin Pop
    return True
829 a8083063 Iustin Pop
830 a8083063 Iustin Pop
831 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
832 a8083063 Iustin Pop
    """Set the maximum sync speed for the MD array.
833 a8083063 Iustin Pop

834 a8083063 Iustin Pop
    """
835 a8083063 Iustin Pop
    result = super(MDRaid1, self).SetSyncSpeed(kbytes)
836 a8083063 Iustin Pop
    if self.minor is None:
837 a8083063 Iustin Pop
      logger.Error("MD array not attached to a device")
838 a8083063 Iustin Pop
      return False
839 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_max" % self.minor, "w")
840 a8083063 Iustin Pop
    try:
841 a8083063 Iustin Pop
      f.write("%d" % kbytes)
842 a8083063 Iustin Pop
    finally:
843 a8083063 Iustin Pop
      f.close()
844 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_min" % self.minor, "w")
845 a8083063 Iustin Pop
    try:
846 a8083063 Iustin Pop
      f.write("%d" % (kbytes/2))
847 a8083063 Iustin Pop
    finally:
848 a8083063 Iustin Pop
      f.close()
849 a8083063 Iustin Pop
    return result
850 a8083063 Iustin Pop
851 a8083063 Iustin Pop
852 a8083063 Iustin Pop
  def GetSyncStatus(self):
853 a8083063 Iustin Pop
    """Returns the sync status of the device.
854 a8083063 Iustin Pop

855 a8083063 Iustin Pop
    Returns:
856 a8083063 Iustin Pop
     (sync_percent, estimated_time)
857 a8083063 Iustin Pop

858 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
859 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
860 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
861 a8083063 Iustin Pop

862 a8083063 Iustin Pop
    """
863 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
864 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
865 a8083063 Iustin Pop
    dev_info = self._GetDevInfo(self.minor)
866 a8083063 Iustin Pop
    is_clean = ("state" in dev_info and
867 a8083063 Iustin Pop
                len(dev_info["state"]) == 1 and
868 a8083063 Iustin Pop
                dev_info["state"][0] in ("clean", "active"))
869 a8083063 Iustin Pop
    sys_path = "/sys/block/md%s/md/" % self.minor
870 a8083063 Iustin Pop
    f = file(sys_path + "sync_action")
871 a8083063 Iustin Pop
    sync_status = f.readline().strip()
872 a8083063 Iustin Pop
    f.close()
873 a8083063 Iustin Pop
    if sync_status == "idle":
874 a8083063 Iustin Pop
      return None, None, not is_clean
875 a8083063 Iustin Pop
    f = file(sys_path + "sync_completed")
876 a8083063 Iustin Pop
    sync_completed = f.readline().strip().split(" / ")
877 a8083063 Iustin Pop
    f.close()
878 a8083063 Iustin Pop
    if len(sync_completed) != 2:
879 a8083063 Iustin Pop
      return 0, None, not is_clean
880 a8083063 Iustin Pop
    sync_done, sync_total = [float(i) for i in sync_completed]
881 a8083063 Iustin Pop
    sync_percent = 100.0*sync_done/sync_total
882 a8083063 Iustin Pop
    f = file(sys_path + "sync_speed")
883 a8083063 Iustin Pop
    sync_speed_k = int(f.readline().strip())
884 a8083063 Iustin Pop
    if sync_speed_k == 0:
885 a8083063 Iustin Pop
      time_est = None
886 a8083063 Iustin Pop
    else:
887 a8083063 Iustin Pop
      time_est = (sync_total - sync_done) / 2 / sync_speed_k
888 a8083063 Iustin Pop
    return sync_percent, time_est, not is_clean
889 a8083063 Iustin Pop
890 a8083063 Iustin Pop
891 a8083063 Iustin Pop
  def Open(self, force=False):
892 a8083063 Iustin Pop
    """Make the device ready for I/O.
893 a8083063 Iustin Pop

894 a8083063 Iustin Pop
    This is a no-op for the MDRaid1 device type, although we could use
895 a8083063 Iustin Pop
    the 2.6.18's new array_state thing.
896 a8083063 Iustin Pop

897 a8083063 Iustin Pop
    """
898 a8083063 Iustin Pop
    return True
899 a8083063 Iustin Pop
900 a8083063 Iustin Pop
901 a8083063 Iustin Pop
  def Close(self):
902 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
903 a8083063 Iustin Pop

904 a8083063 Iustin Pop
    This is a no-op for the MDRaid1 device type, but see comment for
905 a8083063 Iustin Pop
    `Open()`.
906 a8083063 Iustin Pop

907 a8083063 Iustin Pop
    """
908 a8083063 Iustin Pop
    return True
909 a8083063 Iustin Pop
910 a8083063 Iustin Pop
911 a8083063 Iustin Pop
912 a8083063 Iustin Pop
class DRBDev(BlockDev):
913 a8083063 Iustin Pop
  """DRBD block device.
914 a8083063 Iustin Pop

915 a8083063 Iustin Pop
  This implements the local host part of the DRBD device, i.e. it
916 a8083063 Iustin Pop
  doesn't do anything to the supposed peer. If you need a fully
917 a8083063 Iustin Pop
  connected DRBD pair, you need to use this class on both hosts.
918 a8083063 Iustin Pop

919 a8083063 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
920 a8083063 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
921 a8083063 Iustin Pop
  data device and the meta_device. The meta device is checked for
922 a8083063 Iustin Pop
  valid size and is zeroed on create.
923 a8083063 Iustin Pop

924 a8083063 Iustin Pop
  """
925 a8083063 Iustin Pop
  _DRBD_MAJOR = 147
926 a8083063 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
927 a8083063 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
928 a8083063 Iustin Pop
  _ST_CONNECTED = "Connected"
929 a8083063 Iustin Pop
930 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
931 a8083063 Iustin Pop
    super(DRBDev, self).__init__(unique_id, children)
932 a8083063 Iustin Pop
    self.major = self._DRBD_MAJOR
933 a8083063 Iustin Pop
    if len(children) != 2:
934 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
935 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
936 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
937 a8083063 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
938 a8083063 Iustin Pop
    self.Attach()
939 a8083063 Iustin Pop
940 a8083063 Iustin Pop
  @staticmethod
941 a8083063 Iustin Pop
  def _DevPath(minor):
942 a8083063 Iustin Pop
    """Return the path to a drbd device for a given minor.
943 a8083063 Iustin Pop

944 a8083063 Iustin Pop
    """
945 a8083063 Iustin Pop
    return "/dev/drbd%d" % minor
946 a8083063 Iustin Pop
947 a8083063 Iustin Pop
  @staticmethod
948 a8083063 Iustin Pop
  def _GetProcData():
949 a8083063 Iustin Pop
    """Return data from /proc/drbd.
950 a8083063 Iustin Pop

951 a8083063 Iustin Pop
    """
952 a8083063 Iustin Pop
    stat = open("/proc/drbd", "r")
953 a8083063 Iustin Pop
    data = stat.read().splitlines()
954 a8083063 Iustin Pop
    stat.close()
955 a8083063 Iustin Pop
    return data
956 a8083063 Iustin Pop
957 a8083063 Iustin Pop
958 a8083063 Iustin Pop
  @classmethod
959 a8083063 Iustin Pop
  def _GetUsedDevs(cls):
960 a8083063 Iustin Pop
    """Compute the list of used DRBD devices.
961 a8083063 Iustin Pop

962 a8083063 Iustin Pop
    """
963 a8083063 Iustin Pop
    data = cls._GetProcData()
964 a8083063 Iustin Pop
965 a8083063 Iustin Pop
    used_devs = {}
966 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
967 a8083063 Iustin Pop
    for line in data:
968 a8083063 Iustin Pop
      match = valid_line.match(line)
969 a8083063 Iustin Pop
      if not match:
970 a8083063 Iustin Pop
        continue
971 a8083063 Iustin Pop
      minor = int(match.group(1))
972 a8083063 Iustin Pop
      state = match.group(2)
973 a8083063 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
974 a8083063 Iustin Pop
        continue
975 a8083063 Iustin Pop
      used_devs[minor] = state, line
976 a8083063 Iustin Pop
977 a8083063 Iustin Pop
    return used_devs
978 a8083063 Iustin Pop
979 a8083063 Iustin Pop
980 a8083063 Iustin Pop
  @classmethod
981 a8083063 Iustin Pop
  def _FindUnusedMinor(cls):
982 a8083063 Iustin Pop
    """Find an unused DRBD device.
983 a8083063 Iustin Pop

984 a8083063 Iustin Pop
    """
985 a8083063 Iustin Pop
    data = cls._GetProcData()
986 a8083063 Iustin Pop
987 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
988 a8083063 Iustin Pop
    for line in data:
989 a8083063 Iustin Pop
      match = valid_line.match(line)
990 a8083063 Iustin Pop
      if match:
991 a8083063 Iustin Pop
        return int(match.group(1))
992 a8083063 Iustin Pop
    logger.Error("Error: no free drbd minors!")
993 a8083063 Iustin Pop
    return None
994 a8083063 Iustin Pop
995 a8083063 Iustin Pop
996 a8083063 Iustin Pop
  @classmethod
997 a8083063 Iustin Pop
  def _GetDevInfo(cls, minor):
998 a8083063 Iustin Pop
    """Get details about a given DRBD minor.
999 a8083063 Iustin Pop

1000 a8083063 Iustin Pop
    This return, if available, the local backing device in (major,
1001 a8083063 Iustin Pop
    minor) formant and the local and remote (ip, port) information.
1002 a8083063 Iustin Pop

1003 a8083063 Iustin Pop
    """
1004 a8083063 Iustin Pop
    data = {}
1005 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1006 a8083063 Iustin Pop
    if result.failed:
1007 a8083063 Iustin Pop
      logger.Error("Can't display the drbd config: %s" % result.fail_reason)
1008 a8083063 Iustin Pop
      return data
1009 a8083063 Iustin Pop
    out = result.stdout
1010 a8083063 Iustin Pop
    if out == "Not configured\n":
1011 a8083063 Iustin Pop
      return data
1012 a8083063 Iustin Pop
    for line in out.splitlines():
1013 a8083063 Iustin Pop
      if "local_dev" not in data:
1014 a8083063 Iustin Pop
        match = re.match("^Lower device: ([0-9]+):([0-9]+) .*$", line)
1015 a8083063 Iustin Pop
        if match:
1016 a8083063 Iustin Pop
          data["local_dev"] = (int(match.group(1)), int(match.group(2)))
1017 a8083063 Iustin Pop
          continue
1018 a8083063 Iustin Pop
      if "meta_dev" not in data:
1019 a8083063 Iustin Pop
        match = re.match("^Meta device: (([0-9]+):([0-9]+)|internal).*$", line)
1020 a8083063 Iustin Pop
        if match:
1021 a8083063 Iustin Pop
          if match.group(2) is not None and match.group(3) is not None:
1022 a8083063 Iustin Pop
            # matched on the major/minor
1023 a8083063 Iustin Pop
            data["meta_dev"] = (int(match.group(2)), int(match.group(3)))
1024 a8083063 Iustin Pop
          else:
1025 a8083063 Iustin Pop
            # matched on the "internal" string
1026 a8083063 Iustin Pop
            data["meta_dev"] = match.group(1)
1027 a8083063 Iustin Pop
            # in this case, no meta_index is in the output
1028 a8083063 Iustin Pop
            data["meta_index"] = -1
1029 a8083063 Iustin Pop
          continue
1030 a8083063 Iustin Pop
      if "meta_index" not in data:
1031 a8083063 Iustin Pop
        match = re.match("^Meta index: ([0-9]+).*$", line)
1032 a8083063 Iustin Pop
        if match:
1033 a8083063 Iustin Pop
          data["meta_index"] = int(match.group(1))
1034 a8083063 Iustin Pop
          continue
1035 a8083063 Iustin Pop
      if "local_addr" not in data:
1036 a8083063 Iustin Pop
        match = re.match("^Local address: ([0-9.]+):([0-9]+)$", line)
1037 a8083063 Iustin Pop
        if match:
1038 a8083063 Iustin Pop
          data["local_addr"] = (match.group(1), int(match.group(2)))
1039 a8083063 Iustin Pop
          continue
1040 a8083063 Iustin Pop
      if "remote_addr" not in data:
1041 a8083063 Iustin Pop
        match = re.match("^Remote address: ([0-9.]+):([0-9]+)$", line)
1042 a8083063 Iustin Pop
        if match:
1043 a8083063 Iustin Pop
          data["remote_addr"] = (match.group(1), int(match.group(2)))
1044 a8083063 Iustin Pop
          continue
1045 a8083063 Iustin Pop
    return data
1046 a8083063 Iustin Pop
1047 a8083063 Iustin Pop
1048 a8083063 Iustin Pop
  def _MatchesLocal(self, info):
1049 a8083063 Iustin Pop
    """Test if our local config matches with an existing device.
1050 a8083063 Iustin Pop

1051 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1052 a8083063 Iustin Pop
    method tests if our local backing device is the same as the one in
1053 a8083063 Iustin Pop
    the info parameter, in effect testing if we look like the given
1054 a8083063 Iustin Pop
    device.
1055 a8083063 Iustin Pop

1056 a8083063 Iustin Pop
    """
1057 a8083063 Iustin Pop
    if not ("local_dev" in info and "meta_dev" in info and
1058 a8083063 Iustin Pop
            "meta_index" in info):
1059 a8083063 Iustin Pop
      return False
1060 a8083063 Iustin Pop
1061 a8083063 Iustin Pop
    backend = self._children[0]
1062 a8083063 Iustin Pop
    if backend is not None:
1063 a8083063 Iustin Pop
      retval = (info["local_dev"] == (backend.major, backend.minor))
1064 a8083063 Iustin Pop
    else:
1065 a8083063 Iustin Pop
      retval = (info["local_dev"] == (0, 0))
1066 a8083063 Iustin Pop
    meta = self._children[1]
1067 a8083063 Iustin Pop
    if meta is not None:
1068 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == (meta.major, meta.minor))
1069 a8083063 Iustin Pop
      retval = retval and (info["meta_index"] == 0)
1070 a8083063 Iustin Pop
    else:
1071 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == "internal" and
1072 a8083063 Iustin Pop
                           info["meta_index"] == -1)
1073 a8083063 Iustin Pop
    return retval
1074 a8083063 Iustin Pop
1075 a8083063 Iustin Pop
1076 a8083063 Iustin Pop
  def _MatchesNet(self, info):
1077 a8083063 Iustin Pop
    """Test if our network config matches with an existing device.
1078 a8083063 Iustin Pop

1079 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1080 a8083063 Iustin Pop
    method tests if our network configuration is the same as the one
1081 a8083063 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1082 a8083063 Iustin Pop
    device.
1083 a8083063 Iustin Pop

1084 a8083063 Iustin Pop
    """
1085 a8083063 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1086 a8083063 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1087 a8083063 Iustin Pop
      return True
1088 a8083063 Iustin Pop
1089 a8083063 Iustin Pop
    if self._lhost is None:
1090 a8083063 Iustin Pop
      return False
1091 a8083063 Iustin Pop
1092 a8083063 Iustin Pop
    if not ("local_addr" in info and
1093 a8083063 Iustin Pop
            "remote_addr" in info):
1094 a8083063 Iustin Pop
      return False
1095 a8083063 Iustin Pop
1096 a8083063 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1097 a8083063 Iustin Pop
    retval = (retval and
1098 a8083063 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1099 a8083063 Iustin Pop
    return retval
1100 a8083063 Iustin Pop
1101 a8083063 Iustin Pop
1102 a8083063 Iustin Pop
  @staticmethod
1103 a8083063 Iustin Pop
  def _IsValidMeta(meta_device):
1104 a8083063 Iustin Pop
    """Check if the given meta device looks like a valid one.
1105 a8083063 Iustin Pop

1106 a8083063 Iustin Pop
    This currently only check the size, which must be around
1107 a8083063 Iustin Pop
    128MiB.
1108 a8083063 Iustin Pop

1109 a8083063 Iustin Pop
    """
1110 a8083063 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1111 a8083063 Iustin Pop
    if result.failed:
1112 a8083063 Iustin Pop
      logger.Error("Failed to get device size: %s" % result.fail_reason)
1113 a8083063 Iustin Pop
      return False
1114 a8083063 Iustin Pop
    try:
1115 a8083063 Iustin Pop
      sectors = int(result.stdout)
1116 a8083063 Iustin Pop
    except ValueError:
1117 a8083063 Iustin Pop
      logger.Error("Invalid output from blockdev: '%s'" % result.stdout)
1118 a8083063 Iustin Pop
      return False
1119 a8083063 Iustin Pop
    bytes = sectors * 512
1120 a8083063 Iustin Pop
    if bytes < 128*1024*1024: # less than 128MiB
1121 a8083063 Iustin Pop
      logger.Error("Meta device too small (%.2fMib)" % (bytes/1024/1024))
1122 a8083063 Iustin Pop
      return False
1123 a8083063 Iustin Pop
    if bytes > (128+32)*1024*1024: # account for an extra (big) PE on LVM
1124 a8083063 Iustin Pop
      logger.Error("Meta device too big (%.2fMiB)" % (bytes/1024/1024))
1125 a8083063 Iustin Pop
      return False
1126 a8083063 Iustin Pop
    return True
1127 a8083063 Iustin Pop
1128 a8083063 Iustin Pop
1129 a8083063 Iustin Pop
  @classmethod
1130 a8083063 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1131 a8083063 Iustin Pop
    """Configure the local part of a DRBD device.
1132 a8083063 Iustin Pop

1133 a8083063 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1134 a8083063 Iustin Pop
    device. And it must be done only once.
1135 a8083063 Iustin Pop

1136 a8083063 Iustin Pop
    """
1137 a8083063 Iustin Pop
    if not cls._IsValidMeta(meta):
1138 a8083063 Iustin Pop
      return False
1139 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1140 a8083063 Iustin Pop
                           backend, meta, "0", "-e", "detach"])
1141 a8083063 Iustin Pop
    if result.failed:
1142 a8083063 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1143 a8083063 Iustin Pop
    return not result.failed
1144 a8083063 Iustin Pop
1145 a8083063 Iustin Pop
1146 a8083063 Iustin Pop
  @classmethod
1147 a8083063 Iustin Pop
  def _ShutdownLocal(cls, minor):
1148 a8083063 Iustin Pop
    """Detach from the local device.
1149 a8083063 Iustin Pop

1150 a8083063 Iustin Pop
    I/Os will continue to be served from the remote device. If we
1151 a8083063 Iustin Pop
    don't have a remote device, this operation will fail.
1152 a8083063 Iustin Pop

1153 a8083063 Iustin Pop
    """
1154 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1155 a8083063 Iustin Pop
    if result.failed:
1156 a8083063 Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
1157 a8083063 Iustin Pop
    return not result.failed
1158 a8083063 Iustin Pop
1159 a8083063 Iustin Pop
1160 a8083063 Iustin Pop
  @staticmethod
1161 a8083063 Iustin Pop
  def _ShutdownAll(minor):
1162 a8083063 Iustin Pop
    """Deactivate the device.
1163 a8083063 Iustin Pop

1164 a8083063 Iustin Pop
    This will, of course, fail if the device is in use.
1165 a8083063 Iustin Pop

1166 a8083063 Iustin Pop
    """
1167 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", DRBDev._DevPath(minor), "down"])
1168 a8083063 Iustin Pop
    if result.failed:
1169 a8083063 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
1170 a8083063 Iustin Pop
    return not result.failed
1171 a8083063 Iustin Pop
1172 a8083063 Iustin Pop
1173 a8083063 Iustin Pop
  @classmethod
1174 a8083063 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol):
1175 a8083063 Iustin Pop
    """Configure the network part of the device.
1176 a8083063 Iustin Pop

1177 a8083063 Iustin Pop
    This operation can be, in theory, done multiple times, but there
1178 a8083063 Iustin Pop
    have been cases (in lab testing) in which the network part of the
1179 a8083063 Iustin Pop
    device had become stuck and couldn't be shut down because activity
1180 a8083063 Iustin Pop
    from the new peer (also stuck) triggered a timer re-init and
1181 a8083063 Iustin Pop
    needed remote peer interface shutdown in order to clear. So please
1182 a8083063 Iustin Pop
    don't change online the net config.
1183 a8083063 Iustin Pop

1184 a8083063 Iustin Pop
    """
1185 a8083063 Iustin Pop
    lhost, lport, rhost, rport = net_info
1186 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "net",
1187 a8083063 Iustin Pop
                           "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport),
1188 a8083063 Iustin Pop
                           protocol])
1189 a8083063 Iustin Pop
    if result.failed:
1190 a8083063 Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s" %
1191 a8083063 Iustin Pop
                   result.fail_reason)
1192 a8083063 Iustin Pop
      return False
1193 a8083063 Iustin Pop
1194 a8083063 Iustin Pop
    timeout = time.time() + 10
1195 a8083063 Iustin Pop
    ok = False
1196 a8083063 Iustin Pop
    while time.time() < timeout:
1197 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
1198 a8083063 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1199 a8083063 Iustin Pop
        time.sleep(1)
1200 a8083063 Iustin Pop
        continue
1201 a8083063 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1202 a8083063 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1203 a8083063 Iustin Pop
        time.sleep(1)
1204 a8083063 Iustin Pop
        continue
1205 a8083063 Iustin Pop
      ok = True
1206 a8083063 Iustin Pop
      break
1207 a8083063 Iustin Pop
    if not ok:
1208 a8083063 Iustin Pop
      logger.Error("Timeout while configuring network")
1209 a8083063 Iustin Pop
      return False
1210 a8083063 Iustin Pop
    return True
1211 a8083063 Iustin Pop
1212 a8083063 Iustin Pop
1213 a8083063 Iustin Pop
  @classmethod
1214 a8083063 Iustin Pop
  def _ShutdownNet(cls, minor):
1215 a8083063 Iustin Pop
    """Disconnect from the remote peer.
1216 a8083063 Iustin Pop

1217 a8083063 Iustin Pop
    This fails if we don't have a local device.
1218 a8083063 Iustin Pop

1219 a8083063 Iustin Pop
    """
1220 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1221 a8083063 Iustin Pop
    logger.Error("Can't shutdown network: %s" % result.output)
1222 a8083063 Iustin Pop
    return not result.failed
1223 a8083063 Iustin Pop
1224 a8083063 Iustin Pop
1225 a8083063 Iustin Pop
  def _SetFromMinor(self, minor):
1226 a8083063 Iustin Pop
    """Set our parameters based on the given minor.
1227 a8083063 Iustin Pop

1228 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
1229 a8083063 Iustin Pop

1230 a8083063 Iustin Pop
    """
1231 a8083063 Iustin Pop
    if minor is None:
1232 a8083063 Iustin Pop
      self.minor = self.dev_path = None
1233 a8083063 Iustin Pop
    else:
1234 a8083063 Iustin Pop
      self.minor = minor
1235 a8083063 Iustin Pop
      self.dev_path = self._DevPath(minor)
1236 a8083063 Iustin Pop
1237 a8083063 Iustin Pop
1238 a8083063 Iustin Pop
  def Assemble(self):
1239 a8083063 Iustin Pop
    """Assemble the drbd.
1240 a8083063 Iustin Pop

1241 a8083063 Iustin Pop
    Method:
1242 a8083063 Iustin Pop
      - if we have a local backing device, we bind to it by:
1243 a8083063 Iustin Pop
        - checking the list of used drbd devices
1244 a8083063 Iustin Pop
        - check if the local minor use of any of them is our own device
1245 a8083063 Iustin Pop
        - if yes, abort?
1246 a8083063 Iustin Pop
        - if not, bind
1247 a8083063 Iustin Pop
      - if we have a local/remote net info:
1248 a8083063 Iustin Pop
        - redo the local backing device step for the remote device
1249 a8083063 Iustin Pop
        - check if any drbd device is using the local port,
1250 a8083063 Iustin Pop
          if yes abort
1251 a8083063 Iustin Pop
        - check if any remote drbd device is using the remote
1252 a8083063 Iustin Pop
          port, if yes abort (for now)
1253 a8083063 Iustin Pop
        - bind our net port
1254 a8083063 Iustin Pop
        - bind the remote net port
1255 a8083063 Iustin Pop

1256 a8083063 Iustin Pop
    """
1257 a8083063 Iustin Pop
    self.Attach()
1258 a8083063 Iustin Pop
    if self.minor is not None:
1259 a8083063 Iustin Pop
      logger.Info("Already assembled")
1260 a8083063 Iustin Pop
      return True
1261 a8083063 Iustin Pop
1262 a8083063 Iustin Pop
    result = super(DRBDev, self).Assemble()
1263 a8083063 Iustin Pop
    if not result:
1264 a8083063 Iustin Pop
      return result
1265 a8083063 Iustin Pop
1266 a8083063 Iustin Pop
    minor = self._FindUnusedMinor()
1267 a8083063 Iustin Pop
    if minor is None:
1268 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Not enough free minors for DRBD!")
1269 a8083063 Iustin Pop
    need_localdev_teardown = False
1270 a8083063 Iustin Pop
    if self._children[0]:
1271 a8083063 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1272 a8083063 Iustin Pop
                                   self._children[1].dev_path)
1273 a8083063 Iustin Pop
      if not result:
1274 a8083063 Iustin Pop
        return False
1275 a8083063 Iustin Pop
      need_localdev_teardown = True
1276 a8083063 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1277 a8083063 Iustin Pop
      result = self._AssembleNet(minor,
1278 a8083063 Iustin Pop
                                 (self._lhost, self._lport,
1279 a8083063 Iustin Pop
                                  self._rhost, self._rport),
1280 a8083063 Iustin Pop
                                 "C")
1281 a8083063 Iustin Pop
      if not result:
1282 a8083063 Iustin Pop
        if need_localdev_teardown:
1283 a8083063 Iustin Pop
          # we will ignore failures from this
1284 a8083063 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
1285 a8083063 Iustin Pop
          self._ShutdownAll(minor)
1286 a8083063 Iustin Pop
        return False
1287 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1288 a8083063 Iustin Pop
    return True
1289 a8083063 Iustin Pop
1290 a8083063 Iustin Pop
1291 a8083063 Iustin Pop
  def Shutdown(self):
1292 a8083063 Iustin Pop
    """Shutdown the DRBD device.
1293 a8083063 Iustin Pop

1294 a8083063 Iustin Pop
    """
1295 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1296 a8083063 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
1297 a8083063 Iustin Pop
      return True
1298 a8083063 Iustin Pop
    if not self._ShutdownAll(self.minor):
1299 a8083063 Iustin Pop
      return False
1300 a8083063 Iustin Pop
    self.minor = None
1301 a8083063 Iustin Pop
    self.dev_path = None
1302 a8083063 Iustin Pop
    return True
1303 a8083063 Iustin Pop
1304 a8083063 Iustin Pop
1305 a8083063 Iustin Pop
  def Attach(self):
1306 a8083063 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1307 a8083063 Iustin Pop

1308 a8083063 Iustin Pop
    In case of partially attached (local device matches but no network
1309 a8083063 Iustin Pop
    setup), we perform the network attach. If successful, we re-test
1310 a8083063 Iustin Pop
    the attach if can return success.
1311 a8083063 Iustin Pop

1312 a8083063 Iustin Pop
    """
1313 a8083063 Iustin Pop
    for minor in self._GetUsedDevs():
1314 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
1315 a8083063 Iustin Pop
      match_l = self._MatchesLocal(info)
1316 a8083063 Iustin Pop
      match_r = self._MatchesNet(info)
1317 a8083063 Iustin Pop
      if match_l and match_r:
1318 a8083063 Iustin Pop
        break
1319 a8083063 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1320 a8083063 Iustin Pop
        res_r = self._AssembleNet(minor,
1321 a8083063 Iustin Pop
                                  (self._lhost, self._lport,
1322 a8083063 Iustin Pop
                                   self._rhost, self._rport),
1323 a8083063 Iustin Pop
                                  "C")
1324 a8083063 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
1325 a8083063 Iustin Pop
          break
1326 a8083063 Iustin Pop
    else:
1327 a8083063 Iustin Pop
      minor = None
1328 a8083063 Iustin Pop
1329 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1330 a8083063 Iustin Pop
    return minor is not None
1331 a8083063 Iustin Pop
1332 a8083063 Iustin Pop
1333 a8083063 Iustin Pop
  def Open(self, force=False):
1334 a8083063 Iustin Pop
    """Make the local state primary.
1335 a8083063 Iustin Pop

1336 a8083063 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1337 a8083063 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1338 a8083063 Iustin Pop
    force flag should be only given after creation, when it actually
1339 a8083063 Iustin Pop
    has to be given.
1340 a8083063 Iustin Pop

1341 a8083063 Iustin Pop
    """
1342 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1343 a8083063 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1344 a8083063 Iustin Pop
      return False
1345 a8083063 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1346 a8083063 Iustin Pop
    if force:
1347 a8083063 Iustin Pop
      cmd.append("--do-what-I-say")
1348 a8083063 Iustin Pop
    result = utils.RunCmd(cmd)
1349 a8083063 Iustin Pop
    if result.failed:
1350 a8083063 Iustin Pop
      logger.Error("Can't make drbd device primary: %s" % result.output)
1351 a8083063 Iustin Pop
      return False
1352 a8083063 Iustin Pop
    return True
1353 a8083063 Iustin Pop
1354 a8083063 Iustin Pop
1355 a8083063 Iustin Pop
  def Close(self):
1356 a8083063 Iustin Pop
    """Make the local state secondary.
1357 a8083063 Iustin Pop

1358 a8083063 Iustin Pop
    This will, of course, fail if the device is in use.
1359 a8083063 Iustin Pop

1360 a8083063 Iustin Pop
    """
1361 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1362 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1363 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1364 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1365 a8083063 Iustin Pop
    if result.failed:
1366 a8083063 Iustin Pop
      logger.Error("Can't switch drbd device to secondary: %s" % result.output)
1367 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't switch drbd device to secondary")
1368 a8083063 Iustin Pop
1369 a8083063 Iustin Pop
1370 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1371 a8083063 Iustin Pop
    """Set the speed of the DRBD syncer.
1372 a8083063 Iustin Pop

1373 a8083063 Iustin Pop
    """
1374 a8083063 Iustin Pop
    children_result = super(DRBDev, self).SetSyncSpeed(kbytes)
1375 a8083063 Iustin Pop
    if self.minor is None:
1376 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1377 a8083063 Iustin Pop
      return False
1378 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1379 a8083063 Iustin Pop
                           kbytes])
1380 a8083063 Iustin Pop
    if result.failed:
1381 a8083063 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1382 a8083063 Iustin Pop
    return not result.failed and children_result
1383 a8083063 Iustin Pop
1384 a8083063 Iustin Pop
1385 a8083063 Iustin Pop
  def GetSyncStatus(self):
1386 a8083063 Iustin Pop
    """Returns the sync status of the device.
1387 a8083063 Iustin Pop

1388 a8083063 Iustin Pop
    Returns:
1389 a8083063 Iustin Pop
     (sync_percent, estimated_time)
1390 a8083063 Iustin Pop

1391 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
1392 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
1393 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
1394 a8083063 Iustin Pop

1395 a8083063 Iustin Pop
    """
1396 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1397 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1398 a8083063 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1399 a8083063 Iustin Pop
    if self.minor not in proc_info:
1400 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1401 a8083063 Iustin Pop
                                    self.minor)
1402 a8083063 Iustin Pop
    line = proc_info[self.minor]
1403 a8083063 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1404 a8083063 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1405 a8083063 Iustin Pop
    if match:
1406 a8083063 Iustin Pop
      sync_percent = float(match.group(1))
1407 a8083063 Iustin Pop
      hours = int(match.group(2))
1408 a8083063 Iustin Pop
      minutes = int(match.group(3))
1409 a8083063 Iustin Pop
      seconds = int(match.group(4))
1410 a8083063 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1411 a8083063 Iustin Pop
    else:
1412 a8083063 Iustin Pop
      sync_percent = None
1413 a8083063 Iustin Pop
      est_time = None
1414 a8083063 Iustin Pop
    match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1415 a8083063 Iustin Pop
    if not match:
1416 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1417 a8083063 Iustin Pop
                                    self.minor)
1418 a8083063 Iustin Pop
    client_state = match.group(1)
1419 a8083063 Iustin Pop
    is_degraded = client_state != "Connected"
1420 a8083063 Iustin Pop
    return sync_percent, est_time, is_degraded
1421 a8083063 Iustin Pop
1422 a8083063 Iustin Pop
1423 a8083063 Iustin Pop
  @staticmethod
1424 a8083063 Iustin Pop
  def _MassageProcData(data):
1425 a8083063 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
1426 a8083063 Iustin Pop

1427 a8083063 Iustin Pop
    Returns:
1428 a8083063 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
1429 a8083063 Iustin Pop

1430 a8083063 Iustin Pop
    """
1431 a8083063 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
1432 a8083063 Iustin Pop
    results = {}
1433 a8083063 Iustin Pop
    old_minor = old_line = None
1434 a8083063 Iustin Pop
    for line in data:
1435 a8083063 Iustin Pop
      lresult = lmatch.match(line)
1436 a8083063 Iustin Pop
      if lresult is not None:
1437 a8083063 Iustin Pop
        if old_minor is not None:
1438 a8083063 Iustin Pop
          results[old_minor] = old_line
1439 a8083063 Iustin Pop
        old_minor = int(lresult.group(1))
1440 a8083063 Iustin Pop
        old_line = line
1441 a8083063 Iustin Pop
      else:
1442 a8083063 Iustin Pop
        if old_minor is not None:
1443 a8083063 Iustin Pop
          old_line += " " + line.strip()
1444 a8083063 Iustin Pop
    # add last line
1445 a8083063 Iustin Pop
    if old_minor is not None:
1446 a8083063 Iustin Pop
      results[old_minor] = old_line
1447 a8083063 Iustin Pop
    return results
1448 a8083063 Iustin Pop
1449 a8083063 Iustin Pop
1450 a8083063 Iustin Pop
  def GetStatus(self):
1451 a8083063 Iustin Pop
    """Compute the status of the DRBD device
1452 a8083063 Iustin Pop

1453 a8083063 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1454 a8083063 Iustin Pop

1455 a8083063 Iustin Pop
    """
1456 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1457 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
1458 a8083063 Iustin Pop
1459 a8083063 Iustin Pop
    data = self._GetProcData()
1460 a8083063 Iustin Pop
    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
1461 a8083063 Iustin Pop
                       self.minor)
1462 a8083063 Iustin Pop
    for line in data:
1463 a8083063 Iustin Pop
      mresult = match.match(line)
1464 a8083063 Iustin Pop
      if mresult:
1465 a8083063 Iustin Pop
        break
1466 a8083063 Iustin Pop
    else:
1467 a8083063 Iustin Pop
      logger.Error("Can't find myself!")
1468 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
1469 a8083063 Iustin Pop
1470 a8083063 Iustin Pop
    state = mresult.group(2)
1471 a8083063 Iustin Pop
    if state == "Primary":
1472 a8083063 Iustin Pop
      result = self.STATUS_ONLINE
1473 a8083063 Iustin Pop
    else:
1474 a8083063 Iustin Pop
      result = self.STATUS_STANDBY
1475 a8083063 Iustin Pop
1476 a8083063 Iustin Pop
    return result
1477 a8083063 Iustin Pop
1478 a8083063 Iustin Pop
1479 a8083063 Iustin Pop
  @staticmethod
1480 a8083063 Iustin Pop
  def _ZeroDevice(device):
1481 a8083063 Iustin Pop
    """Zero a device.
1482 a8083063 Iustin Pop

1483 a8083063 Iustin Pop
    This writes until we get ENOSPC.
1484 a8083063 Iustin Pop

1485 a8083063 Iustin Pop
    """
1486 a8083063 Iustin Pop
    f = open(device, "w")
1487 a8083063 Iustin Pop
    buf = "\0" * 1048576
1488 a8083063 Iustin Pop
    try:
1489 a8083063 Iustin Pop
      while True:
1490 a8083063 Iustin Pop
        f.write(buf)
1491 a8083063 Iustin Pop
    except IOError, err:
1492 a8083063 Iustin Pop
      if err.errno != errno.ENOSPC:
1493 a8083063 Iustin Pop
        raise
1494 a8083063 Iustin Pop
1495 a8083063 Iustin Pop
1496 a8083063 Iustin Pop
  @classmethod
1497 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
1498 a8083063 Iustin Pop
    """Create a new DRBD device.
1499 a8083063 Iustin Pop

1500 a8083063 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1501 a8083063 Iustin Pop
    function just zeroes the meta device.
1502 a8083063 Iustin Pop

1503 a8083063 Iustin Pop
    """
1504 a8083063 Iustin Pop
    if len(children) != 2:
1505 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1506 a8083063 Iustin Pop
    meta = children[1]
1507 a8083063 Iustin Pop
    meta.Assemble()
1508 a8083063 Iustin Pop
    if not meta.Attach():
1509 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1510 a8083063 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
1511 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device")
1512 a8083063 Iustin Pop
    logger.Info("Started zeroing device %s" % meta.dev_path)
1513 a8083063 Iustin Pop
    cls._ZeroDevice(meta.dev_path)
1514 a8083063 Iustin Pop
    logger.Info("Done zeroing device %s" % meta.dev_path)
1515 a8083063 Iustin Pop
    return cls(unique_id, children)
1516 a8083063 Iustin Pop
1517 a8083063 Iustin Pop
1518 a8083063 Iustin Pop
  def Remove(self):
1519 a8083063 Iustin Pop
    """Stub remove for DRBD devices.
1520 a8083063 Iustin Pop

1521 a8083063 Iustin Pop
    """
1522 a8083063 Iustin Pop
    return self.Shutdown()
1523 a8083063 Iustin Pop
1524 a8083063 Iustin Pop
1525 a8083063 Iustin Pop
DEV_MAP = {
1526 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1527 fe96220b Iustin Pop
  constants.LD_MD_R1: MDRaid1,
1528 fe96220b Iustin Pop
  constants.LD_DRBD7: DRBDev,
1529 a8083063 Iustin Pop
  }
1530 a8083063 Iustin Pop
1531 a8083063 Iustin Pop
1532 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
1533 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1534 a8083063 Iustin Pop

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

1538 a8083063 Iustin Pop
  """
1539 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1540 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1541 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1542 a8083063 Iustin Pop
  if not device.Attach():
1543 a8083063 Iustin Pop
    return None
1544 a8083063 Iustin Pop
  return  device
1545 a8083063 Iustin Pop
1546 a8083063 Iustin Pop
1547 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
1548 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1549 a8083063 Iustin Pop

1550 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
1551 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
1552 a8083063 Iustin Pop

1553 a8083063 Iustin Pop
  """
1554 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1555 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1556 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1557 a8083063 Iustin Pop
  if not device.Attach():
1558 a8083063 Iustin Pop
    device.Assemble()
1559 a8083063 Iustin Pop
  if not device.Attach():
1560 a8083063 Iustin Pop
    raise errors.BlockDeviceError("Can't find a valid block device for"
1561 a8083063 Iustin Pop
                                  " %s/%s/%s" %
1562 a8083063 Iustin Pop
                                  (dev_type, unique_id, children))
1563 a8083063 Iustin Pop
  return device
1564 a8083063 Iustin Pop
1565 a8083063 Iustin Pop
1566 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1567 a8083063 Iustin Pop
  """Create a device.
1568 a8083063 Iustin Pop

1569 a8083063 Iustin Pop
  """
1570 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1571 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1572 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1573 a8083063 Iustin Pop
  return device