Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 686d7433

History | View | Annotate | Download (50.2 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 468c5f77 Iustin Pop
import logging
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
from ganeti import utils
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 abdf0113 Iustin Pop
  the readonly state (LV has it, if needed in the future) and we are
47 abdf0113 Iustin Pop
  usually looking at this like at a stack, so it's easier to
48 abdf0113 Iustin Pop
  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
    - drbd devices are attached to a local disk/remote peer and made primary
55 a8083063 Iustin Pop

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

61 a8083063 Iustin Pop
  Not all devices implement both the first two as distinct items. LVM
62 a8083063 Iustin Pop
  logical volumes have their unique ID (the pair volume group, logical
63 abdf0113 Iustin Pop
  volume name) in a 1-to-1 relation to the dev path. For DRBD devices,
64 abdf0113 Iustin Pop
  the /dev path is again dynamic and the unique id is the pair (host1,
65 abdf0113 Iustin Pop
  dev1), (host2, dev2).
66 a8083063 Iustin Pop

67 a8083063 Iustin Pop
  You can get to a device in two ways:
68 a8083063 Iustin Pop
    - creating the (real) device, which returns you
69 abdf0113 Iustin Pop
      an attached instance (lvcreate)
70 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
71 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

150 abdf0113 Iustin Pop
    This makes sense only for some of the device types: LV and file
151 abdf0113 Iustin Pop
    storeage. Also note that if the device can't attach, the removal
152 abdf0113 Iustin Pop
    can't be completed.
153 a8083063 Iustin Pop

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

160 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
161 f3e513ad Iustin Pop

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

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

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

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

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

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

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

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

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

205 a8083063 Iustin Pop
    Returns:
206 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
207 0834c866 Iustin Pop

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

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

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

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

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

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

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

253 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
254 a0c3fea1 Michael Hanselmann

255 a0c3fea1 Michael Hanselmann
    """
256 a0c3fea1 Michael Hanselmann
    for child in self._children:
257 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
258 a0c3fea1 Michael Hanselmann
259 1005d816 Iustin Pop
  def Grow(self, amount):
260 1005d816 Iustin Pop
    """Grow the block device.
261 1005d816 Iustin Pop

262 1005d816 Iustin Pop
    Arguments:
263 1005d816 Iustin Pop
      amount: the amount (in mebibytes) to grow with
264 1005d816 Iustin Pop

265 1005d816 Iustin Pop
    Returns: None
266 1005d816 Iustin Pop

267 1005d816 Iustin Pop
    """
268 1005d816 Iustin Pop
    raise NotImplementedError
269 a0c3fea1 Michael Hanselmann
270 a8083063 Iustin Pop
  def __repr__(self):
271 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
272 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
273 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
274 a8083063 Iustin Pop
275 a8083063 Iustin Pop
276 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
277 a8083063 Iustin Pop
  """Logical Volume block device.
278 a8083063 Iustin Pop

279 a8083063 Iustin Pop
  """
280 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
281 a8083063 Iustin Pop
    """Attaches to a LV device.
282 a8083063 Iustin Pop

283 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
284 a8083063 Iustin Pop

285 a8083063 Iustin Pop
    """
286 a8083063 Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children)
287 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
288 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
289 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
290 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
291 99e8295c Iustin Pop
    self._degraded = True
292 99e8295c Iustin Pop
    self.major = self.minor = None
293 a8083063 Iustin Pop
    self.Attach()
294 a8083063 Iustin Pop
295 a8083063 Iustin Pop
  @classmethod
296 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
297 a8083063 Iustin Pop
    """Create a new logical volume.
298 a8083063 Iustin Pop

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

329 a8083063 Iustin Pop
    Args:
330 a8083063 Iustin Pop
      vg_name: the volume group name
331 a8083063 Iustin Pop

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

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

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

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

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

397 a8083063 Iustin Pop
    """
398 cb999543 Iustin Pop
    self.attached = False
399 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
400 99e8295c Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor",
401 99e8295c Iustin Pop
                           self.dev_path])
402 a8083063 Iustin Pop
    if result.failed:
403 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
404 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
405 a8083063 Iustin Pop
      return False
406 99e8295c Iustin Pop
    out = result.stdout.strip().rstrip(',')
407 99e8295c Iustin Pop
    out = out.split(",")
408 99e8295c Iustin Pop
    if len(out) != 3:
409 468c5f77 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 3", str(out))
410 99e8295c Iustin Pop
      return False
411 99e8295c Iustin Pop
412 99e8295c Iustin Pop
    status, major, minor = out[:3]
413 99e8295c Iustin Pop
    if len(status) != 6:
414 468c5f77 Iustin Pop
      logging.error("lvs lv_attr is not 6 characters (%s)", status)
415 99e8295c Iustin Pop
      return False
416 99e8295c Iustin Pop
417 99e8295c Iustin Pop
    try:
418 99e8295c Iustin Pop
      major = int(major)
419 99e8295c Iustin Pop
      minor = int(minor)
420 99e8295c Iustin Pop
    except ValueError, err:
421 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
422 99e8295c Iustin Pop
423 99e8295c Iustin Pop
    self.major = major
424 99e8295c Iustin Pop
    self.minor = minor
425 99e8295c Iustin Pop
    self._degraded = status[0] == 'v' # virtual volume, i.e. doesn't backing
426 99e8295c Iustin Pop
                                      # storage
427 cb999543 Iustin Pop
    self.attached = True
428 99e8295c Iustin Pop
    return True
429 a8083063 Iustin Pop
430 a8083063 Iustin Pop
  def Assemble(self):
431 a8083063 Iustin Pop
    """Assemble the device.
432 a8083063 Iustin Pop

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

437 a8083063 Iustin Pop
    """
438 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
439 5574047a Iustin Pop
    if result.failed:
440 468c5f77 Iustin Pop
      logging.error("Can't activate lv %s: %s", self.dev_path, result.output)
441 cb999543 Iustin Pop
      return False
442 cb999543 Iustin Pop
    return self.Attach()
443 a8083063 Iustin Pop
444 a8083063 Iustin Pop
  def Shutdown(self):
445 a8083063 Iustin Pop
    """Shutdown the device.
446 a8083063 Iustin Pop

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

450 a8083063 Iustin Pop
    """
451 a8083063 Iustin Pop
    return True
452 a8083063 Iustin Pop
453 9db6dbce Iustin Pop
  def GetSyncStatus(self):
454 9db6dbce Iustin Pop
    """Returns the sync status of the device.
455 9db6dbce Iustin Pop

456 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
457 9db6dbce Iustin Pop
    status of the mirror.
458 9db6dbce Iustin Pop

459 9db6dbce Iustin Pop
    Returns:
460 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
461 9db6dbce Iustin Pop

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

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

473 99e8295c Iustin Pop
    The status was already read in Attach, so we just return it.
474 99e8295c Iustin Pop

475 9db6dbce Iustin Pop
    """
476 99e8295c Iustin Pop
    return None, None, self._degraded, self._degraded
477 9db6dbce Iustin Pop
478 a8083063 Iustin Pop
  def Open(self, force=False):
479 a8083063 Iustin Pop
    """Make the device ready for I/O.
480 a8083063 Iustin Pop

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

483 a8083063 Iustin Pop
    """
484 fdbd668d Iustin Pop
    pass
485 a8083063 Iustin Pop
486 a8083063 Iustin Pop
  def Close(self):
487 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
488 a8083063 Iustin Pop

489 a8083063 Iustin Pop
    This is a no-op for the LV device type.
490 a8083063 Iustin Pop

491 a8083063 Iustin Pop
    """
492 fdbd668d Iustin Pop
    pass
493 a8083063 Iustin Pop
494 a8083063 Iustin Pop
  def Snapshot(self, size):
495 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
496 a8083063 Iustin Pop

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

527 a0c3fea1 Michael Hanselmann
    """
528 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
529 a0c3fea1 Michael Hanselmann
530 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
531 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
532 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
533 a0c3fea1 Michael Hanselmann
534 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
535 a0c3fea1 Michael Hanselmann
    text = text[:128]
536 a0c3fea1 Michael Hanselmann
537 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
538 a0c3fea1 Michael Hanselmann
                           self.dev_path])
539 a0c3fea1 Michael Hanselmann
    if result.failed:
540 6c896e2f Iustin Pop
      raise errors.BlockDeviceError("Command: %s error: %s - %s" %
541 6c896e2f Iustin Pop
                                    (result.cmd, result.fail_reason,
542 6c896e2f Iustin Pop
                                     result.output))
543 1005d816 Iustin Pop
  def Grow(self, amount):
544 1005d816 Iustin Pop
    """Grow the logical volume.
545 1005d816 Iustin Pop

546 1005d816 Iustin Pop
    """
547 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
548 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
549 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
550 1005d816 Iustin Pop
    # supports 'cling'
551 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
552 1005d816 Iustin Pop
      result = utils.RunCmd(["lvextend", "--alloc", alloc_policy,
553 1005d816 Iustin Pop
                             "-L", "+%dm" % amount, self.dev_path])
554 1005d816 Iustin Pop
      if not result.failed:
555 1005d816 Iustin Pop
        return
556 1005d816 Iustin Pop
    raise errors.BlockDeviceError("Can't grow LV %s: %s" %
557 1005d816 Iustin Pop
                                  (self.dev_path, result.output))
558 a0c3fea1 Michael Hanselmann
559 a0c3fea1 Michael Hanselmann
560 6b90c22e Iustin Pop
class DRBD8Status(object):
561 6b90c22e Iustin Pop
  """A DRBD status representation class.
562 6b90c22e Iustin Pop

563 6b90c22e Iustin Pop
  Note that this doesn't support unconfigured devices (cs:Unconfigured).
564 6b90c22e Iustin Pop

565 6b90c22e Iustin Pop
  """
566 6b90c22e Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+st:([^/]+)/(\S+)"
567 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
568 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
569 6b90c22e Iustin Pop
                       "\sfinish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
570 6b90c22e Iustin Pop
571 6b90c22e Iustin Pop
  def __init__(self, procline):
572 6b90c22e Iustin Pop
    m = self.LINE_RE.match(procline)
573 6b90c22e Iustin Pop
    if not m:
574 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
575 6b90c22e Iustin Pop
    self.cstatus = m.group(1)
576 6b90c22e Iustin Pop
    self.lrole = m.group(2)
577 6b90c22e Iustin Pop
    self.rrole = m.group(3)
578 6b90c22e Iustin Pop
    self.ldisk = m.group(4)
579 6b90c22e Iustin Pop
    self.rdisk = m.group(5)
580 6b90c22e Iustin Pop
581 6b90c22e Iustin Pop
    self.is_standalone = self.cstatus == "StandAlone"
582 6b90c22e Iustin Pop
    self.is_wfconn = self.cstatus == "WFConnection"
583 6b90c22e Iustin Pop
    self.is_connected = self.cstatus == "Connected"
584 6b90c22e Iustin Pop
    self.is_primary = self.lrole == "Primary"
585 6b90c22e Iustin Pop
    self.is_secondary = self.lrole == "Secondary"
586 6b90c22e Iustin Pop
    self.peer_primary = self.rrole == "Primary"
587 6b90c22e Iustin Pop
    self.peer_secondary = self.rrole == "Secondary"
588 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
589 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
590 6b90c22e Iustin Pop
591 6b90c22e Iustin Pop
    self.is_diskless = self.ldisk == "Diskless"
592 6b90c22e Iustin Pop
    self.is_disk_uptodate = self.ldisk == "UpToDate"
593 6b90c22e Iustin Pop
594 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
595 6b90c22e Iustin Pop
    if m:
596 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
597 6b90c22e Iustin Pop
      hours = int(m.group(2))
598 6b90c22e Iustin Pop
      minutes = int(m.group(3))
599 6b90c22e Iustin Pop
      seconds = int(m.group(4))
600 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
601 6b90c22e Iustin Pop
    else:
602 6b90c22e Iustin Pop
      self.sync_percent = None
603 6b90c22e Iustin Pop
      self.est_time = None
604 6b90c22e Iustin Pop
605 6b90c22e Iustin Pop
    self.is_sync_target = self.peer_sync_source = self.cstatus == "SyncTarget"
606 6b90c22e Iustin Pop
    self.peer_sync_target = self.is_sync_source = self.cstatus == "SyncSource"
607 6b90c22e Iustin Pop
    self.is_resync = self.is_sync_target or self.is_sync_source
608 6b90c22e Iustin Pop
609 6b90c22e Iustin Pop
610 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
611 0f7f32d9 Iustin Pop
  """Base DRBD class.
612 a8083063 Iustin Pop

613 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
614 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
615 0f7f32d9 Iustin Pop

616 abdf0113 Iustin Pop
  """
617 abdf0113 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
618 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
619 a8083063 Iustin Pop
620 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
621 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
622 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
623 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
624 a8083063 Iustin Pop
625 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
626 6b90c22e Iustin Pop
627 abdf0113 Iustin Pop
  @staticmethod
628 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
629 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
630 a8083063 Iustin Pop

631 a8083063 Iustin Pop
    """
632 6b90c22e Iustin Pop
    stat = open(filename, "r")
633 abdf0113 Iustin Pop
    try:
634 abdf0113 Iustin Pop
      data = stat.read().splitlines()
635 abdf0113 Iustin Pop
    finally:
636 abdf0113 Iustin Pop
      stat.close()
637 abdf0113 Iustin Pop
    if not data:
638 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("Can't read any data from %s" % filename)
639 abdf0113 Iustin Pop
    return data
640 a8083063 Iustin Pop
641 abdf0113 Iustin Pop
  @staticmethod
642 abdf0113 Iustin Pop
  def _MassageProcData(data):
643 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
644 a8083063 Iustin Pop

645 abdf0113 Iustin Pop
    Returns:
646 abdf0113 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
647 a8083063 Iustin Pop

648 a8083063 Iustin Pop
    """
649 abdf0113 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
650 abdf0113 Iustin Pop
    results = {}
651 abdf0113 Iustin Pop
    old_minor = old_line = None
652 abdf0113 Iustin Pop
    for line in data:
653 abdf0113 Iustin Pop
      lresult = lmatch.match(line)
654 abdf0113 Iustin Pop
      if lresult is not None:
655 abdf0113 Iustin Pop
        if old_minor is not None:
656 abdf0113 Iustin Pop
          results[old_minor] = old_line
657 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
658 abdf0113 Iustin Pop
        old_line = line
659 abdf0113 Iustin Pop
      else:
660 abdf0113 Iustin Pop
        if old_minor is not None:
661 abdf0113 Iustin Pop
          old_line += " " + line.strip()
662 abdf0113 Iustin Pop
    # add last line
663 abdf0113 Iustin Pop
    if old_minor is not None:
664 abdf0113 Iustin Pop
      results[old_minor] = old_line
665 abdf0113 Iustin Pop
    return results
666 a8083063 Iustin Pop
667 abdf0113 Iustin Pop
  @classmethod
668 abdf0113 Iustin Pop
  def _GetVersion(cls):
669 abdf0113 Iustin Pop
    """Return the DRBD version.
670 a8083063 Iustin Pop

671 abdf0113 Iustin Pop
    This will return a dict with keys:
672 abdf0113 Iustin Pop
      k_major,
673 abdf0113 Iustin Pop
      k_minor,
674 abdf0113 Iustin Pop
      k_point,
675 abdf0113 Iustin Pop
      api,
676 abdf0113 Iustin Pop
      proto,
677 abdf0113 Iustin Pop
      proto2 (only on drbd > 8.2.X)
678 a8083063 Iustin Pop

679 a8083063 Iustin Pop
    """
680 abdf0113 Iustin Pop
    proc_data = cls._GetProcData()
681 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
682 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
683 abdf0113 Iustin Pop
    if not version:
684 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
685 abdf0113 Iustin Pop
                                    first_line)
686 a8083063 Iustin Pop
687 abdf0113 Iustin Pop
    values = version.groups()
688 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
689 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
690 abdf0113 Iustin Pop
              'k_point': int(values[2]),
691 abdf0113 Iustin Pop
              'api': int(values[3]),
692 abdf0113 Iustin Pop
              'proto': int(values[4]),
693 abdf0113 Iustin Pop
             }
694 abdf0113 Iustin Pop
    if values[5] is not None:
695 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
696 a8083063 Iustin Pop
697 abdf0113 Iustin Pop
    return retval
698 abdf0113 Iustin Pop
699 abdf0113 Iustin Pop
  @staticmethod
700 abdf0113 Iustin Pop
  def _DevPath(minor):
701 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
702 a8083063 Iustin Pop

703 a8083063 Iustin Pop
    """
704 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
705 a8083063 Iustin Pop
706 abdf0113 Iustin Pop
  @classmethod
707 abdf0113 Iustin Pop
  def _GetUsedDevs(cls):
708 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
709 a8083063 Iustin Pop

710 a8083063 Iustin Pop
    """
711 abdf0113 Iustin Pop
    data = cls._GetProcData()
712 a8083063 Iustin Pop
713 abdf0113 Iustin Pop
    used_devs = {}
714 abdf0113 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
715 abdf0113 Iustin Pop
    for line in data:
716 abdf0113 Iustin Pop
      match = valid_line.match(line)
717 abdf0113 Iustin Pop
      if not match:
718 abdf0113 Iustin Pop
        continue
719 abdf0113 Iustin Pop
      minor = int(match.group(1))
720 abdf0113 Iustin Pop
      state = match.group(2)
721 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
722 abdf0113 Iustin Pop
        continue
723 abdf0113 Iustin Pop
      used_devs[minor] = state, line
724 a8083063 Iustin Pop
725 abdf0113 Iustin Pop
    return used_devs
726 a8083063 Iustin Pop
727 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
728 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
729 0834c866 Iustin Pop

730 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
731 a8083063 Iustin Pop

732 a8083063 Iustin Pop
    """
733 abdf0113 Iustin Pop
    if minor is None:
734 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
735 cb999543 Iustin Pop
      self.attached = False
736 a8083063 Iustin Pop
    else:
737 abdf0113 Iustin Pop
      self.minor = minor
738 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
739 cb999543 Iustin Pop
      self.attached = True
740 a8083063 Iustin Pop
741 a8083063 Iustin Pop
  @staticmethod
742 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
743 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
744 a8083063 Iustin Pop

745 abdf0113 Iustin Pop
    This currently only check the size, which must be around
746 abdf0113 Iustin Pop
    128MiB.
747 a8083063 Iustin Pop

748 a8083063 Iustin Pop
    """
749 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
750 abdf0113 Iustin Pop
    if result.failed:
751 468c5f77 Iustin Pop
      logging.error("Failed to get device size: %s - %s",
752 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
753 abdf0113 Iustin Pop
      return False
754 a8083063 Iustin Pop
    try:
755 abdf0113 Iustin Pop
      sectors = int(result.stdout)
756 abdf0113 Iustin Pop
    except ValueError:
757 468c5f77 Iustin Pop
      logging.error("Invalid output from blockdev: '%s'", result.stdout)
758 abdf0113 Iustin Pop
      return False
759 abdf0113 Iustin Pop
    bytes = sectors * 512
760 abdf0113 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
761 468c5f77 Iustin Pop
      logging.error("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
762 abdf0113 Iustin Pop
      return False
763 abdf0113 Iustin Pop
    if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
764 468c5f77 Iustin Pop
      logging.error("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
765 abdf0113 Iustin Pop
      return False
766 abdf0113 Iustin Pop
    return True
767 a8083063 Iustin Pop
768 abdf0113 Iustin Pop
  def Rename(self, new_id):
769 abdf0113 Iustin Pop
    """Rename a device.
770 a8083063 Iustin Pop

771 abdf0113 Iustin Pop
    This is not supported for drbd devices.
772 a8083063 Iustin Pop

773 a8083063 Iustin Pop
    """
774 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
775 a8083063 Iustin Pop
776 f3e513ad Iustin Pop
777 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
778 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
779 a2cfdea2 Iustin Pop

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

784 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
785 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
786 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
787 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
788 a2cfdea2 Iustin Pop

789 a2cfdea2 Iustin Pop
  """
790 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
791 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
792 a2cfdea2 Iustin Pop
793 a2cfdea2 Iustin Pop
  def __init__(self, unique_id, children):
794 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
795 fc1dc9d7 Iustin Pop
      children = []
796 a2cfdea2 Iustin Pop
    super(DRBD8, self).__init__(unique_id, children)
797 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
798 c3f9340c Guido Trotter
    version = self._GetVersion()
799 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
800 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
801 a2cfdea2 Iustin Pop
                                    " requested ganeti usage: kernel is"
802 c3f9340c Guido Trotter
                                    " %s.%s, ganeti wants 8.x" %
803 c3f9340c Guido Trotter
                                    (version['k_major'], version['k_minor']))
