Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 1a8c0ce1

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
294 a8083063 Iustin Pop
  @classmethod
295 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
296 a8083063 Iustin Pop
    """Create a new logical volume.
297 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

455 a8083063 Iustin Pop
    This is a no-op for the LV device type.
456 a8083063 Iustin Pop

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

463 a8083063 Iustin Pop
    This is a no-op for the LV device type.
464 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

567 a8083063 Iustin Pop
    Currently only uuid is returned.
568 a8083063 Iustin Pop

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

589 a8083063 Iustin Pop
    This code assumes that there are 256 minors only.
590 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

704 f3e513ad Iustin Pop
    This is not supported for md raid1 devices.
705 f3e513ad Iustin Pop

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

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

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

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

788 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
789 a8083063 Iustin Pop

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

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

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

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

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

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

866 a8083063 Iustin Pop
    Returns:
867 a8083063 Iustin Pop
     (sync_percent, estimated_time)
868 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1020 5a47ad20 Iustin Pop
    This sets our minor variable and our dev_path.
1021 5a47ad20 Iustin Pop

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

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

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

1058 f3e513ad Iustin Pop
    This is not supported for drbd devices.
1059 f3e513ad Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1295 a8083063 Iustin Pop
    This fails if we don't have a local device.
1296 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

1444 a8083063 Iustin Pop
    Returns:
1445 a8083063 Iustin Pop
     (sync_percent, estimated_time)
1446 a8083063 Iustin Pop

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

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

1481 a8083063 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1482 a8083063 Iustin Pop

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

1510 a8083063 Iustin Pop
    This writes until we get ENOSPC.
1511 a8083063 Iustin Pop

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

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

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

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

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

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

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

1586 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1587 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1908 a2cfdea2 Iustin Pop
    Returns:
1909 a2cfdea2 Iustin Pop
     (sync_percent, estimated_time)
1910 a2cfdea2 Iustin Pop

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

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

1945 a2cfdea2 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1946 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

2099 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2100 f3e513ad Iustin Pop

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

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

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

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

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

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

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

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

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

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

2206 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
2207 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
2208 a8083063 Iustin Pop

2209 a8083063 Iustin Pop
  """
2210 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2211 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2212 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2213 a8083063 Iustin Pop
  if not device.Attach():
2214 a8083063 Iustin Pop
    device.Assemble()
2215 a8083063 Iustin Pop
  if not device.Attach():
2216 a8083063 Iustin Pop
    raise errors.BlockDeviceError("Can't find a valid block device for"
2217 a8083063 Iustin Pop
                                  " %s/%s/%s" %
2218 a8083063 Iustin Pop
                                  (dev_type, unique_id, children))
2219 a8083063 Iustin Pop
  return device
2220 a8083063 Iustin Pop
2221 a8083063 Iustin Pop
2222 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2223 a8083063 Iustin Pop
  """Create a device.
2224 a8083063 Iustin Pop

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