Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ d4c1bd12

History | View | Annotate | Download (59.8 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Block device abstraction"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import re
25 a8083063 Iustin Pop
import time
26 a8083063 Iustin Pop
import errno
27 a2cfdea2 Iustin Pop
import pyparsing as pyp
28 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 82463074 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 a8083063 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 5b7b5d49 Guido Trotter
    free_size = sum([ pv[0] for pv in pvs_info ])
366 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
367 fecbe9d5 Iustin Pop
    stripes = min(current_pvs, constants.LVM_STRIPECOUNT)
368 5b7b5d49 Guido Trotter
369 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
370 5b7b5d49 Guido Trotter
    # calling the create function.
371 a8083063 Iustin Pop
    if free_size < size:
372 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
373 82463074 Iustin Pop
                  " available %s", size, free_size)
374 fecbe9d5 Iustin Pop
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
375 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
376 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
377 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
378 fecbe9d5 Iustin Pop
    # stripes
379 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
380 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
381 fecbe9d5 Iustin Pop
      if not result.failed:
382 fecbe9d5 Iustin Pop
        break
383 a8083063 Iustin Pop
    if result.failed:
384 82463074 Iustin Pop
      _ThrowError("LV create failed (%s): %s",
385 82463074 Iustin Pop
                  result.fail_reason, result.output)
386 464f8daf Iustin Pop
    return LogicalVolume(unique_id, children, size)
387 a8083063 Iustin Pop
388 a8083063 Iustin Pop
  @staticmethod
389 a8083063 Iustin Pop
  def GetPVInfo(vg_name):
390 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
391 a8083063 Iustin Pop

392 c41eea6e Iustin Pop
    @param vg_name: the volume group name
393 a8083063 Iustin Pop

394 c41eea6e Iustin Pop
    @rtype: list
395 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
396 098c0958 Michael Hanselmann

397 a8083063 Iustin Pop
    """
398 a8083063 Iustin Pop
    command = ["pvs", "--noheadings", "--nosuffix", "--units=m",
399 a8083063 Iustin Pop
               "-opv_name,vg_name,pv_free,pv_attr", "--unbuffered",
400 a8083063 Iustin Pop
               "--separator=:"]
401 a8083063 Iustin Pop
    result = utils.RunCmd(command)
402 a8083063 Iustin Pop
    if result.failed:
403 468c5f77 Iustin Pop
      logging.error("Can't get the PV information: %s - %s",
404 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
405 a8083063 Iustin Pop
      return None
406 a8083063 Iustin Pop
    data = []
407 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
408 a8083063 Iustin Pop
      fields = line.strip().split(':')
409 a8083063 Iustin Pop
      if len(fields) != 4:
410 468c5f77 Iustin Pop
        logging.error("Can't parse pvs output: line '%s'", line)
411 a8083063 Iustin Pop
        return None
412 a8083063 Iustin Pop
      # skip over pvs from another vg or ones which are not allocatable
413 a8083063 Iustin Pop
      if fields[1] != vg_name or fields[3][0] != 'a':
414 a8083063 Iustin Pop
        continue
415 a8083063 Iustin Pop
      data.append((float(fields[2]), fields[0]))
416 a8083063 Iustin Pop
417 a8083063 Iustin Pop
    return data
418 a8083063 Iustin Pop
419 a8083063 Iustin Pop
  def Remove(self):
420 a8083063 Iustin Pop
    """Remove this logical volume.
421 a8083063 Iustin Pop

422 a8083063 Iustin Pop
    """
423 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
424 a8083063 Iustin Pop
      # the LV does not exist
425 0c6c04ec Iustin Pop
      return
426 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
427 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
428 a8083063 Iustin Pop
    if result.failed:
429 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
430 a8083063 Iustin Pop
431 f3e513ad Iustin Pop
  def Rename(self, new_id):
432 f3e513ad Iustin Pop
    """Rename this logical volume.
433 f3e513ad Iustin Pop

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

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

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

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