804 a2cfdea2 Iustin Pop
805 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
806 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
807 f9518d38 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
808 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
809 ffa1c0dc Iustin Pop
    (self._lhost, self._lport,
810 ffa1c0dc Iustin Pop
     self._rhost, self._rport,
811 f9518d38 Iustin Pop
     self._aminor, self._secret) = unique_id
812 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
813 ffa1c0dc Iustin Pop
        self._lport == self._rport):
814 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
815 ffa1c0dc Iustin Pop
                       (unique_id,))
816 a2cfdea2 Iustin Pop
    self.Attach()
817 a2cfdea2 Iustin Pop
818 a2cfdea2 Iustin Pop
  @classmethod
819 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
820 a2cfdea2 Iustin Pop
    """Initialize a meta device.
821 a2cfdea2 Iustin Pop

822 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
823 a2cfdea2 Iustin Pop

824 a2cfdea2 Iustin Pop
    """
825 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
826 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
827 a2cfdea2 Iustin Pop
    if result.failed:
828 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't initialize meta device: %s" %
829 a2cfdea2 Iustin Pop
                                    result.output)
830 a2cfdea2 Iustin Pop
831 a2cfdea2 Iustin Pop
  @classmethod
832 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
833 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
834 a2cfdea2 Iustin Pop

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

