Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 150e978f

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

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

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

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

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

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

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

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

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

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

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

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

115 a8083063 Iustin Pop
    """
116 a8083063 Iustin Pop
    status = True
117 a8083063 Iustin Pop
    for child in self._children:
118 a8083063 Iustin Pop
      if not isinstance(child, BlockDev):
119 a8083063 Iustin Pop
        raise TypeError("Invalid child passed of type '%s'" % type(child))
120 a8083063 Iustin Pop
      if not status:
121 a8083063 Iustin Pop
        break
122 a8083063 Iustin Pop
      status = status and child.Assemble()
123 a8083063 Iustin Pop
      if not status:
124 a8083063 Iustin Pop
        break
125 a8083063 Iustin Pop
      status = status and child.Open()
126 a8083063 Iustin Pop
127 a8083063 Iustin Pop
    if not status:
128 a8083063 Iustin Pop
      for child in self._children:
129 a8083063 Iustin Pop
        child.Shutdown()
130 a8083063 Iustin Pop
    return status
131 a8083063 Iustin Pop
132 a8083063 Iustin Pop
  def Attach(self):
133 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
134 a8083063 Iustin Pop

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

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

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

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

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

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

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

171 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
172 f3e513ad Iustin Pop

173 f3e513ad Iustin Pop
    """
174 f3e513ad Iustin Pop
    raise NotImplementedError
175 f3e513ad Iustin Pop
176 a8083063 Iustin Pop
  def GetStatus(self):
177 a8083063 Iustin Pop
    """Return the status of the device.
178 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

222 a8083063 Iustin Pop
    Returns:
223 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
224 0834c866 Iustin Pop

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

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

230 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
231 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
232 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
233 a8083063 Iustin Pop

234 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
235 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
236 0834c866 Iustin Pop
    return False (not degraded).
237 0834c866 Iustin Pop

238 a8083063 Iustin Pop
    """
239 0834c866 Iustin Pop
    return None, None, False, False
240 a8083063 Iustin Pop
241 a8083063 Iustin Pop
242 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
243 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
244 a8083063 Iustin Pop

245 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
246 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
247 a8083063 Iustin Pop
    children.
248 a8083063 Iustin Pop

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

270 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
271 a0c3fea1 Michael Hanselmann

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

286 a8083063 Iustin Pop
  """
287 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
288 a8083063 Iustin Pop
    """Attaches to a LV device.
289 a8083063 Iustin Pop

290 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
291 a8083063 Iustin Pop

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

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

333 a8083063 Iustin Pop
    Args:
334 a8083063 Iustin Pop
      vg_name: the volume group name
335 a8083063 Iustin Pop

336 a8083063 Iustin Pop
    Returns:
337 a8083063 Iustin Pop
      list of (free_space, name) with free_space in mebibytes
338 098c0958 Michael Hanselmann

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

363 a8083063 Iustin Pop
    """
364 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
365 a8083063 Iustin Pop
      # the LV does not exist
366 a8083063 Iustin Pop
      return True
367 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
368 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
369 a8083063 Iustin Pop
    if result.failed:
370 a8083063 Iustin Pop
      logger.Error("Can't lvremove: %s" % result.fail_reason)
371 a8083063 Iustin Pop
372 a8083063 Iustin Pop
    return not result.failed
373 a8083063 Iustin Pop
374 f3e513ad Iustin Pop
  def Rename(self, new_id):
375 f3e513ad Iustin Pop
    """Rename this logical volume.
376 f3e513ad Iustin Pop

377 f3e513ad Iustin Pop
    """
378 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
379 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
380 f3e513ad Iustin Pop
    new_vg, new_name = new_id
381 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
382 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
383 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
384 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
385 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
386 f3e513ad Iustin Pop
    if result.failed:
387 f3e513ad Iustin Pop
      raise errors.BlockDeviceError("Failed to rename the logical volume: %s" %
388 f3e513ad Iustin Pop
                                    result.output)
389 be345db0 Iustin Pop
    self._lv_name = new_name
390 be345db0 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
391 be345db0 Iustin Pop
392 a8083063 Iustin Pop
393 a8083063 Iustin Pop
  def Attach(self):
394 a8083063 Iustin Pop
    """Attach to an existing LV.
395 a8083063 Iustin Pop

396 a8083063 Iustin Pop
    This method will try to see if an existing and active LV exists
397 a8083063 Iustin Pop
    which matches the our name. If so, its major/minor will be
398 a8083063 Iustin Pop
    recorded.
399 a8083063 Iustin Pop

400 a8083063 Iustin Pop
    """
401 a8083063 Iustin Pop
    result = utils.RunCmd(["lvdisplay", self.dev_path])
402 a8083063 Iustin Pop
    if result.failed:
403 fcb1f331 Michael Hanselmann
      logger.Error("Can't find LV %s: %s, %s" %
404 fcb1f331 Michael Hanselmann
                   (self.dev_path, result.fail_reason, result.output))
405 a8083063 Iustin Pop
      return False
406 a8083063 Iustin Pop
    match = re.compile("^ *Block device *([0-9]+):([0-9]+).*$")
407 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
408 a8083063 Iustin Pop
      match_result = match.match(line)
409 a8083063 Iustin Pop
      if match_result:
410 a8083063 Iustin Pop
        self.major = int(match_result.group(1))
411 a8083063 Iustin Pop
        self.minor = int(match_result.group(2))
412 a8083063 Iustin Pop
        return True
413 a8083063 Iustin Pop
    return False
414 a8083063 Iustin Pop
415 a8083063 Iustin Pop
  def Assemble(self):
416 a8083063 Iustin Pop
    """Assemble the device.
417 a8083063 Iustin Pop

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

421 a8083063 Iustin Pop
    """
422 a8083063 Iustin Pop
    return True
423 a8083063 Iustin Pop
424 a8083063 Iustin Pop
  def Shutdown(self):
425 a8083063 Iustin Pop
    """Shutdown the device.
426 a8083063 Iustin Pop

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

430 a8083063 Iustin Pop
    """
431 a8083063 Iustin Pop
    return True
432 a8083063 Iustin Pop
433 a8083063 Iustin Pop
  def GetStatus(self):
434 a8083063 Iustin Pop
    """Return the status of the device.
435 a8083063 Iustin Pop

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

440 a8083063 Iustin Pop
    """
441 a8083063 Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
442 a8083063 Iustin Pop
    if result.failed:
443 a8083063 Iustin Pop
      logger.Error("Can't display lv: %s" % result.fail_reason)
444 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
445 a8083063 Iustin Pop
    out = result.stdout.strip()
446 a8083063 Iustin Pop
    # format: type/permissions/alloc/fixed_minor/state/open
447 a8083063 Iustin Pop
    if len(out) != 6:
448 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
449 a8083063 Iustin Pop
    #writable = (out[1] == "w")
450 a8083063 Iustin Pop
    active = (out[4] == "a")
451 a8083063 Iustin Pop
    online = (out[5] == "o")
452 a8083063 Iustin Pop
    if online:
453 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
454 a8083063 Iustin Pop
    elif active:
455 a8083063 Iustin Pop
      retval = self.STATUS_STANDBY
456 a8083063 Iustin Pop
    else:
457 a8083063 Iustin Pop
      retval = self.STATUS_EXISTING
458 a8083063 Iustin Pop
459 a8083063 Iustin Pop
    return retval
460 a8083063 Iustin Pop
461 9db6dbce Iustin Pop
  def GetSyncStatus(self):
462 9db6dbce Iustin Pop
    """Returns the sync status of the device.
463 9db6dbce Iustin Pop

464 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
465 9db6dbce Iustin Pop
    status of the mirror.
466 9db6dbce Iustin Pop

467 9db6dbce Iustin Pop
    Returns:
468 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
469 9db6dbce Iustin Pop

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

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

481 9db6dbce Iustin Pop
    """
482 9db6dbce Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
483 9db6dbce Iustin Pop
    if result.failed:
484 9db6dbce Iustin Pop
      logger.Error("Can't display lv: %s" % result.fail_reason)
485 0834c866 Iustin Pop
      return None, None, True, True
486 9db6dbce Iustin Pop
    out = result.stdout.strip()
487 9db6dbce Iustin Pop
    # format: type/permissions/alloc/fixed_minor/state/open
488 9db6dbce Iustin Pop
    if len(out) != 6:
489 0834c866 Iustin Pop
      logger.Debug("Error in lvs output: attrs=%s, len != 6" % out)
490 0834c866 Iustin Pop
      return None, None, True, True
491 0834c866 Iustin Pop
    ldisk = out[0] == 'v' # virtual volume, i.e. doesn't have
492 0834c866 Iustin Pop
                          # backing storage
493 0834c866 Iustin Pop
    return None, None, ldisk, ldisk
494 9db6dbce Iustin Pop
495 a8083063 Iustin Pop
  def Open(self, force=False):
496 a8083063 Iustin Pop
    """Make the device ready for I/O.
497 a8083063 Iustin Pop

498 a8083063 Iustin Pop
    This is a no-op for the LV device type.
499 a8083063 Iustin Pop

500 a8083063 Iustin Pop
    """
501 a8083063 Iustin Pop
    return True
502 a8083063 Iustin Pop
503 a8083063 Iustin Pop
  def Close(self):
504 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
505 a8083063 Iustin Pop

506 a8083063 Iustin Pop
    This is a no-op for the LV device type.
507 a8083063 Iustin Pop

508 a8083063 Iustin Pop
    """
509 a8083063 Iustin Pop
    return True
510 a8083063 Iustin Pop
511 a8083063 Iustin Pop
  def Snapshot(self, size):
512 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
513 a8083063 Iustin Pop

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

543 a0c3fea1 Michael Hanselmann
    """
544 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
545 a0c3fea1 Michael Hanselmann
546 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
547 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
548 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
549 a0c3fea1 Michael Hanselmann
550 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
551 a0c3fea1 Michael Hanselmann
    text = text[:128]
552 a0c3fea1 Michael Hanselmann
553 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
554 a0c3fea1 Michael Hanselmann
                           self.dev_path])
555 a0c3fea1 Michael Hanselmann
    if result.failed:
556 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Command: %s error: %s" %
557 3ecf6786 Iustin Pop
                                    (result.cmd, result.fail_reason))
558 a0c3fea1 Michael Hanselmann
559 a0c3fea1 Michael Hanselmann
560 a8083063 Iustin Pop
class MDRaid1(BlockDev):
561 a8083063 Iustin Pop
  """raid1 device implemented via md.
562 a8083063 Iustin Pop

563 a8083063 Iustin Pop
  """
564 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
565 a8083063 Iustin Pop
    super(MDRaid1, self).__init__(unique_id, children)
566 a8083063 Iustin Pop
    self.major = 9
567 a8083063 Iustin Pop
    self.Attach()
568 a8083063 Iustin Pop
569 a8083063 Iustin Pop
  def Attach(self):
570 a8083063 Iustin Pop
    """Find an array which matches our config and attach to it.
571 a8083063 Iustin Pop

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

574 a8083063 Iustin Pop
    """
575 a8083063 Iustin Pop
    minor = self._FindMDByUUID(self.unique_id)
576 a8083063 Iustin Pop
    if minor is not None:
577 a8083063 Iustin Pop
      self._SetFromMinor(minor)
578 a8083063 Iustin Pop
    else:
579 a8083063 Iustin Pop
      self.minor = None
580 a8083063 Iustin Pop
      self.dev_path = None
581 a8083063 Iustin Pop
582 a8083063 Iustin Pop
    return (minor is not None)
583 a8083063 Iustin Pop
584 a8083063 Iustin Pop
  @staticmethod
585 a8083063 Iustin Pop
  def _GetUsedDevs():
586 a8083063 Iustin Pop
    """Compute the list of in-use MD devices.
587 a8083063 Iustin Pop

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

591 a8083063 Iustin Pop
    """
592 a8083063 Iustin Pop
    mdstat = open("/proc/mdstat", "r")
593 a8083063 Iustin Pop
    data = mdstat.readlines()
594 a8083063 Iustin Pop
    mdstat.close()
595 a8083063 Iustin Pop
596 a8083063 Iustin Pop
    used_md = {}
597 a8083063 Iustin Pop
    valid_line = re.compile("^md([0-9]+) : .*$")
598 a8083063 Iustin Pop
    for line in data:
599 a8083063 Iustin Pop
      match = valid_line.match(line)
600 a8083063 Iustin Pop
      if match:
601 a8083063 Iustin Pop
        md_no = int(match.group(1))
602 a8083063 Iustin Pop
        used_md[md_no] = line
603 a8083063 Iustin Pop
604 a8083063 Iustin Pop
    return used_md
605 a8083063 Iustin Pop
606 a8083063 Iustin Pop
  @staticmethod
607 a8083063 Iustin Pop
  def _GetDevInfo(minor):
608 a8083063 Iustin Pop
    """Get info about a MD device.