520 a8083063 Iustin Pop
    """
521 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
522 5574047a Iustin Pop
    if result.failed:
523 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
524 a8083063 Iustin Pop
525 a8083063 Iustin Pop
  def Shutdown(self):
526 a8083063 Iustin Pop
    """Shutdown the device.
527 a8083063 Iustin Pop

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

531 a8083063 Iustin Pop
    """
532 746f7476 Iustin Pop
    pass
533 a8083063 Iustin Pop
534 9db6dbce Iustin Pop
  def GetSyncStatus(self):
535 9db6dbce Iustin Pop
    """Returns the sync status of the device.
536 9db6dbce Iustin Pop

537 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
538 9db6dbce Iustin Pop
    status of the mirror.
539 9db6dbce Iustin Pop

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

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

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

553 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
554 c41eea6e Iustin Pop

555 9db6dbce Iustin Pop
    """
556 f208978a Michael Hanselmann
    if self._degraded:
557 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
558 f208978a Michael Hanselmann
    else:
559 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
560 f208978a Michael Hanselmann
561 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
562 96acbc09 Michael Hanselmann
                                  major=self.major,
563 96acbc09 Michael Hanselmann
                                  minor=self.minor,
564 96acbc09 Michael Hanselmann
                                  sync_percent=None,
565 96acbc09 Michael Hanselmann
                                  estimated_time=None,
566 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
567 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
568 9db6dbce Iustin Pop
569 a8083063 Iustin Pop
  def Open(self, force=False):
570 a8083063 Iustin Pop
    """Make the device ready for I/O.
571 a8083063 Iustin Pop

572 a8083063 Iustin Pop
    This is a no-op for the LV device type.
573 a8083063 Iustin Pop

574 a8083063 Iustin Pop
    """
575 fdbd668d Iustin Pop
    pass
576 a8083063 Iustin Pop
577 a8083063 Iustin Pop
  def Close(self):
578 a8083063 Iustin Pop
    """Notifies that the device will no longer be used 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 Snapshot(self, size):
586 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
587 a8083063 Iustin Pop

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

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

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

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

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

763 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
764 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
765 0f7f32d9 Iustin Pop

766 abdf0113 Iustin Pop
  """
767 abdf0113 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
768 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
769 a8083063 Iustin Pop
770 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
771 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
772 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
773 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
774 a8083063 Iustin Pop
775 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
776 6b90c22e Iustin Pop
777 abdf0113 Iustin Pop
  @staticmethod
778 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
779 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
780 a8083063 Iustin Pop

781 a8083063 Iustin Pop
    """
782 abdf0113 Iustin Pop
    try:
783 f6eaed12 Iustin Pop
      stat = open(filename, "r")
784 f6eaed12 Iustin Pop
      try:
785 f6eaed12 Iustin Pop
        data = stat.read().splitlines()
786 f6eaed12 Iustin Pop
      finally:
787 f6eaed12 Iustin Pop
        stat.close()
788 f6eaed12 Iustin Pop
    except EnvironmentError, err:
789 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
790 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
791 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
792 f6eaed12 Iustin Pop
      else:
793 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
794 abdf0113 Iustin Pop
    if not data:
795 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
796 abdf0113 Iustin Pop
    return data
797 a8083063 Iustin Pop
798 abdf0113 Iustin Pop
  @staticmethod
799 abdf0113 Iustin Pop
  def _MassageProcData(data):
800 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
801 a8083063 Iustin Pop

802 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
803 c41eea6e Iustin Pop
        for that minor
804 a8083063 Iustin Pop

