Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 162c1c1f

History | View | Annotate | Download (72.8 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Block device abstraction"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import re
25 a8083063 Iustin Pop
import time
26 a8083063 Iustin Pop
import errno
27 a2cfdea2 Iustin Pop
import pyparsing as pyp
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
from ganeti import utils
30 a8083063 Iustin Pop
from ganeti import logger
31 a8083063 Iustin Pop
from ganeti import errors
32 fe96220b Iustin Pop
from ganeti import constants
33 a8083063 Iustin Pop
34 a8083063 Iustin Pop
35 a8083063 Iustin Pop
class BlockDev(object):
36 a8083063 Iustin Pop
  """Block device abstract class.
37 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

161 a8083063 Iustin Pop
    """
162 a8083063 Iustin Pop
    raise NotImplementedError
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 f3e513ad Iustin Pop
  def Rename(self, new_id):
175 f3e513ad Iustin Pop
    """Rename this device.
176 f3e513ad Iustin Pop

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

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

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

191 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
192 a8083063 Iustin Pop
    devices need this.
193 a8083063 Iustin Pop

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

197 a8083063 Iustin Pop
    """
198 a8083063 Iustin Pop
    raise NotImplementedError
199 a8083063 Iustin Pop
200 a8083063 Iustin Pop
  def Shutdown(self):
201 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
202 a8083063 Iustin Pop

203 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
204 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
205 a8083063 Iustin Pop
    assembled after this call.
206 a8083063 Iustin Pop

207 a8083063 Iustin Pop
    """
208 a8083063 Iustin Pop
    raise NotImplementedError
209 a8083063 Iustin Pop
210 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
211 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
212 a8083063 Iustin Pop

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

215 a8083063 Iustin Pop
    """
216 a8083063 Iustin Pop
    result = True
217 a8083063 Iustin Pop
    if self._children:
218 a8083063 Iustin Pop
      for child in self._children:
219 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
220 a8083063 Iustin Pop
    return result
221 a8083063 Iustin Pop
222 a8083063 Iustin Pop
  def GetSyncStatus(self):
223 a8083063 Iustin Pop
    """Returns the sync status of the device.
224 a8083063 Iustin Pop

225 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
226 a8083063 Iustin Pop
    status of the mirror.
227 a8083063 Iustin Pop

228 a8083063 Iustin Pop
    Returns:
229 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
230 0834c866 Iustin Pop

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

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

236 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
237 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
238 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
239 a8083063 Iustin Pop

240 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
241 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
242 0834c866 Iustin Pop
    return False (not degraded).
243 0834c866 Iustin Pop

244 a8083063 Iustin Pop
    """
245 0834c866 Iustin Pop
    return None, None, False, 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 0834c866 Iustin Pop
    min_percent, max_time, is_degraded, ldisk = self.GetSyncStatus()
257 a8083063 Iustin Pop
    if self._children:
258 a8083063 Iustin Pop
      for child in self._children:
259 0834c866 Iustin Pop
        c_percent, c_time, c_degraded, c_ldisk = 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 0834c866 Iustin Pop
        ldisk = ldisk or c_ldisk
270 0834c866 Iustin Pop
    return min_percent, max_time, is_degraded, ldisk
271 a8083063 Iustin Pop
272 a8083063 Iustin Pop
273 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
274 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
275 a0c3fea1 Michael Hanselmann

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

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

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

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

298 a8083063 Iustin Pop
    """
299 a8083063 Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children)
300 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
301 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
302 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
303 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
304 a8083063 Iustin Pop
    self.Attach()
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 be345db0 Iustin Pop
    self._lv_name = new_name
396 be345db0 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
397 be345db0 Iustin Pop
398 a8083063 Iustin Pop
399 a8083063 Iustin Pop
  def Attach(self):
400 a8083063 Iustin Pop
    """Attach to an existing LV.
401 a8083063 Iustin Pop

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

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

424 5574047a Iustin Pop
    We alway run `lvchange -ay` on the LV to ensure it's active before
425 5574047a Iustin Pop
    use, as there were cases when xenvg was not active after boot
426 5574047a Iustin Pop
    (also possibly after disk issues).
427 a8083063 Iustin Pop

428 a8083063 Iustin Pop
    """
429 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
430 5574047a Iustin Pop
    if result.failed:
431 5574047a Iustin Pop
      logger.Error("Can't activate lv %s: %s" % (self.dev_path, result.output))
432 5574047a Iustin Pop
    return not result.failed
433 a8083063 Iustin Pop
434 a8083063 Iustin Pop
  def Shutdown(self):
435 a8083063 Iustin Pop
    """Shutdown the device.
436 a8083063 Iustin Pop

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

440 a8083063 Iustin Pop
    """
441 a8083063 Iustin Pop
    return True
442 a8083063 Iustin Pop
443 a8083063 Iustin Pop
  def GetStatus(self):
444 a8083063 Iustin Pop
    """Return the status of the device.
445 a8083063 Iustin Pop

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

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

474 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
475 9db6dbce Iustin Pop
    status of the mirror.
476 9db6dbce Iustin Pop

477 9db6dbce Iustin Pop
    Returns:
478 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
479 9db6dbce Iustin Pop

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

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

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

508 a8083063 Iustin Pop
    This is a no-op for the LV device type.
509 a8083063 Iustin Pop

510 a8083063 Iustin Pop
    """
511 fdbd668d Iustin Pop
    pass
512 a8083063 Iustin Pop
513 a8083063 Iustin Pop
  def Close(self):
514 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
515 a8083063 Iustin Pop

516 a8083063 Iustin Pop
    This is a no-op for the LV device type.
517 a8083063 Iustin Pop

518 a8083063 Iustin Pop
    """
519 fdbd668d Iustin Pop
    pass
520 a8083063 Iustin Pop
521 a8083063 Iustin Pop
  def Snapshot(self, size):
522 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
523 a8083063 Iustin Pop

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

553 a0c3fea1 Michael Hanselmann
    """
554 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
555 a0c3fea1 Michael Hanselmann
556 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
557 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
558 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
559 a0c3fea1 Michael Hanselmann
560 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
561 a0c3fea1 Michael Hanselmann
    text = text[:128]
562 a0c3fea1 Michael Hanselmann
563 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
564 a0c3fea1 Michael Hanselmann
                           self.dev_path])
565 a0c3fea1 Michael Hanselmann
    if result.failed:
566 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Command: %s error: %s" %
567 3ecf6786 Iustin Pop
                                    (result.cmd, result.fail_reason))
568 a0c3fea1 Michael Hanselmann
569 a0c3fea1 Michael Hanselmann
570 a8083063 Iustin Pop
class MDRaid1(BlockDev):
571 a8083063 Iustin Pop
  """raid1 device implemented via md.
572 a8083063 Iustin Pop

573 a8083063 Iustin Pop
  """
574 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
575 a8083063 Iustin Pop
    super(MDRaid1, self).__init__(unique_id, children)
576 a8083063 Iustin Pop
    self.major = 9
577 a8083063 Iustin Pop
    self.Attach()
578 a8083063 Iustin Pop
579 a8083063 Iustin Pop
  def Attach(self):
580 a8083063 Iustin Pop
    """Find an array which matches our config and attach to it.
581 a8083063 Iustin Pop

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

584 a8083063 Iustin Pop
    """
585 a8083063 Iustin Pop
    minor = self._FindMDByUUID(self.unique_id)
586 a8083063 Iustin Pop
    if minor is not None:
587 a8083063 Iustin Pop
      self._SetFromMinor(minor)
588 a8083063 Iustin Pop
    else:
589 a8083063 Iustin Pop
      self.minor = None
590 a8083063 Iustin Pop
      self.dev_path = None
591 a8083063 Iustin Pop
592 a8083063 Iustin Pop
    return (minor is not None)
593 a8083063 Iustin Pop
594 a8083063 Iustin Pop
  @staticmethod
595 a8083063 Iustin Pop
  def _GetUsedDevs():
596 a8083063 Iustin Pop
    """Compute the list of in-use MD devices.
597 a8083063 Iustin Pop

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

601 a8083063 Iustin Pop
    """
602 a8083063 Iustin Pop
    mdstat = open("/proc/mdstat", "r")
603 a8083063 Iustin Pop
    data = mdstat.readlines()
604 a8083063 Iustin Pop
    mdstat.close()
605 a8083063 Iustin Pop
606 a8083063 Iustin Pop
    used_md = {}
607 a8083063 Iustin Pop
    valid_line = re.compile("^md([0-9]+) : .*$")
608 a8083063 Iustin Pop
    for line in data:
609 a8083063 Iustin Pop
      match = valid_line.match(line)
610 a8083063 Iustin Pop
      if match:
611 a8083063 Iustin Pop
        md_no = int(match.group(1))
612 a8083063 Iustin Pop
        used_md[md_no] = line
613 a8083063 Iustin Pop
614 a8083063 Iustin Pop
    return used_md
615 a8083063 Iustin Pop
616 a8083063 Iustin Pop
  @staticmethod
617 a8083063 Iustin Pop
  def _GetDevInfo(minor):
618 a8083063 Iustin Pop
    """Get info about a MD device.
619 a8083063 Iustin Pop

620 a8083063 Iustin Pop
    Currently only uuid is returned.
621 a8083063 Iustin Pop

622 a8083063 Iustin Pop
    """
623 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-D", "/dev/md%d" % minor])
624 a8083063 Iustin Pop
    if result.failed:
625 a8083063 Iustin Pop
      logger.Error("Can't display md: %s" % result.fail_reason)
626 a8083063 Iustin Pop
      return None
627 a8083063 Iustin Pop
    retval = {}
628 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
629 a8083063 Iustin Pop
      line = line.strip()
630 a8083063 Iustin Pop
      kv = line.split(" : ", 1)
631 a8083063 Iustin Pop
      if kv:
632 a8083063 Iustin Pop
        if kv[0] == "UUID":
633 8d519422 Iustin Pop
          retval["uuid"] = kv[1].split()[0]
634 a8083063 Iustin Pop
        elif kv[0] == "State":
635 a8083063 Iustin Pop
          retval["state"] = kv[1].split(", ")
636 a8083063 Iustin Pop
    return retval
637 a8083063 Iustin Pop
638 a8083063 Iustin Pop
  @staticmethod
639 a8083063 Iustin Pop
  def _FindUnusedMinor():
640 a8083063 Iustin Pop
    """Compute an unused MD minor.
641 a8083063 Iustin Pop

642 a8083063 Iustin Pop
    This code assumes that there are 256 minors only.
643 a8083063 Iustin Pop

644 a8083063 Iustin Pop
    """
645 a8083063 Iustin Pop
    used_md = MDRaid1._GetUsedDevs()
646 a8083063 Iustin Pop
    i = 0
647 a8083063 Iustin Pop
    while i < 256:
648 a8083063 Iustin Pop
      if i not in used_md:
649 a8083063 Iustin Pop
        break
650 a8083063 Iustin Pop
      i += 1
651 a8083063 Iustin Pop
    if i == 256:
652 a8083063 Iustin Pop
      logger.Error("Critical: Out of md minor numbers.")
653 0caf6485 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free MD minor")
654 a8083063 Iustin Pop
    return i
655 a8083063 Iustin Pop
656 a8083063 Iustin Pop
  @classmethod
657 a8083063 Iustin Pop
  def _FindMDByUUID(cls, uuid):
658 a8083063 Iustin Pop
    """Find the minor of an MD array with a given UUID.
659 a8083063 Iustin Pop

660 a8083063 Iustin Pop
    """
661 a8083063 Iustin Pop
    md_list = cls._GetUsedDevs()
662 a8083063 Iustin Pop
    for minor in md_list:
663 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
664 a8083063 Iustin Pop
      if info and info["uuid"] == uuid:
665 a8083063 Iustin Pop
        return minor
666 a8083063 Iustin Pop
    return None
667 a8083063 Iustin Pop
668 1a87dca7 Iustin Pop
  @staticmethod
669 1a87dca7 Iustin Pop
  def _ZeroSuperblock(dev_path):
670 1a87dca7 Iustin Pop
    """Zero the possible locations for an MD superblock.
671 1a87dca7 Iustin Pop

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

676 1a87dca7 Iustin Pop
    The superblocks are located at (negative values are relative to
677 1a87dca7 Iustin Pop
    the end of the block device):
678 1a87dca7 Iustin Pop
      - -128k to end for version 0.90 superblock
679 1a87dca7 Iustin Pop
      - -8k to -12k for version 1.0 superblock (included in the above)
680 1a87dca7 Iustin Pop
      - 0k to 4k for version 1.1 superblock
681 1a87dca7 Iustin Pop
      - 4k to 8k for version 1.2 superblock
682 1a87dca7 Iustin Pop

683 1a87dca7 Iustin Pop
    To cover all situations, the zero-ing will be:
684 1a87dca7 Iustin Pop
      - 0k to 128k
685 1a87dca7 Iustin Pop
      - -128k to end
686 1a87dca7 Iustin Pop

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

690 1a87dca7 Iustin Pop
    Note that this function depends on the fact that one can open,
691 1a87dca7 Iustin Pop
    read and write block devices normally.
692 1a87dca7 Iustin Pop

693 1a87dca7 Iustin Pop
    """
694 1a87dca7 Iustin Pop
    overwrite_size = 128 * 1024
695 1a87dca7 Iustin Pop
    empty_buf = '\0' * overwrite_size
696 1a87dca7 Iustin Pop
    fd = open(dev_path, "r+")
697 1a87dca7 Iustin Pop
    try:
698 1a87dca7 Iustin Pop
      fd.seek(0, 0)
699 1a87dca7 Iustin Pop
      p1 = fd.tell()
700 1a87dca7 Iustin Pop
      fd.write(empty_buf)
701 1a87dca7 Iustin Pop
      p2 = fd.tell()
702 1a87dca7 Iustin Pop
      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
703 1a87dca7 Iustin Pop
      fd.seek(-overwrite_size, 2)
704 1a87dca7 Iustin Pop
      p1 = fd.tell()
705 1a87dca7 Iustin Pop
      fd.write(empty_buf)
706 1a87dca7 Iustin Pop
      p2 = fd.tell()
707 1a87dca7 Iustin Pop
      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
708 1a87dca7 Iustin Pop
    finally:
709 1a87dca7 Iustin Pop
      fd.close()
710 1a87dca7 Iustin Pop
711 a8083063 Iustin Pop
  @classmethod
712 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
713 a8083063 Iustin Pop
    """Create a new MD raid1 array.
714 a8083063 Iustin Pop

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

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

750 a8083063 Iustin Pop
    """
751 a8083063 Iustin Pop
    #TODO: maybe zero superblock on child devices?
752 a8083063 Iustin Pop
    return self.Shutdown()
753 a8083063 Iustin Pop
754 f3e513ad Iustin Pop
  def Rename(self, new_id):
755 f3e513ad Iustin Pop
    """Rename a device.
756 f3e513ad Iustin Pop

757 f3e513ad Iustin Pop
    This is not supported for md raid1 devices.
758 f3e513ad Iustin Pop

759 f3e513ad Iustin Pop
    """
760 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a md raid1 device")
761 a8083063 Iustin Pop
762 153d9724 Iustin Pop
  def AddChildren(self, devices):
763 153d9724 Iustin Pop
    """Add new member(s) to the md raid1.
764 a8083063 Iustin Pop

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

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

830 a8083063 Iustin Pop
    """
831 a8083063 Iustin Pop
    self.Attach()
832 a8083063 Iustin Pop
    if self.minor is None:
833 a8083063 Iustin Pop
      retval = self.STATUS_UNKNOWN
834 a8083063 Iustin Pop
    else:
835 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
836 a8083063 Iustin Pop
    return retval
837 a8083063 Iustin Pop
838 a8083063 Iustin Pop
  def _SetFromMinor(self, minor):
839 a8083063 Iustin Pop
    """Set our parameters based on the given minor.
840 a8083063 Iustin Pop

841 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
842 a8083063 Iustin Pop

843 a8083063 Iustin Pop
    """
844 a8083063 Iustin Pop
    self.minor = minor
845 a8083063 Iustin Pop
    self.dev_path = "/dev/md%d" % minor
846 a8083063 Iustin Pop
847 a8083063 Iustin Pop
  def Assemble(self):
848 a8083063 Iustin Pop
    """Assemble the MD device.
849 a8083063 Iustin Pop

850 a8083063 Iustin Pop
    At this point we should have:
851 a8083063 Iustin Pop
      - list of children devices
852 a8083063 Iustin Pop
      - uuid
853 a8083063 Iustin Pop

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

880 a8083063 Iustin Pop
    This does a 'mdadm --stop' so after this command, the array is no
881 a8083063 Iustin Pop
    longer available.
882 a8083063 Iustin Pop

883 a8083063 Iustin Pop
    """
884 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
885 a8083063 Iustin Pop
      logger.Info("MD object not attached to a device")
886 a8083063 Iustin Pop
      return True
887 a8083063 Iustin Pop
888 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--stop", "/dev/md%d" % self.minor])
889 a8083063 Iustin Pop
    if result.failed:
890 a8083063 Iustin Pop
      logger.Error("Can't stop MD array: %s" % result.fail_reason)
891 a8083063 Iustin Pop
      return False
892 a8083063 Iustin Pop
    self.minor = None
893 a8083063 Iustin Pop
    self.dev_path = None
894 a8083063 Iustin Pop
    return True
895 a8083063 Iustin Pop
896 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
897 a8083063 Iustin Pop
    """Set the maximum sync speed for the MD array.
898 a8083063 Iustin Pop

899 a8083063 Iustin Pop
    """
900 a8083063 Iustin Pop
    result = super(MDRaid1, self).SetSyncSpeed(kbytes)
901 a8083063 Iustin Pop
    if self.minor is None:
902 a8083063 Iustin Pop
      logger.Error("MD array not attached to a device")
903 a8083063 Iustin Pop
      return False
904 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_max" % self.minor, "w")
905 a8083063 Iustin Pop
    try:
906 a8083063 Iustin Pop
      f.write("%d" % kbytes)
907 a8083063 Iustin Pop
    finally:
908 a8083063 Iustin Pop
      f.close()
909 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_min" % self.minor, "w")
910 a8083063 Iustin Pop
    try:
911 a8083063 Iustin Pop
      f.write("%d" % (kbytes/2))
912 a8083063 Iustin Pop
    finally:
913 a8083063 Iustin Pop
      f.close()
914 a8083063 Iustin Pop
    return result
915 a8083063 Iustin Pop
916 a8083063 Iustin Pop
  def GetSyncStatus(self):
917 a8083063 Iustin Pop
    """Returns the sync status of the device.
918 a8083063 Iustin Pop

919 a8083063 Iustin Pop
    Returns:
920 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
921 a8083063 Iustin Pop

922 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
923 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
924 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
925 0834c866 Iustin Pop

926 0834c866 Iustin Pop
    The ldisk parameter is always true for MD devices.
927 a8083063 Iustin Pop

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

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

962 a8083063 Iustin Pop
    """
963 fdbd668d Iustin Pop
    pass
964 a8083063 Iustin Pop
965 a8083063 Iustin Pop
  def Close(self):
966 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
967 a8083063 Iustin Pop

968 a8083063 Iustin Pop
    This is a no-op for the MDRaid1 device type, but see comment for
969 a8083063 Iustin Pop
    `Open()`.
970 a8083063 Iustin Pop

971 a8083063 Iustin Pop
    """
972 fdbd668d Iustin Pop
    pass
973 a8083063 Iustin Pop
974 a8083063 Iustin Pop
975 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
976 0f7f32d9 Iustin Pop
  """Base DRBD class.
977 a8083063 Iustin Pop

978 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
979 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
980 0f7f32d9 Iustin Pop

981 0f7f32d9 Iustin Pop
  """
982 0f7f32d9 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
983 0f7f32d9 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)\)")
984 770fe0f9 Iustin Pop
  _DRBD_MAJOR = 147
