Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ b00b95dd

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
98 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
99 a8083063 Iustin Pop
    self._children = children
100 a8083063 Iustin Pop
    self.dev_path = None
101 a8083063 Iustin Pop
    self.unique_id = unique_id
102 a8083063 Iustin Pop
    self.major = None
103 a8083063 Iustin Pop
    self.minor = None
104 a8083063 Iustin Pop
105 a8083063 Iustin Pop
106 a8083063 Iustin Pop
  def Assemble(self):
107 a8083063 Iustin Pop
    """Assemble the device from its components.
108 a8083063 Iustin Pop

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

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

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

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

145 a8083063 Iustin Pop
    """
146 a8083063 Iustin Pop
    raise NotImplementedError
147 a8083063 Iustin Pop
148 a8083063 Iustin Pop
149 a8083063 Iustin Pop
  @classmethod
150 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
151 a8083063 Iustin Pop
    """Create the device.
152 a8083063 Iustin Pop

153 a8083063 Iustin Pop
    If the device cannot be created, it will return None
154 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
155 a8083063 Iustin Pop

156 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
157 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
158 a8083063 Iustin Pop
    enough for both creation and assembly (later).
159 a8083063 Iustin Pop

160 a8083063 Iustin Pop
    """
161 a8083063 Iustin Pop
    raise NotImplementedError
162 a8083063 Iustin Pop
163 a8083063 Iustin Pop
164 a8083063 Iustin Pop
  def Remove(self):
165 a8083063 Iustin Pop
    """Remove this device.
166 a8083063 Iustin Pop

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

171 a8083063 Iustin Pop
    """
172 a8083063 Iustin Pop
    raise NotImplementedError
173 a8083063 Iustin Pop
174 a8083063 Iustin Pop
175 f3e513ad Iustin Pop
  def Rename(self, new_id):
176 f3e513ad Iustin Pop
    """Rename this device.
177 f3e513ad Iustin Pop

178 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
179 f3e513ad Iustin Pop

180 f3e513ad Iustin Pop
    """
181 f3e513ad Iustin Pop
    raise NotImplementedError
182 f3e513ad Iustin Pop
183 f3e513ad Iustin Pop
184 a8083063 Iustin Pop
  def GetStatus(self):
185 a8083063 Iustin Pop
    """Return the status of the device.
186 a8083063 Iustin Pop

187 a8083063 Iustin Pop
    """
188 a8083063 Iustin Pop
    raise NotImplementedError
189 a8083063 Iustin Pop
190 a8083063 Iustin Pop
191 a8083063 Iustin Pop
  def Open(self, force=False):
192 a8083063 Iustin Pop
    """Make the device ready for use.
193 a8083063 Iustin Pop

194 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
195 a8083063 Iustin Pop
    devices need this.
196 a8083063 Iustin Pop

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

200 a8083063 Iustin Pop
    """
201 a8083063 Iustin Pop
    raise NotImplementedError
202 a8083063 Iustin Pop
203 a8083063 Iustin Pop
204 a8083063 Iustin Pop
  def Shutdown(self):
205 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
206 a8083063 Iustin Pop

207 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
208 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
209 a8083063 Iustin Pop
    assembled after this call.
210 a8083063 Iustin Pop

211 a8083063 Iustin Pop
    """
212 a8083063 Iustin Pop
    raise NotImplementedError
213 a8083063 Iustin Pop
214 a8083063 Iustin Pop
215 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
216 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
217 a8083063 Iustin Pop

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

220 a8083063 Iustin Pop
    """
221 a8083063 Iustin Pop
    result = True
222 a8083063 Iustin Pop
    if self._children:
223 a8083063 Iustin Pop
      for child in self._children:
224 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
225 a8083063 Iustin Pop
    return result
226 a8083063 Iustin Pop
227 a8083063 Iustin Pop
228 a8083063 Iustin Pop
  def GetSyncStatus(self):
229 a8083063 Iustin Pop
    """Returns the sync status of the device.
230 a8083063 Iustin Pop

231 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
232 a8083063 Iustin Pop
    status of the mirror.
233 a8083063 Iustin Pop

234 a8083063 Iustin Pop
    Returns:
235 a8083063 Iustin Pop
     (sync_percent, estimated_time, is_degraded)
236 a8083063 Iustin Pop

237 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
238 a8083063 Iustin Pop
    If estimated_time is None, it means we can't estimate
239 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
240 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
241 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
242 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
243 a8083063 Iustin Pop

244 a8083063 Iustin Pop
    """
245 a8083063 Iustin Pop
    return None, None, False
246 a8083063 Iustin Pop
247 a8083063 Iustin Pop
248 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
249 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
250 a8083063 Iustin Pop

251 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
252 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
253 a8083063 Iustin Pop
    children.
254 a8083063 Iustin Pop

255 a8083063 Iustin Pop
    """
256 a8083063 Iustin Pop
    min_percent, max_time, is_degraded = self.GetSyncStatus()
257 a8083063 Iustin Pop
    if self._children:
258 a8083063 Iustin Pop
      for child in self._children:
259 a8083063 Iustin Pop
        c_percent, c_time, c_degraded = child.GetSyncStatus()
260 a8083063 Iustin Pop
        if min_percent is None:
261 a8083063 Iustin Pop
          min_percent = c_percent
262 a8083063 Iustin Pop
        elif c_percent is not None:
263 a8083063 Iustin Pop
          min_percent = min(min_percent, c_percent)
264 a8083063 Iustin Pop
        if max_time is None:
265 a8083063 Iustin Pop
          max_time = c_time
266 a8083063 Iustin Pop
        elif c_time is not None:
267 a8083063 Iustin Pop
          max_time = max(max_time, c_time)
268 a8083063 Iustin Pop
        is_degraded = is_degraded or c_degraded
269 a8083063 Iustin Pop
    return min_percent, max_time, is_degraded
270 a8083063 Iustin Pop
271 a8083063 Iustin Pop
272 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
273 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
274 a0c3fea1 Michael Hanselmann

275 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
276 a0c3fea1 Michael Hanselmann

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

291 a8083063 Iustin Pop
  """
292 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
293 a8083063 Iustin Pop
    """Attaches to a LV device.
294 a8083063 Iustin Pop

295 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
296 a8083063 Iustin Pop

297 a8083063 Iustin Pop
    """
298 a8083063 Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children)
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
    self._vg_name, self._lv_name = unique_id
302 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
303 a8083063 Iustin Pop
    self.Attach()
304 a8083063 Iustin Pop
305 a8083063 Iustin Pop
306 a8083063 Iustin Pop
  @classmethod
307 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
308 a8083063 Iustin Pop
    """Create a new logical volume.
309 a8083063 Iustin Pop

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

339 a8083063 Iustin Pop
    Args:
340 a8083063 Iustin Pop
      vg_name: the volume group name
341 a8083063 Iustin Pop

342 a8083063 Iustin Pop
    Returns:
343 a8083063 Iustin Pop
      list of (free_space, name) with free_space in mebibytes
344 098c0958 Michael Hanselmann

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

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

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

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

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

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

425 a8083063 Iustin Pop
    """
426 a8083063 Iustin Pop
    return True
427 a8083063 Iustin Pop
428 a8083063 Iustin Pop
429 a8083063 Iustin Pop
  def Shutdown(self):
430 a8083063 Iustin Pop
    """Shutdown the device.
431 a8083063 Iustin Pop

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

435 a8083063 Iustin Pop
    """
436 a8083063 Iustin Pop
    return True
437 a8083063 Iustin Pop
438 a8083063 Iustin Pop
439 a8083063 Iustin Pop
  def GetStatus(self):
440 a8083063 Iustin Pop
    """Return the status of the device.
441 a8083063 Iustin Pop

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

446 a8083063 Iustin Pop
    """
447 a8083063 Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
448 a8083063 Iustin Pop
    if result.failed:
