Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 0fbbf897

History | View | Annotate | Download (69.7 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 a8083063 Iustin Pop
     (sync_percent, estimated_time, is_degraded)
224 a8083063 Iustin Pop

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

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

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

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

263 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
264 a0c3fea1 Michael Hanselmann

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

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

283 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
284 a8083063 Iustin Pop

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

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

326 a8083063 Iustin Pop
    Args:
327 a8083063 Iustin Pop
      vg_name: the volume group name
328 a8083063 Iustin Pop

329 a8083063 Iustin Pop
    Returns:
330 a8083063 Iustin Pop
      list of (free_space, name) with free_space in mebibytes
331 098c0958 Michael Hanselmann

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

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

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

389 a8083063 Iustin Pop
    This method will try to see if an existing and active LV exists
390 a8083063 Iustin Pop
    which matches the our name. If so, its major/minor will be
391 a8083063 Iustin Pop
    recorded.
392 a8083063 Iustin Pop

393 a8083063 Iustin Pop
    """
394 a8083063 Iustin Pop
    result = utils.RunCmd(["lvdisplay", self.dev_path])
395 a8083063 Iustin Pop
    if result.failed:
396 a8083063 Iustin Pop
      logger.Error("Can't find LV %s: %s" %
397 a8083063 Iustin Pop
                   (self.dev_path, result.fail_reason))
398 a8083063 Iustin Pop
      return False
399 a8083063 Iustin Pop
    match = re.compile("^ *Block device *([0-9]+):([0-9]+).*$")
400 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
401 a8083063 Iustin Pop
      match_result = match.match(line)
402 a8083063 Iustin Pop
      if match_result:
403 a8083063 Iustin Pop
        self.major = int(match_result.group(1))
404 a8083063 Iustin Pop
        self.minor = int(match_result.group(2))
405 a8083063 Iustin Pop
        return True
406 a8083063 Iustin Pop
    return False
407 a8083063 Iustin Pop
408 a8083063 Iustin Pop
  def Assemble(self):
409 a8083063 Iustin Pop
    """Assemble the device.
410 a8083063 Iustin Pop

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

414 a8083063 Iustin Pop
    """
415 a8083063 Iustin Pop
    return True
416 a8083063 Iustin Pop
417 a8083063 Iustin Pop
  def Shutdown(self):
418 a8083063 Iustin Pop
    """Shutdown the device.
419 a8083063 Iustin Pop

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

423 a8083063 Iustin Pop
    """
424 a8083063 Iustin Pop
    return True
425 a8083063 Iustin Pop
426 a8083063 Iustin Pop
  def GetStatus(self):
427 a8083063 Iustin Pop
    """Return the status of the device.
428 a8083063 Iustin Pop

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

433 a8083063 Iustin Pop
    """
434 a8083063 Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
435 a8083063 Iustin Pop
    if result.failed:
436 a8083063 Iustin Pop
      logger.Error("Can't display lv: %s" % result.fail_reason)
437 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
438 a8083063 Iustin Pop
    out = result.stdout.strip()
439 a8083063 Iustin Pop
    # format: type/permissions/alloc/fixed_minor/state/open
440 a8083063 Iustin Pop
    if len(out) != 6:
441 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
442 a8083063 Iustin Pop
    #writable = (out[1] == "w")
443 a8083063 Iustin Pop
    active = (out[4] == "a")
444 a8083063 Iustin Pop
    online = (out[5] == "o")
445 a8083063 Iustin Pop
    if online:
446 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
447 a8083063 Iustin Pop
    elif active:
448 a8083063 Iustin Pop
      retval = self.STATUS_STANDBY
449 a8083063 Iustin Pop
    else:
450 a8083063 Iustin Pop
      retval = self.STATUS_EXISTING
451 a8083063 Iustin Pop
452 a8083063 Iustin Pop
    return retval
453 a8083063 Iustin Pop
454 a8083063 Iustin Pop
  def Open(self, force=False):
455 a8083063 Iustin Pop
    """Make the device ready for I/O.
456 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

779 a8083063 Iustin Pop
    """
780 a8083063 Iustin Pop
    self.Attach()
781 a8083063 Iustin Pop
    if self.minor is None:
782 a8083063 Iustin Pop
      retval = self.STATUS_UNKNOWN
783 a8083063 Iustin Pop
    else:
784 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
785 a8083063 Iustin Pop
    return retval
786 a8083063 Iustin Pop
787 a8083063 Iustin Pop
  def _SetFromMinor(self, minor):
788 a8083063 Iustin Pop
    """Set our parameters based on the given minor.
789 a8083063 Iustin Pop

790 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
791 a8083063 Iustin Pop

792 a8083063 Iustin Pop
    """
793 a8083063 Iustin Pop
    self.minor = minor
794 a8083063 Iustin Pop
    self.dev_path = "/dev/md%d" % minor
795 a8083063 Iustin Pop
796 a8083063 Iustin Pop
  def Assemble(self):
797 a8083063 Iustin Pop
    """Assemble the MD device.
798 a8083063 Iustin Pop

799 a8083063 Iustin Pop
    At this point we should have:
800 a8083063 Iustin Pop
      - list of children devices
801 a8083063 Iustin Pop
      - uuid
802 a8083063 Iustin Pop

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

829 a8083063 Iustin Pop
    This does a 'mdadm --stop' so after this command, the array is no
830 a8083063 Iustin Pop
    longer available.
831 a8083063 Iustin Pop

832 a8083063 Iustin Pop
    """
833 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
834 a8083063 Iustin Pop
      logger.Info("MD object not attached to a device")
835 a8083063 Iustin Pop
      return True
836 a8083063 Iustin Pop
837 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--stop", "/dev/md%d" % self.minor])
838 a8083063 Iustin Pop
    if result.failed:
839 a8083063 Iustin Pop
      logger.Error("Can't stop MD array: %s" % result.fail_reason)
840 a8083063 Iustin Pop
      return False
841 a8083063 Iustin Pop
    self.minor = None
842 a8083063 Iustin Pop
    self.dev_path = None
843 a8083063 Iustin Pop
    return True
844 a8083063 Iustin Pop
845 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
846 a8083063 Iustin Pop
    """Set the maximum sync speed for the MD array.
847 a8083063 Iustin Pop

848 a8083063 Iustin Pop
    """
849 a8083063 Iustin Pop
    result = super(MDRaid1, self).SetSyncSpeed(kbytes)
850 a8083063 Iustin Pop
    if self.minor is None:
851 a8083063 Iustin Pop
      logger.Error("MD array not attached to a device")
852 a8083063 Iustin Pop
      return False
853 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_max" % self.minor, "w")
854 a8083063 Iustin Pop
    try:
855 a8083063 Iustin Pop
      f.write("%d" % kbytes)
856 a8083063 Iustin Pop
    finally:
857 a8083063 Iustin Pop
      f.close()
858 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_min" % self.minor, "w")
859 a8083063 Iustin Pop
    try:
860 a8083063 Iustin Pop
      f.write("%d" % (kbytes/2))
861 a8083063 Iustin Pop
    finally:
862 a8083063 Iustin Pop
      f.close()
863 a8083063 Iustin Pop
    return result
864 a8083063 Iustin Pop
865 a8083063 Iustin Pop
  def GetSyncStatus(self):
866 a8083063 Iustin Pop
    """Returns the sync status of the device.
867 a8083063 Iustin Pop

868 a8083063 Iustin Pop
    Returns:
869 583e3f6f Iustin Pop
     (sync_percent, estimated_time, is_degraded)
870 a8083063 Iustin Pop

871 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
872 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
873 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
874 a8083063 Iustin Pop

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

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

909 a8083063 Iustin Pop
    """
910 a8083063 Iustin Pop
    return True
911 a8083063 Iustin Pop
912 a8083063 Iustin Pop
  def Close(self):
913 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
914 a8083063 Iustin Pop

915 a8083063 Iustin Pop
    This is a no-op for the MDRaid1 device type, but see comment for
916 a8083063 Iustin Pop
    `Open()`.
917 a8083063 Iustin Pop

918 a8083063 Iustin Pop
    """
919 a8083063 Iustin Pop
    return True
920 a8083063 Iustin Pop
921 a8083063 Iustin Pop
922 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
923 0f7f32d9 Iustin Pop
  """Base DRBD class.
924 a8083063 Iustin Pop

925 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
926 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
927 0f7f32d9 Iustin Pop

928 0f7f32d9 Iustin Pop
  """
929 0f7f32d9 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
930 0f7f32d9 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)\)")
931 770fe0f9 Iustin Pop
  _DRBD_MAJOR = 147
