Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ f396ad8c

History | View | Annotate | Download (67.9 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 fcee765d Manuel Franceschini
# Copyright (C) 2006, 2007, 2010 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 cea881e5 Michael Hanselmann
from ganeti import compat
36 a744b676 Manuel Franceschini
from ganeti import netutils
37 a8083063 Iustin Pop
38 a8083063 Iustin Pop
39 310fbb64 Iustin Pop
# Size of reads in _CanReadDevice
40 310fbb64 Iustin Pop
_DEVICE_READ_SIZE = 128 * 1024
41 310fbb64 Iustin Pop
42 310fbb64 Iustin Pop
43 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
44 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
45 82463074 Iustin Pop

46 82463074 Iustin Pop
  This is used in order to simplify the execution of cleanup or
47 82463074 Iustin Pop
  rollback functions.
48 82463074 Iustin Pop

49 82463074 Iustin Pop
  @rtype: boolean
50 82463074 Iustin Pop
  @return: True when fn didn't raise an exception, False otherwise
51 82463074 Iustin Pop

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

64 82463074 Iustin Pop
  @type msg: string
65 82463074 Iustin Pop
  @param msg: the text of the exception
66 82463074 Iustin Pop
  @raise errors.BlockDeviceError
67 82463074 Iustin Pop

68 82463074 Iustin Pop
  """
69 82463074 Iustin Pop
  if args:
70 82463074 Iustin Pop
    msg = msg % args
71 82463074 Iustin Pop
  logging.error(msg)
72 82463074 Iustin Pop
  raise errors.BlockDeviceError(msg)
73 82463074 Iustin Pop
74 82463074 Iustin Pop
75 310fbb64 Iustin Pop
def _CanReadDevice(path):
76 310fbb64 Iustin Pop
  """Check if we can read from the given device.
77 310fbb64 Iustin Pop

78 310fbb64 Iustin Pop
  This tries to read the first 128k of the device.
79 310fbb64 Iustin Pop

80 310fbb64 Iustin Pop
  """
81 310fbb64 Iustin Pop
  try:
82 310fbb64 Iustin Pop
    utils.ReadFile(path, size=_DEVICE_READ_SIZE)
83 310fbb64 Iustin Pop
    return True
84 1122eb25 Iustin Pop
  except EnvironmentError:
85 310fbb64 Iustin Pop
    logging.warning("Can't read from device %s", path, exc_info=True)
86 310fbb64 Iustin Pop
    return False
87 310fbb64 Iustin Pop
88 310fbb64 Iustin Pop
89 a8083063 Iustin Pop
class BlockDev(object):
90 a8083063 Iustin Pop
  """Block device abstract class.
91 a8083063 Iustin Pop

92 a8083063 Iustin Pop
  A block device can be in the following states:
93 a8083063 Iustin Pop
    - not existing on the system, and by `Create()` it goes into:
94 a8083063 Iustin Pop
    - existing but not setup/not active, and by `Assemble()` goes into:
95 a8083063 Iustin Pop
    - active read-write and by `Open()` it goes into
96 a8083063 Iustin Pop
    - online (=used, or ready for use)
97 a8083063 Iustin Pop

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

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

109 a8083063 Iustin Pop
  A block device is identified by three items:
110 a8083063 Iustin Pop
    - the /dev path of the device (dynamic)
111 a8083063 Iustin Pop
    - a unique ID of the device (static)
112 a8083063 Iustin Pop
    - it's major/minor pair (dynamic)
113 a8083063 Iustin Pop

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

120 a8083063 Iustin Pop
  You can get to a device in two ways:
121 a8083063 Iustin Pop
    - creating the (real) device, which returns you
122 abdf0113 Iustin Pop
      an attached instance (lvcreate)
123 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
124 a8083063 Iustin Pop

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

131 a8083063 Iustin Pop
  """
132 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
133 a8083063 Iustin Pop
    self._children = children
134 a8083063 Iustin Pop
    self.dev_path = None
135 a8083063 Iustin Pop
    self.unique_id = unique_id
136 a8083063 Iustin Pop
    self.major = None
137 a8083063 Iustin Pop
    self.minor = None
138 cb999543 Iustin Pop
    self.attached = False
139 464f8daf Iustin Pop
    self.size = size
140 a8083063 Iustin Pop
141 a8083063 Iustin Pop
  def Assemble(self):
142 a8083063 Iustin Pop
    """Assemble the device from its components.
143 a8083063 Iustin Pop

144 f87548b5 Iustin Pop
    Implementations of this method by child classes must ensure that:
145 f87548b5 Iustin Pop
      - after the device has been assembled, it knows its major/minor
146 f87548b5 Iustin Pop
        numbers; this allows other devices (usually parents) to probe
147 f87548b5 Iustin Pop
        correctly for their children
148 f87548b5 Iustin Pop
      - calling this method on an existing, in-use device is safe
149 f87548b5 Iustin Pop
      - if the device is already configured (and in an OK state),
150 f87548b5 Iustin Pop
        this method is idempotent
151 a8083063 Iustin Pop

152 a8083063 Iustin Pop
    """
153 1063abd1 Iustin Pop
    pass
154 a8083063 Iustin Pop
155 a8083063 Iustin Pop
  def Attach(self):
156 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
157 a8083063 Iustin Pop

158 a8083063 Iustin Pop
    """
159 a8083063 Iustin Pop
    raise NotImplementedError
160 a8083063 Iustin Pop
161 a8083063 Iustin Pop
  def Close(self):
162 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
163 a8083063 Iustin Pop

164 a8083063 Iustin Pop
    """
165 a8083063 Iustin Pop
    raise NotImplementedError
166 a8083063 Iustin Pop
167 a8083063 Iustin Pop
  @classmethod
168 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
169 a8083063 Iustin Pop
    """Create the device.
170 a8083063 Iustin Pop

171 a8083063 Iustin Pop
    If the device cannot be created, it will return None
172 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
173 a8083063 Iustin Pop

174 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
175 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
176 a8083063 Iustin Pop
    enough for both creation and assembly (later).
177 a8083063 Iustin Pop

178 a8083063 Iustin Pop
    """
179 a8083063 Iustin Pop
    raise NotImplementedError
180 a8083063 Iustin Pop
181 a8083063 Iustin Pop
  def Remove(self):
182 a8083063 Iustin Pop
    """Remove this device.
183 a8083063 Iustin Pop

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

188 a8083063 Iustin Pop
    """
189 a8083063 Iustin Pop
    raise NotImplementedError
190 a8083063 Iustin Pop
191 f3e513ad Iustin Pop
  def Rename(self, new_id):
192 f3e513ad Iustin Pop
    """Rename this device.
193 f3e513ad Iustin Pop

194 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
195 f3e513ad Iustin Pop

196 f3e513ad Iustin Pop
    """
197 f3e513ad Iustin Pop
    raise NotImplementedError
198 f3e513ad Iustin Pop
199 a8083063 Iustin Pop
  def Open(self, force=False):
200 a8083063 Iustin Pop
    """Make the device ready for use.
201 a8083063 Iustin Pop

202 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
203 a8083063 Iustin Pop
    devices need this.
204 a8083063 Iustin Pop

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

208 a8083063 Iustin Pop
    """
209 a8083063 Iustin Pop
    raise NotImplementedError
210 a8083063 Iustin Pop
211 a8083063 Iustin Pop
  def Shutdown(self):
212 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
213 a8083063 Iustin Pop

214 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
215 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
216 a8083063 Iustin Pop
    assembled after this call.
217 a8083063 Iustin Pop

218 a8083063 Iustin Pop
    """
219 a8083063 Iustin Pop
    raise NotImplementedError
220 a8083063 Iustin Pop
221 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
222 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
223 a8083063 Iustin Pop

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

226 a8083063 Iustin Pop
    """
227 a8083063 Iustin Pop
    result = True
228 a8083063 Iustin Pop
    if self._children:
229 a8083063 Iustin Pop
      for child in self._children:
230 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
231 a8083063 Iustin Pop
    return result
232 a8083063 Iustin Pop
233 a3fffcc6 Renรฉ Nussbaumer
  def PauseResumeSync(self, pause):
234 a3fffcc6 Renรฉ Nussbaumer
    """Pause/Resume the sync of the mirror.
235 a3fffcc6 Renรฉ Nussbaumer

236 a3fffcc6 Renรฉ Nussbaumer
    In case this is not a mirroring device, this is no-op.
237 a3fffcc6 Renรฉ Nussbaumer

238 a3fffcc6 Renรฉ Nussbaumer
    @param pause: Wheater to pause or resume
239 a3fffcc6 Renรฉ Nussbaumer

240 a3fffcc6 Renรฉ Nussbaumer
    """
241 a3fffcc6 Renรฉ Nussbaumer
    result = True
242 a3fffcc6 Renรฉ Nussbaumer
    if self._children:
243 a3fffcc6 Renรฉ Nussbaumer
      for child in self._children:
244 a3fffcc6 Renรฉ Nussbaumer
        result = result and child.PauseResumeSync(pause)
245 a3fffcc6 Renรฉ Nussbaumer
    return result
246 a3fffcc6 Renรฉ Nussbaumer
247 a8083063 Iustin Pop
  def GetSyncStatus(self):
248 a8083063 Iustin Pop
    """Returns the sync status of the device.
249 a8083063 Iustin Pop

250 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
251 a8083063 Iustin Pop
    status of the mirror.
252 a8083063 Iustin Pop

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

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

258 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
259 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
260 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
261 a8083063 Iustin Pop

262 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
263 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
264 0834c866 Iustin Pop
    return False (not degraded).
265 0834c866 Iustin Pop

266 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
267 c41eea6e Iustin Pop

268 a8083063 Iustin Pop
    """
269 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
270 96acbc09 Michael Hanselmann
                                  major=self.major,
271 96acbc09 Michael Hanselmann
                                  minor=self.minor,
272 96acbc09 Michael Hanselmann
                                  sync_percent=None,
273 96acbc09 Michael Hanselmann
                                  estimated_time=None,
274 96acbc09 Michael Hanselmann
                                  is_degraded=False,
275 f208978a Michael Hanselmann
                                  ldisk_status=constants.LDS_OKAY)
276 a8083063 Iustin Pop
277 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
278 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
279 a8083063 Iustin Pop

280 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
281 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
282 a8083063 Iustin Pop
    children.
283 a8083063 Iustin Pop

284 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
285 96acbc09 Michael Hanselmann

286 a8083063 Iustin Pop
    """
287 96acbc09 Michael Hanselmann
    status = self.GetSyncStatus()
288 96acbc09 Michael Hanselmann
289 96acbc09 Michael Hanselmann
    min_percent = status.sync_percent
290 96acbc09 Michael Hanselmann
    max_time = status.estimated_time
291 96acbc09 Michael Hanselmann
    is_degraded = status.is_degraded
292 f208978a Michael Hanselmann
    ldisk_status = status.ldisk_status
293 96acbc09 Michael Hanselmann
294 a8083063 Iustin Pop
    if self._children:
295 a8083063 Iustin Pop
      for child in self._children:
296 96acbc09 Michael Hanselmann
        child_status = child.GetSyncStatus()
297 96acbc09 Michael Hanselmann
298 a8083063 Iustin Pop
        if min_percent is None:
299 96acbc09 Michael Hanselmann
          min_percent = child_status.sync_percent
300 96acbc09 Michael Hanselmann
        elif child_status.sync_percent is not None:
301 96acbc09 Michael Hanselmann
          min_percent = min(min_percent, child_status.sync_percent)
302 96acbc09 Michael Hanselmann
303 a8083063 Iustin Pop
        if max_time is None:
304 96acbc09 Michael Hanselmann
          max_time = child_status.estimated_time
305 96acbc09 Michael Hanselmann
        elif child_status.estimated_time is not None:
306 96acbc09 Michael Hanselmann
          max_time = max(max_time, child_status.estimated_time)
307 96acbc09 Michael Hanselmann
308 96acbc09 Michael Hanselmann
        is_degraded = is_degraded or child_status.is_degraded
309 f208978a Michael Hanselmann
310 f208978a Michael Hanselmann
        if ldisk_status is None:
311 f208978a Michael Hanselmann
          ldisk_status = child_status.ldisk_status
312 f208978a Michael Hanselmann
        elif child_status.ldisk_status is not None:
313 f208978a Michael Hanselmann
          ldisk_status = max(ldisk_status, child_status.ldisk_status)
314 96acbc09 Michael Hanselmann
315 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
316 96acbc09 Michael Hanselmann
                                  major=self.major,
317 96acbc09 Michael Hanselmann
                                  minor=self.minor,
318 96acbc09 Michael Hanselmann
                                  sync_percent=min_percent,
319 96acbc09 Michael Hanselmann
                                  estimated_time=max_time,
320 96acbc09 Michael Hanselmann
                                  is_degraded=is_degraded,
321 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
322 a8083063 Iustin Pop
323 a8083063 Iustin Pop
324 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
325 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
326 a0c3fea1 Michael Hanselmann

327 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
328 a0c3fea1 Michael Hanselmann

329 a0c3fea1 Michael Hanselmann
    """
330 a0c3fea1 Michael Hanselmann
    for child in self._children:
331 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
332 a0c3fea1 Michael Hanselmann
333 1005d816 Iustin Pop
  def Grow(self, amount):
334 1005d816 Iustin Pop
    """Grow the block device.
335 1005d816 Iustin Pop

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

338 1005d816 Iustin Pop
    """
339 1005d816 Iustin Pop
    raise NotImplementedError
340 a0c3fea1 Michael Hanselmann
341 fcff3897 Iustin Pop
  def GetActualSize(self):
342 fcff3897 Iustin Pop
    """Return the actual disk size.
343 fcff3897 Iustin Pop

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

346 fcff3897 Iustin Pop
    """
347 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
348 fcff3897 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize64", self.dev_path])
349 fcff3897 Iustin Pop
    if result.failed:
350 fcff3897 Iustin Pop
      _ThrowError("blockdev failed (%s): %s",
351 fcff3897 Iustin Pop
                  result.fail_reason, result.output)
352 fcff3897 Iustin Pop
    try:
353 fcff3897 Iustin Pop
      sz = int(result.output.strip())
354 fcff3897 Iustin Pop
    except (ValueError, TypeError), err:
355 fcff3897 Iustin Pop
      _ThrowError("Failed to parse blockdev output: %s", str(err))
356 fcff3897 Iustin Pop
    return sz
357 fcff3897 Iustin Pop
358 a8083063 Iustin Pop
  def __repr__(self):
359 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
360 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
361 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
362 a8083063 Iustin Pop
363 a8083063 Iustin Pop
364 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
365 a8083063 Iustin Pop
  """Logical Volume block device.
366 a8083063 Iustin Pop

367 a8083063 Iustin Pop
  """
368 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
369 6136f8f0 Iustin Pop
  _INVALID_NAMES = frozenset([".", "..", "snapshot", "pvmove"])
370 6136f8f0 Iustin Pop
  _INVALID_SUBSTRINGS = frozenset(["_mlog", "_mimage"])
371 6136f8f0 Iustin Pop
372 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
373 a8083063 Iustin Pop
    """Attaches to a LV device.
374 a8083063 Iustin Pop

375 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
376 a8083063 Iustin Pop

377 a8083063 Iustin Pop
    """
378 464f8daf Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children, size)
379 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
380 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
381 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
382 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
383 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
384 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
385 99e8295c Iustin Pop
    self._degraded = True
386 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
387 a8083063 Iustin Pop
    self.Attach()
388 a8083063 Iustin Pop
389 a8083063 Iustin Pop
  @classmethod
390 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
391 a8083063 Iustin Pop
    """Create a new logical volume.
392 a8083063 Iustin Pop

393 a8083063 Iustin Pop
    """
394 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
395 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
396 6c626518 Iustin Pop
                                   str(unique_id))
397 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
398 6136f8f0 Iustin Pop
    cls._ValidateName(vg_name)
399 6136f8f0 Iustin Pop
    cls._ValidateName(lv_name)
400 2070598f Iustin Pop
    pvs_info = cls.GetPVInfo([vg_name])
401 a8083063 Iustin Pop
    if not pvs_info:
402 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", vg_name)
403 a8083063 Iustin Pop
    pvs_info.sort()
404 a8083063 Iustin Pop
    pvs_info.reverse()
405 5b7b5d49 Guido Trotter
406 5b7b5d49 Guido Trotter
    pvlist = [ pv[1] for pv in pvs_info ]
407 403f5172 Guido Trotter
    if compat.any(":" in v for v in pvlist):
408 01b6558a Iustin Pop
      _ThrowError("Some of your PVs have the invalid character ':' in their"
409 01b6558a Iustin Pop
                  " name, this is not supported - please filter them out"
410 01b6558a Iustin Pop
                  " in lvm.conf using either 'filter' or 'preferred_names'")
411 5b7b5d49 Guido Trotter
    free_size = sum([ pv[0] for pv in pvs_info ])
412 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
413 fecbe9d5 Iustin Pop
    stripes = min(current_pvs, constants.LVM_STRIPECOUNT)
414 5b7b5d49 Guido Trotter
415 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
416 5b7b5d49 Guido Trotter
    # calling the create function.
417 a8083063 Iustin Pop
    if free_size < size:
418 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
419 82463074 Iustin Pop
                  " available %s", size, free_size)
420 fecbe9d5 Iustin Pop
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
421 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
422 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
423 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
424 fecbe9d5 Iustin Pop
    # stripes
425 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
426 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
427 fecbe9d5 Iustin Pop
      if not result.failed:
428 fecbe9d5 Iustin Pop
        break
429 a8083063 Iustin Pop
    if result.failed:
430 82463074 Iustin Pop
      _ThrowError("LV create failed (%s): %s",
431 82463074 Iustin Pop
                  result.fail_reason, result.output)
432 464f8daf Iustin Pop
    return LogicalVolume(unique_id, children, size)
433 a8083063 Iustin Pop
434 a8083063 Iustin Pop
  @staticmethod
435 197478f2 Renรฉ Nussbaumer
  def _GetVolumeInfo(lvm_cmd, fields):
436 197478f2 Renรฉ Nussbaumer
    """Returns LVM Volumen infos using lvm_cmd
437 197478f2 Renรฉ Nussbaumer

438 197478f2 Renรฉ Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
439 197478f2 Renรฉ Nussbaumer
    @param fields: Fields to return
440 197478f2 Renรฉ Nussbaumer
    @return: A list of dicts each with the parsed fields
441 197478f2 Renรฉ Nussbaumer

442 197478f2 Renรฉ Nussbaumer
    """
443 197478f2 Renรฉ Nussbaumer
    if not fields:
444 197478f2 Renรฉ Nussbaumer
      raise errors.ProgrammerError("No fields specified")
445 197478f2 Renรฉ Nussbaumer
446 197478f2 Renรฉ Nussbaumer
    sep = "|"
447 197478f2 Renรฉ Nussbaumer
    cmd = [lvm_cmd, "--noheadings", "--nosuffix", "--units=m", "--unbuffered",
448 197478f2 Renรฉ Nussbaumer
           "--separator=%s" % sep, "-o%s" % ",".join(fields)]
449 197478f2 Renรฉ Nussbaumer
450 197478f2 Renรฉ Nussbaumer
    result = utils.RunCmd(cmd)
451 197478f2 Renรฉ Nussbaumer
    if result.failed:
452 197478f2 Renรฉ Nussbaumer
      raise errors.CommandError("Can't get the volume information: %s - %s" %
453 197478f2 Renรฉ Nussbaumer
                                (result.fail_reason, result.output))
454 197478f2 Renรฉ Nussbaumer
455 197478f2 Renรฉ Nussbaumer
    data = []
456 197478f2 Renรฉ Nussbaumer
    for line in result.stdout.splitlines():
457 197478f2 Renรฉ Nussbaumer
      splitted_fields = line.strip().split(sep)
458 197478f2 Renรฉ Nussbaumer
459 197478f2 Renรฉ Nussbaumer
      if len(fields) != len(splitted_fields):
460 197478f2 Renรฉ Nussbaumer
        raise errors.CommandError("Can't parse %s output: line '%s'" %
461 197478f2 Renรฉ Nussbaumer
                                  (lvm_cmd, line))
462 197478f2 Renรฉ Nussbaumer
463 197478f2 Renรฉ Nussbaumer
      data.append(splitted_fields)
464 197478f2 Renรฉ Nussbaumer
465 197478f2 Renรฉ Nussbaumer
    return data
466 197478f2 Renรฉ Nussbaumer
467 197478f2 Renรฉ Nussbaumer
  @classmethod
468 197478f2 Renรฉ Nussbaumer
  def GetPVInfo(cls, vg_names, filter_allocatable=True):
469 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
470 a8083063 Iustin Pop

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

474 c41eea6e Iustin Pop
    @rtype: list
475 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
476 098c0958 Michael Hanselmann

477 a8083063 Iustin Pop
    """
478 197478f2 Renรฉ Nussbaumer
    try:
479 197478f2 Renรฉ Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
480 197478f2 Renรฉ Nussbaumer
                                        "pv_attr"])