449 a8083063 Iustin Pop
      logger.Error("Can't display lv: %s" % result.fail_reason)
450 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
451 a8083063 Iustin Pop
    out = result.stdout.strip()
452 a8083063 Iustin Pop
    # format: type/permissions/alloc/fixed_minor/state/open
453 a8083063 Iustin Pop
    if len(out) != 6:
454 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
455 a8083063 Iustin Pop
    #writable = (out[1] == "w")
456 a8083063 Iustin Pop
    active = (out[4] == "a")
457 a8083063 Iustin Pop
    online = (out[5] == "o")
458 a8083063 Iustin Pop
    if online:
459 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
460 a8083063 Iustin Pop
    elif active:
461 a8083063 Iustin Pop
      retval = self.STATUS_STANDBY
462 a8083063 Iustin Pop
    else:
463 a8083063 Iustin Pop
      retval = self.STATUS_EXISTING
464 a8083063 Iustin Pop
465 a8083063 Iustin Pop
    return retval
466 a8083063 Iustin Pop
467 a8083063 Iustin Pop
468 a8083063 Iustin Pop
  def Open(self, force=False):
469 a8083063 Iustin Pop
    """Make the device ready for I/O.
470 a8083063 Iustin Pop

471 a8083063 Iustin Pop
    This is a no-op for the LV device type.
472 a8083063 Iustin Pop

473 a8083063 Iustin Pop
    """
474 a8083063 Iustin Pop
    return True
475 a8083063 Iustin Pop
476 a8083063 Iustin Pop
477 a8083063 Iustin Pop
  def Close(self):
478 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
479 a8083063 Iustin Pop

480 a8083063 Iustin Pop
    This is a no-op for the LV device type.
481 a8083063 Iustin Pop

482 a8083063 Iustin Pop
    """
483 a8083063 Iustin Pop
    return True
484 a8083063 Iustin Pop
485 a8083063 Iustin Pop
486 a8083063 Iustin Pop
  def Snapshot(self, size):
487 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
488 a8083063 Iustin Pop

489 a8083063 Iustin Pop
    """
490 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
491 a8083063 Iustin Pop
492 a8083063 Iustin Pop
    # remove existing snapshot if found
493 a8083063 Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None)
494 a8083063 Iustin Pop
    snap.Remove()
495 a8083063 Iustin Pop
496 a8083063 Iustin Pop
    pvs_info = self.GetPVInfo(self._vg_name)
497 a8083063 Iustin Pop
    if not pvs_info:
498 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
499 3ecf6786 Iustin Pop
                                    self._vg_name)
500 a8083063 Iustin Pop
    pvs_info.sort()
501 a8083063 Iustin Pop
    pvs_info.reverse()
502 a8083063 Iustin Pop
    free_size, pv_name = pvs_info[0]
503 a8083063 Iustin Pop
    if free_size < size:
504 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Not enough free space: required %s,"
505 3ecf6786 Iustin Pop
                                    " available %s" % (size, free_size))
506 a8083063 Iustin Pop
507 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
508 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
509 a8083063 Iustin Pop
    if result.failed:
510 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("command: %s error: %s" %
511 3ecf6786 Iustin Pop
                                    (result.cmd, result.fail_reason))
512 a8083063 Iustin Pop
513 a8083063 Iustin Pop
    return snap_name
514 a8083063 Iustin Pop
515 a8083063 Iustin Pop
516 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
517 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
518 a0c3fea1 Michael Hanselmann

519 a0c3fea1 Michael Hanselmann
    """
520 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
521 a0c3fea1 Michael Hanselmann
522 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
523 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
524 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
525 a0c3fea1 Michael Hanselmann
526 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
527 a0c3fea1 Michael Hanselmann
    text = text[:128]
528 a0c3fea1 Michael Hanselmann
529 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
530 a0c3fea1 Michael Hanselmann
                           self.dev_path])
531 a0c3fea1 Michael Hanselmann
    if result.failed:
532 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Command: %s error: %s" %
533 3ecf6786 Iustin Pop
                                    (result.cmd, result.fail_reason))
534 a0c3fea1 Michael Hanselmann
535 a0c3fea1 Michael Hanselmann
536 a8083063 Iustin Pop
class MDRaid1(BlockDev):
537 a8083063 Iustin Pop
  """raid1 device implemented via md.
538 a8083063 Iustin Pop

539 a8083063 Iustin Pop
  """
540 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
541 a8083063 Iustin Pop
    super(MDRaid1, self).__init__(unique_id, children)
542 a8083063 Iustin Pop
    self.major = 9
543 a8083063 Iustin Pop
    self.Attach()
544 a8083063 Iustin Pop
545 a8083063 Iustin Pop
546 a8083063 Iustin Pop
  def Attach(self):
547 a8083063 Iustin Pop
    """Find an array which matches our config and attach to it.
548 a8083063 Iustin Pop

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

551 a8083063 Iustin Pop
    """
552 a8083063 Iustin Pop
    minor = self._FindMDByUUID(self.unique_id)
553 a8083063 Iustin Pop
    if minor is not None:
554 a8083063 Iustin Pop
      self._SetFromMinor(minor)
555 a8083063 Iustin Pop
    else:
556 a8083063 Iustin Pop
      self.minor = None
557 a8083063 Iustin Pop
      self.dev_path = None
558 a8083063 Iustin Pop
559 a8083063 Iustin Pop
    return (minor is not None)
560 a8083063 Iustin Pop
561 a8083063 Iustin Pop
562 a8083063 Iustin Pop
  @staticmethod
563 a8083063 Iustin Pop
  def _GetUsedDevs():
564 a8083063 Iustin Pop
    """Compute the list of in-use MD devices.
565 a8083063 Iustin Pop

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

569 a8083063 Iustin Pop
    """
570 a8083063 Iustin Pop
    mdstat = open("/proc/mdstat", "r")
571 a8083063 Iustin Pop
    data = mdstat.readlines()
572 a8083063 Iustin Pop
    mdstat.close()
573 a8083063 Iustin Pop
574 a8083063 Iustin Pop
    used_md = {}
575 a8083063 Iustin Pop
    valid_line = re.compile("^md([0-9]+) : .*$")
576 a8083063 Iustin Pop
    for line in data:
577 a8083063 Iustin Pop
      match = valid_line.match(line)
578 a8083063 Iustin Pop
      if match:
579 a8083063 Iustin Pop
        md_no = int(match.group(1))
580 a8083063 Iustin Pop
        used_md[md_no] = line
581 a8083063 Iustin Pop
582 a8083063 Iustin Pop
    return used_md
583 a8083063 Iustin Pop
584 a8083063 Iustin Pop
585 a8083063 Iustin Pop
  @staticmethod
586 a8083063 Iustin Pop
  def _GetDevInfo(minor):
587 a8083063 Iustin Pop
    """Get info about a MD device.
588 a8083063 Iustin Pop

589 a8083063 Iustin Pop
    Currently only uuid is returned.
590 a8083063 Iustin Pop

591 a8083063 Iustin Pop
    """
592 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-D", "/dev/md%d" % minor])
593 a8083063 Iustin Pop
    if result.failed:
594 a8083063 Iustin Pop
      logger.Error("Can't display md: %s" % result.fail_reason)
595 a8083063 Iustin Pop
      return None
596 a8083063 Iustin Pop
    retval = {}
597 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
598 a8083063 Iustin Pop
      line = line.strip()
599 a8083063 Iustin Pop
      kv = line.split(" : ", 1)
600 a8083063 Iustin Pop
      if kv:
601 a8083063 Iustin Pop
        if kv[0] == "UUID":
602 8d519422 Iustin Pop
          retval["uuid"] = kv[1].split()[0]
603 a8083063 Iustin Pop
        elif kv[0] == "State":
604 a8083063 Iustin Pop
          retval["state"] = kv[1].split(", ")
605 a8083063 Iustin Pop
    return retval
606 a8083063 Iustin Pop
607 a8083063 Iustin Pop
608 a8083063 Iustin Pop
  @staticmethod
609 a8083063 Iustin Pop
  def _FindUnusedMinor():
610 a8083063 Iustin Pop
    """Compute an unused MD minor.
