Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 099c52ad

History | View | Annotate | Download (60.4 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 96acbc09 Michael Hanselmann
from ganeti import objects
35 a8083063 Iustin Pop
36 a8083063 Iustin Pop
37 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
38 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
39 82463074 Iustin Pop

40 82463074 Iustin Pop
  This is used in order to simplify the execution of cleanup or
41 82463074 Iustin Pop
  rollback functions.
42 82463074 Iustin Pop

43 82463074 Iustin Pop
  @rtype: boolean
44 82463074 Iustin Pop
  @return: True when fn didn't raise an exception, False otherwise
45 82463074 Iustin Pop

46 82463074 Iustin Pop
  """
47 82463074 Iustin Pop
  try:
48 82463074 Iustin Pop
    fn(*args, **kwargs)
49 82463074 Iustin Pop
    return True
50 82463074 Iustin Pop
  except errors.BlockDeviceError, err:
51 099c52ad Iustin Pop
    logging.warning("Caught BlockDeviceError but ignoring: %s", str(err))
52 82463074 Iustin Pop
    return False
53 82463074 Iustin Pop
54 82463074 Iustin Pop
55 82463074 Iustin Pop
def _ThrowError(msg, *args):
56 82463074 Iustin Pop
  """Log an error to the node daemon and the raise an exception.
57 82463074 Iustin Pop

58 82463074 Iustin Pop
  @type msg: string
59 82463074 Iustin Pop
  @param msg: the text of the exception
60 82463074 Iustin Pop
  @raise errors.BlockDeviceError
61 82463074 Iustin Pop

62 82463074 Iustin Pop
  """
63 82463074 Iustin Pop
  if args:
64 82463074 Iustin Pop
    msg = msg % args
65 82463074 Iustin Pop
  logging.error(msg)
66 82463074 Iustin Pop
  raise errors.BlockDeviceError(msg)
67 82463074 Iustin Pop
68 82463074 Iustin Pop
69 a8083063 Iustin Pop
class BlockDev(object):
70 a8083063 Iustin Pop
  """Block device abstract class.
71 a8083063 Iustin Pop

72 a8083063 Iustin Pop
  A block device can be in the following states:
73 a8083063 Iustin Pop
    - not existing on the system, and by `Create()` it goes into:
74 a8083063 Iustin Pop
    - existing but not setup/not active, and by `Assemble()` goes into:
75 a8083063 Iustin Pop
    - active read-write and by `Open()` it goes into
76 a8083063 Iustin Pop
    - online (=used, or ready for use)
77 a8083063 Iustin Pop

78 a8083063 Iustin Pop
  A device can also be online but read-only, however we are not using
79 abdf0113 Iustin Pop
  the readonly state (LV has it, if needed in the future) and we are
80 abdf0113 Iustin Pop
  usually looking at this like at a stack, so it's easier to
81 abdf0113 Iustin Pop
  conceptualise the transition from not-existing to online and back
82 a8083063 Iustin Pop
  like a linear one.
83 a8083063 Iustin Pop

84 a8083063 Iustin Pop
  The many different states of the device are due to the fact that we
85 a8083063 Iustin Pop
  need to cover many device types:
86 a8083063 Iustin Pop
    - logical volumes are created, lvchange -a y $lv, and used
87 a8083063 Iustin Pop
    - drbd devices are attached to a local disk/remote peer and made primary
88 a8083063 Iustin Pop

89 a8083063 Iustin Pop
  A block device is identified by three items:
90 a8083063 Iustin Pop
    - the /dev path of the device (dynamic)
91 a8083063 Iustin Pop
    - a unique ID of the device (static)
92 a8083063 Iustin Pop
    - it's major/minor pair (dynamic)
93 a8083063 Iustin Pop

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

100 a8083063 Iustin Pop
  You can get to a device in two ways:
101 a8083063 Iustin Pop
    - creating the (real) device, which returns you
102 abdf0113 Iustin Pop
      an attached instance (lvcreate)
103 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
104 a8083063 Iustin Pop

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

111 a8083063 Iustin Pop
  """
112 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
113 a8083063 Iustin Pop
    self._children = children
114 a8083063 Iustin Pop
    self.dev_path = None
115 a8083063 Iustin Pop
    self.unique_id = unique_id
116 a8083063 Iustin Pop
    self.major = None
117 a8083063 Iustin Pop
    self.minor = None
118 cb999543 Iustin Pop
    self.attached = False
119 464f8daf Iustin Pop
    self.size = size
120 a8083063 Iustin Pop
121 a8083063 Iustin Pop
  def Assemble(self):
122 a8083063 Iustin Pop
    """Assemble the device from its components.
123 a8083063 Iustin Pop

124 f87548b5 Iustin Pop
    Implementations of this method by child classes must ensure that:
125 f87548b5 Iustin Pop
      - after the device has been assembled, it knows its major/minor
126 f87548b5 Iustin Pop
        numbers; this allows other devices (usually parents) to probe
127 f87548b5 Iustin Pop
        correctly for their children
128 f87548b5 Iustin Pop
      - calling this method on an existing, in-use device is safe
129 f87548b5 Iustin Pop
      - if the device is already configured (and in an OK state),
130 f87548b5 Iustin Pop
        this method is idempotent
131 a8083063 Iustin Pop

132 a8083063 Iustin Pop
    """
133 1063abd1 Iustin Pop
    pass
134 a8083063 Iustin Pop
135 a8083063 Iustin Pop
  def Attach(self):
136 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
137 a8083063 Iustin Pop

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

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

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

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

158 a8083063 Iustin Pop
    """
159 a8083063 Iustin Pop
    raise NotImplementedError
160 a8083063 Iustin Pop
161 a8083063 Iustin Pop
  def Remove(self):
162 a8083063 Iustin Pop
    """Remove this device.
163 a8083063 Iustin Pop

164 abdf0113 Iustin Pop
    This makes sense only for some of the device types: LV and file
165 5bbd3f7f Michael Hanselmann
    storage. Also note that if the device can't attach, the removal
166 abdf0113 Iustin Pop
    can't be completed.
167 a8083063 Iustin Pop

168 a8083063 Iustin Pop
    """
169 a8083063 Iustin Pop
    raise NotImplementedError
170 a8083063 Iustin Pop
171 f3e513ad Iustin Pop
  def Rename(self, new_id):
172 f3e513ad Iustin Pop
    """Rename this device.
173 f3e513ad Iustin Pop

174 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
175 f3e513ad Iustin Pop

176 f3e513ad Iustin Pop
    """
177 f3e513ad Iustin Pop
    raise NotImplementedError
178 f3e513ad Iustin Pop
179 a8083063 Iustin Pop
  def Open(self, force=False):
180 a8083063 Iustin Pop
    """Make the device ready for use.
181 a8083063 Iustin Pop

182 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
183 a8083063 Iustin Pop
    devices need this.
184 a8083063 Iustin Pop

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

188 a8083063 Iustin Pop
    """
189 a8083063 Iustin Pop
    raise NotImplementedError
190 a8083063 Iustin Pop
191 a8083063 Iustin Pop
  def Shutdown(self):
192 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
193 a8083063 Iustin Pop

194 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
195 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
196 a8083063 Iustin Pop
    assembled after this call.
197 a8083063 Iustin Pop

198 a8083063 Iustin Pop
    """
199 a8083063 Iustin Pop
    raise NotImplementedError
200 a8083063 Iustin Pop
201 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
202 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
203 a8083063 Iustin Pop

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

206 a8083063 Iustin Pop
    """
207 a8083063 Iustin Pop
    result = True
208 a8083063 Iustin Pop
    if self._children:
209 a8083063 Iustin Pop
      for child in self._children:
210 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
211 a8083063 Iustin Pop
    return result
212 a8083063 Iustin Pop
213 a8083063 Iustin Pop
  def GetSyncStatus(self):
214 a8083063 Iustin Pop
    """Returns the sync status of the device.
215 a8083063 Iustin Pop

216 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
217 a8083063 Iustin Pop
    status of the mirror.
218 a8083063 Iustin Pop

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

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

224 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
225 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
226 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
227 a8083063 Iustin Pop

228 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
229 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
230 0834c866 Iustin Pop
    return False (not degraded).
231 0834c866 Iustin Pop

232 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
233 c41eea6e Iustin Pop

234 a8083063 Iustin Pop
    """
235 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
236 96acbc09 Michael Hanselmann
                                  major=self.major,
237 96acbc09 Michael Hanselmann
                                  minor=self.minor,
238 96acbc09 Michael Hanselmann
                                  sync_percent=None,
239 96acbc09 Michael Hanselmann
                                  estimated_time=None,
240 96acbc09 Michael Hanselmann
                                  is_degraded=False,
241 f208978a Michael Hanselmann
                                  ldisk_status=constants.LDS_OKAY)
242 a8083063 Iustin Pop
243 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
244 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
245 a8083063 Iustin Pop

246 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
247 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
248 a8083063 Iustin Pop
    children.
249 a8083063 Iustin Pop

250 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
251 96acbc09 Michael Hanselmann

