Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 333411a7

History | View | Annotate | Download (73.9 kB)

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

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

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

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

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

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

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

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

81 a8083063 Iustin Pop
  """
82 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
83 a8083063 Iustin Pop
    self._children = children
84 a8083063 Iustin Pop
    self.dev_path = None
85 a8083063 Iustin Pop
    self.unique_id = unique_id
86 a8083063 Iustin Pop
    self.major = None
87 a8083063 Iustin Pop
    self.minor = None
88 a8083063 Iustin Pop
89 a8083063 Iustin Pop
  def Assemble(self):
90 a8083063 Iustin Pop
    """Assemble the device from its components.
91 a8083063 Iustin Pop

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

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

100 a8083063 Iustin Pop
    """
101 a8083063 Iustin Pop
    status = True
102 a8083063 Iustin Pop
    for child in self._children:
103 a8083063 Iustin Pop
      if not isinstance(child, BlockDev):
104 a8083063 Iustin Pop
        raise TypeError("Invalid child passed of type '%s'" % type(child))
105 a8083063 Iustin Pop
      if not status:
106 a8083063 Iustin Pop
        break
107 a8083063 Iustin Pop
      status = status and child.Assemble()
108 a8083063 Iustin Pop
      if not status:
109 a8083063 Iustin Pop
        break
110 fdbd668d Iustin Pop
111 fdbd668d Iustin Pop
      try:
112 fdbd668d Iustin Pop
        child.Open()
113 fdbd668d Iustin Pop
      except errors.BlockDeviceError:
114 fdbd668d Iustin Pop
        for child in self._children:
115 fdbd668d Iustin Pop
          child.Shutdown()
116 fdbd668d Iustin Pop
        raise
117 a8083063 Iustin Pop
118 a8083063 Iustin Pop
    if not status:
119 a8083063 Iustin Pop
      for child in self._children:
120 a8083063 Iustin Pop
        child.Shutdown()
121 a8083063 Iustin Pop
    return status
122 a8083063 Iustin Pop
123 a8083063 Iustin Pop
  def Attach(self):
124 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
125 a8083063 Iustin Pop

126 a8083063 Iustin Pop
    """
127 a8083063 Iustin Pop
    raise NotImplementedError
128 a8083063 Iustin Pop
129 a8083063 Iustin Pop
  def Close(self):
130 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
131 a8083063 Iustin Pop

132 a8083063 Iustin Pop
    """
133 a8083063 Iustin Pop
    raise NotImplementedError
134 a8083063 Iustin Pop
135 a8083063 Iustin Pop
  @classmethod
136 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
137 a8083063 Iustin Pop
    """Create the device.
138 a8083063 Iustin Pop

139 a8083063 Iustin Pop
    If the device cannot be created, it will return None
140 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
141 a8083063 Iustin Pop

142 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
143 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
144 a8083063 Iustin Pop
    enough for both creation and assembly (later).
145 a8083063 Iustin Pop

146 a8083063 Iustin Pop
    """
147 a8083063 Iustin Pop
    raise NotImplementedError
148 a8083063 Iustin Pop
149 a8083063 Iustin Pop
  def Remove(self):
150 a8083063 Iustin Pop
    """Remove this device.
151 a8083063 Iustin Pop

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

156 a8083063 Iustin Pop
    """
157 a8083063 Iustin Pop
    raise NotImplementedError
158 a8083063 Iustin Pop
159 f3e513ad Iustin Pop
  def Rename(self, new_id):
160 f3e513ad Iustin Pop
    """Rename this device.
161 f3e513ad Iustin Pop

162 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
163 f3e513ad Iustin Pop

164 f3e513ad Iustin Pop
    """
165 f3e513ad Iustin Pop
    raise NotImplementedError
166 f3e513ad Iustin Pop
167 a8083063 Iustin Pop
  def Open(self, force=False):
168 a8083063 Iustin Pop
    """Make the device ready for use.
169 a8083063 Iustin Pop

170 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
171 a8083063 Iustin Pop
    devices need this.
172 a8083063 Iustin Pop

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

176 a8083063 Iustin Pop
    """
177 a8083063 Iustin Pop
    raise NotImplementedError
178 a8083063 Iustin Pop
179 a8083063 Iustin Pop
  def Shutdown(self):
180 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
181 a8083063 Iustin Pop

182 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
183 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
184 a8083063 Iustin Pop
    assembled after this call.
185 a8083063 Iustin Pop

186 a8083063 Iustin Pop
    """
187 a8083063 Iustin Pop
    raise NotImplementedError
188 a8083063 Iustin Pop
189 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
190 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
191 a8083063 Iustin Pop

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

194 a8083063 Iustin Pop
    """
195 a8083063 Iustin Pop
    result = True
196 a8083063 Iustin Pop
    if self._children:
197 a8083063 Iustin Pop
      for child in self._children:
198 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
199 a8083063 Iustin Pop
    return result
200 a8083063 Iustin Pop
201 a8083063 Iustin Pop
  def GetSyncStatus(self):
202 a8083063 Iustin Pop
    """Returns the sync status of the device.
203 a8083063 Iustin Pop

204 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
205 a8083063 Iustin Pop
    status of the mirror.
206 a8083063 Iustin Pop

207 a8083063 Iustin Pop
    Returns:
208 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
209 0834c866 Iustin Pop

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

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

215 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
216 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
217 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
218 a8083063 Iustin Pop

219 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
220 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
221 0834c866 Iustin Pop
    return False (not degraded).
222 0834c866 Iustin Pop

223 a8083063 Iustin Pop
    """
224 0834c866 Iustin Pop
    return None, None, False, False
225 a8083063 Iustin Pop
226 a8083063 Iustin Pop
227 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
228 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
229 a8083063 Iustin Pop

230 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
231 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
232 a8083063 Iustin Pop
    children.
233 a8083063 Iustin Pop

234 a8083063 Iustin Pop
    """
235 0834c866 Iustin Pop
    min_percent, max_time, is_degraded, ldisk = self.GetSyncStatus()
236 a8083063 Iustin Pop
    if self._children:
237 a8083063 Iustin Pop
      for child in self._children:
238 0834c866 Iustin Pop
        c_percent, c_time, c_degraded, c_ldisk = child.GetSyncStatus()
239 a8083063 Iustin Pop
        if min_percent is None:
240 a8083063 Iustin Pop
          min_percent = c_percent
241 a8083063 Iustin Pop
        elif c_percent is not None:
242 a8083063 Iustin Pop
          min_percent = min(min_percent, c_percent)
243 a8083063 Iustin Pop
        if max_time is None:
244 a8083063 Iustin Pop
          max_time = c_time
245 a8083063 Iustin Pop
        elif c_time is not None:
246 a8083063 Iustin Pop
          max_time = max(max_time, c_time)
247 a8083063 Iustin Pop
        is_degraded = is_degraded or c_degraded
248 0834c866 Iustin Pop
        ldisk = ldisk or c_ldisk
249 0834c866 Iustin Pop
    return min_percent, max_time, is_degraded, ldisk
250 a8083063 Iustin Pop
251 a8083063 Iustin Pop
252 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
253 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
254 a0c3fea1 Michael Hanselmann

255 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
256 a0c3fea1 Michael Hanselmann

257 a0c3fea1 Michael Hanselmann
    """
258 a0c3fea1 Michael Hanselmann
    for child in self._children:
259 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
260 a0c3fea1 Michael Hanselmann
261 a0c3fea1 Michael Hanselmann
262 a8083063 Iustin Pop
  def __repr__(self):
263 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
264 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
265 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
266 a8083063 Iustin Pop
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
269 a8083063 Iustin Pop
  """Logical Volume block device.
270 a8083063 Iustin Pop

271 a8083063 Iustin Pop
  """
272 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
273 a8083063 Iustin Pop
    """Attaches to a LV device.
274 a8083063 Iustin Pop

275 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
276 a8083063 Iustin Pop

277 a8083063 Iustin Pop
    """
278 a8083063 Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children)
279 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
280 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
281 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
282 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
283 a8083063 Iustin Pop
    self.Attach()
284 a8083063 Iustin Pop
285 a8083063 Iustin Pop
  @classmethod
286 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
287 a8083063 Iustin Pop
    """Create a new logical volume.
288 a8083063 Iustin Pop

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

319 a8083063 Iustin Pop
    Args:
320 a8083063 Iustin Pop
      vg_name: the volume group name
321 a8083063 Iustin Pop

322 a8083063 Iustin Pop
    Returns:
323 a8083063 Iustin Pop
      list of (free_space, name) with free_space in mebibytes
324 098c0958 Michael Hanselmann

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

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

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