611 a8083063 Iustin Pop

612 a8083063 Iustin Pop
    This code assumes that there are 256 minors only.
613 a8083063 Iustin Pop

614 a8083063 Iustin Pop
    """
615 a8083063 Iustin Pop
    used_md = MDRaid1._GetUsedDevs()
616 a8083063 Iustin Pop
    i = 0
617 a8083063 Iustin Pop
    while i < 256:
618 a8083063 Iustin Pop
      if i not in used_md:
619 a8083063 Iustin Pop
        break
620 a8083063 Iustin Pop
      i += 1
621 a8083063 Iustin Pop
    if i == 256:
622 a8083063 Iustin Pop
      logger.Error("Critical: Out of md minor numbers.")
623 0caf6485 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free MD minor")
624 a8083063 Iustin Pop
    return i
625 a8083063 Iustin Pop
626 a8083063 Iustin Pop
627 a8083063 Iustin Pop
  @classmethod
628 a8083063 Iustin Pop
  def _FindMDByUUID(cls, uuid):
629 a8083063 Iustin Pop
    """Find the minor of an MD array with a given UUID.
630 a8083063 Iustin Pop

631 a8083063 Iustin Pop
    """
632 a8083063 Iustin Pop
    md_list = cls._GetUsedDevs()
633 a8083063 Iustin Pop
    for minor in md_list:
634 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
635 a8083063 Iustin Pop
      if info and info["uuid"] == uuid:
636 a8083063 Iustin Pop
        return minor
637 a8083063 Iustin Pop
    return None
638 a8083063 Iustin Pop
639 a8083063 Iustin Pop
640 1a87dca7 Iustin Pop
  @staticmethod
641 1a87dca7 Iustin Pop
  def _ZeroSuperblock(dev_path):
642 1a87dca7 Iustin Pop
    """Zero the possible locations for an MD superblock.
643 1a87dca7 Iustin Pop

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

648 1a87dca7 Iustin Pop
    The superblocks are located at (negative values are relative to
649 1a87dca7 Iustin Pop
    the end of the block device):
650 1a87dca7 Iustin Pop
      - -128k to end for version 0.90 superblock
651 1a87dca7 Iustin Pop
      - -8k to -12k for version 1.0 superblock (included in the above)
652 1a87dca7 Iustin Pop
      - 0k to 4k for version 1.1 superblock
653 1a87dca7 Iustin Pop
      - 4k to 8k for version 1.2 superblock
654 1a87dca7 Iustin Pop

655 1a87dca7 Iustin Pop
    To cover all situations, the zero-ing will be:
656 1a87dca7 Iustin Pop
      - 0k to 128k
657 1a87dca7 Iustin Pop
      - -128k to end
658 1a87dca7 Iustin Pop

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

662 1a87dca7 Iustin Pop
    Note that this function depends on the fact that one can open,
663 1a87dca7 Iustin Pop
    read and write block devices normally.
664 1a87dca7 Iustin Pop

665 1a87dca7 Iustin Pop
    """
666 1a87dca7 Iustin Pop
    overwrite_size = 128 * 1024
667 1a87dca7 Iustin Pop
    empty_buf = '\0' * overwrite_size
668 1a87dca7 Iustin Pop
    fd = open(dev_path, "r+")
669 1a87dca7 Iustin Pop
    try:
670 1a87dca7 Iustin Pop
      fd.seek(0, 0)
671 1a87dca7 Iustin Pop
      p1 = fd.tell()
672 1a87dca7 Iustin Pop
      fd.write(empty_buf)
673 1a87dca7 Iustin Pop
      p2 = fd.tell()
674 1a87dca7 Iustin Pop
      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
675 1a87dca7 Iustin Pop
      fd.seek(-overwrite_size, 2)
676 1a87dca7 Iustin Pop
      p1 = fd.tell()
677 1a87dca7 Iustin Pop
      fd.write(empty_buf)
678 1a87dca7 Iustin Pop
      p2 = fd.tell()
679 1a87dca7 Iustin Pop
      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
680 1a87dca7 Iustin Pop
    finally:
681 1a87dca7 Iustin Pop
      fd.close()
682 1a87dca7 Iustin Pop
683 a8083063 Iustin Pop
  @classmethod
684 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
685 a8083063 Iustin Pop
    """Create a new MD raid1 array.
686 a8083063 Iustin Pop

687 a8083063 Iustin Pop
    """
688 a8083063 Iustin Pop
    if not isinstance(children, (tuple, list)):
689 a8083063 Iustin Pop
      raise ValueError("Invalid setup data for MDRaid1 dev: %s" %
690 a8083063 Iustin Pop
                       str(children))
691 a8083063 Iustin Pop
    for i in children:
692 a8083063 Iustin Pop
      if not isinstance(i, BlockDev):
693 a8083063 Iustin Pop
        raise ValueError("Invalid member in MDRaid1 dev: %s" % type(i))
694 a8083063 Iustin Pop
    for i in children:
695 1a87dca7 Iustin Pop
      try:
696 1a87dca7 Iustin Pop
        cls._ZeroSuperblock(i.dev_path)
697 1a87dca7 Iustin Pop
      except EnvironmentError, err:
698 1a87dca7 Iustin Pop
        logger.Error("Can't zero superblock for %s: %s" %
699 1a87dca7 Iustin Pop
                     (i.dev_path, str(err)))
700 a8083063 Iustin Pop
        return None
701 a8083063 Iustin Pop
    minor = cls._FindUnusedMinor()
702 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--create", "/dev/md%d" % minor,
703 a8083063 Iustin Pop
                           "--auto=yes", "--force", "-l1",
704 a8083063 Iustin Pop
                           "-n%d" % len(children)] +
705 a8083063 Iustin Pop
                          [dev.dev_path for dev in children])
706 a8083063 Iustin Pop
707 a8083063 Iustin Pop
    if result.failed:
708 1a87dca7 Iustin Pop
      logger.Error("Can't create md: %s: %s" % (result.fail_reason,
709 1a87dca7 Iustin Pop
                                                result.output))
710 a8083063 Iustin Pop
      return None
711 a8083063 Iustin Pop
    info = cls._GetDevInfo(minor)
712 a8083063 Iustin Pop
    if not info or not "uuid" in info:
713 a8083063 Iustin Pop
      logger.Error("Wrong information returned from mdadm -D: %s" % str(info))
714 a8083063 Iustin Pop
      return None
715 a8083063 Iustin Pop
    return MDRaid1(info["uuid"], children)
716 a8083063 Iustin Pop
717 a8083063 Iustin Pop
718 a8083063 Iustin Pop
  def Remove(self):
719 a8083063 Iustin Pop
    """Stub remove function for MD RAID 1 arrays.
720 a8083063 Iustin Pop

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

723 a8083063 Iustin Pop
    """
724 a8083063 Iustin Pop
    #TODO: maybe zero superblock on child devices?
725 a8083063 Iustin Pop
    return self.Shutdown()
726 a8083063 Iustin Pop
727 f3e513ad Iustin Pop
  def Rename(self, new_id):
728 f3e513ad Iustin Pop
    """Rename a device.
729 f3e513ad Iustin Pop

730 f3e513ad Iustin Pop
    This is not supported for md raid1 devices.
731 f3e513ad Iustin Pop

732 f3e513ad Iustin Pop
    """
733 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a md raid1 device")
734 a8083063 Iustin Pop
735 153d9724 Iustin Pop
  def AddChildren(self, devices):
736 153d9724 Iustin Pop
    """Add new member(s) to the md raid1.
737 a8083063 Iustin Pop

