Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 8d528b7c

History | View | Annotate | Download (73 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Block device abstraction"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import re
25 a8083063 Iustin Pop
import time
26 a8083063 Iustin Pop
import errno
27 a2cfdea2 Iustin Pop
import pyparsing as pyp
28 6f695a2e Manuel Franceschini
import os
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
from ganeti import utils
31 a8083063 Iustin Pop
from ganeti import logger
32 a8083063 Iustin Pop
from ganeti import errors
33 fe96220b Iustin Pop
from ganeti import constants
34 a8083063 Iustin Pop
35 a8083063 Iustin Pop
36 a8083063 Iustin Pop
class BlockDev(object):
37 a8083063 Iustin Pop
  """Block device abstract class.
38 a8083063 Iustin Pop

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

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

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

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

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

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

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

81 a8083063 Iustin Pop
  """
82 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
83 a8083063 Iustin Pop
    self._children = children
84 a8083063 Iustin Pop
    self.dev_path = None
85 a8083063 Iustin Pop
    self.unique_id = unique_id
86 a8083063 Iustin Pop
    self.major = None
87 a8083063 Iustin Pop
    self.minor = None
88 a8083063 Iustin Pop
89 a8083063 Iustin Pop
  def Assemble(self):
90 a8083063 Iustin Pop
    """Assemble the device from its components.
91 a8083063 Iustin Pop

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

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

100 a8083063 Iustin Pop
    """
101 a8083063 Iustin Pop
    status = True
102 a8083063 Iustin Pop
    for child in self._children:
103 a8083063 Iustin Pop
      if not isinstance(child, BlockDev):
104 a8083063 Iustin Pop
        raise TypeError("Invalid child passed of type '%s'" % type(child))
105 a8083063 Iustin Pop
      if not status:
106 a8083063 Iustin Pop
        break
107 a8083063 Iustin Pop
      status = status and child.Assemble()
108 a8083063 Iustin Pop
      if not status:
109 a8083063 Iustin Pop
        break
110 fdbd668d Iustin Pop
111 fdbd668d Iustin Pop
      try:
112 fdbd668d Iustin Pop
        child.Open()
113 fdbd668d Iustin Pop
      except errors.BlockDeviceError:
114 fdbd668d Iustin Pop
        for child in self._children:
115 fdbd668d Iustin Pop
          child.Shutdown()
116 fdbd668d Iustin Pop
        raise
117 a8083063 Iustin Pop
118 a8083063 Iustin Pop
    if not status:
119 a8083063 Iustin Pop
      for child in self._children:
120 a8083063 Iustin Pop
        child.Shutdown()
121 a8083063 Iustin Pop
    return status
122 a8083063 Iustin Pop
123 a8083063 Iustin Pop
  def Attach(self):
124 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
125 a8083063 Iustin Pop

126 a8083063 Iustin Pop
    """
127 a8083063 Iustin Pop
    raise NotImplementedError
128 a8083063 Iustin Pop
129 a8083063 Iustin Pop
  def Close(self):
130 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
131 a8083063 Iustin Pop

132 a8083063 Iustin Pop
    """
133 a8083063 Iustin Pop
    raise NotImplementedError
134 a8083063 Iustin Pop
135 a8083063 Iustin Pop
  @classmethod
136 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
137 a8083063 Iustin Pop
    """Create the device.
138 a8083063 Iustin Pop

139 a8083063 Iustin Pop
    If the device cannot be created, it will return None
140 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
141 a8083063 Iustin Pop

142 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
143 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
144 a8083063 Iustin Pop
    enough for both creation and assembly (later).
145 a8083063 Iustin Pop

146 a8083063 Iustin Pop
    """
147 a8083063 Iustin Pop
    raise NotImplementedError
148 a8083063 Iustin Pop
149 a8083063 Iustin Pop
  def Remove(self):
150 a8083063 Iustin Pop
    """Remove this device.
151 a8083063 Iustin Pop

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

156 a8083063 Iustin Pop
    """
157 a8083063 Iustin Pop
    raise NotImplementedError
158 a8083063 Iustin Pop
159 f3e513ad Iustin Pop
  def Rename(self, new_id):
160 f3e513ad Iustin Pop
    """Rename this device.
161 f3e513ad Iustin Pop

162 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
163 f3e513ad Iustin Pop

164 f3e513ad Iustin Pop
    """
165 f3e513ad Iustin Pop
    raise NotImplementedError
166 f3e513ad Iustin Pop
167 a8083063 Iustin Pop
  def Open(self, force=False):
168 a8083063 Iustin Pop
    """Make the device ready for use.
169 a8083063 Iustin Pop

170 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
171 a8083063 Iustin Pop
    devices need this.
172 a8083063 Iustin Pop

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

176 a8083063 Iustin Pop
    """
177 a8083063 Iustin Pop
    raise NotImplementedError
178 a8083063 Iustin Pop
179 a8083063 Iustin Pop
  def Shutdown(self):
180 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
181 a8083063 Iustin Pop

182 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
183 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
184 a8083063 Iustin Pop
    assembled after this call.
185 a8083063 Iustin Pop

186 a8083063 Iustin Pop
    """
187 a8083063 Iustin Pop
    raise NotImplementedError
188 a8083063 Iustin Pop
189 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
190 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
191 a8083063 Iustin Pop

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

194 a8083063 Iustin Pop
    """
195 a8083063 Iustin Pop
    result = True
196 a8083063 Iustin Pop
    if self._children:
197 a8083063 Iustin Pop
      for child in self._children:
198 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
199 a8083063 Iustin Pop
    return result
200 a8083063 Iustin Pop
201 a8083063 Iustin Pop
  def GetSyncStatus(self):
202 a8083063 Iustin Pop
    """Returns the sync status of the device.
203 a8083063 Iustin Pop

204 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
205 a8083063 Iustin Pop
    status of the mirror.
206 a8083063 Iustin Pop

207 a8083063 Iustin Pop
    Returns:
208 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
209 0834c866 Iustin Pop

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

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

215 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
216 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
217 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
218 a8083063 Iustin Pop

219 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
220 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
221 0834c866 Iustin Pop
    return False (not degraded).
222 0834c866 Iustin Pop

223 a8083063 Iustin Pop
    """
224 0834c866 Iustin Pop
    return None, None, False, False
225 a8083063 Iustin Pop
226 a8083063 Iustin Pop
227 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
228 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
229 a8083063 Iustin Pop

230 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
231 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
232 a8083063 Iustin Pop
    children.
233 a8083063 Iustin Pop

234 a8083063 Iustin Pop
    """
235 0834c866 Iustin Pop
    min_percent, max_time, is_degraded, ldisk = self.GetSyncStatus()
236 a8083063 Iustin Pop
    if self._children:
237 a8083063 Iustin Pop
      for child in self._children:
238 0834c866 Iustin Pop
        c_percent, c_time, c_degraded, c_ldisk = child.GetSyncStatus()
239 a8083063 Iustin Pop
        if min_percent is None:
240 a8083063 Iustin Pop
          min_percent = c_percent
241 a8083063 Iustin Pop
        elif c_percent is not None:
242 a8083063 Iustin Pop
          min_percent = min(min_percent, c_percent)
243 a8083063 Iustin Pop
        if max_time is None:
244 a8083063 Iustin Pop
          max_time = c_time
245 a8083063 Iustin Pop
        elif c_time is not None:
246 a8083063 Iustin Pop
          max_time = max(max_time, c_time)
247 a8083063 Iustin Pop
        is_degraded = is_degraded or c_degraded
248 0834c866 Iustin Pop
        ldisk = ldisk or c_ldisk
249 0834c866 Iustin Pop
    return min_percent, max_time, is_degraded, ldisk
250 a8083063 Iustin Pop
251 a8083063 Iustin Pop
252 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
253 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
254 a0c3fea1 Michael Hanselmann

255 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
256 a0c3fea1 Michael Hanselmann

257 a0c3fea1 Michael Hanselmann
    """
258 a0c3fea1 Michael Hanselmann
    for child in self._children:
259 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
260 a0c3fea1 Michael Hanselmann
261 a0c3fea1 Michael Hanselmann
262 a8083063 Iustin Pop
  def __repr__(self):
263 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
264 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
265 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
266 a8083063 Iustin Pop
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
269 a8083063 Iustin Pop
  """Logical Volume block device.
270 a8083063 Iustin Pop

271 a8083063 Iustin Pop
  """
272 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
273 a8083063 Iustin Pop
    """Attaches to a LV device.
274 a8083063 Iustin Pop

275 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
276 a8083063 Iustin Pop

277 a8083063 Iustin Pop
    """
278 a8083063 Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children)
279 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
280 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
281 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
282 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
283 a8083063 Iustin Pop
    self.Attach()
284 a8083063 Iustin Pop
285 a8083063 Iustin Pop
  @classmethod
286 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
287 a8083063 Iustin Pop
    """Create a new logical volume.
288 a8083063 Iustin Pop

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

318 a8083063 Iustin Pop
    Args:
319 a8083063 Iustin Pop
      vg_name: the volume group name
320 a8083063 Iustin Pop

321 a8083063 Iustin Pop
    Returns:
322 a8083063 Iustin Pop
      list of (free_space, name) with free_space in mebibytes
323 098c0958 Michael Hanselmann

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

348 a8083063 Iustin Pop
    """
349 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
350 a8083063 Iustin Pop
      # the LV does not exist
351 a8083063 Iustin Pop
      return True
352 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
353 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
354 a8083063 Iustin Pop
    if result.failed:
355 a8083063 Iustin Pop
      logger.Error("Can't lvremove: %s" % result.fail_reason)
356 a8083063 Iustin Pop
357 a8083063 Iustin Pop
    return not result.failed
358 a8083063 Iustin Pop
359 f3e513ad Iustin Pop
  def Rename(self, new_id):
360 f3e513ad Iustin Pop
    """Rename this logical volume.
361 f3e513ad Iustin Pop

362 f3e513ad Iustin Pop
    """
363 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
364 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
365 f3e513ad Iustin Pop
    new_vg, new_name = new_id
366 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
367 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
368 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
369 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
370 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
371 f3e513ad Iustin Pop
    if result.failed:
372 f3e513ad Iustin Pop
      raise errors.BlockDeviceError("Failed to rename the logical volume: %s" %
373 f3e513ad Iustin Pop
                                    result.output)
374 be345db0 Iustin Pop
    self._lv_name = new_name
375 be345db0 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
376 be345db0 Iustin Pop
377 a8083063 Iustin Pop
  def Attach(self):
378 a8083063 Iustin Pop
    """Attach to an existing LV.
379 a8083063 Iustin Pop

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

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

402 5574047a Iustin Pop
    We alway run `lvchange -ay` on the LV to ensure it's active before
403 5574047a Iustin Pop
    use, as there were cases when xenvg was not active after boot
404 5574047a Iustin Pop
    (also possibly after disk issues).
405 a8083063 Iustin Pop

406 a8083063 Iustin Pop
    """
407 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
408 5574047a Iustin Pop
    if result.failed:
409 5574047a Iustin Pop
      logger.Error("Can't activate lv %s: %s" % (self.dev_path, result.output))
410 5574047a Iustin Pop
    return not result.failed
411 a8083063 Iustin Pop
412 a8083063 Iustin Pop
  def Shutdown(self):
413 a8083063 Iustin Pop
    """Shutdown the device.
414 a8083063 Iustin Pop

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

418 a8083063 Iustin Pop
    """
