Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ f64c9de6

History | View | Annotate | Download (49.3 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 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 a8083063 Iustin Pop
86 a8083063 Iustin Pop
  def Assemble(self):
87 a8083063 Iustin Pop
    """Assemble the device from its components.
88 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

434 a8083063 Iustin Pop
    """
435 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
436 5574047a Iustin Pop
    if result.failed:
437 5574047a Iustin Pop
      logger.Error("Can't activate lv %s: %s" % (self.dev_path, result.output))
438 5574047a Iustin Pop
    return not result.failed
439 a8083063 Iustin Pop
440 a8083063 Iustin Pop
  def Shutdown(self):
441 a8083063 Iustin Pop
    """Shutdown the device.
442 a8083063 Iustin Pop

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

446 a8083063 Iustin Pop
    """
447 a8083063 Iustin Pop
    return True
448 a8083063 Iustin Pop
449 9db6dbce Iustin Pop
  def GetSyncStatus(self):
450 9db6dbce Iustin Pop
    """Returns the sync status of the device.
451 9db6dbce Iustin Pop

452 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
453 9db6dbce Iustin Pop
    status of the mirror.
454 9db6dbce Iustin Pop

455 9db6dbce Iustin Pop
    Returns:
456 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
457 9db6dbce Iustin Pop

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

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

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

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

477 a8083063 Iustin Pop
    This is a no-op for the LV device type.
478 a8083063 Iustin Pop

479 a8083063 Iustin Pop
    """
480 fdbd668d Iustin Pop
    pass
481 a8083063 Iustin Pop
482 a8083063 Iustin Pop
  def Close(self):
483 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
484 a8083063 Iustin Pop

485 a8083063 Iustin Pop
    This is a no-op for the LV device type.
486 a8083063 Iustin Pop

487 a8083063 Iustin Pop
    """
488 fdbd668d Iustin Pop
    pass
489 a8083063 Iustin Pop
490 a8083063 Iustin Pop
  def Snapshot(self, size):
491 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
492 a8083063 Iustin Pop

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

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

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

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

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

609 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
610 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
611 0f7f32d9 Iustin Pop

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

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

641 abdf0113 Iustin Pop
    Returns:
642 abdf0113 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
643 a8083063 Iustin Pop

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

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

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

699 a8083063 Iustin Pop
    """
700 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
701 a8083063 Iustin Pop
702 abdf0113 Iustin Pop
  @classmethod
703 abdf0113 Iustin Pop
  def _GetUsedDevs(cls):
704 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
705 a8083063 Iustin Pop

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

726 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
727 a8083063 Iustin Pop

728 a8083063 Iustin Pop
    """
729 abdf0113 Iustin Pop
    if minor is None:
730 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
731 a8083063 Iustin Pop
    else:
732 abdf0113 Iustin Pop
      self.minor = minor
733 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
734 a8083063 Iustin Pop
735 a8083063 Iustin Pop
  @staticmethod
736 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
737 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
738 a8083063 Iustin Pop

739 abdf0113 Iustin Pop
    This currently only check the size, which must be around
740 abdf0113 Iustin Pop
    128MiB.
741 a8083063 Iustin Pop

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

765 abdf0113 Iustin Pop
    This is not supported for drbd devices.
766 a8083063 Iustin Pop

767 a8083063 Iustin Pop
    """
768 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
769 a8083063 Iustin Pop
770 f3e513ad Iustin Pop
771 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
772 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
773 a2cfdea2 Iustin Pop

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

778 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
779 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
780 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
781 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
782 a2cfdea2 Iustin Pop

783 a2cfdea2 Iustin Pop
  """
784 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
785 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
786 a2cfdea2 Iustin Pop
787 a2cfdea2 Iustin Pop
  def __init__(self, unique_id, children):
788 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
789 fc1dc9d7 Iustin Pop
      children = []
790 a2cfdea2 Iustin Pop
    super(DRBD8, self).__init__(unique_id, children)
791 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
792 c3f9340c Guido Trotter
    version = self._GetVersion()
793 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
794 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
795 a2cfdea2 Iustin Pop
                                    " requested ganeti usage: kernel is"
796 c3f9340c Guido Trotter
                                    " %s.%s, ganeti wants 8.x" %
797 c3f9340c Guido Trotter
                                    (version['k_major'], version['k_minor']))
798 a2cfdea2 Iustin Pop
799 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
800 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
801 a2cfdea2 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
802 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
803 a2cfdea2 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
804 a2cfdea2 Iustin Pop
    self.Attach()
805 a2cfdea2 Iustin Pop
806 a2cfdea2 Iustin Pop
  @classmethod
807 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
808 a2cfdea2 Iustin Pop
    """Initialize a meta device.
809 a2cfdea2 Iustin Pop

810 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
811 a2cfdea2 Iustin Pop

812 a2cfdea2 Iustin Pop
    """
813 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
814 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
815 a2cfdea2 Iustin Pop
    if result.failed:
816 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't initialize meta device: %s" %
817 a2cfdea2 Iustin Pop
                                    result.output)
818 a2cfdea2 Iustin Pop
819 a2cfdea2 Iustin Pop
  @classmethod
820 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
821 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
822 a2cfdea2 Iustin Pop

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

826 a2cfdea2 Iustin Pop
    """
827 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
828 a2cfdea2 Iustin Pop
829 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
830 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
831 a2cfdea2 Iustin Pop
    highest = None
832 a2cfdea2 Iustin Pop
    for line in data:
833 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
834 a2cfdea2 Iustin Pop
      if match:
835 a2cfdea2 Iustin Pop
        return int(match.group(1))
836 a2cfdea2 Iustin Pop
      match = used_line.match(line)
837 a2cfdea2 Iustin Pop
      if match:
838 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
839 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
840 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
841 a2cfdea2 Iustin Pop
      return 0
842 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
843 a2cfdea2 Iustin Pop
      logger.Error("Error: no free drbd minors!")
844 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
845 a2cfdea2 Iustin Pop
    return highest + 1
846 a2cfdea2 Iustin Pop
847 a2cfdea2 Iustin Pop
  @classmethod
848 a2cfdea2 Iustin Pop
  def _IsValidMeta(cls, meta_device):
849 a2cfdea2 Iustin Pop
    """Check if the given meta device looks like a valid one.
850 a2cfdea2 Iustin Pop

851 a2cfdea2 Iustin Pop
    """
852 a2cfdea2 Iustin Pop
    minor = cls._FindUnusedMinor()
853 a2cfdea2 Iustin Pop
    minor_path = cls._DevPath(minor)
854 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", minor_path,
855 a2cfdea2 Iustin Pop
                           "v08", meta_device, "0",
856 a2cfdea2 Iustin Pop
                           "dstate"])