805 a8083063 Iustin Pop
    """
806 abdf0113 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
807 abdf0113 Iustin Pop
    results = {}
808 abdf0113 Iustin Pop
    old_minor = old_line = None
809 abdf0113 Iustin Pop
    for line in data:
810 abdf0113 Iustin Pop
      lresult = lmatch.match(line)
811 abdf0113 Iustin Pop
      if lresult is not None:
812 abdf0113 Iustin Pop
        if old_minor is not None:
813 abdf0113 Iustin Pop
          results[old_minor] = old_line
814 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
815 abdf0113 Iustin Pop
        old_line = line
816 abdf0113 Iustin Pop
      else:
817 abdf0113 Iustin Pop
        if old_minor is not None:
818 abdf0113 Iustin Pop
          old_line += " " + line.strip()
819 abdf0113 Iustin Pop
    # add last line
820 abdf0113 Iustin Pop
    if old_minor is not None:
821 abdf0113 Iustin Pop
      results[old_minor] = old_line
822 abdf0113 Iustin Pop
    return results
823 a8083063 Iustin Pop
824 abdf0113 Iustin Pop
  @classmethod
825 abdf0113 Iustin Pop
  def _GetVersion(cls):
826 abdf0113 Iustin Pop
    """Return the DRBD version.
827 a8083063 Iustin Pop

828 abdf0113 Iustin Pop
    This will return a dict with keys:
829 c41eea6e Iustin Pop
      - k_major
830 c41eea6e Iustin Pop
      - k_minor
831 c41eea6e Iustin Pop
      - k_point
832 c41eea6e Iustin Pop
      - api
833 c41eea6e Iustin Pop
      - proto
834 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
835 a8083063 Iustin Pop

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

860 a8083063 Iustin Pop
    """
861 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
862 a8083063 Iustin Pop
863 abdf0113 Iustin Pop
  @classmethod
864 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
865 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
866 a8083063 Iustin Pop

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

887 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
888 a8083063 Iustin Pop

889 a8083063 Iustin Pop
    """
890 abdf0113 Iustin Pop
    if minor is None:
891 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
892 cb999543 Iustin Pop
      self.attached = False
893 a8083063 Iustin Pop
    else:
894 abdf0113 Iustin Pop
      self.minor = minor
895 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
896 cb999543 Iustin Pop
      self.attached = True
897 a8083063 Iustin Pop
898 a8083063 Iustin Pop
  @staticmethod
899 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
900 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
901 a8083063 Iustin Pop

902 abdf0113 Iustin Pop
    This currently only check the size, which must be around
903 abdf0113 Iustin Pop
    128MiB.
904 a8083063 Iustin Pop

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

929 abdf0113 Iustin Pop
    This is not supported for drbd devices.
930 a8083063 Iustin Pop

931 a8083063 Iustin Pop
    """
932 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
933 a8083063 Iustin Pop
934 f3e513ad Iustin Pop
935 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
936 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
937 a2cfdea2 Iustin Pop

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

942 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
943 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
944 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
945 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
946 a2cfdea2 Iustin Pop

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

982 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
983 a2cfdea2 Iustin Pop

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

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

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

1022 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1023 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1024 a2cfdea2 Iustin Pop

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

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

1089 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1090 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1091 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1092 3840729d Iustin Pop
    by _GetShowData.
1093 3840729d Iustin Pop

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

1128 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1129 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1130 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1131 a2cfdea2 Iustin Pop
    device.
1132 a2cfdea2 Iustin Pop

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

1157 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1158 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1159 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1160 a2cfdea2 Iustin Pop
    device.
1161 a2cfdea2 Iustin Pop

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

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

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

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

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

1300 7d585316 Iustin Pop
    This is the low-level implementation.
1301 7d585316 Iustin Pop

1302 7d585316 Iustin Pop
    @type minor: int
1303 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1304 7d585316 Iustin Pop
    @type kbytes: int
1305 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1306 7d585316 Iustin Pop
    @rtype: boolean
1307 7d585316 Iustin Pop
    @return: the success of the operation
1308 7d585316 Iustin Pop

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

1320 7d585316 Iustin Pop
    @type kbytes: int
1321 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1322 7d585316 Iustin Pop
    @rtype: boolean
1323 7d585316 Iustin Pop
    @return: the success of the operation
1324 7d585316 Iustin Pop

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

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

1346 a2cfdea2 Iustin Pop

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

1351 0834c866 Iustin Pop

1352 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1353 0834c866 Iustin Pop
    network not connected or local disk missing.
1354 0834c866 Iustin Pop

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

1358 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1359 c41eea6e Iustin Pop

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

1385 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1386 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1387 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1388 f860ff4e Guido Trotter
    is mandatory.
1389 a2cfdea2 Iustin Pop

1390 a2cfdea2 Iustin Pop
    """