252 a8083063 Iustin Pop
    """
253 96acbc09 Michael Hanselmann
    status = self.GetSyncStatus()
254 96acbc09 Michael Hanselmann
255 96acbc09 Michael Hanselmann
    min_percent = status.sync_percent
256 96acbc09 Michael Hanselmann
    max_time = status.estimated_time
257 96acbc09 Michael Hanselmann
    is_degraded = status.is_degraded
258 f208978a Michael Hanselmann
    ldisk_status = status.ldisk_status
259 96acbc09 Michael Hanselmann
260 a8083063 Iustin Pop
    if self._children:
261 a8083063 Iustin Pop
      for child in self._children:
262 96acbc09 Michael Hanselmann
        child_status = child.GetSyncStatus()
263 96acbc09 Michael Hanselmann
264 a8083063 Iustin Pop
        if min_percent is None:
265 96acbc09 Michael Hanselmann
          min_percent = child_status.sync_percent
266 96acbc09 Michael Hanselmann
        elif child_status.sync_percent is not None:
267 96acbc09 Michael Hanselmann
          min_percent = min(min_percent, child_status.sync_percent)
268 96acbc09 Michael Hanselmann
269 a8083063 Iustin Pop
        if max_time is None:
270 96acbc09 Michael Hanselmann
          max_time = child_status.estimated_time
271 96acbc09 Michael Hanselmann
        elif child_status.estimated_time is not None:
272 96acbc09 Michael Hanselmann
          max_time = max(max_time, child_status.estimated_time)
273 96acbc09 Michael Hanselmann
274 96acbc09 Michael Hanselmann
        is_degraded = is_degraded or child_status.is_degraded
275 f208978a Michael Hanselmann
276 f208978a Michael Hanselmann
        if ldisk_status is None:
277 f208978a Michael Hanselmann
          ldisk_status = child_status.ldisk_status
278 f208978a Michael Hanselmann
        elif child_status.ldisk_status is not None:
279 f208978a Michael Hanselmann
          ldisk_status = max(ldisk_status, child_status.ldisk_status)
280 96acbc09 Michael Hanselmann
281 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
282 96acbc09 Michael Hanselmann
                                  major=self.major,
283 96acbc09 Michael Hanselmann
                                  minor=self.minor,
284 96acbc09 Michael Hanselmann
                                  sync_percent=min_percent,
285 96acbc09 Michael Hanselmann
                                  estimated_time=max_time,
286 96acbc09 Michael Hanselmann
                                  is_degraded=is_degraded,
287 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
288 a8083063 Iustin Pop
289 a8083063 Iustin Pop
290 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
291 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
292 a0c3fea1 Michael Hanselmann

293 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
294 a0c3fea1 Michael Hanselmann

295 a0c3fea1 Michael Hanselmann
    """
296 a0c3fea1 Michael Hanselmann
    for child in self._children:
297 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
298 a0c3fea1 Michael Hanselmann
299 1005d816 Iustin Pop
  def Grow(self, amount):
300 1005d816 Iustin Pop
    """Grow the block device.
301 1005d816 Iustin Pop

302 c41eea6e Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
303 1005d816 Iustin Pop

304 1005d816 Iustin Pop
    """
305 1005d816 Iustin Pop
    raise NotImplementedError
306 a0c3fea1 Michael Hanselmann
307 fcff3897 Iustin Pop
  def GetActualSize(self):
308 fcff3897 Iustin Pop
    """Return the actual disk size.
309 fcff3897 Iustin Pop

310 fcff3897 Iustin Pop
    @note: the device needs to be active when this is called
311 fcff3897 Iustin Pop

312 fcff3897 Iustin Pop
    """
313 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
314 fcff3897 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize64", self.dev_path])
315 fcff3897 Iustin Pop
    if result.failed:
316 fcff3897 Iustin Pop
      _ThrowError("blockdev failed (%s): %s",
317 fcff3897 Iustin Pop
                  result.fail_reason, result.output)
318 fcff3897 Iustin Pop
    try:
319 fcff3897 Iustin Pop
      sz = int(result.output.strip())
320 fcff3897 Iustin Pop
    except (ValueError, TypeError), err:
321 fcff3897 Iustin Pop
      _ThrowError("Failed to parse blockdev output: %s", str(err))
322 fcff3897 Iustin Pop
    return sz
323 fcff3897 Iustin Pop
324 a8083063 Iustin Pop
  def __repr__(self):
325 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
326 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
327 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
328 a8083063 Iustin Pop
329 a8083063 Iustin Pop
330 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
331 a8083063 Iustin Pop
  """Logical Volume block device.
332 a8083063 Iustin Pop

333 a8083063 Iustin Pop
  """
334 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
335 a8083063 Iustin Pop
    """Attaches to a LV device.
336 a8083063 Iustin Pop

337 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
338 a8083063 Iustin Pop

339 a8083063 Iustin Pop
    """
340 464f8daf Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children, size)
341 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
342 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
343 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
344 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
345 99e8295c Iustin Pop
    self._degraded = True
346 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
347 a8083063 Iustin Pop
    self.Attach()
348 a8083063 Iustin Pop
349 a8083063 Iustin Pop
  @classmethod
350 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
351 a8083063 Iustin Pop
    """Create a new logical volume.
352 a8083063 Iustin Pop

353 a8083063 Iustin Pop
    """
354 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
355 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
356 6c626518 Iustin Pop
                                   str(unique_id))
357 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
358 2070598f Iustin Pop
    pvs_info = cls.GetPVInfo([vg_name])
359 a8083063 Iustin Pop
    if not pvs_info:
360 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", vg_name)
361 a8083063 Iustin Pop
    pvs_info.sort()
362 a8083063 Iustin Pop
    pvs_info.reverse()
363 5b7b5d49 Guido Trotter
364 5b7b5d49 Guido Trotter
    pvlist = [ pv[1] for pv in pvs_info ]
365 96c7a5b0 Iustin Pop
    if utils.any(pvlist, lambda v: ":" in v):
366 96c7a5b0 Iustin Pop
      _ThrowError("Some of your PVs have invalid character ':'"
367 96c7a5b0 Iustin Pop
                  " in their name")
368 5b7b5d49 Guido Trotter
    free_size = sum([ pv[0] for pv in pvs_info ])
369 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
370 fecbe9d5 Iustin Pop
    stripes = min(current_pvs, constants.LVM_STRIPECOUNT)
371 5b7b5d49 Guido Trotter
372 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
373 5b7b5d49 Guido Trotter
    # calling the create function.
374 a8083063 Iustin Pop
    if free_size < size:
375 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
376 82463074 Iustin Pop
                  " available %s", size, free_size)
377 fecbe9d5 Iustin Pop
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
378 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
379 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
380 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
381 fecbe9d5 Iustin Pop
    # stripes
382 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
383 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
384 fecbe9d5 Iustin Pop
      if not result.failed:
385 fecbe9d5 Iustin Pop
        break
386 a8083063 Iustin Pop
    if result.failed:
387 82463074 Iustin Pop
      _ThrowError("LV create failed (%s): %s",
388 82463074 Iustin Pop
                  result.fail_reason, result.output)
389 464f8daf Iustin Pop
    return LogicalVolume(unique_id, children, size)
390 a8083063 Iustin Pop
391 a8083063 Iustin Pop
  @staticmethod
392 2070598f Iustin Pop
  def GetPVInfo(vg_names, filter_allocatable=True):
393 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
394 a8083063 Iustin Pop

395 2070598f Iustin Pop
    @param vg_names: list of volume group names, if empty all will be returned
396 2070598f Iustin Pop
    @param filter_allocatable: whether to skip over unallocatable PVs
397 a8083063 Iustin Pop

398 c41eea6e Iustin Pop
    @rtype: list
399 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
400 098c0958 Michael Hanselmann

401 a8083063 Iustin Pop
    """
402 96c7a5b0 Iustin Pop
    sep = "|"
403 a8083063 Iustin Pop
    command = ["pvs", "--noheadings", "--nosuffix", "--units=m",
404 a8083063 Iustin Pop
               "-opv_name,vg_name,pv_free,pv_attr", "--unbuffered",
405 96c7a5b0 Iustin Pop
               "--separator=%s" % sep ]
406 a8083063 Iustin Pop
    result = utils.RunCmd(command)
407 a8083063 Iustin Pop
    if result.failed:
408 468c5f77 Iustin Pop
      logging.error("Can't get the PV information: %s - %s",
409 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
410 a8083063 Iustin Pop
      return None
411 a8083063 Iustin Pop
    data = []
412 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
413 96c7a5b0 Iustin Pop
      fields = line.strip().split(sep)
414 a8083063 Iustin Pop
      if len(fields) != 4:
415 468c5f77 Iustin Pop
        logging.error("Can't parse pvs output: line '%s'", line)
416 a8083063 Iustin Pop
        return None
417 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
418 2070598f Iustin Pop
      if filter_allocatable and fields[3][0] != 'a':
419 a8083063 Iustin Pop
        continue
420 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
421 2070598f Iustin Pop
      if vg_names and fields[1] not in vg_names:
422 2070598f Iustin Pop
        continue
423 2070598f Iustin Pop
      data.append((float(fields[2]), fields[0], fields[1]))
424 a8083063 Iustin Pop
425 a8083063 Iustin Pop
    return data
426 a8083063 Iustin Pop
427 a8083063 Iustin Pop
  def Remove(self):
428 a8083063 Iustin Pop
    """Remove this logical volume.
429 a8083063 Iustin Pop

430 a8083063 Iustin Pop
    """
431 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
432 a8083063 Iustin Pop
      # the LV does not exist
433 0c6c04ec Iustin Pop
      return
434 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
435 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
436 a8083063 Iustin Pop
    if result.failed:
437 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
438 a8083063 Iustin Pop
439 f3e513ad Iustin Pop
  def Rename(self, new_id):
440 f3e513ad Iustin Pop
    """Rename this logical volume.