383 a8083063 Iustin Pop
    This method will try to see if an existing and active LV exists
384 c99a3cc0 Manuel Franceschini
    which matches our name. If so, its major/minor will be
385 a8083063 Iustin Pop
    recorded.
386 a8083063 Iustin Pop

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

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

409 a8083063 Iustin Pop
    """
410 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
411 5574047a Iustin Pop
    if result.failed:
412 5574047a Iustin Pop
      logger.Error("Can't activate lv %s: %s" % (self.dev_path, result.output))
413 5574047a Iustin Pop
    return not result.failed
414 a8083063 Iustin Pop
415 a8083063 Iustin Pop
  def Shutdown(self):
416 a8083063 Iustin Pop
    """Shutdown the device.
417 a8083063 Iustin Pop

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

421 a8083063 Iustin Pop
    """
422 a8083063 Iustin Pop
    return True
423 a8083063 Iustin Pop
424 9db6dbce Iustin Pop
  def GetSyncStatus(self):
425 9db6dbce Iustin Pop
    """Returns the sync status of the device.
426 9db6dbce Iustin Pop

427 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
428 9db6dbce Iustin Pop
    status of the mirror.
429 9db6dbce Iustin Pop

430 9db6dbce Iustin Pop
    Returns:
431 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
432 9db6dbce Iustin Pop

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

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

444 9db6dbce Iustin Pop
    """
445 9db6dbce Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
446 9db6dbce Iustin Pop
    if result.failed:
447 6c896e2f Iustin Pop
      logger.Error("Can't display lv: %s - %s" %
448 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
449 0834c866 Iustin Pop
      return None, None, True, True
450 9db6dbce Iustin Pop
    out = result.stdout.strip()
451 9db6dbce Iustin Pop
    # format: type/permissions/alloc/fixed_minor/state/open
452 9db6dbce Iustin Pop
    if len(out) != 6:
453 0834c866 Iustin Pop
      logger.Debug("Error in lvs output: attrs=%s, len != 6" % out)
454 0834c866 Iustin Pop
      return None, None, True, True
455 0834c866 Iustin Pop
    ldisk = out[0] == 'v' # virtual volume, i.e. doesn't have
456 0834c866 Iustin Pop
                          # backing storage
457 0834c866 Iustin Pop
    return None, None, ldisk, ldisk
458 9db6dbce Iustin Pop
459 a8083063 Iustin Pop
  def Open(self, force=False):
460 a8083063 Iustin Pop
    """Make the device ready for I/O.
461 a8083063 Iustin Pop

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

464 a8083063 Iustin Pop
    """
465 fdbd668d Iustin Pop
    pass
466 a8083063 Iustin Pop
467 a8083063 Iustin Pop
  def Close(self):
468 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
469 a8083063 Iustin Pop

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

472 a8083063 Iustin Pop
    """
473 fdbd668d Iustin Pop
    pass
474 a8083063 Iustin Pop
475 a8083063 Iustin Pop
  def Snapshot(self, size):
476 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
477 a8083063 Iustin Pop

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

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

529 a8083063 Iustin Pop
  """
530 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
531 a8083063 Iustin Pop
    super(MDRaid1, self).__init__(unique_id, children)
532 a8083063 Iustin Pop
    self.major = 9
533 a8083063 Iustin Pop
    self.Attach()
534 a8083063 Iustin Pop
535 a8083063 Iustin Pop
  def Attach(self):
536 a8083063 Iustin Pop
    """Find an array which matches our config and attach to it.
537 a8083063 Iustin Pop

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

540 a8083063 Iustin Pop
    """
541 a8083063 Iustin Pop
    minor = self._FindMDByUUID(self.unique_id)
542 a8083063 Iustin Pop
    if minor is not None:
543 a8083063 Iustin Pop
      self._SetFromMinor(minor)
544 a8083063 Iustin Pop
    else:
545 a8083063 Iustin Pop
      self.minor = None
546 a8083063 Iustin Pop
      self.dev_path = None
547 a8083063 Iustin Pop
548 a8083063 Iustin Pop
    return (minor is not None)
549 a8083063 Iustin Pop
550 a8083063 Iustin Pop
  @staticmethod
551 a8083063 Iustin Pop
  def _GetUsedDevs():
552 a8083063 Iustin Pop
    """Compute the list of in-use MD devices.
553 a8083063 Iustin Pop

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

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

576 a8083063 Iustin Pop
    Currently only uuid is returned.
577 a8083063 Iustin Pop

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

599 a8083063 Iustin Pop
    This code assumes that there are 256 minors only.
600 a8083063 Iustin Pop

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

617 a8083063 Iustin Pop
    """
618 a8083063 Iustin Pop
    md_list = cls._GetUsedDevs()
619 a8083063 Iustin Pop
    for minor in md_list:
620 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
621 a8083063 Iustin Pop
      if info and info["uuid"] == uuid:
622 a8083063 Iustin Pop
        return minor
623 a8083063 Iustin Pop
    return None
624 a8083063 Iustin Pop
625 1a87dca7 Iustin Pop
  @staticmethod
626 1a87dca7 Iustin Pop
  def _ZeroSuperblock(dev_path):
627 1a87dca7 Iustin Pop
    """Zero the possible locations for an MD superblock.
628 1a87dca7 Iustin Pop

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

633 1a87dca7 Iustin Pop
    The superblocks are located at (negative values are relative to
634 1a87dca7 Iustin Pop
    the end of the block device):
635 1a87dca7 Iustin Pop
      - -128k to end for version 0.90 superblock
636 1a87dca7 Iustin Pop
      - -8k to -12k for version 1.0 superblock (included in the above)
637 1a87dca7 Iustin Pop
      - 0k to 4k for version 1.1 superblock
638 1a87dca7 Iustin Pop
      - 4k to 8k for version 1.2 superblock
639 1a87dca7 Iustin Pop

640 1a87dca7 Iustin Pop
    To cover all situations, the zero-ing will be:
641 1a87dca7 Iustin Pop
      - 0k to 128k
642 1a87dca7 Iustin Pop
      - -128k to end
643 1a87dca7 Iustin Pop

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

647 1a87dca7 Iustin Pop
    Note that this function depends on the fact that one can open,
648 1a87dca7 Iustin Pop
    read and write block devices normally.
649 1a87dca7 Iustin Pop

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

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

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

707 a8083063 Iustin Pop
    """
708 a8083063 Iustin Pop
    #TODO: maybe zero superblock on child devices?
709 a8083063 Iustin Pop
    return self.Shutdown()
710 a8083063 Iustin Pop
711 f3e513ad Iustin Pop
  def Rename(self, new_id):
712 f3e513ad Iustin Pop
    """Rename a device.
713 f3e513ad Iustin Pop

714 f3e513ad Iustin Pop
    This is not supported for md raid1 devices.
715 f3e513ad Iustin Pop

716 f3e513ad Iustin Pop
    """
717 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a md raid1 device")
718 a8083063 Iustin Pop
719 153d9724 Iustin Pop
  def AddChildren(self, devices):
720 153d9724 Iustin Pop
    """Add new member(s) to the md raid1.
721 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

866 a8083063 Iustin Pop
    Returns:
867 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
868 a8083063 Iustin Pop

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

873 0834c866 Iustin Pop
    The ldisk parameter is always true for MD devices.
874 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

981 c3f9340c Guido Trotter
    This will return a dict with keys:
982 c3f9340c Guido Trotter
      k_major,
983 c3f9340c Guido Trotter
      k_minor,
984 c3f9340c Guido Trotter
      k_point,
985 c3f9340c Guido Trotter
      api,
986 c3f9340c Guido Trotter
      proto,
987 c3f9340c Guido Trotter
      proto2 (only on drbd > 8.2.X)
988 0f7f32d9 Iustin Pop

989 0f7f32d9 Iustin Pop
    """
990 0f7f32d9 Iustin Pop
    proc_data = cls._GetProcData()
991 0f7f32d9 Iustin Pop
    first_line = proc_data[0].strip()
992 0f7f32d9 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
993 0f7f32d9 Iustin Pop
    if not version:
994 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
995 0f7f32d9 Iustin Pop
                                    first_line)
996 c3f9340c Guido Trotter
997 c3f9340c Guido Trotter
    values = version.groups()
998 c3f9340c Guido Trotter
    retval = {'k_major': int(values[0]),
999 c3f9340c Guido Trotter
              'k_minor': int(values[1]),
1000 c3f9340c Guido Trotter
              'k_point': int(values[2]),
1001 c3f9340c Guido Trotter
              'api': int(values[3]),
1002 c3f9340c Guido Trotter
              'proto': int(values[4]),
1003 c3f9340c Guido Trotter
             }
1004 c3f9340c Guido Trotter
    if values[5] is not None:
1005 c3f9340c Guido Trotter
      retval['proto2'] = values[5]
1006 c3f9340c Guido Trotter
1007 c3f9340c Guido Trotter
    return retval
1008 0f7f32d9 Iustin Pop
1009 770fe0f9 Iustin Pop
  @staticmethod
1010 770fe0f9 Iustin Pop
  def _DevPath(minor):
1011 770fe0f9 Iustin Pop
    """Return the path to a drbd device for a given minor.