857 a2cfdea2 Iustin Pop
    if result.failed:
858 a2cfdea2 Iustin Pop
      logger.Error("Invalid meta device %s: %s" % (meta_device, result.output))
859 a2cfdea2 Iustin Pop
      return False
860 a2cfdea2 Iustin Pop
    return True
861 a2cfdea2 Iustin Pop
862 a2cfdea2 Iustin Pop
  @classmethod
863 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
864 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
865 a2cfdea2 Iustin Pop

866 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
867 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
868 a2cfdea2 Iustin Pop

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

916 a2cfdea2 Iustin Pop
    """
917 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
918 a2cfdea2 Iustin Pop
    if result.failed:
919 6c896e2f Iustin Pop
      logger.Error("Can't display the drbd config: %s - %s" %
920 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
921 3840729d Iustin Pop
      return None
922 3840729d Iustin Pop
    return result.stdout
923 3840729d Iustin Pop
924 3840729d Iustin Pop
  @classmethod
925 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
926 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
927 3840729d Iustin Pop

928 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
929 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
930 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
931 3840729d Iustin Pop
    by _GetShowData.
932 3840729d Iustin Pop

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

968 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
969 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
970 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
971 a2cfdea2 Iustin Pop
    device.
972 a2cfdea2 Iustin Pop

973 a2cfdea2 Iustin Pop
    """