441 f3e513ad Iustin Pop

442 f3e513ad Iustin Pop
    """
443 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
444 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
445 f3e513ad Iustin Pop
    new_vg, new_name = new_id
446 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
447 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
448 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
449 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
450 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
451 f3e513ad Iustin Pop
    if result.failed:
452 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
453 be345db0 Iustin Pop
    self._lv_name = new_name
454 be345db0 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
455 be345db0 Iustin Pop
456 a8083063 Iustin Pop
  def Attach(self):
457 a8083063 Iustin Pop
    """Attach to an existing LV.
458 a8083063 Iustin Pop

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

463 a8083063 Iustin Pop
    """
464 cb999543 Iustin Pop
    self.attached = False
465 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
466 38256320 Iustin Pop
                           "--units=m", "--nosuffix",
467 38256320 Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
468 38256320 Iustin Pop
                           "vg_extent_size,stripes", self.dev_path])
469 a8083063 Iustin Pop
    if result.failed:
470 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
471 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
472 a8083063 Iustin Pop
      return False
473 38256320 Iustin Pop
    # the output can (and will) have multiple lines for multi-segment
474 38256320 Iustin Pop
    # LVs, as the 'stripes' parameter is a segment one, so we take
475 38256320 Iustin Pop
    # only the last entry, which is the one we're interested in; note
476 38256320 Iustin Pop
    # that with LVM2 anyway the 'stripes' value must be constant
477 38256320 Iustin Pop
    # across segments, so this is a no-op actually
478 38256320 Iustin Pop
    out = result.stdout.splitlines()
479 38256320 Iustin Pop
    if not out: # totally empty result? splitlines() returns at least
480 38256320 Iustin Pop
                # one line for any non-empty string
481 38256320 Iustin Pop
      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
482 38256320 Iustin Pop
      return False
483 38256320 Iustin Pop
    out = out[-1].strip().rstrip(',')
484 99e8295c Iustin Pop
    out = out.split(",")
485 38256320 Iustin Pop
    if len(out) != 5:
486 38256320 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
487 99e8295c Iustin Pop
      return False
488 99e8295c Iustin Pop
489 38256320 Iustin Pop
    status, major, minor, pe_size, stripes = out
490 99e8295c Iustin Pop
    if len(status) != 6:
491 468c5f77 Iustin Pop
      logging.error("lvs lv_attr is not 6 characters (%s)", status)
492 99e8295c Iustin Pop
      return False
493 99e8295c Iustin Pop
494 99e8295c Iustin Pop
    try:
495 99e8295c Iustin Pop
      major = int(major)
496 99e8295c Iustin Pop
      minor = int(minor)
497 99e8295c Iustin Pop
    except ValueError, err:
498 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
499 99e8295c Iustin Pop
500 38256320 Iustin Pop
    try:
501 38256320 Iustin Pop
      pe_size = int(float(pe_size))
502 38256320 Iustin Pop
    except (TypeError, ValueError), err:
503 38256320 Iustin Pop
      logging.error("Can't parse vg extent size: %s", err)
504 38256320 Iustin Pop
      return False
505 38256320 Iustin Pop
506 38256320 Iustin Pop
    try:
507 38256320 Iustin Pop
      stripes = int(stripes)
508 38256320 Iustin Pop
    except (TypeError, ValueError), err:
509 38256320 Iustin Pop
      logging.error("Can't parse the number of stripes: %s", err)
510 38256320 Iustin Pop
      return False
511 38256320 Iustin Pop
512 99e8295c Iustin Pop
    self.major = major
513 99e8295c Iustin Pop
    self.minor = minor
514 38256320 Iustin Pop
    self.pe_size = pe_size
515 38256320 Iustin Pop
    self.stripe_count = stripes
516 99e8295c Iustin Pop
    self._degraded = status[0] == 'v' # virtual volume, i.e. doesn't backing
517 99e8295c Iustin Pop
                                      # storage
518 cb999543 Iustin Pop
    self.attached = True
519 99e8295c Iustin Pop
    return True
520 a8083063 Iustin Pop
521 a8083063 Iustin Pop
  def Assemble(self):
522 a8083063 Iustin Pop
    """Assemble the device.
523 a8083063 Iustin Pop

524 5bbd3f7f Michael Hanselmann
    We always run `lvchange -ay` on the LV to ensure it's active before
525 5574047a Iustin Pop
    use, as there were cases when xenvg was not active after boot
526 5574047a Iustin Pop
    (also possibly after disk issues).
527 a8083063 Iustin Pop

528 a8083063 Iustin Pop
    """
529 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
530 5574047a Iustin Pop
    if result.failed:
531 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
532 a8083063 Iustin Pop
533 a8083063 Iustin Pop
  def Shutdown(self):
534 a8083063 Iustin Pop
    """Shutdown the device.
535 a8083063 Iustin Pop

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

539 a8083063 Iustin Pop
    """
540 746f7476 Iustin Pop
    pass
541 a8083063 Iustin Pop
542 9db6dbce Iustin Pop
  def GetSyncStatus(self):
543 9db6dbce Iustin Pop
    """Returns the sync status of the device.
544 9db6dbce Iustin Pop

545 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
546 9db6dbce Iustin Pop
    status of the mirror.
547 9db6dbce Iustin Pop

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

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

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

561 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
562 c41eea6e Iustin Pop

563 9db6dbce Iustin Pop
    """
564 f208978a Michael Hanselmann
    if self._degraded:
565 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
566 f208978a Michael Hanselmann
    else:
567 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
568 f208978a Michael Hanselmann
569 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
570 96acbc09 Michael Hanselmann
                                  major=self.major,
571 96acbc09 Michael Hanselmann
                                  minor=self.minor,
572 96acbc09 Michael Hanselmann
                                  sync_percent=None,
573 96acbc09 Michael Hanselmann
                                  estimated_time=None,
574 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
575 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
576 9db6dbce Iustin Pop
577 a8083063 Iustin Pop
  def Open(self, force=False):
578 a8083063 Iustin Pop
    """Make the device ready for I/O.
579 a8083063 Iustin Pop

580 a8083063 Iustin Pop
    This is a no-op for the LV device type.
581 a8083063 Iustin Pop

582 a8083063 Iustin Pop
    """
583 fdbd668d Iustin Pop
    pass
584 a8083063 Iustin Pop
585 a8083063 Iustin Pop
  def Close(self):
586 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
587 a8083063 Iustin Pop

588 a8083063 Iustin Pop
    This is a no-op for the LV device type.
589 a8083063 Iustin Pop

590 a8083063 Iustin Pop
    """
591 fdbd668d Iustin Pop
    pass
592 a8083063 Iustin Pop
593 a8083063 Iustin Pop
  def Snapshot(self, size):
594 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
595 a8083063 Iustin Pop

596 a8083063 Iustin Pop
    """
597 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
598 a8083063 Iustin Pop
599 a8083063 Iustin Pop
    # remove existing snapshot if found
600 464f8daf Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None, size)
601 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
602 a8083063 Iustin Pop
603 2070598f Iustin Pop
    pvs_info = self.GetPVInfo([self._vg_name])
604 a8083063 Iustin Pop
    if not pvs_info:
605 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", self._vg_name)
606 a8083063 Iustin Pop
    pvs_info.sort()
607 a8083063 Iustin Pop
    pvs_info.reverse()
608 2070598f Iustin Pop
    free_size, pv_name, _ = pvs_info[0]
609 a8083063 Iustin Pop
    if free_size < size:
610 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
611 82463074 Iustin Pop
                  " available %s", size, free_size)
612 a8083063 Iustin Pop
613 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
614 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
615 a8083063 Iustin Pop
    if result.failed:
616 82463074 Iustin Pop
      _ThrowError("command: %s error: %s - %s",
617 82463074 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
618 a8083063 Iustin Pop
619 a8083063 Iustin Pop
    return snap_name
620 a8083063 Iustin Pop
621 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
622 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
623 a0c3fea1 Michael Hanselmann

624 a0c3fea1 Michael Hanselmann
    """
625 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
626 a0c3fea1 Michael Hanselmann
627 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
628 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
629 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
630 a0c3fea1 Michael Hanselmann
631 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
632 a0c3fea1 Michael Hanselmann
    text = text[:128]
633 a0c3fea1 Michael Hanselmann
634 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
635 a0c3fea1 Michael Hanselmann
                           self.dev_path])
636 a0c3fea1 Michael Hanselmann
    if result.failed:
637 82463074 Iustin Pop
      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
638 82463074 Iustin Pop
                  result.output)
639 82463074 Iustin Pop
640 1005d816 Iustin Pop
  def Grow(self, amount):
641 1005d816 Iustin Pop
    """Grow the logical volume.
642 1005d816 Iustin Pop

643 1005d816 Iustin Pop
    """
644 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
645 38256320 Iustin Pop
      if not self.Attach():
646 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
647 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
648 38256320 Iustin Pop
    rest = amount % full_stripe_size
649 38256320 Iustin Pop
    if rest != 0:
650 38256320 Iustin Pop
      amount += full_stripe_size - rest
651 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
652 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
653 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
654 1005d816 Iustin Pop
    # supports 'cling'