419 a8083063 Iustin Pop
    return True
420 a8083063 Iustin Pop
421 9db6dbce Iustin Pop
  def GetSyncStatus(self):
422 9db6dbce Iustin Pop
    """Returns the sync status of the device.
423 9db6dbce Iustin Pop

424 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
425 9db6dbce Iustin Pop
    status of the mirror.
426 9db6dbce Iustin Pop

427 9db6dbce Iustin Pop
    Returns:
428 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
429 9db6dbce Iustin Pop

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

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

441 9db6dbce Iustin Pop
    """
442 9db6dbce Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
443 9db6dbce Iustin Pop
    if result.failed:
444 9db6dbce Iustin Pop
      logger.Error("Can't display lv: %s" % result.fail_reason)
445 0834c866 Iustin Pop
      return None, None, True, True
446 9db6dbce Iustin Pop
    out = result.stdout.strip()
447 9db6dbce Iustin Pop
    # format: type/permissions/alloc/fixed_minor/state/open
448 9db6dbce Iustin Pop
    if len(out) != 6:
449 0834c866 Iustin Pop
      logger.Debug("Error in lvs output: attrs=%s, len != 6" % out)
450 0834c866 Iustin Pop
      return None, None, True, True
451 0834c866 Iustin Pop
    ldisk = out[0] == 'v' # virtual volume, i.e. doesn't have
452 0834c866 Iustin Pop
                          # backing storage
453 0834c866 Iustin Pop
    return None, None, ldisk, ldisk
454 9db6dbce Iustin Pop
455 a8083063 Iustin Pop
  def Open(self, force=False):
456 a8083063 Iustin Pop
    """Make the device ready for I/O.
457 a8083063 Iustin Pop

458 a8083063 Iustin Pop
    This is a no-op for the LV device type.
459 a8083063 Iustin Pop

460 a8083063 Iustin Pop
    """
461 fdbd668d Iustin Pop
    pass
462 a8083063 Iustin Pop
463 a8083063 Iustin Pop
  def Close(self):
464 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
465 a8083063 Iustin Pop

466 a8083063 Iustin Pop
    This is a no-op for the LV device type.
467 a8083063 Iustin Pop

468 a8083063 Iustin Pop
    """
469 fdbd668d Iustin Pop
    pass
470 a8083063 Iustin Pop
471 a8083063 Iustin Pop
  def Snapshot(self, size):
472 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
473 a8083063 Iustin Pop

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

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

523 a8083063 Iustin Pop
  """
524 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
525 a8083063 Iustin Pop
    super(MDRaid1, self).__init__(unique_id, children)
526 a8083063 Iustin Pop
    self.major = 9
527 a8083063 Iustin Pop
    self.Attach()
528 a8083063 Iustin Pop
529 a8083063 Iustin Pop
  def Attach(self):
530 a8083063 Iustin Pop
    """Find an array which matches our config and attach to it.
531 a8083063 Iustin Pop

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

534 a8083063 Iustin Pop
    """
535 a8083063 Iustin Pop
    minor = self._FindMDByUUID(self.unique_id)
536 a8083063 Iustin Pop
    if minor is not None:
537 a8083063 Iustin Pop
      self._SetFromMinor(minor)
538 a8083063 Iustin Pop
    else:
539 a8083063 Iustin Pop
      self.minor = None
540 a8083063 Iustin Pop
      self.dev_path = None
541 a8083063 Iustin Pop
542 a8083063 Iustin Pop
    return (minor is not None)
543 a8083063 Iustin Pop
544 a8083063 Iustin Pop
  @staticmethod
545 a8083063 Iustin Pop
  def _GetUsedDevs():
546 a8083063 Iustin Pop
    """Compute the list of in-use MD devices.
547 a8083063 Iustin Pop

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

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

570 a8083063 Iustin Pop
    Currently only uuid is returned.
571 a8083063 Iustin Pop

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

592 a8083063 Iustin Pop
    This code assumes that there are 256 minors only.
593 a8083063 Iustin Pop

594 a8083063 Iustin Pop
    """
595 a8083063 Iustin Pop
    used_md = MDRaid1._GetUsedDevs()
596 a8083063 Iustin Pop
    i = 0
597 a8083063 Iustin Pop
    while i < 256:
598 a8083063 Iustin Pop
      if i not in used_md:
599 a8083063 Iustin Pop
        break
600 a8083063 Iustin Pop
      i += 1
601 a8083063 Iustin Pop
    if i == 256:
602 a8083063 Iustin Pop
      logger.Error("Critical: Out of md minor numbers.")
603 0caf6485 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free MD minor")
604 a8083063 Iustin Pop
    return i
605 a8083063 Iustin Pop
606 a8083063 Iustin Pop
  @classmethod
607 a8083063 Iustin Pop
  def _FindMDByUUID(cls, uuid):
608 a8083063 Iustin Pop
    """Find the minor of an MD array with a given UUID.
609 a8083063 Iustin Pop

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

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

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

633 1a87dca7 Iustin Pop
    To cover all situations, the zero-ing will be:
634 1a87dca7 Iustin Pop
      - 0k to 128k
635 1a87dca7 Iustin Pop
      - -128k to end
636 1a87dca7 Iustin Pop

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

640 1a87dca7 Iustin Pop
    Note that this function depends on the fact that one can open,
641 1a87dca7 Iustin Pop
    read and write block devices normally.
642 1a87dca7 Iustin Pop

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

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

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

700 a8083063 Iustin Pop
    """
701 a8083063 Iustin Pop
    #TODO: maybe zero superblock on child devices?
702 a8083063 Iustin Pop
    return self.Shutdown()
703 a8083063 Iustin Pop
704 f3e513ad Iustin Pop
  def Rename(self, new_id):
705 f3e513ad Iustin Pop
    """Rename a device.
706 f3e513ad Iustin Pop

707 f3e513ad Iustin Pop
    This is not supported for md raid1 devices.
708 f3e513ad Iustin Pop

709 f3e513ad Iustin Pop
    """
710 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a md raid1 device")
711 a8083063 Iustin Pop
712 153d9724 Iustin Pop
  def AddChildren(self, devices):
713 153d9724 Iustin Pop
    """Add new member(s) to the md raid1.
714 a8083063 Iustin Pop

715 a8083063 Iustin Pop
    """
716 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
717 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device")
718 153d9724 Iustin Pop
719 153d9724 Iustin Pop
    args = ["mdadm", "-a", self.dev_path]
720 153d9724 Iustin Pop
    for dev in devices:
721 153d9724 Iustin Pop
      if dev.dev_path is None:
722 153d9724 Iustin Pop
        raise errors.BlockDeviceError("Child '%s' is not initialised" % dev)
723 153d9724 Iustin Pop
      dev.Open()
724 153d9724 Iustin Pop
      args.append(dev.dev_path)
725 153d9724 Iustin Pop
    result = utils.RunCmd(args)
726 a8083063 Iustin Pop
    if result.failed:
727 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Failed to add new device to array: %s" %
728 3ecf6786 Iustin Pop
                                    result.output)
729 153d9724 Iustin Pop
    new_len = len(self._children) + len(devices)
730 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", self.dev_path, "-n", new_len])
731 a8083063 Iustin Pop
    if result.failed:
732 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't grow md array: %s" %
733 3ecf6786 Iustin Pop
                                    result.output)
734 153d9724 Iustin Pop
    self._children.extend(devices)
735 a8083063 Iustin Pop
736 153d9724 Iustin Pop
  def RemoveChildren(self, devices):
737 153d9724 Iustin Pop
    """Remove member(s) from the md raid1.
738 a8083063 Iustin Pop

739 a8083063 Iustin Pop
    """
740 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
741 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device")
742 153d9724 Iustin Pop
    new_len = len(self._children) - len(devices)
743 153d9724 Iustin Pop
    if new_len < 1:
744 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Can't reduce to less than one child")
745 153d9724 Iustin Pop
    args = ["mdadm", "-f", self.dev_path]
746 153d9724 Iustin Pop
    orig_devs = []
747 153d9724 Iustin Pop
    for dev in devices:
748 e739bd57 Iustin Pop
      args.append(dev)
749 153d9724 Iustin Pop
      for c in self._children:
750 e739bd57 Iustin Pop
        if c.dev_path == dev:
751 153d9724 Iustin Pop
          orig_devs.append(c)
752 153d9724 Iustin Pop
          break
753 153d9724 Iustin Pop
      else:
754 153d9724 Iustin Pop
        raise errors.BlockDeviceError("Can't find device '%s' for removal" %
755 153d9724 Iustin Pop
                                      dev)
756 153d9724 Iustin Pop
    result = utils.RunCmd(args)
757 a8083063 Iustin Pop
    if result.failed:
758 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Failed to mark device(s) as failed: %s" %
759 3ecf6786 Iustin Pop
                                    result.output)
760 a8083063 Iustin Pop
761 a8083063 Iustin Pop
    # it seems here we need a short delay for MD to update its
762 a8083063 Iustin Pop
    # superblocks
763 a8083063 Iustin Pop
    time.sleep(0.5)
764 153d9724 Iustin Pop
    args[1] = "-r"
765 153d9724 Iustin Pop
    result = utils.RunCmd(args)
766 a8083063 Iustin Pop
    if result.failed:
767 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Failed to remove device(s) from array:"
768 153d9724 Iustin Pop
                                    " %s" % result.output)
769 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", "--force", self.dev_path,
770 a8083063 Iustin Pop
                           "-n", new_len])
771 a8083063 Iustin Pop
    if result.failed:
772 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't shrink md array: %s" %
773 3ecf6786 Iustin Pop
                                    result.output)
774 153d9724 Iustin Pop
    for dev in orig_devs:
775 153d9724 Iustin Pop
      self._children.remove(dev)
776 a8083063 Iustin Pop
777 a8083063 Iustin Pop
  def _SetFromMinor(self, minor):
778 a8083063 Iustin Pop
    """Set our parameters based on the given minor.
779 a8083063 Iustin Pop

780 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
781 a8083063 Iustin Pop

782 a8083063 Iustin Pop
    """
783 a8083063 Iustin Pop
    self.minor = minor
784 a8083063 Iustin Pop
    self.dev_path = "/dev/md%d" % minor
785 a8083063 Iustin Pop
786 a8083063 Iustin Pop
  def Assemble(self):
787 a8083063 Iustin Pop
    """Assemble the MD device.
788 a8083063 Iustin Pop

789 a8083063 Iustin Pop
    At this point we should have:
790 a8083063 Iustin Pop
      - list of children devices
791 a8083063 Iustin Pop
      - uuid
792 a8083063 Iustin Pop

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

819 a8083063 Iustin Pop
    This does a 'mdadm --stop' so after this command, the array is no
820 a8083063 Iustin Pop
    longer available.
821 a8083063 Iustin Pop

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

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

858 a8083063 Iustin Pop
    Returns:
859 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
860 a8083063 Iustin Pop

861 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
862 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
863 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
864 0834c866 Iustin Pop

865 0834c866 Iustin Pop
    The ldisk parameter is always true for MD devices.
866 a8083063 Iustin Pop

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

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

901 a8083063 Iustin Pop
    """
902 fdbd668d Iustin Pop
    pass
903 a8083063 Iustin Pop
904 a8083063 Iustin Pop
  def Close(self):
905 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
906 a8083063 Iustin Pop