974 b00b95dd Iustin Pop
    if self._children:
975 b00b95dd Iustin Pop
      backend, meta = self._children
976 b00b95dd Iustin Pop
    else:
977 b00b95dd Iustin Pop
      backend = meta = None
978 b00b95dd Iustin Pop
979 a2cfdea2 Iustin Pop
    if backend is not None:
980 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
981 a2cfdea2 Iustin Pop
    else:
982 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
983 b00b95dd Iustin Pop
984 a2cfdea2 Iustin Pop
    if meta is not None:
985 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
986 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
987 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
988 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
989 a2cfdea2 Iustin Pop
    else:
990 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
991 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
992 a2cfdea2 Iustin Pop
    return retval
993 a2cfdea2 Iustin Pop
994 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
995 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
996 a2cfdea2 Iustin Pop

997 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
998 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
999 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1000 a2cfdea2 Iustin Pop
    device.
1001 a2cfdea2 Iustin Pop

1002 a2cfdea2 Iustin Pop
    """
1003 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1004 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1005 a2cfdea2 Iustin Pop
      return True
1006 a2cfdea2 Iustin Pop
1007 a2cfdea2 Iustin Pop
    if self._lhost is None:
1008 a2cfdea2 Iustin Pop
      return False
1009 a2cfdea2 Iustin Pop
1010 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1011 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1012 a2cfdea2 Iustin Pop
      return False
1013 a2cfdea2 Iustin Pop
1014 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1015 a2cfdea2 Iustin Pop
    retval = (retval and
1016 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1017 a2cfdea2 Iustin Pop
    return retval
1018 a2cfdea2 Iustin Pop
1019 a2cfdea2 Iustin Pop
  @classmethod
1020 a2cfdea2 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1021 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1022 a2cfdea2 Iustin Pop

1023 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1024 a2cfdea2 Iustin Pop
    device. And it must be done only once.
1025 a2cfdea2 Iustin Pop

1026 a2cfdea2 Iustin Pop
    """
1027 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta):
1028 a2cfdea2 Iustin Pop
      return False
1029 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1030 333411a7 Guido Trotter
            backend, meta, "0", "-e", "detach", "--create-device"]
1031 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1032 a2cfdea2 Iustin Pop
    if result.failed:
1033 a2cfdea2 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1034 a2cfdea2 Iustin Pop
    return not result.failed
1035 a2cfdea2 Iustin Pop
1036 a2cfdea2 Iustin Pop
  @classmethod
1037 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1038 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1039 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1040 a2cfdea2 Iustin Pop

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

1085 b00b95dd Iustin Pop
    """
1086 b00b95dd Iustin Pop
    if self.minor is None:
1087 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren")
1088 b00b95dd Iustin Pop
    if len(devices) != 2:
1089 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Need two devices for AddChildren")
1090 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1091 03ece5f3 Iustin Pop
    if "local_dev" in info:
1092 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("DRBD8 already attached to a local disk")
1093 b00b95dd Iustin Pop
    backend, meta = devices
1094 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1095 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Children not ready during AddChildren")
1096 b00b95dd Iustin Pop
    backend.Open()
1097 b00b95dd Iustin Pop
    meta.Open()
1098 b00b95dd Iustin Pop
    if not self._CheckMetaSize(meta.dev_path):
1099 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1100 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1101 b00b95dd Iustin Pop
    if not self._IsValidMeta(meta.dev_path):
1102 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1103 b00b95dd Iustin Pop
1104 b00b95dd Iustin Pop
    if not self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path):
1105 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to local storage")
1106 b00b95dd Iustin Pop
    self._children = devices
1107 b00b95dd Iustin Pop
1108 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1109 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1110 b00b95dd Iustin Pop

1111 b00b95dd Iustin Pop
    """