655 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
656 1005d816 Iustin Pop
      result = utils.RunCmd(["lvextend", "--alloc", alloc_policy,
657 1005d816 Iustin Pop
                             "-L", "+%dm" % amount, self.dev_path])
658 1005d816 Iustin Pop
      if not result.failed:
659 1005d816 Iustin Pop
        return
660 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
661 a0c3fea1 Michael Hanselmann
662 a0c3fea1 Michael Hanselmann
663 6b90c22e Iustin Pop
class DRBD8Status(object):
664 6b90c22e Iustin Pop
  """A DRBD status representation class.
665 6b90c22e Iustin Pop

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

668 6b90c22e Iustin Pop
  """
669 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
670 01e2ce3a Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
671 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
672 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
673 6b90c22e Iustin Pop
                       "\sfinish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
674 6b90c22e Iustin Pop
675 3c003d9d Iustin Pop
  CS_UNCONFIGURED = "Unconfigured"
676 3c003d9d Iustin Pop
  CS_STANDALONE = "StandAlone"
677 3c003d9d Iustin Pop
  CS_WFCONNECTION = "WFConnection"
678 3c003d9d Iustin Pop
  CS_WFREPORTPARAMS = "WFReportParams"
679 3c003d9d Iustin Pop
  CS_CONNECTED = "Connected"
680 3c003d9d Iustin Pop
  CS_STARTINGSYNCS = "StartingSyncS"
681 3c003d9d Iustin Pop
  CS_STARTINGSYNCT = "StartingSyncT"
682 3c003d9d Iustin Pop
  CS_WFBITMAPS = "WFBitMapS"
683 3c003d9d Iustin Pop
  CS_WFBITMAPT = "WFBitMapT"
684 3c003d9d Iustin Pop
  CS_WFSYNCUUID = "WFSyncUUID"
685 3c003d9d Iustin Pop
  CS_SYNCSOURCE = "SyncSource"
686 3c003d9d Iustin Pop
  CS_SYNCTARGET = "SyncTarget"
687 3c003d9d Iustin Pop
  CS_PAUSEDSYNCS = "PausedSyncS"
688 3c003d9d Iustin Pop
  CS_PAUSEDSYNCT = "PausedSyncT"
689 3c003d9d Iustin Pop
  CSET_SYNC = frozenset([
690 3c003d9d Iustin Pop
    CS_WFREPORTPARAMS,
691 3c003d9d Iustin Pop
    CS_STARTINGSYNCS,
692 3c003d9d Iustin Pop
    CS_STARTINGSYNCT,
693 3c003d9d Iustin Pop
    CS_WFBITMAPS,
694 3c003d9d Iustin Pop
    CS_WFBITMAPT,
695 3c003d9d Iustin Pop
    CS_WFSYNCUUID,
696 3c003d9d Iustin Pop
    CS_SYNCSOURCE,
697 3c003d9d Iustin Pop
    CS_SYNCTARGET,
698 3c003d9d Iustin Pop
    CS_PAUSEDSYNCS,
699 3c003d9d Iustin Pop
    CS_PAUSEDSYNCT,
700 3c003d9d Iustin Pop
    ])
701 3c003d9d Iustin Pop
702 3c003d9d Iustin Pop
  DS_DISKLESS = "Diskless"
703 3c003d9d Iustin Pop
  DS_ATTACHING = "Attaching" # transient state
704 3c003d9d Iustin Pop
  DS_FAILED = "Failed" # transient state, next: diskless
705 3c003d9d Iustin Pop
  DS_NEGOTIATING = "Negotiating" # transient state
706 3c003d9d Iustin Pop
  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
707 3c003d9d Iustin Pop
  DS_OUTDATED = "Outdated"
708 3c003d9d Iustin Pop
  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
709 3c003d9d Iustin Pop
  DS_CONSISTENT = "Consistent"
710 3c003d9d Iustin Pop
  DS_UPTODATE = "UpToDate" # normal state
711 3c003d9d Iustin Pop
712 3c003d9d Iustin Pop
  RO_PRIMARY = "Primary"
713 3c003d9d Iustin Pop
  RO_SECONDARY = "Secondary"
714 3c003d9d Iustin Pop
  RO_UNKNOWN = "Unknown"
715 3c003d9d Iustin Pop
716 6b90c22e Iustin Pop
  def __init__(self, procline):
717 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
718 767d52d3 Iustin Pop
    if u:
719 3c003d9d Iustin Pop
      self.cstatus = self.CS_UNCONFIGURED
720 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
721 767d52d3 Iustin Pop
    else:
722 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
723 767d52d3 Iustin Pop
      if not m:
724 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
725 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
726 767d52d3 Iustin Pop
      self.lrole = m.group(2)
727 767d52d3 Iustin Pop
      self.rrole = m.group(3)
728 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
729 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
730 767d52d3 Iustin Pop
731 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
732 6b90c22e Iustin Pop
733 3c003d9d Iustin Pop
    self.is_standalone = self.cstatus == self.CS_STANDALONE
734 3c003d9d Iustin Pop
    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
735 3c003d9d Iustin Pop
    self.is_connected = self.cstatus == self.CS_CONNECTED
736 3c003d9d Iustin Pop
    self.is_primary = self.lrole == self.RO_PRIMARY
737 3c003d9d Iustin Pop
    self.is_secondary = self.lrole == self.RO_SECONDARY
738 3c003d9d Iustin Pop
    self.peer_primary = self.rrole == self.RO_PRIMARY
739 3c003d9d Iustin Pop
    self.peer_secondary = self.rrole == self.RO_SECONDARY
740 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
741 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
742 6b90c22e Iustin Pop
743 3c003d9d Iustin Pop
    self.is_diskless = self.ldisk == self.DS_DISKLESS
744 3c003d9d Iustin Pop
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
745 6b90c22e Iustin Pop
746 3c003d9d Iustin Pop
    self.is_in_resync = self.cstatus in self.CSET_SYNC
747 3c003d9d Iustin Pop
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
748 6b93ec9d Iustin Pop
749 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
750 6b90c22e Iustin Pop
    if m:
751 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
752 6b90c22e Iustin Pop
      hours = int(m.group(2))
753 6b90c22e Iustin Pop
      minutes = int(m.group(3))
754 6b90c22e Iustin Pop
      seconds = int(m.group(4))
755 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
756 6b90c22e Iustin Pop
    else:
757 3c003d9d Iustin Pop
      # we have (in this if branch) no percent information, but if
758 3c003d9d Iustin Pop
      # we're resyncing we need to 'fake' a sync percent information,
759 3c003d9d Iustin Pop
      # as this is how cmdlib determines if it makes sense to wait for
760 3c003d9d Iustin Pop
      # resyncing or not
761 3c003d9d Iustin Pop
      if self.is_in_resync:
762 3c003d9d Iustin Pop
        self.sync_percent = 0
763 3c003d9d Iustin Pop
      else:
764 3c003d9d Iustin Pop
        self.sync_percent = None
765 6b90c22e Iustin Pop
      self.est_time = None
766 6b90c22e Iustin Pop
767 6b90c22e Iustin Pop
768 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
769 0f7f32d9 Iustin Pop
  """Base DRBD class.
770 a8083063 Iustin Pop

771 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
772 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
773 0f7f32d9 Iustin Pop

774 abdf0113 Iustin Pop
  """
775 abdf0113 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
776 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
777 a8083063 Iustin Pop
778 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
779 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
780 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
781 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
782 a8083063 Iustin Pop
783 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
784 6b90c22e Iustin Pop
785 abdf0113 Iustin Pop
  @staticmethod
786 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
787 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
788 a8083063 Iustin Pop

789 a8083063 Iustin Pop
    """
790 abdf0113 Iustin Pop
    try:
791 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
792 f6eaed12 Iustin Pop
    except EnvironmentError, err:
793 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
794 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
795 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
796 f6eaed12 Iustin Pop
      else:
797 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
798 abdf0113 Iustin Pop
    if not data:
799 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
800 abdf0113 Iustin Pop
    return data
801 a8083063 Iustin Pop
802 abdf0113 Iustin Pop
  @staticmethod
803 abdf0113 Iustin Pop
  def _MassageProcData(data):
804 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
805 a8083063 Iustin Pop

806 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
807 c41eea6e Iustin Pop
        for that minor
808 a8083063 Iustin Pop

809 a8083063 Iustin Pop
    """
810 abdf0113 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
811 abdf0113 Iustin Pop
    results = {}
812 abdf0113 Iustin Pop
    old_minor = old_line = None
813 abdf0113 Iustin Pop
    for line in data:
814 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
815 67d101d4 Iustin Pop
        continue
816 abdf0113 Iustin Pop
      lresult = lmatch.match(line)
817 abdf0113 Iustin Pop
      if lresult is not None:
818 abdf0113 Iustin Pop
        if old_minor is not None:
819 abdf0113 Iustin Pop
          results[old_minor] = old_line
820 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
821 abdf0113 Iustin Pop
        old_line = line
822 abdf0113 Iustin Pop
      else:
823 abdf0113 Iustin Pop
        if old_minor is not None:
824 abdf0113 Iustin Pop
          old_line += " " + line.strip()
825 abdf0113 Iustin Pop
    # add last line
826 abdf0113 Iustin Pop
    if old_minor is not None:
827 abdf0113 Iustin Pop
      results[old_minor] = old_line
828 abdf0113 Iustin Pop
    return results
829 a8083063 Iustin Pop
830 abdf0113 Iustin Pop
  @classmethod