1012 770fe0f9 Iustin Pop

1013 770fe0f9 Iustin Pop
    """
1014 770fe0f9 Iustin Pop
    return "/dev/drbd%d" % minor
1015 770fe0f9 Iustin Pop
1016 770fe0f9 Iustin Pop
  @classmethod
1017 770fe0f9 Iustin Pop
  def _GetUsedDevs(cls):
1018 770fe0f9 Iustin Pop
    """Compute the list of used DRBD devices.
1019 770fe0f9 Iustin Pop

1020 770fe0f9 Iustin Pop
    """
1021 770fe0f9 Iustin Pop
    data = cls._GetProcData()
1022 770fe0f9 Iustin Pop
1023 770fe0f9 Iustin Pop
    used_devs = {}
1024 770fe0f9 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
1025 770fe0f9 Iustin Pop
    for line in data:
1026 770fe0f9 Iustin Pop
      match = valid_line.match(line)
1027 770fe0f9 Iustin Pop
      if not match:
1028 770fe0f9 Iustin Pop
        continue
1029 770fe0f9 Iustin Pop
      minor = int(match.group(1))
1030 770fe0f9 Iustin Pop
      state = match.group(2)
1031 770fe0f9 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1032 770fe0f9 Iustin Pop
        continue
1033 770fe0f9 Iustin Pop
      used_devs[minor] = state, line
1034 770fe0f9 Iustin Pop
1035 770fe0f9 Iustin Pop
    return used_devs
1036 770fe0f9 Iustin Pop
1037 5a47ad20 Iustin Pop
  def _SetFromMinor(self, minor):
1038 5a47ad20 Iustin Pop
    """Set our parameters based on the given minor.
1039 5a47ad20 Iustin Pop

1040 5a47ad20 Iustin Pop
    This sets our minor variable and our dev_path.
1041 5a47ad20 Iustin Pop

1042 5a47ad20 Iustin Pop
    """
1043 5a47ad20 Iustin Pop
    if minor is None:
1044 5a47ad20 Iustin Pop
      self.minor = self.dev_path = None
1045 5a47ad20 Iustin Pop
    else:
1046 5a47ad20 Iustin Pop
      self.minor = minor
1047 5a47ad20 Iustin Pop
      self.dev_path = self._DevPath(minor)
1048 5a47ad20 Iustin Pop
1049 ae26a287 Iustin Pop
  @staticmethod
1050 ae26a287 Iustin Pop
  def _CheckMetaSize(meta_device):
1051 ae26a287 Iustin Pop
    """Check if the given meta device looks like a valid one.
1052 ae26a287 Iustin Pop

1053 ae26a287 Iustin Pop
    This currently only check the size, which must be around
1054 ae26a287 Iustin Pop
    128MiB.
1055 ae26a287 Iustin Pop

1056 ae26a287 Iustin Pop
    """
1057 ae26a287 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1058 ae26a287 Iustin Pop
    if result.failed:
1059 6c896e2f Iustin Pop
      logger.Error("Failed to get device size: %s - %s" %
1060 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
1061 ae26a287 Iustin Pop
      return False
1062 ae26a287 Iustin Pop
    try:
1063 ae26a287 Iustin Pop
      sectors = int(result.stdout)
1064 ae26a287 Iustin Pop
    except ValueError:
1065 ae26a287 Iustin Pop
      logger.Error("Invalid output from blockdev: '%s'" % result.stdout)
1066 ae26a287 Iustin Pop
      return False
1067 ae26a287 Iustin Pop
    bytes = sectors * 512
1068 ae26a287 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
1069 ae26a287 Iustin Pop
      logger.Error("Meta device too small (%.2fMib)" % (bytes / 1024 / 1024))
1070 ae26a287 Iustin Pop
      return False
1071 ae26a287 Iustin Pop
    if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
1072 ae26a287 Iustin Pop
      logger.Error("Meta device too big (%.2fMiB)" % (bytes / 1024 / 1024))
1073 ae26a287 Iustin Pop
      return False
1074 ae26a287 Iustin Pop
    return True
1075 ae26a287 Iustin Pop
1076 f3e513ad Iustin Pop
  def Rename(self, new_id):
1077 f3e513ad Iustin Pop
    """Rename a device.
1078 f3e513ad Iustin Pop

1079 f3e513ad Iustin Pop
    This is not supported for drbd devices.
1080 f3e513ad Iustin Pop

1081 f3e513ad Iustin Pop
    """
1082 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1083 f3e513ad Iustin Pop
1084 0f7f32d9 Iustin Pop
1085 0f7f32d9 Iustin Pop
class DRBDev(BaseDRBD):
1086 a8083063 Iustin Pop
  """DRBD block device.
1087 a8083063 Iustin Pop

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

1092 a8083063 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1093 a8083063 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1094 a8083063 Iustin Pop
  data device and the meta_device. The meta device is checked for
1095 a8083063 Iustin Pop
  valid size and is zeroed on create.
1096 a8083063 Iustin Pop

1097 a8083063 Iustin Pop
  """
1098 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
1099 a8083063 Iustin Pop
    super(DRBDev, self).__init__(unique_id, children)
1100 a8083063 Iustin Pop
    self.major = self._DRBD_MAJOR
1101 c3f9340c Guido Trotter
    version = self._GetVersion()
1102 c3f9340c Guido Trotter
    if version['k_major'] != 0 and version['k_minor'] != 7:
1103 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1104 0f7f32d9 Iustin Pop
                                    " requested ganeti usage: kernel is"
1105 c3f9340c Guido Trotter
                                    " %s.%s, ganeti wants 0.7" %
1106 c3f9340c Guido Trotter
                                    (version['k_major'], version['k_minor']))
1107 a8083063 Iustin Pop
    if len(children) != 2:
1108 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1109 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1110 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1111 a8083063 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
1112 a8083063 Iustin Pop
    self.Attach()
1113 a8083063 Iustin Pop
1114 a8083063 Iustin Pop
  @classmethod
1115 a8083063 Iustin Pop
  def _FindUnusedMinor(cls):
1116 a8083063 Iustin Pop
    """Find an unused DRBD device.
1117 a8083063 Iustin Pop

1118 a8083063 Iustin Pop
    """
1119 a8083063 Iustin Pop
    data = cls._GetProcData()
1120 a8083063 Iustin Pop
1121 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1122 a8083063 Iustin Pop
    for line in data:
1123 a8083063 Iustin Pop
      match = valid_line.match(line)
1124 a8083063 Iustin Pop
      if match:
1125 a8083063 Iustin Pop
        return int(match.group(1))
1126 a8083063 Iustin Pop
    logger.Error("Error: no free drbd minors!")
1127 0caf6485 Iustin Pop
    raise errors.BlockDeviceError("Can't find a free DRBD minor")
1128 a8083063 Iustin Pop
1129 a8083063 Iustin Pop
  @classmethod
1130 a8083063 Iustin Pop
  def _GetDevInfo(cls, minor):
1131 a8083063 Iustin Pop
    """Get details about a given DRBD minor.
1132 a8083063 Iustin Pop

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

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

1184 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1185 a8083063 Iustin Pop
    method tests if our local backing device is the same as the one in
1186 a8083063 Iustin Pop
    the info parameter, in effect testing if we look like the given
1187 a8083063 Iustin Pop
    device.
1188 a8083063 Iustin Pop

1189 a8083063 Iustin Pop
    """
1190 a8083063 Iustin Pop
    if not ("local_dev" in info and "meta_dev" in info and
1191 a8083063 Iustin Pop
            "meta_index" in info):
1192 a8083063 Iustin Pop
      return False
1193 a8083063 Iustin Pop
1194 a8083063 Iustin Pop
    backend = self._children[0]
1195 a8083063 Iustin Pop
    if backend is not None:
1196 a8083063 Iustin Pop
      retval = (info["local_dev"] == (backend.major, backend.minor))
1197 a8083063 Iustin Pop
    else:
1198 a8083063 Iustin Pop
      retval = (info["local_dev"] == (0, 0))
1199 a8083063 Iustin Pop
    meta = self._children[1]
1200 a8083063 Iustin Pop
    if meta is not None:
1201 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == (meta.major, meta.minor))
1202 a8083063 Iustin Pop
      retval = retval and (info["meta_index"] == 0)
1203 a8083063 Iustin Pop
    else:
1204 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == "internal" and
1205 a8083063 Iustin Pop
                           info["meta_index"] == -1)
1206 a8083063 Iustin Pop
    return retval
1207 a8083063 Iustin Pop
1208 a8083063 Iustin Pop
  def _MatchesNet(self, info):
1209 a8083063 Iustin Pop
    """Test if our network config matches with an existing device.