738 a8083063 Iustin Pop
    """
739 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
740 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device")
741 153d9724 Iustin Pop
742 153d9724 Iustin Pop
    args = ["mdadm", "-a", self.dev_path]
743 153d9724 Iustin Pop
    for dev in devices:
744 153d9724 Iustin Pop
      if dev.dev_path is None:
745 153d9724 Iustin Pop
        raise errors.BlockDeviceError("Child '%s' is not initialised" % dev)
746 153d9724 Iustin Pop
      dev.Open()
747 153d9724 Iustin Pop
      args.append(dev.dev_path)
748 153d9724 Iustin Pop
    result = utils.RunCmd(args)
749 a8083063 Iustin Pop
    if result.failed:
750 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Failed to add new device to array: %s" %
751 3ecf6786 Iustin Pop
                                    result.output)
752 153d9724 Iustin Pop
    new_len = len(self._children) + len(devices)
753 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", self.dev_path, "-n", new_len])
754 a8083063 Iustin Pop
    if result.failed:
755 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't grow md array: %s" %
756 3ecf6786 Iustin Pop
                                    result.output)
757 153d9724 Iustin Pop
    self._children.extend(devices)
758 a8083063 Iustin Pop
759 a8083063 Iustin Pop
760 153d9724 Iustin Pop
  def RemoveChildren(self, devices):
761 153d9724 Iustin Pop
    """Remove member(s) from the md raid1.
762 a8083063 Iustin Pop

763 a8083063 Iustin Pop
    """
764 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
765 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device")
766 153d9724 Iustin Pop
    new_len = len(self._children) - len(devices)
767 153d9724 Iustin Pop
    if new_len < 1:
768 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Can't reduce to less than one child")
769 153d9724 Iustin Pop
    args = ["mdadm", "-f", self.dev_path]
770 153d9724 Iustin Pop
    orig_devs = []
771 153d9724 Iustin Pop
    for dev in devices:
772 153d9724 Iustin Pop
      args.append(dev.dev_path)
773 153d9724 Iustin Pop
      for c in self._children:
774 153d9724 Iustin Pop
        if c.dev_path == dev.dev_path:
775 153d9724 Iustin Pop
          orig_devs.append(c)
776 153d9724 Iustin Pop
          break
777 153d9724 Iustin Pop
      else:
778 153d9724 Iustin Pop
        raise errors.BlockDeviceError("Can't find device '%s' for removal" %
779 153d9724 Iustin Pop
                                      dev)
780 153d9724 Iustin Pop
    result = utils.RunCmd(args)
781 a8083063 Iustin Pop
    if result.failed:
782 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Failed to mark device(s) as failed: %s" %
783 3ecf6786 Iustin Pop
                                    result.output)
784 a8083063 Iustin Pop
785 a8083063 Iustin Pop
    # it seems here we need a short delay for MD to update its
786 a8083063 Iustin Pop
    # superblocks
787 a8083063 Iustin Pop
    time.sleep(0.5)
788 153d9724 Iustin Pop
    args[1] = "-r"
789 153d9724 Iustin Pop
    result = utils.RunCmd(args)
790 a8083063 Iustin Pop
    if result.failed:
791 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Failed to remove device(s) from array:"
792 153d9724 Iustin Pop
                                    " %s" % result.output)
793 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", "--force", self.dev_path,
794 a8083063 Iustin Pop
                           "-n", new_len])
795 a8083063 Iustin Pop
    if result.failed:
796 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't shrink md array: %s" %
797 3ecf6786 Iustin Pop
                                    result.output)
798 153d9724 Iustin Pop
    for dev in orig_devs:
799 153d9724 Iustin Pop
      self._children.remove(dev)
800 a8083063 Iustin Pop
801 a8083063 Iustin Pop
802 a8083063 Iustin Pop
  def GetStatus(self):
803 a8083063 Iustin Pop
    """Return the status of the device.
804 a8083063 Iustin Pop

805 a8083063 Iustin Pop
    """
806 a8083063 Iustin Pop
    self.Attach()
807 a8083063 Iustin Pop
    if self.minor is None:
808 a8083063 Iustin Pop
      retval = self.STATUS_UNKNOWN
809 a8083063 Iustin Pop
    else:
810 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
811 a8083063 Iustin Pop
    return retval
812 a8083063 Iustin Pop
813 a8083063 Iustin Pop
814 a8083063 Iustin Pop
  def _SetFromMinor(self, minor):
815 a8083063 Iustin Pop
    """Set our parameters based on the given minor.
816 a8083063 Iustin Pop

817 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
818 a8083063 Iustin Pop

819 a8083063 Iustin Pop
    """
820 a8083063 Iustin Pop
    self.minor = minor
821 a8083063 Iustin Pop
    self.dev_path = "/dev/md%d" % minor
822 a8083063 Iustin Pop
823 a8083063 Iustin Pop
824 a8083063 Iustin Pop
  def Assemble(self):
825 a8083063 Iustin Pop
    """Assemble the MD device.
826 a8083063 Iustin Pop

827 a8083063 Iustin Pop
    At this point we should have:
828 a8083063 Iustin Pop
      - list of children devices
829 a8083063 Iustin Pop
      - uuid
830 a8083063 Iustin Pop

831 a8083063 Iustin Pop
    """
832 a8083063 Iustin Pop
    result = super(MDRaid1, self).Assemble()
833 a8083063 Iustin Pop
    if not result:
834 a8083063 Iustin Pop
      return result
835 a8083063 Iustin Pop
    md_list = self._GetUsedDevs()
836 a8083063 Iustin Pop
    for minor in md_list:
837 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
838 a8083063 Iustin Pop
      if info and info["uuid"] == self.unique_id:
839 a8083063 Iustin Pop
        self._SetFromMinor(minor)
840 a8083063 Iustin Pop
        logger.Info("MD array %s already started" % str(self))
841 a8083063 Iustin Pop
        return True
842 a8083063 Iustin Pop
    free_minor = self._FindUnusedMinor()
843 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-A", "--auto=yes", "--uuid",
844 a8083063 Iustin Pop
                           self.unique_id, "/dev/md%d" % free_minor] +
845 a8083063 Iustin Pop
                          [bdev.dev_path for bdev in self._children])
846 a8083063 Iustin Pop
    if result.failed:
847 8d519422 Iustin Pop
      logger.Error("Can't assemble MD array: %s: %s" %
848 8d519422 Iustin Pop
                   (result.fail_reason, result.output))
849 a8083063 Iustin Pop
      self.minor = None
850 a8083063 Iustin Pop
    else:
851 a8083063 Iustin Pop
      self.minor = free_minor
852 a8083063 Iustin Pop
    return not result.failed
853 a8083063 Iustin Pop
854 a8083063 Iustin Pop
855 a8083063 Iustin Pop
  def Shutdown(self):
856 a8083063 Iustin Pop
    """Tear down the MD array.
857 a8083063 Iustin Pop

858 a8083063 Iustin Pop
    This does a 'mdadm --stop' so after this command, the array is no
859 a8083063 Iustin Pop
    longer available.
860 a8083063 Iustin Pop

861 a8083063 Iustin Pop
    """
862 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
863 a8083063 Iustin Pop
      logger.Info("MD object not attached to a device")
864 a8083063 Iustin Pop
      return True
865 a8083063 Iustin Pop
866 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--stop", "/dev/md%d" % self.minor])
867 a8083063 Iustin Pop
    if result.failed:
868 a8083063 Iustin Pop
      logger.Error("Can't stop MD array: %s" % result.fail_reason)
869 a8083063 Iustin Pop
      return False
870 a8083063 Iustin Pop
    self.minor = None
871 a8083063 Iustin Pop
    self.dev_path = None
872 a8083063 Iustin Pop
    return True
873 a8083063 Iustin Pop
874 a8083063 Iustin Pop
875 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
876 a8083063 Iustin Pop
    """Set the maximum sync speed for the MD array.
877 a8083063 Iustin Pop