609 a8083063 Iustin Pop

610 a8083063 Iustin Pop
    Currently only uuid is returned.
611 a8083063 Iustin Pop

612 a8083063 Iustin Pop
    """
613 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-D", "/dev/md%d" % minor])
614 a8083063 Iustin Pop
    if result.failed:
615 a8083063 Iustin Pop
      logger.Error("Can't display md: %s" % result.fail_reason)
616 a8083063 Iustin Pop
      return None
617 a8083063 Iustin Pop
    retval = {}
618 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
619 a8083063 Iustin Pop
      line = line.strip()
620 a8083063 Iustin Pop
      kv = line.split(" : ", 1)
621 a8083063 Iustin Pop
      if kv:
622 a8083063 Iustin Pop
        if kv[0] == "UUID":
623 8d519422 Iustin Pop
          retval["uuid"] = kv[1].split()[0]
624 a8083063 Iustin Pop
        elif kv[0] == "State":
625 a8083063 Iustin Pop
          retval["state"] = kv[1].split(", ")
626 a8083063 Iustin Pop
    return retval
627 a8083063 Iustin Pop
628 a8083063 Iustin Pop
  @staticmethod
629 a8083063 Iustin Pop
  def _FindUnusedMinor():
630 a8083063 Iustin Pop
    """Compute an unused MD minor.
631 a8083063 Iustin Pop

632 a8083063 Iustin Pop
    This code assumes that there are 256 minors only.
633 a8083063 Iustin Pop

634 a8083063 Iustin Pop
    """
635 a8083063 Iustin Pop
    used_md = MDRaid1._GetUsedDevs()
636 a8083063 Iustin Pop
    i = 0
637 a8083063 Iustin Pop
    while i < 256:
638 a8083063 Iustin Pop
      if i not in used_md:
639 a8083063 Iustin Pop
        break
640 a8083063 Iustin Pop
      i += 1
641 a8083063 Iustin Pop
    if i == 256:
642 a8083063 Iustin Pop
      logger.Error("Critical: Out of md minor numbers.")
643 0caf6485 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free MD minor")
644 a8083063 Iustin Pop
    return i
645 a8083063 Iustin Pop
646 a8083063 Iustin Pop
  @classmethod
647 a8083063 Iustin Pop
  def _FindMDByUUID(cls, uuid):
648 a8083063 Iustin Pop
    """Find the minor of an MD array with a given UUID.
649 a8083063 Iustin Pop

650 a8083063 Iustin Pop
    """
651 a8083063 Iustin Pop
    md_list = cls._GetUsedDevs()
652 a8083063 Iustin Pop
    for minor in md_list:
653 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
654 a8083063 Iustin Pop
      if info and info["uuid"] == uuid:
655 a8083063 Iustin Pop
        return minor
656 a8083063 Iustin Pop
    return None
657 a8083063 Iustin Pop
658 1a87dca7 Iustin Pop
  @staticmethod
659 1a87dca7 Iustin Pop
  def _ZeroSuperblock(dev_path):
660 1a87dca7 Iustin Pop
    """Zero the possible locations for an MD superblock.
661 1a87dca7 Iustin Pop

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

666 1a87dca7 Iustin Pop
    The superblocks are located at (negative values are relative to
667 1a87dca7 Iustin Pop
    the end of the block device):
668 1a87dca7 Iustin Pop
      - -128k to end for version 0.90 superblock
669 1a87dca7 Iustin Pop
      - -8k to -12k for version 1.0 superblock (included in the above)
670 1a87dca7 Iustin Pop
      - 0k to 4k for version 1.1 superblock
671 1a87dca7 Iustin Pop
      - 4k to 8k for version 1.2 superblock
672 1a87dca7 Iustin Pop

673 1a87dca7 Iustin Pop
    To cover all situations, the zero-ing will be:
674 1a87dca7 Iustin Pop
      - 0k to 128k
675 1a87dca7 Iustin Pop
      - -128k to end
676 1a87dca7 Iustin Pop

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

680 1a87dca7 Iustin Pop
    Note that this function depends on the fact that one can open,
681 1a87dca7 Iustin Pop
    read and write block devices normally.
682 1a87dca7 Iustin Pop

683 1a87dca7 Iustin Pop
    """
684 1a87dca7 Iustin Pop
    overwrite_size = 128 * 1024
685 1a87dca7 Iustin Pop
    empty_buf = '\0' * overwrite_size
686 1a87dca7 Iustin Pop
    fd = open(dev_path, "r+")
687 1a87dca7 Iustin Pop
    try:
688 1a87dca7 Iustin Pop
      fd.seek(0, 0)
689 1a87dca7 Iustin Pop
      p1 = fd.tell()
690 1a87dca7 Iustin Pop
      fd.write(empty_buf)
691 1a87dca7 Iustin Pop
      p2 = fd.tell()
692 1a87dca7 Iustin Pop
      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
693 1a87dca7 Iustin Pop
      fd.seek(-overwrite_size, 2)
694 1a87dca7 Iustin Pop
      p1 = fd.tell()
695 1a87dca7 Iustin Pop
      fd.write(empty_buf)
696 1a87dca7 Iustin Pop
      p2 = fd.tell()
697 1a87dca7 Iustin Pop
      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
698 1a87dca7 Iustin Pop
    finally:
699 1a87dca7 Iustin Pop
      fd.close()
700 1a87dca7 Iustin Pop
701 a8083063 Iustin Pop
  @classmethod
702 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
703 a8083063 Iustin Pop
    """Create a new MD raid1 array.
704 a8083063 Iustin Pop

705 a8083063 Iustin Pop
    """
706 a8083063 Iustin Pop
    if not isinstance(children, (tuple, list)):
707 a8083063 Iustin Pop
      raise ValueError("Invalid setup data for MDRaid1 dev: %s" %
708 a8083063 Iustin Pop
                       str(children))
709 a8083063 Iustin Pop
    for i in children:
710 a8083063 Iustin Pop
      if not isinstance(i, BlockDev):
711 a8083063 Iustin Pop
        raise ValueError("Invalid member in MDRaid1 dev: %s" % type(i))
712 a8083063 Iustin Pop
    for i in children:
713 1a87dca7 Iustin Pop
      try:
714 1a87dca7 Iustin Pop
        cls._ZeroSuperblock(i.dev_path)
715 1a87dca7 Iustin Pop
      except EnvironmentError, err:
716 1a87dca7 Iustin Pop
        logger.Error("Can't zero superblock for %s: %s" %
717 1a87dca7 Iustin Pop
                     (i.dev_path, str(err)))
718 a8083063 Iustin Pop
        return None
719 a8083063 Iustin Pop
    minor = cls._FindUnusedMinor()
720 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--create", "/dev/md%d" % minor,
721 a8083063 Iustin Pop
                           "--auto=yes", "--force", "-l1",
722 a8083063 Iustin Pop
                           "-n%d" % len(children)] +
723 a8083063 Iustin Pop
                          [dev.dev_path for dev in children])
724 a8083063 Iustin Pop
725 a8083063 Iustin Pop
    if result.failed:
726 1a87dca7 Iustin Pop
      logger.Error("Can't create md: %s: %s" % (result.fail_reason,
727 1a87dca7 Iustin Pop
                                                result.output))
728 a8083063 Iustin Pop
      return None
729 a8083063 Iustin Pop
    info = cls._GetDevInfo(minor)
730 a8083063 Iustin Pop
    if not info or not "uuid" in info:
731 a8083063 Iustin Pop
      logger.Error("Wrong information returned from mdadm -D: %s" % str(info))
732 a8083063 Iustin Pop
      return None
733 a8083063 Iustin Pop
    return MDRaid1(info["uuid"], children)
734 a8083063 Iustin Pop
735 a8083063 Iustin Pop
  def Remove(self):
736 a8083063 Iustin Pop
    """Stub remove function for MD RAID 1 arrays.
737 a8083063 Iustin Pop

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

740 a8083063 Iustin Pop
    """
741 a8083063 Iustin Pop
    #TODO: maybe zero superblock on child devices?
742 a8083063 Iustin Pop
    return self.Shutdown()
743 a8083063 Iustin Pop
744 f3e513ad Iustin Pop
  def Rename(self, new_id):
745 f3e513ad Iustin Pop
    """Rename a device.
746 f3e513ad Iustin Pop

747 f3e513ad Iustin Pop
    This is not supported for md raid1 devices.
748 f3e513ad Iustin Pop

749 f3e513ad Iustin Pop
    """
750 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a md raid1 device")
751 a8083063 Iustin Pop
752 153d9724 Iustin Pop
  def AddChildren(self, devices):
753 153d9724 Iustin Pop
    """Add new member(s) to the md raid1.
754 a8083063 Iustin Pop

755 a8083063 Iustin Pop
    """
756 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
757 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device")
758 153d9724 Iustin Pop
759 153d9724 Iustin Pop
    args = ["mdadm", "-a", self.dev_path]
760 153d9724 Iustin Pop
    for dev in devices:
761 153d9724 Iustin Pop
      if dev.dev_path is None:
762 153d9724 Iustin Pop
        raise errors.BlockDeviceError("Child '%s' is not initialised" % dev)
763 153d9724 Iustin Pop
      dev.Open()
764 153d9724 Iustin Pop
      args.append(dev.dev_path)
765 153d9724 Iustin Pop
    result = utils.RunCmd(args)
766 a8083063 Iustin Pop
    if result.failed:
767 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Failed to add new device to array: %s" %
768 3ecf6786 Iustin Pop
                                    result.output)
769 153d9724 Iustin Pop
    new_len = len(self._children) + len(devices)
770 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", self.dev_path, "-n", new_len])
771 a8083063 Iustin Pop
    if result.failed:
772 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't grow md array: %s" %
773 3ecf6786 Iustin Pop
                                    result.output)
774 153d9724 Iustin Pop
    self._children.extend(devices)
775 a8083063 Iustin Pop
776 153d9724 Iustin Pop
  def RemoveChildren(self, devices):
777 153d9724 Iustin Pop
    """Remove member(s) from the md raid1.
778 a8083063 Iustin Pop

779 a8083063 Iustin Pop
    """
780 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
781 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device")
782 153d9724 Iustin Pop
    new_len = len(self._children) - len(devices)
783 153d9724 Iustin Pop
    if new_len < 1:
784 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Can't reduce to less than one child")
785 153d9724 Iustin Pop
    args = ["mdadm", "-f", self.dev_path]
786 153d9724 Iustin Pop
    orig_devs = []
787 153d9724 Iustin Pop
    for dev in devices:
788 e739bd57 Iustin Pop
      args.append(dev)
789 153d9724 Iustin Pop
      for c in self._children:
790 e739bd57 Iustin Pop
        if c.dev_path == dev:
791 153d9724 Iustin Pop
          orig_devs.append(c)
792 153d9724 Iustin Pop
          break
793 153d9724 Iustin Pop
      else:
794 153d9724 Iustin Pop
        raise errors.BlockDeviceError("Can't find device '%s' for removal" %
795 153d9724 Iustin Pop
                                      dev)
796 153d9724 Iustin Pop
    result = utils.RunCmd(args)
797 a8083063 Iustin Pop
    if result.failed:
798 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Failed to mark device(s) as failed: %s" %
799 3ecf6786 Iustin Pop
                                    result.output)
800 a8083063 Iustin Pop
801 a8083063 Iustin Pop
    # it seems here we need a short delay for MD to update its
802 a8083063 Iustin Pop
    # superblocks
803 a8083063 Iustin Pop
    time.sleep(0.5)
804 153d9724 Iustin Pop
    args[1] = "-r"
805 153d9724 Iustin Pop
    result = utils.RunCmd(args)
806 a8083063 Iustin Pop
    if result.failed:
807 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Failed to remove device(s) from array:"
808 153d9724 Iustin Pop
                                    " %s" % result.output)
809 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", "--force", self.dev_path,
810 a8083063 Iustin Pop
                           "-n", new_len])
811 a8083063 Iustin Pop
    if result.failed:
812 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't shrink md array: %s" %
813 3ecf6786 Iustin Pop
                                    result.output)
814 153d9724 Iustin Pop
    for dev in orig_devs:
815 153d9724 Iustin Pop
      self._children.remove(dev)
816 a8083063 Iustin Pop
817 a8083063 Iustin Pop
  def GetStatus(self):
818 a8083063 Iustin Pop
    """Return the status of the device.