838 a2cfdea2 Iustin Pop
    """
839 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
840 a2cfdea2 Iustin Pop
841 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
842 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
843 a2cfdea2 Iustin Pop
    highest = None
844 a2cfdea2 Iustin Pop
    for line in data:
845 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
846 a2cfdea2 Iustin Pop
      if match:
847 a2cfdea2 Iustin Pop
        return int(match.group(1))
848 a2cfdea2 Iustin Pop
      match = used_line.match(line)
849 a2cfdea2 Iustin Pop
      if match:
850 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
851 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
852 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
853 a2cfdea2 Iustin Pop
      return 0
854 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
855 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
856 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
857 a2cfdea2 Iustin Pop
    return highest + 1
858 a2cfdea2 Iustin Pop
859 a2cfdea2 Iustin Pop
  @classmethod
860 a2cfdea2 Iustin Pop
  def _IsValidMeta(cls, meta_device):
861 a2cfdea2 Iustin Pop
    """Check if the given meta device looks like a valid one.
862 a2cfdea2 Iustin Pop

863 a2cfdea2 Iustin Pop
    """
864 a2cfdea2 Iustin Pop
    minor = cls._FindUnusedMinor()
865 a2cfdea2 Iustin Pop
    minor_path = cls._DevPath(minor)
866 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", minor_path,
867 a2cfdea2 Iustin Pop
                           "v08", meta_device, "0",
868 a2cfdea2 Iustin Pop
                           "dstate"])
869 a2cfdea2 Iustin Pop
    if result.failed:
870 468c5f77 Iustin Pop
      logging.error("Invalid meta device %s: %s", meta_device, result.output)
871 a2cfdea2 Iustin Pop
      return False
872 a2cfdea2 Iustin Pop
    return True
873 a2cfdea2 Iustin Pop
874 a2cfdea2 Iustin Pop
  @classmethod
875 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
876 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
877 a2cfdea2 Iustin Pop

878 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
879 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
880 a2cfdea2 Iustin Pop

881 a2cfdea2 Iustin Pop
    """
