Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 24818e8f

History | View | Annotate | Download (68.9 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Block device abstraction"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import re
25 a8083063 Iustin Pop
import time
26 a8083063 Iustin Pop
import errno
27 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 153d9724 Iustin Pop
      args.append(dev.dev_path)
748 153d9724 Iustin Pop
      for c in self._children:
749 153d9724 Iustin Pop
        if c.dev_path == dev.dev_path:
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 a8083063 Iustin Pop
     (sync_percent, estimated_time)
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 a8083063 Iustin Pop
     (sync_percent, estimated_time)
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 a2cfdea2 Iustin Pop
    super(DRBD8, self).__init__(unique_id, children)
1570 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1571 a2cfdea2 Iustin Pop
    [kmaj, kmin, kfix, api, proto] = self._GetVersion()
1572 a2cfdea2 Iustin Pop
    if kmaj != 8:
1573 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1574 a2cfdea2 Iustin Pop
                                    " requested ganeti usage: kernel is"
1575 a2cfdea2 Iustin Pop
                                    " %s.%s, ganeti wants 8.x" % (kmaj, kmin))
1576 a2cfdea2 Iustin Pop
1577 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
1578 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1579 a2cfdea2 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1580 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1581 a2cfdea2 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
1582 a2cfdea2 Iustin Pop
    self.Attach()
1583 a2cfdea2 Iustin Pop
1584 a2cfdea2 Iustin Pop
  @classmethod
1585 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1586 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1587 a2cfdea2 Iustin Pop

1588 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1589 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1874 b00b95dd Iustin Pop
    """
1875 b00b95dd Iustin Pop
    if self.minor is None:
1876 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to drbd8 during"
1877 b00b95dd Iustin Pop
                                    " RemoveChildren")
1878 b00b95dd Iustin Pop
    if len(self._children) != 2:
1879 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We don't have two children: %s" %
1880 b00b95dd Iustin Pop
                                    self._children)
1881 b00b95dd Iustin Pop
1882 b00b95dd Iustin Pop
    if len(devices) != 2:
1883 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We need two children in RemoveChildren")
1884 b00b95dd Iustin Pop
    for idx, dev in enumerate(devices):
1885 b00b95dd Iustin Pop
      if dev.dev_path != self._children[idx].dev_path:
1886 b00b95dd Iustin Pop
        raise errors.BlockDeviceError("Mismatch in local storage (%d) in"
1887 b00b95dd Iustin Pop
                                      " RemoveChildren" % idx)
1888 b00b95dd Iustin Pop
1889 b00b95dd Iustin Pop
    if not self._ShutdownLocal(self.minor):
1890 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't detach from local storage")
1891 b00b95dd Iustin Pop
    self._children = []
1892 b00b95dd Iustin Pop
1893 a2cfdea2 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1894 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1895 a2cfdea2 Iustin Pop

1896 a2cfdea2 Iustin Pop
    """
1897 a2cfdea2 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1898 a2cfdea2 Iustin Pop
    if self.minor is None:
1899 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
1900 a2cfdea2 Iustin Pop
      return False
1901 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1902 a2cfdea2 Iustin Pop
                           kbytes])
1903 a2cfdea2 Iustin Pop
    if result.failed:
1904 a2cfdea2 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1905 a2cfdea2 Iustin Pop
    return not result.failed and children_result
1906 a2cfdea2 Iustin Pop
1907 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1908 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1909 a2cfdea2 Iustin Pop

1910 a2cfdea2 Iustin Pop
    Returns:
1911 a2cfdea2 Iustin Pop
     (sync_percent, estimated_time)
1912 a2cfdea2 Iustin Pop

1913 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1914 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1915 a2cfdea2 Iustin Pop
    the time needed, otherwise it's the time left in seconds
1916 a2cfdea2 Iustin Pop

1917 a2cfdea2 Iustin Pop
    """
1918 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1919 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1920 a2cfdea2 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1921 a2cfdea2 Iustin Pop
    if self.minor not in proc_info:
1922 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1923 a2cfdea2 Iustin Pop
                                    self.minor)
1924 a2cfdea2 Iustin Pop
    line = proc_info[self.minor]
1925 a2cfdea2 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1926 a2cfdea2 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1927 a2cfdea2 Iustin Pop
    if match:
1928 a2cfdea2 Iustin Pop
      sync_percent = float(match.group(1))
1929 a2cfdea2 Iustin Pop
      hours = int(match.group(2))
1930 a2cfdea2 Iustin Pop
      minutes = int(match.group(3))
1931 a2cfdea2 Iustin Pop
      seconds = int(match.group(4))
1932 a2cfdea2 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1933 a2cfdea2 Iustin Pop
    else:
1934 a2cfdea2 Iustin Pop
      sync_percent = None
1935 a2cfdea2 Iustin Pop
      est_time = None
1936 a2cfdea2 Iustin Pop
    match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1937 a2cfdea2 Iustin Pop
    if not match:
1938 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1939 a2cfdea2 Iustin Pop
                                    self.minor)
1940 a2cfdea2 Iustin Pop
    client_state = match.group(1)
1941 a2cfdea2 Iustin Pop
    is_degraded = client_state != "Connected"
1942 a2cfdea2 Iustin Pop
    return sync_percent, est_time, is_degraded
1943 a2cfdea2 Iustin Pop
1944 a2cfdea2 Iustin Pop
  def GetStatus(self):
1945 a2cfdea2 Iustin Pop
    """Compute the status of the DRBD device