985 770fe0f9 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
986 770fe0f9 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
987 770fe0f9 Iustin Pop
  _ST_CONNECTED = "Connected"
988 0f7f32d9 Iustin Pop
989 0f7f32d9 Iustin Pop
  @staticmethod
990 0f7f32d9 Iustin Pop
  def _GetProcData():
991 0f7f32d9 Iustin Pop
    """Return data from /proc/drbd.
992 0f7f32d9 Iustin Pop

993 0f7f32d9 Iustin Pop
    """
994 0f7f32d9 Iustin Pop
    stat = open("/proc/drbd", "r")
995 0f7f32d9 Iustin Pop
    try:
996 0f7f32d9 Iustin Pop
      data = stat.read().splitlines()
997 0f7f32d9 Iustin Pop
    finally:
998 0f7f32d9 Iustin Pop
      stat.close()
999 0f7f32d9 Iustin Pop
    if not data:
1000 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't read any data from /proc/drbd")
1001 0f7f32d9 Iustin Pop
    return data
1002 0f7f32d9 Iustin Pop
1003 5a47ad20 Iustin Pop
  @staticmethod
1004 5a47ad20 Iustin Pop
  def _MassageProcData(data):
1005 5a47ad20 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
1006 5a47ad20 Iustin Pop

1007 5a47ad20 Iustin Pop
    Returns:
1008 5a47ad20 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
1009 5a47ad20 Iustin Pop

1010 5a47ad20 Iustin Pop
    """
1011 5a47ad20 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
1012 5a47ad20 Iustin Pop
    results = {}
1013 5a47ad20 Iustin Pop
    old_minor = old_line = None
1014 5a47ad20 Iustin Pop
    for line in data:
1015 5a47ad20 Iustin Pop
      lresult = lmatch.match(line)
1016 5a47ad20 Iustin Pop
      if lresult is not None:
1017 5a47ad20 Iustin Pop
        if old_minor is not None:
1018 5a47ad20 Iustin Pop
          results[old_minor] = old_line
1019 5a47ad20 Iustin Pop
        old_minor = int(lresult.group(1))
1020 5a47ad20 Iustin Pop
        old_line = line
1021 5a47ad20 Iustin Pop
      else:
1022 5a47ad20 Iustin Pop
        if old_minor is not None:
1023 5a47ad20 Iustin Pop
          old_line += " " + line.strip()
1024 5a47ad20 Iustin Pop
    # add last line
1025 5a47ad20 Iustin Pop
    if old_minor is not None:
1026 5a47ad20 Iustin Pop
      results[old_minor] = old_line
1027 5a47ad20 Iustin Pop
    return results
1028 5a47ad20 Iustin Pop
1029 0f7f32d9 Iustin Pop
  @classmethod
1030 0f7f32d9 Iustin Pop
  def _GetVersion(cls):
1031 0f7f32d9 Iustin Pop
    """Return the DRBD version.