819 a8083063 Iustin Pop

820 a8083063 Iustin Pop
    """
821 a8083063 Iustin Pop
    self.Attach()
822 a8083063 Iustin Pop
    if self.minor is None:
823 a8083063 Iustin Pop
      retval = self.STATUS_UNKNOWN
824 a8083063 Iustin Pop
    else:
825 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
826 a8083063 Iustin Pop
    return retval
827 a8083063 Iustin Pop
828 a8083063 Iustin Pop
  def _SetFromMinor(self, minor):
829 a8083063 Iustin Pop
    """Set our parameters based on the given minor.
830 a8083063 Iustin Pop

831 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
832 a8083063 Iustin Pop

833 a8083063 Iustin Pop
    """
834 a8083063 Iustin Pop
    self.minor = minor
835 a8083063 Iustin Pop
    self.dev_path = "/dev/md%d" % minor
836 a8083063 Iustin Pop
837 a8083063 Iustin Pop
  def Assemble(self):
838 a8083063 Iustin Pop
    """Assemble the MD device.
839 a8083063 Iustin Pop

840 a8083063 Iustin Pop
    At this point we should have:
841 a8083063 Iustin Pop
      - list of children devices
842 a8083063 Iustin Pop
      - uuid
843 a8083063 Iustin Pop

844 a8083063 Iustin Pop
    """
845 a8083063 Iustin Pop
    result = super(MDRaid1, self).Assemble()
846 a8083063 Iustin Pop
    if not result:
847 a8083063 Iustin Pop
      return result
848 a8083063 Iustin Pop
    md_list = self._GetUsedDevs()
849 a8083063 Iustin Pop
    for minor in md_list:
850 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
851 a8083063 Iustin Pop
      if info and info["uuid"] == self.unique_id:
852 a8083063 Iustin Pop
        self._SetFromMinor(minor)
853 a8083063 Iustin Pop
        logger.Info("MD array %s already started" % str(self))
854 a8083063 Iustin Pop
        return True
855 a8083063 Iustin Pop
    free_minor = self._FindUnusedMinor()
856 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-A", "--auto=yes", "--uuid",
857 a8083063 Iustin Pop
                           self.unique_id, "/dev/md%d" % free_minor] +
858 a8083063 Iustin Pop
                          [bdev.dev_path for bdev in self._children])
859 a8083063 Iustin Pop
    if result.failed:
860 8d519422 Iustin Pop
      logger.Error("Can't assemble MD array: %s: %s" %
861 8d519422 Iustin Pop
                   (result.fail_reason, result.output))
862 a8083063 Iustin Pop
      self.minor = None
863 a8083063 Iustin Pop
    else:
864 a8083063 Iustin Pop
      self.minor = free_minor
865 a8083063 Iustin Pop
    return not result.failed
866 a8083063 Iustin Pop
867 a8083063 Iustin Pop
  def Shutdown(self):
868 a8083063 Iustin Pop
    """Tear down the MD array.
869 a8083063 Iustin Pop

870 a8083063 Iustin Pop
    This does a 'mdadm --stop' so after this command, the array is no
871 a8083063 Iustin Pop
    longer available.
872 a8083063 Iustin Pop

873 a8083063 Iustin Pop
    """
874 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
875 a8083063 Iustin Pop
      logger.Info("MD object not attached to a device")
876 a8083063 Iustin Pop
      return True
877 a8083063 Iustin Pop
878 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--stop", "/dev/md%d" % self.minor])
879 a8083063 Iustin Pop
    if result.failed:
880 a8083063 Iustin Pop
      logger.Error("Can't stop MD array: %s" % result.fail_reason)
881 a8083063 Iustin Pop
      return False
882 a8083063 Iustin Pop
    self.minor = None
883 a8083063 Iustin Pop
    self.dev_path = None
884 a8083063 Iustin Pop
    return True
885 a8083063 Iustin Pop
886 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
887 a8083063 Iustin Pop
    """Set the maximum sync speed for the MD array.
888 a8083063 Iustin Pop

889 a8083063 Iustin Pop
    """
890 a8083063 Iustin Pop
    result = super(MDRaid1, self).SetSyncSpeed(kbytes)
891 a8083063 Iustin Pop
    if self.minor is None:
892 a8083063 Iustin Pop
      logger.Error("MD array not attached to a device")
893 a8083063 Iustin Pop
      return False
894 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_max" % self.minor, "w")
895 a8083063 Iustin Pop
    try:
896 a8083063 Iustin Pop
      f.write("%d" % kbytes)
897 a8083063 Iustin Pop
    finally:
898 a8083063 Iustin Pop
      f.close()
899 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_min" % self.minor, "w")
900 a8083063 Iustin Pop
    try:
901 a8083063 Iustin Pop
      f.write("%d" % (kbytes/2))
902 a8083063 Iustin Pop
    finally:
903 a8083063 Iustin Pop
      f.close()
904 a8083063 Iustin Pop
    return result
905 a8083063 Iustin Pop
906 a8083063 Iustin Pop
  def GetSyncStatus(self):
907 a8083063 Iustin Pop
    """Returns the sync status of the device.
908 a8083063 Iustin Pop

909 a8083063 Iustin Pop
    Returns:
910 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
911 a8083063 Iustin Pop

912 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
913 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
914 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
915 0834c866 Iustin Pop

916 0834c866 Iustin Pop
    The ldisk parameter is always true for MD devices.
917 a8083063 Iustin Pop

918 a8083063 Iustin Pop
    """
919 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
920 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
921 a8083063 Iustin Pop
    dev_info = self._GetDevInfo(self.minor)
922 a8083063 Iustin Pop
    is_clean = ("state" in dev_info and
923 a8083063 Iustin Pop
                len(dev_info["state"]) == 1 and
924 a8083063 Iustin Pop
                dev_info["state"][0] in ("clean", "active"))
925 a8083063 Iustin Pop
    sys_path = "/sys/block/md%s/md/" % self.minor
926 a8083063 Iustin Pop
    f = file(sys_path + "sync_action")
927 a8083063 Iustin Pop
    sync_status = f.readline().strip()
928 a8083063 Iustin Pop
    f.close()
929 a8083063 Iustin Pop
    if sync_status == "idle":
930 0834c866 Iustin Pop
      return None, None, not is_clean, False
931 a8083063 Iustin Pop
    f = file(sys_path + "sync_completed")
932 a8083063 Iustin Pop
    sync_completed = f.readline().strip().split(" / ")
933 a8083063 Iustin Pop
    f.close()
934 a8083063 Iustin Pop
    if len(sync_completed) != 2:
935 0834c866 Iustin Pop
      return 0, None, not is_clean, False
936 a8083063 Iustin Pop
    sync_done, sync_total = [float(i) for i in sync_completed]
937 a8083063 Iustin Pop
    sync_percent = 100.0*sync_done/sync_total
938 a8083063 Iustin Pop
    f = file(sys_path + "sync_speed")
939 a8083063 Iustin Pop
    sync_speed_k = int(f.readline().strip())
940 a8083063 Iustin Pop
    if sync_speed_k == 0:
941 a8083063 Iustin Pop
      time_est = None
942 a8083063 Iustin Pop
    else:
943 a8083063 Iustin Pop
      time_est = (sync_total - sync_done) / 2 / sync_speed_k
944 0834c866 Iustin Pop
    return sync_percent, time_est, not is_clean, False
945 a8083063 Iustin Pop
946 a8083063 Iustin Pop
  def Open(self, force=False):
947 a8083063 Iustin Pop
    """Make the device ready for I/O.
948 a8083063 Iustin Pop

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

952 a8083063 Iustin Pop
    """
953 a8083063 Iustin Pop
    return True
954 a8083063 Iustin Pop
955 a8083063 Iustin Pop
  def Close(self):
956 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
957 a8083063 Iustin Pop

958 a8083063 Iustin Pop
    This is a no-op for the MDRaid1 device type, but see comment for
959 a8083063 Iustin Pop
    `Open()`.
960 a8083063 Iustin Pop

961 a8083063 Iustin Pop
    """
962 a8083063 Iustin Pop
    return True
963 a8083063 Iustin Pop
964 a8083063 Iustin Pop
965 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
966 0f7f32d9 Iustin Pop
  """Base DRBD class.
967 a8083063 Iustin Pop

968 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
969 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
970 0f7f32d9 Iustin Pop

971 0f7f32d9 Iustin Pop
  """
972 0f7f32d9 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
973 0f7f32d9 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)\)")
974 770fe0f9 Iustin Pop
  _DRBD_MAJOR = 147
975 770fe0f9 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
976 770fe0f9 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
977 770fe0f9 Iustin Pop
  _ST_CONNECTED = "Connected"
978 0f7f32d9 Iustin Pop
979 0f7f32d9 Iustin Pop
  @staticmethod
980 0f7f32d9 Iustin Pop
  def _GetProcData():
981 0f7f32d9 Iustin Pop
    """Return data from /proc/drbd.
982 0f7f32d9 Iustin Pop

983 0f7f32d9 Iustin Pop
    """
984 0f7f32d9 Iustin Pop
    stat = open("/proc/drbd", "r")
985 0f7f32d9 Iustin Pop
    try:
986 0f7f32d9 Iustin Pop
      data = stat.read().splitlines()
987 0f7f32d9 Iustin Pop
    finally:
988 0f7f32d9 Iustin Pop
      stat.close()
989 0f7f32d9 Iustin Pop
    if not data:
990 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't read any data from /proc/drbd")
991 0f7f32d9 Iustin Pop
    return data
992 0f7f32d9 Iustin Pop
993 5a47ad20 Iustin Pop
  @staticmethod
994 5a47ad20 Iustin Pop
  def _MassageProcData(data):
995 5a47ad20 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
996 5a47ad20 Iustin Pop

997 5a47ad20 Iustin Pop
    Returns:
998 5a47ad20 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
999 5a47ad20 Iustin Pop

1000 5a47ad20 Iustin Pop
    """
1001 5a47ad20 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
1002 5a47ad20 Iustin Pop
    results = {}
1003 5a47ad20 Iustin Pop
    old_minor = old_line = None
1004 5a47ad20 Iustin Pop
    for line in data:
1005 5a47ad20 Iustin Pop
      lresult = lmatch.match(line)
1006 5a47ad20 Iustin Pop
      if lresult is not None:
1007 5a47ad20 Iustin Pop
        if old_minor is not None:
1008 5a47ad20 Iustin Pop
          results[old_minor] = old_line
1009 5a47ad20 Iustin Pop
        old_minor = int(lresult.group(1))
1010 5a47ad20 Iustin Pop
        old_line = line
1011 5a47ad20 Iustin Pop
      else:
1012 5a47ad20 Iustin Pop
        if old_minor is not None:
1013 5a47ad20 Iustin Pop
          old_line += " " + line.strip()
1014 5a47ad20 Iustin Pop
    # add last line
1015 5a47ad20 Iustin Pop
    if old_minor is not None:
1016 5a47ad20 Iustin Pop
      results[old_minor] = old_line
1017 5a47ad20 Iustin Pop
    return results
1018 5a47ad20 Iustin Pop
1019 0f7f32d9 Iustin Pop
  @classmethod
1020 0f7f32d9 Iustin Pop
  def _GetVersion(cls):
1021 0f7f32d9 Iustin Pop
    """Return the DRBD version.
1022 0f7f32d9 Iustin Pop

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

1025 0f7f32d9 Iustin Pop
    """
1026 0f7f32d9 Iustin Pop
    proc_data = cls._GetProcData()
1027 0f7f32d9 Iustin Pop
    first_line = proc_data[0].strip()
1028 0f7f32d9 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
1029 0f7f32d9 Iustin Pop
    if not version:
1030 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
1031 0f7f32d9 Iustin Pop
                                    first_line)
1032 0f7f32d9 Iustin Pop
    return [int(val) for val in version.groups()]
1033 0f7f32d9 Iustin Pop
1034 770fe0f9 Iustin Pop
  @staticmethod
1035 770fe0f9 Iustin Pop
  def _DevPath(minor):
1036 770fe0f9 Iustin Pop
    """Return the path to a drbd device for a given minor.