1946 a2cfdea2 Iustin Pop

1947 a2cfdea2 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1948 a2cfdea2 Iustin Pop

1949 a2cfdea2 Iustin Pop
    """
1950 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1951 a2cfdea2 Iustin Pop
      return self.STATUS_UNKNOWN
1952 a2cfdea2 Iustin Pop
1953 a2cfdea2 Iustin Pop
    data = self._GetProcData()
1954 a2cfdea2 Iustin Pop
    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
1955 a2cfdea2 Iustin Pop
                       self.minor)
1956 a2cfdea2 Iustin Pop
    for line in data:
1957 a2cfdea2 Iustin Pop
      mresult = match.match(line)
1958 a2cfdea2 Iustin Pop
      if mresult:
1959 a2cfdea2 Iustin Pop
        break
1960 a2cfdea2 Iustin Pop
    else:
1961 a2cfdea2 Iustin Pop
      logger.Error("Can't find myself!")
1962 a2cfdea2 Iustin Pop
      return self.STATUS_UNKNOWN
1963 a2cfdea2 Iustin Pop
1964 a2cfdea2 Iustin Pop
    state = mresult.group(2)
1965 a2cfdea2 Iustin Pop
    if state == "Primary":
1966 a2cfdea2 Iustin Pop
      result = self.STATUS_ONLINE
1967 a2cfdea2 Iustin Pop
    else:
1968 a2cfdea2 Iustin Pop
      result = self.STATUS_STANDBY
1969 a2cfdea2 Iustin Pop
1970 a2cfdea2 Iustin Pop
    return result
1971 a2cfdea2 Iustin Pop
1972 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1973 a2cfdea2 Iustin Pop
    """Make the local state primary.
1974 a2cfdea2 Iustin Pop

1975 a2cfdea2 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1976 a2cfdea2 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1977 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1978 a2cfdea2 Iustin Pop
    has to be given.
1979 a2cfdea2 Iustin Pop

1980 a2cfdea2 Iustin Pop
    """
1981 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1982 a2cfdea2 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1983 a2cfdea2 Iustin Pop
      return False
1984 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1985 a2cfdea2 Iustin Pop
    if force:
1986 a2cfdea2 Iustin Pop
      cmd.append("-o")
1987 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1988 a2cfdea2 Iustin Pop
    if result.failed:
1989 a2cfdea2 Iustin Pop
      logger.Error("Can't make drbd device primary: %s" % result.output)
1990 a2cfdea2 Iustin Pop
      return False
1991 a2cfdea2 Iustin Pop
    return True
1992 a2cfdea2 Iustin Pop
1993 a2cfdea2 Iustin Pop
  def Close(self):
1994 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1995 a2cfdea2 Iustin Pop

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

1998 a2cfdea2 Iustin Pop
    """
1999 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2000 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
2001 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
2002 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
2003 a2cfdea2 Iustin Pop
    if result.failed:
2004 a2cfdea2 Iustin Pop
      logger.Error("Can't switch drbd device to secondary: %s" % result.output)
2005 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't switch drbd device to secondary")
2006 a2cfdea2 Iustin Pop
2007 a2cfdea2 Iustin Pop
  def Attach(self):
2008 a2cfdea2 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
2009 a2cfdea2 Iustin Pop

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

2014 a2cfdea2 Iustin Pop
    """
2015 a2cfdea2 Iustin Pop
    for minor in self._GetUsedDevs():
2016 a2cfdea2 Iustin Pop
      info = self._GetDevInfo(minor)
2017 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
2018 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
2019 a2cfdea2 Iustin Pop
      if match_l and match_r:
2020 a2cfdea2 Iustin Pop
        break
2021 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
2022 a2cfdea2 Iustin Pop
        res_r = self._AssembleNet(minor,
2023 a2cfdea2 Iustin Pop
                                  (self._lhost, self._lport,
2024 a2cfdea2 Iustin Pop
                                   self._rhost, self._rport),
2025 a2cfdea2 Iustin Pop
                                  "C")
2026 a2cfdea2 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
2027 a2cfdea2 Iustin Pop
          break
2028 a2cfdea2 Iustin Pop
    else:
2029 a2cfdea2 Iustin Pop
      minor = None
2030 a2cfdea2 Iustin Pop
2031 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2032 a2cfdea2 Iustin Pop
    return minor is not None
2033 a2cfdea2 Iustin Pop
2034 a2cfdea2 Iustin Pop
  def Assemble(self):
2035 a2cfdea2 Iustin Pop
    """Assemble the drbd.