1210 a8083063 Iustin Pop

1211 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1212 a8083063 Iustin Pop
    method tests if our network configuration is the same as the one
1213 a8083063 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1214 a8083063 Iustin Pop
    device.
1215 a8083063 Iustin Pop

1216 a8083063 Iustin Pop
    """
1217 a8083063 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1218 a8083063 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1219 a8083063 Iustin Pop
      return True
1220 a8083063 Iustin Pop
1221 a8083063 Iustin Pop
    if self._lhost is None:
1222 a8083063 Iustin Pop
      return False
1223 a8083063 Iustin Pop
1224 a8083063 Iustin Pop
    if not ("local_addr" in info and
1225 a8083063 Iustin Pop
            "remote_addr" in info):
1226 a8083063 Iustin Pop
      return False
1227 a8083063 Iustin Pop
1228 a8083063 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1229 a8083063 Iustin Pop
    retval = (retval and
1230 a8083063 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1231 a8083063 Iustin Pop
    return retval
1232 a8083063 Iustin Pop
1233 a8083063 Iustin Pop
  @classmethod
1234 a8083063 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1235 a8083063 Iustin Pop
    """Configure the local part of a DRBD device.
1236 a8083063 Iustin Pop

1237 a8083063 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1238 a8083063 Iustin Pop
    device. And it must be done only once.
1239 a8083063 Iustin Pop

1240 a8083063 Iustin Pop
    """
1241 ae26a287 Iustin Pop
    if not cls._CheckMetaSize(meta):
1242 a8083063 Iustin Pop
      return False
1243 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1244 a8083063 Iustin Pop
                           backend, meta, "0", "-e", "detach"])
1245 a8083063 Iustin Pop
    if result.failed:
1246 a8083063 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1247 a8083063 Iustin Pop
    return not result.failed
1248 a8083063 Iustin Pop
1249 a8083063 Iustin Pop
  @classmethod
1250 a8083063 Iustin Pop
  def _ShutdownLocal(cls, minor):
1251 a8083063 Iustin Pop
    """Detach from the local device.
1252 a8083063 Iustin Pop

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

1256 a8083063 Iustin Pop
    """
1257 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1258 a8083063 Iustin Pop
    if result.failed:
1259 a8083063 Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
1260 a8083063 Iustin Pop
    return not result.failed
1261 a8083063 Iustin Pop
1262 a8083063 Iustin Pop
  @staticmethod
1263 a8083063 Iustin Pop
  def _ShutdownAll(minor):
1264 a8083063 Iustin Pop
    """Deactivate the device.
1265 a8083063 Iustin Pop

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

1268 a8083063 Iustin Pop
    """
1269 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", DRBDev._DevPath(minor), "down"])
1270 a8083063 Iustin Pop
    if result.failed:
1271 a8083063 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
1272 a8083063 Iustin Pop
    return not result.failed
1273 a8083063 Iustin Pop
1274 a8083063 Iustin Pop
  @classmethod
1275 a8083063 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol):
1276 a8083063 Iustin Pop
    """Configure the network part of the device.
1277 a8083063 Iustin Pop

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

1285 a8083063 Iustin Pop
    """
1286 a8083063 Iustin Pop
    lhost, lport, rhost, rport = net_info
1287 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "net",
1288 a8083063 Iustin Pop
                           "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport),
1289 a8083063 Iustin Pop
                           protocol])
1290 a8083063 Iustin Pop
    if result.failed:
1291 6c896e2f Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s - %s" %
1292 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
1293 a8083063 Iustin Pop
      return False
1294 a8083063 Iustin Pop
1295 a8083063 Iustin Pop
    timeout = time.time() + 10
1296 a8083063 Iustin Pop
    ok = False
1297 a8083063 Iustin Pop
    while time.time() < timeout:
1298 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
1299 a8083063 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1300 a8083063 Iustin Pop
        time.sleep(1)
1301 a8083063 Iustin Pop
        continue
1302 a8083063 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1303 a8083063 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1304 a8083063 Iustin Pop
        time.sleep(1)
1305 a8083063 Iustin Pop
        continue
1306 a8083063 Iustin Pop
      ok = True
1307 a8083063 Iustin Pop
      break
1308 a8083063 Iustin Pop
    if not ok:
1309 a8083063 Iustin Pop
      logger.Error("Timeout while configuring network")
1310 a8083063 Iustin Pop
      return False
1311 a8083063 Iustin Pop
    return True
1312 a8083063 Iustin Pop
1313 a8083063 Iustin Pop
  @classmethod
1314 a8083063 Iustin Pop
  def _ShutdownNet(cls, minor):
1315 a8083063 Iustin Pop
    """Disconnect from the remote peer.
1316 a8083063 Iustin Pop

1317 a8083063 Iustin Pop
    This fails if we don't have a local device.
1318 a8083063 Iustin Pop

1319 a8083063 Iustin Pop
    """
1320 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1321 a8459f1c Iustin Pop
    if result.failed:
1322 a8459f1c Iustin Pop
      logger.Error("Can't shutdown network: %s" % result.output)
1323 a8083063 Iustin Pop
    return not result.failed
1324 a8083063 Iustin Pop
1325 a8083063 Iustin Pop
  def Assemble(self):
1326 a8083063 Iustin Pop
    """Assemble the drbd.
1327 a8083063 Iustin Pop

1328 a8083063 Iustin Pop
    Method:
1329 a8083063 Iustin Pop
      - if we have a local backing device, we bind to it by:
1330 a8083063 Iustin Pop
        - checking the list of used drbd devices
1331 a8083063 Iustin Pop
        - check if the local minor use of any of them is our own device
1332 a8083063 Iustin Pop
        - if yes, abort?
1333 a8083063 Iustin Pop
        - if not, bind
1334 a8083063 Iustin Pop
      - if we have a local/remote net info:
1335 a8083063 Iustin Pop
        - redo the local backing device step for the remote device
1336 a8083063 Iustin Pop
        - check if any drbd device is using the local port,
1337 a8083063 Iustin Pop
          if yes abort
1338 a8083063 Iustin Pop
        - check if any remote drbd device is using the remote
1339 a8083063 Iustin Pop
          port, if yes abort (for now)
1340 a8083063 Iustin Pop
        - bind our net port
1341 a8083063 Iustin Pop
        - bind the remote net port
1342 a8083063 Iustin Pop

1343 a8083063 Iustin Pop
    """
1344 a8083063 Iustin Pop
    self.Attach()
1345 a8083063 Iustin Pop
    if self.minor is not None:
1346 a8083063 Iustin Pop
      logger.Info("Already assembled")
1347 a8083063 Iustin Pop
      return True
1348 a8083063 Iustin Pop
1349 a8083063 Iustin Pop
    result = super(DRBDev, self).Assemble()
1350 a8083063 Iustin Pop
    if not result:
1351 a8083063 Iustin Pop
      return result
1352 a8083063 Iustin Pop
1353 a8083063 Iustin Pop
    minor = self._FindUnusedMinor()
1354 a8083063 Iustin Pop
    need_localdev_teardown = False
1355 a8083063 Iustin Pop
    if self._children[0]:
1356 a8083063 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1357 a8083063 Iustin Pop
                                   self._children[1].dev_path)
1358 a8083063 Iustin Pop
      if not result:
1359 a8083063 Iustin Pop
        return False
1360 a8083063 Iustin Pop
      need_localdev_teardown = True
1361 a8083063 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1362 a8083063 Iustin Pop
      result = self._AssembleNet(minor,
1363 a8083063 Iustin Pop
                                 (self._lhost, self._lport,
1364 a8083063 Iustin Pop
                                  self._rhost, self._rport),
1365 a8083063 Iustin Pop
                                 "C")
1366 a8083063 Iustin Pop
      if not result:
1367 a8083063 Iustin Pop
        if need_localdev_teardown:
1368 a8083063 Iustin Pop
          # we will ignore failures from this
1369 a8083063 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
1370 a8083063 Iustin Pop
          self._ShutdownAll(minor)
1371 a8083063 Iustin Pop
        return False
1372 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1373 a8083063 Iustin Pop
    return True
1374 a8083063 Iustin Pop
1375 a8083063 Iustin Pop
  def Shutdown(self):
1376 a8083063 Iustin Pop
    """Shutdown the DRBD device.