831 abdf0113 Iustin Pop
  def _GetVersion(cls):
832 abdf0113 Iustin Pop
    """Return the DRBD version.
833 a8083063 Iustin Pop

834 abdf0113 Iustin Pop
    This will return a dict with keys:
835 c41eea6e Iustin Pop
      - k_major
836 c41eea6e Iustin Pop
      - k_minor
837 c41eea6e Iustin Pop
      - k_point
838 c41eea6e Iustin Pop
      - api
839 c41eea6e Iustin Pop
      - proto
840 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
841 a8083063 Iustin Pop

842 a8083063 Iustin Pop
    """
843 abdf0113 Iustin Pop
    proc_data = cls._GetProcData()
844 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
845 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
846 abdf0113 Iustin Pop
    if not version:
847 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
848 abdf0113 Iustin Pop
                                    first_line)
849 a8083063 Iustin Pop
850 abdf0113 Iustin Pop
    values = version.groups()
851 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
852 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
853 abdf0113 Iustin Pop
              'k_point': int(values[2]),
854 abdf0113 Iustin Pop
              'api': int(values[3]),
855 abdf0113 Iustin Pop
              'proto': int(values[4]),
856 abdf0113 Iustin Pop
             }
857 abdf0113 Iustin Pop
    if values[5] is not None:
858 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
859 a8083063 Iustin Pop
860 abdf0113 Iustin Pop
    return retval
861 abdf0113 Iustin Pop
862 abdf0113 Iustin Pop
  @staticmethod
863 abdf0113 Iustin Pop
  def _DevPath(minor):
864 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
865 a8083063 Iustin Pop

866 a8083063 Iustin Pop
    """
867 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
868 a8083063 Iustin Pop
869 abdf0113 Iustin Pop
  @classmethod
870 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
871 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
872 a8083063 Iustin Pop

873 a8083063 Iustin Pop
    """
874 abdf0113 Iustin Pop
    data = cls._GetProcData()
875 a8083063 Iustin Pop
876 abdf0113 Iustin Pop
    used_devs = {}
877 abdf0113 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
878 abdf0113 Iustin Pop
    for line in data:
879 abdf0113 Iustin Pop
      match = valid_line.match(line)
880 abdf0113 Iustin Pop
      if not match:
881 abdf0113 Iustin Pop
        continue
882 abdf0113 Iustin Pop
      minor = int(match.group(1))
883 abdf0113 Iustin Pop
      state = match.group(2)
884 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
885 abdf0113 Iustin Pop
        continue
886 abdf0113 Iustin Pop
      used_devs[minor] = state, line
887 a8083063 Iustin Pop
888 abdf0113 Iustin Pop
    return used_devs
889 a8083063 Iustin Pop
890 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
891 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
892 0834c866 Iustin Pop

893 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
894 a8083063 Iustin Pop

895 a8083063 Iustin Pop
    """
896 abdf0113 Iustin Pop
    if minor is None:
897 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
898 cb999543 Iustin Pop
      self.attached = False
899 a8083063 Iustin Pop
    else:
900 abdf0113 Iustin Pop
      self.minor = minor
901 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
902 cb999543 Iustin Pop
      self.attached = True
903 a8083063 Iustin Pop
904 a8083063 Iustin Pop
  @staticmethod
905 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
906 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
907 a8083063 Iustin Pop

908 abdf0113 Iustin Pop
    This currently only check the size, which must be around
909 abdf0113 Iustin Pop
    128MiB.
910 a8083063 Iustin Pop

911 a8083063 Iustin Pop
    """
912 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
913 abdf0113 Iustin Pop
    if result.failed:
914 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
915 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
916 a8083063 Iustin Pop
    try:
917 abdf0113 Iustin Pop
      sectors = int(result.stdout)
918 abdf0113 Iustin Pop
    except ValueError:
919 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
920 abdf0113 Iustin Pop
    bytes = sectors * 512
921 abdf0113 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
922 9c793cfb Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
923 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
924 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
925 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
926 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
927 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
928 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
929 1dc10972 Iustin Pop
    if bytes > 1024 * 1024 * 1024:
930 9c793cfb Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
931 a8083063 Iustin Pop
932 abdf0113 Iustin Pop
  def Rename(self, new_id):
933 abdf0113 Iustin Pop
    """Rename a device.
934 a8083063 Iustin Pop

935 abdf0113 Iustin Pop
    This is not supported for drbd devices.
936 a8083063 Iustin Pop

937 a8083063 Iustin Pop
    """
938 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
939 a8083063 Iustin Pop
940 f3e513ad Iustin Pop
941 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
942 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
943 a2cfdea2 Iustin Pop

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

948 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
949 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
950 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
951 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
952 a2cfdea2 Iustin Pop

953 a2cfdea2 Iustin Pop
  """
954 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
955 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
956 a2cfdea2 Iustin Pop
957 cf8df3f3 Iustin Pop
  # timeout constants
958 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
959 cf8df3f3 Iustin Pop
960 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
961 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
962 fc1dc9d7 Iustin Pop
      children = []
963 464f8daf Iustin Pop
    super(DRBD8, self).__init__(unique_id, children, size)
964 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
965 c3f9340c Guido Trotter
    version = self._GetVersion()
966 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
967 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
968 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
969 82463074 Iustin Pop
                  version['k_major'], version['k_minor'])
970 a2cfdea2 Iustin Pop
971 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
972 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
973 f9518d38 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
974 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
975 ffa1c0dc Iustin Pop
    (self._lhost, self._lport,
976 ffa1c0dc Iustin Pop
     self._rhost, self._rport,
977 f9518d38 Iustin Pop
     self._aminor, self._secret) = unique_id
978 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
979 ffa1c0dc Iustin Pop
        self._lport == self._rport):
980 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
981 ffa1c0dc Iustin Pop
                       (unique_id,))
982 a2cfdea2 Iustin Pop
    self.Attach()
983 a2cfdea2 Iustin Pop
984 a2cfdea2 Iustin Pop
  @classmethod
985 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
986 a2cfdea2 Iustin Pop
    """Initialize a meta device.
987 a2cfdea2 Iustin Pop

988 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
989 a2cfdea2 Iustin Pop

990 a2cfdea2 Iustin Pop
    """
991 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
992 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
993 a2cfdea2 Iustin Pop
    if result.failed:
994 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
995 a2cfdea2 Iustin Pop
996 a2cfdea2 Iustin Pop
  @classmethod
997 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
998 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
999 a2cfdea2 Iustin Pop

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

1003 a2cfdea2 Iustin Pop
    """
1004 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1005 a2cfdea2 Iustin Pop
1006 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1007 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
1008 a2cfdea2 Iustin Pop
    highest = None
1009 a2cfdea2 Iustin Pop
    for line in data:
1010 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
1011 a2cfdea2 Iustin Pop
      if match:
1012 a2cfdea2 Iustin Pop
        return int(match.group(1))
1013 a2cfdea2 Iustin Pop
      match = used_line.match(line)
1014 a2cfdea2 Iustin Pop
      if match:
1015 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1016 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1017 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1018 a2cfdea2 Iustin Pop
      return 0
1019 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1020 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1021 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1022 a2cfdea2 Iustin Pop
    return highest + 1
1023 a2cfdea2 Iustin Pop
1024 a2cfdea2 Iustin Pop
  @classmethod
1025 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1026 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1027 a2cfdea2 Iustin Pop

1028 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1029 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1030 a2cfdea2 Iustin Pop

1031 a2cfdea2 Iustin Pop
    """
1032 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1033 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1034 a2cfdea2 Iustin Pop
1035 a2cfdea2 Iustin Pop
    # pyparsing setup
1036 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1037 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1038 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1039 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1040 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1041 a2cfdea2 Iustin Pop
1042 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1043 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1044 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1045 a2cfdea2 Iustin Pop
1046 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1047 a2cfdea2 Iustin Pop
1048 a2cfdea2 Iustin Pop
    # value types
1049 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1050 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1051 34e71fea Karsten Keil
    addr_type = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1052 34e71fea Karsten Keil
                 pyp.Optional(pyp.Literal("ipv6")).suppress())
1053 34e71fea Karsten Keil
    addr_port = (addr_type + pyp.Word(pyp.nums + '.') +
1054 34e71fea Karsten Keil
                 pyp.Literal(':').suppress() + number)
1055 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1056 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
1057 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
1058 01e2ce3a Iustin Pop
    # device name, extended syntax
1059 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1060 a2cfdea2 Iustin Pop
1061 a2cfdea2 Iustin Pop
    # a statement
1062 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1063 01e2ce3a Iustin Pop
            pyp.Optional(addr_port ^ value ^ quoted ^ meta_value ^
1064 01e2ce3a Iustin Pop
                         device_value) +
1065 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1066 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1067 a2cfdea2 Iustin Pop
1068 a2cfdea2 Iustin Pop
    # an entire section
1069 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1070 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1071 a2cfdea2 Iustin Pop
1072 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1073 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1074 a2cfdea2 Iustin Pop
1075 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1076 a2cfdea2 Iustin Pop
1077 a2cfdea2 Iustin Pop
    return bnf
1078 a2cfdea2 Iustin Pop
1079 a2cfdea2 Iustin Pop
  @classmethod
1080 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1081 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1082 a2cfdea2 Iustin Pop

1083 a2cfdea2 Iustin Pop
    """
1084 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1085 a2cfdea2 Iustin Pop
    if result.failed:
1086 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1087 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1088 3840729d Iustin Pop
      return None
1089 3840729d Iustin Pop
    return result.stdout
1090 3840729d Iustin Pop
1091 3840729d Iustin Pop
  @classmethod
1092 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1093 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1094 3840729d Iustin Pop

1095 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1096 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1097 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1098 3840729d Iustin Pop
    by _GetShowData.
1099 3840729d Iustin Pop

1100 3840729d Iustin Pop
    """
1101 3840729d Iustin Pop
    data = {}
1102 a2cfdea2 Iustin Pop
    if not out:
1103 a2cfdea2 Iustin Pop
      return data
1104 a2cfdea2 Iustin Pop
1105 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1106 a2cfdea2 Iustin Pop
    # run pyparse
1107 a2cfdea2 Iustin Pop
1108 a2cfdea2 Iustin Pop
    try:
1109 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1110 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1111 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1112 a2cfdea2 Iustin Pop
1113 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1114 a2cfdea2 Iustin Pop
    for section in results:
1115 a2cfdea2 Iustin Pop
      sname = section[0]
1116 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1117 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1118 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1119 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1120 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1121 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1122 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1123 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1124 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1125 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1126 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1127 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1128 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1129 a2cfdea2 Iustin Pop
    return data
1130 a2cfdea2 Iustin Pop
1131 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1132 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1133 a2cfdea2 Iustin Pop

1134 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1135 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1136 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1137 a2cfdea2 Iustin Pop
    device.
1138 a2cfdea2 Iustin Pop

1139 a2cfdea2 Iustin Pop
    """
1140 b00b95dd Iustin Pop
    if self._children:
1141 b00b95dd Iustin Pop
      backend, meta = self._children
1142 b00b95dd Iustin Pop
    else:
1143 b00b95dd Iustin Pop
      backend = meta = None
1144 b00b95dd Iustin Pop
1145 a2cfdea2 Iustin Pop
    if backend is not None:
1146 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1147 a2cfdea2 Iustin Pop
    else:
1148 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1149 b00b95dd Iustin Pop
1150 a2cfdea2 Iustin Pop
    if meta is not None:
1151 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1152 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1153 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1154 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1155 a2cfdea2 Iustin Pop
    else:
1156 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1157 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1158 a2cfdea2 Iustin Pop
    return retval
1159 a2cfdea2 Iustin Pop
1160 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1161 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1162 a2cfdea2 Iustin Pop

1163 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1164 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1165 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1166 a2cfdea2 Iustin Pop
    device.
1167 a2cfdea2 Iustin Pop

1168 a2cfdea2 Iustin Pop
    """
1169 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1170 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1171 a2cfdea2 Iustin Pop
      return True
1172 a2cfdea2 Iustin Pop
1173 a2cfdea2 Iustin Pop
    if self._lhost is None:
1174 a2cfdea2 Iustin Pop
      return False
1175 a2cfdea2 Iustin Pop
1176 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1177 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1178 a2cfdea2 Iustin Pop
      return False
1179 a2cfdea2 Iustin Pop
1180 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1181 a2cfdea2 Iustin Pop
    retval = (retval and
1182 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1183 a2cfdea2 Iustin Pop
    return retval
1184 a2cfdea2 Iustin Pop
1185 a2cfdea2 Iustin Pop
  @classmethod
1186 f069addf Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta, size):
1187 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1188 a2cfdea2 Iustin Pop

1189 a2cfdea2 Iustin Pop
    """
1190 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1191 f069addf Iustin Pop
            backend, meta, "0",
1192 f069addf Iustin Pop
            "-e", "detach",
1193 f069addf Iustin Pop
            "--create-device"]
1194 60bca04a Iustin Pop
    if size:
1195 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1196 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1197 a2cfdea2 Iustin Pop
    if result.failed:
1198 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1199 a2cfdea2 Iustin Pop
1200 a2cfdea2 Iustin Pop
  @classmethod
1201 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1202 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1203 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1204 a2cfdea2 Iustin Pop

1205 a2cfdea2 Iustin Pop
    """
1206 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1207 52857176 Iustin Pop
    if None in net_info:
1208 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1209 52857176 Iustin Pop
      # sure its shutdown
1210 1063abd1 Iustin Pop
      cls._ShutdownNet(minor)
1211 1063abd1 Iustin Pop
      return
1212 52857176 Iustin Pop
1213 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1214 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1215 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1216 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1217 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1218 7d585316 Iustin Pop
    # about its peer.
1219 7d585316 Iustin Pop
    cls._SetMinorSyncSpeed(minor, constants.SYNC_SPEED)
1220 7d585316 Iustin Pop
1221 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1222 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1223 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1224 f38478b2 Iustin Pop
            "-B", "consensus",
1225 ab6cc81c Iustin Pop
            "--create-device",
1226 f38478b2 Iustin Pop
            ]
1227 a2cfdea2 Iustin Pop
    if dual_pri:
1228 a2cfdea2 Iustin Pop
      args.append("-m")
1229 a2cfdea2 Iustin Pop
    if hmac and secret:
1230 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1231 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1232 a2cfdea2 Iustin Pop
    if result.failed:
1233 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1234 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1235 a2cfdea2 Iustin Pop
1236 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1237 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1238 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1239 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1240 def8e2f6 Michael Hanselmann
1241 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1242 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1243 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1244 def8e2f6 Michael Hanselmann
1245 def8e2f6 Michael Hanselmann
    try:
1246 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1247 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1248 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1249 a2cfdea2 Iustin Pop
1250 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1251 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1252 b00b95dd Iustin Pop

1253 b00b95dd Iustin Pop
    """
1254 b00b95dd Iustin Pop
    if self.minor is None:
1255 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1256 82463074 Iustin Pop
                  self._aminor)
1257 b00b95dd Iustin Pop
    if len(devices) != 2:
1258 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1259 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1260 03ece5f3 Iustin Pop
    if "local_dev" in info:
1261 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1262 b00b95dd Iustin Pop
    backend, meta = devices
1263 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1264 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1265 b00b95dd Iustin Pop
    backend.Open()
1266 b00b95dd Iustin Pop
    meta.Open()
1267 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1268 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1269 b00b95dd Iustin Pop
1270 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1271 b00b95dd Iustin Pop
    self._children = devices
1272 b00b95dd Iustin Pop
1273 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1274 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1275 b00b95dd Iustin Pop

1276 b00b95dd Iustin Pop
    """
1277 b00b95dd Iustin Pop
    if self.minor is None:
1278 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1279 82463074 Iustin Pop
                  self._aminor)
1280 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1281 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1282 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1283 03ece5f3 Iustin Pop
      return
1284 b00b95dd Iustin Pop
    if len(self._children) != 2:
1285 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1286 82463074 Iustin Pop
                  self._children)
1287 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1288 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1289 e739bd57 Iustin Pop
      return
1290 b00b95dd Iustin Pop
    if len(devices) != 2:
1291 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1292 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1293 e739bd57 Iustin Pop
      if dev != child.dev_path:
1294 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1295 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1296 b00b95dd Iustin Pop
1297 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1298 b00b95dd Iustin Pop
    self._children = []
1299 b00b95dd Iustin Pop
1300 7d585316 Iustin Pop
  @classmethod
1301 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1302 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1303 a2cfdea2 Iustin Pop

1304 7d585316 Iustin Pop
    This is the low-level implementation.
1305 7d585316 Iustin Pop

1306 7d585316 Iustin Pop
    @type minor: int
1307 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1308 7d585316 Iustin Pop
    @type kbytes: int
1309 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1310 7d585316 Iustin Pop
    @rtype: boolean
1311 7d585316 Iustin Pop
    @return: the success of the operation
1312 7d585316 Iustin Pop

1313 a2cfdea2 Iustin Pop
    """
1314 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1315 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1316 a2cfdea2 Iustin Pop
    if result.failed:
1317 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1318 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1319 7d585316 Iustin Pop
    return not result.failed
1320 7d585316 Iustin Pop
1321 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1322 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1323 7d585316 Iustin Pop

1324 7d585316 Iustin Pop
    @type kbytes: int
1325 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1326 7d585316 Iustin Pop
    @rtype: boolean
1327 7d585316 Iustin Pop
    @return: the success of the operation
1328 7d585316 Iustin Pop

1329 7d585316 Iustin Pop
    """
1330 7d585316 Iustin Pop
    if self.minor is None:
1331 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1332 7d585316 Iustin Pop
      return False
1333 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1334 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1335 a2cfdea2 Iustin Pop
1336 6b90c22e Iustin Pop
  def GetProcStatus(self):
1337 6b90c22e Iustin Pop
    """Return device data from /proc.
1338 6b90c22e Iustin Pop

1339 6b90c22e Iustin Pop
    """
1340 6b90c22e Iustin Pop
    if self.minor is None:
1341 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1342 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1343 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1344 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1345 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1346 6b90c22e Iustin Pop
1347 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1348 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1349 a2cfdea2 Iustin Pop

1350 a2cfdea2 Iustin Pop

1351 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1352 5bbd3f7f Michael Hanselmann
    If estimated_time is None, it means we can't estimate
1353 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1354 0834c866 Iustin Pop

1355 0834c866 Iustin Pop

1356 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1357 0834c866 Iustin Pop
    network not connected or local disk missing.
1358 0834c866 Iustin Pop

1359 5bbd3f7f Michael Hanselmann
    We compute the ldisk parameter based on whether we have a local
1360 0834c866 Iustin Pop
    disk or not.
1361 a2cfdea2 Iustin Pop

1362 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1363 c41eea6e Iustin Pop

1364 a2cfdea2 Iustin Pop
    """