878 a8083063 Iustin Pop
    """
879 a8083063 Iustin Pop
    result = super(MDRaid1, self).SetSyncSpeed(kbytes)
880 a8083063 Iustin Pop
    if self.minor is None:
881 a8083063 Iustin Pop
      logger.Error("MD array not attached to a device")
882 a8083063 Iustin Pop
      return False
883 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_max" % self.minor, "w")
884 a8083063 Iustin Pop
    try:
885 a8083063 Iustin Pop
      f.write("%d" % kbytes)
886 a8083063 Iustin Pop
    finally:
887 a8083063 Iustin Pop
      f.close()
888 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_min" % self.minor, "w")
889 a8083063 Iustin Pop
    try:
890 a8083063 Iustin Pop
      f.write("%d" % (kbytes/2))
891 a8083063 Iustin Pop
    finally:
892 a8083063 Iustin Pop
      f.close()
893 a8083063 Iustin Pop
    return result
894 a8083063 Iustin Pop
895 a8083063 Iustin Pop
896 a8083063 Iustin Pop
  def GetSyncStatus(self):
897 a8083063 Iustin Pop
    """Returns the sync status of the device.
898 a8083063 Iustin Pop

899 a8083063 Iustin Pop
    Returns:
900 a8083063 Iustin Pop
     (sync_percent, estimated_time)
901 a8083063 Iustin Pop

902 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
903 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
904 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
905 a8083063 Iustin Pop

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

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

941 a8083063 Iustin Pop
    """
942 a8083063 Iustin Pop
    return True
943 a8083063 Iustin Pop
944 a8083063 Iustin Pop
945 a8083063 Iustin Pop
  def Close(self):
946 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
947 a8083063 Iustin Pop

948 a8083063 Iustin Pop
    This is a no-op for the MDRaid1 device type, but see comment for
949 a8083063 Iustin Pop
    `Open()`.
950 a8083063 Iustin Pop

951 a8083063 Iustin Pop
    """
952 a8083063 Iustin Pop
    return True
953 a8083063 Iustin Pop
954 a8083063 Iustin Pop
955 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
956 0f7f32d9 Iustin Pop
  """Base DRBD class.
957 a8083063 Iustin Pop

958 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
959 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
960 0f7f32d9 Iustin Pop

961 0f7f32d9 Iustin Pop
  """
962 0f7f32d9 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
963 0f7f32d9 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)\)")
964 770fe0f9 Iustin Pop
  _DRBD_MAJOR = 147
965 770fe0f9 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
966 770fe0f9 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
967 770fe0f9 Iustin Pop
  _ST_CONNECTED = "Connected"
968 0f7f32d9 Iustin Pop
969 0f7f32d9 Iustin Pop
  @staticmethod
970 0f7f32d9 Iustin Pop
  def _GetProcData():
971 0f7f32d9 Iustin Pop
    """Return data from /proc/drbd.
972 0f7f32d9 Iustin Pop

973 0f7f32d9 Iustin Pop
    """
974 0f7f32d9 Iustin Pop
    stat = open("/proc/drbd", "r")
975 0f7f32d9 Iustin Pop
    try:
976 0f7f32d9 Iustin Pop
      data = stat.read().splitlines()
977 0f7f32d9 Iustin Pop
    finally:
978 0f7f32d9 Iustin Pop
      stat.close()
979 0f7f32d9 Iustin Pop
    if not data:
980 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't read any data from /proc/drbd")
981 0f7f32d9 Iustin Pop
    return data
982 0f7f32d9 Iustin Pop
983 5a47ad20 Iustin Pop
  @staticmethod
984 5a47ad20 Iustin Pop
  def _MassageProcData(data):
985 5a47ad20 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
986 5a47ad20 Iustin Pop

987 5a47ad20 Iustin Pop
    Returns:
988 5a47ad20 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
989 5a47ad20 Iustin Pop

990 5a47ad20 Iustin Pop
    """
991 5a47ad20 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
992 5a47ad20 Iustin Pop
    results = {}
993 5a47ad20 Iustin Pop
    old_minor = old_line = None
994 5a47ad20 Iustin Pop
    for line in data:
995 5a47ad20 Iustin Pop
      lresult = lmatch.match(line)
996 5a47ad20 Iustin Pop
      if lresult is not None:
997 5a47ad20 Iustin Pop
        if old_minor is not None:
998 5a47ad20 Iustin Pop
          results[old_minor] = old_line
999 5a47ad20 Iustin Pop
        old_minor = int(lresult.group(1))
1000 5a47ad20 Iustin Pop
        old_line = line
1001 5a47ad20 Iustin Pop
      else:
1002 5a47ad20 Iustin Pop
        if old_minor is not None:
1003 5a47ad20 Iustin Pop
          old_line += " " + line.strip()
1004 5a47ad20 Iustin Pop
    # add last line
1005 5a47ad20 Iustin Pop
    if old_minor is not None:
1006 5a47ad20 Iustin Pop
      results[old_minor] = old_line
1007 5a47ad20 Iustin Pop
    return results
1008 5a47ad20 Iustin Pop
1009 0f7f32d9 Iustin Pop
  @classmethod
1010 0f7f32d9 Iustin Pop
  def _GetVersion(cls):
1011 0f7f32d9 Iustin Pop
    """Return the DRBD version.
1012 0f7f32d9 Iustin Pop

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

1015 0f7f32d9 Iustin Pop
    """
1016 0f7f32d9 Iustin Pop
    proc_data = cls._GetProcData()
1017 0f7f32d9 Iustin Pop
    first_line = proc_data[0].strip()
1018 0f7f32d9 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
1019 0f7f32d9 Iustin Pop
    if not version:
1020 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
1021 0f7f32d9 Iustin Pop
                                    first_line)
1022 0f7f32d9 Iustin Pop
    return [int(val) for val in version.groups()]
1023 0f7f32d9 Iustin Pop
1024 770fe0f9 Iustin Pop
  @staticmethod
1025 770fe0f9 Iustin Pop
  def _DevPath(minor):
1026 770fe0f9 Iustin Pop
    """Return the path to a drbd device for a given minor.
1027 770fe0f9 Iustin Pop

1028 770fe0f9 Iustin Pop
    """
1029 770fe0f9 Iustin Pop
    return "/dev/drbd%d" % minor
1030 770fe0f9 Iustin Pop
1031 770fe0f9 Iustin Pop
  @classmethod
1032 770fe0f9 Iustin Pop
  def _GetUsedDevs(cls):
1033 770fe0f9 Iustin Pop
    """Compute the list of used DRBD devices.
1034 770fe0f9 Iustin Pop

1035 770fe0f9 Iustin Pop
    """
1036 770fe0f9 Iustin Pop
    data = cls._GetProcData()
1037 770fe0f9 Iustin Pop
1038 770fe0f9 Iustin Pop
    used_devs = {}
1039 770fe0f9 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
1040 770fe0f9 Iustin Pop
    for line in data:
1041 770fe0f9 Iustin Pop
      match = valid_line.match(line)
1042 770fe0f9 Iustin Pop
      if not match:
1043 770fe0f9 Iustin Pop
        continue
1044 770fe0f9 Iustin Pop
      minor = int(match.group(1))
1045 770fe0f9 Iustin Pop
      state = match.group(2)
1046 770fe0f9 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1047 770fe0f9 Iustin Pop
        continue
1048 770fe0f9 Iustin Pop
      used_devs[minor] = state, line
1049 770fe0f9 Iustin Pop
1050 770fe0f9 Iustin Pop
    return used_devs
1051 770fe0f9 Iustin Pop
1052 5a47ad20 Iustin Pop
  def _SetFromMinor(self, minor):
1053 5a47ad20 Iustin Pop
    """Set our parameters based on the given minor.
1054 5a47ad20 Iustin Pop

1055 5a47ad20 Iustin Pop
    This sets our minor variable and our dev_path.
1056 5a47ad20 Iustin Pop