1377 a8083063 Iustin Pop

1378 a8083063 Iustin Pop
    """
1379 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1380 a8083063 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
1381 a8083063 Iustin Pop
      return True
1382 a8083063 Iustin Pop
    if not self._ShutdownAll(self.minor):
1383 a8083063 Iustin Pop
      return False
1384 a8083063 Iustin Pop
    self.minor = None
1385 a8083063 Iustin Pop
    self.dev_path = None
1386 a8083063 Iustin Pop
    return True
1387 a8083063 Iustin Pop
1388 a8083063 Iustin Pop
  def Attach(self):
1389 a8083063 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1390 a8083063 Iustin Pop

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

1395 a8083063 Iustin Pop
    """
1396 a8083063 Iustin Pop
    for minor in self._GetUsedDevs():
1397 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
1398 a8083063 Iustin Pop
      match_l = self._MatchesLocal(info)
1399 a8083063 Iustin Pop
      match_r = self._MatchesNet(info)
1400 a8083063 Iustin Pop
      if match_l and match_r:
1401 a8083063 Iustin Pop
        break
1402 a8083063 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1403 a8083063 Iustin Pop
        res_r = self._AssembleNet(minor,
1404 a8083063 Iustin Pop
                                  (self._lhost, self._lport,
1405 a8083063 Iustin Pop
                                   self._rhost, self._rport),
1406 a8083063 Iustin Pop
                                  "C")
1407 a8083063 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
1408 a8083063 Iustin Pop
          break
1409 a8083063 Iustin Pop
    else:
1410 a8083063 Iustin Pop
      minor = None
1411 a8083063 Iustin Pop
1412 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1413 a8083063 Iustin Pop
    return minor is not None
1414 a8083063 Iustin Pop
1415 a8083063 Iustin Pop
  def Open(self, force=False):
1416 a8083063 Iustin Pop
    """Make the local state primary.
1417 a8083063 Iustin Pop

1418 a8083063 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1419 a8083063 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1420 a8083063 Iustin Pop
    force flag should be only given after creation, when it actually
1421 a8083063 Iustin Pop
    has to be given.
1422 a8083063 Iustin Pop

1423 a8083063 Iustin Pop
    """
1424 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1425 a8083063 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1426 a8083063 Iustin Pop
      return False
1427 a8083063 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1428 a8083063 Iustin Pop
    if force:
1429 a8083063 Iustin Pop
      cmd.append("--do-what-I-say")
1430 a8083063 Iustin Pop
    result = utils.RunCmd(cmd)
1431 a8083063 Iustin Pop
    if result.failed:
1432 fdbd668d Iustin Pop
      msg = ("Can't make drbd device primary: %s" % result.output)
1433 fdbd668d Iustin Pop
      logger.Error(msg)
1434 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1435 a8083063 Iustin Pop
1436 a8083063 Iustin Pop
  def Close(self):
1437 a8083063 Iustin Pop
    """Make the local state secondary.
1438 a8083063 Iustin Pop

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

1441 a8083063 Iustin Pop
    """
1442 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1443 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1444 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1445 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1446 a8083063 Iustin Pop
    if result.failed:
1447 fdbd668d Iustin Pop
      msg = ("Can't switch drbd device to"
1448 fdbd668d Iustin Pop
             " secondary: %s" % result.output)
1449 fdbd668d Iustin Pop
      logger.Error(msg)
1450 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1451 a8083063 Iustin Pop
1452 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1453 a8083063 Iustin Pop
    """Set the speed of the DRBD syncer.
1454 a8083063 Iustin Pop

1455 a8083063 Iustin Pop
    """
1456 a8083063 Iustin Pop
    children_result = super(DRBDev, self).SetSyncSpeed(kbytes)
1457 a8083063 Iustin Pop
    if self.minor is None:
1458 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1459 a8083063 Iustin Pop
      return False
1460 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1461 a8083063 Iustin Pop
                           kbytes])
1462 a8083063 Iustin Pop
    if result.failed:
1463 6c896e2f Iustin Pop
      logger.Error("Can't change syncer rate: %s - %s" %
1464 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
1465 a8083063 Iustin Pop
    return not result.failed and children_result
1466 a8083063 Iustin Pop
1467 a8083063 Iustin Pop
  def GetSyncStatus(self):
1468 a8083063 Iustin Pop
    """Returns the sync status of the device.
1469 a8083063 Iustin Pop

1470 a8083063 Iustin Pop
    Returns:
1471 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
1472 a8083063 Iustin Pop

1473 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
1474 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
1475 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1476 0834c866 Iustin Pop

1477 0834c866 Iustin Pop
    The ldisk parameter will be returned as True, since the DRBD7
1478 0834c866 Iustin Pop
    devices have not been converted.
1479 a8083063 Iustin Pop

1480 a8083063 Iustin Pop
    """
1481 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1482 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1483 a8083063 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1484 a8083063 Iustin Pop
    if self.minor not in proc_info:
1485 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1486 a8083063 Iustin Pop
                                    self.minor)
1487 a8083063 Iustin Pop
    line = proc_info[self.minor]
1488 a8083063 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1489 a8083063 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1490 a8083063 Iustin Pop
    if match:
1491 a8083063 Iustin Pop
      sync_percent = float(match.group(1))
1492 a8083063 Iustin Pop
      hours = int(match.group(2))
1493 a8083063 Iustin Pop
      minutes = int(match.group(3))
1494 a8083063 Iustin Pop
      seconds = int(match.group(4))
1495 a8083063 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1496 a8083063 Iustin Pop
    else:
1497 a8083063 Iustin Pop
      sync_percent = None
1498 a8083063 Iustin Pop
      est_time = None
1499 a8083063 Iustin Pop
    match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1500 a8083063 Iustin Pop
    if not match:
1501 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1502 a8083063 Iustin Pop
                                    self.minor)
1503 a8083063 Iustin Pop
    client_state = match.group(1)
1504 a8083063 Iustin Pop
    is_degraded = client_state != "Connected"
1505 0834c866 Iustin Pop
    return sync_percent, est_time, is_degraded, False
1506 a8083063 Iustin Pop
1507 a8083063 Iustin Pop
  @staticmethod
1508 a8083063 Iustin Pop
  def _ZeroDevice(device):
1509 a8083063 Iustin Pop
    """Zero a device.
1510 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1696 a2cfdea2 Iustin Pop
    """
1697 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1698 a2cfdea2 Iustin Pop
    if result.failed:
1699 6c896e2f Iustin Pop
      logger.Error("Can't display the drbd config: %s - %s" %
1700 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
1701 3840729d Iustin Pop
      return None
1702 3840729d Iustin Pop
    return result.stdout
1703 3840729d Iustin Pop
1704 3840729d Iustin Pop
  @classmethod
1705 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1706 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1707 3840729d Iustin Pop

1708 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1709 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1710 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1711 3840729d Iustin Pop
    by _GetShowData.
1712 3840729d Iustin Pop

1713 3840729d Iustin Pop
    """
1714 3840729d Iustin Pop
    data = {}
1715 a2cfdea2 Iustin Pop
    if not out:
1716 a2cfdea2 Iustin Pop
      return data
1717 a2cfdea2 Iustin Pop
1718 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1719 a2cfdea2 Iustin Pop
    # run pyparse
1720 a2cfdea2 Iustin Pop
1721 a2cfdea2 Iustin Pop
    try:
1722 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1723 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1724 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't parse drbdsetup show output: %s" %
1725 a2cfdea2 Iustin Pop
                                    str(err))
1726 a2cfdea2 Iustin Pop
1727 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1728 a2cfdea2 Iustin Pop
    for section in results:
1729 a2cfdea2 Iustin Pop
      sname = section[0]
1730 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1731 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1732 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1733 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1734 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1735 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1736 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1737 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1738 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1739 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1740 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1741 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1742 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1743 a2cfdea2 Iustin Pop
    return data
1744 a2cfdea2 Iustin Pop
1745 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1746 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1747 a2cfdea2 Iustin Pop

1748 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1749 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1750 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1751 a2cfdea2 Iustin Pop
    device.
1752 a2cfdea2 Iustin Pop

1753 a2cfdea2 Iustin Pop
    """
1754 b00b95dd Iustin Pop
    if self._children:
1755 b00b95dd Iustin Pop
      backend, meta = self._children
1756 b00b95dd Iustin Pop
    else:
1757 b00b95dd Iustin Pop
      backend = meta = None
1758 b00b95dd Iustin Pop
1759 a2cfdea2 Iustin Pop
    if backend is not None:
1760 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1761 a2cfdea2 Iustin Pop
    else:
1762 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1763 b00b95dd Iustin Pop
1764 a2cfdea2 Iustin Pop
    if meta is not None:
1765 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1766 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1767 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1768 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1769 a2cfdea2 Iustin Pop
    else:
1770 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1771 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1772 a2cfdea2 Iustin Pop
    return retval
1773 a2cfdea2 Iustin Pop
1774 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1775 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1776 a2cfdea2 Iustin Pop

1777 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1778 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1779 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1780 a2cfdea2 Iustin Pop
    device.
1781 a2cfdea2 Iustin Pop

1782 a2cfdea2 Iustin Pop
    """
1783 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1784 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1785 a2cfdea2 Iustin Pop
      return True
1786 a2cfdea2 Iustin Pop
1787 a2cfdea2 Iustin Pop
    if self._lhost is None:
1788 a2cfdea2 Iustin Pop
      return False
1789 a2cfdea2 Iustin Pop
1790 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1791 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1792 a2cfdea2 Iustin Pop
      return False
1793 a2cfdea2 Iustin Pop
1794 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1795 a2cfdea2 Iustin Pop
    retval = (retval and
1796 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1797 a2cfdea2 Iustin Pop
    return retval
1798 a2cfdea2 Iustin Pop
1799 a2cfdea2 Iustin Pop
  @classmethod
1800 a2cfdea2 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1801 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1802 a2cfdea2 Iustin Pop

1803 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1804 a2cfdea2 Iustin Pop
    device. And it must be done only once.
1805 a2cfdea2 Iustin Pop

1806 a2cfdea2 Iustin Pop
    """
1807 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta):
1808 a2cfdea2 Iustin Pop
      return False