907 a8083063 Iustin Pop
    This is a no-op for the MDRaid1 device type, but see comment for
908 a8083063 Iustin Pop
    `Open()`.
909 a8083063 Iustin Pop

910 a8083063 Iustin Pop
    """
911 fdbd668d Iustin Pop
    pass
912 a8083063 Iustin Pop
913 a8083063 Iustin Pop
914 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
915 0f7f32d9 Iustin Pop
  """Base DRBD class.
916 a8083063 Iustin Pop

917 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
918 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
919 0f7f32d9 Iustin Pop

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

933 0f7f32d9 Iustin Pop
    """
934 0f7f32d9 Iustin Pop
    stat = open("/proc/drbd", "r")
935 0f7f32d9 Iustin Pop
    try:
936 0f7f32d9 Iustin Pop
      data = stat.read().splitlines()
937 0f7f32d9 Iustin Pop
    finally:
938 0f7f32d9 Iustin Pop
      stat.close()
939 0f7f32d9 Iustin Pop
    if not data:
940 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't read any data from /proc/drbd")
941 0f7f32d9 Iustin Pop
    return data
942 0f7f32d9 Iustin Pop
943 5a47ad20 Iustin Pop
  @staticmethod
944 5a47ad20 Iustin Pop
  def _MassageProcData(data):
945 5a47ad20 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
946 5a47ad20 Iustin Pop

947 5a47ad20 Iustin Pop
    Returns:
948 5a47ad20 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
949 5a47ad20 Iustin Pop

950 5a47ad20 Iustin Pop
    """
951 5a47ad20 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
952 5a47ad20 Iustin Pop
    results = {}
953 5a47ad20 Iustin Pop
    old_minor = old_line = None
954 5a47ad20 Iustin Pop
    for line in data:
955 5a47ad20 Iustin Pop
      lresult = lmatch.match(line)
956 5a47ad20 Iustin Pop
      if lresult is not None:
957 5a47ad20 Iustin Pop
        if old_minor is not None:
958 5a47ad20 Iustin Pop
          results[old_minor] = old_line
959 5a47ad20 Iustin Pop
        old_minor = int(lresult.group(1))
960 5a47ad20 Iustin Pop
        old_line = line
961 5a47ad20 Iustin Pop
      else:
962 5a47ad20 Iustin Pop
        if old_minor is not None:
963 5a47ad20 Iustin Pop
          old_line += " " + line.strip()
964 5a47ad20 Iustin Pop
    # add last line
965 5a47ad20 Iustin Pop
    if old_minor is not None:
966 5a47ad20 Iustin Pop
      results[old_minor] = old_line
967 5a47ad20 Iustin Pop
    return results
968 5a47ad20 Iustin Pop
969 0f7f32d9 Iustin Pop
  @classmethod
970 0f7f32d9 Iustin Pop
  def _GetVersion(cls):
971 0f7f32d9 Iustin Pop
    """Return the DRBD version.
972 0f7f32d9 Iustin Pop

973 c3f9340c Guido Trotter
    This will return a dict with keys:
974 c3f9340c Guido Trotter
      k_major,
975 c3f9340c Guido Trotter
      k_minor,
976 c3f9340c Guido Trotter
      k_point,
977 c3f9340c Guido Trotter
      api,
978 c3f9340c Guido Trotter
      proto,
979 c3f9340c Guido Trotter
      proto2 (only on drbd > 8.2.X)
980 0f7f32d9 Iustin Pop

981 0f7f32d9 Iustin Pop
    """
982 0f7f32d9 Iustin Pop
    proc_data = cls._GetProcData()
983 0f7f32d9 Iustin Pop
    first_line = proc_data[0].strip()
984 0f7f32d9 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
985 0f7f32d9 Iustin Pop
    if not version:
986 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
987 0f7f32d9 Iustin Pop
                                    first_line)
988 c3f9340c Guido Trotter
989 c3f9340c Guido Trotter
    values = version.groups()
990 c3f9340c Guido Trotter
    retval = {'k_major': int(values[0]),
991 c3f9340c Guido Trotter
              'k_minor': int(values[1]),
992 c3f9340c Guido Trotter
              'k_point': int(values[2]),
993 c3f9340c Guido Trotter
              'api': int(values[3]),
994 c3f9340c Guido Trotter
              'proto': int(values[4]),
995 c3f9340c Guido Trotter
             }
996 c3f9340c Guido Trotter
    if values[5] is not None:
997 c3f9340c Guido Trotter
      retval['proto2'] = values[5]
998 c3f9340c Guido Trotter
999 c3f9340c Guido Trotter
    return retval
1000 0f7f32d9 Iustin Pop
1001 770fe0f9 Iustin Pop
  @staticmethod
1002 770fe0f9 Iustin Pop
  def _DevPath(minor):
1003 770fe0f9 Iustin Pop
    """Return the path to a drbd device for a given minor.
1004 770fe0f9 Iustin Pop

1005 770fe0f9 Iustin Pop
    """
1006 770fe0f9 Iustin Pop
    return "/dev/drbd%d" % minor
1007 770fe0f9 Iustin Pop
1008 770fe0f9 Iustin Pop
  @classmethod
1009 770fe0f9 Iustin Pop
  def _GetUsedDevs(cls):
1010 770fe0f9 Iustin Pop
    """Compute the list of used DRBD devices.
1011 770fe0f9 Iustin Pop

1012 770fe0f9 Iustin Pop
    """
1013 770fe0f9 Iustin Pop
    data = cls._GetProcData()
1014 770fe0f9 Iustin Pop
1015 770fe0f9 Iustin Pop
    used_devs = {}
1016 770fe0f9 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
1017 770fe0f9 Iustin Pop
    for line in data:
1018 770fe0f9 Iustin Pop
      match = valid_line.match(line)
1019 770fe0f9 Iustin Pop
      if not match:
1020 770fe0f9 Iustin Pop
        continue
1021 770fe0f9 Iustin Pop
      minor = int(match.group(1))
1022 770fe0f9 Iustin Pop
      state = match.group(2)
1023 770fe0f9 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1024 770fe0f9 Iustin Pop
        continue
1025 770fe0f9 Iustin Pop
      used_devs[minor] = state, line
1026 770fe0f9 Iustin Pop
1027 770fe0f9 Iustin Pop
    return used_devs
1028 770fe0f9 Iustin Pop
1029 5a47ad20 Iustin Pop
  def _SetFromMinor(self, minor):
1030 5a47ad20 Iustin Pop
    """Set our parameters based on the given minor.
1031 5a47ad20 Iustin Pop

1032 5a47ad20 Iustin Pop
    This sets our minor variable and our dev_path.
1033 5a47ad20 Iustin Pop

1034 5a47ad20 Iustin Pop
    """
1035 5a47ad20 Iustin Pop
    if minor is None:
1036 5a47ad20 Iustin Pop
      self.minor = self.dev_path = None
1037 5a47ad20 Iustin Pop
    else:
1038 5a47ad20 Iustin Pop
      self.minor = minor
1039 5a47ad20 Iustin Pop
      self.dev_path = self._DevPath(minor)
1040 5a47ad20 Iustin Pop
1041 ae26a287 Iustin Pop
  @staticmethod
1042 ae26a287 Iustin Pop
  def _CheckMetaSize(meta_device):
1043 ae26a287 Iustin Pop
    """Check if the given meta device looks like a valid one.
1044 ae26a287 Iustin Pop

1045 ae26a287 Iustin Pop
    This currently only check the size, which must be around
1046 ae26a287 Iustin Pop
    128MiB.
1047 ae26a287 Iustin Pop

1048 ae26a287 Iustin Pop
    """
1049 ae26a287 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1050 ae26a287 Iustin Pop
    if result.failed:
1051 ae26a287 Iustin Pop
      logger.Error("Failed to get device size: %s" % result.fail_reason)
1052 ae26a287 Iustin Pop
      return False
1053 ae26a287 Iustin Pop
    try:
1054 ae26a287 Iustin Pop
      sectors = int(result.stdout)
1055 ae26a287 Iustin Pop
    except ValueError:
1056 ae26a287 Iustin Pop
      logger.Error("Invalid output from blockdev: '%s'" % result.stdout)
1057 ae26a287 Iustin Pop
      return False
1058 ae26a287 Iustin Pop
    bytes = sectors * 512
1059 ae26a287 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
1060 ae26a287 Iustin Pop
      logger.Error("Meta device too small (%.2fMib)" % (bytes / 1024 / 1024))
1061 ae26a287 Iustin Pop
      return False
1062 ae26a287 Iustin Pop
    if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
1063 ae26a287 Iustin Pop
      logger.Error("Meta device too big (%.2fMiB)" % (bytes / 1024 / 1024))
1064 ae26a287 Iustin Pop
      return False
1065 ae26a287 Iustin Pop
    return True
1066 ae26a287 Iustin Pop
1067 f3e513ad Iustin Pop
  def Rename(self, new_id):
1068 f3e513ad Iustin Pop
    """Rename a device.
1069 f3e513ad Iustin Pop

1070 f3e513ad Iustin Pop
    This is not supported for drbd devices.
1071 f3e513ad Iustin Pop

1072 f3e513ad Iustin Pop
    """
1073 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1074 f3e513ad Iustin Pop
1075 0f7f32d9 Iustin Pop
1076 0f7f32d9 Iustin Pop
class DRBDev(BaseDRBD):
1077 a8083063 Iustin Pop
  """DRBD block device.
1078 a8083063 Iustin Pop

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

1083 a8083063 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1084 a8083063 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1085 a8083063 Iustin Pop
  data device and the meta_device. The meta device is checked for
1086 a8083063 Iustin Pop
  valid size and is zeroed on create.
1087 a8083063 Iustin Pop

1088 a8083063 Iustin Pop
  """
1089 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
1090 a8083063 Iustin Pop
    super(DRBDev, self).__init__(unique_id, children)
1091 a8083063 Iustin Pop
    self.major = self._DRBD_MAJOR
1092 c3f9340c Guido Trotter
    version = self._GetVersion()
1093 c3f9340c Guido Trotter
    if version['k_major'] != 0 and version['k_minor'] != 7:
1094 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1095 0f7f32d9 Iustin Pop
                                    " requested ganeti usage: kernel is"
1096 c3f9340c Guido Trotter
                                    " %s.%s, ganeti wants 0.7" %
1097 c3f9340c Guido Trotter
                                    (version['k_major'], version['k_minor']))
1098 a8083063 Iustin Pop
    if len(children) != 2:
1099 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1100 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1101 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1102 a8083063 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
1103 a8083063 Iustin Pop
    self.Attach()
1104 a8083063 Iustin Pop
1105 a8083063 Iustin Pop
  @classmethod
1106 a8083063 Iustin Pop
  def _FindUnusedMinor(cls):
1107 a8083063 Iustin Pop
    """Find an unused DRBD device.
1108 a8083063 Iustin Pop

1109 a8083063 Iustin Pop
    """
1110 a8083063 Iustin Pop
    data = cls._GetProcData()
1111 a8083063 Iustin Pop
1112 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1113 a8083063 Iustin Pop
    for line in data:
1114 a8083063 Iustin Pop
      match = valid_line.match(line)
1115 a8083063 Iustin Pop
      if match:
1116 a8083063 Iustin Pop
        return int(match.group(1))
1117 a8083063 Iustin Pop
    logger.Error("Error: no free drbd minors!")