1391 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1392 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1393 a2cfdea2 Iustin Pop
      return False
1394 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1395 a2cfdea2 Iustin Pop
    if force:
1396 a2cfdea2 Iustin Pop
      cmd.append("-o")
1397 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1398 a2cfdea2 Iustin Pop
    if result.failed:
1399 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1400 82463074 Iustin Pop
                  result.output)
1401 a2cfdea2 Iustin Pop
1402 a2cfdea2 Iustin Pop
  def Close(self):
1403 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1404 a2cfdea2 Iustin Pop

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

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

1418 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1419 cf8df3f3 Iustin Pop

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

1428 cf8df3f3 Iustin Pop
    """
1429 cf8df3f3 Iustin Pop
    if self.minor is None:
1430 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1431 cf8df3f3 Iustin Pop
1432 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1433 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1434 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1435 cf8df3f3 Iustin Pop
1436 1063abd1 Iustin Pop
    ever_disconnected = _IgnoreError(self._ShutdownNet, self.minor)
1437 cf8df3f3 Iustin Pop
    timeout_limit = time.time() + self._NET_RECONFIG_TIMEOUT
1438 5bbd3f7f Michael Hanselmann
    sleep_time = 0.100 # we start the retry time at 100 milliseconds
1439 cf8df3f3 Iustin Pop
    while time.time() < timeout_limit:
1440 cf8df3f3 Iustin Pop
      status = self.GetProcStatus()
1441 cf8df3f3 Iustin Pop
      if status.is_standalone:
1442 cf8df3f3 Iustin Pop
        break
1443 cf8df3f3 Iustin Pop
      # retry the disconnect, it seems possible that due to a
1444 cf8df3f3 Iustin Pop
      # well-time disconnect on the peer, my disconnect command might
1445 5bbd3f7f Michael Hanselmann
      # be ignored and forgotten
1446 1063abd1 Iustin Pop
      ever_disconnected = _IgnoreError(self._ShutdownNet, self.minor) or \
1447 1063abd1 Iustin Pop
                          ever_disconnected
1448 cf8df3f3 Iustin Pop
      time.sleep(sleep_time)
1449 cf8df3f3 Iustin Pop
      sleep_time = min(2, sleep_time * 1.5)
1450 cf8df3f3 Iustin Pop
1451 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1452 cf8df3f3 Iustin Pop
      if ever_disconnected:
1453 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1454 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1455 cf8df3f3 Iustin Pop
      else:
1456 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1457 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1458 cf8df3f3 Iustin Pop
1459 cf8df3f3 Iustin Pop
    reconfig_time = time.time() - timeout_limit + self._NET_RECONFIG_TIMEOUT
1460 cf8df3f3 Iustin Pop
    if reconfig_time > 15: # hardcoded alert limit
1461 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1462 82463074 Iustin Pop
                   self.minor, reconfig_time)
1463 cf8df3f3 Iustin Pop
1464 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1465 cf8df3f3 Iustin Pop
    """Reconnects the network.
1466 cf8df3f3 Iustin Pop

1467 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1468 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1469 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1470 cf8df3f3 Iustin Pop

1471 cf8df3f3 Iustin Pop
    Args:
1472 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1473 cf8df3f3 Iustin Pop

