Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 770fe0f9

History | View | Annotate | Download (46.9 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 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
912 0f7f32d9 Iustin Pop
  """Base DRBD class.
913 a8083063 Iustin Pop

914 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
915 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
916 0f7f32d9 Iustin Pop

917 0f7f32d9 Iustin Pop
  """
918 0f7f32d9 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
919 0f7f32d9 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)\)")
920 770fe0f9 Iustin Pop
  _DRBD_MAJOR = 147
921 770fe0f9 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
922 770fe0f9 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
923 770fe0f9 Iustin Pop
  _ST_CONNECTED = "Connected"
924 0f7f32d9 Iustin Pop
925 0f7f32d9 Iustin Pop
  @staticmethod
926 0f7f32d9 Iustin Pop
  def _GetProcData():
927 0f7f32d9 Iustin Pop
    """Return data from /proc/drbd.
928 0f7f32d9 Iustin Pop

929 0f7f32d9 Iustin Pop
    """
930 0f7f32d9 Iustin Pop
    stat = open("/proc/drbd", "r")
931 0f7f32d9 Iustin Pop
    try:
932 0f7f32d9 Iustin Pop
      data = stat.read().splitlines()
933 0f7f32d9 Iustin Pop
    finally:
934 0f7f32d9 Iustin Pop
      stat.close()
935 0f7f32d9 Iustin Pop
    if not data:
936 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't read any data from /proc/drbd")
937 0f7f32d9 Iustin Pop
    return data
938 0f7f32d9 Iustin Pop
939 0f7f32d9 Iustin Pop
  @classmethod
940 0f7f32d9 Iustin Pop
  def _GetVersion(cls):
941 0f7f32d9 Iustin Pop
    """Return the DRBD version.
942 0f7f32d9 Iustin Pop

943 0f7f32d9 Iustin Pop
    This will return a list [k_major, k_minor, k_point, api, proto].
944 0f7f32d9 Iustin Pop

945 0f7f32d9 Iustin Pop
    """
946 0f7f32d9 Iustin Pop
    proc_data = cls._GetProcData()
947 0f7f32d9 Iustin Pop
    first_line = proc_data[0].strip()
948 0f7f32d9 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
949 0f7f32d9 Iustin Pop
    if not version:
950 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
951 0f7f32d9 Iustin Pop
                                    first_line)
952 0f7f32d9 Iustin Pop
    return [int(val) for val in version.groups()]
953 0f7f32d9 Iustin Pop
954 770fe0f9 Iustin Pop
  @staticmethod
955 770fe0f9 Iustin Pop
  def _DevPath(minor):
956 770fe0f9 Iustin Pop
    """Return the path to a drbd device for a given minor.
957 770fe0f9 Iustin Pop

958 770fe0f9 Iustin Pop
    """
959 770fe0f9 Iustin Pop
    return "/dev/drbd%d" % minor
960 770fe0f9 Iustin Pop
961 770fe0f9 Iustin Pop
  @classmethod
962 770fe0f9 Iustin Pop
  def _GetUsedDevs(cls):
963 770fe0f9 Iustin Pop
    """Compute the list of used DRBD devices.
964 770fe0f9 Iustin Pop

965 770fe0f9 Iustin Pop
    """
966 770fe0f9 Iustin Pop
    data = cls._GetProcData()
967 770fe0f9 Iustin Pop
968 770fe0f9 Iustin Pop
    used_devs = {}
969 770fe0f9 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
970 770fe0f9 Iustin Pop
    for line in data:
971 770fe0f9 Iustin Pop
      match = valid_line.match(line)
972 770fe0f9 Iustin Pop
      if not match:
973 770fe0f9 Iustin Pop
        continue
974 770fe0f9 Iustin Pop
      minor = int(match.group(1))
975 770fe0f9 Iustin Pop
      state = match.group(2)
976 770fe0f9 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
977 770fe0f9 Iustin Pop
        continue
978 770fe0f9 Iustin Pop
      used_devs[minor] = state, line
979 770fe0f9 Iustin Pop
980 770fe0f9 Iustin Pop
    return used_devs
981 770fe0f9 Iustin Pop
982 0f7f32d9 Iustin Pop
983 0f7f32d9 Iustin Pop
class DRBDev(BaseDRBD):
984 a8083063 Iustin Pop
  """DRBD block device.
985 a8083063 Iustin Pop

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

990 a8083063 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
991 a8083063 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
992 a8083063 Iustin Pop
  data device and the meta_device. The meta device is checked for
993 a8083063 Iustin Pop
  valid size and is zeroed on create.
994 a8083063 Iustin Pop

995 a8083063 Iustin Pop
  """