1365 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1366 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1367 96acbc09 Michael Hanselmann
1368 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1369 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1370 f208978a Michael Hanselmann
1371 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1372 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1373 f208978a Michael Hanselmann
    elif stats.is_diskless:
1374 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1375 f208978a Michael Hanselmann
    else:
1376 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1377 96acbc09 Michael Hanselmann
1378 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1379 96acbc09 Michael Hanselmann
                                  major=self.major,
1380 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1381 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1382 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1383 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1384 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1385 a2cfdea2 Iustin Pop
1386 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1387 a2cfdea2 Iustin Pop
    """Make the local state primary.
1388 a2cfdea2 Iustin Pop

1389 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1390 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1391 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1392 f860ff4e Guido Trotter
    is mandatory.
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.error("DRBD cannot attach to a device during open")
1397 a2cfdea2 Iustin Pop
      return False
1398 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1399 a2cfdea2 Iustin Pop
    if force:
1400 a2cfdea2 Iustin Pop
      cmd.append("-o")
1401 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1402 a2cfdea2 Iustin Pop
    if result.failed:
1403 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1404 82463074 Iustin Pop
                  result.output)
1405 a2cfdea2 Iustin Pop
1406 a2cfdea2 Iustin Pop
  def Close(self):
1407 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1408 a2cfdea2 Iustin Pop

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

1411 a2cfdea2 Iustin Pop
    """
1412 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1413 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1414 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1415 a2cfdea2 Iustin Pop
    if result.failed:
1416 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1417 82463074 Iustin Pop
                  self.minor, result.output)
1418 a2cfdea2 Iustin Pop
1419 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1420 cf8df3f3 Iustin Pop
    """Removes network configuration.
1421 cf8df3f3 Iustin Pop

1422 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1423 cf8df3f3 Iustin Pop

1424 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1425 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1426 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1427 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1428 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1429 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1430 cf8df3f3 Iustin Pop
    exception.
1431 cf8df3f3 Iustin Pop

1432 cf8df3f3 Iustin Pop
    """
1433 cf8df3f3 Iustin Pop
    if self.minor is None:
1434 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1435 cf8df3f3 Iustin Pop
1436 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1437 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1438 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1439 cf8df3f3 Iustin Pop
1440 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1441 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1442 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1443 cf8df3f3 Iustin Pop
1444 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1445 def8e2f6 Michael Hanselmann
1446 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1447 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1448 def8e2f6 Michael Hanselmann
        return
1449 def8e2f6 Michael Hanselmann
1450 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1451 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1452 def8e2f6 Michael Hanselmann
      # forgotten
1453 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1454 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1455 def8e2f6 Michael Hanselmann
1456 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1457 def8e2f6 Michael Hanselmann
1458 def8e2f6 Michael Hanselmann
    # Keep start time
1459 def8e2f6 Michael Hanselmann
    start_time = time.time()
1460 def8e2f6 Michael Hanselmann
1461 def8e2f6 Michael Hanselmann
    try:
1462 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1463 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1464 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1465 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1466 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1467 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1468 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1469 cf8df3f3 Iustin Pop
      else:
1470 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1471 def8e2f6 Michael Hanselmann
1472 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1473 cf8df3f3 Iustin Pop
1474 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1475 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1476 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1477 82463074 Iustin Pop
                   self.minor, reconfig_time)
1478 cf8df3f3 Iustin Pop
1479 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1480 cf8df3f3 Iustin Pop
    """Reconnects the network.
1481 cf8df3f3 Iustin Pop

1482 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1483 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1484 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1485 cf8df3f3 Iustin Pop

1486 cf8df3f3 Iustin Pop
    Args:
1487 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1488 cf8df3f3 Iustin Pop

1489 cf8df3f3 Iustin Pop
    """
1490 cf8df3f3 Iustin Pop
    if self.minor is None:
1491 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1492 cf8df3f3 Iustin Pop
1493 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1494 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1495 cf8df3f3 Iustin Pop
1496 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1497 cf8df3f3 Iustin Pop
1498 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1499 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1500 cf8df3f3 Iustin Pop
1501 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1502 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1503 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1504 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1505 cf8df3f3 Iustin Pop
1506 a2cfdea2 Iustin Pop
  def Attach(self):
1507 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1508 2d0c8319 Iustin Pop

1509 2d0c8319 Iustin Pop
    This doesn't do any device configurations - it only checks if the
1510 2d0c8319 Iustin Pop
    minor is in a state different from Unconfigured.
1511 2d0c8319 Iustin Pop

1512 2d0c8319 Iustin Pop
    Note that this function will not change the state of the system in
1513 2d0c8319 Iustin Pop
    any way (except in case of side-effects caused by reading from
1514 2d0c8319 Iustin Pop
    /proc).
1515 2d0c8319 Iustin Pop

1516 2d0c8319 Iustin Pop
    """
1517 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1518 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1519 2d0c8319 Iustin Pop
      minor = self._aminor
1520 2d0c8319 Iustin Pop
    else:
1521 2d0c8319 Iustin Pop
      minor = None
1522 2d0c8319 Iustin Pop
1523 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1524 2d0c8319 Iustin Pop
    return minor is not None
1525 2d0c8319 Iustin Pop
1526 2d0c8319 Iustin Pop
  def Assemble(self):
1527 2d0c8319 Iustin Pop
    """Assemble the drbd.
1528 2d0c8319 Iustin Pop

1529 2d0c8319 Iustin Pop
    Method:
1530 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1531 2d0c8319 Iustin Pop
        our config
1532 2d0c8319 Iustin Pop
      - if not, we create it from zero
1533 2d0c8319 Iustin Pop

1534 2d0c8319 Iustin Pop
    """
1535 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1536 2d0c8319 Iustin Pop
1537 2d0c8319 Iustin Pop
    self.Attach()
1538 2d0c8319 Iustin Pop
    if self.minor is None:
1539 2d0c8319 Iustin Pop
      # local device completely unconfigured
1540 1063abd1 Iustin Pop
      self._FastAssemble()
1541 2d0c8319 Iustin Pop
    else:
1542 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1543 2d0c8319 Iustin Pop
      # the device
1544 1063abd1 Iustin Pop
      self._SlowAssemble()
1545 2d0c8319 Iustin Pop
1546 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1547 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1548 a2cfdea2 Iustin Pop

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

1553 a2cfdea2 Iustin Pop
    """
1554 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1555 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1556 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1557 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1558 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1559 1063abd1 Iustin Pop
1560 a2cfdea2 Iustin Pop
      if match_l and match_r:
1561 1063abd1 Iustin Pop
        # everything matches
1562 a2cfdea2 Iustin Pop
        break
1563 1063abd1 Iustin Pop
1564 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1565 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1566 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1567 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1568 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1569 1063abd1 Iustin Pop
          break
1570 1063abd1 Iustin Pop
        else:
1571 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1572 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1573 1063abd1 Iustin Pop
1574 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1575 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1576 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1577 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1578 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1579 1063abd1 Iustin Pop
          break
1580 1063abd1 Iustin Pop
        else:
1581 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1582 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1583 bf25af3b Iustin Pop
1584 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1585 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1586 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1587 bf25af3b Iustin Pop
      # configuration
1588 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1589 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1590 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1591 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1592 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1593 9cdbe77f Iustin Pop
        # reconnect to our correct one
1594 1063abd1 Iustin Pop
        try:
1595 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1596 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1597 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1598 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1599 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1600 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1601 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1602 9cdbe77f Iustin Pop
        # None)
1603 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1604 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1605 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1606 9cdbe77f Iustin Pop
          break
1607 1063abd1 Iustin Pop
        else:
1608 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1609 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1610 9cdbe77f Iustin Pop
1611 a2cfdea2 Iustin Pop
    else:
1612 a2cfdea2 Iustin Pop
      minor = None
1613 a2cfdea2 Iustin Pop
1614 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1615 1063abd1 Iustin Pop
    if minor is None:
1616 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1617 1063abd1 Iustin Pop
                  self._aminor)
1618 a2cfdea2 Iustin Pop
1619 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1620 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1621 a2cfdea2 Iustin Pop

1622 2d0c8319 Iustin Pop
    This is run when in Assemble we detect our minor is unused.
1623 a2cfdea2 Iustin Pop

1624 a2cfdea2 Iustin Pop
    """
1625 a1578d63 Iustin Pop
    minor = self._aminor
1626 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1627 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1628 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1629 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1630 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1631 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1632 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1633 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1634 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1635 a2cfdea2 Iustin Pop
1636 a2cfdea2 Iustin Pop
  @classmethod
1637 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1638 b00b95dd Iustin Pop
    """Detach from the local device.
1639 b00b95dd Iustin Pop

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

1643 b00b95dd Iustin Pop
    """
1644 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1645 b00b95dd Iustin Pop
    if result.failed:
1646 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1647 b00b95dd Iustin Pop
1648 b00b95dd Iustin Pop
  @classmethod
1649 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1650 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1651 f3e513ad Iustin Pop

1652 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1653 f3e513ad Iustin Pop

1654 f3e513ad Iustin Pop
    """
1655 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1656 a8459f1c Iustin Pop
    if result.failed:
1657 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1658 f3e513ad Iustin Pop
1659 f3e513ad Iustin Pop
  @classmethod
1660 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1661 a2cfdea2 Iustin Pop
    """Deactivate the device.
1662 a2cfdea2 Iustin Pop

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

1665 a2cfdea2 Iustin Pop
    """
1666 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1667 a2cfdea2 Iustin Pop
    if result.failed:
1668 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1669 33bc6f01 Iustin Pop
                  minor, result.output)
1670 a2cfdea2 Iustin Pop
1671 a2cfdea2 Iustin Pop
  def Shutdown(self):
1672 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1673 a2cfdea2 Iustin Pop

1674 a2cfdea2 Iustin Pop
    """
1675 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1676 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1677 746f7476 Iustin Pop
      return
1678 746f7476 Iustin Pop
    minor = self.minor
1679 a2cfdea2 Iustin Pop
    self.minor = None
1680 a2cfdea2 Iustin Pop
    self.dev_path = None
1681 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1682 a2cfdea2 Iustin Pop
1683 a2cfdea2 Iustin Pop
  def Remove(self):
1684 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1685 a2cfdea2 Iustin Pop

1686 a2cfdea2 Iustin Pop
    """
1687 0c6c04ec Iustin Pop
    self.Shutdown()
1688 a2cfdea2 Iustin Pop
1689 a2cfdea2 Iustin Pop
  @classmethod
1690 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1691 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1692 a2cfdea2 Iustin Pop

1693 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1694 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1695 a2cfdea2 Iustin Pop

1696 a2cfdea2 Iustin Pop
    """
1697 a2cfdea2 Iustin Pop
    if len(children) != 2:
1698 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1699 767d52d3 Iustin Pop
    # check that the minor is unused
1700 767d52d3 Iustin Pop
    aminor = unique_id[4]
1701 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1702 767d52d3 Iustin Pop
    if aminor in proc_info:
1703 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1704 767d52d3 Iustin Pop
      in_use = status.is_in_use
1705 767d52d3 Iustin Pop
    else:
1706 767d52d3 Iustin Pop
      in_use = False
1707 767d52d3 Iustin Pop
    if in_use:
1708 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1709 a2cfdea2 Iustin Pop
    meta = children[1]
1710 a2cfdea2 Iustin Pop
    meta.Assemble()
1711 a2cfdea2 Iustin Pop
    if not meta.Attach():
1712 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1713 33bc6f01 Iustin Pop
                  aminor, meta)
1714 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1715 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1716 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1717 a2cfdea2 Iustin Pop
1718 1005d816 Iustin Pop
  def Grow(self, amount):
1719 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1720 1005d816 Iustin Pop

1721 1005d816 Iustin Pop
    """
1722 1005d816 Iustin Pop
    if self.minor is None:
1723 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1724 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1725 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1726 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1727 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1728 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1729 1005d816 Iustin Pop
    if result.failed:
1730 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1731 1005d816 Iustin Pop
1732 a8083063 Iustin Pop
1733 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1734 6f695a2e Manuel Franceschini
  """File device.
1735 abdf0113 Iustin Pop

1736 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1737 6f695a2e Manuel Franceschini

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

1740 6f695a2e Manuel Franceschini
  """
1741 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1742 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1743 6f695a2e Manuel Franceschini

1744 6f695a2e Manuel Franceschini
    """
1745 6f695a2e Manuel Franceschini
    if children:
1746 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1747 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1748 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1749 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1750 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1751 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1752 ecb091e3 Iustin Pop
    self.Attach()
1753 6f695a2e Manuel Franceschini
1754 6f695a2e Manuel Franceschini
  def Assemble(self):
1755 6f695a2e Manuel Franceschini
    """Assemble the device.
1756 6f695a2e Manuel Franceschini

1757 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1758 6f695a2e Manuel Franceschini

1759 6f695a2e Manuel Franceschini
    """
1760 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1761 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1762 6f695a2e Manuel Franceschini
1763 6f695a2e Manuel Franceschini
  def Shutdown(self):
1764 6f695a2e Manuel Franceschini
    """Shutdown the device.
1765 6f695a2e Manuel Franceschini

1766 5bbd3f7f Michael Hanselmann
    This is a no-op for the file type, as we don't deactivate
1767 6f695a2e Manuel Franceschini
    the file on shutdown.
1768 6f695a2e Manuel Franceschini

1769 6f695a2e Manuel Franceschini
    """
1770 746f7476 Iustin Pop
    pass
1771 6f695a2e Manuel Franceschini
1772 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1773 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1774 6f695a2e Manuel Franceschini

1775 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1776 6f695a2e Manuel Franceschini

1777 6f695a2e Manuel Franceschini
    """
1778 6f695a2e Manuel Franceschini
    pass
1779 6f695a2e Manuel Franceschini
1780 6f695a2e Manuel Franceschini
  def Close(self):
1781 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1782 6f695a2e Manuel Franceschini

1783 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1784 6f695a2e Manuel Franceschini

1785 6f695a2e Manuel Franceschini
    """
1786 6f695a2e Manuel Franceschini
    pass
1787 6f695a2e Manuel Franceschini
1788 6f695a2e Manuel Franceschini
  def Remove(self):
1789 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1790 6f695a2e Manuel Franceschini

1791 c41eea6e Iustin Pop
    @rtype: boolean
1792 c41eea6e Iustin Pop
    @return: True if the removal was successful
1793 6f695a2e Manuel Franceschini

1794 6f695a2e Manuel Franceschini
    """
1795 6f695a2e Manuel Franceschini
    try:
1796 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1797 6f695a2e Manuel Franceschini
    except OSError, err:
1798 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1799 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1800 6f695a2e Manuel Franceschini
1801 6f695a2e Manuel Franceschini
  def Attach(self):
1802 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1803 6f695a2e Manuel Franceschini

1804 6f695a2e Manuel Franceschini
    Check if this file already exists.
1805 6f695a2e Manuel Franceschini

1806 c41eea6e Iustin Pop
    @rtype: boolean
1807 c41eea6e Iustin Pop
    @return: True if file exists
1808 6f695a2e Manuel Franceschini

1809 6f695a2e Manuel Franceschini
    """
1810 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1811 ecb091e3 Iustin Pop
    return self.attached
1812 6f695a2e Manuel Franceschini
1813 fcff3897 Iustin Pop
  def GetActualSize(self):
1814 fcff3897 Iustin Pop
    """Return the actual disk size.
1815 fcff3897 Iustin Pop

1816 fcff3897 Iustin Pop
    @note: the device needs to be active when this is called
1817 fcff3897 Iustin Pop

1818 fcff3897 Iustin Pop
    """
1819 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
1820 fcff3897 Iustin Pop
    try:
1821 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
1822 fcff3897 Iustin Pop
      return st.st_size
1823 fcff3897 Iustin Pop
    except OSError, err:
1824 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
1825 fcff3897 Iustin Pop
1826 6f695a2e Manuel Franceschini
  @classmethod
1827 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1828 6f695a2e Manuel Franceschini
    """Create a new file.
1829 6f695a2e Manuel Franceschini

1830 c41eea6e Iustin Pop
    @param size: the size of file in MiB
1831 6f695a2e Manuel Franceschini

1832 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
1833 c41eea6e Iustin Pop
    @return: an instance of FileStorage
1834 6f695a2e Manuel Franceschini

1835 6f695a2e Manuel Franceschini
    """
1836 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1837 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1838 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1839 aed77cea Guido Trotter
    if os.path.exists(dev_path):
1840 aed77cea Guido Trotter
      _ThrowError("File already existing: %s", dev_path)
1841 6f695a2e Manuel Franceschini
    try:
1842 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
1843 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1844 6f695a2e Manuel Franceschini
      f.close()
1845 6c626518 Iustin Pop
    except IOError, err:
1846 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
1847 6f695a2e Manuel Franceschini
1848 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
1849 6f695a2e Manuel Franceschini
1850 6f695a2e Manuel Franceschini
1851 a8083063 Iustin Pop
DEV_MAP = {
1852 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1853 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1854 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
1855 a8083063 Iustin Pop
  }
1856 a8083063 Iustin Pop
1857 a8083063 Iustin Pop
1858 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
1859 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1860 a8083063 Iustin Pop

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

1864 a8083063 Iustin Pop
  """
1865 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1866 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1867 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1868 cb999543 Iustin Pop
  if not device.attached:
1869 a8083063 Iustin Pop
    return None
1870 ecb091e3 Iustin Pop
  return device
1871 a8083063 Iustin Pop
1872 a8083063 Iustin Pop
1873 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
1874 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1875 a8083063 Iustin Pop

1876 f96e3c4f Iustin Pop
  This will attach to assemble the device, as needed, to bring it
1877 f96e3c4f Iustin Pop
  fully up. It must be safe to run on already-assembled devices.
1878 a8083063 Iustin Pop

1879 a8083063 Iustin Pop
  """
1880 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1881 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1882 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1883 1063abd1 Iustin Pop
  device.Assemble()
1884 a8083063 Iustin Pop
  return device
1885 a8083063 Iustin Pop
1886 a8083063 Iustin Pop
1887 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1888 a8083063 Iustin Pop
  """Create a device.
1889 a8083063 Iustin Pop

1890 a8083063 Iustin Pop
  """
1891 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1892 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1893 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1894 a8083063 Iustin Pop
  return device