1474 cf8df3f3 Iustin Pop
    """
1475 cf8df3f3 Iustin Pop
    if self.minor is None:
1476 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1477 cf8df3f3 Iustin Pop
1478 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1479 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1480 cf8df3f3 Iustin Pop
1481 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1482 cf8df3f3 Iustin Pop
1483 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1484 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1485 cf8df3f3 Iustin Pop
1486 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1487 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1488 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1489 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1490 cf8df3f3 Iustin Pop
1491 a2cfdea2 Iustin Pop
  def Attach(self):
1492 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1493 2d0c8319 Iustin Pop

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

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

1501 2d0c8319 Iustin Pop
    """
1502 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1503 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1504 2d0c8319 Iustin Pop
      minor = self._aminor
1505 2d0c8319 Iustin Pop
    else:
1506 2d0c8319 Iustin Pop
      minor = None
1507 2d0c8319 Iustin Pop
1508 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1509 2d0c8319 Iustin Pop
    return minor is not None
1510 2d0c8319 Iustin Pop
1511 2d0c8319 Iustin Pop
  def Assemble(self):
1512 2d0c8319 Iustin Pop
    """Assemble the drbd.
1513 2d0c8319 Iustin Pop

1514 2d0c8319 Iustin Pop
    Method:
1515 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1516 2d0c8319 Iustin Pop
        our config
1517 2d0c8319 Iustin Pop
      - if not, we create it from zero
1518 2d0c8319 Iustin Pop

1519 2d0c8319 Iustin Pop
    """
1520 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1521 2d0c8319 Iustin Pop
1522 2d0c8319 Iustin Pop
    self.Attach()
1523 2d0c8319 Iustin Pop
    if self.minor is None:
1524 2d0c8319 Iustin Pop
      # local device completely unconfigured
1525 1063abd1 Iustin Pop
      self._FastAssemble()
1526 2d0c8319 Iustin Pop
    else:
1527 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1528 2d0c8319 Iustin Pop
      # the device
1529 1063abd1 Iustin Pop
      self._SlowAssemble()
1530 2d0c8319 Iustin Pop
1531 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1532 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1533 a2cfdea2 Iustin Pop

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

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

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

1609 a2cfdea2 Iustin Pop
    """
1610 a1578d63 Iustin Pop
    minor = self._aminor
1611 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1612 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1613 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1614 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1615 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1616 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1617 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1618 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1619 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1620 a2cfdea2 Iustin Pop
1621 a2cfdea2 Iustin Pop
  @classmethod
1622 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1623 b00b95dd Iustin Pop
    """Detach from the local device.
1624 b00b95dd Iustin Pop

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

1628 b00b95dd Iustin Pop
    """
1629 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1630 b00b95dd Iustin Pop
    if result.failed:
1631 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1632 b00b95dd Iustin Pop
1633 b00b95dd Iustin Pop
  @classmethod
1634 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1635 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1636 f3e513ad Iustin Pop

1637 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1638 f3e513ad Iustin Pop

1639 f3e513ad Iustin Pop
    """
1640 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1641 a8459f1c Iustin Pop
    if result.failed:
1642 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1643 f3e513ad Iustin Pop
1644 f3e513ad Iustin Pop
  @classmethod
1645 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1646 a2cfdea2 Iustin Pop
    """Deactivate the device.
1647 a2cfdea2 Iustin Pop

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

1650 a2cfdea2 Iustin Pop
    """
1651 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1652 a2cfdea2 Iustin Pop
    if result.failed:
1653 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1654 33bc6f01 Iustin Pop
                  minor, result.output)
1655 a2cfdea2 Iustin Pop
1656 a2cfdea2 Iustin Pop
  def Shutdown(self):
1657 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1658 a2cfdea2 Iustin Pop

1659 a2cfdea2 Iustin Pop
    """
1660 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1661 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1662 746f7476 Iustin Pop
      return
1663 746f7476 Iustin Pop
    minor = self.minor
1664 a2cfdea2 Iustin Pop
    self.minor = None
1665 a2cfdea2 Iustin Pop
    self.dev_path = None
1666 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1667 a2cfdea2 Iustin Pop
1668 a2cfdea2 Iustin Pop
  def Remove(self):
1669 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1670 a2cfdea2 Iustin Pop

1671 a2cfdea2 Iustin Pop
    """