1057 5a47ad20 Iustin Pop
    """
1058 5a47ad20 Iustin Pop
    if minor is None:
1059 5a47ad20 Iustin Pop
      self.minor = self.dev_path = None
1060 5a47ad20 Iustin Pop
    else:
1061 5a47ad20 Iustin Pop
      self.minor = minor
1062 5a47ad20 Iustin Pop
      self.dev_path = self._DevPath(minor)
1063 5a47ad20 Iustin Pop
1064 ae26a287 Iustin Pop
  @staticmethod
1065 ae26a287 Iustin Pop
  def _CheckMetaSize(meta_device):
1066 ae26a287 Iustin Pop
    """Check if the given meta device looks like a valid one.
1067 ae26a287 Iustin Pop

1068 ae26a287 Iustin Pop
    This currently only check the size, which must be around
1069 ae26a287 Iustin Pop
    128MiB.
1070 ae26a287 Iustin Pop

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

1093 f3e513ad Iustin Pop
    This is not supported for drbd devices.
1094 f3e513ad Iustin Pop

1095 f3e513ad Iustin Pop
    """
1096 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1097 f3e513ad Iustin Pop
1098 0f7f32d9 Iustin Pop
1099 0f7f32d9 Iustin Pop
class DRBDev(BaseDRBD):
1100 a8083063 Iustin Pop
  """DRBD block device.
1101 a8083063 Iustin Pop

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

1106 a8083063 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1107 a8083063 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1108 a8083063 Iustin Pop
  data device and the meta_device. The meta device is checked for
1109 a8083063 Iustin Pop
  valid size and is zeroed on create.
1110 a8083063 Iustin Pop

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

1132 a8083063 Iustin Pop
    """
1133 a8083063 Iustin Pop
    data = cls._GetProcData()
1134 a8083063 Iustin Pop
1135 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1136 a8083063 Iustin Pop
    for line in data:
1137 a8083063 Iustin Pop
      match = valid_line.match(line)
1138 a8083063 Iustin Pop
      if match:
1139 a8083063 Iustin Pop
        return int(match.group(1))
1140 a8083063 Iustin Pop
    logger.Error("Error: no free drbd minors!")
1141 0caf6485 Iustin Pop
    raise errors.BlockDeviceError("Can't find a free DRBD minor")
1142 a8083063 Iustin Pop
1143 a8083063 Iustin Pop
  @classmethod
1144 a8083063 Iustin Pop
  def _GetDevInfo(cls, minor):
1145 a8083063 Iustin Pop
    """Get details about a given DRBD minor.
1146 a8083063 Iustin Pop

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

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

1198 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1199 a8083063 Iustin Pop
    method tests if our local backing device is the same as the one in
1200 a8083063 Iustin Pop
    the info parameter, in effect testing if we look like the given
1201 a8083063 Iustin Pop
    device.
1202 a8083063 Iustin Pop

1203 a8083063 Iustin Pop
    """
1204 a8083063 Iustin Pop
    if not ("local_dev" in info and "meta_dev" in info and
1205 a8083063 Iustin Pop
            "meta_index" in info):
1206 a8083063 Iustin Pop
      return False
1207 a8083063 Iustin Pop
1208 a8083063 Iustin Pop
    backend = self._children[0]
1209 a8083063 Iustin Pop
    if backend is not None:
1210 a8083063 Iustin Pop
      retval = (info["local_dev"] == (backend.major, backend.minor))
1211 a8083063 Iustin Pop
    else:
1212 a8083063 Iustin Pop
      retval = (info["local_dev"] == (0, 0))
1213 a8083063 Iustin Pop
    meta = self._children[1]
1214 a8083063 Iustin Pop
    if meta is not None:
1215 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == (meta.major, meta.minor))
1216 a8083063 Iustin Pop
      retval = retval and (info["meta_index"] == 0)
1217 a8083063 Iustin Pop
    else:
1218 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == "internal" and
1219 a8083063 Iustin Pop
                           info["meta_index"] == -1)
1220 a8083063 Iustin Pop
    return retval
1221 a8083063 Iustin Pop
1222 a8083063 Iustin Pop
1223 a8083063 Iustin Pop
  def _MatchesNet(self, info):
1224 a8083063 Iustin Pop
    """Test if our network config matches with an existing device.
1225 a8083063 Iustin Pop

1226 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1227 a8083063 Iustin Pop
    method tests if our network configuration is the same as the one
1228 a8083063 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1229 a8083063 Iustin Pop
    device.
1230 a8083063 Iustin Pop

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

1253 a8083063 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1254 a8083063 Iustin Pop
    device. And it must be done only once.
1255 a8083063 Iustin Pop

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

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

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

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

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

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

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

1337 a8083063 Iustin Pop
    This fails if we don't have a local device.
1338 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

1493 a8083063 Iustin Pop
    Returns:
1494 a8083063 Iustin Pop
     (sync_percent, estimated_time)
1495 a8083063 Iustin Pop

1496 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
1497 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
1498 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
1499 a8083063 Iustin Pop

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

1533 a8083063 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1534 a8083063 Iustin Pop

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

1563 a8083063 Iustin Pop
    This writes until we get ENOSPC.
1564 a8083063 Iustin Pop

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

1580 a8083063 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1581 a8083063 Iustin Pop
    function just zeroes the meta device.
1582 a8083063 Iustin Pop

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

1601 a8083063 Iustin Pop
    """
1602 a8083063 Iustin Pop
    return self.Shutdown()
1603 a8083063 Iustin Pop
1604 f3e513ad Iustin Pop
1605 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1606 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1607 a2cfdea2 Iustin Pop

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

1612 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1613 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1614 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1615 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1616 a2cfdea2 Iustin Pop

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

1642 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1643 a2cfdea2 Iustin Pop

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

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

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

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

1698 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1699 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1700 a2cfdea2 Iustin Pop

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

1748 a2cfdea2 Iustin Pop
    This return, if available, the local backing device (as a path)
1749 a2cfdea2 Iustin Pop
    and the local and remote (ip, port) information.
1750 a2cfdea2 Iustin Pop

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

1791 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1792 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1793 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1794 a2cfdea2 Iustin Pop
    device.
1795 a2cfdea2 Iustin Pop

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

1820 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1821 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1822 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1823 a2cfdea2 Iustin Pop
    device.
1824 a2cfdea2 Iustin Pop

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

1846 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1847 a2cfdea2 Iustin Pop
    device. And it must be done only once.
1848 a2cfdea2 Iustin Pop

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

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

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

1928 b00b95dd Iustin Pop
    """
1929 b00b95dd Iustin Pop
    if self.minor is None:
1930 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to drbd8 during"
1931 b00b95dd Iustin Pop
                                    " RemoveChildren")
1932 b00b95dd Iustin Pop
    if len(self._children) != 2:
1933 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We don't have two children: %s" %
1934 b00b95dd Iustin Pop
                                    self._children)
1935 b00b95dd Iustin Pop
1936 b00b95dd Iustin Pop
    if len(devices) != 2:
1937 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We need two children in RemoveChildren")
1938 b00b95dd Iustin Pop
    for idx, dev in enumerate(devices):
1939 b00b95dd Iustin Pop
      if dev.dev_path != self._children[idx].dev_path:
1940 b00b95dd Iustin Pop
        raise errors.BlockDeviceError("Mismatch in local storage (%d) in"
1941 b00b95dd Iustin Pop
                                      " RemoveChildren" % idx)
1942 b00b95dd Iustin Pop
1943 b00b95dd Iustin Pop
    if not self._ShutdownLocal(self.minor):
1944 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't detach from local storage")
1945 b00b95dd Iustin Pop
    self._children = []
1946 b00b95dd Iustin Pop
1947 a2cfdea2 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1948 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1949 a2cfdea2 Iustin Pop

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

1964 a2cfdea2 Iustin Pop
    Returns:
1965 a2cfdea2 Iustin Pop
     (sync_percent, estimated_time)