1032 0f7f32d9 Iustin Pop

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

1035 0f7f32d9 Iustin Pop
    """
1036 0f7f32d9 Iustin Pop
    proc_data = cls._GetProcData()
1037 0f7f32d9 Iustin Pop
    first_line = proc_data[0].strip()
1038 0f7f32d9 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
1039 0f7f32d9 Iustin Pop
    if not version:
1040 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
1041 0f7f32d9 Iustin Pop
                                    first_line)
1042 0f7f32d9 Iustin Pop
    return [int(val) for val in version.groups()]
1043 0f7f32d9 Iustin Pop
1044 770fe0f9 Iustin Pop
  @staticmethod
1045 770fe0f9 Iustin Pop
  def _DevPath(minor):
1046 770fe0f9 Iustin Pop
    """Return the path to a drbd device for a given minor.
1047 770fe0f9 Iustin Pop

1048 770fe0f9 Iustin Pop
    """
1049 770fe0f9 Iustin Pop
    return "/dev/drbd%d" % minor
1050 770fe0f9 Iustin Pop
1051 770fe0f9 Iustin Pop
  @classmethod
1052 770fe0f9 Iustin Pop
  def _GetUsedDevs(cls):
1053 770fe0f9 Iustin Pop
    """Compute the list of used DRBD devices.
1054 770fe0f9 Iustin Pop

1055 770fe0f9 Iustin Pop
    """
1056 770fe0f9 Iustin Pop
    data = cls._GetProcData()
1057 770fe0f9 Iustin Pop
1058 770fe0f9 Iustin Pop
    used_devs = {}
1059 770fe0f9 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
1060 770fe0f9 Iustin Pop
    for line in data:
1061 770fe0f9 Iustin Pop
      match = valid_line.match(line)
1062 770fe0f9 Iustin Pop
      if not match:
1063 770fe0f9 Iustin Pop
        continue
1064 770fe0f9 Iustin Pop
      minor = int(match.group(1))
1065 770fe0f9 Iustin Pop
      state = match.group(2)
1066 770fe0f9 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1067 770fe0f9 Iustin Pop
        continue
1068 770fe0f9 Iustin Pop
      used_devs[minor] = state, line
1069 770fe0f9 Iustin Pop
1070 770fe0f9 Iustin Pop
    return used_devs
1071 770fe0f9 Iustin Pop
1072 5a47ad20 Iustin Pop
  def _SetFromMinor(self, minor):
1073 5a47ad20 Iustin Pop
    """Set our parameters based on the given minor.
1074 5a47ad20 Iustin Pop

1075 5a47ad20 Iustin Pop
    This sets our minor variable and our dev_path.
1076 5a47ad20 Iustin Pop