1118 0caf6485 Iustin Pop
    raise errors.BlockDeviceError("Can't find a free DRBD minor")
1119 a8083063 Iustin Pop
1120 a8083063 Iustin Pop
  @classmethod
1121 a8083063 Iustin Pop
  def _GetDevInfo(cls, minor):
1122 a8083063 Iustin Pop
    """Get details about a given DRBD minor.
1123 a8083063 Iustin Pop

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

1127 a8083063 Iustin Pop
    """
1128 a8083063 Iustin Pop
    data = {}
1129 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1130 a8083063 Iustin Pop
    if result.failed:
1131 a8083063 Iustin Pop
      logger.Error("Can't display the drbd config: %s" % result.fail_reason)
1132 a8083063 Iustin Pop
      return data
1133 a8083063 Iustin Pop
    out = result.stdout
1134 a8083063 Iustin Pop
    if out == "Not configured\n":
1135 a8083063 Iustin Pop
      return data
1136 a8083063 Iustin Pop
    for line in out.splitlines():
1137 a8083063 Iustin Pop
      if "local_dev" not in data:
1138 a8083063 Iustin Pop
        match = re.match("^Lower device: ([0-9]+):([0-9]+) .*$", line)
1139 a8083063 Iustin Pop
        if match:
1140 a8083063 Iustin Pop
          data["local_dev"] = (int(match.group(1)), int(match.group(2)))
1141 a8083063 Iustin Pop
          continue
1142 a8083063 Iustin Pop
      if "meta_dev" not in data:
1143 a8083063 Iustin Pop
        match = re.match("^Meta device: (([0-9]+):([0-9]+)|internal).*$", line)
1144 a8083063 Iustin Pop
        if match:
1145 a8083063 Iustin Pop
          if match.group(2) is not None and match.group(3) is not None:
1146 a8083063 Iustin Pop
            # matched on the major/minor
1147 a8083063 Iustin Pop
            data["meta_dev"] = (int(match.group(2)), int(match.group(3)))
1148 a8083063 Iustin Pop
          else:
1149 a8083063 Iustin Pop
            # matched on the "internal" string
1150 a8083063 Iustin Pop
            data["meta_dev"] = match.group(1)
1151 a8083063 Iustin Pop
            # in this case, no meta_index is in the output
1152 a8083063 Iustin Pop
            data["meta_index"] = -1
1153 a8083063 Iustin Pop
          continue
1154 a8083063 Iustin Pop
      if "meta_index" not in data:
1155 a8083063 Iustin Pop
        match = re.match("^Meta index: ([0-9]+).*$", line)
1156 a8083063 Iustin Pop
        if match:
1157 a8083063 Iustin Pop
          data["meta_index"] = int(match.group(1))
1158 a8083063 Iustin Pop
          continue
1159 a8083063 Iustin Pop
      if "local_addr" not in data:
1160 a8083063 Iustin Pop
        match = re.match("^Local address: ([0-9.]+):([0-9]+)$", line)
1161 a8083063 Iustin Pop
        if match:
1162 a8083063 Iustin Pop
          data["local_addr"] = (match.group(1), int(match.group(2)))
1163 a8083063 Iustin Pop
          continue
1164 a8083063 Iustin Pop
      if "remote_addr" not in data:
1165 a8083063 Iustin Pop
        match = re.match("^Remote address: ([0-9.]+):([0-9]+)$", line)
1166 a8083063 Iustin Pop
        if match:
1167 a8083063 Iustin Pop
          data["remote_addr"] = (match.group(1), int(match.group(2)))
1168 a8083063 Iustin Pop
          continue
1169 a8083063 Iustin Pop
    return data
1170 a8083063 Iustin Pop
1171 a8083063 Iustin Pop
  def _MatchesLocal(self, info):
1172 a8083063 Iustin Pop
    """Test if our local config matches with an existing device.
1173 a8083063 Iustin Pop

1174 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1175 a8083063 Iustin Pop
    method tests if our local backing device is the same as the one in
1176 a8083063 Iustin Pop
    the info parameter, in effect testing if we look like the given
1177 a8083063 Iustin Pop
    device.
1178 a8083063 Iustin Pop

1179 a8083063 Iustin Pop
    """
1180 a8083063 Iustin Pop
    if not ("local_dev" in info and "meta_dev" in info and
1181 a8083063 Iustin Pop
            "meta_index" in info):
1182 a8083063 Iustin Pop
      return False
1183 a8083063 Iustin Pop
1184 a8083063 Iustin Pop
    backend = self._children[0]
1185 a8083063 Iustin Pop
    if backend is not None:
1186 a8083063 Iustin Pop
      retval = (info["local_dev"] == (backend.major, backend.minor))
1187 a8083063 Iustin Pop
    else:
1188 a8083063 Iustin Pop
      retval = (info["local_dev"] == (0, 0))
1189 a8083063 Iustin Pop
    meta = self._children[1]
1190 a8083063 Iustin Pop
    if meta is not None:
1191 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == (meta.major, meta.minor))
1192 a8083063 Iustin Pop
      retval = retval and (info["meta_index"] == 0)
1193 a8083063 Iustin Pop
    else:
1194 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == "internal" and
1195 a8083063 Iustin Pop
                           info["meta_index"] == -1)
1196 a8083063 Iustin Pop
    return retval
1197 a8083063 Iustin Pop
1198 a8083063 Iustin Pop
  def _MatchesNet(self, info):
1199 a8083063 Iustin Pop
    """Test if our network config matches with an existing device.
1200 a8083063 Iustin Pop

1201 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1202 a8083063 Iustin Pop
    method tests if our network configuration is the same as the one
1203 a8083063 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1204 a8083063 Iustin Pop
    device.
1205 a8083063 Iustin Pop

1206 a8083063 Iustin Pop
    """
1207 a8083063 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1208 a8083063 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1209 a8083063 Iustin Pop
      return True
1210 a8083063 Iustin Pop
1211 a8083063 Iustin Pop
    if self._lhost is None:
1212 a8083063 Iustin Pop
      return False
1213 a8083063 Iustin Pop
1214 a8083063 Iustin Pop
    if not ("local_addr" in info and
1215 a8083063 Iustin Pop
            "remote_addr" in info):
1216 a8083063 Iustin Pop
      return False
1217 a8083063 Iustin Pop
1218 a8083063 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1219 a8083063 Iustin Pop
    retval = (retval and
1220 a8083063 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1221 a8083063 Iustin Pop
    return retval
1222 a8083063 Iustin Pop
1223 a8083063 Iustin Pop
  @classmethod
1224 a8083063 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1225 a8083063 Iustin Pop
    """Configure the local part of a DRBD device.
1226 a8083063 Iustin Pop

1227 a8083063 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1228 a8083063 Iustin Pop
    device. And it must be done only once.
1229 a8083063 Iustin Pop

1230 a8083063 Iustin Pop
    """
1231 ae26a287 Iustin Pop
    if not cls._CheckMetaSize(meta):
1232 a8083063 Iustin Pop
      return False
1233 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1234 a8083063 Iustin Pop
                           backend, meta, "0", "-e", "detach"])
1235 a8083063 Iustin Pop
    if result.failed:
1236 a8083063 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1237 a8083063 Iustin Pop
    return not result.failed
1238 a8083063 Iustin Pop
1239 a8083063 Iustin Pop
  @classmethod
1240 a8083063 Iustin Pop
  def _ShutdownLocal(cls, minor):
1241 a8083063 Iustin Pop
    """Detach from the local device.
1242 a8083063 Iustin Pop

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

1246 a8083063 Iustin Pop
    """
1247 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1248 a8083063 Iustin Pop
    if result.failed:
1249 a8083063 Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
1250 a8083063 Iustin Pop
    return not result.failed
1251 a8083063 Iustin Pop
1252 a8083063 Iustin Pop
  @staticmethod
1253 a8083063 Iustin Pop
  def _ShutdownAll(minor):
1254 a8083063 Iustin Pop
    """Deactivate the device.
1255 a8083063 Iustin Pop

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

1258 a8083063 Iustin Pop
    """
1259 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", DRBDev._DevPath(minor), "down"])
1260 a8083063 Iustin Pop
    if result.failed:
1261 a8083063 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
1262 a8083063 Iustin Pop
    return not result.failed
1263 a8083063 Iustin Pop
1264 a8083063 Iustin Pop
  @classmethod
1265 a8083063 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol):
1266 a8083063 Iustin Pop
    """Configure the network part of the device.
1267 a8083063 Iustin Pop

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

1275 a8083063 Iustin Pop
    """
1276 a8083063 Iustin Pop
    lhost, lport, rhost, rport = net_info
1277 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "net",
1278 a8083063 Iustin Pop
                           "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport),
1279 a8083063 Iustin Pop
                           protocol])
1280 a8083063 Iustin Pop
    if result.failed:
1281 a8083063 Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s" %
1282 a8083063 Iustin Pop
                   result.fail_reason)
1283 a8083063 Iustin Pop
      return False
1284 a8083063 Iustin Pop
1285 a8083063 Iustin Pop
    timeout = time.time() + 10
1286 a8083063 Iustin Pop
    ok = False
1287 a8083063 Iustin Pop
    while time.time() < timeout:
1288 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
1289 a8083063 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1290 a8083063 Iustin Pop
        time.sleep(1)
1291 a8083063 Iustin Pop
        continue
1292 a8083063 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1293 a8083063 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1294 a8083063 Iustin Pop
        time.sleep(1)
1295 a8083063 Iustin Pop
        continue
1296 a8083063 Iustin Pop
      ok = True
1297 a8083063 Iustin Pop
      break
1298 a8083063 Iustin Pop
    if not ok:
1299 a8083063 Iustin Pop
      logger.Error("Timeout while configuring network")
1300 a8083063 Iustin Pop
      return False
1301 a8083063 Iustin Pop
    return True
1302 a8083063 Iustin Pop
1303 a8083063 Iustin Pop
  @classmethod
1304 a8083063 Iustin Pop
  def _ShutdownNet(cls, minor):
1305 a8083063 Iustin Pop
    """Disconnect from the remote peer.
1306 a8083063 Iustin Pop

1307 a8083063 Iustin Pop
    This fails if we don't have a local device.
1308 a8083063 Iustin Pop

1309 a8083063 Iustin Pop
    """
1310 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1311 a8459f1c Iustin Pop
    if result.failed:
1312 a8459f1c Iustin Pop
      logger.Error("Can't shutdown network: %s" % result.output)
1313 a8083063 Iustin Pop
    return not result.failed
1314 a8083063 Iustin Pop
1315 a8083063 Iustin Pop
  def Assemble(self):
1316 a8083063 Iustin Pop
    """Assemble the drbd.
1317 a8083063 Iustin Pop

1318 a8083063 Iustin Pop
    Method:
1319 a8083063 Iustin Pop
      - if we have a local backing device, we bind to it by:
1320 a8083063 Iustin Pop
        - checking the list of used drbd devices
1321 a8083063 Iustin Pop
        - check if the local minor use of any of them is our own device
1322 a8083063 Iustin Pop
        - if yes, abort?
1323 a8083063 Iustin Pop
        - if not, bind
1324 a8083063 Iustin Pop
      - if we have a local/remote net info:
1325 a8083063 Iustin Pop
        - redo the local backing device step for the remote device
1326 a8083063 Iustin Pop
        - check if any drbd device is using the local port,
1327 a8083063 Iustin Pop
          if yes abort
1328 a8083063 Iustin Pop
        - check if any remote drbd device is using the remote
1329 a8083063 Iustin Pop
          port, if yes abort (for now)
1330 a8083063 Iustin Pop
        - bind our net port
1331 a8083063 Iustin Pop
        - bind the remote net port
1332 a8083063 Iustin Pop

1333 a8083063 Iustin Pop
    """