1112 b00b95dd Iustin Pop
    if self.minor is None:
1113 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to drbd8 during"
1114 b00b95dd Iustin Pop
                                    " RemoveChildren")
1115 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1116 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1117 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1118 03ece5f3 Iustin Pop
      return
1119 b00b95dd Iustin Pop
    if len(self._children) != 2:
1120 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We don't have two children: %s" %
1121 b00b95dd Iustin Pop
                                    self._children)
1122 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1123 e739bd57 Iustin Pop
      logger.Error("Requested detach while detached")
1124 e739bd57 Iustin Pop
      return
1125 b00b95dd Iustin Pop
    if len(devices) != 2:
1126 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We need two children in RemoveChildren")
1127 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1128 e739bd57 Iustin Pop
      if dev != child.dev_path:
1129 e739bd57 Iustin Pop
        raise errors.BlockDeviceError("Mismatch in local storage"
1130 e739bd57 Iustin Pop
                                      " (%s != %s) in RemoveChildren" %
1131 e739bd57 Iustin Pop
                                      (dev, child.dev_path))
1132 b00b95dd Iustin Pop
1133 b00b95dd Iustin Pop
    if not self._ShutdownLocal(self.minor):
1134 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't detach from local storage")
1135 b00b95dd Iustin Pop
    self._children = []
1136 b00b95dd Iustin Pop
1137 a2cfdea2 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1138 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1139 a2cfdea2 Iustin Pop

1140 a2cfdea2 Iustin Pop
    """
1141 a2cfdea2 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1142 a2cfdea2 Iustin Pop
    if self.minor is None:
1143 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
1144 a2cfdea2 Iustin Pop
      return False
1145 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1146 a2cfdea2 Iustin Pop
                           kbytes])
1147 a2cfdea2 Iustin Pop
    if result.failed:
1148 6c896e2f Iustin Pop
      logger.Error("Can't change syncer rate: %s - %s" %
1149 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
1150 a2cfdea2 Iustin Pop
    return not result.failed and children_result
1151 a2cfdea2 Iustin Pop
1152 6b90c22e Iustin Pop
  def GetProcStatus(self):
1153 6b90c22e Iustin Pop
    """Return device data from /proc.
1154 6b90c22e Iustin Pop

1155 6b90c22e Iustin Pop
    """
1156 6b90c22e Iustin Pop
    if self.minor is None:
1157 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("GetStats() called while not attached")
1158 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1159 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1160 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1161 6b90c22e Iustin Pop
                                    self.minor)
1162 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1163 6b90c22e Iustin Pop
1164 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1165 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1166 a2cfdea2 Iustin Pop

1167 a2cfdea2 Iustin Pop
    Returns:
1168 583e3f6f Iustin Pop
     (sync_percent, estimated_time, is_degraded)
1169 a2cfdea2 Iustin Pop

1170 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1171 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1172 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1173 0834c866 Iustin Pop

1174 0834c866 Iustin Pop

1175 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1176 0834c866 Iustin Pop
    network not connected or local disk missing.
1177 0834c866 Iustin Pop

1178 0834c866 Iustin Pop
    We compute the ldisk parameter based on wheter we have a local
1179 0834c866 Iustin Pop
    disk or not.
1180 a2cfdea2 Iustin Pop

1181 a2cfdea2 Iustin Pop
    """
1182 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1183 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1184 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1185 6b90c22e Iustin Pop
    ldisk = not stats.is_disk_uptodate
1186 6b90c22e Iustin Pop
    is_degraded = not stats.is_connected