481 197478f2 Renรฉ Nussbaumer
    except errors.GenericError, err:
482 197478f2 Renรฉ Nussbaumer
      logging.error("Can't get PV information: %s", err)
483 a8083063 Iustin Pop
      return None
484 197478f2 Renรฉ Nussbaumer
485 a8083063 Iustin Pop
    data = []
486 197478f2 Renรฉ Nussbaumer
    for pv_name, vg_name, pv_free, pv_attr in info:
487 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
488 197478f2 Renรฉ Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
489 a8083063 Iustin Pop
        continue
490 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
491 197478f2 Renรฉ Nussbaumer
      if vg_names and vg_name not in vg_names:
492 2070598f Iustin Pop
        continue
493 197478f2 Renรฉ Nussbaumer
      data.append((float(pv_free), pv_name, vg_name))
494 197478f2 Renรฉ Nussbaumer
495 197478f2 Renรฉ Nussbaumer
    return data
496 197478f2 Renรฉ Nussbaumer
497 197478f2 Renรฉ Nussbaumer
  @classmethod
498 197478f2 Renรฉ Nussbaumer
  def GetVGInfo(cls, vg_names, filter_readonly=True):
499 197478f2 Renรฉ Nussbaumer
    """Get the free space info for specific VGs.
500 197478f2 Renรฉ Nussbaumer

501 197478f2 Renรฉ Nussbaumer
    @param vg_names: list of volume group names, if empty all will be returned
502 197478f2 Renรฉ Nussbaumer
    @param filter_readonly: whether to skip over readonly VGs
503 197478f2 Renรฉ Nussbaumer

504 197478f2 Renรฉ Nussbaumer
    @rtype: list
505 673cd9c4 Renรฉ Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
506 673cd9c4 Renรฉ Nussbaumer
             MiB
507 197478f2 Renรฉ Nussbaumer

508 197478f2 Renรฉ Nussbaumer
    """