1334 a8083063 Iustin Pop
    self.Attach()
1335 a8083063 Iustin Pop
    if self.minor is not None:
1336 a8083063 Iustin Pop
      logger.Info("Already assembled")
1337 a8083063 Iustin Pop
      return True
1338 a8083063 Iustin Pop
1339 a8083063 Iustin Pop
    result = super(DRBDev, self).Assemble()
1340 a8083063 Iustin Pop
    if not result:
1341 a8083063 Iustin Pop
      return result
1342 a8083063 Iustin Pop
1343 a8083063 Iustin Pop
    minor = self._FindUnusedMinor()
1344 a8083063 Iustin Pop
    need_localdev_teardown = False
1345 a8083063 Iustin Pop
    if self._children[0]:
1346 a8083063 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1347 a8083063 Iustin Pop
                                   self._children[1].dev_path)
1348 a8083063 Iustin Pop
      if not result:
1349 a8083063 Iustin Pop
        return False
1350 a8083063 Iustin Pop
      need_localdev_teardown = True
1351 a8083063 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1352 a8083063 Iustin Pop
      result = 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 not result:
1357 a8083063 Iustin Pop
        if need_localdev_teardown:
1358 a8083063 Iustin Pop
          # we will ignore failures from this
1359 a8083063 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
1360 a8083063 Iustin Pop
          self._ShutdownAll(minor)
1361 a8083063 Iustin Pop
        return False
1362 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1363 a8083063 Iustin Pop
    return True
1364 a8083063 Iustin Pop
1365 a8083063 Iustin Pop
  def Shutdown(self):
1366 a8083063 Iustin Pop
    """Shutdown the DRBD device.
1367 a8083063 Iustin Pop

1368 a8083063 Iustin Pop
    """
1369 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1370 a8083063 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
1371 a8083063 Iustin Pop
      return True
1372 a8083063 Iustin Pop
    if not self._ShutdownAll(self.minor):
1373 a8083063 Iustin Pop
      return False
1374 a8083063 Iustin Pop
    self.minor = None
1375 a8083063 Iustin Pop
    self.dev_path = None
1376 a8083063 Iustin Pop
    return True
1377 a8083063 Iustin Pop
1378 a8083063 Iustin Pop
  def Attach(self):
1379 a8083063 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1380 a8083063 Iustin Pop

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

1385 a8083063 Iustin Pop
    """
1386 a8083063 Iustin Pop
    for minor in self._GetUsedDevs():
1387 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
1388 a8083063 Iustin Pop
      match_l = self._MatchesLocal(info)
1389 a8083063 Iustin Pop
      match_r = self._MatchesNet(info)
1390 a8083063 Iustin Pop
      if match_l and match_r:
1391 a8083063 Iustin Pop
        break
1392 a8083063 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1393 a8083063 Iustin Pop
        res_r = self._AssembleNet(minor,
1394 a8083063 Iustin Pop
                                  (self._lhost, self._lport,
1395 a8083063 Iustin Pop
                                   self._rhost, self._rport),
1396 a8083063 Iustin Pop
                                  "C")
1397 a8083063 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
1398 a8083063 Iustin Pop
          break
1399 a8083063 Iustin Pop
    else:
1400 a8083063 Iustin Pop
      minor = None
1401 a8083063 Iustin Pop
1402 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1403 a8083063 Iustin Pop
    return minor is not None
1404 a8083063 Iustin Pop
1405 a8083063 Iustin Pop
  def Open(self, force=False):
1406 a8083063 Iustin Pop
    """Make the local state primary.
1407 a8083063 Iustin Pop

1408 a8083063 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1409 a8083063 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1410 a8083063 Iustin Pop
    force flag should be only given after creation, when it actually
1411 a8083063 Iustin Pop
    has to be given.
1412 a8083063 Iustin Pop

1413 a8083063 Iustin Pop
    """
1414 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1415 a8083063 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1416 a8083063 Iustin Pop
      return False
1417 a8083063 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1418 a8083063 Iustin Pop
    if force:
1419 a8083063 Iustin Pop
      cmd.append("--do-what-I-say")
1420 a8083063 Iustin Pop
    result = utils.RunCmd(cmd)
1421 a8083063 Iustin Pop
    if result.failed:
1422 fdbd668d Iustin Pop
      msg = ("Can't make drbd device primary: %s" % result.output)
1423 fdbd668d Iustin Pop
      logger.Error(msg)
1424 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1425 a8083063 Iustin Pop
1426 a8083063 Iustin Pop
  def Close(self):
1427 a8083063 Iustin Pop
    """Make the local state secondary.
1428 a8083063 Iustin Pop

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

1431 a8083063 Iustin Pop
    """
1432 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1433 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1434 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1435 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1436 a8083063 Iustin Pop
    if result.failed:
1437 fdbd668d Iustin Pop
      msg = ("Can't switch drbd device to"
1438 fdbd668d Iustin Pop
             " secondary: %s" % result.output)
1439 fdbd668d Iustin Pop
      logger.Error(msg)
1440 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1441 a8083063 Iustin Pop
1442 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1443 a8083063 Iustin Pop
    """Set the speed of the DRBD syncer.
1444 a8083063 Iustin Pop

1445 a8083063 Iustin Pop
    """
1446 a8083063 Iustin Pop
    children_result = super(DRBDev, self).SetSyncSpeed(kbytes)
1447 a8083063 Iustin Pop
    if self.minor is None:
1448 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1449 a8083063 Iustin Pop
      return False
1450 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1451 a8083063 Iustin Pop
                           kbytes])
1452 a8083063 Iustin Pop
    if result.failed:
1453 a8083063 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1454 a8083063 Iustin Pop
    return not result.failed and children_result
1455 a8083063 Iustin Pop
1456 a8083063 Iustin Pop
  def GetSyncStatus(self):
1457 a8083063 Iustin Pop
    """Returns the sync status of the device.
1458 a8083063 Iustin Pop

1459 a8083063 Iustin Pop
    Returns:
1460 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
1461 a8083063 Iustin Pop

1462 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
1463 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
1464 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1465 0834c866 Iustin Pop

1466 0834c866 Iustin Pop
    The ldisk parameter will be returned as True, since the DRBD7
1467 0834c866 Iustin Pop
    devices have not been converted.
1468 a8083063 Iustin Pop

1469 a8083063 Iustin Pop
    """
1470 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1471 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1472 a8083063 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1473 a8083063 Iustin Pop
    if self.minor not in proc_info:
1474 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1475 a8083063 Iustin Pop
                                    self.minor)
1476 a8083063 Iustin Pop
    line = proc_info[self.minor]
1477 a8083063 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1478 a8083063 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1479 a8083063 Iustin Pop
    if match:
1480 a8083063 Iustin Pop
      sync_percent = float(match.group(1))
1481 a8083063 Iustin Pop
      hours = int(match.group(2))
1482 a8083063 Iustin Pop
      minutes = int(match.group(3))
1483 a8083063 Iustin Pop
      seconds = int(match.group(4))
1484 a8083063 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1485 a8083063 Iustin Pop
    else:
1486 a8083063 Iustin Pop
      sync_percent = None
1487 a8083063 Iustin Pop
      est_time = None
1488 a8083063 Iustin Pop
    match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1489 a8083063 Iustin Pop
    if not match:
1490 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1491 a8083063 Iustin Pop
                                    self.minor)
1492 a8083063 Iustin Pop
    client_state = match.group(1)
1493 a8083063 Iustin Pop
    is_degraded = client_state != "Connected"
1494 0834c866 Iustin Pop
    return sync_percent, est_time, is_degraded, False
1495 a8083063 Iustin Pop
1496 a8083063 Iustin Pop
  @staticmethod
1497 a8083063 Iustin Pop
  def _ZeroDevice(device):
1498 a8083063 Iustin Pop
    """Zero a device.
1499 a8083063 Iustin Pop

1500 a8083063 Iustin Pop
    This writes until we get ENOSPC.
1501 a8083063 Iustin Pop

1502 a8083063 Iustin Pop
    """
1503 a8083063 Iustin Pop
    f = open(device, "w")
1504 a8083063 Iustin Pop
    buf = "\0" * 1048576
1505 a8083063 Iustin Pop
    try:
1506 a8083063 Iustin Pop
      while True:
1507 a8083063 Iustin Pop
        f.write(buf)
1508 a8083063 Iustin Pop
    except IOError, err:
1509 a8083063 Iustin Pop
      if err.errno != errno.ENOSPC:
1510 a8083063 Iustin Pop
        raise
1511 a8083063 Iustin Pop
1512 a8083063 Iustin Pop
  @classmethod
1513 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
1514 a8083063 Iustin Pop
    """Create a new DRBD device.
1515 a8083063 Iustin Pop

1516 a8083063 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1517 a8083063 Iustin Pop
    function just zeroes the meta device.
1518 a8083063 Iustin Pop

1519 a8083063 Iustin Pop
    """
1520 a8083063 Iustin Pop
    if len(children) != 2:
1521 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1522 a8083063 Iustin Pop
    meta = children[1]
1523 a8083063 Iustin Pop
    meta.Assemble()
1524 a8083063 Iustin Pop
    if not meta.Attach():
1525 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1526 ae26a287 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
1527 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device")
1528 a8083063 Iustin Pop
    logger.Info("Started zeroing device %s" % meta.dev_path)
1529 a8083063 Iustin Pop
    cls._ZeroDevice(meta.dev_path)
1530 a8083063 Iustin Pop
    logger.Info("Done zeroing device %s" % meta.dev_path)
1531 a8083063 Iustin Pop
    return cls(unique_id, children)
1532 a8083063 Iustin Pop
1533 a8083063 Iustin Pop
  def Remove(self):
1534 a8083063 Iustin Pop
    """Stub remove for DRBD devices.
1535 a8083063 Iustin Pop

1536 a8083063 Iustin Pop
    """
1537 a8083063 Iustin Pop
    return self.Shutdown()
1538 a8083063 Iustin Pop
1539 f3e513ad Iustin Pop
1540 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1541 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1542 a2cfdea2 Iustin Pop

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

1547 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1548 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1549 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1550 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1551 a2cfdea2 Iustin Pop

1552 a2cfdea2 Iustin Pop
  """
1553 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1554 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1555 a2cfdea2 Iustin Pop
1556 a2cfdea2 Iustin Pop
  def __init__(self, unique_id, children):
1557 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1558 fc1dc9d7 Iustin Pop
      children = []
1559 a2cfdea2 Iustin Pop
    super(DRBD8, self).__init__(unique_id, children)
1560 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1561 c3f9340c Guido Trotter
    version = self._GetVersion()
1562 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
1563 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1564 a2cfdea2 Iustin Pop
                                    " requested ganeti usage: kernel is"
1565 c3f9340c Guido Trotter
                                    " %s.%s, ganeti wants 8.x" %
1566 c3f9340c Guido Trotter
                                    (version['k_major'], version['k_minor']))