1037 770fe0f9 Iustin Pop

1038 770fe0f9 Iustin Pop
    """
1039 770fe0f9 Iustin Pop
    return "/dev/drbd%d" % minor
1040 770fe0f9 Iustin Pop
1041 770fe0f9 Iustin Pop
  @classmethod
1042 770fe0f9 Iustin Pop
  def _GetUsedDevs(cls):
1043 770fe0f9 Iustin Pop
    """Compute the list of used DRBD devices.
1044 770fe0f9 Iustin Pop

1045 770fe0f9 Iustin Pop
    """
1046 770fe0f9 Iustin Pop
    data = cls._GetProcData()
1047 770fe0f9 Iustin Pop
1048 770fe0f9 Iustin Pop
    used_devs = {}
1049 770fe0f9 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
1050 770fe0f9 Iustin Pop
    for line in data:
1051 770fe0f9 Iustin Pop
      match = valid_line.match(line)
1052 770fe0f9 Iustin Pop
      if not match:
1053 770fe0f9 Iustin Pop
        continue
1054 770fe0f9 Iustin Pop
      minor = int(match.group(1))
1055 770fe0f9 Iustin Pop
      state = match.group(2)
1056 770fe0f9 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1057 770fe0f9 Iustin Pop
        continue
1058 770fe0f9 Iustin Pop
      used_devs[minor] = state, line
1059 770fe0f9 Iustin Pop
1060 770fe0f9 Iustin Pop
    return used_devs
1061 770fe0f9 Iustin Pop
1062 5a47ad20 Iustin Pop
  def _SetFromMinor(self, minor):
1063 5a47ad20 Iustin Pop
    """Set our parameters based on the given minor.
1064 5a47ad20 Iustin Pop

1065 5a47ad20 Iustin Pop
    This sets our minor variable and our dev_path.
1066 5a47ad20 Iustin Pop

1067 5a47ad20 Iustin Pop
    """
1068 5a47ad20 Iustin Pop
    if minor is None:
1069 5a47ad20 Iustin Pop
      self.minor = self.dev_path = None
1070 5a47ad20 Iustin Pop
    else:
1071 5a47ad20 Iustin Pop
      self.minor = minor
1072 5a47ad20 Iustin Pop
      self.dev_path = self._DevPath(minor)
1073 5a47ad20 Iustin Pop
1074 ae26a287 Iustin Pop
  @staticmethod
1075 ae26a287 Iustin Pop
  def _CheckMetaSize(meta_device):
1076 ae26a287 Iustin Pop
    """Check if the given meta device looks like a valid one.
1077 ae26a287 Iustin Pop

1078 ae26a287 Iustin Pop
    This currently only check the size, which must be around
1079 ae26a287 Iustin Pop
    128MiB.
1080 ae26a287 Iustin Pop

1081 ae26a287 Iustin Pop
    """
1082 ae26a287 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1083 ae26a287 Iustin Pop
    if result.failed:
1084 ae26a287 Iustin Pop
      logger.Error("Failed to get device size: %s" % result.fail_reason)
1085 ae26a287 Iustin Pop
      return False
1086 ae26a287 Iustin Pop
    try:
1087 ae26a287 Iustin Pop
      sectors = int(result.stdout)
1088 ae26a287 Iustin Pop
    except ValueError:
1089 ae26a287 Iustin Pop
      logger.Error("Invalid output from blockdev: '%s'" % result.stdout)
1090 ae26a287 Iustin Pop
      return False
1091 ae26a287 Iustin Pop
    bytes = sectors * 512
1092 ae26a287 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
1093 ae26a287 Iustin Pop
      logger.Error("Meta device too small (%.2fMib)" % (bytes / 1024 / 1024))
1094 ae26a287 Iustin Pop
      return False
1095 ae26a287 Iustin Pop
    if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
1096 ae26a287 Iustin Pop
      logger.Error("Meta device too big (%.2fMiB)" % (bytes / 1024 / 1024))
1097 ae26a287 Iustin Pop
      return False
1098 ae26a287 Iustin Pop
    return True
1099 ae26a287 Iustin Pop
1100 f3e513ad Iustin Pop
  def Rename(self, new_id):
1101 f3e513ad Iustin Pop
    """Rename a device.
1102 f3e513ad Iustin Pop

1103 f3e513ad Iustin Pop
    This is not supported for drbd devices.
1104 f3e513ad Iustin Pop

1105 f3e513ad Iustin Pop
    """
1106 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1107 f3e513ad Iustin Pop
1108 0f7f32d9 Iustin Pop
1109 0f7f32d9 Iustin Pop
class DRBDev(BaseDRBD):
1110 a8083063 Iustin Pop
  """DRBD block device.
1111 a8083063 Iustin Pop

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

1116 a8083063 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1117 a8083063 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1118 a8083063 Iustin Pop
  data device and the meta_device. The meta device is checked for
1119 a8083063 Iustin Pop
  valid size and is zeroed on create.
1120 a8083063 Iustin Pop

1121 a8083063 Iustin Pop
  """
1122 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
1123 a8083063 Iustin Pop
    super(DRBDev, self).__init__(unique_id, children)
1124 a8083063 Iustin Pop
    self.major = self._DRBD_MAJOR
1125 0f7f32d9 Iustin Pop
    [kmaj, kmin, kfix, api, proto] = self._GetVersion()
1126 0f7f32d9 Iustin Pop
    if kmaj != 0 and kmin != 7:
1127 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1128 0f7f32d9 Iustin Pop
                                    " requested ganeti usage: kernel is"
1129 0f7f32d9 Iustin Pop
                                    " %s.%s, ganeti wants 0.7" % (kmaj, kmin))
1130 0f7f32d9 Iustin Pop
1131 a8083063 Iustin Pop
    if len(children) != 2:
1132 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1133 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1134 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1135 a8083063 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
1136 a8083063 Iustin Pop
    self.Attach()
1137 a8083063 Iustin Pop
1138 a8083063 Iustin Pop
  @classmethod
1139 a8083063 Iustin Pop
  def _FindUnusedMinor(cls):
1140 a8083063 Iustin Pop
    """Find an unused DRBD device.
1141 a8083063 Iustin Pop

1142 a8083063 Iustin Pop
    """
1143 a8083063 Iustin Pop
    data = cls._GetProcData()
1144 a8083063 Iustin Pop
1145 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1146 a8083063 Iustin Pop
    for line in data:
1147 a8083063 Iustin Pop
      match = valid_line.match(line)
1148 a8083063 Iustin Pop
      if match:
1149 a8083063 Iustin Pop
        return int(match.group(1))
1150 a8083063 Iustin Pop
    logger.Error("Error: no free drbd minors!")
1151 0caf6485 Iustin Pop
    raise errors.BlockDeviceError("Can't find a free DRBD minor")
1152 a8083063 Iustin Pop
1153 a8083063 Iustin Pop
  @classmethod
1154 a8083063 Iustin Pop
  def _GetDevInfo(cls, minor):
1155 a8083063 Iustin Pop
    """Get details about a given DRBD minor.
1156 a8083063 Iustin Pop

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

1160 a8083063 Iustin Pop
    """
1161 a8083063 Iustin Pop
    data = {}
1162 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1163 a8083063 Iustin Pop
    if result.failed:
1164 a8083063 Iustin Pop
      logger.Error("Can't display the drbd config: %s" % result.fail_reason)
1165 a8083063 Iustin Pop
      return data
1166 a8083063 Iustin Pop
    out = result.stdout
1167 a8083063 Iustin Pop
    if out == "Not configured\n":
1168 a8083063 Iustin Pop
      return data
1169 a8083063 Iustin Pop
    for line in out.splitlines():
1170 a8083063 Iustin Pop
      if "local_dev" not in data:
1171 a8083063 Iustin Pop
        match = re.match("^Lower device: ([0-9]+):([0-9]+) .*$", line)
1172 a8083063 Iustin Pop
        if match:
1173 a8083063 Iustin Pop
          data["local_dev"] = (int(match.group(1)), int(match.group(2)))
1174 a8083063 Iustin Pop
          continue
1175 a8083063 Iustin Pop
      if "meta_dev" not in data:
1176 a8083063 Iustin Pop
        match = re.match("^Meta device: (([0-9]+):([0-9]+)|internal).*$", line)
1177 a8083063 Iustin Pop
        if match:
1178 a8083063 Iustin Pop
          if match.group(2) is not None and match.group(3) is not None:
1179 a8083063 Iustin Pop
            # matched on the major/minor
1180 a8083063 Iustin Pop
            data["meta_dev"] = (int(match.group(2)), int(match.group(3)))
1181 a8083063 Iustin Pop
          else:
1182 a8083063 Iustin Pop
            # matched on the "internal" string
1183 a8083063 Iustin Pop
            data["meta_dev"] = match.group(1)
1184 a8083063 Iustin Pop
            # in this case, no meta_index is in the output
1185 a8083063 Iustin Pop
            data["meta_index"] = -1
1186 a8083063 Iustin Pop
          continue
1187 a8083063 Iustin Pop
      if "meta_index" not in data:
1188 a8083063 Iustin Pop
        match = re.match("^Meta index: ([0-9]+).*$", line)
1189 a8083063 Iustin Pop
        if match:
1190 a8083063 Iustin Pop
          data["meta_index"] = int(match.group(1))
1191 a8083063 Iustin Pop
          continue
1192 a8083063 Iustin Pop
      if "local_addr" not in data:
1193 a8083063 Iustin Pop
        match = re.match("^Local address: ([0-9.]+):([0-9]+)$", line)
1194 a8083063 Iustin Pop
        if match:
1195 a8083063 Iustin Pop
          data["local_addr"] = (match.group(1), int(match.group(2)))
1196 a8083063 Iustin Pop
          continue
1197 a8083063 Iustin Pop
      if "remote_addr" not in data:
1198 a8083063 Iustin Pop
        match = re.match("^Remote address: ([0-9.]+):([0-9]+)$", line)
1199 a8083063 Iustin Pop
        if match:
1200 a8083063 Iustin Pop
          data["remote_addr"] = (match.group(1), int(match.group(2)))
1201 a8083063 Iustin Pop
          continue
1202 a8083063 Iustin Pop
    return data
1203 a8083063 Iustin Pop
1204 a8083063 Iustin Pop
  def _MatchesLocal(self, info):
1205 a8083063 Iustin Pop
    """Test if our local config matches with an existing device.
1206 a8083063 Iustin Pop

1207 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1208 a8083063 Iustin Pop
    method tests if our local backing device is the same as the one in
1209 a8083063 Iustin Pop
    the info parameter, in effect testing if we look like the given
1210 a8083063 Iustin Pop
    device.
1211 a8083063 Iustin Pop

1212 a8083063 Iustin Pop
    """
1213 a8083063 Iustin Pop
    if not ("local_dev" in info and "meta_dev" in info and
1214 a8083063 Iustin Pop
            "meta_index" in info):
1215 a8083063 Iustin Pop
      return False
1216 a8083063 Iustin Pop
1217 a8083063 Iustin Pop
    backend = self._children[0]
1218 a8083063 Iustin Pop
    if backend is not None:
1219 a8083063 Iustin Pop
      retval = (info["local_dev"] == (backend.major, backend.minor))
1220 a8083063 Iustin Pop
    else:
1221 a8083063 Iustin Pop
      retval = (info["local_dev"] == (0, 0))
1222 a8083063 Iustin Pop
    meta = self._children[1]
1223 a8083063 Iustin Pop
    if meta is not None:
1224 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == (meta.major, meta.minor))
1225 a8083063 Iustin Pop
      retval = retval and (info["meta_index"] == 0)
1226 a8083063 Iustin Pop
    else:
1227 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == "internal" and
1228 a8083063 Iustin Pop
                           info["meta_index"] == -1)
1229 a8083063 Iustin Pop
    return retval
1230 a8083063 Iustin Pop
1231 a8083063 Iustin Pop
  def _MatchesNet(self, info):
1232 a8083063 Iustin Pop
    """Test if our network config matches with an existing device.
1233 a8083063 Iustin Pop