1672 0c6c04ec Iustin Pop
    self.Shutdown()
1673 a2cfdea2 Iustin Pop
1674 a2cfdea2 Iustin Pop
  @classmethod
1675 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1676 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1677 a2cfdea2 Iustin Pop

1678 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1679 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1680 a2cfdea2 Iustin Pop

1681 a2cfdea2 Iustin Pop
    """
1682 a2cfdea2 Iustin Pop
    if len(children) != 2:
1683 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1684 767d52d3 Iustin Pop
    # check that the minor is unused
1685 767d52d3 Iustin Pop
    aminor = unique_id[4]
1686 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1687 767d52d3 Iustin Pop
    if aminor in proc_info:
1688 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1689 767d52d3 Iustin Pop
      in_use = status.is_in_use
1690 767d52d3 Iustin Pop
    else:
1691 767d52d3 Iustin Pop
      in_use = False
1692 767d52d3 Iustin Pop
    if in_use:
1693 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1694 a2cfdea2 Iustin Pop
    meta = children[1]
1695 a2cfdea2 Iustin Pop
    meta.Assemble()
1696 a2cfdea2 Iustin Pop
    if not meta.Attach():
1697 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1698 33bc6f01 Iustin Pop
                  aminor, meta)
1699 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1700 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1701 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1702 a2cfdea2 Iustin Pop
1703 1005d816 Iustin Pop
  def Grow(self, amount):
1704 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1705 1005d816 Iustin Pop

1706 1005d816 Iustin Pop
    """
1707 1005d816 Iustin Pop
    if self.minor is None:
1708 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1709 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1710 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1711 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1712 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1713 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1714 1005d816 Iustin Pop
    if result.failed:
1715 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1716 1005d816 Iustin Pop
1717 a8083063 Iustin Pop
1718 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1719 6f695a2e Manuel Franceschini
  """File device.
1720 abdf0113 Iustin Pop

1721 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1722 6f695a2e Manuel Franceschini

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

1725 6f695a2e Manuel Franceschini
  """
1726 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1727 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1728 6f695a2e Manuel Franceschini

1729 6f695a2e Manuel Franceschini
    """
1730 6f695a2e Manuel Franceschini
    if children:
1731 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1732 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1733 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1734 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1735 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1736 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1737 ecb091e3 Iustin Pop
    self.Attach()
1738 6f695a2e Manuel Franceschini
1739 6f695a2e Manuel Franceschini
  def Assemble(self):
1740 6f695a2e Manuel Franceschini
    """Assemble the device.
1741 6f695a2e Manuel Franceschini

1742 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1743 6f695a2e Manuel Franceschini

1744 6f695a2e Manuel Franceschini
    """
1745 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1746 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1747 6f695a2e Manuel Franceschini
1748 6f695a2e Manuel Franceschini
  def Shutdown(self):
1749 6f695a2e Manuel Franceschini
    """Shutdown the device.
1750 6f695a2e Manuel Franceschini

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

1754 6f695a2e Manuel Franceschini
    """
1755 746f7476 Iustin Pop
    pass
1756 6f695a2e Manuel Franceschini
1757 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1758 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1759 6f695a2e Manuel Franceschini

1760 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1761 6f695a2e Manuel Franceschini

1762 6f695a2e Manuel Franceschini
    """
1763 6f695a2e Manuel Franceschini
    pass
1764 6f695a2e Manuel Franceschini
1765 6f695a2e Manuel Franceschini
  def Close(self):
1766 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1767 6f695a2e Manuel Franceschini

1768 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1769 6f695a2e Manuel Franceschini