996 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
997 a8083063 Iustin Pop
    super(DRBDev, self).__init__(unique_id, children)
998 a8083063 Iustin Pop
    self.major = self._DRBD_MAJOR
999 0f7f32d9 Iustin Pop
    [kmaj, kmin, kfix, api, proto] = self._GetVersion()
1000 0f7f32d9 Iustin Pop
    if kmaj != 0 and kmin != 7:
1001 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1002 0f7f32d9 Iustin Pop
                                    " requested ganeti usage: kernel is"
1003 0f7f32d9 Iustin Pop
                                    " %s.%s, ganeti wants 0.7" % (kmaj, kmin))
1004 0f7f32d9 Iustin Pop
1005 a8083063 Iustin Pop
    if len(children) != 2:
1006 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1007 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1008 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1009 a8083063 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
1010 a8083063 Iustin Pop
    self.Attach()
1011 a8083063 Iustin Pop
1012 a8083063 Iustin Pop
  @classmethod
1013 a8083063 Iustin Pop
  def _FindUnusedMinor(cls):
1014 a8083063 Iustin Pop
    """Find an unused DRBD device.
1015 a8083063 Iustin Pop

1016 a8083063 Iustin Pop
    """
1017 a8083063 Iustin Pop
    data = cls._GetProcData()
1018 a8083063 Iustin Pop
1019 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1020 a8083063 Iustin Pop
    for line in data:
1021 a8083063 Iustin Pop
      match = valid_line.match(line)
1022 a8083063 Iustin Pop
      if match:
1023 a8083063 Iustin Pop
        return int(match.group(1))
1024 a8083063 Iustin Pop
    logger.Error("Error: no free drbd minors!")
1025 a8083063 Iustin Pop
    return None
1026 a8083063 Iustin Pop
1027 a8083063 Iustin Pop
1028 a8083063 Iustin Pop
  @classmethod
1029 a8083063 Iustin Pop
  def _GetDevInfo(cls, minor):
1030 a8083063 Iustin Pop
    """Get details about a given DRBD minor.
1031 a8083063 Iustin Pop

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

1035 a8083063 Iustin Pop
    """
1036 a8083063 Iustin Pop
    data = {}
1037 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1038 a8083063 Iustin Pop
    if result.failed:
1039 a8083063 Iustin Pop
      logger.Error("Can't display the drbd config: %s" % result.fail_reason)
1040 a8083063 Iustin Pop
      return data
1041 a8083063 Iustin Pop
    out = result.stdout
1042 a8083063 Iustin Pop
    if out == "Not configured\n":
1043 a8083063 Iustin Pop
      return data
1044 a8083063 Iustin Pop
    for line in out.splitlines():
1045 a8083063 Iustin Pop
      if "local_dev" not in data:
1046 a8083063 Iustin Pop
        match = re.match("^Lower device: ([0-9]+):([0-9]+) .*$", line)
1047 a8083063 Iustin Pop
        if match:
1048 a8083063 Iustin Pop
          data["local_dev"] = (int(match.group(1)), int(match.group(2)))
1049 a8083063 Iustin Pop
          continue
1050 a8083063 Iustin Pop
      if "meta_dev" not in data:
1051 a8083063 Iustin Pop
        match = re.match("^Meta device: (([0-9]+):([0-9]+)|internal).*$", line)
1052 a8083063 Iustin Pop
        if match:
1053 a8083063 Iustin Pop
          if match.group(2) is not None and match.group(3) is not None:
1054 a8083063 Iustin Pop
            # matched on the major/minor
1055 a8083063 Iustin Pop
            data["meta_dev"] = (int(match.group(2)), int(match.group(3)))
1056 a8083063 Iustin Pop
          else:
1057 a8083063 Iustin Pop
            # matched on the "internal" string
1058 a8083063 Iustin Pop
            data["meta_dev"] = match.group(1)
1059 a8083063 Iustin Pop
            # in this case, no meta_index is in the output
1060 a8083063 Iustin Pop
            data["meta_index"] = -1
1061 a8083063 Iustin Pop
          continue
1062 a8083063 Iustin Pop
      if "meta_index" not in data:
1063 a8083063 Iustin Pop
        match = re.match("^Meta index: ([0-9]+).*$", line)
1064 a8083063 Iustin Pop
        if match:
1065 a8083063 Iustin Pop
          data["meta_index"] = int(match.group(1))
1066 a8083063 Iustin Pop
          continue
1067 a8083063 Iustin Pop
      if "local_addr" not in data:
1068 a8083063 Iustin Pop
        match = re.match("^Local address: ([0-9.]+):([0-9]+)$", line)
1069 a8083063 Iustin Pop
        if match:
1070 a8083063 Iustin Pop
          data["local_addr"] = (match.group(1), int(match.group(2)))
1071 a8083063 Iustin Pop
          continue
1072 a8083063 Iustin Pop
      if "remote_addr" not in data:
1073 a8083063 Iustin Pop
        match = re.match("^Remote address: ([0-9.]+):([0-9]+)$", line)
1074 a8083063 Iustin Pop
        if match:
1075 a8083063 Iustin Pop
          data["remote_addr"] = (match.group(1), int(match.group(2)))
1076 a8083063 Iustin Pop
          continue
1077 a8083063 Iustin Pop
    return data
1078 a8083063 Iustin Pop
1079 a8083063 Iustin Pop
1080 a8083063 Iustin Pop
  def _MatchesLocal(self, info):
1081 a8083063 Iustin Pop
    """Test if our local config matches with an existing device.
1082 a8083063 Iustin Pop

1083 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1084 a8083063 Iustin Pop
    method tests if our local backing device is the same as the one in
1085 a8083063 Iustin Pop
    the info parameter, in effect testing if we look like the given
1086 a8083063 Iustin Pop
    device.
1087 a8083063 Iustin Pop

1088 a8083063 Iustin Pop
    """
1089 a8083063 Iustin Pop
    if not ("local_dev" in info and "meta_dev" in info and
1090 a8083063 Iustin Pop
            "meta_index" in info):
1091 a8083063 Iustin Pop
      return False
1092 a8083063 Iustin Pop
1093 a8083063 Iustin Pop
    backend = self._children[0]
1094 a8083063 Iustin Pop
    if backend is not None:
1095 a8083063 Iustin Pop
      retval = (info["local_dev"] == (backend.major, backend.minor))
1096 a8083063 Iustin Pop
    else:
1097 a8083063 Iustin Pop
      retval = (info["local_dev"] == (0, 0))
1098 a8083063 Iustin Pop
    meta = self._children[1]
1099 a8083063 Iustin Pop
    if meta is not None:
1100 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == (meta.major, meta.minor))
1101 a8083063 Iustin Pop
      retval = retval and (info["meta_index"] == 0)
1102 a8083063 Iustin Pop
    else:
1103 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == "internal" and
1104 a8083063 Iustin Pop
                           info["meta_index"] == -1)
1105 a8083063 Iustin Pop
    return retval
1106 a8083063 Iustin Pop
1107 a8083063 Iustin Pop
1108 a8083063 Iustin Pop
  def _MatchesNet(self, info):
1109 a8083063 Iustin Pop
    """Test if our network config matches with an existing device.
1110 a8083063 Iustin Pop

1111 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1112 a8083063 Iustin Pop
    method tests if our network configuration is the same as the one
1113 a8083063 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1114 a8083063 Iustin Pop
    device.
1115 a8083063 Iustin Pop

1116 a8083063 Iustin Pop
    """
1117 a8083063 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1118 a8083063 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1119 a8083063 Iustin Pop
      return True
1120 a8083063 Iustin Pop
1121 a8083063 Iustin Pop
    if self._lhost is None:
1122 a8083063 Iustin Pop
      return False
1123 a8083063 Iustin Pop
1124 a8083063 Iustin Pop
    if not ("local_addr" in info and
1125 a8083063 Iustin Pop
            "remote_addr" in info):
1126 a8083063 Iustin Pop
      return False