932 770fe0f9 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
933 770fe0f9 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
934 770fe0f9 Iustin Pop
  _ST_CONNECTED = "Connected"
935 0f7f32d9 Iustin Pop
936 0f7f32d9 Iustin Pop
  @staticmethod
937 0f7f32d9 Iustin Pop
  def _GetProcData():
938 0f7f32d9 Iustin Pop
    """Return data from /proc/drbd.
939 0f7f32d9 Iustin Pop

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

954 5a47ad20 Iustin Pop
    Returns:
955 5a47ad20 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
956 5a47ad20 Iustin Pop

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

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

982 0f7f32d9 Iustin Pop
    """
983 0f7f32d9 Iustin Pop
    proc_data = cls._GetProcData()
984 0f7f32d9 Iustin Pop
    first_line = proc_data[0].strip()
985 0f7f32d9 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
986 0f7f32d9 Iustin Pop
    if not version:
987 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
988 0f7f32d9 Iustin Pop
                                    first_line)
989 0f7f32d9 Iustin Pop
    return [int(val) for val in version.groups()]
990 0f7f32d9 Iustin Pop
991 770fe0f9 Iustin Pop
  @staticmethod
992 770fe0f9 Iustin Pop
  def _DevPath(minor):
993 770fe0f9 Iustin Pop
    """Return the path to a drbd device for a given minor.
994 770fe0f9 Iustin Pop

995 770fe0f9 Iustin Pop
    """
996 770fe0f9 Iustin Pop
    return "/dev/drbd%d" % minor
997 770fe0f9 Iustin Pop
998 770fe0f9 Iustin Pop
  @classmethod
999 770fe0f9 Iustin Pop
  def _GetUsedDevs(cls):
1000 770fe0f9 Iustin Pop
    """Compute the list of used DRBD devices.
1001 770fe0f9 Iustin Pop

1002 770fe0f9 Iustin Pop
    """
1003 770fe0f9 Iustin Pop
    data = cls._GetProcData()
1004 770fe0f9 Iustin Pop
1005 770fe0f9 Iustin Pop
    used_devs = {}
1006 770fe0f9 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
1007 770fe0f9 Iustin Pop
    for line in data:
1008 770fe0f9 Iustin Pop
      match = valid_line.match(line)
1009 770fe0f9 Iustin Pop
      if not match:
1010 770fe0f9 Iustin Pop
        continue
1011 770fe0f9 Iustin Pop
      minor = int(match.group(1))
1012 770fe0f9 Iustin Pop
      state = match.group(2)
1013 770fe0f9 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1014 770fe0f9 Iustin Pop
        continue
1015 770fe0f9 Iustin Pop
      used_devs[minor] = state, line
1016 770fe0f9 Iustin Pop
1017 770fe0f9 Iustin Pop
    return used_devs
1018 770fe0f9 Iustin Pop
1019 5a47ad20 Iustin Pop
  def _SetFromMinor(self, minor):
1020 5a47ad20 Iustin Pop
    """Set our parameters based on the given minor.
1021 5a47ad20 Iustin Pop

1022 5a47ad20 Iustin Pop
    This sets our minor variable and our dev_path.
1023 5a47ad20 Iustin Pop

1024 5a47ad20 Iustin Pop
    """
