Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 82599b3e

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

886 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
887 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
888 0f7f32d9 Iustin Pop

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

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

924 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
925 c41eea6e Iustin Pop
        for that minor
926 a8083063 Iustin Pop

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

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

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

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

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

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

1025 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1026 a8083063 Iustin Pop

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

1040 abdf0113 Iustin Pop
    This currently only check the size, which must be around
1041 abdf0113 Iustin Pop
    128MiB.
1042 a8083063 Iustin Pop

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

1067 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1068 a8083063 Iustin Pop

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

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

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

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

1124 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1125 a2cfdea2 Iustin Pop

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

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

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

1162 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1163 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1164 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

1473 7d585316 Iustin Pop
    This is the low-level implementation.
1474 7d585316 Iustin Pop

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

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

1493 7d585316 Iustin Pop
    @type kbytes: int
1494 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1495 7d585316 Iustin Pop
    @rtype: boolean
1496 7d585316 Iustin Pop
    @return: the success of the operation
1497 7d585316 Iustin Pop

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

1508 a3fffcc6 Renรฉ Nussbaumer
    @param pause: Wether to pause or resume
1509 a3fffcc6 Renรฉ Nussbaumer
    @return: the success of the operation
1510 a3fffcc6 Renรฉ Nussbaumer

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

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

1543 a2cfdea2 Iustin Pop

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

1548 0834c866 Iustin Pop

1549 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1550 0834c866 Iustin Pop
    network not connected or local disk missing.
1551 0834c866 Iustin Pop

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

1555 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1556 c41eea6e Iustin Pop

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

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

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

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

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

1615 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1616 cf8df3f3 Iustin Pop

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

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

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

1679 cf8df3f3 Iustin Pop
    Args:
1680 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1681 cf8df3f3 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

1847 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1848 f3e513ad Iustin Pop

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

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

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

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

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

1888 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1889 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1890 a2cfdea2 Iustin Pop

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

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

1931 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1932 6f695a2e Manuel Franceschini

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

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

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

1952 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1953 6f695a2e Manuel Franceschini

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

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

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

1970 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1971 6f695a2e Manuel Franceschini

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

1978 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1979 6f695a2e Manuel Franceschini

1980 6f695a2e Manuel Franceschini
    """
1981 6f695a2e Manuel Franceschini
    pass
1982 6f695a2e Manuel Franceschini
1983 6f695a2e Manuel Franceschini
  def Remove(self):
1984 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1985 6f695a2e Manuel Franceschini

1986 c41eea6e Iustin Pop
    @rtype: boolean
1987 c41eea6e Iustin Pop
    @return: True if the removal was successful
1988 6f695a2e Manuel Franceschini

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

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

2006 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2007 bbe4cc16 Iustin Pop

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

2024 6f695a2e Manuel Franceschini
    Check if this file already exists.
2025 6f695a2e Manuel Franceschini

2026 c41eea6e Iustin Pop
    @rtype: boolean
2027 c41eea6e Iustin Pop
    @return: True if file exists
2028 6f695a2e Manuel Franceschini

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

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

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

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

2052 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2053 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2054 6f695a2e Manuel Franceschini

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

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

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

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

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

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