509 197478f2 Renรฉ Nussbaumer
    try:
510 673cd9c4 Renรฉ Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
511 673cd9c4 Renรฉ Nussbaumer
                                        "vg_size"])
512 197478f2 Renรฉ Nussbaumer
    except errors.GenericError, err:
513 197478f2 Renรฉ Nussbaumer
      logging.error("Can't get VG information: %s", err)
514 197478f2 Renรฉ Nussbaumer
      return None
515 197478f2 Renรฉ Nussbaumer
516 197478f2 Renรฉ Nussbaumer
    data = []
517 673cd9c4 Renรฉ Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
518 197478f2 Renรฉ Nussbaumer
      # (possibly) skip over vgs which are not writable
519 197478f2 Renรฉ Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
520 197478f2 Renรฉ Nussbaumer
        continue
521 197478f2 Renรฉ Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
522 197478f2 Renรฉ Nussbaumer
      if vg_names and vg_name not in vg_names:
523 197478f2 Renรฉ Nussbaumer
        continue
524 673cd9c4 Renรฉ Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
525 a8083063 Iustin Pop
526 a8083063 Iustin Pop
    return data
527 a8083063 Iustin Pop
528 6136f8f0 Iustin Pop
  @classmethod
529 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
530 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
531 6136f8f0 Iustin Pop

532 6136f8f0 Iustin Pop
    The list of valid characters and restricted names is taken out of
533 6136f8f0 Iustin Pop
    the lvm(8) manpage, with the simplification that we enforce both
534 6136f8f0 Iustin Pop
    VG and LV restrictions on the names.
535 6136f8f0 Iustin Pop

536 6136f8f0 Iustin Pop
    """
537 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
538 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
539 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
540 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
541 6136f8f0 Iustin Pop
542 a8083063 Iustin Pop
  def Remove(self):
543 a8083063 Iustin Pop
    """Remove this logical volume.
544 a8083063 Iustin Pop

545 a8083063 Iustin Pop
    """
546 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
547 a8083063 Iustin Pop
      # the LV does not exist
548 0c6c04ec Iustin Pop
      return
549 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
550 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
551 a8083063 Iustin Pop
    if result.failed:
552 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
553 a8083063 Iustin Pop
554 f3e513ad Iustin Pop
  def Rename(self, new_id):
555 f3e513ad Iustin Pop
    """Rename this logical volume.
556 f3e513ad Iustin Pop

557 f3e513ad Iustin Pop
    """
558 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
559 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
560 f3e513ad Iustin Pop
    new_vg, new_name = new_id
561 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
562 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
563 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
564 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
565 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
566 f3e513ad Iustin Pop
    if result.failed:
567 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
568 be345db0 Iustin Pop
    self._lv_name = new_name
569 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
570 be345db0 Iustin Pop
571 a8083063 Iustin Pop
  def Attach(self):
572 a8083063 Iustin Pop
    """Attach to an existing LV.
573 a8083063 Iustin Pop

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

578 a8083063 Iustin Pop
    """
579 cb999543 Iustin Pop
    self.attached = False
580 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
581 38256320 Iustin Pop
                           "--units=m", "--nosuffix",
582 38256320 Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
583 38256320 Iustin Pop
                           "vg_extent_size,stripes", self.dev_path])
584 a8083063 Iustin Pop
    if result.failed:
585 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
586 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
587 a8083063 Iustin Pop
      return False
588 38256320 Iustin Pop
    # the output can (and will) have multiple lines for multi-segment
589 38256320 Iustin Pop
    # LVs, as the 'stripes' parameter is a segment one, so we take
590 38256320 Iustin Pop
    # only the last entry, which is the one we're interested in; note
591 38256320 Iustin Pop
    # that with LVM2 anyway the 'stripes' value must be constant
592 38256320 Iustin Pop
    # across segments, so this is a no-op actually
593 38256320 Iustin Pop
    out = result.stdout.splitlines()
594 38256320 Iustin Pop
    if not out: # totally empty result? splitlines() returns at least
595 38256320 Iustin Pop
                # one line for any non-empty string
596 38256320 Iustin Pop
      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
597 38256320 Iustin Pop
      return False
598 38256320 Iustin Pop
    out = out[-1].strip().rstrip(',')
599 99e8295c Iustin Pop
    out = out.split(",")
600 38256320 Iustin Pop
    if len(out) != 5:
601 38256320 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
602 99e8295c Iustin Pop
      return False
603 99e8295c Iustin Pop
604 38256320 Iustin Pop
    status, major, minor, pe_size, stripes = out
605 99e8295c Iustin Pop
    if len(status) != 6:
606 468c5f77 Iustin Pop
      logging.error("lvs lv_attr is not 6 characters (%s)", status)
607 99e8295c Iustin Pop
      return False
608 99e8295c Iustin Pop
609 99e8295c Iustin Pop
    try:
610 99e8295c Iustin Pop
      major = int(major)
611 99e8295c Iustin Pop
      minor = int(minor)
612 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
613 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
614 99e8295c Iustin Pop
615 38256320 Iustin Pop
    try:
616 38256320 Iustin Pop
      pe_size = int(float(pe_size))
617 38256320 Iustin Pop
    except (TypeError, ValueError), err:
618 38256320 Iustin Pop
      logging.error("Can't parse vg extent size: %s", err)
619 38256320 Iustin Pop
      return False
620 38256320 Iustin Pop
621 38256320 Iustin Pop
    try:
622 38256320 Iustin Pop
      stripes = int(stripes)
623 38256320 Iustin Pop
    except (TypeError, ValueError), err:
624 38256320 Iustin Pop
      logging.error("Can't parse the number of stripes: %s", err)
625 38256320 Iustin Pop
      return False
626 38256320 Iustin Pop
627 99e8295c Iustin Pop
    self.major = major
628 99e8295c Iustin Pop
    self.minor = minor
629 38256320 Iustin Pop
    self.pe_size = pe_size
630 38256320 Iustin Pop
    self.stripe_count = stripes
631 99e8295c Iustin Pop
    self._degraded = status[0] == 'v' # virtual volume, i.e. doesn't backing
632 99e8295c Iustin Pop
                                      # storage
633 cb999543 Iustin Pop
    self.attached = True
634 99e8295c Iustin Pop
    return True
635 a8083063 Iustin Pop
636 a8083063 Iustin Pop
  def Assemble(self):
637 a8083063 Iustin Pop
    """Assemble the device.
638 a8083063 Iustin Pop

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

643 a8083063 Iustin Pop
    """
644 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
645 5574047a Iustin Pop
    if result.failed:
646 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
647 a8083063 Iustin Pop
648 a8083063 Iustin Pop
  def Shutdown(self):
649 a8083063 Iustin Pop
    """Shutdown the device.
650 a8083063 Iustin Pop

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

654 a8083063 Iustin Pop
    """
655 746f7476 Iustin Pop
    pass
656 a8083063 Iustin Pop
657 9db6dbce Iustin Pop
  def GetSyncStatus(self):
658 9db6dbce Iustin Pop
    """Returns the sync status of the device.
659 9db6dbce Iustin Pop

660 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
661 9db6dbce Iustin Pop
    status of the mirror.
662 9db6dbce Iustin Pop

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

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

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

676 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
677 c41eea6e Iustin Pop

678 9db6dbce Iustin Pop
    """
679 f208978a Michael Hanselmann
    if self._degraded:
680 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
681 f208978a Michael Hanselmann
    else:
682 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
683 f208978a Michael Hanselmann
684 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
685 96acbc09 Michael Hanselmann
                                  major=self.major,
686 96acbc09 Michael Hanselmann
                                  minor=self.minor,
687 96acbc09 Michael Hanselmann
                                  sync_percent=None,
688 96acbc09 Michael Hanselmann
                                  estimated_time=None,
689 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
690 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
691 9db6dbce Iustin Pop
692 a8083063 Iustin Pop
  def Open(self, force=False):
693 a8083063 Iustin Pop
    """Make the device ready for I/O.
694 a8083063 Iustin Pop

695 a8083063 Iustin Pop
    This is a no-op for the LV device type.
696 a8083063 Iustin Pop

697 a8083063 Iustin Pop
    """
698 fdbd668d Iustin Pop
    pass
699 a8083063 Iustin Pop
700 a8083063 Iustin Pop
  def Close(self):
701 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
702 a8083063 Iustin Pop

703 a8083063 Iustin Pop
    This is a no-op for the LV device type.
704 a8083063 Iustin Pop

705 a8083063 Iustin Pop
    """
706 fdbd668d Iustin Pop
    pass
707 a8083063 Iustin Pop
708 a8083063 Iustin Pop
  def Snapshot(self, size):
709 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
710 a8083063 Iustin Pop

711 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
712 800ac399 Iustin Pop

713 a8083063 Iustin Pop
    """
714 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
715 a8083063 Iustin Pop
716 a8083063 Iustin Pop
    # remove existing snapshot if found
717 464f8daf Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None, size)
718 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
719 a8083063 Iustin Pop
720 197478f2 Renรฉ Nussbaumer
    vg_info = self.GetVGInfo([self._vg_name])
721 197478f2 Renรฉ Nussbaumer
    if not vg_info:
722 197478f2 Renรฉ Nussbaumer
      _ThrowError("Can't compute VG info for vg %s", self._vg_name)
723 673cd9c4 Renรฉ Nussbaumer
    free_size, _, _ = vg_info[0]
724 a8083063 Iustin Pop
    if free_size < size:
725 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
726 82463074 Iustin Pop
                  " available %s", size, free_size)
727 a8083063 Iustin Pop
728 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
729 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
730 a8083063 Iustin Pop
    if result.failed:
731 82463074 Iustin Pop
      _ThrowError("command: %s error: %s - %s",
732 82463074 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
733 a8083063 Iustin Pop
734 800ac399 Iustin Pop
    return (self._vg_name, snap_name)
735 a8083063 Iustin Pop
736 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
737 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
738 a0c3fea1 Michael Hanselmann

739 a0c3fea1 Michael Hanselmann
    """