1025 5a47ad20 Iustin Pop
    if minor is None:
1026 5a47ad20 Iustin Pop
      self.minor = self.dev_path = None
1027 5a47ad20 Iustin Pop
    else:
1028 5a47ad20 Iustin Pop
      self.minor = minor
1029 5a47ad20 Iustin Pop
      self.dev_path = self._DevPath(minor)
1030 5a47ad20 Iustin Pop
1031 ae26a287 Iustin Pop
  @staticmethod
1032 ae26a287 Iustin Pop
  def _CheckMetaSize(meta_device):
1033 ae26a287 Iustin Pop
    """Check if the given meta device looks like a valid one.
1034 ae26a287 Iustin Pop

1035 ae26a287 Iustin Pop
    This currently only check the size, which must be around
1036 ae26a287 Iustin Pop
    128MiB.
1037 ae26a287 Iustin Pop

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

1060 f3e513ad Iustin Pop
    This is not supported for drbd devices.
1061 f3e513ad Iustin Pop

1062 f3e513ad Iustin Pop
    """
1063 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1064 f3e513ad Iustin Pop
1065 0f7f32d9 Iustin Pop
1066 0f7f32d9 Iustin Pop
class DRBDev(BaseDRBD):
1067 a8083063 Iustin Pop
  """DRBD block device.
1068 a8083063 Iustin Pop

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

1073 a8083063 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1074 a8083063 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1075 a8083063 Iustin Pop
  data device and the meta_device. The meta device is checked for
1076 a8083063 Iustin Pop
  valid size and is zeroed on create.
1077 a8083063 Iustin Pop

1078 a8083063 Iustin Pop
  """
1079 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
1080 a8083063 Iustin Pop
    super(DRBDev, self).__init__(unique_id, children)
1081 a8083063 Iustin Pop
    self.major = self._DRBD_MAJOR
1082 0f7f32d9 Iustin Pop
    [kmaj, kmin, kfix, api, proto] = self._GetVersion()
1083 0f7f32d9 Iustin Pop
    if kmaj != 0 and kmin != 7:
1084 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1085 0f7f32d9 Iustin Pop
                                    " requested ganeti usage: kernel is"
1086 0f7f32d9 Iustin Pop
                                    " %s.%s, ganeti wants 0.7" % (kmaj, kmin))
1087 0f7f32d9 Iustin Pop
1088 a8083063 Iustin Pop
    if len(children) != 2:
1089 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1090 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1091 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1092 a8083063 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
1093 a8083063 Iustin Pop
    self.Attach()
1094 a8083063 Iustin Pop
1095 a8083063 Iustin Pop
  @classmethod
1096 a8083063 Iustin Pop
  def _FindUnusedMinor(cls):
1097 a8083063 Iustin Pop
    """Find an unused DRBD device.
1098 a8083063 Iustin Pop

1099 a8083063 Iustin Pop
    """
1100 a8083063 Iustin Pop
    data = cls._GetProcData()
1101 a8083063 Iustin Pop
1102 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1103 a8083063 Iustin Pop
    for line in data:
1104 a8083063 Iustin Pop
      match = valid_line.match(line)
1105 a8083063 Iustin Pop
      if match:
1106 a8083063 Iustin Pop
        return int(match.group(1))
1107 a8083063 Iustin Pop
    logger.Error("Error: no free drbd minors!")
1108 0caf6485 Iustin Pop
    raise errors.BlockDeviceError("Can't find a free DRBD minor")
1109 a8083063 Iustin Pop
1110 a8083063 Iustin Pop
  @classmethod
1111 a8083063 Iustin Pop
  def _GetDevInfo(cls, minor):
1112 a8083063 Iustin Pop
    """Get details about a given DRBD minor.
1113 a8083063 Iustin Pop

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

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

1164 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1165 a8083063 Iustin Pop
    method tests if our local backing device is the same as the one in
1166 a8083063 Iustin Pop
    the info parameter, in effect testing if we look like the given
1167 a8083063 Iustin Pop
    device.
1168 a8083063 Iustin Pop

1169 a8083063 Iustin Pop
    """
1170 a8083063 Iustin Pop
    if not ("local_dev" in info and "meta_dev" in info and
1171 a8083063 Iustin Pop
            "meta_index" in info):
1172 a8083063 Iustin Pop
      return False
1173 a8083063 Iustin Pop
1174 a8083063 Iustin Pop
    backend = self._children[0]
1175 a8083063 Iustin Pop
    if backend is not None:
1176 a8083063 Iustin Pop
      retval = (info["local_dev"] == (backend.major, backend.minor))
1177 a8083063 Iustin Pop
    else:
1178 a8083063 Iustin Pop
      retval = (info["local_dev"] == (0, 0))
1179 a8083063 Iustin Pop
    meta = self._children[1]
1180 a8083063 Iustin Pop
    if meta is not None:
1181 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == (meta.major, meta.minor))
1182 a8083063 Iustin Pop
      retval = retval and (info["meta_index"] == 0)
1183 a8083063 Iustin Pop
    else:
1184 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == "internal" and
1185 a8083063 Iustin Pop
                           info["meta_index"] == -1)
1186 a8083063 Iustin Pop
    return retval
1187 a8083063 Iustin Pop
1188 a8083063 Iustin Pop
  def _MatchesNet(self, info):
1189 a8083063 Iustin Pop
    """Test if our network config matches with an existing device.
1190 a8083063 Iustin Pop

1191 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1192 a8083063 Iustin Pop
    method tests if our network configuration is the same as the one
1193 a8083063 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1194 a8083063 Iustin Pop
    device.
1195 a8083063 Iustin Pop

1196 a8083063 Iustin Pop
    """
1197 a8083063 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1198 a8083063 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1199 a8083063 Iustin Pop
      return True
1200 a8083063 Iustin Pop
1201 a8083063 Iustin Pop
    if self._lhost is None:
1202 a8083063 Iustin Pop
      return False