1077 5a47ad20 Iustin Pop
    """
1078 5a47ad20 Iustin Pop
    if minor is None:
1079 5a47ad20 Iustin Pop
      self.minor = self.dev_path = None
1080 5a47ad20 Iustin Pop
    else:
1081 5a47ad20 Iustin Pop
      self.minor = minor
1082 5a47ad20 Iustin Pop
      self.dev_path = self._DevPath(minor)
1083 5a47ad20 Iustin Pop
1084 ae26a287 Iustin Pop
  @staticmethod
1085 ae26a287 Iustin Pop
  def _CheckMetaSize(meta_device):
1086 ae26a287 Iustin Pop
    """Check if the given meta device looks like a valid one.
1087 ae26a287 Iustin Pop

1088 ae26a287 Iustin Pop
    This currently only check the size, which must be around
1089 ae26a287 Iustin Pop
    128MiB.
1090 ae26a287 Iustin Pop

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

1113 f3e513ad Iustin Pop
    This is not supported for drbd devices.
1114 f3e513ad Iustin Pop

1115 f3e513ad Iustin Pop
    """
1116 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1117 f3e513ad Iustin Pop
1118 0f7f32d9 Iustin Pop
1119 0f7f32d9 Iustin Pop
class DRBDev(BaseDRBD):
1120 a8083063 Iustin Pop
  """DRBD block device.
1121 a8083063 Iustin Pop

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

1126 a8083063 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1127 a8083063 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1128 a8083063 Iustin Pop
  data device and the meta_device. The meta device is checked for
1129 a8083063 Iustin Pop
  valid size and is zeroed on create.
1130 a8083063 Iustin Pop

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

1152 a8083063 Iustin Pop
    """
1153 a8083063 Iustin Pop
    data = cls._GetProcData()
1154 a8083063 Iustin Pop
1155 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1156 a8083063 Iustin Pop
    for line in data:
1157 a8083063 Iustin Pop
      match = valid_line.match(line)
1158 a8083063 Iustin Pop
      if match:
1159 a8083063 Iustin Pop
        return int(match.group(1))
1160 a8083063 Iustin Pop
    logger.Error("Error: no free drbd minors!")
1161 0caf6485 Iustin Pop
    raise errors.BlockDeviceError("Can't find a free DRBD minor")
1162 a8083063 Iustin Pop
1163 a8083063 Iustin Pop
  @classmethod
1164 a8083063 Iustin Pop
  def _GetDevInfo(cls, minor):
1165 a8083063 Iustin Pop
    """Get details about a given DRBD minor.
1166 a8083063 Iustin Pop

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

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

1217 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1218 a8083063 Iustin Pop
    method tests if our local backing device is the same as the one in
1219 a8083063 Iustin Pop
    the info parameter, in effect testing if we look like the given
1220 a8083063 Iustin Pop
    device.
1221 a8083063 Iustin Pop

1222 a8083063 Iustin Pop
    """
1223 a8083063 Iustin Pop
    if not ("local_dev" in info and "meta_dev" in info and
1224 a8083063 Iustin Pop
            "meta_index" in info):
1225 a8083063 Iustin Pop
      return False
1226 a8083063 Iustin Pop
1227 a8083063 Iustin Pop
    backend = self._children[0]
1228 a8083063 Iustin Pop
    if backend is not None:
1229 a8083063 Iustin Pop
      retval = (info["local_dev"] == (backend.major, backend.minor))
1230 a8083063 Iustin Pop
    else:
1231 a8083063 Iustin Pop
      retval = (info["local_dev"] == (0, 0))
1232 a8083063 Iustin Pop
    meta = self._children[1]
1233 a8083063 Iustin Pop
    if meta is not None:
1234 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == (meta.major, meta.minor))
1235 a8083063 Iustin Pop
      retval = retval and (info["meta_index"] == 0)
1236 a8083063 Iustin Pop
    else:
1237 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == "internal" and
1238 a8083063 Iustin Pop
                           info["meta_index"] == -1)
1239 a8083063 Iustin Pop
    return retval
1240 a8083063 Iustin Pop
1241 a8083063 Iustin Pop
  def _MatchesNet(self, info):
1242 a8083063 Iustin Pop
    """Test if our network config matches with an existing device.
1243 a8083063 Iustin Pop

1244 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1245 a8083063 Iustin Pop
    method tests if our network configuration is the same as the one
1246 a8083063 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1247 a8083063 Iustin Pop
    device.
1248 a8083063 Iustin Pop

1249 a8083063 Iustin Pop
    """
1250 a8083063 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1251 a8083063 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1252 a8083063 Iustin Pop
      return True
1253 a8083063 Iustin Pop
1254 a8083063 Iustin Pop
    if self._lhost is None:
1255 a8083063 Iustin Pop
      return False
1256 a8083063 Iustin Pop
1257 a8083063 Iustin Pop
    if not ("local_addr" in info and
1258 a8083063 Iustin Pop
            "remote_addr" in info):
1259 a8083063 Iustin Pop
      return False
1260 a8083063 Iustin Pop
1261 a8083063 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1262 a8083063 Iustin Pop
    retval = (retval and
1263 a8083063 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1264 a8083063 Iustin Pop
    return retval
1265 a8083063 Iustin Pop
1266 a8083063 Iustin Pop
  @classmethod
1267 a8083063 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1268 a8083063 Iustin Pop
    """Configure the local part of a DRBD device.
1269 a8083063 Iustin Pop

1270 a8083063 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1271 a8083063 Iustin Pop
    device. And it must be done only once.
1272 a8083063 Iustin Pop

1273 a8083063 Iustin Pop
    """
1274 ae26a287 Iustin Pop
    if not cls._CheckMetaSize(meta):
1275 a8083063 Iustin Pop
      return False
1276 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1277 a8083063 Iustin Pop
                           backend, meta, "0", "-e", "detach"])
1278 a8083063 Iustin Pop
    if result.failed:
1279 a8083063 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1280 a8083063 Iustin Pop
    return not result.failed
1281 a8083063 Iustin Pop
1282 a8083063 Iustin Pop
  @classmethod
1283 a8083063 Iustin Pop
  def _ShutdownLocal(cls, minor):
1284 a8083063 Iustin Pop
    """Detach from the local device.
1285 a8083063 Iustin Pop

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