1234 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1235 a8083063 Iustin Pop
    method tests if our network configuration is the same as the one
1236 a8083063 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1237 a8083063 Iustin Pop
    device.
1238 a8083063 Iustin Pop

1239 a8083063 Iustin Pop
    """
1240 a8083063 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1241 a8083063 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1242 a8083063 Iustin Pop
      return True
1243 a8083063 Iustin Pop
1244 a8083063 Iustin Pop
    if self._lhost is None:
1245 a8083063 Iustin Pop
      return False
1246 a8083063 Iustin Pop
1247 a8083063 Iustin Pop
    if not ("local_addr" in info and
1248 a8083063 Iustin Pop
            "remote_addr" in info):
1249 a8083063 Iustin Pop
      return False
1250 a8083063 Iustin Pop
1251 a8083063 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1252 a8083063 Iustin Pop
    retval = (retval and
1253 a8083063 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1254 a8083063 Iustin Pop
    return retval
1255 a8083063 Iustin Pop
1256 a8083063 Iustin Pop
  @classmethod
1257 a8083063 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1258 a8083063 Iustin Pop
    """Configure the local part of a DRBD device.
1259 a8083063 Iustin Pop

1260 a8083063 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1261 a8083063 Iustin Pop
    device. And it must be done only once.
1262 a8083063 Iustin Pop

1263 a8083063 Iustin Pop
    """
1264 ae26a287 Iustin Pop
    if not cls._CheckMetaSize(meta):
1265 a8083063 Iustin Pop
      return False
1266 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1267 a8083063 Iustin Pop
                           backend, meta, "0", "-e", "detach"])
1268 a8083063 Iustin Pop
    if result.failed:
1269 a8083063 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1270 a8083063 Iustin Pop
    return not result.failed
1271 a8083063 Iustin Pop
1272 a8083063 Iustin Pop
  @classmethod
1273 a8083063 Iustin Pop
  def _ShutdownLocal(cls, minor):
1274 a8083063 Iustin Pop
    """Detach from the local device.
1275 a8083063 Iustin Pop

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

1279 a8083063 Iustin Pop
    """
1280 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1281 a8083063 Iustin Pop
    if result.failed:
1282 a8083063 Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
1283 a8083063 Iustin Pop
    return not result.failed
1284 a8083063 Iustin Pop
1285 a8083063 Iustin Pop
  @staticmethod
1286 a8083063 Iustin Pop
  def _ShutdownAll(minor):
1287 a8083063 Iustin Pop
    """Deactivate the device.
1288 a8083063 Iustin Pop

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

1291 a8083063 Iustin Pop
    """
1292 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", DRBDev._DevPath(minor), "down"])
1293 a8083063 Iustin Pop
    if result.failed:
1294 a8083063 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
1295 a8083063 Iustin Pop
    return not result.failed
1296 a8083063 Iustin Pop
1297 a8083063 Iustin Pop
  @classmethod
1298 a8083063 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol):
1299 a8083063 Iustin Pop
    """Configure the network part of the device.
1300 a8083063 Iustin Pop

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

1308 a8083063 Iustin Pop
    """
1309 a8083063 Iustin Pop
    lhost, lport, rhost, rport = net_info
1310 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "net",
1311 a8083063 Iustin Pop
                           "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport),
1312 a8083063 Iustin Pop
                           protocol])
1313 a8083063 Iustin Pop
    if result.failed:
1314 a8083063 Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s" %
1315 a8083063 Iustin Pop
                   result.fail_reason)
1316 a8083063 Iustin Pop
      return False
1317 a8083063 Iustin Pop
1318 a8083063 Iustin Pop
    timeout = time.time() + 10
1319 a8083063 Iustin Pop
    ok = False
1320 a8083063 Iustin Pop
    while time.time() < timeout:
1321 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
1322 a8083063 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1323 a8083063 Iustin Pop
        time.sleep(1)
1324 a8083063 Iustin Pop
        continue
1325 a8083063 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1326 a8083063 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1327 a8083063 Iustin Pop
        time.sleep(1)
1328 a8083063 Iustin Pop
        continue
1329 a8083063 Iustin Pop
      ok = True
1330 a8083063 Iustin Pop
      break
1331 a8083063 Iustin Pop
    if not ok:
1332 a8083063 Iustin Pop
      logger.Error("Timeout while configuring network")
1333 a8083063 Iustin Pop
      return False
1334 a8083063 Iustin Pop
    return True
1335 a8083063 Iustin Pop
1336 a8083063 Iustin Pop
  @classmethod
1337 a8083063 Iustin Pop
  def _ShutdownNet(cls, minor):
1338 a8083063 Iustin Pop
    """Disconnect from the remote peer.
1339 a8083063 Iustin Pop

1340 a8083063 Iustin Pop
    This fails if we don't have a local device.
1341 a8083063 Iustin Pop

1342 a8083063 Iustin Pop
    """
1343 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1344 a8459f1c Iustin Pop
    if result.failed:
1345 a8459f1c Iustin Pop
      logger.Error("Can't shutdown network: %s" % result.output)
1346 a8083063 Iustin Pop
    return not result.failed
1347 a8083063 Iustin Pop
1348 a8083063 Iustin Pop
  def Assemble(self):
1349 a8083063 Iustin Pop
    """Assemble the drbd.
1350 a8083063 Iustin Pop

1351 a8083063 Iustin Pop
    Method:
1352 a8083063 Iustin Pop
      - if we have a local backing device, we bind to it by:
1353 a8083063 Iustin Pop
        - checking the list of used drbd devices
1354 a8083063 Iustin Pop
        - check if the local minor use of any of them is our own device
1355 a8083063 Iustin Pop
        - if yes, abort?
1356 a8083063 Iustin Pop
        - if not, bind
1357 a8083063 Iustin Pop
      - if we have a local/remote net info:
1358 a8083063 Iustin Pop
        - redo the local backing device step for the remote device
1359 a8083063 Iustin Pop
        - check if any drbd device is using the local port,
1360 a8083063 Iustin Pop
          if yes abort
1361 a8083063 Iustin Pop
        - check if any remote drbd device is using the remote
1362 a8083063 Iustin Pop
          port, if yes abort (for now)
1363 a8083063 Iustin Pop
        - bind our net port
1364 a8083063 Iustin Pop
        - bind the remote net port
1365 a8083063 Iustin Pop

1366 a8083063 Iustin Pop
    """
1367 a8083063 Iustin Pop
    self.Attach()
1368 a8083063 Iustin Pop
    if self.minor is not None:
1369 a8083063 Iustin Pop
      logger.Info("Already assembled")
1370 a8083063 Iustin Pop
      return True
1371 a8083063 Iustin Pop
1372 a8083063 Iustin Pop
    result = super(DRBDev, self).Assemble()
1373 a8083063 Iustin Pop
    if not result:
1374 a8083063 Iustin Pop
      return result
1375 a8083063 Iustin Pop
1376 a8083063 Iustin Pop
    minor = self._FindUnusedMinor()
1377 a8083063 Iustin Pop
    need_localdev_teardown = False
1378 a8083063 Iustin Pop
    if self._children[0]:
1379 a8083063 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1380 a8083063 Iustin Pop
                                   self._children[1].dev_path)
1381 a8083063 Iustin Pop
      if not result:
1382 a8083063 Iustin Pop
        return False
1383 a8083063 Iustin Pop
      need_localdev_teardown = True
1384 a8083063 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1385 a8083063 Iustin Pop
      result = self._AssembleNet(minor,
1386 a8083063 Iustin Pop
                                 (self._lhost, self._lport,
1387 a8083063 Iustin Pop
                                  self._rhost, self._rport),
1388 a8083063 Iustin Pop
                                 "C")
1389 a8083063 Iustin Pop
      if not result:
1390 a8083063 Iustin Pop
        if need_localdev_teardown:
1391 a8083063 Iustin Pop
          # we will ignore failures from this
1392 a8083063 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
1393 a8083063 Iustin Pop
          self._ShutdownAll(minor)
1394 a8083063 Iustin Pop
        return False
1395 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1396 a8083063 Iustin Pop
    return True
1397 a8083063 Iustin Pop
1398 a8083063 Iustin Pop
  def Shutdown(self):
1399 a8083063 Iustin Pop
    """Shutdown the DRBD device.
1400 a8083063 Iustin Pop

1401 a8083063 Iustin Pop
    """
1402 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1403 a8083063 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
1404 a8083063 Iustin Pop
      return True
1405 a8083063 Iustin Pop
    if not self._ShutdownAll(self.minor):
1406 a8083063 Iustin Pop
      return False
1407 a8083063 Iustin Pop
    self.minor = None
1408 a8083063 Iustin Pop
    self.dev_path = None
1409 a8083063 Iustin Pop
    return True
1410 a8083063 Iustin Pop
1411 a8083063 Iustin Pop
  def Attach(self):
1412 a8083063 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1413 a8083063 Iustin Pop

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

1418 a8083063 Iustin Pop
    """
1419 a8083063 Iustin Pop
    for minor in self._GetUsedDevs():
1420 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
1421 a8083063 Iustin Pop
      match_l = self._MatchesLocal(info)
1422 a8083063 Iustin Pop
      match_r = self._MatchesNet(info)
1423 a8083063 Iustin Pop
      if match_l and match_r:
1424 a8083063 Iustin Pop
        break
1425 a8083063 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1426 a8083063 Iustin Pop
        res_r = self._AssembleNet(minor,
1427 a8083063 Iustin Pop
                                  (self._lhost, self._lport,
1428 a8083063 Iustin Pop
                                   self._rhost, self._rport),
1429 a8083063 Iustin Pop
                                  "C")
1430 a8083063 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
1431 a8083063 Iustin Pop
          break
1432 a8083063 Iustin Pop
    else:
1433 a8083063 Iustin Pop
      minor = None
1434 a8083063 Iustin Pop
1435 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1436 a8083063 Iustin Pop
    return minor is not None
1437 a8083063 Iustin Pop
1438 a8083063 Iustin Pop
  def Open(self, force=False):
1439 a8083063 Iustin Pop
    """Make the local state primary.
1440 a8083063 Iustin Pop

1441 a8083063 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1442 a8083063 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1443 a8083063 Iustin Pop
    force flag should be only given after creation, when it actually
1444 a8083063 Iustin Pop
    has to be given.
1445 a8083063 Iustin Pop

1446 a8083063 Iustin Pop
    """
1447 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1448 a8083063 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1449 a8083063 Iustin Pop
      return False
1450 a8083063 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1451 a8083063 Iustin Pop
    if force:
1452 a8083063 Iustin Pop
      cmd.append("--do-what-I-say")
1453 a8083063 Iustin Pop
    result = utils.RunCmd(cmd)
1454 a8083063 Iustin Pop
    if result.failed:
1455 a8083063 Iustin Pop
      logger.Error("Can't make drbd device primary: %s" % result.output)
1456 a8083063 Iustin Pop
      return False
1457 a8083063 Iustin Pop
    return True
1458 a8083063 Iustin Pop
1459 a8083063 Iustin Pop
  def Close(self):
1460 a8083063 Iustin Pop
    """Make the local state secondary.
1461 a8083063 Iustin Pop

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

1464 a8083063 Iustin Pop
    """
1465 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1466 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1467 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1468 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1469 a8083063 Iustin Pop
    if result.failed:
1470 a8083063 Iustin Pop
      logger.Error("Can't switch drbd device to secondary: %s" % result.output)
1471 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't switch drbd device to secondary")
1472 a8083063 Iustin Pop
1473 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1474 a8083063 Iustin Pop
    """Set the speed of the DRBD syncer.
1475 a8083063 Iustin Pop

1476 a8083063 Iustin Pop
    """
1477 a8083063 Iustin Pop
    children_result = super(DRBDev, self).SetSyncSpeed(kbytes)
1478 a8083063 Iustin Pop
    if self.minor is None:
1479 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1480 a8083063 Iustin Pop
      return False
1481 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1482 a8083063 Iustin Pop
                           kbytes])
1483 a8083063 Iustin Pop
    if result.failed:
1484 a8083063 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1485 a8083063 Iustin Pop
    return not result.failed and children_result
1486 a8083063 Iustin Pop
1487 a8083063 Iustin Pop
  def GetSyncStatus(self):