1187 6b90c22e Iustin Pop
    return stats.sync_percent, stats.est_time, is_degraded or ldisk, ldisk
1188 a2cfdea2 Iustin Pop
1189 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1190 a2cfdea2 Iustin Pop
    """Make the local state primary.
1191 a2cfdea2 Iustin Pop

1192 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1193 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1194 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1195 f860ff4e Guido Trotter
    is mandatory.
1196 a2cfdea2 Iustin Pop

1197 a2cfdea2 Iustin Pop
    """
1198 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1199 a2cfdea2 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1200 a2cfdea2 Iustin Pop
      return False
1201 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1202 a2cfdea2 Iustin Pop
    if force:
1203 a2cfdea2 Iustin Pop
      cmd.append("-o")
1204 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1205 a2cfdea2 Iustin Pop
    if result.failed:
1206 fdbd668d Iustin Pop
      msg = ("Can't make drbd device primary: %s" % result.output)
1207 fdbd668d Iustin Pop
      logger.Error(msg)
1208 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1209 a2cfdea2 Iustin Pop
1210 a2cfdea2 Iustin Pop
  def Close(self):
1211 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1212 a2cfdea2 Iustin Pop

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

1215 a2cfdea2 Iustin Pop
    """
1216 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1217 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
1218 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1219 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1220 a2cfdea2 Iustin Pop
    if result.failed:
1221 fdbd668d Iustin Pop
      msg = ("Can't switch drbd device to"
1222 fdbd668d Iustin Pop
             " secondary: %s" % result.output)
1223 fdbd668d Iustin Pop
      logger.Error(msg)
1224 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1225 a2cfdea2 Iustin Pop
1226 a2cfdea2 Iustin Pop
  def Attach(self):
1227 a2cfdea2 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1228 a2cfdea2 Iustin Pop

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

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

1285 a2cfdea2 Iustin Pop
    Method:
1286 a2cfdea2 Iustin Pop
      - if we have a local backing device, we bind to it by:
1287 a2cfdea2 Iustin Pop
        - checking the list of used drbd devices
1288 a2cfdea2 Iustin Pop
        - check if the local minor use of any of them is our own device
1289 a2cfdea2 Iustin Pop
        - if yes, abort?
1290 a2cfdea2 Iustin Pop
        - if not, bind
1291 a2cfdea2 Iustin Pop
      - if we have a local/remote net info:
1292 a2cfdea2 Iustin Pop
        - redo the local backing device step for the remote device
1293 a2cfdea2 Iustin Pop
        - check if any drbd device is using the local port,
1294 a2cfdea2 Iustin Pop
          if yes abort
1295 a2cfdea2 Iustin Pop
        - check if any remote drbd device is using the remote
1296 a2cfdea2 Iustin Pop
          port, if yes abort (for now)
1297 a2cfdea2 Iustin Pop
        - bind our net port
1298 a2cfdea2 Iustin Pop
        - bind the remote net port
1299 a2cfdea2 Iustin Pop

1300 a2cfdea2 Iustin Pop
    """
1301 a2cfdea2 Iustin Pop
    self.Attach()
1302 a2cfdea2 Iustin Pop
    if self.minor is not None:
1303 a2cfdea2 Iustin Pop
      logger.Info("Already assembled")
1304 a2cfdea2 Iustin Pop
      return True
1305 a2cfdea2 Iustin Pop
1306 a2cfdea2 Iustin Pop
    result = super(DRBD8, self).Assemble()
1307 a2cfdea2 Iustin Pop
    if not result:
1308 a2cfdea2 Iustin Pop
      return result
1309 a2cfdea2 Iustin Pop
1310 a2cfdea2 Iustin Pop
    minor = self._FindUnusedMinor()
1311 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
1312 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1313 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1314 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
1315 a2cfdea2 Iustin Pop
      if not result:
1316 a2cfdea2 Iustin Pop
        return False