1203 a8083063 Iustin Pop
1204 a8083063 Iustin Pop
    if not ("local_addr" in info and
1205 a8083063 Iustin Pop
            "remote_addr" in info):
1206 a8083063 Iustin Pop
      return False
1207 a8083063 Iustin Pop
1208 a8083063 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1209 a8083063 Iustin Pop
    retval = (retval and
1210 a8083063 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1211 a8083063 Iustin Pop
    return retval
1212 a8083063 Iustin Pop
1213 a8083063 Iustin Pop
  @classmethod
1214 a8083063 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1215 a8083063 Iustin Pop
    """Configure the local part of a DRBD device.
1216 a8083063 Iustin Pop

1217 a8083063 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1218 a8083063 Iustin Pop
    device. And it must be done only once.
1219 a8083063 Iustin Pop

1220 a8083063 Iustin Pop
    """
1221 ae26a287 Iustin Pop
    if not cls._CheckMetaSize(meta):
1222 a8083063 Iustin Pop
      return False
1223 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1224 a8083063 Iustin Pop
                           backend, meta, "0", "-e", "detach"])
1225 a8083063 Iustin Pop
    if result.failed:
1226 a8083063 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1227 a8083063 Iustin Pop
    return not result.failed
1228 a8083063 Iustin Pop
1229 a8083063 Iustin Pop
  @classmethod
1230 a8083063 Iustin Pop
  def _ShutdownLocal(cls, minor):
1231 a8083063 Iustin Pop
    """Detach from the local device.
1232 a8083063 Iustin Pop

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

1236 a8083063 Iustin Pop
    """
1237 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1238 a8083063 Iustin Pop
    if result.failed:
1239 a8083063 Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
1240 a8083063 Iustin Pop
    return not result.failed
1241 a8083063 Iustin Pop
1242 a8083063 Iustin Pop
  @staticmethod
1243 a8083063 Iustin Pop
  def _ShutdownAll(minor):
1244 a8083063 Iustin Pop
    """Deactivate the device.
1245 a8083063 Iustin Pop

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

1248 a8083063 Iustin Pop
    """
1249 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", DRBDev._DevPath(minor), "down"])
1250 a8083063 Iustin Pop
    if result.failed:
1251 a8083063 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
1252 a8083063 Iustin Pop
    return not result.failed
1253 a8083063 Iustin Pop
1254 a8083063 Iustin Pop
  @classmethod
1255 a8083063 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol):
1256 a8083063 Iustin Pop
    """Configure the network part of the device.
1257 a8083063 Iustin Pop

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

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

1297 a8083063 Iustin Pop
    This fails if we don't have a local device.
1298 a8083063 Iustin Pop

1299 a8083063 Iustin Pop
    """
1300 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1301 a8083063 Iustin Pop
    logger.Error("Can't shutdown network: %s" % result.output)
1302 a8083063 Iustin Pop
    return not result.failed
1303 a8083063 Iustin Pop
1304 a8083063 Iustin Pop
  def Assemble(self):
1305 a8083063 Iustin Pop
    """Assemble the drbd.
1306 a8083063 Iustin Pop

1307 a8083063 Iustin Pop
    Method:
1308 a8083063 Iustin Pop
      - if we have a local backing device, we bind to it by:
1309 a8083063 Iustin Pop
        - checking the list of used drbd devices
1310 a8083063 Iustin Pop
        - check if the local minor use of any of them is our own device
1311 a8083063 Iustin Pop
        - if yes, abort?
1312 a8083063 Iustin Pop
        - if not, bind
1313 a8083063 Iustin Pop
      - if we have a local/remote net info:
1314 a8083063 Iustin Pop
        - redo the local backing device step for the remote device
1315 a8083063 Iustin Pop
        - check if any drbd device is using the local port,
1316 a8083063 Iustin Pop
          if yes abort
1317 a8083063 Iustin Pop
        - check if any remote drbd device is using the remote
1318 a8083063 Iustin Pop
          port, if yes abort (for now)
1319 a8083063 Iustin Pop
        - bind our net port
1320 a8083063 Iustin Pop
        - bind the remote net port
1321 a8083063 Iustin Pop

1322 a8083063 Iustin Pop
    """
1323 a8083063 Iustin Pop
    self.Attach()
1324 a8083063 Iustin Pop
    if self.minor is not None:
1325 a8083063 Iustin Pop
      logger.Info("Already assembled")
1326 a8083063 Iustin Pop
      return True
1327 a8083063 Iustin Pop
1328 a8083063 Iustin Pop
    result = super(DRBDev, self).Assemble()
1329 a8083063 Iustin Pop
    if not result:
1330 a8083063 Iustin Pop
      return result
1331 a8083063 Iustin Pop
1332 a8083063 Iustin Pop
    minor = self._FindUnusedMinor()
1333 a8083063 Iustin Pop
    need_localdev_teardown = False
1334 a8083063 Iustin Pop
    if self._children[0]:
1335 a8083063 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1336 a8083063 Iustin Pop
                                   self._children[1].dev_path)
1337 a8083063 Iustin Pop
      if not result:
1338 a8083063 Iustin Pop
        return False
1339 a8083063 Iustin Pop
      need_localdev_teardown = True
1340 a8083063 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1341 a8083063 Iustin Pop
      result = self._AssembleNet(minor,
1342 a8083063 Iustin Pop
                                 (self._lhost, self._lport,
1343 a8083063 Iustin Pop
                                  self._rhost, self._rport),
1344 a8083063 Iustin Pop
                                 "C")
1345 a8083063 Iustin Pop
      if not result:
1346 a8083063 Iustin Pop
        if need_localdev_teardown:
1347 a8083063 Iustin Pop
          # we will ignore failures from this
1348 a8083063 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
1349 a8083063 Iustin Pop
          self._ShutdownAll(minor)
1350 a8083063 Iustin Pop
        return False
1351 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1352 a8083063 Iustin Pop
    return True
1353 a8083063 Iustin Pop
1354 a8083063 Iustin Pop
  def Shutdown(self):