1809 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1810 333411a7 Guido Trotter
            backend, meta, "0", "-e", "detach", "--create-device"]
1811 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1812 a2cfdea2 Iustin Pop
    if result.failed:
1813 a2cfdea2 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1814 a2cfdea2 Iustin Pop
    return not result.failed
1815 a2cfdea2 Iustin Pop
1816 a2cfdea2 Iustin Pop
  @classmethod
1817 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1818 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1819 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1820 a2cfdea2 Iustin Pop

1821 a2cfdea2 Iustin Pop
    """
1822 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1823 52857176 Iustin Pop
    if None in net_info:
1824 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1825 52857176 Iustin Pop
      # sure its shutdown
1826 52857176 Iustin Pop
      return cls._ShutdownNet(minor)
1827 52857176 Iustin Pop
1828 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1829 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1830 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1831 f38478b2 Iustin Pop
            "-B", "consensus",
1832 ab6cc81c Iustin Pop
            "--create-device",
1833 f38478b2 Iustin Pop
            ]
1834 a2cfdea2 Iustin Pop
    if dual_pri:
1835 a2cfdea2 Iustin Pop
      args.append("-m")
1836 a2cfdea2 Iustin Pop
    if hmac and secret:
1837 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1838 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1839 a2cfdea2 Iustin Pop
    if result.failed:
1840 6c896e2f Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s - %s" %
1841 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
1842 a2cfdea2 Iustin Pop
      return False
1843 a2cfdea2 Iustin Pop
1844 a2cfdea2 Iustin Pop
    timeout = time.time() + 10
1845 a2cfdea2 Iustin Pop
    ok = False
1846 a2cfdea2 Iustin Pop
    while time.time() < timeout:
1847 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1848 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1849 a2cfdea2 Iustin Pop
        time.sleep(1)
1850 a2cfdea2 Iustin Pop
        continue
1851 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1852 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1853 a2cfdea2 Iustin Pop
        time.sleep(1)
1854 a2cfdea2 Iustin Pop
        continue
1855 a2cfdea2 Iustin Pop
      ok = True
1856 a2cfdea2 Iustin Pop
      break
1857 a2cfdea2 Iustin Pop
    if not ok:
1858 a2cfdea2 Iustin Pop
      logger.Error("Timeout while configuring network")
1859 a2cfdea2 Iustin Pop
      return False
1860 a2cfdea2 Iustin Pop
    return True
1861 a2cfdea2 Iustin Pop
1862 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1863 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1864 b00b95dd Iustin Pop

1865 b00b95dd Iustin Pop
    """
1866 b00b95dd Iustin Pop
    if self.minor is None:
1867 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren")
1868 b00b95dd Iustin Pop
    if len(devices) != 2:
1869 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Need two devices for AddChildren")
1870 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1871 03ece5f3 Iustin Pop
    if "local_dev" in info:
1872 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("DRBD8 already attached to a local disk")
1873 b00b95dd Iustin Pop
    backend, meta = devices
1874 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1875 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Children not ready during AddChildren")
1876 b00b95dd Iustin Pop
    backend.Open()
1877 b00b95dd Iustin Pop
    meta.Open()
1878 b00b95dd Iustin Pop
    if not self._CheckMetaSize(meta.dev_path):
1879 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1880 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1881 b00b95dd Iustin Pop
    if not self._IsValidMeta(meta.dev_path):
1882 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1883 b00b95dd Iustin Pop
1884 b00b95dd Iustin Pop
    if not self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path):
1885 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to local storage")
1886 b00b95dd Iustin Pop
    self._children = devices
1887 b00b95dd Iustin Pop
1888 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1889 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1890 b00b95dd Iustin Pop

1891 b00b95dd Iustin Pop
    """
1892 b00b95dd Iustin Pop
    if self.minor is None:
1893 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to drbd8 during"
1894 b00b95dd Iustin Pop
                                    " RemoveChildren")
1895 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1896 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1897 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1898 03ece5f3 Iustin Pop
      return
1899 b00b95dd Iustin Pop
    if len(self._children) != 2:
1900 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We don't have two children: %s" %
1901 b00b95dd Iustin Pop
                                    self._children)
1902 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1903 e739bd57 Iustin Pop
      logger.Error("Requested detach while detached")
1904 e739bd57 Iustin Pop
      return
1905 b00b95dd Iustin Pop
    if len(devices) != 2:
1906 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We need two children in RemoveChildren")
1907 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1908 e739bd57 Iustin Pop
      if dev != child.dev_path:
1909 e739bd57 Iustin Pop
        raise errors.BlockDeviceError("Mismatch in local storage"
1910 e739bd57 Iustin Pop
                                      " (%s != %s) in RemoveChildren" %
1911 e739bd57 Iustin Pop
                                      (dev, child.dev_path))
1912 b00b95dd Iustin Pop
1913 b00b95dd Iustin Pop
    if not self._ShutdownLocal(self.minor):
1914 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't detach from local storage")
1915 b00b95dd Iustin Pop
    self._children = []
1916 b00b95dd Iustin Pop
1917 a2cfdea2 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1918 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1919 a2cfdea2 Iustin Pop

1920 a2cfdea2 Iustin Pop
    """
1921 a2cfdea2 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1922 a2cfdea2 Iustin Pop
    if self.minor is None:
1923 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
1924 a2cfdea2 Iustin Pop
      return False
1925 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1926 a2cfdea2 Iustin Pop
                           kbytes])
1927 a2cfdea2 Iustin Pop
    if result.failed:
1928 6c896e2f Iustin Pop
      logger.Error("Can't change syncer rate: %s - %s" %
1929 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
1930 a2cfdea2 Iustin Pop
    return not result.failed and children_result
1931 a2cfdea2 Iustin Pop
1932 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1933 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1934 a2cfdea2 Iustin Pop

1935 a2cfdea2 Iustin Pop
    Returns:
1936 583e3f6f Iustin Pop
     (sync_percent, estimated_time, is_degraded)
1937 a2cfdea2 Iustin Pop

1938 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1939 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1940 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1941 0834c866 Iustin Pop

1942 0834c866 Iustin Pop

1943 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1944 0834c866 Iustin Pop
    network not connected or local disk missing.
1945 0834c866 Iustin Pop

1946 0834c866 Iustin Pop
    We compute the ldisk parameter based on wheter we have a local
1947 0834c866 Iustin Pop
    disk or not.
1948 a2cfdea2 Iustin Pop

1949 a2cfdea2 Iustin Pop
    """
1950 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1951 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1952 a2cfdea2 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1953 a2cfdea2 Iustin Pop
    if self.minor not in proc_info:
1954 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1955 a2cfdea2 Iustin Pop
                                    self.minor)
1956 a2cfdea2 Iustin Pop
    line = proc_info[self.minor]
1957 a2cfdea2 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1958 a2cfdea2 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1959 a2cfdea2 Iustin Pop
    if match:
1960 a2cfdea2 Iustin Pop
      sync_percent = float(match.group(1))
1961 a2cfdea2 Iustin Pop
      hours = int(match.group(2))
1962 a2cfdea2 Iustin Pop
      minutes = int(match.group(3))
1963 a2cfdea2 Iustin Pop
      seconds = int(match.group(4))
1964 a2cfdea2 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1965 a2cfdea2 Iustin Pop
    else:
1966 a2cfdea2 Iustin Pop
      sync_percent = None
1967 a2cfdea2 Iustin Pop
      est_time = None
1968 583e3f6f Iustin Pop
    match = re.match("^ *\d+: cs:(\w+).*ds:(\w+)/(\w+).*$", line)
1969 a2cfdea2 Iustin Pop
    if not match:
1970 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1971 a2cfdea2 Iustin Pop
                                    self.minor)
1972 a2cfdea2 Iustin Pop
    client_state = match.group(1)
1973 583e3f6f Iustin Pop
    local_disk_state = match.group(2)
1974 0834c866 Iustin Pop
    ldisk = local_disk_state != "UpToDate"
1975 0834c866 Iustin Pop
    is_degraded = client_state != "Connected"
1976 0834c866 Iustin Pop
    return sync_percent, est_time, is_degraded or ldisk, ldisk
1977 a2cfdea2 Iustin Pop
1978 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1979 a2cfdea2 Iustin Pop
    """Make the local state primary.
1980 a2cfdea2 Iustin Pop

1981 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1982 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1983 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1984 f860ff4e Guido Trotter
    is mandatory.
1985 a2cfdea2 Iustin Pop

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

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

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

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

2022 a2cfdea2 Iustin Pop
    """
2023 a2cfdea2 Iustin Pop
    for minor in self._GetUsedDevs():
2024 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
2025 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
2026 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
2027 a2cfdea2 Iustin Pop
      if match_l and match_r:
2028 a2cfdea2 Iustin Pop
        break
2029 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
2030 a2cfdea2 Iustin Pop
        res_r = self._AssembleNet(minor,
2031 a2cfdea2 Iustin Pop
                                  (self._lhost, self._lport,
2032 a2cfdea2 Iustin Pop
                                   self._rhost, self._rport),
2033 a2cfdea2 Iustin Pop
                                  "C")
2034 3840729d Iustin Pop
        if res_r:
2035 3840729d Iustin Pop
          if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2036 3840729d Iustin Pop
            break
2037 fc1dc9d7 Iustin Pop
      # the weakest case: we find something that is only net attached
2038 fc1dc9d7 Iustin Pop
      # even though we were passed some children at init time
2039 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
2040 fc1dc9d7 Iustin Pop
        break
2041 bf25af3b Iustin Pop
2042 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
2043 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
2044 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
2045 bf25af3b Iustin Pop
      # configuration
2046 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
2047 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
2048 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
2049 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
2050 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
2051 9cdbe77f Iustin Pop
        # reconnect to our correct one
2052 9cdbe77f Iustin Pop
        if not self._ShutdownNet(minor):
2053 9cdbe77f Iustin Pop
          raise errors.BlockDeviceError("Device has correct local storage,"
2054 9cdbe77f Iustin Pop
                                        " wrong remote peer and is unable to"
2055 9cdbe77f Iustin Pop
                                        " disconnect in order to attach to"
2056 9cdbe77f Iustin Pop
                                        " the correct peer")
2057 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
2058 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
2059 9cdbe77f Iustin Pop
        # None)
2060 9cdbe77f Iustin Pop
        if (self._AssembleNet(minor, (self._lhost, self._lport,
2061 9cdbe77f Iustin Pop
                                      self._rhost, self._rport), "C") and
2062 3840729d Iustin Pop
            self._MatchesNet(self._GetDevInfo(self._GetShowData(minor)))):
2063 9cdbe77f Iustin Pop
          break
2064 9cdbe77f Iustin Pop
2065 a2cfdea2 Iustin Pop
    else:
2066 a2cfdea2 Iustin Pop
      minor = None
2067 a2cfdea2 Iustin Pop
2068 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2069 a2cfdea2 Iustin Pop
    return minor is not None
2070 a2cfdea2 Iustin Pop
2071 a2cfdea2 Iustin Pop
  def Assemble(self):
2072 a2cfdea2 Iustin Pop
    """Assemble the drbd.
2073 a2cfdea2 Iustin Pop

2074 a2cfdea2 Iustin Pop
    Method:
2075 a2cfdea2 Iustin Pop
      - if we have a local backing device, we bind to it by:
2076 a2cfdea2 Iustin Pop
        - checking the list of used drbd devices
2077 a2cfdea2 Iustin Pop
        - check if the local minor use of any of them is our own device
2078 a2cfdea2 Iustin Pop
        - if yes, abort?
2079 a2cfdea2 Iustin Pop
        - if not, bind
2080 a2cfdea2 Iustin Pop
      - if we have a local/remote net info:
2081 a2cfdea2 Iustin Pop
        - redo the local backing device step for the remote device
2082 a2cfdea2 Iustin Pop
        - check if any drbd device is using the local port,
2083 a2cfdea2 Iustin Pop
          if yes abort
2084 a2cfdea2 Iustin Pop
        - check if any remote drbd device is using the remote
2085 a2cfdea2 Iustin Pop
          port, if yes abort (for now)
2086 a2cfdea2 Iustin Pop
        - bind our net port
2087 a2cfdea2 Iustin Pop
        - bind the remote net port
2088 a2cfdea2 Iustin Pop

2089 a2cfdea2 Iustin Pop
    """
2090 a2cfdea2 Iustin Pop
    self.Attach()
2091 a2cfdea2 Iustin Pop
    if self.minor is not None:
2092 a2cfdea2 Iustin Pop
      logger.Info("Already assembled")
2093 a2cfdea2 Iustin Pop
      return True
2094 a2cfdea2 Iustin Pop
2095 a2cfdea2 Iustin Pop
    result = super(DRBD8, self).Assemble()
2096 a2cfdea2 Iustin Pop
    if not result:
2097 a2cfdea2 Iustin Pop
      return result
2098 a2cfdea2 Iustin Pop
2099 a2cfdea2 Iustin Pop
    minor = self._FindUnusedMinor()
2100 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
2101 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
2102 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
2103 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
2104 a2cfdea2 Iustin Pop
      if not result:
2105 a2cfdea2 Iustin Pop
        return False
2106 a2cfdea2 Iustin Pop
      need_localdev_teardown = True
2107 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
2108 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
2109 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
2110 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
2111 a2cfdea2 Iustin Pop
                                 "C")
2112 a2cfdea2 Iustin Pop
      if not result:
2113 a2cfdea2 Iustin Pop
        if need_localdev_teardown:
2114 a2cfdea2 Iustin Pop
          # we will ignore failures from this
2115 a2cfdea2 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
2116 a2cfdea2 Iustin Pop
          self._ShutdownAll(minor)
2117 a2cfdea2 Iustin Pop
        return False
2118 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2119 a2cfdea2 Iustin Pop
    return True
2120 a2cfdea2 Iustin Pop
2121 a2cfdea2 Iustin Pop
  @classmethod
2122 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
2123 b00b95dd Iustin Pop
    """Detach from the local device.
2124 b00b95dd Iustin Pop

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

2128 b00b95dd Iustin Pop
    """
2129 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2130 b00b95dd Iustin Pop
    if result.failed:
2131 b00b95dd Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
2132 b00b95dd Iustin Pop
    return not result.failed
2133 b00b95dd Iustin Pop
2134 b00b95dd Iustin Pop
  @classmethod
2135 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2136 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2137 f3e513ad Iustin Pop

2138 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2139 f3e513ad Iustin Pop

2140 f3e513ad Iustin Pop
    """
2141 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2142 a8459f1c Iustin Pop
    if result.failed:
2143 a8459f1c Iustin Pop
      logger.Error("Can't shutdown network: %s" % result.output)
2144 f3e513ad Iustin Pop
    return not result.failed
2145 f3e513ad Iustin Pop
2146 f3e513ad Iustin Pop
  @classmethod
2147 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2148 a2cfdea2 Iustin Pop
    """Deactivate the device.
2149 a2cfdea2 Iustin Pop

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

2152 a2cfdea2 Iustin Pop
    """
2153 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2154 a2cfdea2 Iustin Pop
    if result.failed:
2155 a2cfdea2 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
2156 a2cfdea2 Iustin Pop
    return not result.failed
2157 a2cfdea2 Iustin Pop
2158 a2cfdea2 Iustin Pop
  def Shutdown(self):
2159 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2160 a2cfdea2 Iustin Pop

2161 a2cfdea2 Iustin Pop
    """
2162 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2163 a2cfdea2 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
2164 a2cfdea2 Iustin Pop
      return True
2165 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
2166 a2cfdea2 Iustin Pop
      return False
2167 a2cfdea2 Iustin Pop
    self.minor = None
2168 a2cfdea2 Iustin Pop
    self.dev_path = None
2169 a2cfdea2 Iustin Pop
    return True
2170 a2cfdea2 Iustin Pop
2171 a2cfdea2 Iustin Pop
  def Remove(self):
2172 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2173 a2cfdea2 Iustin Pop

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

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

2184 a2cfdea2 Iustin Pop
    """
2185 a2cfdea2 Iustin Pop
    if len(children) != 2:
2186 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2187 a2cfdea2 Iustin Pop
    meta = children[1]
2188 a2cfdea2 Iustin Pop
    meta.Assemble()
2189 a2cfdea2 Iustin Pop
    if not meta.Attach():
2190 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
2191 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
2192 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
2193 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
2194 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
2195 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
2196 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
2197 a2cfdea2 Iustin Pop
2198 a8083063 Iustin Pop
2199 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
2200 6f695a2e Manuel Franceschini
  """File device.
2201 6f695a2e Manuel Franceschini
  
2202 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
2203 6f695a2e Manuel Franceschini

2204 6f695a2e Manuel Franceschini
  The unique_id for the file device is a (file_driver, file_path) tuple.
2205 6f695a2e Manuel Franceschini
  
2206 6f695a2e Manuel Franceschini
  """
2207 6f695a2e Manuel Franceschini
  def __init__(self, unique_id, children):
2208 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
2209 6f695a2e Manuel Franceschini

2210 6f695a2e Manuel Franceschini
    """
2211 6f695a2e Manuel Franceschini
    if children:
2212 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
2213 6f695a2e Manuel Franceschini
    super(FileStorage, self).__init__(unique_id, children)
2214 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2215 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2216 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
2217 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
2218 6f695a2e Manuel Franceschini
2219 6f695a2e Manuel Franceschini
  def Assemble(self):
2220 6f695a2e Manuel Franceschini
    """Assemble the device.
2221 6f695a2e Manuel Franceschini

2222 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
2223 6f695a2e Manuel Franceschini

2224 6f695a2e Manuel Franceschini
    """
2225 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
2226 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("File device '%s' does not exist." %
2227 6f695a2e Manuel Franceschini
                                    self.dev_path)
2228 6f695a2e Manuel Franceschini
    return True
2229 6f695a2e Manuel Franceschini
2230 6f695a2e Manuel Franceschini
  def Shutdown(self):
2231 6f695a2e Manuel Franceschini
    """Shutdown the device.
2232 6f695a2e Manuel Franceschini

2233 6f695a2e Manuel Franceschini
    This is a no-op for the file type, as we don't deacivate
2234 6f695a2e Manuel Franceschini
    the file on shutdown.
2235 6f695a2e Manuel Franceschini

2236 6f695a2e Manuel Franceschini
    """
2237 6f695a2e Manuel Franceschini
    return True
2238 6f695a2e Manuel Franceschini
2239 6f695a2e Manuel Franceschini
  def Open(self, force=False):
2240 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
2241 6f695a2e Manuel Franceschini

2242 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2243 6f695a2e Manuel Franceschini

2244 6f695a2e Manuel Franceschini
    """
2245 6f695a2e Manuel Franceschini
    pass
2246 6f695a2e Manuel Franceschini
2247 6f695a2e Manuel Franceschini
  def Close(self):
2248 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
2249 6f695a2e Manuel Franceschini

2250 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2251 6f695a2e Manuel Franceschini

2252 6f695a2e Manuel Franceschini
    """
2253 6f695a2e Manuel Franceschini
    pass
2254 6f695a2e Manuel Franceschini
2255 6f695a2e Manuel Franceschini
  def Remove(self):
2256 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
2257 6f695a2e Manuel Franceschini

2258 6f695a2e Manuel Franceschini
    Returns:
2259 6f695a2e Manuel Franceschini
      boolean indicating wheter removal of file was successful or not.
2260 6f695a2e Manuel Franceschini

2261 6f695a2e Manuel Franceschini
    """
2262 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
2263 6f695a2e Manuel Franceschini
      return True
2264 6f695a2e Manuel Franceschini
    try:
2265 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
2266 6f695a2e Manuel Franceschini
      return True
2267 6f695a2e Manuel Franceschini
    except OSError, err:
2268 6f695a2e Manuel Franceschini
      logger.Error("Can't remove file '%s': %s"
2269 6f695a2e Manuel Franceschini
                   % (self.dev_path, err))
2270 6f695a2e Manuel Franceschini
      return False
2271 6f695a2e Manuel Franceschini
2272 6f695a2e Manuel Franceschini
  def Attach(self):
2273 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2274 6f695a2e Manuel Franceschini

2275 6f695a2e Manuel Franceschini
    Check if this file already exists.
2276 6f695a2e Manuel Franceschini

2277 6f695a2e Manuel Franceschini
    Returns:
2278 6f695a2e Manuel Franceschini
      boolean indicating if file exists or not.
2279 6f695a2e Manuel Franceschini

2280 6f695a2e Manuel Franceschini
    """
2281 6f695a2e Manuel Franceschini
    if os.path.exists(self.dev_path):
2282 6f695a2e Manuel Franceschini
      return True
2283 6f695a2e Manuel Franceschini
    return False
2284 6f695a2e Manuel Franceschini
2285 6f695a2e Manuel Franceschini
  @classmethod
2286 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
2287 6f695a2e Manuel Franceschini
    """Create a new file.
2288 6f695a2e Manuel Franceschini

2289 6f695a2e Manuel Franceschini
    Args:
2290 6f695a2e Manuel Franceschini
      children:
2291 6f695a2e Manuel Franceschini
      size: integer size of file in MiB
2292 6f695a2e Manuel Franceschini

2293 6f695a2e Manuel Franceschini
    Returns:
2294 6f695a2e Manuel Franceschini
      A ganeti.bdev.FileStorage object.
2295 6f695a2e Manuel Franceschini

2296 6f695a2e Manuel Franceschini
    """
2297 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2298 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2299 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2300 6f695a2e Manuel Franceschini
    try:
2301 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
2302 6f695a2e Manuel Franceschini
    except IOError, err:
2303 b62ddbe5 Guido Trotter
      raise errors.BlockDeviceError("Could not create '%'" % err)
2304 6f695a2e Manuel Franceschini
    else:
2305 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2306 6f695a2e Manuel Franceschini
      f.close()
2307 6f695a2e Manuel Franceschini
2308 6f695a2e Manuel Franceschini
    return FileStorage(unique_id, children)
2309 6f695a2e Manuel Franceschini
2310 6f695a2e Manuel Franceschini
2311 a8083063 Iustin Pop
DEV_MAP = {
2312 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2313 fe96220b Iustin Pop
  constants.LD_MD_R1: MDRaid1,
2314 fe96220b Iustin Pop
  constants.LD_DRBD7: DRBDev,
2315 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2316 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
2317 a8083063 Iustin Pop
  }
2318 a8083063 Iustin Pop
2319 a8083063 Iustin Pop
2320 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
2321 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2322 a8083063 Iustin Pop

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

2326 a8083063 Iustin Pop
  """
2327 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2328 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2329 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2330 a8083063 Iustin Pop
  if not device.Attach():
2331 a8083063 Iustin Pop
    return None
2332 a8083063 Iustin Pop
  return  device
2333 a8083063 Iustin Pop
2334 a8083063 Iustin Pop
2335 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
2336 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2337 a8083063 Iustin Pop

2338 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
2339 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
2340 a8083063 Iustin Pop

2341 a8083063 Iustin Pop
  """
2342 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2343 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2344 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2345 a8083063 Iustin Pop
  if not device.Attach():
2346 a8083063 Iustin Pop
    device.Assemble()
2347 be1ba2bd Iustin Pop
    if not device.Attach():
2348 be1ba2bd Iustin Pop
      raise errors.BlockDeviceError("Can't find a valid block device for"
2349 be1ba2bd Iustin Pop
                                    " %s/%s/%s" %
2350 be1ba2bd Iustin Pop
                                    (dev_type, unique_id, children))
2351 a8083063 Iustin Pop
  return device
2352 a8083063 Iustin Pop
2353 a8083063 Iustin Pop
2354 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2355 a8083063 Iustin Pop
  """Create a device.
2356 a8083063 Iustin Pop

2357 a8083063 Iustin Pop
  """
2358 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2359 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2360 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2361 a8083063 Iustin Pop
  return device