882 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
883 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
884 a2cfdea2 Iustin Pop
885 a2cfdea2 Iustin Pop
    # pyparsing setup
886 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
887 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
888 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
889 a2cfdea2 Iustin Pop
    # this also converts the value to an int
890 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
891 a2cfdea2 Iustin Pop
892 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
893 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
894 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
895 a2cfdea2 Iustin Pop
896 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
897 a2cfdea2 Iustin Pop
898 a2cfdea2 Iustin Pop
    # value types
899 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
900 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
901 a2cfdea2 Iustin Pop
    addr_port = (pyp.Word(pyp.nums + '.') + pyp.Literal(':').suppress() +
902 a2cfdea2 Iustin Pop
                 number)
903 a2cfdea2 Iustin Pop
    # meta device, extended syntax
904 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
905 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
906 a2cfdea2 Iustin Pop
907 a2cfdea2 Iustin Pop
    # a statement
908 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
909 63012024 Guido Trotter
            pyp.Optional(addr_port ^ value ^ quoted ^ meta_value) +
910 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
911 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
912 a2cfdea2 Iustin Pop
913 a2cfdea2 Iustin Pop
    # an entire section
914 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
915 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
916 a2cfdea2 Iustin Pop
917 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
918 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
919 a2cfdea2 Iustin Pop
920 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
921 a2cfdea2 Iustin Pop
922 a2cfdea2 Iustin Pop
    return bnf
923 a2cfdea2 Iustin Pop
924 a2cfdea2 Iustin Pop
  @classmethod
925 3840729d Iustin Pop
  def _GetShowData(cls, minor):
926 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
927 a2cfdea2 Iustin Pop

928 a2cfdea2 Iustin Pop
    """
929 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
930 a2cfdea2 Iustin Pop
    if result.failed:
931 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
932 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
933 3840729d Iustin Pop
      return None
934 3840729d Iustin Pop
    return result.stdout
935 3840729d Iustin Pop
936 3840729d Iustin Pop
  @classmethod
937 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
938 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
939 3840729d Iustin Pop

940 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
941 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
942 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
943 3840729d Iustin Pop
    by _GetShowData.
944 3840729d Iustin Pop

945 3840729d Iustin Pop
    """
946 3840729d Iustin Pop
    data = {}
947 a2cfdea2 Iustin Pop
    if not out:
948 a2cfdea2 Iustin Pop
      return data
949 a2cfdea2 Iustin Pop
950 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
951 a2cfdea2 Iustin Pop
    # run pyparse
952 a2cfdea2 Iustin Pop
953 a2cfdea2 Iustin Pop
    try:
954 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
955 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
956 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't parse drbdsetup show output: %s" %
957 a2cfdea2 Iustin Pop
                                    str(err))
958 a2cfdea2 Iustin Pop
959 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
960 a2cfdea2 Iustin Pop
    for section in results:
961 a2cfdea2 Iustin Pop
      sname = section[0]
962 a2cfdea2 Iustin Pop
      if sname == "_this_host":
963 a2cfdea2 Iustin Pop
        for lst in section[1:]:
964 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
965 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
966 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
967 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
968 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
969 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
970 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
971 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
972 a2cfdea2 Iustin Pop
        for lst in section[1:]:
973 a2cfdea2 Iustin Pop
          if lst[0] == "address":
974 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
975 a2cfdea2 Iustin Pop
    return data