1355 a8083063 Iustin Pop
    """Shutdown the DRBD device.
1356 a8083063 Iustin Pop

1357 a8083063 Iustin Pop
    """
1358 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1359 a8083063 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
1360 a8083063 Iustin Pop
      return True
1361 a8083063 Iustin Pop
    if not self._ShutdownAll(self.minor):
1362 a8083063 Iustin Pop
      return False
1363 a8083063 Iustin Pop
    self.minor = None
1364 a8083063 Iustin Pop
    self.dev_path = None
1365 a8083063 Iustin Pop
    return True
1366 a8083063 Iustin Pop
1367 a8083063 Iustin Pop
  def Attach(self):
1368 a8083063 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1369 a8083063 Iustin Pop

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

1374 a8083063 Iustin Pop
    """
1375 a8083063 Iustin Pop
    for minor in self._GetUsedDevs():
1376 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
1377 a8083063 Iustin Pop
      match_l = self._MatchesLocal(info)
1378 a8083063 Iustin Pop
      match_r = self._MatchesNet(info)
1379 a8083063 Iustin Pop
      if match_l and match_r:
1380 a8083063 Iustin Pop
        break
1381 a8083063 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1382 a8083063 Iustin Pop
        res_r = self._AssembleNet(minor,
1383 a8083063 Iustin Pop
                                  (self._lhost, self._lport,
1384 a8083063 Iustin Pop
                                   self._rhost, self._rport),
1385 a8083063 Iustin Pop
                                  "C")
1386 a8083063 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
1387 a8083063 Iustin Pop
          break
1388 a8083063 Iustin Pop
    else:
1389 a8083063 Iustin Pop
      minor = None
1390 a8083063 Iustin Pop
1391 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1392 a8083063 Iustin Pop
    return minor is not None
1393 a8083063 Iustin Pop
1394 a8083063 Iustin Pop
  def Open(self, force=False):
1395 a8083063 Iustin Pop
    """Make the local state primary.
1396 a8083063 Iustin Pop

1397 a8083063 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1398 a8083063 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1399 a8083063 Iustin Pop
    force flag should be only given after creation, when it actually
1400 a8083063 Iustin Pop
    has to be given.
1401 a8083063 Iustin Pop

1402 a8083063 Iustin Pop
    """
1403 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1404 a8083063 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1405 a8083063 Iustin Pop
      return False
1406 a8083063 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1407 a8083063 Iustin Pop
    if force:
1408 a8083063 Iustin Pop
      cmd.append("--do-what-I-say")
1409 a8083063 Iustin Pop
    result = utils.RunCmd(cmd)
1410 a8083063 Iustin Pop
    if result.failed:
1411 a8083063 Iustin Pop
      logger.Error("Can't make drbd device primary: %s" % result.output)
1412 a8083063 Iustin Pop
      return False
1413 a8083063 Iustin Pop
    return True
1414 a8083063 Iustin Pop
1415 a8083063 Iustin Pop
  def Close(self):
1416 a8083063 Iustin Pop
    """Make the local state secondary.
1417 a8083063 Iustin Pop

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

1420 a8083063 Iustin Pop
    """
1421 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1422 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1423 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1424 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1425 a8083063 Iustin Pop
    if result.failed:
1426 a8083063 Iustin Pop
      logger.Error("Can't switch drbd device to secondary: %s" % result.output)
1427 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't switch drbd device to secondary")
1428 a8083063 Iustin Pop
1429 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1430 a8083063 Iustin Pop
    """Set the speed of the DRBD syncer.
1431 a8083063 Iustin Pop

1432 a8083063 Iustin Pop
    """
1433 a8083063 Iustin Pop
    children_result = super(DRBDev, self).SetSyncSpeed(kbytes)
1434 a8083063 Iustin Pop
    if self.minor is None:
1435 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1436 a8083063 Iustin Pop
      return False
1437 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1438 a8083063 Iustin Pop
                           kbytes])
1439 a8083063 Iustin Pop
    if result.failed:
1440 a8083063 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1441 a8083063 Iustin Pop
    return not result.failed and children_result
1442 a8083063 Iustin Pop
1443 a8083063 Iustin Pop
  def GetSyncStatus(self):
1444 a8083063 Iustin Pop
    """Returns the sync status of the device.
1445 a8083063 Iustin Pop

1446 a8083063 Iustin Pop
    Returns:
1447 583e3f6f Iustin Pop
     (sync_percent, estimated_time, is_degraded)
1448 a8083063 Iustin Pop

1449 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
1450 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
1451 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
1452 a8083063 Iustin Pop

1453 a8083063 Iustin Pop
    """
1454 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1455 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1456 a8083063 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1457 a8083063 Iustin Pop
    if self.minor not in proc_info:
1458 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1459 a8083063 Iustin Pop
                                    self.minor)
1460 a8083063 Iustin Pop
    line = proc_info[self.minor]
1461 a8083063 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1462 a8083063 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1463 a8083063 Iustin Pop
    if match:
1464 a8083063 Iustin Pop
      sync_percent = float(match.group(1))
1465 a8083063 Iustin Pop
      hours = int(match.group(2))
1466 a8083063 Iustin Pop
      minutes = int(match.group(3))
1467 a8083063 Iustin Pop
      seconds = int(match.group(4))
1468 a8083063 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1469 a8083063 Iustin Pop
    else:
1470 a8083063 Iustin Pop
      sync_percent = None
1471 a8083063 Iustin Pop
      est_time = None
1472 a8083063 Iustin Pop
    match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1473 a8083063 Iustin Pop
    if not match:
1474 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1475 a8083063 Iustin Pop
                                    self.minor)
1476 a8083063 Iustin Pop
    client_state = match.group(1)
1477 a8083063 Iustin Pop
    is_degraded = client_state != "Connected"
1478 a8083063 Iustin Pop
    return sync_percent, est_time, is_degraded
1479 a8083063 Iustin Pop
1480 a8083063 Iustin Pop
  def GetStatus(self):
1481 a8083063 Iustin Pop
    """Compute the status of the DRBD device