740 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
741 a0c3fea1 Michael Hanselmann
742 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
743 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
744 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
745 a0c3fea1 Michael Hanselmann
746 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
747 a0c3fea1 Michael Hanselmann
    text = text[:128]
748 a0c3fea1 Michael Hanselmann
749 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
750 a0c3fea1 Michael Hanselmann
                           self.dev_path])
751 a0c3fea1 Michael Hanselmann
    if result.failed:
752 82463074 Iustin Pop
      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
753 82463074 Iustin Pop
                  result.output)
754 82463074 Iustin Pop
755 1005d816 Iustin Pop
  def Grow(self, amount):
756 1005d816 Iustin Pop
    """Grow the logical volume.
757 1005d816 Iustin Pop

758 1005d816 Iustin Pop
    """
759 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
760 38256320 Iustin Pop
      if not self.Attach():
761 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
762 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
763 38256320 Iustin Pop
    rest = amount % full_stripe_size
764 38256320 Iustin Pop
    if rest != 0:
765 38256320 Iustin Pop
      amount += full_stripe_size - rest
766 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
767 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
768 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
769 1005d816 Iustin Pop
    # supports 'cling'
770 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
771 1005d816 Iustin Pop
      result = utils.RunCmd(["lvextend", "--alloc", alloc_policy,
772 1005d816 Iustin Pop
                             "-L", "+%dm" % amount, self.dev_path])
773 1005d816 Iustin Pop
      if not result.failed:
774 1005d816 Iustin Pop
        return
775 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
776 a0c3fea1 Michael Hanselmann
777 a0c3fea1 Michael Hanselmann
778 6b90c22e Iustin Pop
class DRBD8Status(object):
779 6b90c22e Iustin Pop
  """A DRBD status representation class.
780 6b90c22e Iustin Pop

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

783 6b90c22e Iustin Pop
  """
784 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
785 01e2ce3a Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
786 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
787 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
788 7a380ddf Renรฉ Nussbaumer
                       # Due to a bug in drbd in the kernel, introduced in
789 7a380ddf Renรฉ Nussbaumer
                       # commit 4b0715f096 (still unfixed as of 2011-08-22)
790 7a380ddf Renรฉ Nussbaumer
                       "(?:\s|M)"
791 7a380ddf Renรฉ Nussbaumer
                       "finish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
792 6b90c22e Iustin Pop
793 3c003d9d Iustin Pop
  CS_UNCONFIGURED = "Unconfigured"
794 3c003d9d Iustin Pop
  CS_STANDALONE = "StandAlone"
795 3c003d9d Iustin Pop
  CS_WFCONNECTION = "WFConnection"
796 3c003d9d Iustin Pop
  CS_WFREPORTPARAMS = "WFReportParams"
797 3c003d9d Iustin Pop
  CS_CONNECTED = "Connected"
798 3c003d9d Iustin Pop
  CS_STARTINGSYNCS = "StartingSyncS"
799 3c003d9d Iustin Pop
  CS_STARTINGSYNCT = "StartingSyncT"
800 3c003d9d Iustin Pop
  CS_WFBITMAPS = "WFBitMapS"
801 3c003d9d Iustin Pop
  CS_WFBITMAPT = "WFBitMapT"
802 3c003d9d Iustin Pop
  CS_WFSYNCUUID = "WFSyncUUID"
803 3c003d9d Iustin Pop
  CS_SYNCSOURCE = "SyncSource"
804 3c003d9d Iustin Pop
  CS_SYNCTARGET = "SyncTarget"
805 3c003d9d Iustin Pop
  CS_PAUSEDSYNCS = "PausedSyncS"
806 3c003d9d Iustin Pop
  CS_PAUSEDSYNCT = "PausedSyncT"
807 3c003d9d Iustin Pop
  CSET_SYNC = frozenset([
808 3c003d9d Iustin Pop
    CS_WFREPORTPARAMS,
809 3c003d9d Iustin Pop
    CS_STARTINGSYNCS,
810 3c003d9d Iustin Pop
    CS_STARTINGSYNCT,
811 3c003d9d Iustin Pop
    CS_WFBITMAPS,
812 3c003d9d Iustin Pop
    CS_WFBITMAPT,
813 3c003d9d Iustin Pop
    CS_WFSYNCUUID,
814 3c003d9d Iustin Pop
    CS_SYNCSOURCE,
815 3c003d9d Iustin Pop
    CS_SYNCTARGET,
816 3c003d9d Iustin Pop
    CS_PAUSEDSYNCS,
817 3c003d9d Iustin Pop
    CS_PAUSEDSYNCT,
818 3c003d9d Iustin Pop
    ])
819 3c003d9d Iustin Pop
820 3c003d9d Iustin Pop
  DS_DISKLESS = "Diskless"
821 3c003d9d Iustin Pop
  DS_ATTACHING = "Attaching" # transient state
822 3c003d9d Iustin Pop
  DS_FAILED = "Failed" # transient state, next: diskless
823 3c003d9d Iustin Pop
  DS_NEGOTIATING = "Negotiating" # transient state
824 3c003d9d Iustin Pop
  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
825 3c003d9d Iustin Pop
  DS_OUTDATED = "Outdated"
826 3c003d9d Iustin Pop
  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
827 3c003d9d Iustin Pop
  DS_CONSISTENT = "Consistent"
828 3c003d9d Iustin Pop
  DS_UPTODATE = "UpToDate" # normal state
829 3c003d9d Iustin Pop
830 3c003d9d Iustin Pop
  RO_PRIMARY = "Primary"
831 3c003d9d Iustin Pop
  RO_SECONDARY = "Secondary"
832 3c003d9d Iustin Pop
  RO_UNKNOWN = "Unknown"
833 3c003d9d Iustin Pop
834 6b90c22e Iustin Pop
  def __init__(self, procline):
835 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
836 767d52d3 Iustin Pop
    if u:
837 3c003d9d Iustin Pop
      self.cstatus = self.CS_UNCONFIGURED
838 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
839 767d52d3 Iustin Pop
    else:
840 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
841 767d52d3 Iustin Pop
      if not m:
842 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
843 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
844 767d52d3 Iustin Pop
      self.lrole = m.group(2)
845 767d52d3 Iustin Pop
      self.rrole = m.group(3)
846 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
847 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
848 767d52d3 Iustin Pop
849 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
850 6b90c22e Iustin Pop
851 3c003d9d Iustin Pop
    self.is_standalone = self.cstatus == self.CS_STANDALONE
852 3c003d9d Iustin Pop
    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
853 3c003d9d Iustin Pop
    self.is_connected = self.cstatus == self.CS_CONNECTED
854 3c003d9d Iustin Pop
    self.is_primary = self.lrole == self.RO_PRIMARY
855 3c003d9d Iustin Pop
    self.is_secondary = self.lrole == self.RO_SECONDARY
856 3c003d9d Iustin Pop
    self.peer_primary = self.rrole == self.RO_PRIMARY
857 3c003d9d Iustin Pop
    self.peer_secondary = self.rrole == self.RO_SECONDARY
858 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
859 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
860 6b90c22e Iustin Pop
861 3c003d9d Iustin Pop
    self.is_diskless = self.ldisk == self.DS_DISKLESS
862 3c003d9d Iustin Pop
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
863 6b90c22e Iustin Pop
864 3c003d9d Iustin Pop
    self.is_in_resync = self.cstatus in self.CSET_SYNC
865 3c003d9d Iustin Pop
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
866 6b93ec9d Iustin Pop
867 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
868 6b90c22e Iustin Pop
    if m:
869 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
870 6b90c22e Iustin Pop
      hours = int(m.group(2))
871 6b90c22e Iustin Pop
      minutes = int(m.group(3))
872 6b90c22e Iustin Pop
      seconds = int(m.group(4))
873 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
874 6b90c22e Iustin Pop
    else:
875 3c003d9d Iustin Pop
      # we have (in this if branch) no percent information, but if
876 3c003d9d Iustin Pop
      # we're resyncing we need to 'fake' a sync percent information,
877 3c003d9d Iustin Pop
      # as this is how cmdlib determines if it makes sense to wait for
878 3c003d9d Iustin Pop
      # resyncing or not
879 3c003d9d Iustin Pop
      if self.is_in_resync:
880 3c003d9d Iustin Pop
        self.sync_percent = 0
881 3c003d9d Iustin Pop
      else:
882 3c003d9d Iustin Pop
        self.sync_percent = None
883 6b90c22e Iustin Pop
      self.est_time = None
884 6b90c22e Iustin Pop
885 6b90c22e Iustin Pop
886 fe267188 Iustin Pop
class BaseDRBD(BlockDev): # pylint: disable-msg=W0223
887 0f7f32d9 Iustin Pop
  """Base DRBD class.
888 a8083063 Iustin Pop

889 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
890 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
891 0f7f32d9 Iustin Pop

892 abdf0113 Iustin Pop
  """
893 fcee765d Manuel Franceschini
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)(?:\.\d+)?"
894 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
895 9122e60a Iustin Pop
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
896 9122e60a Iustin Pop
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
897 a8083063 Iustin Pop
898 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
899 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
900 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
901 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
902 a8083063 Iustin Pop
903 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
904 549071a0 Luca Bigliardi
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
905 6b90c22e Iustin Pop
906 abdf0113 Iustin Pop
  @staticmethod
907 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
908 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
909 a8083063 Iustin Pop

910 a8083063 Iustin Pop
    """
911 abdf0113 Iustin Pop
    try:
912 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
913 f6eaed12 Iustin Pop
    except EnvironmentError, err:
914 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
915 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
916 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
917 f6eaed12 Iustin Pop
      else:
918 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
919 abdf0113 Iustin Pop
    if not data:
920 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
921 abdf0113 Iustin Pop
    return data
922 a8083063 Iustin Pop
923 9122e60a Iustin Pop
  @classmethod
924 9122e60a Iustin Pop
  def _MassageProcData(cls, data):
925 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
926 a8083063 Iustin Pop

927 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
928 c41eea6e Iustin Pop
        for that minor
929 a8083063 Iustin Pop

930 a8083063 Iustin Pop
    """
931 abdf0113 Iustin Pop
    results = {}
932 abdf0113 Iustin Pop
    old_minor = old_line = None
933 abdf0113 Iustin Pop
    for line in data:
934 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
935 67d101d4 Iustin Pop
        continue