1966 a2cfdea2 Iustin Pop

1967 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1968 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1969 a2cfdea2 Iustin Pop
    the time needed, otherwise it's the time left in seconds
1970 a2cfdea2 Iustin Pop

1971 a2cfdea2 Iustin Pop
    """
1972 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1973 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1974 a2cfdea2 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1975 a2cfdea2 Iustin Pop
    if self.minor not in proc_info:
1976 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1977 a2cfdea2 Iustin Pop
                                    self.minor)
1978 a2cfdea2 Iustin Pop
    line = proc_info[self.minor]
1979 a2cfdea2 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1980 a2cfdea2 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1981 a2cfdea2 Iustin Pop
    if match:
1982 a2cfdea2 Iustin Pop
      sync_percent = float(match.group(1))
1983 a2cfdea2 Iustin Pop
      hours = int(match.group(2))
1984 a2cfdea2 Iustin Pop
      minutes = int(match.group(3))
1985 a2cfdea2 Iustin Pop
      seconds = int(match.group(4))
1986 a2cfdea2 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1987 a2cfdea2 Iustin Pop
    else:
1988 a2cfdea2 Iustin Pop
      sync_percent = None
1989 a2cfdea2 Iustin Pop
      est_time = None
1990 a2cfdea2 Iustin Pop
    match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1991 a2cfdea2 Iustin Pop
    if not match:
1992 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1993 a2cfdea2 Iustin Pop
                                    self.minor)
1994 a2cfdea2 Iustin Pop
    client_state = match.group(1)
1995 a2cfdea2 Iustin Pop
    is_degraded = client_state != "Connected"
1996 a2cfdea2 Iustin Pop
    return sync_percent, est_time, is_degraded
1997 a2cfdea2 Iustin Pop
1998 a2cfdea2 Iustin Pop
  def GetStatus(self):
1999 a2cfdea2 Iustin Pop
    """Compute the status of the DRBD device
2000 a2cfdea2 Iustin Pop

2001 a2cfdea2 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
2002 a2cfdea2 Iustin Pop

2003 a2cfdea2 Iustin Pop
    """
2004 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2005 a2cfdea2 Iustin Pop
      return self.STATUS_UNKNOWN
2006 a2cfdea2 Iustin Pop
2007 a2cfdea2 Iustin Pop
    data = self._GetProcData()
2008 a2cfdea2 Iustin Pop
    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
2009 a2cfdea2 Iustin Pop
                       self.minor)
2010 a2cfdea2 Iustin Pop
    for line in data:
2011 a2cfdea2 Iustin Pop
      mresult = match.match(line)
2012 a2cfdea2 Iustin Pop
      if mresult:
2013 a2cfdea2 Iustin Pop
        break
2014 a2cfdea2 Iustin Pop
    else:
2015 a2cfdea2 Iustin Pop
      logger.Error("Can't find myself!")
2016 a2cfdea2 Iustin Pop
      return self.STATUS_UNKNOWN
2017 a2cfdea2 Iustin Pop
2018 a2cfdea2 Iustin Pop
    state = mresult.group(2)
2019 a2cfdea2 Iustin Pop
    if state == "Primary":
2020 a2cfdea2 Iustin Pop
      result = self.STATUS_ONLINE
2021 a2cfdea2 Iustin Pop
    else:
2022 a2cfdea2 Iustin Pop
      result = self.STATUS_STANDBY
2023 a2cfdea2 Iustin Pop
2024 a2cfdea2 Iustin Pop
    return result
2025 a2cfdea2 Iustin Pop
2026 a2cfdea2 Iustin Pop
  def Open(self, force=False):
2027 a2cfdea2 Iustin Pop
    """Make the local state primary.
2028 a2cfdea2 Iustin Pop

2029 a2cfdea2 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
2030 a2cfdea2 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
2031 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
2032 a2cfdea2 Iustin Pop
    has to be given.
2033 a2cfdea2 Iustin Pop

2034 a2cfdea2 Iustin Pop
    """
2035 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2036 a2cfdea2 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
2037 a2cfdea2 Iustin Pop
      return False
2038 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
2039 a2cfdea2 Iustin Pop
    if force:
2040 a2cfdea2 Iustin Pop
      cmd.append("-o")
2041 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
2042 a2cfdea2 Iustin Pop
    if result.failed:
2043 a2cfdea2 Iustin Pop
      logger.Error("Can't make drbd device primary: %s" % result.output)
2044 a2cfdea2 Iustin Pop
      return False
2045 a2cfdea2 Iustin Pop
    return True
2046 a2cfdea2 Iustin Pop
2047 a2cfdea2 Iustin Pop
  def Close(self):
2048 a2cfdea2 Iustin Pop
    """Make the local state secondary.
2049 a2cfdea2 Iustin Pop

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

2052 a2cfdea2 Iustin Pop
    """
2053 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2054 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
2055 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
2056 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
2057 a2cfdea2 Iustin Pop
    if result.failed:
2058 a2cfdea2 Iustin Pop
      logger.Error("Can't switch drbd device to secondary: %s" % result.output)
2059 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't switch drbd device to secondary")
2060 a2cfdea2 Iustin Pop
2061 a2cfdea2 Iustin Pop
  def Attach(self):
2062 a2cfdea2 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
2063 a2cfdea2 Iustin Pop

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

2068 a2cfdea2 Iustin Pop
    """
2069 a2cfdea2 Iustin Pop
    for minor in self._GetUsedDevs():
2070 a2cfdea2 Iustin Pop
      info = self._GetDevInfo(minor)
2071 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
2072 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
2073 a2cfdea2 Iustin Pop
      if match_l and match_r:
2074 a2cfdea2 Iustin Pop
        break
2075 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
2076 a2cfdea2 Iustin Pop
        res_r = self._AssembleNet(minor,
2077 a2cfdea2 Iustin Pop
                                  (self._lhost, self._lport,
2078 a2cfdea2 Iustin Pop
                                   self._rhost, self._rport),
2079 a2cfdea2 Iustin Pop
                                  "C")
2080 a2cfdea2 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
2081 a2cfdea2 Iustin Pop
          break
2082 a2cfdea2 Iustin Pop
    else:
2083 a2cfdea2 Iustin Pop
      minor = None
2084 a2cfdea2 Iustin Pop
2085 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2086 a2cfdea2 Iustin Pop
    return minor is not None
2087 a2cfdea2 Iustin Pop
2088 a2cfdea2 Iustin Pop
  def Assemble(self):
2089 a2cfdea2 Iustin Pop
    """Assemble the drbd.
2090 a2cfdea2 Iustin Pop

2091 a2cfdea2 Iustin Pop
    Method:
2092 a2cfdea2 Iustin Pop
      - if we have a local backing device, we bind to it by:
2093 a2cfdea2 Iustin Pop
        - checking the list of used drbd devices
2094 a2cfdea2 Iustin Pop
        - check if the local minor use of any of them is our own device
2095 a2cfdea2 Iustin Pop
        - if yes, abort?
2096 a2cfdea2 Iustin Pop
        - if not, bind
2097 a2cfdea2 Iustin Pop
      - if we have a local/remote net info:
2098 a2cfdea2 Iustin Pop
        - redo the local backing device step for the remote device
2099 a2cfdea2 Iustin Pop
        - check if any drbd device is using the local port,
2100 a2cfdea2 Iustin Pop
          if yes abort
2101 a2cfdea2 Iustin Pop
        - check if any remote drbd device is using the remote
2102 a2cfdea2 Iustin Pop
          port, if yes abort (for now)
2103 a2cfdea2 Iustin Pop
        - bind our net port
2104 a2cfdea2 Iustin Pop
        - bind the remote net port
2105 a2cfdea2 Iustin Pop

2106 a2cfdea2 Iustin Pop
    """
2107 a2cfdea2 Iustin Pop
    self.Attach()
2108 a2cfdea2 Iustin Pop
    if self.minor is not None:
2109 a2cfdea2 Iustin Pop
      logger.Info("Already assembled")
2110 a2cfdea2 Iustin Pop
      return True
2111 a2cfdea2 Iustin Pop
2112 a2cfdea2 Iustin Pop
    result = super(DRBD8, self).Assemble()
2113 a2cfdea2 Iustin Pop
    if not result:
2114 a2cfdea2 Iustin Pop
      return result
2115 a2cfdea2 Iustin Pop
2116 a2cfdea2 Iustin Pop
    minor = self._FindUnusedMinor()
2117 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
2118 a2cfdea2 Iustin Pop
    if self._children[0]:
2119 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
2120 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
2121 a2cfdea2 Iustin Pop
      if not result:
2122 a2cfdea2 Iustin Pop
        return False
2123 a2cfdea2 Iustin Pop
      need_localdev_teardown = True
2124 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
2125 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
2126 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
2127 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
2128 a2cfdea2 Iustin Pop
                                 "C")
2129 a2cfdea2 Iustin Pop
      if not result:
2130 a2cfdea2 Iustin Pop
        if need_localdev_teardown:
2131 a2cfdea2 Iustin Pop
          # we will ignore failures from this
2132 a2cfdea2 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
2133 a2cfdea2 Iustin Pop
          self._ShutdownAll(minor)
2134 a2cfdea2 Iustin Pop
        return False
2135 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2136 a2cfdea2 Iustin Pop
    return True
2137 a2cfdea2 Iustin Pop
2138 a2cfdea2 Iustin Pop
  @classmethod
2139 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
2140 b00b95dd Iustin Pop
    """Detach from the local device.
2141 b00b95dd Iustin Pop

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

2145 b00b95dd Iustin Pop
    """
2146 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2147 b00b95dd Iustin Pop
    if result.failed:
2148 b00b95dd Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
2149 b00b95dd Iustin Pop
    return not result.failed
2150 b00b95dd Iustin Pop
2151 b00b95dd Iustin Pop
  @classmethod
2152 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2153 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2154 f3e513ad Iustin Pop

2155 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2156 f3e513ad Iustin Pop

2157 f3e513ad Iustin Pop
    """
2158 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2159 f3e513ad Iustin Pop
    logger.Error("Can't shutdown network: %s" % result.output)
2160 f3e513ad Iustin Pop
    return not result.failed
2161 f3e513ad Iustin Pop
2162 f3e513ad Iustin Pop
  @classmethod
2163 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2164 a2cfdea2 Iustin Pop
    """Deactivate the device.
2165 a2cfdea2 Iustin Pop

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

2168 a2cfdea2 Iustin Pop
    """
2169 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2170 a2cfdea2 Iustin Pop
    if result.failed:
2171 a2cfdea2 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
2172 a2cfdea2 Iustin Pop
    return not result.failed
2173 a2cfdea2 Iustin Pop
2174 a2cfdea2 Iustin Pop
  def Shutdown(self):
2175 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2176 a2cfdea2 Iustin Pop

2177 a2cfdea2 Iustin Pop
    """
2178 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2179 a2cfdea2 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
2180 a2cfdea2 Iustin Pop
      return True
2181 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
2182 a2cfdea2 Iustin Pop
      return False
2183 a2cfdea2 Iustin Pop
    self.minor = None
2184 a2cfdea2 Iustin Pop
    self.dev_path = None
2185 a2cfdea2 Iustin Pop
    return True
2186 a2cfdea2 Iustin Pop
2187 f3e513ad Iustin Pop
  def Rename(self, new_uid):
2188 f3e513ad Iustin Pop
    """Re-connect this device to another peer.
2189 f3e513ad Iustin Pop

2190 f3e513ad Iustin Pop
    """
2191 f3e513ad Iustin Pop
    if self.minor is None:
2192 f3e513ad Iustin Pop
      raise errors.BlockDeviceError("Device not attached during rename")
2193 f3e513ad Iustin Pop
    if self._rhost is not None:
2194 f3e513ad Iustin Pop
      # this means we did have a host when we attached, so we are connected
2195 f3e513ad Iustin Pop
      if not self._ShutdownNet(self.minor):
2196 f3e513ad Iustin Pop
        raise errors.BlockDeviceError("Can't disconnect from remote peer")
2197 f3e513ad Iustin Pop
      old_id = self.unique_id
2198 f3e513ad Iustin Pop
    else:
2199 f3e513ad Iustin Pop
      old_id = None
2200 f3e513ad Iustin Pop
    self.unique_id = new_uid
2201 f3e513ad Iustin Pop
    if not self._AssembleNet(self.minor, self.unique_id, "C"):
2202 f3e513ad Iustin Pop
      logger.Error("Can't attach to new peer!")
2203 f3e513ad Iustin Pop
      if self.old_id is not None:
2204 f3e513ad Iustin Pop
        self._AssembleNet(self.minor, old_id, "C")
2205 f3e513ad Iustin Pop
      self.unique_id = old_id
2206 f3e513ad Iustin Pop
      raise errors.BlockDeviceError("Can't attach to new peer")
2207 f3e513ad Iustin Pop
2208 a2cfdea2 Iustin Pop
  def Remove(self):
2209 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2210 a2cfdea2 Iustin Pop

2211 a2cfdea2 Iustin Pop
    """
2212 a2cfdea2 Iustin Pop
    return self.Shutdown()
2213 a2cfdea2 Iustin Pop
2214 a2cfdea2 Iustin Pop
  @classmethod
2215 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
2216 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2217 a2cfdea2 Iustin Pop

2218 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2219 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2220 a2cfdea2 Iustin Pop

2221 a2cfdea2 Iustin Pop
    """
2222 a2cfdea2 Iustin Pop
    if len(children) != 2:
2223 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2224 a2cfdea2 Iustin Pop
    meta = children[1]
2225 a2cfdea2 Iustin Pop
    meta.Assemble()
2226 a2cfdea2 Iustin Pop
    if not meta.Attach():
2227 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
2228 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
2229 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
2230 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
2231 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
2232 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
2233 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
2234 a2cfdea2 Iustin Pop
2235 a8083063 Iustin Pop
2236 a8083063 Iustin Pop
DEV_MAP = {
2237 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2238 fe96220b Iustin Pop
  constants.LD_MD_R1: MDRaid1,
2239 fe96220b Iustin Pop
  constants.LD_DRBD7: DRBDev,
2240 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2241 a8083063 Iustin Pop
  }
2242 a8083063 Iustin Pop
2243 a8083063 Iustin Pop
2244 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
2245 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2246 a8083063 Iustin Pop

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

2250 a8083063 Iustin Pop
  """
2251 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2252 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2253 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2254 a8083063 Iustin Pop
  if not device.Attach():
2255 a8083063 Iustin Pop
    return None
2256 a8083063 Iustin Pop
  return  device
2257 a8083063 Iustin Pop
2258 a8083063 Iustin Pop
2259 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
2260 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2261 a8083063 Iustin Pop

2262 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
2263 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
2264 a8083063 Iustin Pop

2265 a8083063 Iustin Pop
  """
2266 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2267 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2268 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2269 a8083063 Iustin Pop
  if not device.Attach():
2270 a8083063 Iustin Pop
    device.Assemble()
2271 a8083063 Iustin Pop
  if not device.Attach():
2272 a8083063 Iustin Pop
    raise errors.BlockDeviceError("Can't find a valid block device for"
2273 a8083063 Iustin Pop
                                  " %s/%s/%s" %
2274 a8083063 Iustin Pop
                                  (dev_type, unique_id, children))
2275 a8083063 Iustin Pop
  return device
2276 a8083063 Iustin Pop
2277 a8083063 Iustin Pop
2278 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2279 a8083063 Iustin Pop
  """Create a device.
2280 a8083063 Iustin Pop

2281 a8083063 Iustin Pop
  """
2282 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2283 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2284 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2285 a8083063 Iustin Pop
  return device