1289 a8083063 Iustin Pop
    """
1290 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1291 a8083063 Iustin Pop
    if result.failed:
1292 a8083063 Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
1293 a8083063 Iustin Pop
    return not result.failed
1294 a8083063 Iustin Pop
1295 a8083063 Iustin Pop
  @staticmethod
1296 a8083063 Iustin Pop
  def _ShutdownAll(minor):
1297 a8083063 Iustin Pop
    """Deactivate the device.
1298 a8083063 Iustin Pop

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

1301 a8083063 Iustin Pop
    """
1302 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", DRBDev._DevPath(minor), "down"])
1303 a8083063 Iustin Pop
    if result.failed:
1304 a8083063 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
1305 a8083063 Iustin Pop
    return not result.failed
1306 a8083063 Iustin Pop
1307 a8083063 Iustin Pop
  @classmethod
1308 a8083063 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol):
1309 a8083063 Iustin Pop
    """Configure the network part of the device.
1310 a8083063 Iustin Pop

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

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

1350 a8083063 Iustin Pop
    This fails if we don't have a local device.
1351 a8083063 Iustin Pop

1352 a8083063 Iustin Pop
    """
1353 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1354 a8459f1c Iustin Pop
    if result.failed:
1355 a8459f1c Iustin Pop
      logger.Error("Can't shutdown network: %s" % result.output)
1356 a8083063 Iustin Pop
    return not result.failed
1357 a8083063 Iustin Pop
1358 a8083063 Iustin Pop
  def Assemble(self):
1359 a8083063 Iustin Pop
    """Assemble the drbd.
1360 a8083063 Iustin Pop

1361 a8083063 Iustin Pop
    Method:
1362 a8083063 Iustin Pop
      - if we have a local backing device, we bind to it by:
1363 a8083063 Iustin Pop
        - checking the list of used drbd devices
1364 a8083063 Iustin Pop
        - check if the local minor use of any of them is our own device
1365 a8083063 Iustin Pop
        - if yes, abort?
1366 a8083063 Iustin Pop
        - if not, bind
1367 a8083063 Iustin Pop
      - if we have a local/remote net info:
1368 a8083063 Iustin Pop
        - redo the local backing device step for the remote device
1369 a8083063 Iustin Pop
        - check if any drbd device is using the local port,
1370 a8083063 Iustin Pop
          if yes abort
1371 a8083063 Iustin Pop
        - check if any remote drbd device is using the remote
1372 a8083063 Iustin Pop
          port, if yes abort (for now)
1373 a8083063 Iustin Pop
        - bind our net port
1374 a8083063 Iustin Pop
        - bind the remote net port
1375 a8083063 Iustin Pop

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

1411 a8083063 Iustin Pop
    """
1412 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1413 a8083063 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
1414 a8083063 Iustin Pop
      return True
1415 a8083063 Iustin Pop
    if not self._ShutdownAll(self.minor):
1416 a8083063 Iustin Pop
      return False
1417 a8083063 Iustin Pop
    self.minor = None
1418 a8083063 Iustin Pop
    self.dev_path = None
1419 a8083063 Iustin Pop
    return True
1420 a8083063 Iustin Pop
1421 a8083063 Iustin Pop
  def Attach(self):
1422 a8083063 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1423 a8083063 Iustin Pop

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

1428 a8083063 Iustin Pop
    """
1429 a8083063 Iustin Pop
    for minor in self._GetUsedDevs():
1430 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
1431 a8083063 Iustin Pop
      match_l = self._MatchesLocal(info)
1432 a8083063 Iustin Pop
      match_r = self._MatchesNet(info)
1433 a8083063 Iustin Pop
      if match_l and match_r:
1434 a8083063 Iustin Pop
        break
1435 a8083063 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1436 a8083063 Iustin Pop
        res_r = self._AssembleNet(minor,
1437 a8083063 Iustin Pop
                                  (self._lhost, self._lport,
1438 a8083063 Iustin Pop
                                   self._rhost, self._rport),
1439 a8083063 Iustin Pop
                                  "C")
1440 a8083063 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
1441 a8083063 Iustin Pop
          break
1442 a8083063 Iustin Pop
    else:
1443 a8083063 Iustin Pop
      minor = None
1444 a8083063 Iustin Pop
1445 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1446 a8083063 Iustin Pop
    return minor is not None
1447 a8083063 Iustin Pop
1448 a8083063 Iustin Pop
  def Open(self, force=False):
1449 a8083063 Iustin Pop
    """Make the local state primary.
1450 a8083063 Iustin Pop

1451 a8083063 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1452 a8083063 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1453 a8083063 Iustin Pop
    force flag should be only given after creation, when it actually
1454 a8083063 Iustin Pop
    has to be given.
1455 a8083063 Iustin Pop

1456 a8083063 Iustin Pop
    """
1457 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1458 a8083063 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1459 a8083063 Iustin Pop
      return False
1460 a8083063 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1461 a8083063 Iustin Pop
    if force:
1462 a8083063 Iustin Pop
      cmd.append("--do-what-I-say")
1463 a8083063 Iustin Pop
    result = utils.RunCmd(cmd)
1464 a8083063 Iustin Pop
    if result.failed:
1465 fdbd668d Iustin Pop
      msg = ("Can't make drbd device primary: %s" % result.output)
1466 fdbd668d Iustin Pop
      logger.Error(msg)
1467 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1468 a8083063 Iustin Pop
1469 a8083063 Iustin Pop
  def Close(self):
1470 a8083063 Iustin Pop
    """Make the local state secondary.
1471 a8083063 Iustin Pop

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

1474 a8083063 Iustin Pop
    """
1475 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1476 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1477 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1478 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1479 a8083063 Iustin Pop
    if result.failed:
1480 fdbd668d Iustin Pop
      msg = ("Can't switch drbd device to"
1481 fdbd668d Iustin Pop
             " secondary: %s" % result.output)
1482 fdbd668d Iustin Pop
      logger.Error(msg)
1483 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1484 a8083063 Iustin Pop
1485 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1486 a8083063 Iustin Pop
    """Set the speed of the DRBD syncer.
1487 a8083063 Iustin Pop

1488 a8083063 Iustin Pop
    """
1489 a8083063 Iustin Pop
    children_result = super(DRBDev, self).SetSyncSpeed(kbytes)
1490 a8083063 Iustin Pop
    if self.minor is None:
1491 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1492 a8083063 Iustin Pop
      return False
1493 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1494 a8083063 Iustin Pop
                           kbytes])
1495 a8083063 Iustin Pop
    if result.failed:
1496 a8083063 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1497 a8083063 Iustin Pop
    return not result.failed and children_result