936 9122e60a Iustin Pop
      lresult = cls._VALID_LINE_RE.match(line)
937 abdf0113 Iustin Pop
      if lresult is not None:
938 abdf0113 Iustin Pop
        if old_minor is not None:
939 abdf0113 Iustin Pop
          results[old_minor] = old_line
940 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
941 abdf0113 Iustin Pop
        old_line = line
942 abdf0113 Iustin Pop
      else:
943 abdf0113 Iustin Pop
        if old_minor is not None:
944 abdf0113 Iustin Pop
          old_line += " " + line.strip()
945 abdf0113 Iustin Pop
    # add last line
946 abdf0113 Iustin Pop
    if old_minor is not None:
947 abdf0113 Iustin Pop
      results[old_minor] = old_line
948 abdf0113 Iustin Pop
    return results
949 a8083063 Iustin Pop
950 abdf0113 Iustin Pop
  @classmethod
951 fcee765d Manuel Franceschini
  def _GetVersion(cls, proc_data):
952 abdf0113 Iustin Pop
    """Return the DRBD version.
953 a8083063 Iustin Pop

954 abdf0113 Iustin Pop
    This will return a dict with keys:
955 c41eea6e Iustin Pop
      - k_major
956 c41eea6e Iustin Pop
      - k_minor
957 c41eea6e Iustin Pop
      - k_point
958 c41eea6e Iustin Pop
      - api
959 c41eea6e Iustin Pop
      - proto
960 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
961 a8083063 Iustin Pop

962 a8083063 Iustin Pop
    """
963 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
964 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
965 abdf0113 Iustin Pop
    if not version:
966 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
967 abdf0113 Iustin Pop
                                    first_line)
968 a8083063 Iustin Pop
969 abdf0113 Iustin Pop
    values = version.groups()
970 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
971 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
972 abdf0113 Iustin Pop
              'k_point': int(values[2]),
973 abdf0113 Iustin Pop
              'api': int(values[3]),
974 abdf0113 Iustin Pop
              'proto': int(values[4]),
975 abdf0113 Iustin Pop
             }
976 abdf0113 Iustin Pop
    if values[5] is not None:
977 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
978 a8083063 Iustin Pop
979 abdf0113 Iustin Pop
    return retval
980 abdf0113 Iustin Pop
981 abdf0113 Iustin Pop
  @staticmethod
982 549071a0 Luca Bigliardi
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
983 549071a0 Luca Bigliardi
    """Returns DRBD usermode_helper currently set.
984 549071a0 Luca Bigliardi

985 549071a0 Luca Bigliardi
    """
986 549071a0 Luca Bigliardi
    try:
987 549071a0 Luca Bigliardi
      helper = utils.ReadFile(filename).splitlines()[0]
988 549071a0 Luca Bigliardi
    except EnvironmentError, err:
989 549071a0 Luca Bigliardi
      if err.errno == errno.ENOENT:
990 549071a0 Luca Bigliardi
        _ThrowError("The file %s cannot be opened, check if the module"
991 549071a0 Luca Bigliardi
                    " is loaded (%s)", filename, str(err))
992 549071a0 Luca Bigliardi
      else:
993 549071a0 Luca Bigliardi
        _ThrowError("Can't read DRBD helper file %s: %s", filename, str(err))
994 549071a0 Luca Bigliardi
    if not helper:
995 549071a0 Luca Bigliardi
      _ThrowError("Can't read any data from %s", filename)
996 549071a0 Luca Bigliardi
    return helper
997 549071a0 Luca Bigliardi
998 549071a0 Luca Bigliardi
  @staticmethod
999 abdf0113 Iustin Pop
  def _DevPath(minor):
1000 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
1001 a8083063 Iustin Pop

1002 a8083063 Iustin Pop
    """
1003 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
1004 a8083063 Iustin Pop
1005 abdf0113 Iustin Pop
  @classmethod
1006 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
1007 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
1008 a8083063 Iustin Pop

1009 a8083063 Iustin Pop
    """
1010 abdf0113 Iustin Pop
    data = cls._GetProcData()
1011 a8083063 Iustin Pop
1012 abdf0113 Iustin Pop
    used_devs = {}
1013 abdf0113 Iustin Pop
    for line in data:
1014 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1015 abdf0113 Iustin Pop
      if not match:
1016 abdf0113 Iustin Pop
        continue
1017 abdf0113 Iustin Pop
      minor = int(match.group(1))
1018 abdf0113 Iustin Pop
      state = match.group(2)
1019 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1020 abdf0113 Iustin Pop
        continue
1021 abdf0113 Iustin Pop
      used_devs[minor] = state, line
1022 a8083063 Iustin Pop
1023 abdf0113 Iustin Pop
    return used_devs
1024 a8083063 Iustin Pop
1025 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
1026 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
1027 0834c866 Iustin Pop

1028 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1029 a8083063 Iustin Pop

1030 a8083063 Iustin Pop
    """
1031 abdf0113 Iustin Pop
    if minor is None:
1032 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
1033 cb999543 Iustin Pop
      self.attached = False
1034 a8083063 Iustin Pop
    else:
1035 abdf0113 Iustin Pop
      self.minor = minor
1036 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
1037 cb999543 Iustin Pop
      self.attached = True
1038 a8083063 Iustin Pop
1039 a8083063 Iustin Pop
  @staticmethod
1040 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
1041 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
1042 a8083063 Iustin Pop

1043 abdf0113 Iustin Pop
    This currently only check the size, which must be around
1044 abdf0113 Iustin Pop
    128MiB.
1045 a8083063 Iustin Pop

1046 a8083063 Iustin Pop
    """
1047 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1048 abdf0113 Iustin Pop
    if result.failed:
1049 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
1050 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
1051 a8083063 Iustin Pop
    try:
1052 abdf0113 Iustin Pop
      sectors = int(result.stdout)
1053 691744c4 Iustin Pop
    except (TypeError, ValueError):
1054 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
1055 c04bc777 Iustin Pop
    num_bytes = sectors * 512
1056 c04bc777 Iustin Pop
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
1057 c04bc777 Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (num_bytes / 1024 / 1024))
1058 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
1059 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
1060 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
1061 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
1062 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
1063 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
1064 c04bc777 Iustin Pop
    if num_bytes > 1024 * 1024 * 1024:
1065 c04bc777 Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (num_bytes / 1024 / 1024))
1066 a8083063 Iustin Pop
1067 abdf0113 Iustin Pop
  def Rename(self, new_id):
1068 abdf0113 Iustin Pop
    """Rename a device.
1069 a8083063 Iustin Pop

1070 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1071 a8083063 Iustin Pop

1072 a8083063 Iustin Pop
    """
1073 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1074 a8083063 Iustin Pop
1075 f3e513ad Iustin Pop
1076 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1077 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1078 a2cfdea2 Iustin Pop

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

1083 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1084 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1085 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1086 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1087 a2cfdea2 Iustin Pop

1088 a2cfdea2 Iustin Pop
  """
1089 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1090 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1091 a2cfdea2 Iustin Pop
1092 cf8df3f3 Iustin Pop
  # timeout constants
1093 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
1094 cf8df3f3 Iustin Pop
1095 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1096 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1097 fc1dc9d7 Iustin Pop
      children = []
1098 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
1099 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1100 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
1101 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1102 310fbb64 Iustin Pop
    (self._lhost, self._lport,
1103 310fbb64 Iustin Pop
     self._rhost, self._rport,
1104 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
1105 310fbb64 Iustin Pop
    if children:
1106 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
1107 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
1108 310fbb64 Iustin Pop
        children = []
1109 464f8daf Iustin Pop
    super(DRBD8, self).__init__(unique_id, children, size)
1110 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1111 fcee765d Manuel Franceschini
    version = self._GetVersion(self._GetProcData())
1112 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
1113 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
1114 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
1115 82463074 Iustin Pop
                  version['k_major'], version['k_minor'])
1116 a2cfdea2 Iustin Pop
1117 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1118 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1119 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1120 ffa1c0dc Iustin Pop
                       (unique_id,))
1121 a2cfdea2 Iustin Pop
    self.Attach()
1122 a2cfdea2 Iustin Pop
1123 a2cfdea2 Iustin Pop
  @classmethod
1124 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1125 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1126 a2cfdea2 Iustin Pop

1127 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1128 a2cfdea2 Iustin Pop

1129 a2cfdea2 Iustin Pop
    """
1130 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1131 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1132 a2cfdea2 Iustin Pop
    if result.failed:
1133 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1134 a2cfdea2 Iustin Pop
1135 a2cfdea2 Iustin Pop
  @classmethod
1136 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1137 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1138 a2cfdea2 Iustin Pop

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

1142 a2cfdea2 Iustin Pop
    """
1143 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1144 a2cfdea2 Iustin Pop
1145 a2cfdea2 Iustin Pop
    highest = None
1146 a2cfdea2 Iustin Pop
    for line in data:
1147 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1148 a2cfdea2 Iustin Pop
      if match:
1149 a2cfdea2 Iustin Pop
        return int(match.group(1))
1150 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1151 a2cfdea2 Iustin Pop
      if match:
1152 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1153 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1154 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1155 a2cfdea2 Iustin Pop
      return 0
1156 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1157 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1158 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1159 a2cfdea2 Iustin Pop
    return highest + 1
1160 a2cfdea2 Iustin Pop
1161 a2cfdea2 Iustin Pop
  @classmethod
1162 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1163 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1164 a2cfdea2 Iustin Pop

1165 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1166 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1167 a2cfdea2 Iustin Pop

1168 a2cfdea2 Iustin Pop
    """
1169 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1170 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1171 a2cfdea2 Iustin Pop
1172 a2cfdea2 Iustin Pop
    # pyparsing setup
1173 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1174 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1175 5a672c30 Manuel Franceschini
    lbracket = pyp.Literal("[").suppress()
1176 5a672c30 Manuel Franceschini
    rbracket = pyp.Literal("]").suppress()
1177 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1178 5a672c30 Manuel Franceschini
    colon = pyp.Literal(":").suppress()
1179 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1180 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1181 a2cfdea2 Iustin Pop
1182 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1183 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1184 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1185 a2cfdea2 Iustin Pop
1186 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1187 a2cfdea2 Iustin Pop
1188 a2cfdea2 Iustin Pop
    # value types
1189 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1190 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1191 5a672c30 Manuel Franceschini
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1192 5a672c30 Manuel Franceschini
                 pyp.Word(pyp.nums + ".") + colon + number)
1193 5a672c30 Manuel Franceschini
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
1194 5a672c30 Manuel Franceschini
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
1195 5a672c30 Manuel Franceschini
                 pyp.Optional(rbracket) + colon + number)
1196 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1197 5a672c30 Manuel Franceschini
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
1198 01e2ce3a Iustin Pop
    # device name, extended syntax
1199 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1200 a2cfdea2 Iustin Pop
1201 a2cfdea2 Iustin Pop
    # a statement