2036 a2cfdea2 Iustin Pop

2037 a2cfdea2 Iustin Pop
    Method:
2038 a2cfdea2 Iustin Pop
      - if we have a local backing device, we bind to it by:
2039 a2cfdea2 Iustin Pop
        - checking the list of used drbd devices
2040 a2cfdea2 Iustin Pop
        - check if the local minor use of any of them is our own device
2041 a2cfdea2 Iustin Pop
        - if yes, abort?
2042 a2cfdea2 Iustin Pop
        - if not, bind
2043 a2cfdea2 Iustin Pop
      - if we have a local/remote net info:
2044 a2cfdea2 Iustin Pop
        - redo the local backing device step for the remote device
2045 a2cfdea2 Iustin Pop
        - check if any drbd device is using the local port,
2046 a2cfdea2 Iustin Pop
          if yes abort
2047 a2cfdea2 Iustin Pop
        - check if any remote drbd device is using the remote
2048 a2cfdea2 Iustin Pop
          port, if yes abort (for now)
2049 a2cfdea2 Iustin Pop
        - bind our net port
2050 a2cfdea2 Iustin Pop
        - bind the remote net port
2051 a2cfdea2 Iustin Pop

2052 a2cfdea2 Iustin Pop
    """
2053 a2cfdea2 Iustin Pop
    self.Attach()
2054 a2cfdea2 Iustin Pop
    if self.minor is not None:
2055 a2cfdea2 Iustin Pop
      logger.Info("Already assembled")
2056 a2cfdea2 Iustin Pop
      return True
2057 a2cfdea2 Iustin Pop
2058 a2cfdea2 Iustin Pop
    result = super(DRBD8, self).Assemble()
2059 a2cfdea2 Iustin Pop
    if not result:
2060 a2cfdea2 Iustin Pop
      return result
2061 a2cfdea2 Iustin Pop
2062 a2cfdea2 Iustin Pop
    minor = self._FindUnusedMinor()
2063 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
2064 a2cfdea2 Iustin Pop
    if self._children[0]:
2065 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
2066 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
2067 a2cfdea2 Iustin Pop
      if not result:
2068 a2cfdea2 Iustin Pop
        return False
2069 a2cfdea2 Iustin Pop
      need_localdev_teardown = True
2070 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
2071 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
2072 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
2073 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
2074 a2cfdea2 Iustin Pop
                                 "C")
2075 a2cfdea2 Iustin Pop
      if not result:
2076 a2cfdea2 Iustin Pop
        if need_localdev_teardown:
2077 a2cfdea2 Iustin Pop
          # we will ignore failures from this
2078 a2cfdea2 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
2079 a2cfdea2 Iustin Pop
          self._ShutdownAll(minor)
2080 a2cfdea2 Iustin Pop
        return False
2081 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2082 a2cfdea2 Iustin Pop
    return True
2083 a2cfdea2 Iustin Pop
2084 a2cfdea2 Iustin Pop
  @classmethod
2085 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
2086 b00b95dd Iustin Pop
    """Detach from the local device.
2087 b00b95dd Iustin Pop

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

2091 b00b95dd Iustin Pop
    """
2092 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2093 b00b95dd Iustin Pop
    if result.failed:
2094 b00b95dd Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
2095 b00b95dd Iustin Pop
    return not result.failed
2096 b00b95dd Iustin Pop
2097 b00b95dd Iustin Pop
  @classmethod
2098 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2099 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2100 f3e513ad Iustin Pop

2101 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2102 f3e513ad Iustin Pop

2103 f3e513ad Iustin Pop
    """
2104 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2105 f3e513ad Iustin Pop
    logger.Error("Can't shutdown network: %s" % result.output)
2106 f3e513ad Iustin Pop
    return not result.failed
2107 f3e513ad Iustin Pop
2108 f3e513ad Iustin Pop
  @classmethod
2109 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2110 a2cfdea2 Iustin Pop
    """Deactivate the device.
2111 a2cfdea2 Iustin Pop

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

2114 a2cfdea2 Iustin Pop
    """
2115 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2116 a2cfdea2 Iustin Pop
    if result.failed:
2117 a2cfdea2 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
2118 a2cfdea2 Iustin Pop
    return not result.failed
2119 a2cfdea2 Iustin Pop
2120 a2cfdea2 Iustin Pop
  def Shutdown(self):
2121 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2122 a2cfdea2 Iustin Pop

2123 a2cfdea2 Iustin Pop
    """
2124 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2125 a2cfdea2 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
2126 a2cfdea2 Iustin Pop
      return True
2127 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
2128 a2cfdea2 Iustin Pop
      return False
2129 a2cfdea2 Iustin Pop
    self.minor = None
2130 a2cfdea2 Iustin Pop
    self.dev_path = None
2131 a2cfdea2 Iustin Pop
    return True
2132 a2cfdea2 Iustin Pop
2133 f3e513ad Iustin Pop
  def Rename(self, new_uid):
2134 f3e513ad Iustin Pop
    """Re-connect this device to another peer.
2135 f3e513ad Iustin Pop

2136 f3e513ad Iustin Pop
    """
2137 f3e513ad Iustin Pop
    if self.minor is None:
2138 f3e513ad Iustin Pop
      raise errors.BlockDeviceError("Device not attached during rename")
2139 f3e513ad Iustin Pop
    if self._rhost is not None:
2140 f3e513ad Iustin Pop
      # this means we did have a host when we attached, so we are connected
2141 f3e513ad Iustin Pop
      if not self._ShutdownNet(self.minor):
2142 f3e513ad Iustin Pop
        raise errors.BlockDeviceError("Can't disconnect from remote peer")
2143 f3e513ad Iustin Pop
      old_id = self.unique_id
2144 f3e513ad Iustin Pop
    else:
2145 f3e513ad Iustin Pop
      old_id = None
2146 f3e513ad Iustin Pop
    self.unique_id = new_uid
2147 f3e513ad Iustin Pop
    if not self._AssembleNet(self.minor, self.unique_id, "C"):
2148 f3e513ad Iustin Pop
      logger.Error("Can't attach to new peer!")
2149 f3e513ad Iustin Pop
      if self.old_id is not None:
2150 f3e513ad Iustin Pop
        self._AssembleNet(self.minor, old_id, "C")
2151 f3e513ad Iustin Pop
      self.unique_id = old_id
2152 f3e513ad Iustin Pop
      raise errors.BlockDeviceError("Can't attach to new peer")
2153 f3e513ad Iustin Pop
2154 a2cfdea2 Iustin Pop
  def Remove(self):
2155 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2156 a2cfdea2 Iustin Pop

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

2164 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2165 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2166 a2cfdea2 Iustin Pop

2167 a2cfdea2 Iustin Pop
    """
2168 a2cfdea2 Iustin Pop
    if len(children) != 2:
2169 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2170 a2cfdea2 Iustin Pop
    meta = children[1]
2171 a2cfdea2 Iustin Pop
    meta.Assemble()
2172 a2cfdea2 Iustin Pop
    if not meta.Attach():
2173 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
2174 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
2175 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
2176 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
2177 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
2178 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
2179 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
2180 a2cfdea2 Iustin Pop
2181 a8083063 Iustin Pop
2182 a8083063 Iustin Pop
DEV_MAP = {
2183 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2184 fe96220b Iustin Pop
  constants.LD_MD_R1: MDRaid1,
2185 fe96220b Iustin Pop
  constants.LD_DRBD7: DRBDev,
2186 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2187 a8083063 Iustin Pop
  }
2188 a8083063 Iustin Pop
2189 a8083063 Iustin Pop
2190 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
2191 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2192 a8083063 Iustin Pop

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

2196 a8083063 Iustin Pop
  """
2197 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2198 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2199 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2200 a8083063 Iustin Pop
  if not device.Attach():
2201 a8083063 Iustin Pop
    return None
2202 a8083063 Iustin Pop
  return  device
2203 a8083063 Iustin Pop
2204 a8083063 Iustin Pop
2205 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
2206 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2207 a8083063 Iustin Pop

2208 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
2209 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
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
    device.Assemble()
2217 a8083063 Iustin Pop
  if not device.Attach():
2218 a8083063 Iustin Pop
    raise errors.BlockDeviceError("Can't find a valid block device for"
2219 a8083063 Iustin Pop
                                  " %s/%s/%s" %
2220 a8083063 Iustin Pop
                                  (dev_type, unique_id, children))
2221 a8083063 Iustin Pop
  return device
2222 a8083063 Iustin Pop
2223 a8083063 Iustin Pop
2224 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2225 a8083063 Iustin Pop
  """Create a device.
2226 a8083063 Iustin Pop

2227 a8083063 Iustin Pop
  """
2228 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2229 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2230 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2231 a8083063 Iustin Pop
  return device