1488 a8083063 Iustin Pop
    """Returns the sync status of the device.
1489 a8083063 Iustin Pop

1490 a8083063 Iustin Pop
    Returns:
1491 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
1492 a8083063 Iustin Pop

1493 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
1494 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
1495 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1496 0834c866 Iustin Pop

1497 0834c866 Iustin Pop
    The ldisk parameter will be returned as True, since the DRBD7
1498 0834c866 Iustin Pop
    devices have not been converted.
1499 a8083063 Iustin Pop

1500 a8083063 Iustin Pop
    """
1501 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1502 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1503 a8083063 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1504 a8083063 Iustin Pop
    if self.minor not in proc_info:
1505 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1506 a8083063 Iustin Pop
                                    self.minor)
1507 a8083063 Iustin Pop
    line = proc_info[self.minor]
1508 a8083063 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1509 a8083063 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1510 a8083063 Iustin Pop
    if match:
1511 a8083063 Iustin Pop
      sync_percent = float(match.group(1))
1512 a8083063 Iustin Pop
      hours = int(match.group(2))
1513 a8083063 Iustin Pop
      minutes = int(match.group(3))
1514 a8083063 Iustin Pop
      seconds = int(match.group(4))
1515 a8083063 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1516 a8083063 Iustin Pop
    else:
1517 a8083063 Iustin Pop
      sync_percent = None
1518 a8083063 Iustin Pop
      est_time = None
1519 a8083063 Iustin Pop
    match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1520 a8083063 Iustin Pop
    if not match:
1521 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1522 a8083063 Iustin Pop
                                    self.minor)
1523 a8083063 Iustin Pop
    client_state = match.group(1)
1524 a8083063 Iustin Pop
    is_degraded = client_state != "Connected"
1525 0834c866 Iustin Pop
    return sync_percent, est_time, is_degraded, False
1526 a8083063 Iustin Pop
1527 a8083063 Iustin Pop
  def GetStatus(self):
1528 a8083063 Iustin Pop
    """Compute the status of the DRBD device
1529 a8083063 Iustin Pop

1530 a8083063 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1531 a8083063 Iustin Pop

1532 a8083063 Iustin Pop
    """
1533 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1534 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
1535 a8083063 Iustin Pop
1536 a8083063 Iustin Pop
    data = self._GetProcData()
1537 a8083063 Iustin Pop
    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
1538 a8083063 Iustin Pop
                       self.minor)
1539 a8083063 Iustin Pop
    for line in data:
1540 a8083063 Iustin Pop
      mresult = match.match(line)
1541 a8083063 Iustin Pop
      if mresult:
1542 a8083063 Iustin Pop
        break
1543 a8083063 Iustin Pop
    else:
1544 a8083063 Iustin Pop
      logger.Error("Can't find myself!")
1545 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
1546 a8083063 Iustin Pop
1547 a8083063 Iustin Pop
    state = mresult.group(2)
1548 a8083063 Iustin Pop
    if state == "Primary":
1549 a8083063 Iustin Pop
      result = self.STATUS_ONLINE
1550 a8083063 Iustin Pop
    else:
1551 a8083063 Iustin Pop
      result = self.STATUS_STANDBY
1552 a8083063 Iustin Pop
1553 a8083063 Iustin Pop
    return result
1554 a8083063 Iustin Pop
1555 a8083063 Iustin Pop
  @staticmethod
1556 a8083063 Iustin Pop
  def _ZeroDevice(device):
1557 a8083063 Iustin Pop
    """Zero a device.
1558 a8083063 Iustin Pop

1559 a8083063 Iustin Pop
    This writes until we get ENOSPC.
1560 a8083063 Iustin Pop

1561 a8083063 Iustin Pop
    """
1562 a8083063 Iustin Pop
    f = open(device, "w")
1563 a8083063 Iustin Pop
    buf = "\0" * 1048576
1564 a8083063 Iustin Pop
    try:
1565 a8083063 Iustin Pop
      while True:
1566 a8083063 Iustin Pop
        f.write(buf)
1567 a8083063 Iustin Pop
    except IOError, err:
1568 a8083063 Iustin Pop
      if err.errno != errno.ENOSPC:
1569 a8083063 Iustin Pop
        raise
1570 a8083063 Iustin Pop
1571 a8083063 Iustin Pop
  @classmethod
1572 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
1573 a8083063 Iustin Pop
    """Create a new DRBD device.
1574 a8083063 Iustin Pop

1575 a8083063 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1576 a8083063 Iustin Pop
    function just zeroes the meta device.
1577 a8083063 Iustin Pop

1578 a8083063 Iustin Pop
    """
1579 a8083063 Iustin Pop
    if len(children) != 2:
1580 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1581 a8083063 Iustin Pop
    meta = children[1]
1582 a8083063 Iustin Pop
    meta.Assemble()
1583 a8083063 Iustin Pop
    if not meta.Attach():
1584 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1585 ae26a287 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
1586 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device")
1587 a8083063 Iustin Pop
    logger.Info("Started zeroing device %s" % meta.dev_path)
1588 a8083063 Iustin Pop
    cls._ZeroDevice(meta.dev_path)
1589 a8083063 Iustin Pop
    logger.Info("Done zeroing device %s" % meta.dev_path)
1590 a8083063 Iustin Pop
    return cls(unique_id, children)
1591 a8083063 Iustin Pop
1592 a8083063 Iustin Pop
  def Remove(self):
1593 a8083063 Iustin Pop
    """Stub remove for DRBD devices.
1594 a8083063 Iustin Pop

1595 a8083063 Iustin Pop
    """
1596 a8083063 Iustin Pop
    return self.Shutdown()
1597 a8083063 Iustin Pop
1598 f3e513ad Iustin Pop
1599 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1600 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1601 a2cfdea2 Iustin Pop

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

1606 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1607 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1608 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1609 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1610 a2cfdea2 Iustin Pop

1611 a2cfdea2 Iustin Pop
  """
1612 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1613 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1614 a2cfdea2 Iustin Pop
1615 a2cfdea2 Iustin Pop
  def __init__(self, unique_id, children):
1616 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1617 fc1dc9d7 Iustin Pop
      children = []
1618 a2cfdea2 Iustin Pop
    super(DRBD8, self).__init__(unique_id, children)
1619 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1620 a2cfdea2 Iustin Pop
    [kmaj, kmin, kfix, api, proto] = self._GetVersion()
1621 a2cfdea2 Iustin Pop
    if kmaj != 8:
1622 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1623 a2cfdea2 Iustin Pop
                                    " requested ganeti usage: kernel is"
1624 a2cfdea2 Iustin Pop
                                    " %s.%s, ganeti wants 8.x" % (kmaj, kmin))
1625 a2cfdea2 Iustin Pop
1626 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
1627 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1628 a2cfdea2 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1629 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1630 a2cfdea2 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
1631 a2cfdea2 Iustin Pop
    self.Attach()
1632 a2cfdea2 Iustin Pop
1633 a2cfdea2 Iustin Pop
  @classmethod
1634 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1635 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1636 a2cfdea2 Iustin Pop

1637 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1638 a2cfdea2 Iustin Pop

1639 a2cfdea2 Iustin Pop
    """
1640 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1641 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1642 a2cfdea2 Iustin Pop
    if result.failed:
1643 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't initialize meta device: %s" %
1644 a2cfdea2 Iustin Pop
                                    result.output)
1645 a2cfdea2 Iustin Pop
1646 a2cfdea2 Iustin Pop
  @classmethod
1647 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1648 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1649 a2cfdea2 Iustin Pop

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

1653 a2cfdea2 Iustin Pop
    """
1654 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1655 a2cfdea2 Iustin Pop
1656 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1657 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
1658 a2cfdea2 Iustin Pop
    highest = None
1659 a2cfdea2 Iustin Pop
    for line in data:
1660 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
1661 a2cfdea2 Iustin Pop
      if match:
1662 a2cfdea2 Iustin Pop
        return int(match.group(1))
1663 a2cfdea2 Iustin Pop
      match = used_line.match(line)
1664 a2cfdea2 Iustin Pop
      if match:
1665 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1666 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1667 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1668 a2cfdea2 Iustin Pop
      return 0
1669 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1670 a2cfdea2 Iustin Pop
      logger.Error("Error: no free drbd minors!")
1671 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1672 a2cfdea2 Iustin Pop
    return highest + 1
1673 a2cfdea2 Iustin Pop
1674 a2cfdea2 Iustin Pop
  @classmethod
1675 a2cfdea2 Iustin Pop
  def _IsValidMeta(cls, meta_device):
1676 a2cfdea2 Iustin Pop
    """Check if the given meta device looks like a valid one.
1677 a2cfdea2 Iustin Pop

1678 a2cfdea2 Iustin Pop
    """
1679 a2cfdea2 Iustin Pop
    minor = cls._FindUnusedMinor()
1680 a2cfdea2 Iustin Pop
    minor_path = cls._DevPath(minor)
1681 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", minor_path,
1682 a2cfdea2 Iustin Pop
                           "v08", meta_device, "0",
1683 a2cfdea2 Iustin Pop
                           "dstate"])
1684 a2cfdea2 Iustin Pop
    if result.failed:
1685 a2cfdea2 Iustin Pop
      logger.Error("Invalid meta device %s: %s" % (meta_device, result.output))
1686 a2cfdea2 Iustin Pop
      return False
1687 a2cfdea2 Iustin Pop
    return True
1688 a2cfdea2 Iustin Pop
1689 a2cfdea2 Iustin Pop
  @classmethod
1690 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1691 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1692 a2cfdea2 Iustin Pop

1693 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1694 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1695 a2cfdea2 Iustin Pop

1696 a2cfdea2 Iustin Pop
    """
1697 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1698 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1699 a2cfdea2 Iustin Pop
1700 a2cfdea2 Iustin Pop
    # pyparsing setup
1701 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1702 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1703 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1704 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1705 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1706 a2cfdea2 Iustin Pop
1707 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1708 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1709 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1710 a2cfdea2 Iustin Pop
1711 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1712 a2cfdea2 Iustin Pop
1713 a2cfdea2 Iustin Pop
    # value types
1714 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1715 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1716 a2cfdea2 Iustin Pop
    addr_port = (pyp.Word(pyp.nums + '.') + pyp.Literal(':').suppress() +
1717 a2cfdea2 Iustin Pop
                 number)
1718 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1719 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
1720 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
1721 a2cfdea2 Iustin Pop
1722 a2cfdea2 Iustin Pop
    # a statement
1723 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1724 a2cfdea2 Iustin Pop
            (addr_port ^ value ^ quoted ^ meta_value) +
1725 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1726 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1727 a2cfdea2 Iustin Pop
1728 a2cfdea2 Iustin Pop
    # an entire section
1729 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1730 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1731 a2cfdea2 Iustin Pop
1732 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1733 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1734 a2cfdea2 Iustin Pop
1735 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1736 a2cfdea2 Iustin Pop
1737 a2cfdea2 Iustin Pop
    return bnf
1738 a2cfdea2 Iustin Pop
1739 a2cfdea2 Iustin Pop
  @classmethod
1740 a2cfdea2 Iustin Pop
  def _GetDevInfo(cls, minor):
1741 a2cfdea2 Iustin Pop
    """Get details about a given DRBD minor.
1742 a2cfdea2 Iustin Pop

1743 a2cfdea2 Iustin Pop
    This return, if available, the local backing device (as a path)
1744 a2cfdea2 Iustin Pop
    and the local and remote (ip, port) information.
1745 a2cfdea2 Iustin Pop

1746 a2cfdea2 Iustin Pop
    """
1747 a2cfdea2 Iustin Pop
    data = {}
1748 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1749 a2cfdea2 Iustin Pop
    if result.failed:
1750 a2cfdea2 Iustin Pop
      logger.Error("Can't display the drbd config: %s" % result.fail_reason)
1751 a2cfdea2 Iustin Pop
      return data
1752 a2cfdea2 Iustin Pop
    out = result.stdout
1753 a2cfdea2 Iustin Pop
    if not out:
1754 a2cfdea2 Iustin Pop
      return data
1755 a2cfdea2 Iustin Pop
1756 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1757 a2cfdea2 Iustin Pop
    # run pyparse
1758 a2cfdea2 Iustin Pop
1759 a2cfdea2 Iustin Pop
    try:
1760 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1761 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1762 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't parse drbdsetup show output: %s" %
1763 a2cfdea2 Iustin Pop
                                    str(err))