1317 a2cfdea2 Iustin Pop
      need_localdev_teardown = True
1318 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1319 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
1320 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
1321 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
1322 a2cfdea2 Iustin Pop
                                 "C")
1323 a2cfdea2 Iustin Pop
      if not result:
1324 a2cfdea2 Iustin Pop
        if need_localdev_teardown:
1325 a2cfdea2 Iustin Pop
          # we will ignore failures from this
1326 a2cfdea2 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
1327 a2cfdea2 Iustin Pop
          self._ShutdownAll(minor)
1328 a2cfdea2 Iustin Pop
        return False
1329 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1330 a2cfdea2 Iustin Pop
    return True
1331 a2cfdea2 Iustin Pop
1332 a2cfdea2 Iustin Pop
  @classmethod
1333 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1334 b00b95dd Iustin Pop
    """Detach from the local device.
1335 b00b95dd Iustin Pop

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

1339 b00b95dd Iustin Pop
    """
1340 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1341 b00b95dd Iustin Pop
    if result.failed:
1342 b00b95dd Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
1343 b00b95dd Iustin Pop
    return not result.failed
1344 b00b95dd Iustin Pop
1345 b00b95dd Iustin Pop
  @classmethod
1346 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1347 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1348 f3e513ad Iustin Pop

1349 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1350 f3e513ad Iustin Pop

1351 f3e513ad Iustin Pop
    """
1352 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1353 a8459f1c Iustin Pop
    if result.failed:
1354 a8459f1c Iustin Pop
      logger.Error("Can't shutdown network: %s" % result.output)
1355 f3e513ad Iustin Pop
    return not result.failed
1356 f3e513ad Iustin Pop
1357 f3e513ad Iustin Pop
  @classmethod
1358 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1359 a2cfdea2 Iustin Pop
    """Deactivate the device.
1360 a2cfdea2 Iustin Pop

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

1363 a2cfdea2 Iustin Pop
    """
1364 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1365 a2cfdea2 Iustin Pop
    if result.failed:
1366 a2cfdea2 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
1367 a2cfdea2 Iustin Pop
    return not result.failed
1368 a2cfdea2 Iustin Pop
1369 a2cfdea2 Iustin Pop
  def Shutdown(self):
1370 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1371 a2cfdea2 Iustin Pop

1372 a2cfdea2 Iustin Pop
    """
1373 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1374 a2cfdea2 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
1375 a2cfdea2 Iustin Pop
      return True
1376 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
1377 a2cfdea2 Iustin Pop
      return False
1378 a2cfdea2 Iustin Pop
    self.minor = None
1379 a2cfdea2 Iustin Pop
    self.dev_path = None
1380 a2cfdea2 Iustin Pop
    return True
1381 a2cfdea2 Iustin Pop
1382 a2cfdea2 Iustin Pop
  def Remove(self):
1383 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1384 a2cfdea2 Iustin Pop

1385 a2cfdea2 Iustin Pop
    """
1386 a2cfdea2 Iustin Pop
    return self.Shutdown()
1387 a2cfdea2 Iustin Pop
1388 a2cfdea2 Iustin Pop
  @classmethod
1389 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1390 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1391 a2cfdea2 Iustin Pop

1392 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1393 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1394 a2cfdea2 Iustin Pop

1395 a2cfdea2 Iustin Pop
    """
1396 a2cfdea2 Iustin Pop
    if len(children) != 2:
1397 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1398 a2cfdea2 Iustin Pop
    meta = children[1]
1399 a2cfdea2 Iustin Pop
    meta.Assemble()
1400 a2cfdea2 Iustin Pop
    if not meta.Attach():
1401 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1402 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
1403 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1404 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
1405 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
1406 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1407 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
1408 a2cfdea2 Iustin Pop
1409 1005d816 Iustin Pop
  def Grow(self, amount):
1410 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1411 1005d816 Iustin Pop