1770 6f695a2e Manuel Franceschini
    """
1771 6f695a2e Manuel Franceschini
    pass
1772 6f695a2e Manuel Franceschini
1773 6f695a2e Manuel Franceschini
  def Remove(self):
1774 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1775 6f695a2e Manuel Franceschini

1776 c41eea6e Iustin Pop
    @rtype: boolean
1777 c41eea6e Iustin Pop
    @return: True if the removal was successful
1778 6f695a2e Manuel Franceschini

1779 6f695a2e Manuel Franceschini
    """
1780 6f695a2e Manuel Franceschini
    try:
1781 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1782 6f695a2e Manuel Franceschini
    except OSError, err:
1783 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1784 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1785 6f695a2e Manuel Franceschini
1786 6f695a2e Manuel Franceschini
  def Attach(self):
1787 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1788 6f695a2e Manuel Franceschini

1789 6f695a2e Manuel Franceschini
    Check if this file already exists.
1790 6f695a2e Manuel Franceschini

1791 c41eea6e Iustin Pop
    @rtype: boolean
1792 c41eea6e Iustin Pop
    @return: True if file exists
1793 6f695a2e Manuel Franceschini

1794 6f695a2e Manuel Franceschini
    """
1795 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1796 ecb091e3 Iustin Pop
    return self.attached
1797 6f695a2e Manuel Franceschini
1798 fcff3897 Iustin Pop
  def GetActualSize(self):
1799 fcff3897 Iustin Pop
    """Return the actual disk size.
1800 fcff3897 Iustin Pop

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

1803 fcff3897 Iustin Pop
    """
1804 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
1805 fcff3897 Iustin Pop
    try:
1806 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
1807 fcff3897 Iustin Pop
      return st.st_size
1808 fcff3897 Iustin Pop
    except OSError, err:
1809 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
1810 fcff3897 Iustin Pop
1811 6f695a2e Manuel Franceschini
  @classmethod
1812 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1813 6f695a2e Manuel Franceschini
    """Create a new file.
1814 6f695a2e Manuel Franceschini

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

1817 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
1818 c41eea6e Iustin Pop
    @return: an instance of FileStorage
1819 6f695a2e Manuel Franceschini

1820 6f695a2e Manuel Franceschini
    """
1821 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1822 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1823 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1824 aed77cea Guido Trotter
    if os.path.exists(dev_path):
1825 aed77cea Guido Trotter
      _ThrowError("File already existing: %s", dev_path)
1826 6f695a2e Manuel Franceschini
    try:
1827 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
1828 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1829 6f695a2e Manuel Franceschini
      f.close()
1830 6c626518 Iustin Pop
    except IOError, err:
1831 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
1832 6f695a2e Manuel Franceschini
1833 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
1834 6f695a2e Manuel Franceschini
1835 6f695a2e Manuel Franceschini
1836 a8083063 Iustin Pop
DEV_MAP = {
1837 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1838 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1839 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
1840 a8083063 Iustin Pop
  }
1841 a8083063 Iustin Pop
1842 a8083063 Iustin Pop
1843 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
1844 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1845 a8083063 Iustin Pop

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

1849 a8083063 Iustin Pop
  """
1850 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1851 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1852 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1853 cb999543 Iustin Pop
  if not device.attached:
1854 a8083063 Iustin Pop
    return None
1855 ecb091e3 Iustin Pop
  return device
1856 a8083063 Iustin Pop
1857 a8083063 Iustin Pop
1858 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
1859 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1860 a8083063 Iustin Pop

1861 f96e3c4f Iustin Pop
  This will attach to assemble the device, as needed, to bring it
1862 f96e3c4f Iustin Pop
  fully up. It must be safe to run on already-assembled devices.
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 1063abd1 Iustin Pop
  device.Assemble()
1869 a8083063 Iustin Pop
  return device
1870 a8083063 Iustin Pop
1871 a8083063 Iustin Pop
1872 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1873 a8083063 Iustin Pop
  """Create a device.
1874 a8083063 Iustin Pop

1875 a8083063 Iustin Pop
  """
1876 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1877 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1878 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1879 a8083063 Iustin Pop
  return device