1482 a8083063 Iustin Pop

1483 a8083063 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1484 a8083063 Iustin Pop

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

1512 a8083063 Iustin Pop
    This writes until we get ENOSPC.
1513 a8083063 Iustin Pop

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

1528 a8083063 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1529 a8083063 Iustin Pop
    function just zeroes the meta device.
1530 a8083063 Iustin Pop

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

1548 a8083063 Iustin Pop
    """
1549 a8083063 Iustin Pop
    return self.Shutdown()
1550 a8083063 Iustin Pop
1551 f3e513ad Iustin Pop
1552 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1553 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1554 a2cfdea2 Iustin Pop

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

1559 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1560 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1561 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1562 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1563 a2cfdea2 Iustin Pop

1564 a2cfdea2 Iustin Pop
  """
1565 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1566 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1567 a2cfdea2 Iustin Pop
1568 a2cfdea2 Iustin Pop
  def __init__(self, unique_id, children):
1569 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1570 fc1dc9d7 Iustin Pop
      children = []
1571 a2cfdea2 Iustin Pop
    super(DRBD8, self).__init__(unique_id, children)
1572 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1573 a2cfdea2 Iustin Pop
    [kmaj, kmin, kfix, api, proto] = self._GetVersion()
1574 a2cfdea2 Iustin Pop
    if kmaj != 8:
1575 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1576 a2cfdea2 Iustin Pop
                                    " requested ganeti usage: kernel is"
1577 a2cfdea2 Iustin Pop
                                    " %s.%s, ganeti wants 8.x" % (kmaj, kmin))
1578 a2cfdea2 Iustin Pop
1579 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
1580 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1581 a2cfdea2 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1582 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1583 a2cfdea2 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
1584 a2cfdea2 Iustin Pop
    self.Attach()
1585 a2cfdea2 Iustin Pop
1586 a2cfdea2 Iustin Pop
  @classmethod
1587 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1588 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1589 a2cfdea2 Iustin Pop

1590 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1591 a2cfdea2 Iustin Pop

1592 a2cfdea2 Iustin Pop
    """
1593 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1594 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1595 a2cfdea2 Iustin Pop
    if result.failed:
1596 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't initialize meta device: %s" %
1597 a2cfdea2 Iustin Pop
                                    result.output)
1598 a2cfdea2 Iustin Pop
1599 a2cfdea2 Iustin Pop
  @classmethod
1600 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1601 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1602 a2cfdea2 Iustin Pop

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

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

1631 a2cfdea2 Iustin Pop
    """
1632 a2cfdea2 Iustin Pop
    minor = cls._FindUnusedMinor()
1633 a2cfdea2 Iustin Pop
    minor_path = cls._DevPath(minor)
1634 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", minor_path,
1635 a2cfdea2 Iustin Pop
                           "v08", meta_device, "0",
1636 a2cfdea2 Iustin Pop
                           "dstate"])
1637 a2cfdea2 Iustin Pop
    if result.failed:
1638 a2cfdea2 Iustin Pop
      logger.Error("Invalid meta device %s: %s" % (meta_device, result.output))
1639 a2cfdea2 Iustin Pop
      return False
1640 a2cfdea2 Iustin Pop
    return True
1641 a2cfdea2 Iustin Pop
1642 a2cfdea2 Iustin Pop
  @classmethod
1643 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1644 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1645 a2cfdea2 Iustin Pop

1646 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1647 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1648 a2cfdea2 Iustin Pop

1649 a2cfdea2 Iustin Pop
    """
1650 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1651 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1652 a2cfdea2 Iustin Pop
1653 a2cfdea2 Iustin Pop
    # pyparsing setup
1654 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1655 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1656 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1657 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1658 a2cfdea2 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t:(l, [int(t[0])]))
1659 a2cfdea2 Iustin Pop
1660 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1661 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1662 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1663 a2cfdea2 Iustin Pop
1664 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1665 a2cfdea2 Iustin Pop
1666 a2cfdea2 Iustin Pop
    # value types
1667 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1668 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1669 a2cfdea2 Iustin Pop
    addr_port = (pyp.Word(pyp.nums + '.') + pyp.Literal(':').suppress() +
1670 a2cfdea2 Iustin Pop
                 number)
1671 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1672 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
1673 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
1674 a2cfdea2 Iustin Pop
1675 a2cfdea2 Iustin Pop
    # a statement
1676 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1677 a2cfdea2 Iustin Pop
            (addr_port ^ value ^ quoted ^ meta_value) +
1678 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1679 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1680 a2cfdea2 Iustin Pop
1681 a2cfdea2 Iustin Pop
    # an entire section
1682 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1683 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1684 a2cfdea2 Iustin Pop
1685 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1686 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1687 a2cfdea2 Iustin Pop
1688 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1689 a2cfdea2 Iustin Pop
1690 a2cfdea2 Iustin Pop
    return bnf
1691 a2cfdea2 Iustin Pop
1692 a2cfdea2 Iustin Pop
  @classmethod
1693 a2cfdea2 Iustin Pop
  def _GetDevInfo(cls, minor):
1694 a2cfdea2 Iustin Pop
    """Get details about a given DRBD minor.
1695 a2cfdea2 Iustin Pop

1696 a2cfdea2 Iustin Pop
    This return, if available, the local backing device (as a path)
1697 a2cfdea2 Iustin Pop
    and the local and remote (ip, port) information.
1698 a2cfdea2 Iustin Pop

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

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

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

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

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

1794 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1795 a2cfdea2 Iustin Pop
    device. And it must be done only once.
1796 a2cfdea2 Iustin Pop

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

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

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

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

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

1919 a2cfdea2 Iustin Pop
    Returns:
1920 583e3f6f Iustin Pop
     (sync_percent, estimated_time, is_degraded)
1921 a2cfdea2 Iustin Pop

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