976 a2cfdea2 Iustin Pop
977 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
978 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
979 a2cfdea2 Iustin Pop

980 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
981 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
982 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
983 a2cfdea2 Iustin Pop
    device.
984 a2cfdea2 Iustin Pop

985 a2cfdea2 Iustin Pop
    """
986 b00b95dd Iustin Pop
    if self._children:
987 b00b95dd Iustin Pop
      backend, meta = self._children
988 b00b95dd Iustin Pop
    else:
989 b00b95dd Iustin Pop
      backend = meta = None
990 b00b95dd Iustin Pop
991 a2cfdea2 Iustin Pop
    if backend is not None:
992 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
993 a2cfdea2 Iustin Pop
    else:
994 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
995 b00b95dd Iustin Pop
996 a2cfdea2 Iustin Pop
    if meta is not None:
997 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
998 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
999 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1000 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1001 a2cfdea2 Iustin Pop
    else:
1002 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1003 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1004 a2cfdea2 Iustin Pop
    return retval
1005 a2cfdea2 Iustin Pop
1006 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1007 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1008 a2cfdea2 Iustin Pop

1009 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1010 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1011 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1012 a2cfdea2 Iustin Pop
    device.
1013 a2cfdea2 Iustin Pop

1014 a2cfdea2 Iustin Pop
    """
1015 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1016 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1017 a2cfdea2 Iustin Pop
      return True
1018 a2cfdea2 Iustin Pop
1019 a2cfdea2 Iustin Pop
    if self._lhost is None:
1020 a2cfdea2 Iustin Pop
      return False
1021 a2cfdea2 Iustin Pop
1022 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1023 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1024 a2cfdea2 Iustin Pop
      return False
1025 a2cfdea2 Iustin Pop
1026 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1027 a2cfdea2 Iustin Pop
    retval = (retval and
1028 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1029 a2cfdea2 Iustin Pop
    return retval
1030 a2cfdea2 Iustin Pop
1031 a2cfdea2 Iustin Pop
  @classmethod
1032 a2cfdea2 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1033 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1034 a2cfdea2 Iustin Pop

1035 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1036 a2cfdea2 Iustin Pop
    device. And it must be done only once.
1037 a2cfdea2 Iustin Pop

1038 a2cfdea2 Iustin Pop
    """
1039 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta):
1040 a2cfdea2 Iustin Pop
      return False
1041 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1042 333411a7 Guido Trotter
            backend, meta, "0", "-e", "detach", "--create-device"]
1043 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1044 a2cfdea2 Iustin Pop
    if result.failed:
1045 468c5f77 Iustin Pop
      logging.error("Can't attach local disk: %s", result.output)
1046 a2cfdea2 Iustin Pop
    return not result.failed
1047 a2cfdea2 Iustin Pop
1048 a2cfdea2 Iustin Pop
  @classmethod
1049 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1050 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1051 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1052 a2cfdea2 Iustin Pop

1053 a2cfdea2 Iustin Pop
    """
1054 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1055 52857176 Iustin Pop
    if None in net_info:
1056 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1057 52857176 Iustin Pop
      # sure its shutdown
1058 52857176 Iustin Pop
      return cls._ShutdownNet(minor)
1059 52857176 Iustin Pop
1060 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1061 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1062 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1063 f38478b2 Iustin Pop
            "-B", "consensus",
1064 ab6cc81c Iustin Pop
            "--create-device",
1065 f38478b2 Iustin Pop
            ]
1066 a2cfdea2 Iustin Pop
    if dual_pri:
1067 a2cfdea2 Iustin Pop
      args.append("-m")
1068 a2cfdea2 Iustin Pop
    if hmac and secret:
1069 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1070 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1071 a2cfdea2 Iustin Pop
    if result.failed:
1072 468c5f77 Iustin Pop
      logging.error("Can't setup network for dbrd device: %s - %s",
1073 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1074 a2cfdea2 Iustin Pop
      return False
1075 a2cfdea2 Iustin Pop
1076 a2cfdea2 Iustin Pop
    timeout = time.time() + 10
1077 a2cfdea2 Iustin Pop
    ok = False
1078 a2cfdea2 Iustin Pop
    while time.time() < timeout:
1079 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1080 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1081 a2cfdea2 Iustin Pop
        time.sleep(1)
1082 a2cfdea2 Iustin Pop
        continue
1083 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1084 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1085 a2cfdea2 Iustin Pop
        time.sleep(1)
1086 a2cfdea2 Iustin Pop
        continue
1087 a2cfdea2 Iustin Pop
      ok = True
1088 a2cfdea2 Iustin Pop
      break
1089 a2cfdea2 Iustin Pop
    if not ok:
1090 468c5f77 Iustin Pop
      logging.error("Timeout while configuring network")
1091 a2cfdea2 Iustin Pop
      return False
1092 a2cfdea2 Iustin Pop
    return True
1093 a2cfdea2 Iustin Pop
1094 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1095 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1096 b00b95dd Iustin Pop

1097 b00b95dd Iustin Pop
    """
1098 b00b95dd Iustin Pop
    if self.minor is None:
1099 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren")
1100 b00b95dd Iustin Pop
    if len(devices) != 2:
1101 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Need two devices for AddChildren")
1102 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1103 03ece5f3 Iustin Pop
    if "local_dev" in info:
1104 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("DRBD8 already attached to a local disk")
1105 b00b95dd Iustin Pop
    backend, meta = devices
1106 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1107 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Children not ready during AddChildren")
1108 b00b95dd Iustin Pop
    backend.Open()
1109 b00b95dd Iustin Pop
    meta.Open()
1110 b00b95dd Iustin Pop
    if not self._CheckMetaSize(meta.dev_path):
1111 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1112 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1113 b00b95dd Iustin Pop
    if not self._IsValidMeta(meta.dev_path):
1114 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1115 b00b95dd Iustin Pop
1116 b00b95dd Iustin Pop
    if not self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path):
1117 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to local storage")
1118 b00b95dd Iustin Pop
    self._children = devices
1119 b00b95dd Iustin Pop
1120 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1121 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1122 b00b95dd Iustin Pop

1123 b00b95dd Iustin Pop
    """
1124 b00b95dd Iustin Pop
    if self.minor is None:
1125 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to drbd8 during"
1126 b00b95dd Iustin Pop
                                    " RemoveChildren")
1127 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1128 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1129 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1130 03ece5f3 Iustin Pop
      return
1131 b00b95dd Iustin Pop
    if len(self._children) != 2:
1132 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We don't have two children: %s" %
1133 b00b95dd Iustin Pop
                                    self._children)
1134 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1135 468c5f77 Iustin Pop
      logging.error("Requested detach while detached")
1136 e739bd57 Iustin Pop
      return
1137 b00b95dd Iustin Pop
    if len(devices) != 2:
1138 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We need two children in RemoveChildren")
1139 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1140 e739bd57 Iustin Pop
      if dev != child.dev_path:
1141 e739bd57 Iustin Pop
        raise errors.BlockDeviceError("Mismatch in local storage"
1142 e739bd57 Iustin Pop
                                      " (%s != %s) in RemoveChildren" %
1143 e739bd57 Iustin Pop
                                      (dev, child.dev_path))
1144 b00b95dd Iustin Pop
1145 b00b95dd Iustin Pop
    if not self._ShutdownLocal(self.minor):
1146 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't detach from local storage")
1147 b00b95dd Iustin Pop
    self._children = []
1148 b00b95dd Iustin Pop
1149 a2cfdea2 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1150 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1151 a2cfdea2 Iustin Pop

1152 a2cfdea2 Iustin Pop
    """
1153 a2cfdea2 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1154 a2cfdea2 Iustin Pop
    if self.minor is None:
1155 468c5f77 Iustin Pop
      logging.info("Instance not attached to a device")
1156 a2cfdea2 Iustin Pop
      return False
1157 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1158 a2cfdea2 Iustin Pop
                           kbytes])
1159 a2cfdea2 Iustin Pop
    if result.failed:
1160 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1161 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1162 a2cfdea2 Iustin Pop
    return not result.failed and children_result
1163 a2cfdea2 Iustin Pop
1164 6b90c22e Iustin Pop
  def GetProcStatus(self):
1165 6b90c22e Iustin Pop
    """Return device data from /proc.
1166 6b90c22e Iustin Pop

1167 6b90c22e Iustin Pop
    """
1168 6b90c22e Iustin Pop
    if self.minor is None:
1169 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("GetStats() called while not attached")
1170 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1171 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1172 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1173 6b90c22e Iustin Pop
                                    self.minor)
1174 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1175 6b90c22e Iustin Pop
1176 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1177 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1178 a2cfdea2 Iustin Pop

1179 a2cfdea2 Iustin Pop
    Returns:
1180 583e3f6f Iustin Pop
     (sync_percent, estimated_time, is_degraded)
1181 a2cfdea2 Iustin Pop

1182 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1183 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1184 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1185 0834c866 Iustin Pop

1186 0834c866 Iustin Pop

1187 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1188 0834c866 Iustin Pop
    network not connected or local disk missing.
1189 0834c866 Iustin Pop

1190 0834c866 Iustin Pop
    We compute the ldisk parameter based on wheter we have a local
1191 0834c866 Iustin Pop
    disk or not.
1192 a2cfdea2 Iustin Pop

1193 a2cfdea2 Iustin Pop
    """
1194 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1195 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1196 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1197 6b90c22e Iustin Pop
    ldisk = not stats.is_disk_uptodate
1198 6b90c22e Iustin Pop
    is_degraded = not stats.is_connected
1199 6b90c22e Iustin Pop
    return stats.sync_percent, stats.est_time, is_degraded or ldisk, ldisk
1200 a2cfdea2 Iustin Pop
1201 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1202 a2cfdea2 Iustin Pop
    """Make the local state primary.
1203 a2cfdea2 Iustin Pop

1204 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1205 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1206 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1207 f860ff4e Guido Trotter
    is mandatory.
1208 a2cfdea2 Iustin Pop

1209 a2cfdea2 Iustin Pop
    """
1210 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1211 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1212 a2cfdea2 Iustin Pop
      return False
1213 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1214 a2cfdea2 Iustin Pop
    if force:
1215 a2cfdea2 Iustin Pop
      cmd.append("-o")
1216 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1217 a2cfdea2 Iustin Pop
    if result.failed:
1218 fdbd668d Iustin Pop
      msg = ("Can't make drbd device primary: %s" % result.output)
1219 468c5f77 Iustin Pop
      logging.error(msg)
1220 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1221 a2cfdea2 Iustin Pop
1222 a2cfdea2 Iustin Pop
  def Close(self):
1223 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1224 a2cfdea2 Iustin Pop

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

1227 a2cfdea2 Iustin Pop
    """
1228 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1229 468c5f77 Iustin Pop
      logging.info("Instance not attached to a device")
1230 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1231 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1232 a2cfdea2 Iustin Pop
    if result.failed:
1233 fdbd668d Iustin Pop
      msg = ("Can't switch drbd device to"
1234 fdbd668d Iustin Pop
             " secondary: %s" % result.output)
1235 468c5f77 Iustin Pop
      logging.error(msg)
1236 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1237 a2cfdea2 Iustin Pop
1238 a2cfdea2 Iustin Pop
  def Attach(self):
1239 a2cfdea2 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1240 a2cfdea2 Iustin Pop

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

1245 a2cfdea2 Iustin Pop
    """
1246 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1247 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1248 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1249 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1250 a2cfdea2 Iustin Pop
      if match_l and match_r:
1251 a2cfdea2 Iustin Pop
        break
1252 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1253 a2cfdea2 Iustin Pop
        res_r = self._AssembleNet(minor,
1254 a2cfdea2 Iustin Pop
                                  (self._lhost, self._lport,
1255 a2cfdea2 Iustin Pop
                                   self._rhost, self._rport),
1256 3c03759a Iustin Pop
                                  constants.DRBD_NET_PROTOCOL,
1257 3c03759a Iustin Pop
                                  hmac=constants.DRBD_HMAC_ALG,
1258 2899d9de Iustin Pop
                                  secret=self._secret
1259 2899d9de Iustin Pop
                                  )
1260 3840729d Iustin Pop
        if res_r:
1261 3840729d Iustin Pop
          if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1262 3840729d Iustin Pop
            break
1263 fc1dc9d7 Iustin Pop
      # the weakest case: we find something that is only net attached
1264 fc1dc9d7 Iustin Pop
      # even though we were passed some children at init time
1265 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1266 fc1dc9d7 Iustin Pop
        break
1267 bf25af3b Iustin Pop
1268 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1269 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1270 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1271 bf25af3b Iustin Pop
      # configuration
1272 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1273 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1274 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1275 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1276 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1277 9cdbe77f Iustin Pop
        # reconnect to our correct one
1278 9cdbe77f Iustin Pop
        if not self._ShutdownNet(minor):
1279 9cdbe77f Iustin Pop
          raise errors.BlockDeviceError("Device has correct local storage,"
1280 9cdbe77f Iustin Pop
                                        " wrong remote peer and is unable to"
1281 9cdbe77f Iustin Pop
                                        " disconnect in order to attach to"
1282 9cdbe77f Iustin Pop
                                        " the correct peer")
1283 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1284 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1285 9cdbe77f Iustin Pop
        # None)
1286 9cdbe77f Iustin Pop
        if (self._AssembleNet(minor, (self._lhost, self._lport,
1287 3c03759a Iustin Pop
                                      self._rhost, self._rport),
1288 3c03759a Iustin Pop
                              constants.DRBD_NET_PROTOCOL,
1289 2899d9de Iustin Pop
                              hmac=constants.DRBD_HMAC_ALG,
1290 2899d9de Iustin Pop
                              secret=self._secret) and
1291 3840729d Iustin Pop
            self._MatchesNet(self._GetDevInfo(self._GetShowData(minor)))):