1202 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1203 5a672c30 Manuel Franceschini
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
1204 01e2ce3a Iustin Pop
                         device_value) +
1205 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1206 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1207 a2cfdea2 Iustin Pop
1208 a2cfdea2 Iustin Pop
    # an entire section
1209 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1210 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1211 a2cfdea2 Iustin Pop
1212 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1213 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1214 a2cfdea2 Iustin Pop
1215 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1216 a2cfdea2 Iustin Pop
1217 a2cfdea2 Iustin Pop
    return bnf
1218 a2cfdea2 Iustin Pop
1219 a2cfdea2 Iustin Pop
  @classmethod
1220 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1221 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1222 a2cfdea2 Iustin Pop

1223 a2cfdea2 Iustin Pop
    """
1224 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1225 a2cfdea2 Iustin Pop
    if result.failed:
1226 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1227 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1228 3840729d Iustin Pop
      return None
1229 3840729d Iustin Pop
    return result.stdout
1230 3840729d Iustin Pop
1231 3840729d Iustin Pop
  @classmethod
1232 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1233 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1234 3840729d Iustin Pop

1235 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1236 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1237 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1238 3840729d Iustin Pop
    by _GetShowData.
1239 3840729d Iustin Pop

1240 3840729d Iustin Pop
    """
1241 3840729d Iustin Pop
    data = {}
1242 a2cfdea2 Iustin Pop
    if not out:
1243 a2cfdea2 Iustin Pop
      return data
1244 a2cfdea2 Iustin Pop
1245 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1246 a2cfdea2 Iustin Pop
    # run pyparse
1247 a2cfdea2 Iustin Pop
1248 a2cfdea2 Iustin Pop
    try:
1249 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1250 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1251 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1252 a2cfdea2 Iustin Pop
1253 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1254 a2cfdea2 Iustin Pop
    for section in results:
1255 a2cfdea2 Iustin Pop
      sname = section[0]
1256 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1257 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1258 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1259 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1260 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1261 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1262 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1263 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1264 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1265 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1266 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1267 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1268 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1269 a2cfdea2 Iustin Pop
    return data
1270 a2cfdea2 Iustin Pop
1271 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1272 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1273 a2cfdea2 Iustin Pop

1274 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1275 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1276 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1277 a2cfdea2 Iustin Pop
    device.
1278 a2cfdea2 Iustin Pop

1279 a2cfdea2 Iustin Pop
    """
1280 b00b95dd Iustin Pop
    if self._children:
1281 b00b95dd Iustin Pop
      backend, meta = self._children
1282 b00b95dd Iustin Pop
    else:
1283 b00b95dd Iustin Pop
      backend = meta = None
1284 b00b95dd Iustin Pop
1285 a2cfdea2 Iustin Pop
    if backend is not None:
1286 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1287 a2cfdea2 Iustin Pop
    else:
1288 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1289 b00b95dd Iustin Pop
1290 a2cfdea2 Iustin Pop
    if meta is not None:
1291 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1292 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1293 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1294 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1295 a2cfdea2 Iustin Pop
    else:
1296 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1297 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1298 a2cfdea2 Iustin Pop
    return retval
1299 a2cfdea2 Iustin Pop
1300 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1301 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1302 a2cfdea2 Iustin Pop

1303 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1304 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1305 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1306 a2cfdea2 Iustin Pop
    device.
1307 a2cfdea2 Iustin Pop

1308 a2cfdea2 Iustin Pop
    """
1309 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1310 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1311 a2cfdea2 Iustin Pop
      return True
1312 a2cfdea2 Iustin Pop
1313 a2cfdea2 Iustin Pop
    if self._lhost is None:
1314 a2cfdea2 Iustin Pop
      return False
1315 a2cfdea2 Iustin Pop
1316 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1317 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1318 a2cfdea2 Iustin Pop
      return False
1319 a2cfdea2 Iustin Pop
1320 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1321 a2cfdea2 Iustin Pop
    retval = (retval and
1322 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1323 a2cfdea2 Iustin Pop
    return retval
1324 a2cfdea2 Iustin Pop
1325 a2cfdea2 Iustin Pop
  @classmethod
1326 f069addf Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta, size):
1327 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1328 a2cfdea2 Iustin Pop

1329 a2cfdea2 Iustin Pop
    """
1330 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1331 f069addf Iustin Pop
            backend, meta, "0",
1332 f069addf Iustin Pop
            "-e", "detach",
1333 f069addf Iustin Pop
            "--create-device"]
1334 60bca04a Iustin Pop
    if size:
1335 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1336 89b70f39 Iustin Pop
    if not constants.DRBD_BARRIERS: # disable barriers, if configured so
1337 fcee765d Manuel Franceschini
      version = cls._GetVersion(cls._GetProcData())
1338 89b70f39 Iustin Pop
      # various DRBD versions support different disk barrier options;
1339 89b70f39 Iustin Pop
      # what we aim here is to revert back to the 'drain' method of
1340 89b70f39 Iustin Pop
      # disk flushes and to disable metadata barriers, in effect going
1341 89b70f39 Iustin Pop
      # back to pre-8.0.7 behaviour
1342 89b70f39 Iustin Pop
      vmaj = version['k_major']
1343 89b70f39 Iustin Pop
      vmin = version['k_minor']
1344 89b70f39 Iustin Pop
      vrel = version['k_point']
1345 89b70f39 Iustin Pop
      assert vmaj == 8
1346 89b70f39 Iustin Pop
      if vmin == 0: # 8.0.x
1347 89b70f39 Iustin Pop
        if vrel >= 12:
1348 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1349 89b70f39 Iustin Pop
      elif vmin == 2: # 8.2.x
1350 89b70f39 Iustin Pop
        if vrel >= 7:
1351 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1352 89b70f39 Iustin Pop
      elif vmaj >= 3: # 8.3.x or newer
1353 89b70f39 Iustin Pop
        args.extend(['-i', '-a', 'm'])
1354 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1355 a2cfdea2 Iustin Pop
    if result.failed:
1356 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1357 a2cfdea2 Iustin Pop
1358 a2cfdea2 Iustin Pop
  @classmethod
1359 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1360 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1361 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1362 a2cfdea2 Iustin Pop

1363 a2cfdea2 Iustin Pop
    """
1364 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1365 52857176 Iustin Pop
    if None in net_info:
1366 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1367 52857176 Iustin Pop
      # sure its shutdown
1368 1063abd1 Iustin Pop
      cls._ShutdownNet(minor)
1369 1063abd1 Iustin Pop
      return
1370 52857176 Iustin Pop
1371 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1372 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1373 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1374 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1375 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1376 7d585316 Iustin Pop
    # about its peer.
1377 7d585316 Iustin Pop
    cls._SetMinorSyncSpeed(minor, constants.SYNC_SPEED)
1378 7d585316 Iustin Pop
1379 8b312c1d Manuel Franceschini
    if netutils.IP6Address.IsValid(lhost):
1380 8b312c1d Manuel Franceschini
      if not netutils.IP6Address.IsValid(rhost):
1381 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1382 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1383 5a672c30 Manuel Franceschini
      family = "ipv6"
1384 8b312c1d Manuel Franceschini
    elif netutils.IP4Address.IsValid(lhost):
1385 8b312c1d Manuel Franceschini
      if not netutils.IP4Address.IsValid(rhost):
1386 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1387 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1388 5a672c30 Manuel Franceschini
      family = "ipv4"
1389 5a672c30 Manuel Franceschini
    else:
1390 5a672c30 Manuel Franceschini
      _ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
1391 5a672c30 Manuel Franceschini
1392 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1393 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, lhost, lport),
1394 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, rhost, rport), protocol,
1395 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1396 f38478b2 Iustin Pop
            "-B", "consensus",
1397 ab6cc81c Iustin Pop
            "--create-device",
1398 f38478b2 Iustin Pop
            ]
1399 a2cfdea2 Iustin Pop
    if dual_pri:
1400 a2cfdea2 Iustin Pop
      args.append("-m")
1401 a2cfdea2 Iustin Pop
    if hmac and secret:
1402 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1403 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1404 a2cfdea2 Iustin Pop
    if result.failed:
1405 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1406 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1407 a2cfdea2 Iustin Pop
1408 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1409 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1410 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1411 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1412 def8e2f6 Michael Hanselmann
1413 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1414 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1415 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1416 def8e2f6 Michael Hanselmann
1417 def8e2f6 Michael Hanselmann
    try:
1418 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1419 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1420 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1421 a2cfdea2 Iustin Pop
1422 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1423 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1424 b00b95dd Iustin Pop

1425 b00b95dd Iustin Pop
    """
1426 b00b95dd Iustin Pop
    if self.minor is None:
1427 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1428 82463074 Iustin Pop
                  self._aminor)
1429 b00b95dd Iustin Pop
    if len(devices) != 2:
1430 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1431 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1432 03ece5f3 Iustin Pop
    if "local_dev" in info:
1433 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1434 b00b95dd Iustin Pop
    backend, meta = devices
1435 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1436 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1437 b00b95dd Iustin Pop
    backend.Open()
1438 b00b95dd Iustin Pop
    meta.Open()
1439 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1440 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1441 b00b95dd Iustin Pop
1442 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1443 b00b95dd Iustin Pop
    self._children = devices
1444 b00b95dd Iustin Pop
1445 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1446 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1447 b00b95dd Iustin Pop

1448 b00b95dd Iustin Pop
    """
1449 b00b95dd Iustin Pop
    if self.minor is None:
1450 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1451 82463074 Iustin Pop
                  self._aminor)
1452 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1453 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1454 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1455 03ece5f3 Iustin Pop
      return
1456 b00b95dd Iustin Pop
    if len(self._children) != 2:
1457 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1458 82463074 Iustin Pop
                  self._children)
1459 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1460 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1461 e739bd57 Iustin Pop
      return
1462 b00b95dd Iustin Pop
    if len(devices) != 2:
1463 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1464 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1465 e739bd57 Iustin Pop
      if dev != child.dev_path:
1466 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1467 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1468 b00b95dd Iustin Pop
1469 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1470 b00b95dd Iustin Pop
    self._children = []
1471 b00b95dd Iustin Pop
1472 7d585316 Iustin Pop
  @classmethod
1473 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1474 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1475 a2cfdea2 Iustin Pop

1476 7d585316 Iustin Pop
    This is the low-level implementation.
1477 7d585316 Iustin Pop

1478 7d585316 Iustin Pop
    @type minor: int
1479 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1480 7d585316 Iustin Pop
    @type kbytes: int
1481 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1482 7d585316 Iustin Pop
    @rtype: boolean
1483 7d585316 Iustin Pop
    @return: the success of the operation
1484 7d585316 Iustin Pop