1926 a2cfdea2 Iustin Pop
    """
1927 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1928 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1929 a2cfdea2 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1930 a2cfdea2 Iustin Pop
    if self.minor not in proc_info:
1931 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1932 a2cfdea2 Iustin Pop
                                    self.minor)
1933 a2cfdea2 Iustin Pop
    line = proc_info[self.minor]
1934 a2cfdea2 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1935 a2cfdea2 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1936 a2cfdea2 Iustin Pop
    if match:
1937 a2cfdea2 Iustin Pop
      sync_percent = float(match.group(1))
1938 a2cfdea2 Iustin Pop
      hours = int(match.group(2))
1939 a2cfdea2 Iustin Pop
      minutes = int(match.group(3))
1940 a2cfdea2 Iustin Pop
      seconds = int(match.group(4))
1941 a2cfdea2 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1942 a2cfdea2 Iustin Pop
    else:
1943 a2cfdea2 Iustin Pop
      sync_percent = None
1944 a2cfdea2 Iustin Pop
      est_time = None
1945 583e3f6f Iustin Pop
    match = re.match("^ *\d+: cs:(\w+).*ds:(\w+)/(\w+).*$", line)
1946 a2cfdea2 Iustin Pop
    if not match:
1947 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1948 a2cfdea2 Iustin Pop
                                    self.minor)
1949 a2cfdea2 Iustin Pop
    client_state = match.group(1)
1950 583e3f6f Iustin Pop
    local_disk_state = match.group(2)
1951 583e3f6f Iustin Pop
    is_degraded = (client_state != "Connected" or
1952 583e3f6f Iustin Pop
                   local_disk_state != "UpToDate")
1953 a2cfdea2 Iustin Pop
    return sync_percent, est_time, is_degraded
1954 a2cfdea2 Iustin Pop
1955 a2cfdea2 Iustin Pop
  def GetStatus(self):
1956 a2cfdea2 Iustin Pop
    """Compute the status of the DRBD device
1957 a2cfdea2 Iustin Pop

1958 a2cfdea2 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1959 a2cfdea2 Iustin Pop

1960 a2cfdea2 Iustin Pop
    """
1961 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1962 a2cfdea2 Iustin Pop
      return self.STATUS_UNKNOWN
1963 a2cfdea2 Iustin Pop
1964 a2cfdea2 Iustin Pop
    data = self._GetProcData()
1965 a2cfdea2 Iustin Pop
    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
1966 a2cfdea2 Iustin Pop
                       self.minor)
1967 a2cfdea2 Iustin Pop
    for line in data:
1968 a2cfdea2 Iustin Pop
      mresult = match.match(line)
1969 a2cfdea2 Iustin Pop
      if mresult:
1970 a2cfdea2 Iustin Pop
        break
1971 a2cfdea2 Iustin Pop
    else:
1972 a2cfdea2 Iustin Pop
      logger.Error("Can't find myself!")
1973 a2cfdea2 Iustin Pop
      return self.STATUS_UNKNOWN
1974 a2cfdea2 Iustin Pop
1975 a2cfdea2 Iustin Pop
    state = mresult.group(2)
1976 a2cfdea2 Iustin Pop
    if state == "Primary":
1977 a2cfdea2 Iustin Pop
      result = self.STATUS_ONLINE
1978 a2cfdea2 Iustin Pop
    else:
1979 a2cfdea2 Iustin Pop
      result = self.STATUS_STANDBY
1980 a2cfdea2 Iustin Pop
1981 a2cfdea2 Iustin Pop
    return result
1982 a2cfdea2 Iustin Pop
1983 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1984 a2cfdea2 Iustin Pop
    """Make the local state primary.
1985 a2cfdea2 Iustin Pop

1986 a2cfdea2 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1987 a2cfdea2 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1988 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1989 a2cfdea2 Iustin Pop
    has to be given.
1990 a2cfdea2 Iustin Pop

1991 a2cfdea2 Iustin Pop
    """
1992 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1993 a2cfdea2 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1994 a2cfdea2 Iustin Pop
      return False
1995 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1996 a2cfdea2 Iustin Pop
    if force:
1997 a2cfdea2 Iustin Pop
      cmd.append("-o")
1998 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1999 a2cfdea2 Iustin Pop
    if result.failed:
2000 a2cfdea2 Iustin Pop
      logger.Error("Can't make drbd device primary: %s" % result.output)
2001 a2cfdea2 Iustin Pop
      return False
2002 a2cfdea2 Iustin Pop
    return True
2003 a2cfdea2 Iustin Pop
2004 a2cfdea2 Iustin Pop
  def Close(self):
2005 a2cfdea2 Iustin Pop
    """Make the local state secondary.
2006 a2cfdea2 Iustin Pop

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

2009 a2cfdea2 Iustin Pop
    """
2010 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2011 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
2012 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
2013 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
2014 a2cfdea2 Iustin Pop
    if result.failed:
2015 a2cfdea2 Iustin Pop
      logger.Error("Can't switch drbd device to secondary: %s" % result.output)
2016 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't switch drbd device to secondary")
2017 a2cfdea2 Iustin Pop
2018 a2cfdea2 Iustin Pop
  def Attach(self):
2019 a2cfdea2 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
2020 a2cfdea2 Iustin Pop

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

2025 a2cfdea2 Iustin Pop
    """
2026 a2cfdea2 Iustin Pop
    for minor in self._GetUsedDevs():
2027 a2cfdea2 Iustin Pop
      info = self._GetDevInfo(minor)
2028 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
2029 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
2030 a2cfdea2 Iustin Pop
      if match_l and match_r:
2031 a2cfdea2 Iustin Pop
        break
2032 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
2033 a2cfdea2 Iustin Pop
        res_r = self._AssembleNet(minor,
2034 a2cfdea2 Iustin Pop
                                  (self._lhost, self._lport,
2035 a2cfdea2 Iustin Pop
                                   self._rhost, self._rport),
2036 a2cfdea2 Iustin Pop
                                  "C")