1127 a8083063 Iustin Pop
1128 a8083063 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1129 a8083063 Iustin Pop
    retval = (retval and
1130 a8083063 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1131 a8083063 Iustin Pop
    return retval
1132 a8083063 Iustin Pop
1133 a8083063 Iustin Pop
1134 a8083063 Iustin Pop
  @staticmethod
1135 a8083063 Iustin Pop
  def _IsValidMeta(meta_device):
1136 a8083063 Iustin Pop
    """Check if the given meta device looks like a valid one.
1137 a8083063 Iustin Pop

1138 a8083063 Iustin Pop
    This currently only check the size, which must be around
1139 a8083063 Iustin Pop
    128MiB.
1140 a8083063 Iustin Pop

1141 a8083063 Iustin Pop
    """
1142 a8083063 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1143 a8083063 Iustin Pop
    if result.failed:
1144 a8083063 Iustin Pop
      logger.Error("Failed to get device size: %s" % result.fail_reason)
1145 a8083063 Iustin Pop
      return False
1146 a8083063 Iustin Pop
    try:
1147 a8083063 Iustin Pop
      sectors = int(result.stdout)
1148 a8083063 Iustin Pop
    except ValueError:
1149 a8083063 Iustin Pop
      logger.Error("Invalid output from blockdev: '%s'" % result.stdout)
1150 a8083063 Iustin Pop
      return False
1151 a8083063 Iustin Pop
    bytes = sectors * 512
1152 a8083063 Iustin Pop
    if bytes < 128*1024*1024: # less than 128MiB
1153 a8083063 Iustin Pop
      logger.Error("Meta device too small (%.2fMib)" % (bytes/1024/1024))
1154 a8083063 Iustin Pop
      return False
1155 a8083063 Iustin Pop
    if bytes > (128+32)*1024*1024: # account for an extra (big) PE on LVM
1156 a8083063 Iustin Pop
      logger.Error("Meta device too big (%.2fMiB)" % (bytes/1024/1024))
1157 a8083063 Iustin Pop
      return False
1158 a8083063 Iustin Pop
    return True
1159 a8083063 Iustin Pop
1160 a8083063 Iustin Pop
1161 a8083063 Iustin Pop
  @classmethod
1162 a8083063 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1163 a8083063 Iustin Pop
    """Configure the local part of a DRBD device.
1164 a8083063 Iustin Pop

1165 a8083063 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1166 a8083063 Iustin Pop
    device. And it must be done only once.
1167 a8083063 Iustin Pop

1168 a8083063 Iustin Pop
    """
1169 a8083063 Iustin Pop
    if not cls._IsValidMeta(meta):
1170 a8083063 Iustin Pop
      return False
1171 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1172 a8083063 Iustin Pop
                           backend, meta, "0", "-e", "detach"])
1173 a8083063 Iustin Pop
    if result.failed:
1174 a8083063 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1175 a8083063 Iustin Pop
    return not result.failed
1176 a8083063 Iustin Pop
1177 a8083063 Iustin Pop
1178 a8083063 Iustin Pop
  @classmethod
1179 a8083063 Iustin Pop
  def _ShutdownLocal(cls, minor):
1180 a8083063 Iustin Pop
    """Detach from the local device.
1181 a8083063 Iustin Pop

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

1185 a8083063 Iustin Pop
    """
1186 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1187 a8083063 Iustin Pop
    if result.failed:
1188 a8083063 Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
1189 a8083063 Iustin Pop
    return not result.failed
1190 a8083063 Iustin Pop
1191 a8083063 Iustin Pop
1192 a8083063 Iustin Pop
  @staticmethod
1193 a8083063 Iustin Pop
  def _ShutdownAll(minor):
1194 a8083063 Iustin Pop
    """Deactivate the device.
1195 a8083063 Iustin Pop

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

1198 a8083063 Iustin Pop
    """
1199 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", DRBDev._DevPath(minor), "down"])
1200 a8083063 Iustin Pop
    if result.failed:
1201 a8083063 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
1202 a8083063 Iustin Pop
    return not result.failed
1203 a8083063 Iustin Pop
1204 a8083063 Iustin Pop
1205 a8083063 Iustin Pop
  @classmethod
1206 a8083063 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol):
1207 a8083063 Iustin Pop
    """Configure the network part of the device.
1208 a8083063 Iustin Pop

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

1216 a8083063 Iustin Pop
    """
1217 a8083063 Iustin Pop
    lhost, lport, rhost, rport = net_info
1218 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "net",
1219 a8083063 Iustin Pop
                           "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport),
1220 a8083063 Iustin Pop
                           protocol])
1221 a8083063 Iustin Pop
    if result.failed:
1222 a8083063 Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s" %
1223 a8083063 Iustin Pop
                   result.fail_reason)
1224 a8083063 Iustin Pop
      return False
1225 a8083063 Iustin Pop
1226 a8083063 Iustin Pop
    timeout = time.time() + 10
1227 a8083063 Iustin Pop
    ok = False
1228 a8083063 Iustin Pop
    while time.time() < timeout:
1229 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
1230 a8083063 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1231 a8083063 Iustin Pop
        time.sleep(1)
1232 a8083063 Iustin Pop
        continue
1233 a8083063 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1234 a8083063 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1235 a8083063 Iustin Pop
        time.sleep(1)
1236 a8083063 Iustin Pop
        continue
1237 a8083063 Iustin Pop
      ok = True
1238 a8083063 Iustin Pop
      break
1239 a8083063 Iustin Pop
    if not ok:
1240 a8083063 Iustin Pop
      logger.Error("Timeout while configuring network")
1241 a8083063 Iustin Pop
      return False