1485 a2cfdea2 Iustin Pop
    """
1486 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1487 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1488 a2cfdea2 Iustin Pop
    if result.failed:
1489 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1490 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1491 7d585316 Iustin Pop
    return not result.failed
1492 7d585316 Iustin Pop
1493 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1494 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1495 7d585316 Iustin Pop

1496 7d585316 Iustin Pop
    @type kbytes: int
1497 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1498 7d585316 Iustin Pop
    @rtype: boolean
1499 7d585316 Iustin Pop
    @return: the success of the operation
1500 7d585316 Iustin Pop

1501 7d585316 Iustin Pop
    """
1502 7d585316 Iustin Pop
    if self.minor is None:
1503 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1504 7d585316 Iustin Pop
      return False
1505 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1506 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1507 a2cfdea2 Iustin Pop
1508 a3fffcc6 Renรฉ Nussbaumer
  def PauseResumeSync(self, pause):
1509 a3fffcc6 Renรฉ Nussbaumer
    """Pauses or resumes the sync of a DRBD device.
1510 a3fffcc6 Renรฉ Nussbaumer

1511 a3fffcc6 Renรฉ Nussbaumer
    @param pause: Wether to pause or resume
1512 a3fffcc6 Renรฉ Nussbaumer
    @return: the success of the operation
1513 a3fffcc6 Renรฉ Nussbaumer

1514 a3fffcc6 Renรฉ Nussbaumer
    """
1515 a3fffcc6 Renรฉ Nussbaumer
    if self.minor is None:
1516 a3fffcc6 Renรฉ Nussbaumer
      logging.info("Not attached during PauseSync")
1517 a3fffcc6 Renรฉ Nussbaumer
      return False
1518 a3fffcc6 Renรฉ Nussbaumer
1519 a3fffcc6 Renรฉ Nussbaumer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
1520 a3fffcc6 Renรฉ Nussbaumer
1521 a3fffcc6 Renรฉ Nussbaumer
    if pause:
1522 a3fffcc6 Renรฉ Nussbaumer
      cmd = "pause-sync"
1523 a3fffcc6 Renรฉ Nussbaumer
    else:
1524 a3fffcc6 Renรฉ Nussbaumer
      cmd = "resume-sync"
1525 a3fffcc6 Renรฉ Nussbaumer
1526 a3fffcc6 Renรฉ Nussbaumer
    result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
1527 a3fffcc6 Renรฉ Nussbaumer
    if result.failed:
1528 a3fffcc6 Renรฉ Nussbaumer
      logging.error("Can't %s: %s - %s", cmd,
1529 a3fffcc6 Renรฉ Nussbaumer
                    result.fail_reason, result.output)
1530 a3fffcc6 Renรฉ Nussbaumer
    return not result.failed and children_result
1531 a3fffcc6 Renรฉ Nussbaumer
1532 6b90c22e Iustin Pop
  def GetProcStatus(self):
1533 6b90c22e Iustin Pop
    """Return device data from /proc.
1534 6b90c22e Iustin Pop

1535 6b90c22e Iustin Pop
    """
1536 6b90c22e Iustin Pop
    if self.minor is None:
1537 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1538 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1539 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1540 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1541 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1542 6b90c22e Iustin Pop
1543 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1544 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1545 a2cfdea2 Iustin Pop

1546 a2cfdea2 Iustin Pop

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

1551 0834c866 Iustin Pop

1552 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1553 0834c866 Iustin Pop
    network not connected or local disk missing.
1554 0834c866 Iustin Pop

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

1558 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1559 c41eea6e Iustin Pop

1560 a2cfdea2 Iustin Pop
    """
1561 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1562 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1563 96acbc09 Michael Hanselmann
1564 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1565 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1566 f208978a Michael Hanselmann
1567 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1568 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1569 f208978a Michael Hanselmann
    elif stats.is_diskless:
1570 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1571 f208978a Michael Hanselmann
    else:
1572 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1573 96acbc09 Michael Hanselmann
1574 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1575 96acbc09 Michael Hanselmann
                                  major=self.major,
1576 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1577 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1578 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1579 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1580 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1581 a2cfdea2 Iustin Pop
1582 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1583 a2cfdea2 Iustin Pop
    """Make the local state primary.
1584 a2cfdea2 Iustin Pop

1585 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1586 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1587 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1588 f860ff4e Guido Trotter
    is mandatory.
1589 a2cfdea2 Iustin Pop

1590 a2cfdea2 Iustin Pop
    """
1591 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1592 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1593 a2cfdea2 Iustin Pop
      return False
1594 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1595 a2cfdea2 Iustin Pop
    if force:
1596 a2cfdea2 Iustin Pop
      cmd.append("-o")
1597 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1598 a2cfdea2 Iustin Pop
    if result.failed:
1599 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1600 82463074 Iustin Pop
                  result.output)
1601 a2cfdea2 Iustin Pop
1602 a2cfdea2 Iustin Pop
  def Close(self):
1603 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1604 a2cfdea2 Iustin Pop

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

1607 a2cfdea2 Iustin Pop
    """
1608 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1609 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1610 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1611 a2cfdea2 Iustin Pop
    if result.failed:
1612 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1613 82463074 Iustin Pop
                  self.minor, result.output)
1614 a2cfdea2 Iustin Pop
1615 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1616 cf8df3f3 Iustin Pop
    """Removes network configuration.
1617 cf8df3f3 Iustin Pop

1618 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1619 cf8df3f3 Iustin Pop

1620 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1621 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1622 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1623 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1624 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1625 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1626 cf8df3f3 Iustin Pop
    exception.
1627 cf8df3f3 Iustin Pop

1628 cf8df3f3 Iustin Pop
    """
1629 cf8df3f3 Iustin Pop
    if self.minor is None:
1630 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1631 cf8df3f3 Iustin Pop
1632 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1633 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1634 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1635 cf8df3f3 Iustin Pop
1636 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1637 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1638 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1639 cf8df3f3 Iustin Pop
1640 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1641 def8e2f6 Michael Hanselmann
1642 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1643 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1644 def8e2f6 Michael Hanselmann
        return
1645 def8e2f6 Michael Hanselmann
1646 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1647 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1648 def8e2f6 Michael Hanselmann
      # forgotten
1649 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1650 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1651 def8e2f6 Michael Hanselmann
1652 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1653 def8e2f6 Michael Hanselmann
1654 def8e2f6 Michael Hanselmann
    # Keep start time
1655 def8e2f6 Michael Hanselmann
    start_time = time.time()
1656 def8e2f6 Michael Hanselmann
1657 def8e2f6 Michael Hanselmann
    try:
1658 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1659 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1660 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1661 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1662 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1663 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1664 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1665 cf8df3f3 Iustin Pop
      else:
1666 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1667 def8e2f6 Michael Hanselmann
1668 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1669 cf8df3f3 Iustin Pop
1670 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1671 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1672 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1673 82463074 Iustin Pop
                   self.minor, reconfig_time)
1674 cf8df3f3 Iustin Pop
1675 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1676 cf8df3f3 Iustin Pop
    """Reconnects the network.
1677 cf8df3f3 Iustin Pop

1678 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1679 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1680 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1681 cf8df3f3 Iustin Pop

1682 cf8df3f3 Iustin Pop
    Args:
1683 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1684 cf8df3f3 Iustin Pop

1685 cf8df3f3 Iustin Pop
    """
1686 cf8df3f3 Iustin Pop
    if self.minor is None:
1687 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1688 cf8df3f3 Iustin Pop
1689 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1690 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1691 cf8df3f3 Iustin Pop
1692 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1693 cf8df3f3 Iustin Pop
1694 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1695 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1696 cf8df3f3 Iustin Pop
1697 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1698 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1699 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1700 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1701 cf8df3f3 Iustin Pop
1702 a2cfdea2 Iustin Pop
  def Attach(self):
1703 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1704 2d0c8319 Iustin Pop

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

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

1712 2d0c8319 Iustin Pop
    """
1713 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1714 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1715 2d0c8319 Iustin Pop
      minor = self._aminor
1716 2d0c8319 Iustin Pop
    else:
1717 2d0c8319 Iustin Pop
      minor = None
1718 2d0c8319 Iustin Pop
1719 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1720 2d0c8319 Iustin Pop
    return minor is not None
1721 2d0c8319 Iustin Pop
1722 2d0c8319 Iustin Pop
  def Assemble(self):
1723 2d0c8319 Iustin Pop
    """Assemble the drbd.
1724 2d0c8319 Iustin Pop

1725 2d0c8319 Iustin Pop
    Method:
1726 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1727 2d0c8319 Iustin Pop
        our config
1728 2d0c8319 Iustin Pop
      - if not, we create it from zero
1729 2d0c8319 Iustin Pop

1730 2d0c8319 Iustin Pop
    """
1731 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1732 2d0c8319 Iustin Pop
1733 2d0c8319 Iustin Pop
    self.Attach()
1734 2d0c8319 Iustin Pop
    if self.minor is None:
1735 2d0c8319 Iustin Pop
      # local device completely unconfigured
1736 1063abd1 Iustin Pop
      self._FastAssemble()
1737 2d0c8319 Iustin Pop
    else:
1738 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1739 2d0c8319 Iustin Pop
      # the device
1740 1063abd1 Iustin Pop
      self._SlowAssemble()
1741 2d0c8319 Iustin Pop
1742 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1743 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1744 a2cfdea2 Iustin Pop

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

1749 a2cfdea2 Iustin Pop
    """
1750 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
1751 527a15ac Iustin Pop
    # pylint: disable-msg=W0631
1752 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1753 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1754 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1755 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1756 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1757 1063abd1 Iustin Pop
1758 a2cfdea2 Iustin Pop
      if match_l and match_r:
1759 1063abd1 Iustin Pop
        # everything matches
1760 a2cfdea2 Iustin Pop
        break
1761 1063abd1 Iustin Pop
1762 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1763 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1764 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1765 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1766 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1767 1063abd1 Iustin Pop
          break
1768 1063abd1 Iustin Pop
        else:
1769 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1770 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1771 1063abd1 Iustin Pop
1772 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1773 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1774 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1775 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1776 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1777 1063abd1 Iustin Pop
          break
1778 1063abd1 Iustin Pop
        else:
1779 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1780 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1781 bf25af3b Iustin Pop
1782 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1783 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1784 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1785 bf25af3b Iustin Pop
      # configuration
1786 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1787 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1788 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1789 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1790 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1791 9cdbe77f Iustin Pop
        # reconnect to our correct one
1792 1063abd1 Iustin Pop
        try:
1793 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1794 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1795 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1796 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1797 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1798 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1799 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1800 9cdbe77f Iustin Pop
        # None)
1801 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1802 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1803 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1804 9cdbe77f Iustin Pop
          break
1805 1063abd1 Iustin Pop
        else:
1806 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1807 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1808 9cdbe77f Iustin Pop
1809 a2cfdea2 Iustin Pop
    else:
1810 a2cfdea2 Iustin Pop
      minor = None