1567 a2cfdea2 Iustin Pop
1568 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
1569 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1570 a2cfdea2 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1571 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1572 a2cfdea2 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
1573 a2cfdea2 Iustin Pop
    self.Attach()
1574 a2cfdea2 Iustin Pop
1575 a2cfdea2 Iustin Pop
  @classmethod
1576 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1577 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1578 a2cfdea2 Iustin Pop

1579 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1580 a2cfdea2 Iustin Pop

1581 a2cfdea2 Iustin Pop
    """
1582 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1583 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1584 a2cfdea2 Iustin Pop
    if result.failed:
1585 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't initialize meta device: %s" %
1586 a2cfdea2 Iustin Pop
                                    result.output)
1587 a2cfdea2 Iustin Pop
1588 a2cfdea2 Iustin Pop
  @classmethod
1589 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1590 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1591 a2cfdea2 Iustin Pop

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

1595 a2cfdea2 Iustin Pop
    """
1596 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1597 a2cfdea2 Iustin Pop
1598 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1599 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
1600 a2cfdea2 Iustin Pop
    highest = None
1601 a2cfdea2 Iustin Pop
    for line in data:
1602 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
1603 a2cfdea2 Iustin Pop
      if match:
1604 a2cfdea2 Iustin Pop
        return int(match.group(1))
1605 a2cfdea2 Iustin Pop
      match = used_line.match(line)
1606 a2cfdea2 Iustin Pop
      if match:
1607 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1608 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1609 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1610 a2cfdea2 Iustin Pop
      return 0
1611 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1612 a2cfdea2 Iustin Pop
      logger.Error("Error: no free drbd minors!")
1613 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1614 a2cfdea2 Iustin Pop
    return highest + 1
1615 a2cfdea2 Iustin Pop
1616 a2cfdea2 Iustin Pop
  @classmethod
1617 a2cfdea2 Iustin Pop
  def _IsValidMeta(cls, meta_device):
1618 a2cfdea2 Iustin Pop
    """Check if the given meta device looks like a valid one.
1619 a2cfdea2 Iustin Pop

1620 a2cfdea2 Iustin Pop
    """
1621 a2cfdea2 Iustin Pop
    minor = cls._FindUnusedMinor()
1622 a2cfdea2 Iustin Pop
    minor_path = cls._DevPath(minor)
1623 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", minor_path,
1624 a2cfdea2 Iustin Pop
                           "v08", meta_device, "0",
1625 a2cfdea2 Iustin Pop
                           "dstate"])
1626 a2cfdea2 Iustin Pop
    if result.failed:
1627 a2cfdea2 Iustin Pop
      logger.Error("Invalid meta device %s: %s" % (meta_device, result.output))
1628 a2cfdea2 Iustin Pop
      return False
1629 a2cfdea2 Iustin Pop
    return True
1630 a2cfdea2 Iustin Pop
1631 a2cfdea2 Iustin Pop
  @classmethod
1632 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1633 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1634 a2cfdea2 Iustin Pop

1635 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1636 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1637 a2cfdea2 Iustin Pop

1638 a2cfdea2 Iustin Pop
    """
1639 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1640 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1641 a2cfdea2 Iustin Pop
1642 a2cfdea2 Iustin Pop
    # pyparsing setup
1643 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1644 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1645 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1646 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1647 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1648 a2cfdea2 Iustin Pop
1649 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1650 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1651 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1652 a2cfdea2 Iustin Pop
1653 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1654 a2cfdea2 Iustin Pop
1655 a2cfdea2 Iustin Pop
    # value types
1656 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1657 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1658 a2cfdea2 Iustin Pop
    addr_port = (pyp.Word(pyp.nums + '.') + pyp.Literal(':').suppress() +
1659 a2cfdea2 Iustin Pop
                 number)
1660 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1661 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
1662 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
1663 a2cfdea2 Iustin Pop
1664 a2cfdea2 Iustin Pop
    # a statement
1665 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1666 a2cfdea2 Iustin Pop
            (addr_port ^ value ^ quoted ^ meta_value) +
1667 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1668 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1669 a2cfdea2 Iustin Pop
1670 a2cfdea2 Iustin Pop
    # an entire section
1671 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1672 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1673 a2cfdea2 Iustin Pop
1674 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1675 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1676 a2cfdea2 Iustin Pop
1677 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1678 a2cfdea2 Iustin Pop
1679 a2cfdea2 Iustin Pop
    return bnf
1680 a2cfdea2 Iustin Pop
1681 a2cfdea2 Iustin Pop
  @classmethod
1682 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1683 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1684 a2cfdea2 Iustin Pop

1685 a2cfdea2 Iustin Pop
    """
1686 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1687 a2cfdea2 Iustin Pop
    if result.failed:
1688 a2cfdea2 Iustin Pop
      logger.Error("Can't display the drbd config: %s" % result.fail_reason)
1689 3840729d Iustin Pop
      return None
1690 3840729d Iustin Pop
    return result.stdout
1691 3840729d Iustin Pop
1692 3840729d Iustin Pop
  @classmethod
1693 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1694 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1695 3840729d Iustin Pop

1696 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1697 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1698 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1699 3840729d Iustin Pop
    by _GetShowData.
1700 3840729d Iustin Pop

1701 3840729d Iustin Pop
    """
1702 3840729d Iustin Pop
    data = {}
1703 a2cfdea2 Iustin Pop
    if not out:
1704 a2cfdea2 Iustin Pop
      return data
1705 a2cfdea2 Iustin Pop
1706 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1707 a2cfdea2 Iustin Pop
    # run pyparse
1708 a2cfdea2 Iustin Pop
1709 a2cfdea2 Iustin Pop
    try:
1710 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1711 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1712 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't parse drbdsetup show output: %s" %
1713 a2cfdea2 Iustin Pop
                                    str(err))
1714 a2cfdea2 Iustin Pop
1715 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1716 a2cfdea2 Iustin Pop
    for section in results:
1717 a2cfdea2 Iustin Pop
      sname = section[0]
1718 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1719 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1720 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1721 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1722 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1723 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1724 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1725 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1726 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1727 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1728 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1729 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1730 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1731 a2cfdea2 Iustin Pop
    return data
1732 a2cfdea2 Iustin Pop
1733 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1734 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1735 a2cfdea2 Iustin Pop

1736 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1737 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1738 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1739 a2cfdea2 Iustin Pop
    device.
1740 a2cfdea2 Iustin Pop

1741 a2cfdea2 Iustin Pop
    """
1742 b00b95dd Iustin Pop
    if self._children:
1743 b00b95dd Iustin Pop
      backend, meta = self._children
1744 b00b95dd Iustin Pop
    else:
1745 b00b95dd Iustin Pop
      backend = meta = None
1746 b00b95dd Iustin Pop
1747 a2cfdea2 Iustin Pop
    if backend is not None:
1748 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1749 a2cfdea2 Iustin Pop
    else:
1750 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1751 b00b95dd Iustin Pop
1752 a2cfdea2 Iustin Pop
    if meta is not None:
1753 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1754 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1755 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1756 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1757 a2cfdea2 Iustin Pop
    else:
1758 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1759 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1760 a2cfdea2 Iustin Pop
    return retval
1761 a2cfdea2 Iustin Pop
1762 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1763 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1764 a2cfdea2 Iustin Pop

1765 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1766 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1767 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1768 a2cfdea2 Iustin Pop
    device.
1769 a2cfdea2 Iustin Pop

1770 a2cfdea2 Iustin Pop
    """
1771 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1772 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1773 a2cfdea2 Iustin Pop
      return True
1774 a2cfdea2 Iustin Pop
1775 a2cfdea2 Iustin Pop
    if self._lhost is None:
1776 a2cfdea2 Iustin Pop
      return False
1777 a2cfdea2 Iustin Pop
1778 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1779 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1780 a2cfdea2 Iustin Pop
      return False
1781 a2cfdea2 Iustin Pop
1782 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1783 a2cfdea2 Iustin Pop
    retval = (retval and
1784 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1785 a2cfdea2 Iustin Pop
    return retval
1786 a2cfdea2 Iustin Pop
1787 a2cfdea2 Iustin Pop
  @classmethod
1788 a2cfdea2 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1789 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1790 a2cfdea2 Iustin Pop

1791 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1792 a2cfdea2 Iustin Pop
    device. And it must be done only once.
1793 a2cfdea2 Iustin Pop

1794 a2cfdea2 Iustin Pop
    """
1795 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta):
1796 a2cfdea2 Iustin Pop
      return False
1797 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1798 a2cfdea2 Iustin Pop
                           backend, meta, "0", "-e", "detach",
1799 a2cfdea2 Iustin Pop
                           "--create-device"])
1800 a2cfdea2 Iustin Pop
    if result.failed:
1801 a2cfdea2 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1802 a2cfdea2 Iustin Pop
    return not result.failed
1803 a2cfdea2 Iustin Pop
1804 a2cfdea2 Iustin Pop
  @classmethod
1805 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1806 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1807 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1808 a2cfdea2 Iustin Pop

1809 a2cfdea2 Iustin Pop
    """
1810 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1811 52857176 Iustin Pop
    if None in net_info:
1812 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1813 52857176 Iustin Pop
      # sure its shutdown
1814 52857176 Iustin Pop
      return cls._ShutdownNet(minor)
1815 52857176 Iustin Pop
1816 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1817 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1818 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1819 f38478b2 Iustin Pop
            "-B", "consensus",
1820 f38478b2 Iustin Pop
            ]
1821 a2cfdea2 Iustin Pop
    if dual_pri:
1822 a2cfdea2 Iustin Pop
      args.append("-m")
1823 a2cfdea2 Iustin Pop
    if hmac and secret:
1824 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1825 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1826 a2cfdea2 Iustin Pop
    if result.failed:
1827 a2cfdea2 Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s" %
1828 a2cfdea2 Iustin Pop
                   result.fail_reason)
1829 a2cfdea2 Iustin Pop
      return False
1830 a2cfdea2 Iustin Pop
1831 a2cfdea2 Iustin Pop
    timeout = time.time() + 10
1832 a2cfdea2 Iustin Pop
    ok = False
1833 a2cfdea2 Iustin Pop
    while time.time() < timeout:
1834 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1835 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1836 a2cfdea2 Iustin Pop
        time.sleep(1)
1837 a2cfdea2 Iustin Pop
        continue
1838 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1839 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1840 a2cfdea2 Iustin Pop
        time.sleep(1)
1841 a2cfdea2 Iustin Pop
        continue
1842 a2cfdea2 Iustin Pop
      ok = True
1843 a2cfdea2 Iustin Pop
      break
1844 a2cfdea2 Iustin Pop
    if not ok:
1845 a2cfdea2 Iustin Pop
      logger.Error("Timeout while configuring network")
1846 a2cfdea2 Iustin Pop
      return False
1847 a2cfdea2 Iustin Pop
    return True
1848 a2cfdea2 Iustin Pop
1849 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1850 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1851 b00b95dd Iustin Pop

1852 b00b95dd Iustin Pop
    """
1853 b00b95dd Iustin Pop
    if self.minor is None:
1854 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren")
1855 b00b95dd Iustin Pop
    if len(devices) != 2:
1856 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Need two devices for AddChildren")
1857 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1858 03ece5f3 Iustin Pop
    if "local_dev" in info:
1859 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("DRBD8 already attached to a local disk")
1860 b00b95dd Iustin Pop
    backend, meta = devices