1498 a8083063 Iustin Pop
1499 a8083063 Iustin Pop
  def GetSyncStatus(self):
1500 a8083063 Iustin Pop
    """Returns the sync status of the device.
1501 a8083063 Iustin Pop

1502 a8083063 Iustin Pop
    Returns:
1503 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
1504 a8083063 Iustin Pop

1505 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
1506 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
1507 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1508 0834c866 Iustin Pop

1509 0834c866 Iustin Pop
    The ldisk parameter will be returned as True, since the DRBD7
1510 0834c866 Iustin Pop
    devices have not been converted.
1511 a8083063 Iustin Pop

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

1542 a8083063 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1543 a8083063 Iustin Pop

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

1571 a8083063 Iustin Pop
    This writes until we get ENOSPC.
1572 a8083063 Iustin Pop

1573 a8083063 Iustin Pop
    """
1574 a8083063 Iustin Pop
    f = open(device, "w")
1575 a8083063 Iustin Pop
    buf = "\0" * 1048576
1576 a8083063 Iustin Pop
    try:
1577 a8083063 Iustin Pop
      while True:
1578 a8083063 Iustin Pop
        f.write(buf)
1579 a8083063 Iustin Pop
    except IOError, err:
1580 a8083063 Iustin Pop
      if err.errno != errno.ENOSPC:
1581 a8083063 Iustin Pop
        raise
1582 a8083063 Iustin Pop
1583 a8083063 Iustin Pop
  @classmethod
1584 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
1585 a8083063 Iustin Pop
    """Create a new DRBD device.
1586 a8083063 Iustin Pop

1587 a8083063 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1588 a8083063 Iustin Pop
    function just zeroes the meta device.
1589 a8083063 Iustin Pop

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

1607 a8083063 Iustin Pop
    """
1608 a8083063 Iustin Pop
    return self.Shutdown()
1609 a8083063 Iustin Pop
1610 f3e513ad Iustin Pop
1611 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1612 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1613 a2cfdea2 Iustin Pop

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

1618 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1619 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1620 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1621 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1622 a2cfdea2 Iustin Pop

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

1649 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1650 a2cfdea2 Iustin Pop

1651 a2cfdea2 Iustin Pop
    """
1652 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1653 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1654 a2cfdea2 Iustin Pop
    if result.failed:
1655 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't initialize meta device: %s" %
1656 a2cfdea2 Iustin Pop
                                    result.output)
1657 a2cfdea2 Iustin Pop
1658 a2cfdea2 Iustin Pop
  @classmethod
1659 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1660 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1661 a2cfdea2 Iustin Pop

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

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

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

1705 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1706 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1707 a2cfdea2 Iustin Pop

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

1755 a2cfdea2 Iustin Pop
    """
1756 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1757 a2cfdea2 Iustin Pop
    if result.failed:
1758 a2cfdea2 Iustin Pop
      logger.Error("Can't display the drbd config: %s" % result.fail_reason)
1759 3840729d Iustin Pop
      return None
1760 3840729d Iustin Pop
    return result.stdout
1761 3840729d Iustin Pop
1762 3840729d Iustin Pop
  @classmethod
1763 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1764 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1765 3840729d Iustin Pop

1766 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1767 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1768 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1769 3840729d Iustin Pop
    by _GetShowData.
1770 3840729d Iustin Pop

1771 3840729d Iustin Pop
    """
1772 3840729d Iustin Pop
    data = {}
1773 a2cfdea2 Iustin Pop
    if not out:
1774 a2cfdea2 Iustin Pop
      return data
1775 a2cfdea2 Iustin Pop
1776 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1777 a2cfdea2 Iustin Pop
    # run pyparse
1778 a2cfdea2 Iustin Pop
1779 a2cfdea2 Iustin Pop
    try:
1780 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1781 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1782 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't parse drbdsetup show output: %s" %
1783 a2cfdea2 Iustin Pop
                                    str(err))
1784 a2cfdea2 Iustin Pop
1785 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1786 a2cfdea2 Iustin Pop
    for section in results:
1787 a2cfdea2 Iustin Pop
      sname = section[0]
1788 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1789 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1790 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1791 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1792 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1793 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1794 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1795 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1796 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1797 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1798 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1799 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1800 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1801 a2cfdea2 Iustin Pop
    return data
1802 a2cfdea2 Iustin Pop
1803 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1804 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1805 a2cfdea2 Iustin Pop

1806 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1807 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1808 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1809 a2cfdea2 Iustin Pop
    device.
1810 a2cfdea2 Iustin Pop

1811 a2cfdea2 Iustin Pop
    """
1812 b00b95dd Iustin Pop
    if self._children:
1813 b00b95dd Iustin Pop
      backend, meta = self._children
1814 b00b95dd Iustin Pop
    else:
1815 b00b95dd Iustin Pop
      backend = meta = None
1816 b00b95dd Iustin Pop
1817 a2cfdea2 Iustin Pop
    if backend is not None:
1818 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1819 a2cfdea2 Iustin Pop
    else:
1820 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1821 b00b95dd Iustin Pop
1822 a2cfdea2 Iustin Pop
    if meta is not None:
1823 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1824 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1825 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1826 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1827 a2cfdea2 Iustin Pop
    else:
1828 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1829 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1830 a2cfdea2 Iustin Pop
    return retval
1831 a2cfdea2 Iustin Pop
1832 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1833 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1834 a2cfdea2 Iustin Pop

1835 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1836 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1837 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1838 a2cfdea2 Iustin Pop
    device.
1839 a2cfdea2 Iustin Pop

1840 a2cfdea2 Iustin Pop
    """
1841 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1842 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1843 a2cfdea2 Iustin Pop
      return True
1844 a2cfdea2 Iustin Pop
1845 a2cfdea2 Iustin Pop
    if self._lhost is None:
1846 a2cfdea2 Iustin Pop
      return False
1847 a2cfdea2 Iustin Pop
1848 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1849 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1850 a2cfdea2 Iustin Pop
      return False
1851 a2cfdea2 Iustin Pop
1852 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1853 a2cfdea2 Iustin Pop
    retval = (retval and
1854 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1855 a2cfdea2 Iustin Pop
    return retval
1856 a2cfdea2 Iustin Pop
1857 a2cfdea2 Iustin Pop
  @classmethod
1858 a2cfdea2 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1859 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1860 a2cfdea2 Iustin Pop

1861 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1862 a2cfdea2 Iustin Pop
    device. And it must be done only once.
1863 a2cfdea2 Iustin Pop

1864 a2cfdea2 Iustin Pop
    """