1292 9cdbe77f Iustin Pop
          break
1293 9cdbe77f Iustin Pop
1294 a2cfdea2 Iustin Pop
    else:
1295 a2cfdea2 Iustin Pop
      minor = None
1296 a2cfdea2 Iustin Pop
1297 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1298 a2cfdea2 Iustin Pop
    return minor is not None
1299 a2cfdea2 Iustin Pop
1300 a2cfdea2 Iustin Pop
  def Assemble(self):
1301 a2cfdea2 Iustin Pop
    """Assemble the drbd.
1302 a2cfdea2 Iustin Pop

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

1318 a2cfdea2 Iustin Pop
    """
1319 a2cfdea2 Iustin Pop
    self.Attach()
1320 a2cfdea2 Iustin Pop
    if self.minor is not None:
1321 468c5f77 Iustin Pop
      logging.info("Already assembled")
1322 a2cfdea2 Iustin Pop
      return True
1323 a2cfdea2 Iustin Pop
1324 a2cfdea2 Iustin Pop
    result = super(DRBD8, self).Assemble()
1325 a2cfdea2 Iustin Pop
    if not result:
1326 a2cfdea2 Iustin Pop
      return result
1327 a2cfdea2 Iustin Pop
1328 a1578d63 Iustin Pop
    # TODO: maybe completely tear-down the minor (drbdsetup ... down)
1329 a1578d63 Iustin Pop
    # before attaching our own?
1330 a1578d63 Iustin Pop
    minor = self._aminor
1331 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
1332 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1333 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1334 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
1335 a2cfdea2 Iustin Pop
      if not result:
1336 a2cfdea2 Iustin Pop
        return False
1337 a2cfdea2 Iustin Pop
      need_localdev_teardown = True
1338 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1339 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
1340 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
1341 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
1342 3c03759a Iustin Pop
                                 constants.DRBD_NET_PROTOCOL,
1343 3c03759a Iustin Pop
                                 hmac=constants.DRBD_HMAC_ALG,
1344 2899d9de Iustin Pop
                                 secret=self._secret)
1345 a2cfdea2 Iustin Pop
      if not result:
1346 a2cfdea2 Iustin Pop
        if need_localdev_teardown:
1347 a2cfdea2 Iustin Pop
          # we will ignore failures from this
1348 468c5f77 Iustin Pop
          logging.error("net setup failed, tearing down local device")
1349 a2cfdea2 Iustin Pop
          self._ShutdownAll(minor)
1350 a2cfdea2 Iustin Pop
        return False
1351 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1352 a2cfdea2 Iustin Pop
    return True
1353 a2cfdea2 Iustin Pop
1354 a2cfdea2 Iustin Pop
  @classmethod
1355 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1356 b00b95dd Iustin Pop
    """Detach from the local device.
1357 b00b95dd Iustin Pop

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

1361 b00b95dd Iustin Pop
    """
1362 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1363 b00b95dd Iustin Pop
    if result.failed:
1364 468c5f77 Iustin Pop
      logging.error("Can't detach local device: %s", result.output)
1365 b00b95dd Iustin Pop
    return not result.failed
1366 b00b95dd Iustin Pop
1367 b00b95dd Iustin Pop
  @classmethod
1368 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1369 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1370 f3e513ad Iustin Pop

1371 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1372 f3e513ad Iustin Pop

1373 f3e513ad Iustin Pop
    """
1374 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1375 a8459f1c Iustin Pop
    if result.failed:
1376 468c5f77 Iustin Pop
      logging.error("Can't shutdown network: %s", result.output)
1377 f3e513ad Iustin Pop
    return not result.failed
1378 f3e513ad Iustin Pop
1379 f3e513ad Iustin Pop
  @classmethod
1380 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1381 a2cfdea2 Iustin Pop
    """Deactivate the device.
1382 a2cfdea2 Iustin Pop

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

1385 a2cfdea2 Iustin Pop
    """
1386 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1387 a2cfdea2 Iustin Pop
    if result.failed:
1388 468c5f77 Iustin Pop
      logging.error("Can't shutdown drbd device: %s", result.output)
1389 a2cfdea2 Iustin Pop
    return not result.failed
1390 a2cfdea2 Iustin Pop
1391 a2cfdea2 Iustin Pop
  def Shutdown(self):
1392 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1393 a2cfdea2 Iustin Pop

1394 a2cfdea2 Iustin Pop
    """
1395 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1396 468c5f77 Iustin Pop
      logging.info("DRBD device not attached to a device during Shutdown")
1397 a2cfdea2 Iustin Pop
      return True
1398 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
1399 a2cfdea2 Iustin Pop
      return False
1400 a2cfdea2 Iustin Pop
    self.minor = None
1401 a2cfdea2 Iustin Pop
    self.dev_path = None
1402 a2cfdea2 Iustin Pop
    return True
1403 a2cfdea2 Iustin Pop
1404 a2cfdea2 Iustin Pop
  def Remove(self):
1405 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1406 a2cfdea2 Iustin Pop

1407 a2cfdea2 Iustin Pop
    """
1408 a2cfdea2 Iustin Pop
    return self.Shutdown()
1409 a2cfdea2 Iustin Pop
1410 a2cfdea2 Iustin Pop
  @classmethod
1411 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1412 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1413 a2cfdea2 Iustin Pop

1414 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1415 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1416 a2cfdea2 Iustin Pop

1417 a2cfdea2 Iustin Pop
    """
1418 a2cfdea2 Iustin Pop
    if len(children) != 2:
1419 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1420 a2cfdea2 Iustin Pop
    meta = children[1]
1421 a2cfdea2 Iustin Pop
    meta.Assemble()
1422 a2cfdea2 Iustin Pop
    if not meta.Attach():
1423 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1424 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
1425 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1426 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
1427 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
1428 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1429 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
1430 a2cfdea2 Iustin Pop
1431 1005d816 Iustin Pop
  def Grow(self, amount):
1432 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1433 1005d816 Iustin Pop

1434 1005d816 Iustin Pop
    """
1435 1005d816 Iustin Pop
    if self.minor is None:
1436 1005d816 Iustin Pop
      raise errors.ProgrammerError("drbd8: Grow called while not attached")
1437 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1438 1005d816 Iustin Pop
      raise errors.BlockDeviceError("Cannot grow diskless DRBD8 device")
1439 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1440 1005d816 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize"])
1441 1005d816 Iustin Pop
    if result.failed:
1442 1005d816 Iustin Pop
      raise errors.BlockDeviceError("resize failed for %s: %s" %
1443 1005d816 Iustin Pop
                                    (self.dev_path, result.output))
1444 1005d816 Iustin Pop
    return
1445 1005d816 Iustin Pop
1446 a8083063 Iustin Pop
1447 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1448 6f695a2e Manuel Franceschini
  """File device.