1412 1005d816 Iustin Pop
    """
1413 1005d816 Iustin Pop
    if self.minor is None:
1414 1005d816 Iustin Pop
      raise errors.ProgrammerError("drbd8: Grow called while not attached")
1415 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1416 1005d816 Iustin Pop
      raise errors.BlockDeviceError("Cannot grow diskless DRBD8 device")
1417 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1418 1005d816 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize"])
1419 1005d816 Iustin Pop
    if result.failed:
1420 1005d816 Iustin Pop
      raise errors.BlockDeviceError("resize failed for %s: %s" %
1421 1005d816 Iustin Pop
                                    (self.dev_path, result.output))
1422 1005d816 Iustin Pop
    return
1423 1005d816 Iustin Pop
1424 a8083063 Iustin Pop
1425 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1426 6f695a2e Manuel Franceschini
  """File device.
1427 abdf0113 Iustin Pop

1428 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1429 6f695a2e Manuel Franceschini

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

1432 6f695a2e Manuel Franceschini
  """
1433 6f695a2e Manuel Franceschini
  def __init__(self, unique_id, children):
1434 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1435 6f695a2e Manuel Franceschini

1436 6f695a2e Manuel Franceschini
    """
1437 6f695a2e Manuel Franceschini
    if children:
1438 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1439 6f695a2e Manuel Franceschini
    super(FileStorage, self).__init__(unique_id, children)
1440 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1441 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1442 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1443 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1444 6f695a2e Manuel Franceschini
1445 6f695a2e Manuel Franceschini
  def Assemble(self):
1446 6f695a2e Manuel Franceschini
    """Assemble the device.
1447 6f695a2e Manuel Franceschini

1448 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1449 6f695a2e Manuel Franceschini

1450 6f695a2e Manuel Franceschini
    """
1451 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1452 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("File device '%s' does not exist." %
1453 6f695a2e Manuel Franceschini
                                    self.dev_path)
1454 6f695a2e Manuel Franceschini
    return True
1455 6f695a2e Manuel Franceschini
1456 6f695a2e Manuel Franceschini
  def Shutdown(self):
1457 6f695a2e Manuel Franceschini
    """Shutdown the device.
1458 6f695a2e Manuel Franceschini

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

1462 6f695a2e Manuel Franceschini
    """
1463 6f695a2e Manuel Franceschini
    return True
1464 6f695a2e Manuel Franceschini
1465 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1466 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1467 6f695a2e Manuel Franceschini

1468 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1469 6f695a2e Manuel Franceschini

1470 6f695a2e Manuel Franceschini
    """
1471 6f695a2e Manuel Franceschini
    pass
1472 6f695a2e Manuel Franceschini
1473 6f695a2e Manuel Franceschini
  def Close(self):
1474 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1475 6f695a2e Manuel Franceschini

1476 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1477 6f695a2e Manuel Franceschini

1478 6f695a2e Manuel Franceschini
    """
1479 6f695a2e Manuel Franceschini
    pass
1480 6f695a2e Manuel Franceschini
1481 6f695a2e Manuel Franceschini
  def Remove(self):
1482 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1483 6f695a2e Manuel Franceschini

1484 6f695a2e Manuel Franceschini
    Returns:
1485 6f695a2e Manuel Franceschini
      boolean indicating wheter removal of file was successful or not.
1486 6f695a2e Manuel Franceschini