1242 a8083063 Iustin Pop
    return True
1243 a8083063 Iustin Pop
1244 a8083063 Iustin Pop
1245 a8083063 Iustin Pop
  @classmethod
1246 a8083063 Iustin Pop
  def _ShutdownNet(cls, minor):
1247 a8083063 Iustin Pop
    """Disconnect from the remote peer.
1248 a8083063 Iustin Pop

1249 a8083063 Iustin Pop
    This fails if we don't have a local device.
1250 a8083063 Iustin Pop

1251 a8083063 Iustin Pop
    """
1252 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1253 a8083063 Iustin Pop
    logger.Error("Can't shutdown network: %s" % result.output)
1254 a8083063 Iustin Pop
    return not result.failed
1255 a8083063 Iustin Pop
1256 a8083063 Iustin Pop
1257 a8083063 Iustin Pop
  def _SetFromMinor(self, minor):
1258 a8083063 Iustin Pop
    """Set our parameters based on the given minor.
1259 a8083063 Iustin Pop

1260 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
1261 a8083063 Iustin Pop

1262 a8083063 Iustin Pop
    """
1263 a8083063 Iustin Pop
    if minor is None:
1264 a8083063 Iustin Pop
      self.minor = self.dev_path = None
1265 a8083063 Iustin Pop
    else:
1266 a8083063 Iustin Pop
      self.minor = minor
1267 a8083063 Iustin Pop
      self.dev_path = self._DevPath(minor)
1268 a8083063 Iustin Pop
1269 a8083063 Iustin Pop
1270 a8083063 Iustin Pop
  def Assemble(self):
1271 a8083063 Iustin Pop
    """Assemble the drbd.
1272 a8083063 Iustin Pop

1273 a8083063 Iustin Pop
    Method:
1274 a8083063 Iustin Pop
      - if we have a local backing device, we bind to it by:
1275 a8083063 Iustin Pop
        - checking the list of used drbd devices
1276 a8083063 Iustin Pop
        - check if the local minor use of any of them is our own device
1277 a8083063 Iustin Pop
        - if yes, abort?
1278 a8083063 Iustin Pop
        - if not, bind
1279 a8083063 Iustin Pop
      - if we have a local/remote net info:
1280 a8083063 Iustin Pop
        - redo the local backing device step for the remote device
1281 a8083063 Iustin Pop
        - check if any drbd device is using the local port,
1282 a8083063 Iustin Pop
          if yes abort
1283 a8083063 Iustin Pop
        - check if any remote drbd device is using the remote
1284 a8083063 Iustin Pop
          port, if yes abort (for now)
1285 a8083063 Iustin Pop
        - bind our net port
1286 a8083063 Iustin Pop
        - bind the remote net port
1287 a8083063 Iustin Pop

1288 a8083063 Iustin Pop
    """
1289 a8083063 Iustin Pop
    self.Attach()
1290 a8083063 Iustin Pop
    if self.minor is not None:
1291 a8083063 Iustin Pop
      logger.Info("Already assembled")
1292 a8083063 Iustin Pop
      return True
1293 a8083063 Iustin Pop
1294 a8083063 Iustin Pop
    result = super(DRBDev, self).Assemble()
1295 a8083063 Iustin Pop
    if not result:
1296 a8083063 Iustin Pop
      return result
1297 a8083063 Iustin Pop
1298 a8083063 Iustin Pop
    minor = self._FindUnusedMinor()
1299 a8083063 Iustin Pop
    if minor is None:
1300 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Not enough free minors for DRBD!")
1301 a8083063 Iustin Pop
    need_localdev_teardown = False
1302 a8083063 Iustin Pop
    if self._children[0]:
1303 a8083063 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1304 a8083063 Iustin Pop
                                   self._children[1].dev_path)
1305 a8083063 Iustin Pop
      if not result:
1306 a8083063 Iustin Pop
        return False
1307 a8083063 Iustin Pop
      need_localdev_teardown = True
1308 a8083063 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1309 a8083063 Iustin Pop
      result = self._AssembleNet(minor,
1310 a8083063 Iustin Pop
                                 (self._lhost, self._lport,
1311 a8083063 Iustin Pop
                                  self._rhost, self._rport),
1312 a8083063 Iustin Pop
                                 "C")
1313 a8083063 Iustin Pop
      if not result:
1314 a8083063 Iustin Pop
        if need_localdev_teardown:
1315 a8083063 Iustin Pop
          # we will ignore failures from this
1316 a8083063 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
1317 a8083063 Iustin Pop
          self._ShutdownAll(minor)