1449 abdf0113 Iustin Pop

1450 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1451 6f695a2e Manuel Franceschini

1452 6f695a2e Manuel Franceschini
  The unique_id for the file device is a (file_driver, file_path) tuple.
1453 abdf0113 Iustin Pop

1454 6f695a2e Manuel Franceschini
  """
1455 6f695a2e Manuel Franceschini
  def __init__(self, unique_id, children):
1456 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1457 6f695a2e Manuel Franceschini

1458 6f695a2e Manuel Franceschini
    """
1459 6f695a2e Manuel Franceschini
    if children:
1460 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1461 6f695a2e Manuel Franceschini
    super(FileStorage, self).__init__(unique_id, children)
1462 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1463 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1464 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1465 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1466 6f695a2e Manuel Franceschini
1467 6f695a2e Manuel Franceschini
  def Assemble(self):
1468 6f695a2e Manuel Franceschini
    """Assemble the device.
1469 6f695a2e Manuel Franceschini

1470 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1471 6f695a2e Manuel Franceschini

1472 6f695a2e Manuel Franceschini
    """
1473 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1474 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("File device '%s' does not exist." %
1475 6f695a2e Manuel Franceschini
                                    self.dev_path)
1476 6f695a2e Manuel Franceschini
    return True
1477 6f695a2e Manuel Franceschini
1478 6f695a2e Manuel Franceschini
  def Shutdown(self):
1479 6f695a2e Manuel Franceschini
    """Shutdown the device.
1480 6f695a2e Manuel Franceschini

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

1484 6f695a2e Manuel Franceschini
    """
1485 6f695a2e Manuel Franceschini
    return True
1486 6f695a2e Manuel Franceschini
1487 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1488 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1489 6f695a2e Manuel Franceschini

1490 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1491 6f695a2e Manuel Franceschini

1492 6f695a2e Manuel Franceschini
    """
1493 6f695a2e Manuel Franceschini
    pass
1494 6f695a2e Manuel Franceschini
1495 6f695a2e Manuel Franceschini
  def Close(self):
1496 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1497 6f695a2e Manuel Franceschini

1498 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1499 6f695a2e Manuel Franceschini

1500 6f695a2e Manuel Franceschini
    """
1501 6f695a2e Manuel Franceschini
    pass
1502 6f695a2e Manuel Franceschini
1503 6f695a2e Manuel Franceschini
  def Remove(self):
1504 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1505 6f695a2e Manuel Franceschini

1506 6f695a2e Manuel Franceschini
    Returns:
1507 6f695a2e Manuel Franceschini
      boolean indicating wheter removal of file was successful or not.
1508 6f695a2e Manuel Franceschini

1509 6f695a2e Manuel Franceschini
    """
1510 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1511 6f695a2e Manuel Franceschini
      return True
1512 6f695a2e Manuel Franceschini
    try:
1513 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1514 6f695a2e Manuel Franceschini
      return True
1515 6f695a2e Manuel Franceschini
    except OSError, err:
1516 468c5f77 Iustin Pop
      logging.error("Can't remove file '%s': %s", self.dev_path, err)
1517 6f695a2e Manuel Franceschini
      return False
1518 6f695a2e Manuel Franceschini
1519 6f695a2e Manuel Franceschini
  def Attach(self):
1520 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1521 6f695a2e Manuel Franceschini

1522 6f695a2e Manuel Franceschini
    Check if this file already exists.
1523 6f695a2e Manuel Franceschini

1524 6f695a2e Manuel Franceschini
    Returns:
1525 6f695a2e Manuel Franceschini
      boolean indicating if file exists or not.
1526 6f695a2e Manuel Franceschini

1527 6f695a2e Manuel Franceschini
    """
1528 6f695a2e Manuel Franceschini
    if os.path.exists(self.dev_path):
1529 6f695a2e Manuel Franceschini
      return True
1530 6f695a2e Manuel Franceschini
    return False
1531 6f695a2e Manuel Franceschini
1532 6f695a2e Manuel Franceschini
  @classmethod
1533 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1534 6f695a2e Manuel Franceschini
    """Create a new file.
1535 6f695a2e Manuel Franceschini

1536 6f695a2e Manuel Franceschini
    Args:
1537 6f695a2e Manuel Franceschini
      children:
1538 6f695a2e Manuel Franceschini
      size: integer size of file in MiB
1539 6f695a2e Manuel Franceschini

1540 6f695a2e Manuel Franceschini
    Returns:
1541 6f695a2e Manuel Franceschini
      A ganeti.bdev.FileStorage object.
1542 6f695a2e Manuel Franceschini

1543 6f695a2e Manuel Franceschini
    """
1544 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1545 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1546 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1547 6f695a2e Manuel Franceschini
    try:
1548 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
1549 6f695a2e Manuel Franceschini
    except IOError, err:
1550 b62ddbe5 Guido Trotter
      raise errors.BlockDeviceError("Could not create '%'" % err)
1551 6f695a2e Manuel Franceschini
    else:
1552 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1553 6f695a2e Manuel Franceschini
      f.close()
1554 6f695a2e Manuel Franceschini
1555 6f695a2e Manuel Franceschini
    return FileStorage(unique_id, children)
1556 6f695a2e Manuel Franceschini
1557 6f695a2e Manuel Franceschini
1558 a8083063 Iustin Pop
DEV_MAP = {
1559 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1560 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1561 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
1562 a8083063 Iustin Pop
  }
1563 a8083063 Iustin Pop
1564 a8083063 Iustin Pop
1565 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
1566 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1567 a8083063 Iustin Pop

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

1571 a8083063 Iustin Pop
  """
1572 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1573 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1574 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1575 cb999543 Iustin Pop
  if not device.attached:
1576 a8083063 Iustin Pop
    return None
1577 a8083063 Iustin Pop
  return  device
1578 a8083063 Iustin Pop
1579 a8083063 Iustin Pop
1580 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
1581 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1582 a8083063 Iustin Pop

1583 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
1584 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
1585 a8083063 Iustin Pop

1586 a8083063 Iustin Pop
  """
1587 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1588 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1589 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1590 cb999543 Iustin Pop
  if not device.attached:
1591 a8083063 Iustin Pop
    device.Assemble()
1592 cb999543 Iustin Pop
    if not device.attached:
1593 be1ba2bd Iustin Pop
      raise errors.BlockDeviceError("Can't find a valid block device for"
1594 be1ba2bd Iustin Pop
                                    " %s/%s/%s" %
1595 be1ba2bd Iustin Pop
                                    (dev_type, unique_id, children))
1596 a8083063 Iustin Pop
  return device
1597 a8083063 Iustin Pop
1598 a8083063 Iustin Pop
1599 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1600 a8083063 Iustin Pop
  """Create a device.
1601 a8083063 Iustin Pop

1602 a8083063 Iustin Pop
  """
1603 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1604 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1605 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1606 a8083063 Iustin Pop
  return device