1861 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1862 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Children not ready during AddChildren")
1863 b00b95dd Iustin Pop
    backend.Open()
1864 b00b95dd Iustin Pop
    meta.Open()
1865 b00b95dd Iustin Pop
    if not self._CheckMetaSize(meta.dev_path):
1866 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1867 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1868 b00b95dd Iustin Pop
    if not self._IsValidMeta(meta.dev_path):
1869 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1870 b00b95dd Iustin Pop
1871 b00b95dd Iustin Pop
    if not self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path):
1872 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to local storage")
1873 b00b95dd Iustin Pop
    self._children = devices
1874 b00b95dd Iustin Pop
1875 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1876 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1877 b00b95dd Iustin Pop

1878 b00b95dd Iustin Pop
    """
1879 b00b95dd Iustin Pop
    if self.minor is None:
1880 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to drbd8 during"
1881 b00b95dd Iustin Pop
                                    " RemoveChildren")
1882 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1883 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1884 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1885 03ece5f3 Iustin Pop
      return
1886 b00b95dd Iustin Pop
    if len(self._children) != 2:
1887 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We don't have two children: %s" %
1888 b00b95dd Iustin Pop
                                    self._children)
1889 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1890 e739bd57 Iustin Pop
      logger.Error("Requested detach while detached")
1891 e739bd57 Iustin Pop
      return
1892 b00b95dd Iustin Pop
    if len(devices) != 2:
1893 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We need two children in RemoveChildren")
1894 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1895 e739bd57 Iustin Pop
      if dev != child.dev_path:
1896 e739bd57 Iustin Pop
        raise errors.BlockDeviceError("Mismatch in local storage"
1897 e739bd57 Iustin Pop
                                      " (%s != %s) in RemoveChildren" %
1898 e739bd57 Iustin Pop
                                      (dev, child.dev_path))
1899 b00b95dd Iustin Pop
1900 b00b95dd Iustin Pop
    if not self._ShutdownLocal(self.minor):
1901 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't detach from local storage")
1902 b00b95dd Iustin Pop
    self._children = []
1903 b00b95dd Iustin Pop
1904 a2cfdea2 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1905 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1906 a2cfdea2 Iustin Pop

1907 a2cfdea2 Iustin Pop
    """
1908 a2cfdea2 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1909 a2cfdea2 Iustin Pop
    if self.minor is None:
1910 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
1911 a2cfdea2 Iustin Pop
      return False
1912 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1913 a2cfdea2 Iustin Pop
                           kbytes])
1914 a2cfdea2 Iustin Pop
    if result.failed:
1915 a2cfdea2 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1916 a2cfdea2 Iustin Pop
    return not result.failed and children_result
1917 a2cfdea2 Iustin Pop
1918 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1919 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1920 a2cfdea2 Iustin Pop

1921 a2cfdea2 Iustin Pop
    Returns:
1922 583e3f6f Iustin Pop
     (sync_percent, estimated_time, is_degraded)
1923 a2cfdea2 Iustin Pop

1924 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1925 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1926 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1927 0834c866 Iustin Pop

1928 0834c866 Iustin Pop

1929 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1930 0834c866 Iustin Pop
    network not connected or local disk missing.
1931 0834c866 Iustin Pop

1932 0834c866 Iustin Pop
    We compute the ldisk parameter based on wheter we have a local
1933 0834c866 Iustin Pop
    disk or not.
1934 a2cfdea2 Iustin Pop

1935 a2cfdea2 Iustin Pop
    """
1936 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1937 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1938 a2cfdea2 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1939 a2cfdea2 Iustin Pop
    if self.minor not in proc_info:
1940 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1941 a2cfdea2 Iustin Pop
                                    self.minor)
1942 a2cfdea2 Iustin Pop
    line = proc_info[self.minor]
1943 a2cfdea2 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1944 a2cfdea2 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1945 a2cfdea2 Iustin Pop
    if match:
1946 a2cfdea2 Iustin Pop
      sync_percent = float(match.group(1))
1947 a2cfdea2 Iustin Pop
      hours = int(match.group(2))
1948 a2cfdea2 Iustin Pop
      minutes = int(match.group(3))
1949 a2cfdea2 Iustin Pop
      seconds = int(match.group(4))
1950 a2cfdea2 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1951 a2cfdea2 Iustin Pop
    else:
1952 a2cfdea2 Iustin Pop
      sync_percent = None
1953 a2cfdea2 Iustin Pop
      est_time = None
1954 583e3f6f Iustin Pop
    match = re.match("^ *\d+: cs:(\w+).*ds:(\w+)/(\w+).*$", line)
1955 a2cfdea2 Iustin Pop
    if not match:
1956 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1957 a2cfdea2 Iustin Pop
                                    self.minor)
1958 a2cfdea2 Iustin Pop
    client_state = match.group(1)
1959 583e3f6f Iustin Pop
    local_disk_state = match.group(2)
1960 0834c866 Iustin Pop
    ldisk = local_disk_state != "UpToDate"
1961 0834c866 Iustin Pop
    is_degraded = client_state != "Connected"
1962 0834c866 Iustin Pop
    return sync_percent, est_time, is_degraded or ldisk, ldisk
1963 a2cfdea2 Iustin Pop
1964 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1965 a2cfdea2 Iustin Pop
    """Make the local state primary.
1966 a2cfdea2 Iustin Pop

1967 a2cfdea2 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1968 a2cfdea2 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1969 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1970 a2cfdea2 Iustin Pop
    has to be given.
1971 a2cfdea2 Iustin Pop

1972 a2cfdea2 Iustin Pop
    """
1973 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1974 a2cfdea2 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1975 a2cfdea2 Iustin Pop
      return False
1976 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1977 a2cfdea2 Iustin Pop
    if force:
1978 a2cfdea2 Iustin Pop
      cmd.append("-o")
1979 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1980 a2cfdea2 Iustin Pop
    if result.failed:
1981 fdbd668d Iustin Pop
      msg = ("Can't make drbd device primary: %s" % result.output)
1982 fdbd668d Iustin Pop
      logger.Error(msg)
1983 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1984 a2cfdea2 Iustin Pop
1985 a2cfdea2 Iustin Pop
  def Close(self):
1986 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1987 a2cfdea2 Iustin Pop

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

1990 a2cfdea2 Iustin Pop
    """
1991 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1992 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
1993 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1994 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1995 a2cfdea2 Iustin Pop
    if result.failed:
1996 fdbd668d Iustin Pop
      msg = ("Can't switch drbd device to"
1997 fdbd668d Iustin Pop
             " secondary: %s" % result.output)
1998 fdbd668d Iustin Pop
      logger.Error(msg)
1999 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
2000 a2cfdea2 Iustin Pop
2001 a2cfdea2 Iustin Pop
  def Attach(self):
2002 a2cfdea2 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
2003 a2cfdea2 Iustin Pop

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

2008 a2cfdea2 Iustin Pop
    """
2009 a2cfdea2 Iustin Pop
    for minor in self._GetUsedDevs():
2010 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
2011 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
2012 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
2013 a2cfdea2 Iustin Pop
      if match_l and match_r:
2014 a2cfdea2 Iustin Pop
        break
2015 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
2016 a2cfdea2 Iustin Pop
        res_r = self._AssembleNet(minor,
2017 a2cfdea2 Iustin Pop
                                  (self._lhost, self._lport,
2018 a2cfdea2 Iustin Pop
                                   self._rhost, self._rport),
2019 a2cfdea2 Iustin Pop
                                  "C")
2020 3840729d Iustin Pop
        if res_r:
2021 3840729d Iustin Pop
          if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2022 3840729d Iustin Pop
            break
2023 fc1dc9d7 Iustin Pop
      # the weakest case: we find something that is only net attached
2024 fc1dc9d7 Iustin Pop
      # even though we were passed some children at init time
2025 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
2026 fc1dc9d7 Iustin Pop
        break
2027 9cdbe77f Iustin Pop
      if match_l and not match_r and "local_addr" in info:
2028 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
2029 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
2030 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
2031 9cdbe77f Iustin Pop
        # reconnect to our correct one
2032 9cdbe77f Iustin Pop
        if not self._ShutdownNet(minor):
2033 9cdbe77f Iustin Pop
          raise errors.BlockDeviceError("Device has correct local storage,"
2034 9cdbe77f Iustin Pop
                                        " wrong remote peer and is unable to"
2035 9cdbe77f Iustin Pop
                                        " disconnect in order to attach to"
2036 9cdbe77f Iustin Pop
                                        " the correct peer")
2037 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
2038 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
2039 9cdbe77f Iustin Pop
        # None)
2040 9cdbe77f Iustin Pop
        if (self._AssembleNet(minor, (self._lhost, self._lport,
2041 9cdbe77f Iustin Pop
                                      self._rhost, self._rport), "C") and
2042 3840729d Iustin Pop
            self._MatchesNet(self._GetDevInfo(self._GetShowData(minor)))):
2043 9cdbe77f Iustin Pop
          break
2044 9cdbe77f Iustin Pop
2045 a2cfdea2 Iustin Pop
    else:
2046 a2cfdea2 Iustin Pop
      minor = None
2047 a2cfdea2 Iustin Pop
2048 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2049 a2cfdea2 Iustin Pop
    return minor is not None
2050 a2cfdea2 Iustin Pop
2051 a2cfdea2 Iustin Pop
  def Assemble(self):
2052 a2cfdea2 Iustin Pop
    """Assemble the drbd.
2053 a2cfdea2 Iustin Pop

2054 a2cfdea2 Iustin Pop
    Method:
2055 a2cfdea2 Iustin Pop
      - if we have a local backing device, we bind to it by:
2056 a2cfdea2 Iustin Pop
        - checking the list of used drbd devices
2057 a2cfdea2 Iustin Pop
        - check if the local minor use of any of them is our own device
2058 a2cfdea2 Iustin Pop
        - if yes, abort?
2059 a2cfdea2 Iustin Pop
        - if not, bind
2060 a2cfdea2 Iustin Pop
      - if we have a local/remote net info:
2061 a2cfdea2 Iustin Pop
        - redo the local backing device step for the remote device
2062 a2cfdea2 Iustin Pop
        - check if any drbd device is using the local port,
2063 a2cfdea2 Iustin Pop
          if yes abort
2064 a2cfdea2 Iustin Pop
        - check if any remote drbd device is using the remote
2065 a2cfdea2 Iustin Pop
          port, if yes abort (for now)
2066 a2cfdea2 Iustin Pop
        - bind our net port
2067 a2cfdea2 Iustin Pop
        - bind the remote net port
2068 a2cfdea2 Iustin Pop