1318 a8083063 Iustin Pop
        return False
1319 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1320 a8083063 Iustin Pop
    return True
1321 a8083063 Iustin Pop
1322 a8083063 Iustin Pop
1323 a8083063 Iustin Pop
  def Shutdown(self):
1324 a8083063 Iustin Pop
    """Shutdown the DRBD device.
1325 a8083063 Iustin Pop

1326 a8083063 Iustin Pop
    """
1327 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1328 a8083063 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
1329 a8083063 Iustin Pop
      return True
1330 a8083063 Iustin Pop
    if not self._ShutdownAll(self.minor):
1331 a8083063 Iustin Pop
      return False
1332 a8083063 Iustin Pop
    self.minor = None
1333 a8083063 Iustin Pop
    self.dev_path = None
1334 a8083063 Iustin Pop
    return True
1335 a8083063 Iustin Pop
1336 a8083063 Iustin Pop
1337 a8083063 Iustin Pop
  def Attach(self):
1338 a8083063 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1339 a8083063 Iustin Pop

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

1344 a8083063 Iustin Pop
    """
1345 a8083063 Iustin Pop
    for minor in self._GetUsedDevs():
1346 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
1347 a8083063 Iustin Pop
      match_l = self._MatchesLocal(info)
1348 a8083063 Iustin Pop
      match_r = self._MatchesNet(info)
1349 a8083063 Iustin Pop
      if match_l and match_r:
1350 a8083063 Iustin Pop
        break
1351 a8083063 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1352 a8083063 Iustin Pop
        res_r = self._AssembleNet(minor,
1353 a8083063 Iustin Pop
                                  (self._lhost, self._lport,
1354 a8083063 Iustin Pop
                                   self._rhost, self._rport),
1355 a8083063 Iustin Pop
                                  "C")
1356 a8083063 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
1357 a8083063 Iustin Pop
          break
1358 a8083063 Iustin Pop
    else:
1359 a8083063 Iustin Pop
      minor = None
1360 a8083063 Iustin Pop
1361 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1362 a8083063 Iustin Pop
    return minor is not None
1363 a8083063 Iustin Pop
1364 a8083063 Iustin Pop
1365 a8083063 Iustin Pop
  def Open(self, force=False):
1366 a8083063 Iustin Pop
    """Make the local state primary.
1367 a8083063 Iustin Pop

1368 a8083063 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1369 a8083063 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1370 a8083063 Iustin Pop
    force flag should be only given after creation, when it actually
1371 a8083063 Iustin Pop
    has to be given.
1372 a8083063 Iustin Pop

1373 a8083063 Iustin Pop
    """
1374 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1375 a8083063 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1376 a8083063 Iustin Pop
      return False
1377 a8083063 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1378 a8083063 Iustin Pop
    if force:
1379 a8083063 Iustin Pop
      cmd.append("--do-what-I-say")
1380 a8083063 Iustin Pop
    result = utils.RunCmd(cmd)
1381 a8083063 Iustin Pop
    if result.failed:
1382 a8083063 Iustin Pop
      logger.Error("Can't make drbd device primary: %s" % result.output)
1383 a8083063 Iustin Pop
      return False
1384 a8083063 Iustin Pop
    return True
1385 a8083063 Iustin Pop
1386 a8083063 Iustin Pop
1387 a8083063 Iustin Pop
  def Close(self):
1388 a8083063 Iustin Pop
    """Make the local state secondary.
1389 a8083063 Iustin Pop

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

1392 a8083063 Iustin Pop
    """
1393 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1394 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1395 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1396 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1397 a8083063 Iustin Pop
    if result.failed:
1398 a8083063 Iustin Pop
      logger.Error("Can't switch drbd device to secondary: %s" % result.output)
1399 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't switch drbd device to secondary")
1400 a8083063 Iustin Pop
1401 a8083063 Iustin Pop
1402 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1403 a8083063 Iustin Pop
    """Set the speed of the DRBD syncer.
1404 a8083063 Iustin Pop

1405 a8083063 Iustin Pop
    """
1406 a8083063 Iustin Pop
    children_result = super(DRBDev, self).SetSyncSpeed(kbytes)
1407 a8083063 Iustin Pop
    if self.minor is None:
1408 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1409 a8083063 Iustin Pop
      return False
1410 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1411 a8083063 Iustin Pop
                           kbytes])
1412 a8083063 Iustin Pop
    if result.failed:
1413 a8083063 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1414 a8083063 Iustin Pop
    return not result.failed and children_result
1415 a8083063 Iustin Pop
1416 a8083063 Iustin Pop
1417 a8083063 Iustin Pop
  def GetSyncStatus(self):
1418 a8083063 Iustin Pop
    """Returns the sync status of the device.