1811 a2cfdea2 Iustin Pop
1812 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1813 1063abd1 Iustin Pop
    if minor is None:
1814 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1815 1063abd1 Iustin Pop
                  self._aminor)
1816 a2cfdea2 Iustin Pop
1817 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1818 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1819 a2cfdea2 Iustin Pop

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

1822 a2cfdea2 Iustin Pop
    """
1823 a1578d63 Iustin Pop
    minor = self._aminor
1824 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1825 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1826 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1827 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1828 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1829 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1830 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1831 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1832 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1833 a2cfdea2 Iustin Pop
1834 a2cfdea2 Iustin Pop
  @classmethod
1835 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1836 b00b95dd Iustin Pop
    """Detach from the local device.
1837 b00b95dd Iustin Pop

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

1841 b00b95dd Iustin Pop
    """
1842 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1843 b00b95dd Iustin Pop
    if result.failed:
1844 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1845 b00b95dd Iustin Pop
1846 b00b95dd Iustin Pop
  @classmethod
1847 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1848 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1849 f3e513ad Iustin Pop

1850 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1851 f3e513ad Iustin Pop

1852 f3e513ad Iustin Pop
    """
1853 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1854 a8459f1c Iustin Pop
    if result.failed:
1855 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1856 f3e513ad Iustin Pop
1857 f3e513ad Iustin Pop
  @classmethod
1858 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1859 a2cfdea2 Iustin Pop
    """Deactivate the device.
1860 a2cfdea2 Iustin Pop

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

1863 a2cfdea2 Iustin Pop
    """
1864 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1865 a2cfdea2 Iustin Pop
    if result.failed:
1866 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1867 33bc6f01 Iustin Pop
                  minor, result.output)
1868 a2cfdea2 Iustin Pop
1869 a2cfdea2 Iustin Pop
  def Shutdown(self):
1870 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1871 a2cfdea2 Iustin Pop

1872 a2cfdea2 Iustin Pop
    """
1873 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1874 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1875 746f7476 Iustin Pop
      return
1876 746f7476 Iustin Pop
    minor = self.minor
1877 a2cfdea2 Iustin Pop
    self.minor = None
1878 a2cfdea2 Iustin Pop
    self.dev_path = None
1879 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1880 a2cfdea2 Iustin Pop
1881 a2cfdea2 Iustin Pop
  def Remove(self):
1882 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1883 a2cfdea2 Iustin Pop

1884 a2cfdea2 Iustin Pop
    """
1885 0c6c04ec Iustin Pop
    self.Shutdown()
1886 a2cfdea2 Iustin Pop
1887 a2cfdea2 Iustin Pop
  @classmethod
1888 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1889 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1890 a2cfdea2 Iustin Pop

1891 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1892 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1893 a2cfdea2 Iustin Pop

1894 a2cfdea2 Iustin Pop
    """
1895 a2cfdea2 Iustin Pop
    if len(children) != 2:
1896 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1897 767d52d3 Iustin Pop
    # check that the minor is unused
1898 767d52d3 Iustin Pop
    aminor = unique_id[4]
1899 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1900 767d52d3 Iustin Pop
    if aminor in proc_info:
1901 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1902 767d52d3 Iustin Pop
      in_use = status.is_in_use
1903 767d52d3 Iustin Pop
    else:
1904 767d52d3 Iustin Pop
      in_use = False
1905 767d52d3 Iustin Pop
    if in_use:
1906 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1907 a2cfdea2 Iustin Pop
    meta = children[1]
1908 a2cfdea2 Iustin Pop
    meta.Assemble()
1909 a2cfdea2 Iustin Pop
    if not meta.Attach():
1910 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1911 33bc6f01 Iustin Pop
                  aminor, meta)
1912 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1913 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1914 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1915 a2cfdea2 Iustin Pop
1916 1005d816 Iustin Pop
  def Grow(self, amount):
1917 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1918 1005d816 Iustin Pop

1919 1005d816 Iustin Pop
    """
1920 1005d816 Iustin Pop
    if self.minor is None:
1921 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1922 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1923 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1924 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1925 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1926 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1927 1005d816 Iustin Pop
    if result.failed:
1928 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1929 1005d816 Iustin Pop
1930 a8083063 Iustin Pop
1931 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1932 6f695a2e Manuel Franceschini
  """File device.
1933 abdf0113 Iustin Pop

1934 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1935 6f695a2e Manuel Franceschini

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

1938 6f695a2e Manuel Franceschini
  """
1939 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1940 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1941 6f695a2e Manuel Franceschini

1942 6f695a2e Manuel Franceschini
    """
1943 6f695a2e Manuel Franceschini
    if children:
1944 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1945 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1946 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1947 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1948 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1949 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1950 ecb091e3 Iustin Pop
    self.Attach()
1951 6f695a2e Manuel Franceschini
1952 6f695a2e Manuel Franceschini
  def Assemble(self):
1953 6f695a2e Manuel Franceschini
    """Assemble the device.
1954 6f695a2e Manuel Franceschini

1955 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1956 6f695a2e Manuel Franceschini

1957 6f695a2e Manuel Franceschini
    """
1958 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1959 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1960 6f695a2e Manuel Franceschini
1961 6f695a2e Manuel Franceschini
  def Shutdown(self):
1962 6f695a2e Manuel Franceschini
    """Shutdown the device.
1963 6f695a2e Manuel Franceschini

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

1967 6f695a2e Manuel Franceschini
    """
1968 746f7476 Iustin Pop
    pass
1969 6f695a2e Manuel Franceschini
1970 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1971 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1972 6f695a2e Manuel Franceschini

1973 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1974 6f695a2e Manuel Franceschini

1975 6f695a2e Manuel Franceschini
    """
1976 6f695a2e Manuel Franceschini
    pass
1977 6f695a2e Manuel Franceschini
1978 6f695a2e Manuel Franceschini
  def Close(self):
1979 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1980 6f695a2e Manuel Franceschini

1981 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1982 6f695a2e Manuel Franceschini

1983 6f695a2e Manuel Franceschini
    """
1984 6f695a2e Manuel Franceschini
    pass
1985 6f695a2e Manuel Franceschini
1986 6f695a2e Manuel Franceschini
  def Remove(self):
1987 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1988 6f695a2e Manuel Franceschini

1989 c41eea6e Iustin Pop
    @rtype: boolean
1990 c41eea6e Iustin Pop
    @return: True if the removal was successful
1991 6f695a2e Manuel Franceschini

1992 6f695a2e Manuel Franceschini
    """
1993 6f695a2e Manuel Franceschini
    try:
1994 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1995 6f695a2e Manuel Franceschini
    except OSError, err:
1996 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1997 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1998 6f695a2e Manuel Franceschini
1999 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
2000 bbe4cc16 Iustin Pop
    """Renames the file.
2001 bbe4cc16 Iustin Pop

2002 bbe4cc16 Iustin Pop
    """
2003 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
2004 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
2005 bbe4cc16 Iustin Pop
2006 bbe4cc16 Iustin Pop
  def Grow(self, amount):
2007 bbe4cc16 Iustin Pop
    """Grow the file
2008 bbe4cc16 Iustin Pop

2009 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2010 bbe4cc16 Iustin Pop

2011 bbe4cc16 Iustin Pop
    """
2012 91e2d9ec Guido Trotter
    # Check that the file exists
2013 91e2d9ec Guido Trotter
    self.Assemble()
2014 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
2015 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
2016 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
2017 91e2d9ec Guido Trotter
    try:
2018 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
2019 91e2d9ec Guido Trotter
      f.truncate(new_size)
2020 91e2d9ec Guido Trotter
      f.close()
2021 91e2d9ec Guido Trotter
    except EnvironmentError, err:
2022 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
2023 bbe4cc16 Iustin Pop
2024 6f695a2e Manuel Franceschini
  def Attach(self):
2025 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2026 6f695a2e Manuel Franceschini

2027 6f695a2e Manuel Franceschini
    Check if this file already exists.
2028 6f695a2e Manuel Franceschini

2029 c41eea6e Iustin Pop
    @rtype: boolean
2030 c41eea6e Iustin Pop
    @return: True if file exists
2031 6f695a2e Manuel Franceschini

2032 6f695a2e Manuel Franceschini
    """
2033 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
2034 ecb091e3 Iustin Pop
    return self.attached
2035 6f695a2e Manuel Franceschini
2036 fcff3897 Iustin Pop
  def GetActualSize(self):
2037 fcff3897 Iustin Pop
    """Return the actual disk size.
2038 fcff3897 Iustin Pop

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

2041 fcff3897 Iustin Pop
    """
2042 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2043 fcff3897 Iustin Pop
    try:
2044 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2045 fcff3897 Iustin Pop
      return st.st_size
2046 fcff3897 Iustin Pop
    except OSError, err:
2047 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2048 fcff3897 Iustin Pop
2049 6f695a2e Manuel Franceschini
  @classmethod
2050 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
2051 6f695a2e Manuel Franceschini
    """Create a new file.
2052 6f695a2e Manuel Franceschini

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

2055 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2056 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2057 6f695a2e Manuel Franceschini

2058 6f695a2e Manuel Franceschini
    """
2059 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2060 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2061 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2062 6f695a2e Manuel Franceschini
    try:
2063 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
2064 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
2065 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2066 6f695a2e Manuel Franceschini
      f.close()
2067 cdeefd9b Guido Trotter
    except EnvironmentError, err:
2068 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
2069 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
2070 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
2071 6f695a2e Manuel Franceschini
2072 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
2073 6f695a2e Manuel Franceschini
2074 6f695a2e Manuel Franceschini
2075 a8083063 Iustin Pop
DEV_MAP = {
2076 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2077 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2078 a8083063 Iustin Pop
  }
2079 a8083063 Iustin Pop
2080 cb7c0198 Iustin Pop
if constants.ENABLE_FILE_STORAGE:
2081 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
2082 cb7c0198 Iustin Pop
2083 a8083063 Iustin Pop
2084 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
2085 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2086 a8083063 Iustin Pop

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

2090 a8083063 Iustin Pop
  """
2091 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2092 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2093 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
2094 cb999543 Iustin Pop
  if not device.attached:
2095 a8083063 Iustin Pop
    return None
2096 ecb091e3 Iustin Pop
  return device
2097 a8083063 Iustin Pop
2098 a8083063 Iustin Pop
2099 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
2100 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2101 a8083063 Iustin Pop

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

2105 a8083063 Iustin Pop
  """
2106 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2107 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2108 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
2109 1063abd1 Iustin Pop
  device.Assemble()
2110 a8083063 Iustin Pop
  return device
2111 a8083063 Iustin Pop
2112 a8083063 Iustin Pop
2113 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2114 a8083063 Iustin Pop
  """Create a device.
2115 a8083063 Iustin Pop

2116 a8083063 Iustin Pop
  """
2117 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2118 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2119 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2120 a8083063 Iustin Pop
  return device