2069 a2cfdea2 Iustin Pop
    """
2070 a2cfdea2 Iustin Pop
    self.Attach()
2071 a2cfdea2 Iustin Pop
    if self.minor is not None:
2072 a2cfdea2 Iustin Pop
      logger.Info("Already assembled")
2073 a2cfdea2 Iustin Pop
      return True
2074 a2cfdea2 Iustin Pop
2075 a2cfdea2 Iustin Pop
    result = super(DRBD8, self).Assemble()
2076 a2cfdea2 Iustin Pop
    if not result:
2077 a2cfdea2 Iustin Pop
      return result
2078 a2cfdea2 Iustin Pop
2079 a2cfdea2 Iustin Pop
    minor = self._FindUnusedMinor()
2080 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
2081 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
2082 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
2083 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
2084 a2cfdea2 Iustin Pop
      if not result:
2085 a2cfdea2 Iustin Pop
        return False
2086 a2cfdea2 Iustin Pop
      need_localdev_teardown = True
2087 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
2088 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
2089 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
2090 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
2091 a2cfdea2 Iustin Pop
                                 "C")
2092 a2cfdea2 Iustin Pop
      if not result:
2093 a2cfdea2 Iustin Pop
        if need_localdev_teardown:
2094 a2cfdea2 Iustin Pop
          # we will ignore failures from this
2095 a2cfdea2 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
2096 a2cfdea2 Iustin Pop
          self._ShutdownAll(minor)
2097 a2cfdea2 Iustin Pop
        return False
2098 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2099 a2cfdea2 Iustin Pop
    return True
2100 a2cfdea2 Iustin Pop
2101 a2cfdea2 Iustin Pop
  @classmethod
2102 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
2103 b00b95dd Iustin Pop
    """Detach from the local device.
2104 b00b95dd Iustin Pop

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

2108 b00b95dd Iustin Pop
    """
2109 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2110 b00b95dd Iustin Pop
    if result.failed:
2111 b00b95dd Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
2112 b00b95dd Iustin Pop
    return not result.failed
2113 b00b95dd Iustin Pop
2114 b00b95dd Iustin Pop
  @classmethod
2115 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2116 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2117 f3e513ad Iustin Pop

2118 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2119 f3e513ad Iustin Pop

2120 f3e513ad Iustin Pop
    """
2121 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2122 a8459f1c Iustin Pop
    if result.failed:
2123 a8459f1c Iustin Pop
      logger.Error("Can't shutdown network: %s" % result.output)
2124 f3e513ad Iustin Pop
    return not result.failed
2125 f3e513ad Iustin Pop
2126 f3e513ad Iustin Pop
  @classmethod
2127 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2128 a2cfdea2 Iustin Pop
    """Deactivate the device.
2129 a2cfdea2 Iustin Pop

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

2132 a2cfdea2 Iustin Pop
    """
2133 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2134 a2cfdea2 Iustin Pop
    if result.failed:
2135 a2cfdea2 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
2136 a2cfdea2 Iustin Pop
    return not result.failed
2137 a2cfdea2 Iustin Pop
2138 a2cfdea2 Iustin Pop
  def Shutdown(self):
2139 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2140 a2cfdea2 Iustin Pop

2141 a2cfdea2 Iustin Pop
    """
2142 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2143 a2cfdea2 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
2144 a2cfdea2 Iustin Pop
      return True
2145 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
2146 a2cfdea2 Iustin Pop
      return False
2147 a2cfdea2 Iustin Pop
    self.minor = None
2148 a2cfdea2 Iustin Pop
    self.dev_path = None
2149 a2cfdea2 Iustin Pop
    return True
2150 a2cfdea2 Iustin Pop
2151 a2cfdea2 Iustin Pop
  def Remove(self):
2152 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2153 a2cfdea2 Iustin Pop

2154 a2cfdea2 Iustin Pop
    """
2155 a2cfdea2 Iustin Pop
    return self.Shutdown()
2156 a2cfdea2 Iustin Pop
2157 a2cfdea2 Iustin Pop
  @classmethod
2158 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
2159 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2160 a2cfdea2 Iustin Pop

2161 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2162 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2163 a2cfdea2 Iustin Pop

2164 a2cfdea2 Iustin Pop
    """
2165 a2cfdea2 Iustin Pop
    if len(children) != 2:
2166 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2167 a2cfdea2 Iustin Pop
    meta = children[1]
2168 a2cfdea2 Iustin Pop
    meta.Assemble()
2169 a2cfdea2 Iustin Pop
    if not meta.Attach():
2170 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
2171 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
2172 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
2173 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
2174 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
2175 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
2176 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
2177 a2cfdea2 Iustin Pop
2178 a8083063 Iustin Pop
2179 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
2180 6f695a2e Manuel Franceschini
  """File device.
2181 6f695a2e Manuel Franceschini
  
2182 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
2183 6f695a2e Manuel Franceschini

2184 6f695a2e Manuel Franceschini
  The unique_id for the file device is a (file_driver, file_path) tuple.
2185 6f695a2e Manuel Franceschini
  
2186 6f695a2e Manuel Franceschini
  """
2187 6f695a2e Manuel Franceschini
  def __init__(self, unique_id, children):
2188 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
2189 6f695a2e Manuel Franceschini

2190 6f695a2e Manuel Franceschini
    """
2191 6f695a2e Manuel Franceschini
    if children:
2192 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
2193 6f695a2e Manuel Franceschini
    super(FileStorage, self).__init__(unique_id, children)
2194 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2195 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2196 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
2197 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
2198 6f695a2e Manuel Franceschini
2199 6f695a2e Manuel Franceschini
  def Assemble(self):
2200 6f695a2e Manuel Franceschini
    """Assemble the device.
2201 6f695a2e Manuel Franceschini

2202 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
2203 6f695a2e Manuel Franceschini

2204 6f695a2e Manuel Franceschini
    """
2205 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
2206 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("File device '%s' does not exist." %
2207 6f695a2e Manuel Franceschini
                                    self.dev_path)
2208 6f695a2e Manuel Franceschini
    return True
2209 6f695a2e Manuel Franceschini
2210 6f695a2e Manuel Franceschini
  def Shutdown(self):
2211 6f695a2e Manuel Franceschini
    """Shutdown the device.
2212 6f695a2e Manuel Franceschini

2213 6f695a2e Manuel Franceschini
    This is a no-op for the file type, as we don't deacivate
2214 6f695a2e Manuel Franceschini
    the file on shutdown.
2215 6f695a2e Manuel Franceschini

2216 6f695a2e Manuel Franceschini
    """
2217 6f695a2e Manuel Franceschini
    return True
2218 6f695a2e Manuel Franceschini
2219 6f695a2e Manuel Franceschini
  def Open(self, force=False):
2220 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
2221 6f695a2e Manuel Franceschini

2222 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2223 6f695a2e Manuel Franceschini

2224 6f695a2e Manuel Franceschini
    """
2225 6f695a2e Manuel Franceschini
    pass
2226 6f695a2e Manuel Franceschini
2227 6f695a2e Manuel Franceschini
  def Close(self):
2228 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
2229 6f695a2e Manuel Franceschini

2230 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2231 6f695a2e Manuel Franceschini

2232 6f695a2e Manuel Franceschini
    """
2233 6f695a2e Manuel Franceschini
    pass
2234 6f695a2e Manuel Franceschini
2235 6f695a2e Manuel Franceschini
  def Remove(self):
2236 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
2237 6f695a2e Manuel Franceschini

2238 6f695a2e Manuel Franceschini
    Returns:
2239 6f695a2e Manuel Franceschini
      boolean indicating wheter removal of file was successful or not.
2240 6f695a2e Manuel Franceschini

2241 6f695a2e Manuel Franceschini
    """
2242 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
2243 6f695a2e Manuel Franceschini
      return True
2244 6f695a2e Manuel Franceschini
    try:
2245 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
2246 6f695a2e Manuel Franceschini
      return True
2247 6f695a2e Manuel Franceschini
    except OSError, err:
2248 6f695a2e Manuel Franceschini
      logger.Error("Can't remove file '%s': %s"
2249 6f695a2e Manuel Franceschini
                   % (self.dev_path, err))
2250 6f695a2e Manuel Franceschini
      return False
2251 6f695a2e Manuel Franceschini
2252 6f695a2e Manuel Franceschini
  def Attach(self):
2253 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2254 6f695a2e Manuel Franceschini

2255 6f695a2e Manuel Franceschini
    Check if this file already exists.
2256 6f695a2e Manuel Franceschini

2257 6f695a2e Manuel Franceschini
    Returns:
2258 6f695a2e Manuel Franceschini
      boolean indicating if file exists or not.
2259 6f695a2e Manuel Franceschini

2260 6f695a2e Manuel Franceschini
    """
2261 6f695a2e Manuel Franceschini
    if os.path.exists(self.dev_path):
2262 6f695a2e Manuel Franceschini
      return True
2263 6f695a2e Manuel Franceschini
    return False
2264 6f695a2e Manuel Franceschini
2265 6f695a2e Manuel Franceschini
  @classmethod
2266 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
2267 6f695a2e Manuel Franceschini
    """Create a new file.
2268 6f695a2e Manuel Franceschini

2269 6f695a2e Manuel Franceschini
    Args:
2270 6f695a2e Manuel Franceschini
      children:
2271 6f695a2e Manuel Franceschini
      size: integer size of file in MiB
2272 6f695a2e Manuel Franceschini

2273 6f695a2e Manuel Franceschini
    Returns:
2274 6f695a2e Manuel Franceschini
      A ganeti.bdev.FileStorage object.
2275 6f695a2e Manuel Franceschini

2276 6f695a2e Manuel Franceschini
    """
2277 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2278 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2279 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2280 6f695a2e Manuel Franceschini
    try:
2281 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
2282 6f695a2e Manuel Franceschini
    except IOError, err:
2283 b62ddbe5 Guido Trotter
      raise errors.BlockDeviceError("Could not create '%'" % err)
2284 6f695a2e Manuel Franceschini
    else:
2285 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2286 6f695a2e Manuel Franceschini
      f.close()
2287 6f695a2e Manuel Franceschini
2288 6f695a2e Manuel Franceschini
    return FileStorage(unique_id, children)
2289 6f695a2e Manuel Franceschini
2290 6f695a2e Manuel Franceschini
2291 a8083063 Iustin Pop
DEV_MAP = {
2292 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2293 fe96220b Iustin Pop
  constants.LD_MD_R1: MDRaid1,
2294 fe96220b Iustin Pop
  constants.LD_DRBD7: DRBDev,
2295 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2296 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
2297 a8083063 Iustin Pop
  }
2298 a8083063 Iustin Pop
2299 a8083063 Iustin Pop
2300 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
2301 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2302 a8083063 Iustin Pop

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

2306 a8083063 Iustin Pop
  """
2307 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2308 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2309 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2310 a8083063 Iustin Pop
  if not device.Attach():
2311 a8083063 Iustin Pop
    return None
2312 a8083063 Iustin Pop
  return  device
2313 a8083063 Iustin Pop
2314 a8083063 Iustin Pop
2315 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
2316 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2317 a8083063 Iustin Pop

2318 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
2319 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
2320 a8083063 Iustin Pop

2321 a8083063 Iustin Pop
  """
2322 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2323 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2324 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2325 a8083063 Iustin Pop
  if not device.Attach():
2326 a8083063 Iustin Pop
    device.Assemble()
2327 be1ba2bd Iustin Pop
    if not device.Attach():
2328 be1ba2bd Iustin Pop
      raise errors.BlockDeviceError("Can't find a valid block device for"
2329 be1ba2bd Iustin Pop
                                    " %s/%s/%s" %
2330 be1ba2bd Iustin Pop
                                    (dev_type, unique_id, children))
2331 a8083063 Iustin Pop
  return device
2332 a8083063 Iustin Pop
2333 a8083063 Iustin Pop
2334 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2335 a8083063 Iustin Pop
  """Create a device.
2336 a8083063 Iustin Pop

2337 a8083063 Iustin Pop
  """
2338 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2339 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2340 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2341 a8083063 Iustin Pop
  return device