1764 a2cfdea2 Iustin Pop
1765 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1766 a2cfdea2 Iustin Pop
    for section in results:
1767 a2cfdea2 Iustin Pop
      sname = section[0]
1768 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1769 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1770 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1771 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1772 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1773 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1774 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1775 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1776 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1777 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1778 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1779 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1780 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1781 a2cfdea2 Iustin Pop
    return data
1782 a2cfdea2 Iustin Pop
1783 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1784 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1785 a2cfdea2 Iustin Pop

1786 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1787 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1788 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1789 a2cfdea2 Iustin Pop
    device.
1790 a2cfdea2 Iustin Pop

1791 a2cfdea2 Iustin Pop
    """
1792 b00b95dd Iustin Pop
    if self._children:
1793 b00b95dd Iustin Pop
      backend, meta = self._children
1794 b00b95dd Iustin Pop
    else:
1795 b00b95dd Iustin Pop
      backend = meta = None
1796 b00b95dd Iustin Pop
1797 a2cfdea2 Iustin Pop
    if backend is not None:
1798 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1799 a2cfdea2 Iustin Pop
    else:
1800 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1801 b00b95dd Iustin Pop
1802 a2cfdea2 Iustin Pop
    if meta is not None:
1803 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1804 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1805 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1806 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1807 a2cfdea2 Iustin Pop
    else:
1808 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1809 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1810 a2cfdea2 Iustin Pop
    return retval
1811 a2cfdea2 Iustin Pop
1812 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1813 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1814 a2cfdea2 Iustin Pop

1815 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1816 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1817 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1818 a2cfdea2 Iustin Pop
    device.
1819 a2cfdea2 Iustin Pop

1820 a2cfdea2 Iustin Pop
    """
1821 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1822 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1823 a2cfdea2 Iustin Pop
      return True
1824 a2cfdea2 Iustin Pop
1825 a2cfdea2 Iustin Pop
    if self._lhost is None:
1826 a2cfdea2 Iustin Pop
      return False
1827 a2cfdea2 Iustin Pop
1828 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1829 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1830 a2cfdea2 Iustin Pop
      return False
1831 a2cfdea2 Iustin Pop
1832 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1833 a2cfdea2 Iustin Pop
    retval = (retval and
1834 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1835 a2cfdea2 Iustin Pop
    return retval
1836 a2cfdea2 Iustin Pop
1837 a2cfdea2 Iustin Pop
  @classmethod
1838 a2cfdea2 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1839 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1840 a2cfdea2 Iustin Pop

1841 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1842 a2cfdea2 Iustin Pop
    device. And it must be done only once.
1843 a2cfdea2 Iustin Pop

1844 a2cfdea2 Iustin Pop
    """
1845 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta):
1846 a2cfdea2 Iustin Pop
      return False
1847 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1848 a2cfdea2 Iustin Pop
                           backend, meta, "0", "-e", "detach",
1849 a2cfdea2 Iustin Pop
                           "--create-device"])
1850 a2cfdea2 Iustin Pop
    if result.failed:
1851 a2cfdea2 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1852 a2cfdea2 Iustin Pop
    return not result.failed
1853 a2cfdea2 Iustin Pop
1854 a2cfdea2 Iustin Pop
  @classmethod
1855 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1856 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1857 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1858 a2cfdea2 Iustin Pop

1859 a2cfdea2 Iustin Pop
    """
1860 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1861 52857176 Iustin Pop
    if None in net_info:
1862 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1863 52857176 Iustin Pop
      # sure its shutdown
1864 52857176 Iustin Pop
      return cls._ShutdownNet(minor)
1865 52857176 Iustin Pop
1866 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1867 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1868 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1869 f38478b2 Iustin Pop
            "-B", "consensus",
1870 f38478b2 Iustin Pop
            ]
1871 a2cfdea2 Iustin Pop
    if dual_pri:
1872 a2cfdea2 Iustin Pop
      args.append("-m")
1873 a2cfdea2 Iustin Pop
    if hmac and secret:
1874 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1875 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1876 a2cfdea2 Iustin Pop
    if result.failed:
1877 a2cfdea2 Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s" %
1878 a2cfdea2 Iustin Pop
                   result.fail_reason)
1879 a2cfdea2 Iustin Pop
      return False
1880 a2cfdea2 Iustin Pop
1881 a2cfdea2 Iustin Pop
    timeout = time.time() + 10
1882 a2cfdea2 Iustin Pop
    ok = False
1883 a2cfdea2 Iustin Pop
    while time.time() < timeout:
1884 a2cfdea2 Iustin Pop
      info = cls._GetDevInfo(minor)
1885 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1886 a2cfdea2 Iustin Pop
        time.sleep(1)
1887 a2cfdea2 Iustin Pop
        continue
1888 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1889 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1890 a2cfdea2 Iustin Pop
        time.sleep(1)
1891 a2cfdea2 Iustin Pop
        continue
1892 a2cfdea2 Iustin Pop
      ok = True
1893 a2cfdea2 Iustin Pop
      break
1894 a2cfdea2 Iustin Pop
    if not ok:
1895 a2cfdea2 Iustin Pop
      logger.Error("Timeout while configuring network")
1896 a2cfdea2 Iustin Pop
      return False
1897 a2cfdea2 Iustin Pop
    return True
1898 a2cfdea2 Iustin Pop
1899 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1900 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1901 b00b95dd Iustin Pop

1902 b00b95dd Iustin Pop
    """
1903 b00b95dd Iustin Pop
    if self.minor is None:
1904 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren")
1905 b00b95dd Iustin Pop
    if len(devices) != 2:
1906 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Need two devices for AddChildren")
1907 03ece5f3 Iustin Pop
    info = self._GetDevInfo(self.minor)
1908 03ece5f3 Iustin Pop
    if "local_dev" in info:
1909 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("DRBD8 already attached to a local disk")
1910 b00b95dd Iustin Pop
    backend, meta = devices
1911 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1912 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Children not ready during AddChildren")
1913 b00b95dd Iustin Pop
    backend.Open()
1914 b00b95dd Iustin Pop
    meta.Open()
1915 b00b95dd Iustin Pop
    if not self._CheckMetaSize(meta.dev_path):
1916 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1917 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1918 b00b95dd Iustin Pop
    if not self._IsValidMeta(meta.dev_path):
1919 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1920 b00b95dd Iustin Pop
1921 b00b95dd Iustin Pop
    if not self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path):
1922 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to local storage")
1923 b00b95dd Iustin Pop
    self._children = devices
1924 b00b95dd Iustin Pop
1925 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1926 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1927 b00b95dd Iustin Pop

1928 b00b95dd Iustin Pop
    """
1929 b00b95dd Iustin Pop
    if self.minor is None:
1930 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to drbd8 during"
1931 b00b95dd Iustin Pop
                                    " RemoveChildren")
1932 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1933 03ece5f3 Iustin Pop
    info = self._GetDevInfo(self.minor)
1934 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1935 03ece5f3 Iustin Pop
      return
1936 b00b95dd Iustin Pop
    if len(self._children) != 2:
1937 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We don't have two children: %s" %
1938 b00b95dd Iustin Pop
                                    self._children)
1939 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1940 e739bd57 Iustin Pop
      logger.Error("Requested detach while detached")
1941 e739bd57 Iustin Pop
      return
1942 b00b95dd Iustin Pop
    if len(devices) != 2:
1943 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We need two children in RemoveChildren")
1944 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1945 e739bd57 Iustin Pop
      if dev != child.dev_path:
1946 e739bd57 Iustin Pop
        raise errors.BlockDeviceError("Mismatch in local storage"
1947 e739bd57 Iustin Pop
                                      " (%s != %s) in RemoveChildren" %
1948 e739bd57 Iustin Pop
                                      (dev, child.dev_path))
1949 b00b95dd Iustin Pop
1950 b00b95dd Iustin Pop
    if not self._ShutdownLocal(self.minor):
1951 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't detach from local storage")
1952 b00b95dd Iustin Pop
    self._children = []
1953 b00b95dd Iustin Pop
1954 a2cfdea2 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1955 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1956 a2cfdea2 Iustin Pop

1957 a2cfdea2 Iustin Pop
    """
1958 a2cfdea2 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1959 a2cfdea2 Iustin Pop
    if self.minor is None:
1960 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
1961 a2cfdea2 Iustin Pop
      return False
1962 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1963 a2cfdea2 Iustin Pop
                           kbytes])
1964 a2cfdea2 Iustin Pop
    if result.failed:
1965 a2cfdea2 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1966 a2cfdea2 Iustin Pop
    return not result.failed and children_result
1967 a2cfdea2 Iustin Pop
1968 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1969 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1970 a2cfdea2 Iustin Pop

1971 a2cfdea2 Iustin Pop
    Returns:
1972 583e3f6f Iustin Pop
     (sync_percent, estimated_time, is_degraded)
1973 a2cfdea2 Iustin Pop

1974 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1975 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1976 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1977 0834c866 Iustin Pop

1978 0834c866 Iustin Pop

1979 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1980 0834c866 Iustin Pop
    network not connected or local disk missing.
1981 0834c866 Iustin Pop

1982 0834c866 Iustin Pop
    We compute the ldisk parameter based on wheter we have a local
1983 0834c866 Iustin Pop
    disk or not.
1984 a2cfdea2 Iustin Pop

1985 a2cfdea2 Iustin Pop
    """
1986 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1987 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1988 a2cfdea2 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1989 a2cfdea2 Iustin Pop
    if self.minor not in proc_info:
1990 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1991 a2cfdea2 Iustin Pop
                                    self.minor)
1992 a2cfdea2 Iustin Pop
    line = proc_info[self.minor]
1993 a2cfdea2 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1994 a2cfdea2 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1995 a2cfdea2 Iustin Pop
    if match:
1996 a2cfdea2 Iustin Pop
      sync_percent = float(match.group(1))
1997 a2cfdea2 Iustin Pop
      hours = int(match.group(2))
1998 a2cfdea2 Iustin Pop
      minutes = int(match.group(3))
1999 a2cfdea2 Iustin Pop
      seconds = int(match.group(4))
2000 a2cfdea2 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
2001 a2cfdea2 Iustin Pop
    else:
2002 a2cfdea2 Iustin Pop
      sync_percent = None
2003 a2cfdea2 Iustin Pop
      est_time = None
2004 583e3f6f Iustin Pop
    match = re.match("^ *\d+: cs:(\w+).*ds:(\w+)/(\w+).*$", line)
2005 a2cfdea2 Iustin Pop
    if not match:
2006 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
2007 a2cfdea2 Iustin Pop
                                    self.minor)
2008 a2cfdea2 Iustin Pop
    client_state = match.group(1)
2009 583e3f6f Iustin Pop
    local_disk_state = match.group(2)
2010 0834c866 Iustin Pop
    ldisk = local_disk_state != "UpToDate"
2011 0834c866 Iustin Pop
    is_degraded = client_state != "Connected"
2012 0834c866 Iustin Pop
    return sync_percent, est_time, is_degraded or ldisk, ldisk
2013 a2cfdea2 Iustin Pop
2014 a2cfdea2 Iustin Pop
  def GetStatus(self):
2015 a2cfdea2 Iustin Pop
    """Compute the status of the DRBD device
2016 a2cfdea2 Iustin Pop

2017 a2cfdea2 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
2018 a2cfdea2 Iustin Pop

2019 a2cfdea2 Iustin Pop
    """
2020 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2021 a2cfdea2 Iustin Pop
      return self.STATUS_UNKNOWN
2022 a2cfdea2 Iustin Pop
2023 a2cfdea2 Iustin Pop
    data = self._GetProcData()
2024 a2cfdea2 Iustin Pop
    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
2025 a2cfdea2 Iustin Pop
                       self.minor)
2026 a2cfdea2 Iustin Pop
    for line in data:
2027 a2cfdea2 Iustin Pop
      mresult = match.match(line)
2028 a2cfdea2 Iustin Pop
      if mresult:
2029 a2cfdea2 Iustin Pop
        break
2030 a2cfdea2 Iustin Pop
    else:
2031 a2cfdea2 Iustin Pop
      logger.Error("Can't find myself!")
2032 a2cfdea2 Iustin Pop
      return self.STATUS_UNKNOWN
2033 a2cfdea2 Iustin Pop
2034 a2cfdea2 Iustin Pop
    state = mresult.group(2)
2035 a2cfdea2 Iustin Pop
    if state == "Primary":