1865 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta):
1866 a2cfdea2 Iustin Pop
      return False
1867 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1868 a2cfdea2 Iustin Pop
                           backend, meta, "0", "-e", "detach",
1869 a2cfdea2 Iustin Pop
                           "--create-device"])
1870 a2cfdea2 Iustin Pop
    if result.failed:
1871 a2cfdea2 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1872 a2cfdea2 Iustin Pop
    return not result.failed
1873 a2cfdea2 Iustin Pop
1874 a2cfdea2 Iustin Pop
  @classmethod
1875 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1876 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1877 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1878 a2cfdea2 Iustin Pop

1879 a2cfdea2 Iustin Pop
    """
1880 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1881 52857176 Iustin Pop
    if None in net_info:
1882 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1883 52857176 Iustin Pop
      # sure its shutdown
1884 52857176 Iustin Pop
      return cls._ShutdownNet(minor)
1885 52857176 Iustin Pop
1886 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1887 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1888 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1889 f38478b2 Iustin Pop
            "-B", "consensus",
1890 f38478b2 Iustin Pop
            ]
1891 a2cfdea2 Iustin Pop
    if dual_pri:
1892 a2cfdea2 Iustin Pop
      args.append("-m")
1893 a2cfdea2 Iustin Pop
    if hmac and secret:
1894 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1895 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1896 a2cfdea2 Iustin Pop
    if result.failed:
1897 a2cfdea2 Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s" %
1898 a2cfdea2 Iustin Pop
                   result.fail_reason)
1899 a2cfdea2 Iustin Pop
      return False
1900 a2cfdea2 Iustin Pop
1901 a2cfdea2 Iustin Pop
    timeout = time.time() + 10
1902 a2cfdea2 Iustin Pop
    ok = False
1903 a2cfdea2 Iustin Pop
    while time.time() < timeout:
1904 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1905 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1906 a2cfdea2 Iustin Pop
        time.sleep(1)
1907 a2cfdea2 Iustin Pop
        continue
1908 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1909 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1910 a2cfdea2 Iustin Pop
        time.sleep(1)
1911 a2cfdea2 Iustin Pop
        continue
1912 a2cfdea2 Iustin Pop
      ok = True
1913 a2cfdea2 Iustin Pop
      break
1914 a2cfdea2 Iustin Pop
    if not ok:
1915 a2cfdea2 Iustin Pop
      logger.Error("Timeout while configuring network")
1916 a2cfdea2 Iustin Pop
      return False
1917 a2cfdea2 Iustin Pop
    return True
1918 a2cfdea2 Iustin Pop
1919 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1920 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1921 b00b95dd Iustin Pop

1922 b00b95dd Iustin Pop
    """
1923 b00b95dd Iustin Pop
    if self.minor is None:
1924 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren")
1925 b00b95dd Iustin Pop
    if len(devices) != 2:
1926 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Need two devices for AddChildren")
1927 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1928 03ece5f3 Iustin Pop
    if "local_dev" in info:
1929 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("DRBD8 already attached to a local disk")
1930 b00b95dd Iustin Pop
    backend, meta = devices
1931 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1932 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Children not ready during AddChildren")
1933 b00b95dd Iustin Pop
    backend.Open()
1934 b00b95dd Iustin Pop
    meta.Open()
1935 b00b95dd Iustin Pop
    if not self._CheckMetaSize(meta.dev_path):
1936 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1937 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1938 b00b95dd Iustin Pop
    if not self._IsValidMeta(meta.dev_path):
1939 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1940 b00b95dd Iustin Pop
1941 b00b95dd Iustin Pop
    if not self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path):
1942 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to local storage")
1943 b00b95dd Iustin Pop
    self._children = devices
1944 b00b95dd Iustin Pop
1945 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1946 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1947 b00b95dd Iustin Pop

1948 b00b95dd Iustin Pop
    """
1949 b00b95dd Iustin Pop
    if self.minor is None:
1950 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to drbd8 during"
1951 b00b95dd Iustin Pop
                                    " RemoveChildren")
1952 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1953 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1954 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1955 03ece5f3 Iustin Pop
      return
1956 b00b95dd Iustin Pop
    if len(self._children) != 2:
1957 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We don't have two children: %s" %
1958 b00b95dd Iustin Pop
                                    self._children)
1959 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1960 e739bd57 Iustin Pop
      logger.Error("Requested detach while detached")
1961 e739bd57 Iustin Pop
      return
1962 b00b95dd Iustin Pop
    if len(devices) != 2:
1963 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We need two children in RemoveChildren")
1964 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1965 e739bd57 Iustin Pop
      if dev != child.dev_path:
1966 e739bd57 Iustin Pop
        raise errors.BlockDeviceError("Mismatch in local storage"
1967 e739bd57 Iustin Pop
                                      " (%s != %s) in RemoveChildren" %
1968 e739bd57 Iustin Pop
                                      (dev, child.dev_path))
1969 b00b95dd Iustin Pop
1970 b00b95dd Iustin Pop
    if not self._ShutdownLocal(self.minor):
1971 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't detach from local storage")
1972 b00b95dd Iustin Pop
    self._children = []
1973 b00b95dd Iustin Pop
1974 a2cfdea2 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1975 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1976 a2cfdea2 Iustin Pop

1977 a2cfdea2 Iustin Pop
    """
1978 a2cfdea2 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1979 a2cfdea2 Iustin Pop
    if self.minor is None:
1980 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
1981 a2cfdea2 Iustin Pop
      return False
1982 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1983 a2cfdea2 Iustin Pop
                           kbytes])
1984 a2cfdea2 Iustin Pop
    if result.failed:
1985 a2cfdea2 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1986 a2cfdea2 Iustin Pop
    return not result.failed and children_result
1987 a2cfdea2 Iustin Pop
1988 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1989 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1990 a2cfdea2 Iustin Pop

1991 a2cfdea2 Iustin Pop
    Returns:
1992 583e3f6f Iustin Pop
     (sync_percent, estimated_time, is_degraded)
1993 a2cfdea2 Iustin Pop

1994 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1995 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1996 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1997 0834c866 Iustin Pop

1998 0834c866 Iustin Pop

1999 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
2000 0834c866 Iustin Pop
    network not connected or local disk missing.
2001 0834c866 Iustin Pop

2002 0834c866 Iustin Pop
    We com