1487 6f695a2e Manuel Franceschini
    """
1488 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1489 6f695a2e Manuel Franceschini
      return True
1490 6f695a2e Manuel Franceschini
    try:
1491 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1492 6f695a2e Manuel Franceschini
      return True
1493 6f695a2e Manuel Franceschini
    except OSError, err:
1494 6f695a2e Manuel Franceschini
      logger.Error("Can't remove file '%s': %s"
1495 6f695a2e Manuel Franceschini
                   % (self.dev_path, err))
1496 6f695a2e Manuel Franceschini
      return False
1497 6f695a2e Manuel Franceschini
1498 6f695a2e Manuel Franceschini
  def Attach(self):
1499 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1500 6f695a2e Manuel Franceschini

1501 6f695a2e Manuel Franceschini
    Check if this file already exists.
1502 6f695a2e Manuel Franceschini

1503 6f695a2e Manuel Franceschini
    Returns:
1504 6f695a2e Manuel Franceschini
      boolean indicating if file exists or not.
1505 6f695a2e Manuel Franceschini

1506 6f695a2e Manuel Franceschini
    """
1507 6f695a2e Manuel Franceschini
    if os.path.exists(self.dev_path):
1508 6f695a2e Manuel Franceschini
      return True
1509 6f695a2e Manuel Franceschini
    return False
1510 6f695a2e Manuel Franceschini
1511 6f695a2e Manuel Franceschini
  @classmethod
1512 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1513 6f695a2e Manuel Franceschini
    """Create a new file.
1514 6f695a2e Manuel Franceschini

1515 6f695a2e Manuel Franceschini
    Args:
1516 6f695a2e Manuel Franceschini
      children:
1517 6f695a2e Manuel Franceschini
      size: integer size of file in MiB
1518 6f695a2e Manuel Franceschini

1519 6f695a2e Manuel Franceschini
    Returns:
1520 6f695a2e Manuel Franceschini
      A ganeti.bdev.FileStorage object.
1521 6f695a2e Manuel Franceschini

1522 6f695a2e Manuel Franceschini
    """
1523 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1524 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1525 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1526 6f695a2e Manuel Franceschini
    try:
1527 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
1528 6f695a2e Manuel Franceschini
    except IOError, err:
1529 b62ddbe5 Guido Trotter
      raise errors.BlockDeviceError("Could not create '%'" % err)
1530 6f695a2e Manuel Franceschini
    else:
1531 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1532 6f695a2e Manuel Franceschini
      f.close()
1533 6f695a2e Manuel Franceschini
1534 6f695a2e Manuel Franceschini
    return FileStorage(unique_id, children)
1535 6f695a2e Manuel Franceschini
1536 6f695a2e Manuel Franceschini
1537 a8083063 Iustin Pop
DEV_MAP = {
1538 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1539 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1540 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
1541 a8083063 Iustin Pop
  }
1542 a8083063 Iustin Pop
1543 a8083063 Iustin Pop
1544 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
1545 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1546 a8083063 Iustin Pop

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

1550 a8083063 Iustin Pop
  """
1551 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1552 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1553 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1554 a8083063 Iustin Pop
  if not device.Attach():
1555 a8083063 Iustin Pop
    return None
1556 a8083063 Iustin Pop
  return  device
1557 a8083063 Iustin Pop
1558 a8083063 Iustin Pop
1559 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
1560 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1561 a8083063 Iustin Pop

1562 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
1563 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
1564 a8083063 Iustin Pop

1565 a8083063 Iustin Pop
  """
1566 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1567 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1568 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1569 a8083063 Iustin Pop
  if not device.Attach():
1570 a8083063 Iustin Pop
    device.Assemble()
1571 be1ba2bd Iustin Pop
    if not device.Attach():
1572 be1ba2bd Iustin Pop
      raise errors.BlockDeviceError("Can't find a valid block device for"
1573 be1ba2bd Iustin Pop
                                    " %s/%s/%s" %
1574 be1ba2bd Iustin Pop
                                    (dev_type, unique_id, children))
1575 a8083063 Iustin Pop
  return device
1576 a8083063 Iustin Pop
1577 a8083063 Iustin Pop
1578 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1579 a8083063 Iustin Pop
  """Create a device.
1580 a8083063 Iustin Pop

1581 a8083063 Iustin Pop
  """
1582 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1583 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1584 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1585 a8083063 Iustin Pop
  return device