2036 a2cfdea2 Iustin Pop
      result = self.STATUS_ONLINE
2037 a2cfdea2 Iustin Pop
    else:
2038 a2cfdea2 Iustin Pop
      result = self.STATUS_STANDBY
2039 a2cfdea2 Iustin Pop
2040 a2cfdea2 Iustin Pop
    return result
2041 a2cfdea2 Iustin Pop
2042 a2cfdea2 Iustin Pop
  def Open(self, force=False):
2043 a2cfdea2 Iustin Pop
    """Make the local state primary.
2044 a2cfdea2 Iustin Pop

2045 a2cfdea2 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
2046 a2cfdea2 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
2047 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
2048 a2cfdea2 Iustin Pop
    has to be given.
2049 a2cfdea2 Iustin Pop

2050 a2cfdea2 Iustin Pop
    """
2051 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2052 a2cfdea2 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
2053 a2cfdea2 Iustin Pop
      return False
2054 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
2055 a2cfdea2 Iustin Pop
    if force:
2056 a2cfdea2 Iustin Pop
      cmd.append("-o")
2057 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
2058 a2cfdea2 Iustin Pop
    if result.failed:
2059 a2cfdea2 Iustin Pop
      logger.Error("Can't make drbd device primary: %s" % result.output)
2060 a2cfdea2 Iustin Pop
      return False
2061 a2cfdea2 Iustin Pop
    return True
2062 a2cfdea2 Iustin Pop
2063 a2cfdea2 Iustin Pop
  def Close(self):
2064 a2cfdea2 Iustin Pop
    """Make the local state secondary.
2065 a2cfdea2 Iustin Pop

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

2068 a2cfdea2 Iustin Pop
    """
2069 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2070 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
2071 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
2072 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
2073 a2cfdea2 Iustin Pop
    if result.failed:
2074 a2cfdea2 Iustin Pop
      logger.Error("Can't switch drbd device to secondary: %s" % result.output)
2075 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't switch drbd device to secondary")
2076 a2cfdea2 Iustin Pop
2077 a2cfdea2 Iustin Pop
  def Attach(self):
2078 a2cfdea2 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
2079 a2cfdea2 Iustin Pop

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

2084 a2cfdea2 Iustin Pop
    """
2085 a2cfdea2 Iustin Pop
    for minor in self._GetUsedDevs():
2086 a2cfdea2 Iustin Pop
      info = self._GetDevInfo(minor)
2087 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
2088 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
2089 a2cfdea2 Iustin Pop
      if match_l and match_r:
2090 a2cfdea2 Iustin Pop
        break
2091 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
2092 a2cfdea2 Iustin Pop
        res_r = self._AssembleNet(minor,
2093 a2cfdea2 Iustin Pop
                                  (self._lhost, self._lport,
2094 a2cfdea2 Iustin Pop
                                   self._rhost, self._rport),
2095 a2cfdea2 Iustin Pop
                                  "C")
2096 a2cfdea2 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
2097 a2cfdea2 Iustin Pop
          break
2098 fc1dc9d7 Iustin Pop
      # the weakest case: we find something that is only net attached
2099 fc1dc9d7 Iustin Pop
      # even though we were passed some children at init time
2100 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
2101 fc1dc9d7 Iustin Pop
        break
2102 9cdbe77f Iustin Pop
      if match_l and not match_r and "local_addr" in info:
2103 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
2104 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
2105 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
2106 9cdbe77f Iustin Pop
        # reconnect to our correct one
2107 9cdbe77f Iustin Pop
        if not self._ShutdownNet(minor):
2108 9cdbe77f Iustin Pop
          raise errors.BlockDeviceError("Device has correct local storage,"
2109 9cdbe77f Iustin Pop
                                        " wrong remote peer and is unable to"
2110 9cdbe77f Iustin Pop
                                        " disconnect in order to attach to"
2111 9cdbe77f Iustin Pop
                                        " the correct peer")
2112 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
2113 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
2114 9cdbe77f Iustin Pop
        # None)
2115 9cdbe77f Iustin Pop
        if (self._AssembleNet(minor, (self._lhost, self._lport,
2116 9cdbe77f Iustin Pop
                                      self._rhost, self._rport), "C") and
2117 9cdbe77f Iustin Pop
            self._MatchesNet(self._GetDevInfo(minor))):
2118 9cdbe77f Iustin Pop
          break
2119 9cdbe77f Iustin Pop
2120 a2cfdea2 Iustin Pop
    else:
2121 a2cfdea2 Iustin Pop
      minor = None
2122 a2cfdea2 Iustin Pop
2123 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2124 a2cfdea2 Iustin Pop
    return minor is not None
2125 a2cfdea2 Iustin Pop
2126 a2cfdea2 Iustin Pop
  def Assemble(self):
2127 a2cfdea2 Iustin Pop
    """Assemble the drbd.
2128 a2cfdea2 Iustin Pop

2129 a2cfdea2 Iustin Pop
    Method:
2130 a2cfdea2 Iustin Pop
      - if we have a local backing device, we bind to it by:
2131 a2cfdea2 Iustin Pop
        - checking the list of used drbd devices
2132 a2cfdea2 Iustin Pop
        - check if the local minor use of any of them is our own device
2133 a2cfdea2 Iustin Pop
        - if yes, abort?
2134 a2cfdea2 Iustin Pop
        - if not, bind
2135 a2cfdea2 Iustin Pop
      - if we have a local/remote net info:
2136 a2cfdea2 Iustin Pop
        - redo the local backing device step for the remote device
2137 a2cfdea2 Iustin Pop
        - check if any drbd device is using the local port,
2138 a2cfdea2 Iustin Pop
          if yes abort
2139 a2cfdea2 Iustin Pop
        - check if any remote drbd device is using the remote
2140 a2cfdea2 Iustin Pop
          port, if yes abort (for now)
2141 a2cfdea2 Iustin Pop
        - bind our net port
2142 a2cfdea2 Iustin Pop
        - bind the remote net port
2143 a2cfdea2 Iustin Pop

2144 a2cfdea2 Iustin Pop
    """
2145 a2cfdea2 Iustin Pop
    self.Attach()
2146 a2cfdea2 Iustin Pop
    if self.minor is not None:
2147 a2cfdea2 Iustin Pop
      logger.Info("Already assembled")
2148 a2cfdea2 Iustin Pop
      return True
2149 a2cfdea2 Iustin Pop
2150 a2cfdea2 Iustin Pop
    result = super(DRBD8, self).Assemble()
2151 a2cfdea2 Iustin Pop
    if not result:
2152 a2cfdea2 Iustin Pop
      return result
2153 a2cfdea2 Iustin Pop
2154 a2cfdea2 Iustin Pop
    minor = self._FindUnusedMinor()
2155 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
2156 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
2157 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
2158 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
2159 a2cfdea2 Iustin Pop
      if not result:
2160 a2cfdea2 Iustin Pop
        return False
2161 a2cfdea2 Iustin Pop
      need_localdev_teardown = True
2162 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
2163 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
2164 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
2165 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
2166 a2cfdea2 Iustin Pop
                                 "C")
2167 a2cfdea2 Iustin Pop
      if not result:
2168 a2cfdea2 Iustin Pop
        if need_localdev_teardown:
2169 a2cfdea2 Iustin Pop
          # we will ignore failures from this
2170 a2cfdea2 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
2171 a2cfdea2 Iustin Pop
          self._ShutdownAll(minor)
2172 a2cfdea2 Iustin Pop
        return False
2173 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2174 a2cfdea2 Iustin Pop
    return True
2175 a2cfdea2 Iustin Pop
2176 a2cfdea2 Iustin Pop
  @classmethod
2177 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
2178 b00b95dd Iustin Pop
    """Detach from the local device.
2179 b00b95dd Iustin Pop

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

2183 b00b95dd Iustin Pop
    """
2184 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2185 b00b95dd Iustin Pop
    if result.failed:
2186 b00b95dd Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
2187 b00b95dd Iustin Pop
    return not result.failed
2188 b00b95dd Iustin Pop
2189 b00b95dd Iustin Pop
  @classmethod
2190 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2191 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2192 f3e513ad Iustin Pop

2193 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2194 f3e513ad Iustin Pop

2195 f3e513ad Iustin Pop
    """
2196 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2197 a8459f1c Iustin Pop
    if result.failed:
2198 a8459f1c Iustin Pop
      logger.Error("Can't shutdown network: %s" % result.output)
2199 f3e513ad Iustin Pop
    return not result.failed
2200 f3e513ad Iustin Pop
2201 f3e513ad Iustin Pop
  @classmethod
2202 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2203 a2cfdea2 Iustin Pop
    """Deactivate the device.
2204 a2cfdea2 Iustin Pop

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

2207 a2cfdea2 Iustin Pop
    """
2208 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2209 a2cfdea2 Iustin Pop
    if result.failed:
2210 a2cfdea2 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
2211 a2cfdea2 Iustin Pop
    return not result.failed
2212 a2cfdea2 Iustin Pop
2213 a2cfdea2 Iustin Pop
  def Shutdown(self):
2214 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2215 a2cfdea2 Iustin Pop

2216 a2cfdea2 Iustin Pop
    """
2217 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2218 a2cfdea2 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
2219 a2cfdea2 Iustin Pop
      return True
2220 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
2221 a2cfdea2 Iustin Pop
      return False
2222 a2cfdea2 Iustin Pop
    self.minor = None
2223 a2cfdea2 Iustin Pop
    self.dev_path = None
2224 a2cfdea2 Iustin Pop
    return True
2225 a2cfdea2 Iustin Pop
2226 a2cfdea2 Iustin Pop
  def Remove(self):
2227 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2228 a2cfdea2 Iustin Pop

2229 a2cfdea2 Iustin Pop
    """
2230 a2cfdea2 Iustin Pop
    return self.Shutdown()
2231 a2cfdea2 Iustin Pop
2232 a2cfdea2 Iustin Pop
  @classmethod
2233 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
2234 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2235 a2cfdea2 Iustin Pop

2236 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2237 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2238 a2cfdea2 Iustin Pop

2239 a2cfdea2 Iustin Pop
    """
2240 a2cfdea2 Iustin Pop
    if len(children) != 2:
2241 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2242 a2cfdea2 Iustin Pop
    meta = children[1]
2243 a2cfdea2 Iustin Pop
    meta.Assemble()
2244 a2cfdea2 Iustin Pop
    if not meta.Attach():
2245 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
2246 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
2247 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
2248 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
2249 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
2250 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
2251 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
2252 a2cfdea2 Iustin Pop
2253 a8083063 Iustin Pop
2254 a8083063 Iustin Pop
DEV_MAP = {
2255 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2256 fe96220b Iustin Pop
  constants.LD_MD_R1: MDRaid1,
2257 fe96220b Iustin Pop
  constants.LD_DRBD7: DRBDev,
2258 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2259 a8083063 Iustin Pop
  }
2260 a8083063 Iustin Pop
2261 a8083063 Iustin Pop
2262 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
2263 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2264 a8083063 Iustin Pop

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

2268 a8083063 Iustin Pop
  """
2269 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2270 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2271 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2272 a8083063 Iustin Pop
  if not device.Attach():
2273 a8083063 Iustin Pop
    return None
2274 a8083063 Iustin Pop
  return  device
2275 a8083063 Iustin Pop
2276 a8083063 Iustin Pop
2277 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
2278 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2279 a8083063 Iustin Pop

2280 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
2281 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
2282 a8083063 Iustin Pop

2283 a8083063 Iustin Pop
  """
2284 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2285 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2286 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2287 a8083063 Iustin Pop
  if not device.Attach():
2288 a8083063 Iustin Pop
    device.Assemble()
2289 a8083063 Iustin Pop
  if not device.Attach():
2290 a8083063 Iustin Pop
    raise errors.BlockDeviceError("Can't find a valid block device for"
2291 a8083063 Iustin Pop
                                  " %s/%s/%s" %
2292 a8083063 Iustin Pop
                                  (dev_type, unique_id, children))
2293 a8083063 Iustin Pop
  return device
2294 a8083063 Iustin Pop
2295 a8083063 Iustin Pop
2296 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2297 a8083063 Iustin Pop
  """Create a device.
2298 a8083063 Iustin Pop

2299 a8083063 Iustin Pop
  """
2300 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2301 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2302 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2303 a8083063 Iustin Pop
  return device