1419 a8083063 Iustin Pop

1420 a8083063 Iustin Pop
    Returns:
1421 a8083063 Iustin Pop
     (sync_percent, estimated_time)
1422 a8083063 Iustin Pop

1423 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
1424 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
1425 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
1426 a8083063 Iustin Pop

1427 a8083063 Iustin Pop
    """
1428 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1429 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1430 a8083063 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1431 a8083063 Iustin Pop
    if self.minor not in proc_info:
1432 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1433 a8083063 Iustin Pop
                                    self.minor)
1434 a8083063 Iustin Pop
    line = proc_info[self.minor]
1435 a8083063 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1436 a8083063 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1437 a8083063 Iustin Pop
    if match:
1438 a8083063 Iustin Pop
      sync_percent = float(match.group(1))
1439 a8083063 Iustin Pop
      hours = int(match.group(2))
1440 a8083063 Iustin Pop
      minutes = int(match.group(3))
1441 a8083063 Iustin Pop
      seconds = int(match.group(4))
1442 a8083063 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1443 a8083063 Iustin Pop
    else:
1444 a8083063 Iustin Pop
      sync_percent = None
1445 a8083063 Iustin Pop
      est_time = None
1446 a8083063 Iustin Pop
    match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1447 a8083063 Iustin Pop
    if not match:
1448 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1449 a8083063 Iustin Pop
                                    self.minor)
1450 a8083063 Iustin Pop
    client_state = match.group(1)
1451 a8083063 Iustin Pop
    is_degraded = client_state != "Connected"
1452 a8083063 Iustin Pop
    return sync_percent, est_time, is_degraded
1453 a8083063 Iustin Pop
1454 a8083063 Iustin Pop
1455 a8083063 Iustin Pop
  @staticmethod
1456 a8083063 Iustin Pop
  def _MassageProcData(data):
1457 a8083063 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
1458 a8083063 Iustin Pop

1459 a8083063 Iustin Pop
    Returns:
1460 a8083063 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
1461 a8083063 Iustin Pop

1462 a8083063 Iustin Pop
    """
1463 a8083063 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
1464 a8083063 Iustin Pop
    results = {}
1465 a8083063 Iustin Pop
    old_minor = old_line = None
1466 a8083063 Iustin Pop
    for line in data:
1467 a8083063 Iustin Pop
      lresult = lmatch.match(line)
1468 a8083063 Iustin Pop
      if lresult is not None:
1469 a8083063 Iustin Pop
        if old_minor is not None:
1470 a8083063 Iustin Pop
          results[old_minor] = old_line
1471 a8083063 Iustin Pop
        old_minor = int(lresult.group(1))
1472 a8083063 Iustin Pop
        old_line = line
1473 a8083063 Iustin Pop
      else:
1474 a8083063 Iustin Pop
        if old_minor is not None:
1475 a8083063 Iustin Pop
          old_line += " " + line.strip()
1476 a8083063 Iustin Pop
    # add last line
1477 a8083063 Iustin Pop
    if old_minor is not None:
1478 a8083063 Iustin Pop
      results[old_minor] = old_line
1479 a8083063 Iustin Pop
    return results
1480 a8083063 Iustin Pop
1481 a8083063 Iustin Pop
1482 a8083063 Iustin Pop
  def GetStatus(self):
1483 a8083063 Iustin Pop
    """Compute the status of the DRBD device
1484 a8083063 Iustin Pop

1485 a8083063 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1486 a8083063 Iustin Pop

1487 a8083063 Iustin Pop
    """
1488 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1489 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
1490 a8083063 Iustin Pop
1491 a8083063 Iustin Pop
    data = self._GetProcData()
1492 a8083063 Iustin Pop
    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
1493 a8083063 Iustin Pop
                       self.minor)
1494 a8083063 Iustin Pop
    for line in data:
1495 a8083063 Iustin Pop
      mresult = match.match(line)
1496 a8083063 Iustin Pop
      if mresult:
1497 a8083063 Iustin Pop
        break
1498 a8083063 Iustin Pop
    else:
1499 a8083063 Iustin Pop
      logger.Error("Can't find myself!")
1500 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
1501 a8083063 Iustin Pop
1502 a8083063 Iustin Pop
    state = mresult.group(2)
1503 a8083063 Iustin Pop
    if state == "Primary":
1504 a8083063 Iustin Pop
      result = self.STATUS_ONLINE
1505 a8083063 Iustin Pop
    else:
1506 a8083063 Iustin Pop
      result = self.STATUS_STANDBY
1507 a8083063 Iustin Pop
1508 a8083063 Iustin Pop
    return result
1509 a8083063 Iustin Pop
1510 a8083063 Iustin Pop
1511 a8083063 Iustin Pop
  @staticmethod
1512 a8083063 Iustin Pop
  def _ZeroDevice(device):
1513 a8083063 Iustin Pop
    """Zero a device.
1514 a8083063 Iustin Pop

1515 a8083063 Iustin Pop
    This writes until we get ENOSPC.
1516 a8083063 Iustin Pop

1517 a8083063 Iustin Pop
    """
1518 a8083063 Iustin Pop
    f = open(device, "w")
1519 a8083063 Iustin Pop
    buf = "\0" * 1048576
1520 a8083063 Iustin Pop
    try:
1521 a8083063 Iustin Pop
      while True:
1522 a8083063 Iustin Pop
        f.write(buf)
1523 a8083063 Iustin Pop
    except IOError, err:
1524 a8083063 Iustin Pop
      if err.errno != errno.ENOSPC:
1525 a8083063 Iustin Pop
        raise
1526 a8083063 Iustin Pop
1527 a8083063 Iustin Pop
1528 a8083063 Iustin Pop
  @classmethod
1529 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
1530 a8083063 Iustin Pop
    """Create a new DRBD device.
1531 a8083063 Iustin Pop

1532 a8083063 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1533 a8083063 Iustin Pop
    function just zeroes the meta device.
1534 a8083063 Iustin Pop

1535 a8083063 Iustin Pop
    """
1536 a8083063 Iustin Pop
    if len(children) != 2:
1537 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1538 a8083063 Iustin Pop
    meta = children[1]
1539 a8083063 Iustin Pop
    meta.Assemble()
1540 a8083063 Iustin Pop
    if not meta.Attach():
1541 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1542 a8083063 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
1543 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device")
1544 a8083063 Iustin Pop
    logger.Info("Started zeroing device %s" % meta.dev_path)
1545 a8083063 Iustin Pop
    cls._ZeroDevice(meta.dev_path)
1546 a8083063 Iustin Pop
    logger.Info("Done zeroing device %s" % meta.dev_path)
1547 a8083063 Iustin Pop
    return cls(unique_id, children)
1548 a8083063 Iustin Pop
1549 a8083063 Iustin Pop
1550 a8083063 Iustin Pop
  def Remove(self):
1551 a8083063 Iustin Pop
    """Stub remove for DRBD devices.
1552 a8083063 Iustin Pop

1553 a8083063 Iustin Pop
    """
1554 a8083063 Iustin Pop
    return self.Shutdown()
1555 a8083063 Iustin Pop
1556 a8083063 Iustin Pop
1557 a8083063 Iustin Pop
DEV_MAP = {
1558 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1559 fe96220b Iustin Pop
  constants.LD_MD_R1: MDRaid1,
1560 fe96220b Iustin Pop
  constants.LD_DRBD7: DRBDev,
1561 a8083063 Iustin Pop
  }
1562 a8083063 Iustin Pop
1563 a8083063 Iustin Pop
1564 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
1565 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1566 a8083063 Iustin Pop

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

1570 a8083063 Iustin Pop
  """
1571 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1572 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1573 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1574 a8083063 Iustin Pop
  if not device.Attach():
1575 a8083063 Iustin Pop
    return None
1576 a8083063 Iustin Pop
  return  device
1577 a8083063 Iustin Pop
1578 a8083063 Iustin Pop
1579 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
1580 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1581 a8083063 Iustin Pop

1582 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
1583 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
1584 a8083063 Iustin Pop

1585 a8083063 Iustin Pop
  """
1586 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1587 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1588 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1589 a8083063 Iustin Pop
  if not device.Attach():
1590 a8083063 Iustin Pop
    device.Assemble()
1591 a8083063 Iustin Pop
  if not device.Attach():
1592 a8083063 Iustin Pop
    raise errors.BlockDeviceError("Can't find a valid block device for"
1593 a8083063 Iustin Pop
                                  " %s/%s/%s" %
1594 a8083063 Iustin Pop
                                  (dev_type, unique_id, children))
1595 a8083063 Iustin Pop
  return device
1596 a8083063 Iustin Pop
1597 a8083063 Iustin Pop
1598 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1599 a8083063 Iustin Pop
  """Create a device.
1600 a8083063 Iustin Pop

1601 a8083063 Iustin Pop
  """
1602 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1603 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1604 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1605 a8083063 Iustin Pop
  return device