2037 a2cfdea2 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
2038 a2cfdea2 Iustin Pop
          break
2039 fc1dc9d7 Iustin Pop
      # the weakest case: we find something that is only net attached
2040 fc1dc9d7 Iustin Pop
      # even though we were passed some children at init time
2041 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
2042 fc1dc9d7 Iustin Pop
        break
2043 a2cfdea2 Iustin Pop
    else:
2044 a2cfdea2 Iustin Pop
      minor = None
2045 a2cfdea2 Iustin Pop
2046 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2047 a2cfdea2 Iustin Pop
    return minor is not None
2048 a2cfdea2 Iustin Pop
2049 a2cfdea2 Iustin Pop
  def Assemble(self):
2050 a2cfdea2 Iustin Pop
    """Assemble the drbd.
2051 a2cfdea2 Iustin Pop

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

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

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

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

2116 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2117 f3e513ad Iustin Pop

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

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

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

2138 a2cfdea2 Iustin Pop
    """
2139 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2140 a2cfdea2 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
2141 a2cfdea2 Iustin Pop
      return True
2142 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
2143 a2cfdea2 Iustin Pop
      return False
2144 a2cfdea2 Iustin Pop
    self.minor = None
2145 a2cfdea2 Iustin Pop
    self.dev_path = None
2146 a2cfdea2 Iustin Pop
    return True
2147 a2cfdea2 Iustin Pop
2148 f3e513ad Iustin Pop
  def Rename(self, new_uid):
2149 f3e513ad Iustin Pop
    """Re-connect this device to another peer.
2150 f3e513ad Iustin Pop

2151 f3e513ad Iustin Pop
    """
2152 f3e513ad Iustin Pop
    if self.minor is None:
2153 f3e513ad Iustin Pop
      raise errors.BlockDeviceError("Device not attached during rename")
2154 f3e513ad Iustin Pop
    if self._rhost is not None:
2155 f3e513ad Iustin Pop
      # this means we did have a host when we attached, so we are connected
2156 f3e513ad Iustin Pop
      if not self._ShutdownNet(self.minor):
2157 f3e513ad Iustin Pop
        raise errors.BlockDeviceError("Can't disconnect from remote peer")
2158 f3e513ad Iustin Pop
      old_id = self.unique_id
2159 f3e513ad Iustin Pop
    else:
2160 f3e513ad Iustin Pop
      old_id = None
2161 f3e513ad Iustin Pop
    self.unique_id = new_uid
2162 f3e513ad Iustin Pop
    if not self._AssembleNet(self.minor, self.unique_id, "C"):
2163 f3e513ad Iustin Pop
      logger.Error("Can't attach to new peer!")
2164 65fe4693 Iustin Pop
      if old_id is not None:
2165 f3e513ad Iustin Pop
        self._AssembleNet(self.minor, old_id, "C")
2166 f3e513ad Iustin Pop
      self.unique_id = old_id
2167 f3e513ad Iustin Pop
      raise errors.BlockDeviceError("Can't attach to new peer")
2168 f3e513ad Iustin Pop
2169 a2cfdea2 Iustin Pop
  def Remove(self):
2170 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2171 a2cfdea2 Iustin Pop

2172 a2cfdea2 Iustin Pop
    """
2173 a2cfdea2 Iustin Pop
    return self.Shutdown()
2174 a2cfdea2 Iustin Pop
2175 a2cfdea2 Iustin Pop
  @classmethod
2176 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
2177 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2178 a2cfdea2 Iustin Pop

2179 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2180 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2181 a2cfdea2 Iustin Pop

2182 a2cfdea2 Iustin Pop
    """
2183 a2cfdea2 Iustin Pop
    if len(children) != 2:
2184 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2185 a2cfdea2 Iustin Pop
    meta = children[1]
2186 a2cfdea2 Iustin Pop
    meta.Assemble()
2187 a2cfdea2 Iustin Pop
    if not meta.Attach():
2188 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
2189 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
2190 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
2191 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
2192 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
2193 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
2194 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
2195 a2cfdea2 Iustin Pop
2196 a8083063 Iustin Pop
2197 a8083063 Iustin Pop
DEV_MAP = {
2198 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2199 fe96220b Iustin Pop
  constants.LD_MD_R1: MDRaid1,
2200 fe96220b Iustin Pop
  constants.LD_DRBD7: DRBDev,
2201 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2202 a8083063 Iustin Pop
  }
2203 a8083063 Iustin Pop
2204 a8083063 Iustin Pop
2205 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
2206 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2207 a8083063 Iustin Pop

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

2211 a8083063 Iustin Pop
  """
2212 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2213 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2214 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2215 a8083063 Iustin Pop
  if not device.Attach():
2216 a8083063 Iustin Pop
    return None
2217 a8083063 Iustin Pop
  return  device
2218 a8083063 Iustin Pop
2219 a8083063 Iustin Pop
2220 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
2221 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2222 a8083063 Iustin Pop

2223 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
2224 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
2225 a8083063 Iustin Pop

2226 a8083063 Iustin Pop
  """
2227 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2228 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2229 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2230 a8083063 Iustin Pop
  if not device.Attach():
2231 a8083063 Iustin Pop
    device.Assemble()
2232 a8083063 Iustin Pop
  if not device.Attach():
2233 a8083063 Iustin Pop
    raise errors.BlockDeviceError("Can't find a valid block device for"
2234 a8083063 Iustin Pop
                                  " %s/%s/%s" %
2235 a8083063 Iustin Pop
                                  (dev_type, unique_id, children))
2236 a8083063 Iustin Pop
  return device
2237 a8083063 Iustin Pop
2238 a8083063 Iustin Pop
2239 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2240 a8083063 Iustin Pop
  """Create a device.
2241 a8083063 Iustin Pop

2242 a8083063 Iustin Pop
  """
2243 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2244 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2245 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2246 a8083063 Iustin Pop
  return device