Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 999b183c

History | View | Annotate | Download (64.9 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Block device abstraction"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import re
25 a8083063 Iustin Pop
import time
26 a8083063 Iustin Pop
import errno
27 a2cfdea2 Iustin Pop
import pyparsing as pyp
28 6f695a2e Manuel Franceschini
import os
29 468c5f77 Iustin Pop
import logging
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
from ganeti import utils
32 a8083063 Iustin Pop
from ganeti import errors
33 fe96220b Iustin Pop
from ganeti import constants
34 96acbc09 Michael Hanselmann
from ganeti import objects
35 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 a8083063 Iustin Pop
  def GetSyncStatus(self):
234 a8083063 Iustin Pop
    """Returns the sync status of the device.
235 a8083063 Iustin Pop

236 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
237 a8083063 Iustin Pop
    status of the mirror.
238 a8083063 Iustin Pop

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

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

244 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
245 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
246 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
247 a8083063 Iustin Pop

248 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
249 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
250 0834c866 Iustin Pop
    return False (not degraded).
251 0834c866 Iustin Pop

252 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
253 c41eea6e Iustin Pop

254 a8083063 Iustin Pop
    """
255 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
256 96acbc09 Michael Hanselmann
                                  major=self.major,
257 96acbc09 Michael Hanselmann
                                  minor=self.minor,
258 96acbc09 Michael Hanselmann
                                  sync_percent=None,
259 96acbc09 Michael Hanselmann
                                  estimated_time=None,
260 96acbc09 Michael Hanselmann
                                  is_degraded=False,
261 f208978a Michael Hanselmann
                                  ldisk_status=constants.LDS_OKAY)
262 a8083063 Iustin Pop
263 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
264 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
265 a8083063 Iustin Pop

266 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
267 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
268 a8083063 Iustin Pop
    children.
269 a8083063 Iustin Pop

270 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
271 96acbc09 Michael Hanselmann

272 a8083063 Iustin Pop
    """
273 96acbc09 Michael Hanselmann
    status = self.GetSyncStatus()
274 96acbc09 Michael Hanselmann
275 96acbc09 Michael Hanselmann
    min_percent = status.sync_percent
276 96acbc09 Michael Hanselmann
    max_time = status.estimated_time
277 96acbc09 Michael Hanselmann
    is_degraded = status.is_degraded
278 f208978a Michael Hanselmann
    ldisk_status = status.ldisk_status
279 96acbc09 Michael Hanselmann
280 a8083063 Iustin Pop
    if self._children:
281 a8083063 Iustin Pop
      for child in self._children:
282 96acbc09 Michael Hanselmann
        child_status = child.GetSyncStatus()
283 96acbc09 Michael Hanselmann
284 a8083063 Iustin Pop
        if min_percent is None:
285 96acbc09 Michael Hanselmann
          min_percent = child_status.sync_percent
286 96acbc09 Michael Hanselmann
        elif child_status.sync_percent is not None:
287 96acbc09 Michael Hanselmann
          min_percent = min(min_percent, child_status.sync_percent)
288 96acbc09 Michael Hanselmann
289 a8083063 Iustin Pop
        if max_time is None:
290 96acbc09 Michael Hanselmann
          max_time = child_status.estimated_time
291 96acbc09 Michael Hanselmann
        elif child_status.estimated_time is not None:
292 96acbc09 Michael Hanselmann
          max_time = max(max_time, child_status.estimated_time)
293 96acbc09 Michael Hanselmann
294 96acbc09 Michael Hanselmann
        is_degraded = is_degraded or child_status.is_degraded
295 f208978a Michael Hanselmann
296 f208978a Michael Hanselmann
        if ldisk_status is None:
297 f208978a Michael Hanselmann
          ldisk_status = child_status.ldisk_status
298 f208978a Michael Hanselmann
        elif child_status.ldisk_status is not None:
299 f208978a Michael Hanselmann
          ldisk_status = max(ldisk_status, child_status.ldisk_status)
300 96acbc09 Michael Hanselmann
301 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
302 96acbc09 Michael Hanselmann
                                  major=self.major,
303 96acbc09 Michael Hanselmann
                                  minor=self.minor,
304 96acbc09 Michael Hanselmann
                                  sync_percent=min_percent,
305 96acbc09 Michael Hanselmann
                                  estimated_time=max_time,
306 96acbc09 Michael Hanselmann
                                  is_degraded=is_degraded,
307 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
308 a8083063 Iustin Pop
309 a8083063 Iustin Pop
310 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
311 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
312 a0c3fea1 Michael Hanselmann

313 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
314 a0c3fea1 Michael Hanselmann

315 a0c3fea1 Michael Hanselmann
    """
316 a0c3fea1 Michael Hanselmann
    for child in self._children:
317 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
318 a0c3fea1 Michael Hanselmann
319 1005d816 Iustin Pop
  def Grow(self, amount):
320 1005d816 Iustin Pop
    """Grow the block device.
321 1005d816 Iustin Pop

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

324 1005d816 Iustin Pop
    """
325 1005d816 Iustin Pop
    raise NotImplementedError
326 a0c3fea1 Michael Hanselmann
327 fcff3897 Iustin Pop
  def GetActualSize(self):
328 fcff3897 Iustin Pop
    """Return the actual disk size.
329 fcff3897 Iustin Pop

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

332 fcff3897 Iustin Pop
    """
333 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
334 fcff3897 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize64", self.dev_path])
335 fcff3897 Iustin Pop
    if result.failed:
336 fcff3897 Iustin Pop
      _ThrowError("blockdev failed (%s): %s",
337 fcff3897 Iustin Pop
                  result.fail_reason, result.output)
338 fcff3897 Iustin Pop
    try:
339 fcff3897 Iustin Pop
      sz = int(result.output.strip())
340 fcff3897 Iustin Pop
    except (ValueError, TypeError), err:
341 fcff3897 Iustin Pop
      _ThrowError("Failed to parse blockdev output: %s", str(err))
342 fcff3897 Iustin Pop
    return sz
343 fcff3897 Iustin Pop
344 a8083063 Iustin Pop
  def __repr__(self):
345 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
346 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
347 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
348 a8083063 Iustin Pop
349 a8083063 Iustin Pop
350 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
351 a8083063 Iustin Pop
  """Logical Volume block device.
352 a8083063 Iustin Pop

353 a8083063 Iustin Pop
  """
354 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
355 6136f8f0 Iustin Pop
  _INVALID_NAMES = frozenset([".", "..", "snapshot", "pvmove"])
356 6136f8f0 Iustin Pop
  _INVALID_SUBSTRINGS = frozenset(["_mlog", "_mimage"])
357 6136f8f0 Iustin Pop
358 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
359 a8083063 Iustin Pop
    """Attaches to a LV device.
360 a8083063 Iustin Pop

361 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
362 a8083063 Iustin Pop

363 a8083063 Iustin Pop
    """
364 464f8daf Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children, size)
365 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
366 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
367 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
368 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
369 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
370 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
371 99e8295c Iustin Pop
    self._degraded = True
372 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
373 a8083063 Iustin Pop
    self.Attach()
374 a8083063 Iustin Pop
375 a8083063 Iustin Pop
  @classmethod
376 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
377 a8083063 Iustin Pop
    """Create a new logical volume.
378 a8083063 Iustin Pop

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

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

427 c41eea6e Iustin Pop
    @rtype: list
428 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
429 098c0958 Michael Hanselmann

430 a8083063 Iustin Pop
    """
431 96c7a5b0 Iustin Pop
    sep = "|"
432 a8083063 Iustin Pop
    command = ["pvs", "--noheadings", "--nosuffix", "--units=m",
433 a8083063 Iustin Pop
               "-opv_name,vg_name,pv_free,pv_attr", "--unbuffered",
434 96c7a5b0 Iustin Pop
               "--separator=%s" % sep ]
435 a8083063 Iustin Pop
    result = utils.RunCmd(command)
436 a8083063 Iustin Pop
    if result.failed:
437 468c5f77 Iustin Pop
      logging.error("Can't get the PV information: %s - %s",
438 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
439 a8083063 Iustin Pop
      return None
440 a8083063 Iustin Pop
    data = []
441 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
442 96c7a5b0 Iustin Pop
      fields = line.strip().split(sep)
443 a8083063 Iustin Pop
      if len(fields) != 4:
444 468c5f77 Iustin Pop
        logging.error("Can't parse pvs output: line '%s'", line)
445 a8083063 Iustin Pop
        return None
446 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
447 2070598f Iustin Pop
      if filter_allocatable and fields[3][0] != 'a':
448 a8083063 Iustin Pop
        continue
449 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
450 2070598f Iustin Pop
      if vg_names and fields[1] not in vg_names:
451 2070598f Iustin Pop
        continue
452 2070598f Iustin Pop
      data.append((float(fields[2]), fields[0], fields[1]))
453 a8083063 Iustin Pop
454 a8083063 Iustin Pop
    return data
455 a8083063 Iustin Pop
456 6136f8f0 Iustin Pop
  @classmethod
457 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
458 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
459 6136f8f0 Iustin Pop

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

464 6136f8f0 Iustin Pop
    """
465 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
466 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
467 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
468 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
469 6136f8f0 Iustin Pop
470 a8083063 Iustin Pop
  def Remove(self):
471 a8083063 Iustin Pop
    """Remove this logical volume.
472 a8083063 Iustin Pop

473 a8083063 Iustin Pop
    """
474 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
475 a8083063 Iustin Pop
      # the LV does not exist
476 0c6c04ec Iustin Pop
      return
477 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
478 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
479 a8083063 Iustin Pop
    if result.failed:
480 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
481 a8083063 Iustin Pop
482 f3e513ad Iustin Pop
  def Rename(self, new_id):
483 f3e513ad Iustin Pop
    """Rename this logical volume.
484 f3e513ad Iustin Pop

485 f3e513ad Iustin Pop
    """
486 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
487 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
488 f3e513ad Iustin Pop
    new_vg, new_name = new_id
489 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
490 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
491 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
492 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
493 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
494 f3e513ad Iustin Pop
    if result.failed:
495 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
496 be345db0 Iustin Pop
    self._lv_name = new_name
497 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
498 be345db0 Iustin Pop
499 a8083063 Iustin Pop
  def Attach(self):
500 a8083063 Iustin Pop
    """Attach to an existing LV.
501 a8083063 Iustin Pop

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

506 a8083063 Iustin Pop
    """
507 cb999543 Iustin Pop
    self.attached = False
508 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
509 38256320 Iustin Pop
                           "--units=m", "--nosuffix",
510 38256320 Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
511 38256320 Iustin Pop
                           "vg_extent_size,stripes", self.dev_path])
512 a8083063 Iustin Pop
    if result.failed:
513 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
514 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
515 a8083063 Iustin Pop
      return False
516 38256320 Iustin Pop
    # the output can (and will) have multiple lines for multi-segment
517 38256320 Iustin Pop
    # LVs, as the 'stripes' parameter is a segment one, so we take
518 38256320 Iustin Pop
    # only the last entry, which is the one we're interested in; note
519 38256320 Iustin Pop
    # that with LVM2 anyway the 'stripes' value must be constant
520 38256320 Iustin Pop
    # across segments, so this is a no-op actually
521 38256320 Iustin Pop
    out = result.stdout.splitlines()
522 38256320 Iustin Pop
    if not out: # totally empty result? splitlines() returns at least
523 38256320 Iustin Pop
                # one line for any non-empty string
524 38256320 Iustin Pop
      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
525 38256320 Iustin Pop
      return False
526 38256320 Iustin Pop
    out = out[-1].strip().rstrip(',')
527 99e8295c Iustin Pop
    out = out.split(",")
528 38256320 Iustin Pop
    if len(out) != 5:
529 38256320 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
530 99e8295c Iustin Pop
      return False
531 99e8295c Iustin Pop
532 38256320 Iustin Pop
    status, major, minor, pe_size, stripes = out
533 99e8295c Iustin Pop
    if len(status) != 6:
534 468c5f77 Iustin Pop
      logging.error("lvs lv_attr is not 6 characters (%s)", status)
535 99e8295c Iustin Pop
      return False
536 99e8295c Iustin Pop
537 99e8295c Iustin Pop
    try:
538 99e8295c Iustin Pop
      major = int(major)
539 99e8295c Iustin Pop
      minor = int(minor)
540 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
541 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
542 99e8295c Iustin Pop
543 38256320 Iustin Pop
    try:
544 38256320 Iustin Pop
      pe_size = int(float(pe_size))
545 38256320 Iustin Pop
    except (TypeError, ValueError), err:
546 38256320 Iustin Pop
      logging.error("Can't parse vg extent size: %s", err)
547 38256320 Iustin Pop
      return False
548 38256320 Iustin Pop
549 38256320 Iustin Pop
    try:
550 38256320 Iustin Pop
      stripes = int(stripes)
551 38256320 Iustin Pop
    except (TypeError, ValueError), err:
552 38256320 Iustin Pop
      logging.error("Can't parse the number of stripes: %s", err)
553 38256320 Iustin Pop
      return False
554 38256320 Iustin Pop
555 99e8295c Iustin Pop
    self.major = major
556 99e8295c Iustin Pop
    self.minor = minor
557 38256320 Iustin Pop
    self.pe_size = pe_size
558 38256320 Iustin Pop
    self.stripe_count = stripes
559 99e8295c Iustin Pop
    self._degraded = status[0] == 'v' # virtual volume, i.e. doesn't backing
560 99e8295c Iustin Pop
                                      # storage
561 cb999543 Iustin Pop
    self.attached = True
562 99e8295c Iustin Pop
    return True
563 a8083063 Iustin Pop
564 a8083063 Iustin Pop
  def Assemble(self):
565 a8083063 Iustin Pop
    """Assemble the device.
566 a8083063 Iustin Pop

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

571 a8083063 Iustin Pop
    """
572 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
573 5574047a Iustin Pop
    if result.failed:
574 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
575 a8083063 Iustin Pop
576 a8083063 Iustin Pop
  def Shutdown(self):
577 a8083063 Iustin Pop
    """Shutdown the device.
578 a8083063 Iustin Pop

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

582 a8083063 Iustin Pop
    """
583 746f7476 Iustin Pop
    pass
584 a8083063 Iustin Pop
585 9db6dbce Iustin Pop
  def GetSyncStatus(self):
586 9db6dbce Iustin Pop
    """Returns the sync status of the device.
587 9db6dbce Iustin Pop

588 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
589 9db6dbce Iustin Pop
    status of the mirror.
590 9db6dbce Iustin Pop

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

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

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

604 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
605 c41eea6e Iustin Pop

606 9db6dbce Iustin Pop
    """
607 f208978a Michael Hanselmann
    if self._degraded:
608 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
609 f208978a Michael Hanselmann
    else:
610 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
611 f208978a Michael Hanselmann
612 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
613 96acbc09 Michael Hanselmann
                                  major=self.major,
614 96acbc09 Michael Hanselmann
                                  minor=self.minor,
615 96acbc09 Michael Hanselmann
                                  sync_percent=None,
616 96acbc09 Michael Hanselmann
                                  estimated_time=None,
617 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
618 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
619 9db6dbce Iustin Pop
620 a8083063 Iustin Pop
  def Open(self, force=False):
621 a8083063 Iustin Pop
    """Make the device ready for I/O.
622 a8083063 Iustin Pop

623 a8083063 Iustin Pop
    This is a no-op for the LV device type.
624 a8083063 Iustin Pop

625 a8083063 Iustin Pop
    """
626 fdbd668d Iustin Pop
    pass
627 a8083063 Iustin Pop
628 a8083063 Iustin Pop
  def Close(self):
629 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
630 a8083063 Iustin Pop

631 a8083063 Iustin Pop
    This is a no-op for the LV device type.
632 a8083063 Iustin Pop

633 a8083063 Iustin Pop
    """
634 fdbd668d Iustin Pop
    pass
635 a8083063 Iustin Pop
636 a8083063 Iustin Pop
  def Snapshot(self, size):
637 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
638 a8083063 Iustin Pop

639 a8083063 Iustin Pop
    """
640 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
641 a8083063 Iustin Pop
642 a8083063 Iustin Pop
    # remove existing snapshot if found
643 464f8daf Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None, size)
644 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
645 a8083063 Iustin Pop
646 2070598f Iustin Pop
    pvs_info = self.GetPVInfo([self._vg_name])
647 a8083063 Iustin Pop
    if not pvs_info:
648 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", self._vg_name)
649 a8083063 Iustin Pop
    pvs_info.sort()
650 a8083063 Iustin Pop
    pvs_info.reverse()
651 1122eb25 Iustin Pop
    free_size, _, _ = pvs_info[0]
652 a8083063 Iustin Pop
    if free_size < size:
653 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
654 82463074 Iustin Pop
                  " available %s", size, free_size)
655 a8083063 Iustin Pop
656 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
657 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
658 a8083063 Iustin Pop
    if result.failed:
659 82463074 Iustin Pop
      _ThrowError("command: %s error: %s - %s",
660 82463074 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
661 a8083063 Iustin Pop
662 a8083063 Iustin Pop
    return snap_name
663 a8083063 Iustin Pop
664 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
665 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
666 a0c3fea1 Michael Hanselmann

667 a0c3fea1 Michael Hanselmann
    """
668 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
669 a0c3fea1 Michael Hanselmann
670 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
671 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
672 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
673 a0c3fea1 Michael Hanselmann
674 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
675 a0c3fea1 Michael Hanselmann
    text = text[:128]
676 a0c3fea1 Michael Hanselmann
677 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
678 a0c3fea1 Michael Hanselmann
                           self.dev_path])
679 a0c3fea1 Michael Hanselmann
    if result.failed:
680 82463074 Iustin Pop
      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
681 82463074 Iustin Pop
                  result.output)
682 82463074 Iustin Pop
683 1005d816 Iustin Pop
  def Grow(self, amount):
684 1005d816 Iustin Pop
    """Grow the logical volume.
685 1005d816 Iustin Pop

686 1005d816 Iustin Pop
    """
687 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
688 38256320 Iustin Pop
      if not self.Attach():
689 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
690 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
691 38256320 Iustin Pop
    rest = amount % full_stripe_size
692 38256320 Iustin Pop
    if rest != 0:
693 38256320 Iustin Pop
      amount += full_stripe_size - rest
694 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
695 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
696 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
697 1005d816 Iustin Pop
    # supports 'cling'
698 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
699 1005d816 Iustin Pop
      result = utils.RunCmd(["lvextend", "--alloc", alloc_policy,
700 1005d816 Iustin Pop
                             "-L", "+%dm" % amount, self.dev_path])
701 1005d816 Iustin Pop
      if not result.failed:
702 1005d816 Iustin Pop
        return
703 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
704 a0c3fea1 Michael Hanselmann
705 a0c3fea1 Michael Hanselmann
706 6b90c22e Iustin Pop
class DRBD8Status(object):
707 6b90c22e Iustin Pop
  """A DRBD status representation class.
708 6b90c22e Iustin Pop

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

711 6b90c22e Iustin Pop
  """
712 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
713 01e2ce3a Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
714 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
715 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
716 6b90c22e Iustin Pop
                       "\sfinish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
717 6b90c22e Iustin Pop
718 3c003d9d Iustin Pop
  CS_UNCONFIGURED = "Unconfigured"
719 3c003d9d Iustin Pop
  CS_STANDALONE = "StandAlone"
720 3c003d9d Iustin Pop
  CS_WFCONNECTION = "WFConnection"
721 3c003d9d Iustin Pop
  CS_WFREPORTPARAMS = "WFReportParams"
722 3c003d9d Iustin Pop
  CS_CONNECTED = "Connected"
723 3c003d9d Iustin Pop
  CS_STARTINGSYNCS = "StartingSyncS"
724 3c003d9d Iustin Pop
  CS_STARTINGSYNCT = "StartingSyncT"
725 3c003d9d Iustin Pop
  CS_WFBITMAPS = "WFBitMapS"
726 3c003d9d Iustin Pop
  CS_WFBITMAPT = "WFBitMapT"
727 3c003d9d Iustin Pop
  CS_WFSYNCUUID = "WFSyncUUID"
728 3c003d9d Iustin Pop
  CS_SYNCSOURCE = "SyncSource"
729 3c003d9d Iustin Pop
  CS_SYNCTARGET = "SyncTarget"
730 3c003d9d Iustin Pop
  CS_PAUSEDSYNCS = "PausedSyncS"
731 3c003d9d Iustin Pop
  CS_PAUSEDSYNCT = "PausedSyncT"
732 3c003d9d Iustin Pop
  CSET_SYNC = frozenset([
733 3c003d9d Iustin Pop
    CS_WFREPORTPARAMS,
734 3c003d9d Iustin Pop
    CS_STARTINGSYNCS,
735 3c003d9d Iustin Pop
    CS_STARTINGSYNCT,
736 3c003d9d Iustin Pop
    CS_WFBITMAPS,
737 3c003d9d Iustin Pop
    CS_WFBITMAPT,
738 3c003d9d Iustin Pop
    CS_WFSYNCUUID,
739 3c003d9d Iustin Pop
    CS_SYNCSOURCE,
740 3c003d9d Iustin Pop
    CS_SYNCTARGET,
741 3c003d9d Iustin Pop
    CS_PAUSEDSYNCS,
742 3c003d9d Iustin Pop
    CS_PAUSEDSYNCT,
743 3c003d9d Iustin Pop
    ])
744 3c003d9d Iustin Pop
745 3c003d9d Iustin Pop
  DS_DISKLESS = "Diskless"
746 3c003d9d Iustin Pop
  DS_ATTACHING = "Attaching" # transient state
747 3c003d9d Iustin Pop
  DS_FAILED = "Failed" # transient state, next: diskless
748 3c003d9d Iustin Pop
  DS_NEGOTIATING = "Negotiating" # transient state
749 3c003d9d Iustin Pop
  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
750 3c003d9d Iustin Pop
  DS_OUTDATED = "Outdated"
751 3c003d9d Iustin Pop
  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
752 3c003d9d Iustin Pop
  DS_CONSISTENT = "Consistent"
753 3c003d9d Iustin Pop
  DS_UPTODATE = "UpToDate" # normal state
754 3c003d9d Iustin Pop
755 3c003d9d Iustin Pop
  RO_PRIMARY = "Primary"
756 3c003d9d Iustin Pop
  RO_SECONDARY = "Secondary"
757 3c003d9d Iustin Pop
  RO_UNKNOWN = "Unknown"
758 3c003d9d Iustin Pop
759 6b90c22e Iustin Pop
  def __init__(self, procline):
760 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
761 767d52d3 Iustin Pop
    if u:
762 3c003d9d Iustin Pop
      self.cstatus = self.CS_UNCONFIGURED
763 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
764 767d52d3 Iustin Pop
    else:
765 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
766 767d52d3 Iustin Pop
      if not m:
767 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
768 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
769 767d52d3 Iustin Pop
      self.lrole = m.group(2)
770 767d52d3 Iustin Pop
      self.rrole = m.group(3)
771 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
772 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
773 767d52d3 Iustin Pop
774 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
775 6b90c22e Iustin Pop
776 3c003d9d Iustin Pop
    self.is_standalone = self.cstatus == self.CS_STANDALONE
777 3c003d9d Iustin Pop
    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
778 3c003d9d Iustin Pop
    self.is_connected = self.cstatus == self.CS_CONNECTED
779 3c003d9d Iustin Pop
    self.is_primary = self.lrole == self.RO_PRIMARY
780 3c003d9d Iustin Pop
    self.is_secondary = self.lrole == self.RO_SECONDARY
781 3c003d9d Iustin Pop
    self.peer_primary = self.rrole == self.RO_PRIMARY
782 3c003d9d Iustin Pop
    self.peer_secondary = self.rrole == self.RO_SECONDARY
783 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
784 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
785 6b90c22e Iustin Pop
786 3c003d9d Iustin Pop
    self.is_diskless = self.ldisk == self.DS_DISKLESS
787 3c003d9d Iustin Pop
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
788 6b90c22e Iustin Pop
789 3c003d9d Iustin Pop
    self.is_in_resync = self.cstatus in self.CSET_SYNC
790 3c003d9d Iustin Pop
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
791 6b93ec9d Iustin Pop
792 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
793 6b90c22e Iustin Pop
    if m:
794 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
795 6b90c22e Iustin Pop
      hours = int(m.group(2))
796 6b90c22e Iustin Pop
      minutes = int(m.group(3))
797 6b90c22e Iustin Pop
      seconds = int(m.group(4))
798 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
799 6b90c22e Iustin Pop
    else:
800 3c003d9d Iustin Pop
      # we have (in this if branch) no percent information, but if
801 3c003d9d Iustin Pop
      # we're resyncing we need to 'fake' a sync percent information,
802 3c003d9d Iustin Pop
      # as this is how cmdlib determines if it makes sense to wait for
803 3c003d9d Iustin Pop
      # resyncing or not
804 3c003d9d Iustin Pop
      if self.is_in_resync:
805 3c003d9d Iustin Pop
        self.sync_percent = 0
806 3c003d9d Iustin Pop
      else:
807 3c003d9d Iustin Pop
        self.sync_percent = None
808 6b90c22e Iustin Pop
      self.est_time = None
809 6b90c22e Iustin Pop
810 6b90c22e Iustin Pop
811 fe267188 Iustin Pop
class BaseDRBD(BlockDev): # pylint: disable-msg=W0223
812 0f7f32d9 Iustin Pop
  """Base DRBD class.
813 a8083063 Iustin Pop

814 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
815 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
816 0f7f32d9 Iustin Pop

817 abdf0113 Iustin Pop
  """
818 abdf0113 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
819 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
820 9122e60a Iustin Pop
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
821 9122e60a Iustin Pop
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
822 a8083063 Iustin Pop
823 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
824 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
825 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
826 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
827 a8083063 Iustin Pop
828 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
829 549071a0 Luca Bigliardi
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
830 6b90c22e Iustin Pop
831 abdf0113 Iustin Pop
  @staticmethod
832 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
833 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
834 a8083063 Iustin Pop

835 a8083063 Iustin Pop
    """
836 abdf0113 Iustin Pop
    try:
837 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
838 f6eaed12 Iustin Pop
    except EnvironmentError, err:
839 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
840 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
841 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
842 f6eaed12 Iustin Pop
      else:
843 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
844 abdf0113 Iustin Pop
    if not data:
845 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
846 abdf0113 Iustin Pop
    return data
847 a8083063 Iustin Pop
848 9122e60a Iustin Pop
  @classmethod
849 9122e60a Iustin Pop
  def _MassageProcData(cls, data):
850 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
851 a8083063 Iustin Pop

852 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
853 c41eea6e Iustin Pop
        for that minor
854 a8083063 Iustin Pop

855 a8083063 Iustin Pop
    """
856 abdf0113 Iustin Pop
    results = {}
857 abdf0113 Iustin Pop
    old_minor = old_line = None
858 abdf0113 Iustin Pop
    for line in data:
859 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
860 67d101d4 Iustin Pop
        continue
861 9122e60a Iustin Pop
      lresult = cls._VALID_LINE_RE.match(line)
862 abdf0113 Iustin Pop
      if lresult is not None:
863 abdf0113 Iustin Pop
        if old_minor is not None:
864 abdf0113 Iustin Pop
          results[old_minor] = old_line
865 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
866 abdf0113 Iustin Pop
        old_line = line
867 abdf0113 Iustin Pop
      else:
868 abdf0113 Iustin Pop
        if old_minor is not None:
869 abdf0113 Iustin Pop
          old_line += " " + line.strip()
870 abdf0113 Iustin Pop
    # add last line
871 abdf0113 Iustin Pop
    if old_minor is not None:
872 abdf0113 Iustin Pop
      results[old_minor] = old_line
873 abdf0113 Iustin Pop
    return results
874 a8083063 Iustin Pop
875 abdf0113 Iustin Pop
  @classmethod
876 abdf0113 Iustin Pop
  def _GetVersion(cls):
877 abdf0113 Iustin Pop
    """Return the DRBD version.
878 a8083063 Iustin Pop

879 abdf0113 Iustin Pop
    This will return a dict with keys:
880 c41eea6e Iustin Pop
      - k_major
881 c41eea6e Iustin Pop
      - k_minor
882 c41eea6e Iustin Pop
      - k_point
883 c41eea6e Iustin Pop
      - api
884 c41eea6e Iustin Pop
      - proto
885 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
886 a8083063 Iustin Pop

887 a8083063 Iustin Pop
    """
888 abdf0113 Iustin Pop
    proc_data = cls._GetProcData()
889 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
890 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
891 abdf0113 Iustin Pop
    if not version:
892 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
893 abdf0113 Iustin Pop
                                    first_line)
894 a8083063 Iustin Pop
895 abdf0113 Iustin Pop
    values = version.groups()
896 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
897 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
898 abdf0113 Iustin Pop
              'k_point': int(values[2]),
899 abdf0113 Iustin Pop
              'api': int(values[3]),
900 abdf0113 Iustin Pop
              'proto': int(values[4]),
901 abdf0113 Iustin Pop
             }
902 abdf0113 Iustin Pop
    if values[5] is not None:
903 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
904 a8083063 Iustin Pop
905 abdf0113 Iustin Pop
    return retval
906 abdf0113 Iustin Pop
907 abdf0113 Iustin Pop
  @staticmethod
908 549071a0 Luca Bigliardi
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
909 549071a0 Luca Bigliardi
    """Returns DRBD usermode_helper currently set.
910 549071a0 Luca Bigliardi

911 549071a0 Luca Bigliardi
    """
912 549071a0 Luca Bigliardi
    try:
913 549071a0 Luca Bigliardi
      helper = utils.ReadFile(filename).splitlines()[0]
914 549071a0 Luca Bigliardi
    except EnvironmentError, err:
915 549071a0 Luca Bigliardi
      if err.errno == errno.ENOENT:
916 549071a0 Luca Bigliardi
        _ThrowError("The file %s cannot be opened, check if the module"
917 549071a0 Luca Bigliardi
                    " is loaded (%s)", filename, str(err))
918 549071a0 Luca Bigliardi
      else:
919 549071a0 Luca Bigliardi
        _ThrowError("Can't read DRBD helper file %s: %s", filename, str(err))
920 549071a0 Luca Bigliardi
    if not helper:
921 549071a0 Luca Bigliardi
      _ThrowError("Can't read any data from %s", filename)
922 549071a0 Luca Bigliardi
    return helper
923 549071a0 Luca Bigliardi
924 549071a0 Luca Bigliardi
  @staticmethod
925 abdf0113 Iustin Pop
  def _DevPath(minor):
926 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
927 a8083063 Iustin Pop

928 a8083063 Iustin Pop
    """
929 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
930 a8083063 Iustin Pop
931 abdf0113 Iustin Pop
  @classmethod
932 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
933 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
934 a8083063 Iustin Pop

935 a8083063 Iustin Pop
    """
936 abdf0113 Iustin Pop
    data = cls._GetProcData()
937 a8083063 Iustin Pop
938 abdf0113 Iustin Pop
    used_devs = {}
939 abdf0113 Iustin Pop
    for line in data:
940 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
941 abdf0113 Iustin Pop
      if not match:
942 abdf0113 Iustin Pop
        continue
943 abdf0113 Iustin Pop
      minor = int(match.group(1))
944 abdf0113 Iustin Pop
      state = match.group(2)
945 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
946 abdf0113 Iustin Pop
        continue
947 abdf0113 Iustin Pop
      used_devs[minor] = state, line
948 a8083063 Iustin Pop
949 abdf0113 Iustin Pop
    return used_devs
950 a8083063 Iustin Pop
951 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
952 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
953 0834c866 Iustin Pop

954 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
955 a8083063 Iustin Pop

956 a8083063 Iustin Pop
    """
957 abdf0113 Iustin Pop
    if minor is None:
958 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
959 cb999543 Iustin Pop
      self.attached = False
960 a8083063 Iustin Pop
    else:
961 abdf0113 Iustin Pop
      self.minor = minor
962 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
963 cb999543 Iustin Pop
      self.attached = True
964 a8083063 Iustin Pop
965 a8083063 Iustin Pop
  @staticmethod
966 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
967 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
968 a8083063 Iustin Pop

969 abdf0113 Iustin Pop
    This currently only check the size, which must be around
970 abdf0113 Iustin Pop
    128MiB.
971 a8083063 Iustin Pop

972 a8083063 Iustin Pop
    """
973 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
974 abdf0113 Iustin Pop
    if result.failed:
975 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
976 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
977 a8083063 Iustin Pop
    try:
978 abdf0113 Iustin Pop
      sectors = int(result.stdout)
979 691744c4 Iustin Pop
    except (TypeError, ValueError):
980 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
981 c04bc777 Iustin Pop
    num_bytes = sectors * 512
982 c04bc777 Iustin Pop
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
983 c04bc777 Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (num_bytes / 1024 / 1024))
984 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
985 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
986 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
987 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
988 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
989 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
990 c04bc777 Iustin Pop
    if num_bytes > 1024 * 1024 * 1024:
991 c04bc777 Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (num_bytes / 1024 / 1024))
992 a8083063 Iustin Pop
993 abdf0113 Iustin Pop
  def Rename(self, new_id):
994 abdf0113 Iustin Pop
    """Rename a device.
995 a8083063 Iustin Pop

996 abdf0113 Iustin Pop
    This is not supported for drbd devices.
997 a8083063 Iustin Pop

998 a8083063 Iustin Pop
    """
999 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1000 a8083063 Iustin Pop
1001 f3e513ad Iustin Pop
1002 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1003 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1004 a2cfdea2 Iustin Pop

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

1009 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1010 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1011 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1012 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1013 a2cfdea2 Iustin Pop

1014 a2cfdea2 Iustin Pop
  """
1015 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1016 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1017 a2cfdea2 Iustin Pop
1018 cf8df3f3 Iustin Pop
  # timeout constants
1019 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
1020 cf8df3f3 Iustin Pop
1021 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1022 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1023 fc1dc9d7 Iustin Pop
      children = []
1024 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
1025 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1026 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
1027 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1028 310fbb64 Iustin Pop
    (self._lhost, self._lport,
1029 310fbb64 Iustin Pop
     self._rhost, self._rport,
1030 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
1031 310fbb64 Iustin Pop
    if children:
1032 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
1033 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
1034 310fbb64 Iustin Pop
        children = []
1035 464f8daf Iustin Pop
    super(DRBD8, self).__init__(unique_id, children, size)
1036 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1037 c3f9340c Guido Trotter
    version = self._GetVersion()
1038 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
1039 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
1040 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
1041 82463074 Iustin Pop
                  version['k_major'], version['k_minor'])
1042 a2cfdea2 Iustin Pop
1043 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1044 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1045 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1046 ffa1c0dc Iustin Pop
                       (unique_id,))
1047 a2cfdea2 Iustin Pop
    self.Attach()
1048 a2cfdea2 Iustin Pop
1049 a2cfdea2 Iustin Pop
  @classmethod
1050 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1051 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1052 a2cfdea2 Iustin Pop

1053 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1054 a2cfdea2 Iustin Pop

1055 a2cfdea2 Iustin Pop
    """
1056 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1057 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1058 a2cfdea2 Iustin Pop
    if result.failed:
1059 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1060 a2cfdea2 Iustin Pop
1061 a2cfdea2 Iustin Pop
  @classmethod
1062 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1063 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1064 a2cfdea2 Iustin Pop

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

1068 a2cfdea2 Iustin Pop
    """
1069 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1070 a2cfdea2 Iustin Pop
1071 a2cfdea2 Iustin Pop
    highest = None
1072 a2cfdea2 Iustin Pop
    for line in data:
1073 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1074 a2cfdea2 Iustin Pop
      if match:
1075 a2cfdea2 Iustin Pop
        return int(match.group(1))
1076 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1077 a2cfdea2 Iustin Pop
      if match:
1078 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1079 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1080 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1081 a2cfdea2 Iustin Pop
      return 0
1082 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1083 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1084 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1085 a2cfdea2 Iustin Pop
    return highest + 1
1086 a2cfdea2 Iustin Pop
1087 a2cfdea2 Iustin Pop
  @classmethod
1088 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1089 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1090 a2cfdea2 Iustin Pop

1091 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1092 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1093 a2cfdea2 Iustin Pop

1094 a2cfdea2 Iustin Pop
    """
1095 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1096 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1097 a2cfdea2 Iustin Pop
1098 a2cfdea2 Iustin Pop
    # pyparsing setup
1099 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1100 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1101 5a672c30 Manuel Franceschini
    lbracket = pyp.Literal("[").suppress()
1102 5a672c30 Manuel Franceschini
    rbracket = pyp.Literal("]").suppress()
1103 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1104 5a672c30 Manuel Franceschini
    colon = pyp.Literal(":").suppress()
1105 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1106 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1107 a2cfdea2 Iustin Pop
1108 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1109 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1110 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1111 a2cfdea2 Iustin Pop
1112 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1113 a2cfdea2 Iustin Pop
1114 a2cfdea2 Iustin Pop
    # value types
1115 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1116 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1117 5a672c30 Manuel Franceschini
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1118 5a672c30 Manuel Franceschini
                 pyp.Word(pyp.nums + ".") + colon + number)
1119 5a672c30 Manuel Franceschini
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
1120 5a672c30 Manuel Franceschini
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
1121 5a672c30 Manuel Franceschini
                 pyp.Optional(rbracket) + colon + number)
1122 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1123 5a672c30 Manuel Franceschini
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
1124 01e2ce3a Iustin Pop
    # device name, extended syntax
1125 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1126 a2cfdea2 Iustin Pop
1127 a2cfdea2 Iustin Pop
    # a statement
1128 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1129 5a672c30 Manuel Franceschini
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
1130 01e2ce3a Iustin Pop
                         device_value) +
1131 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1132 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1133 a2cfdea2 Iustin Pop
1134 a2cfdea2 Iustin Pop
    # an entire section
1135 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1136 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1137 a2cfdea2 Iustin Pop
1138 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1139 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1140 a2cfdea2 Iustin Pop
1141 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1142 a2cfdea2 Iustin Pop
1143 a2cfdea2 Iustin Pop
    return bnf
1144 a2cfdea2 Iustin Pop
1145 a2cfdea2 Iustin Pop
  @classmethod
1146 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1147 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1148 a2cfdea2 Iustin Pop

1149 a2cfdea2 Iustin Pop
    """
1150 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1151 a2cfdea2 Iustin Pop
    if result.failed:
1152 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1153 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1154 3840729d Iustin Pop
      return None
1155 3840729d Iustin Pop
    return result.stdout
1156 3840729d Iustin Pop
1157 3840729d Iustin Pop
  @classmethod
1158 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1159 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1160 3840729d Iustin Pop

1161 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1162 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1163 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1164 3840729d Iustin Pop
    by _GetShowData.
1165 3840729d Iustin Pop

1166 3840729d Iustin Pop
    """
1167 3840729d Iustin Pop
    data = {}
1168 a2cfdea2 Iustin Pop
    if not out:
1169 a2cfdea2 Iustin Pop
      return data
1170 a2cfdea2 Iustin Pop
1171 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1172 a2cfdea2 Iustin Pop
    # run pyparse
1173 a2cfdea2 Iustin Pop
1174 a2cfdea2 Iustin Pop
    try:
1175 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1176 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1177 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1178 a2cfdea2 Iustin Pop
1179 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1180 a2cfdea2 Iustin Pop
    for section in results:
1181 a2cfdea2 Iustin Pop
      sname = section[0]
1182 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1183 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1184 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1185 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1186 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1187 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1188 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1189 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1190 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1191 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1192 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1193 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1194 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1195 a2cfdea2 Iustin Pop
    return data
1196 a2cfdea2 Iustin Pop
1197 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1198 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1199 a2cfdea2 Iustin Pop

1200 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1201 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1202 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1203 a2cfdea2 Iustin Pop
    device.
1204 a2cfdea2 Iustin Pop

1205 a2cfdea2 Iustin Pop
    """
1206 b00b95dd Iustin Pop
    if self._children:
1207 b00b95dd Iustin Pop
      backend, meta = self._children
1208 b00b95dd Iustin Pop
    else:
1209 b00b95dd Iustin Pop
      backend = meta = None
1210 b00b95dd Iustin Pop
1211 a2cfdea2 Iustin Pop
    if backend is not None:
1212 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1213 a2cfdea2 Iustin Pop
    else:
1214 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1215 b00b95dd Iustin Pop
1216 a2cfdea2 Iustin Pop
    if meta is not None:
1217 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1218 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1219 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1220 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1221 a2cfdea2 Iustin Pop
    else:
1222 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1223 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1224 a2cfdea2 Iustin Pop
    return retval
1225 a2cfdea2 Iustin Pop
1226 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1227 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1228 a2cfdea2 Iustin Pop

1229 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1230 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1231 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1232 a2cfdea2 Iustin Pop
    device.
1233 a2cfdea2 Iustin Pop

1234 a2cfdea2 Iustin Pop
    """
1235 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1236 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1237 a2cfdea2 Iustin Pop
      return True
1238 a2cfdea2 Iustin Pop
1239 a2cfdea2 Iustin Pop
    if self._lhost is None:
1240 a2cfdea2 Iustin Pop
      return False
1241 a2cfdea2 Iustin Pop
1242 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1243 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1244 a2cfdea2 Iustin Pop
      return False
1245 a2cfdea2 Iustin Pop
1246 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1247 a2cfdea2 Iustin Pop
    retval = (retval and
1248 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1249 a2cfdea2 Iustin Pop
    return retval
1250 a2cfdea2 Iustin Pop
1251 a2cfdea2 Iustin Pop
  @classmethod
1252 f069addf Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta, size):
1253 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1254 a2cfdea2 Iustin Pop

1255 a2cfdea2 Iustin Pop
    """
1256 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1257 f069addf Iustin Pop
            backend, meta, "0",
1258 f069addf Iustin Pop
            "-e", "detach",
1259 f069addf Iustin Pop
            "--create-device"]
1260 60bca04a Iustin Pop
    if size:
1261 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1262 89b70f39 Iustin Pop
    if not constants.DRBD_BARRIERS: # disable barriers, if configured so
1263 89b70f39 Iustin Pop
      version = cls._GetVersion()
1264 89b70f39 Iustin Pop
      # various DRBD versions support different disk barrier options;
1265 89b70f39 Iustin Pop
      # what we aim here is to revert back to the 'drain' method of
1266 89b70f39 Iustin Pop
      # disk flushes and to disable metadata barriers, in effect going
1267 89b70f39 Iustin Pop
      # back to pre-8.0.7 behaviour
1268 89b70f39 Iustin Pop
      vmaj = version['k_major']
1269 89b70f39 Iustin Pop
      vmin = version['k_minor']
1270 89b70f39 Iustin Pop
      vrel = version['k_point']
1271 89b70f39 Iustin Pop
      assert vmaj == 8
1272 89b70f39 Iustin Pop
      if vmin == 0: # 8.0.x
1273 89b70f39 Iustin Pop
        if vrel >= 12:
1274 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1275 89b70f39 Iustin Pop
      elif vmin == 2: # 8.2.x
1276 89b70f39 Iustin Pop
        if vrel >= 7:
1277 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1278 89b70f39 Iustin Pop
      elif vmaj >= 3: # 8.3.x or newer
1279 89b70f39 Iustin Pop
        args.extend(['-i', '-a', 'm'])
1280 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1281 a2cfdea2 Iustin Pop
    if result.failed:
1282 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1283 a2cfdea2 Iustin Pop
1284 a2cfdea2 Iustin Pop
  @classmethod
1285 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1286 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1287 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1288 a2cfdea2 Iustin Pop

1289 a2cfdea2 Iustin Pop
    """
1290 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1291 52857176 Iustin Pop
    if None in net_info:
1292 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1293 52857176 Iustin Pop
      # sure its shutdown
1294 1063abd1 Iustin Pop
      cls._ShutdownNet(minor)
1295 1063abd1 Iustin Pop
      return
1296 52857176 Iustin Pop
1297 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1298 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1299 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1300 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1301 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1302 7d585316 Iustin Pop
    # about its peer.
1303 7d585316 Iustin Pop
    cls._SetMinorSyncSpeed(minor, constants.SYNC_SPEED)
1304 7d585316 Iustin Pop
1305 a744b676 Manuel Franceschini
    if netutils.IsValidIP6(lhost):
1306 a744b676 Manuel Franceschini
      if not netutils.IsValidIP6(rhost):
1307 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1308 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1309 5a672c30 Manuel Franceschini
      family = "ipv6"
1310 a744b676 Manuel Franceschini
    elif netutils.IsValidIP4(lhost):
1311 a744b676 Manuel Franceschini
      if not netutils.IsValidIP4(rhost):
1312 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1313 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1314 5a672c30 Manuel Franceschini
      family = "ipv4"
1315 5a672c30 Manuel Franceschini
    else:
1316 5a672c30 Manuel Franceschini
      _ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
1317 5a672c30 Manuel Franceschini
1318 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1319 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, lhost, lport),
1320 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, rhost, rport), protocol,
1321 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1322 f38478b2 Iustin Pop
            "-B", "consensus",
1323 ab6cc81c Iustin Pop
            "--create-device",
1324 f38478b2 Iustin Pop
            ]
1325 a2cfdea2 Iustin Pop
    if dual_pri:
1326 a2cfdea2 Iustin Pop
      args.append("-m")
1327 a2cfdea2 Iustin Pop
    if hmac and secret:
1328 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1329 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1330 a2cfdea2 Iustin Pop
    if result.failed:
1331 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1332 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1333 a2cfdea2 Iustin Pop
1334 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1335 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1336 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1337 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1338 def8e2f6 Michael Hanselmann
1339 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1340 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1341 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1342 def8e2f6 Michael Hanselmann
1343 def8e2f6 Michael Hanselmann
    try:
1344 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1345 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1346 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1347 a2cfdea2 Iustin Pop
1348 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1349 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1350 b00b95dd Iustin Pop

1351 b00b95dd Iustin Pop
    """
1352 b00b95dd Iustin Pop
    if self.minor is None:
1353 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1354 82463074 Iustin Pop
                  self._aminor)
1355 b00b95dd Iustin Pop
    if len(devices) != 2:
1356 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1357 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1358 03ece5f3 Iustin Pop
    if "local_dev" in info:
1359 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1360 b00b95dd Iustin Pop
    backend, meta = devices
1361 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1362 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1363 b00b95dd Iustin Pop
    backend.Open()
1364 b00b95dd Iustin Pop
    meta.Open()
1365 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1366 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1367 b00b95dd Iustin Pop
1368 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1369 b00b95dd Iustin Pop
    self._children = devices
1370 b00b95dd Iustin Pop
1371 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1372 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1373 b00b95dd Iustin Pop

1374 b00b95dd Iustin Pop
    """
1375 b00b95dd Iustin Pop
    if self.minor is None:
1376 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1377 82463074 Iustin Pop
                  self._aminor)
1378 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1379 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1380 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1381 03ece5f3 Iustin Pop
      return
1382 b00b95dd Iustin Pop
    if len(self._children) != 2:
1383 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1384 82463074 Iustin Pop
                  self._children)
1385 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1386 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1387 e739bd57 Iustin Pop
      return
1388 b00b95dd Iustin Pop
    if len(devices) != 2:
1389 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1390 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1391 e739bd57 Iustin Pop
      if dev != child.dev_path:
1392 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1393 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1394 b00b95dd Iustin Pop
1395 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1396 b00b95dd Iustin Pop
    self._children = []
1397 b00b95dd Iustin Pop
1398 7d585316 Iustin Pop
  @classmethod
1399 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1400 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1401 a2cfdea2 Iustin Pop

1402 7d585316 Iustin Pop
    This is the low-level implementation.
1403 7d585316 Iustin Pop

1404 7d585316 Iustin Pop
    @type minor: int
1405 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1406 7d585316 Iustin Pop
    @type kbytes: int
1407 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1408 7d585316 Iustin Pop
    @rtype: boolean
1409 7d585316 Iustin Pop
    @return: the success of the operation
1410 7d585316 Iustin Pop

1411 a2cfdea2 Iustin Pop
    """
1412 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1413 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1414 a2cfdea2 Iustin Pop
    if result.failed:
1415 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1416 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1417 7d585316 Iustin Pop
    return not result.failed
1418 7d585316 Iustin Pop
1419 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1420 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1421 7d585316 Iustin Pop

1422 7d585316 Iustin Pop
    @type kbytes: int
1423 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1424 7d585316 Iustin Pop
    @rtype: boolean
1425 7d585316 Iustin Pop
    @return: the success of the operation
1426 7d585316 Iustin Pop

1427 7d585316 Iustin Pop
    """
1428 7d585316 Iustin Pop
    if self.minor is None:
1429 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1430 7d585316 Iustin Pop
      return False
1431 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1432 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1433 a2cfdea2 Iustin Pop
1434 6b90c22e Iustin Pop
  def GetProcStatus(self):
1435 6b90c22e Iustin Pop
    """Return device data from /proc.
1436 6b90c22e Iustin Pop

1437 6b90c22e Iustin Pop
    """
1438 6b90c22e Iustin Pop
    if self.minor is None:
1439 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1440 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1441 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1442 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1443 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1444 6b90c22e Iustin Pop
1445 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1446 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1447 a2cfdea2 Iustin Pop

1448 a2cfdea2 Iustin Pop

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

1453 0834c866 Iustin Pop

1454 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1455 0834c866 Iustin Pop
    network not connected or local disk missing.
1456 0834c866 Iustin Pop

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

1460 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1461 c41eea6e Iustin Pop

1462 a2cfdea2 Iustin Pop
    """
1463 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1464 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1465 96acbc09 Michael Hanselmann
1466 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1467 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1468 f208978a Michael Hanselmann
1469 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1470 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1471 f208978a Michael Hanselmann
    elif stats.is_diskless:
1472 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1473 f208978a Michael Hanselmann
    else:
1474 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1475 96acbc09 Michael Hanselmann
1476 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1477 96acbc09 Michael Hanselmann
                                  major=self.major,
1478 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1479 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1480 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1481 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1482 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1483 a2cfdea2 Iustin Pop
1484 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1485 a2cfdea2 Iustin Pop
    """Make the local state primary.
1486 a2cfdea2 Iustin Pop

1487 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1488 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1489 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1490 f860ff4e Guido Trotter
    is mandatory.
1491 a2cfdea2 Iustin Pop

1492 a2cfdea2 Iustin Pop
    """
1493 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1494 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1495 a2cfdea2 Iustin Pop
      return False
1496 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1497 a2cfdea2 Iustin Pop
    if force:
1498 a2cfdea2 Iustin Pop
      cmd.append("-o")
1499 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1500 a2cfdea2 Iustin Pop
    if result.failed:
1501 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1502 82463074 Iustin Pop
                  result.output)
1503 a2cfdea2 Iustin Pop
1504 a2cfdea2 Iustin Pop
  def Close(self):
1505 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1506 a2cfdea2 Iustin Pop

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

1509 a2cfdea2 Iustin Pop
    """
1510 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1511 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1512 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1513 a2cfdea2 Iustin Pop
    if result.failed:
1514 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1515 82463074 Iustin Pop
                  self.minor, result.output)
1516 a2cfdea2 Iustin Pop
1517 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1518 cf8df3f3 Iustin Pop
    """Removes network configuration.
1519 cf8df3f3 Iustin Pop

1520 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1521 cf8df3f3 Iustin Pop

1522 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1523 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1524 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1525 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1526 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1527 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1528 cf8df3f3 Iustin Pop
    exception.
1529 cf8df3f3 Iustin Pop

1530 cf8df3f3 Iustin Pop
    """
1531 cf8df3f3 Iustin Pop
    if self.minor is None:
1532 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1533 cf8df3f3 Iustin Pop
1534 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1535 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1536 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1537 cf8df3f3 Iustin Pop
1538 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1539 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1540 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1541 cf8df3f3 Iustin Pop
1542 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1543 def8e2f6 Michael Hanselmann
1544 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1545 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1546 def8e2f6 Michael Hanselmann
        return
1547 def8e2f6 Michael Hanselmann
1548 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1549 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1550 def8e2f6 Michael Hanselmann
      # forgotten
1551 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1552 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1553 def8e2f6 Michael Hanselmann
1554 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1555 def8e2f6 Michael Hanselmann
1556 def8e2f6 Michael Hanselmann
    # Keep start time
1557 def8e2f6 Michael Hanselmann
    start_time = time.time()
1558 def8e2f6 Michael Hanselmann
1559 def8e2f6 Michael Hanselmann
    try:
1560 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1561 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1562 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1563 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1564 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1565 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1566 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1567 cf8df3f3 Iustin Pop
      else:
1568 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1569 def8e2f6 Michael Hanselmann
1570 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1571 cf8df3f3 Iustin Pop
1572 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1573 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1574 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1575 82463074 Iustin Pop
                   self.minor, reconfig_time)
1576 cf8df3f3 Iustin Pop
1577 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1578 cf8df3f3 Iustin Pop
    """Reconnects the network.
1579 cf8df3f3 Iustin Pop

1580 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1581 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1582 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1583 cf8df3f3 Iustin Pop

1584 cf8df3f3 Iustin Pop
    Args:
1585 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1586 cf8df3f3 Iustin Pop

1587 cf8df3f3 Iustin Pop
    """
1588 cf8df3f3 Iustin Pop
    if self.minor is None:
1589 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1590 cf8df3f3 Iustin Pop
1591 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1592 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1593 cf8df3f3 Iustin Pop
1594 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1595 cf8df3f3 Iustin Pop
1596 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1597 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1598 cf8df3f3 Iustin Pop
1599 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1600 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1601 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1602 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1603 cf8df3f3 Iustin Pop
1604 a2cfdea2 Iustin Pop
  def Attach(self):
1605 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1606 2d0c8319 Iustin Pop

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

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

1614 2d0c8319 Iustin Pop
    """
1615 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1616 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1617 2d0c8319 Iustin Pop
      minor = self._aminor
1618 2d0c8319 Iustin Pop
    else:
1619 2d0c8319 Iustin Pop
      minor = None
1620 2d0c8319 Iustin Pop
1621 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1622 2d0c8319 Iustin Pop
    return minor is not None
1623 2d0c8319 Iustin Pop
1624 2d0c8319 Iustin Pop
  def Assemble(self):
1625 2d0c8319 Iustin Pop
    """Assemble the drbd.
1626 2d0c8319 Iustin Pop

1627 2d0c8319 Iustin Pop
    Method:
1628 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1629 2d0c8319 Iustin Pop
        our config
1630 2d0c8319 Iustin Pop
      - if not, we create it from zero
1631 2d0c8319 Iustin Pop

1632 2d0c8319 Iustin Pop
    """
1633 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1634 2d0c8319 Iustin Pop
1635 2d0c8319 Iustin Pop
    self.Attach()
1636 2d0c8319 Iustin Pop
    if self.minor is None:
1637 2d0c8319 Iustin Pop
      # local device completely unconfigured
1638 1063abd1 Iustin Pop
      self._FastAssemble()
1639 2d0c8319 Iustin Pop
    else:
1640 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1641 2d0c8319 Iustin Pop
      # the device
1642 1063abd1 Iustin Pop
      self._SlowAssemble()
1643 2d0c8319 Iustin Pop
1644 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1645 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1646 a2cfdea2 Iustin Pop

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

1651 a2cfdea2 Iustin Pop
    """
1652 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
1653 527a15ac Iustin Pop
    # pylint: disable-msg=W0631
1654 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1655 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1656 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1657 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1658 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1659 1063abd1 Iustin Pop
1660 a2cfdea2 Iustin Pop
      if match_l and match_r:
1661 1063abd1 Iustin Pop
        # everything matches
1662 a2cfdea2 Iustin Pop
        break
1663 1063abd1 Iustin Pop
1664 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1665 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1666 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1667 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1668 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1669 1063abd1 Iustin Pop
          break
1670 1063abd1 Iustin Pop
        else:
1671 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1672 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1673 1063abd1 Iustin Pop
1674 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1675 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1676 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1677 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1678 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1679 1063abd1 Iustin Pop
          break
1680 1063abd1 Iustin Pop
        else:
1681 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1682 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1683 bf25af3b Iustin Pop
1684 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1685 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1686 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1687 bf25af3b Iustin Pop
      # configuration
1688 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1689 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1690 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1691 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1692 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1693 9cdbe77f Iustin Pop
        # reconnect to our correct one
1694 1063abd1 Iustin Pop
        try:
1695 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1696 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1697 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1698 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1699 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1700 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1701 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1702 9cdbe77f Iustin Pop
        # None)
1703 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1704 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1705 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1706 9cdbe77f Iustin Pop
          break
1707 1063abd1 Iustin Pop
        else:
1708 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1709 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1710 9cdbe77f Iustin Pop
1711 a2cfdea2 Iustin Pop
    else:
1712 a2cfdea2 Iustin Pop
      minor = None
1713 a2cfdea2 Iustin Pop
1714 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1715 1063abd1 Iustin Pop
    if minor is None:
1716 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1717 1063abd1 Iustin Pop
                  self._aminor)
1718 a2cfdea2 Iustin Pop
1719 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1720 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1721 a2cfdea2 Iustin Pop

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

1724 a2cfdea2 Iustin Pop
    """
1725 a1578d63 Iustin Pop
    minor = self._aminor
1726 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1727 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1728 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1729 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1730 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1731 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1732 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1733 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1734 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1735 a2cfdea2 Iustin Pop
1736 a2cfdea2 Iustin Pop
  @classmethod
1737 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1738 b00b95dd Iustin Pop
    """Detach from the local device.
1739 b00b95dd Iustin Pop

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

1743 b00b95dd Iustin Pop
    """
1744 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1745 b00b95dd Iustin Pop
    if result.failed:
1746 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1747 b00b95dd Iustin Pop
1748 b00b95dd Iustin Pop
  @classmethod
1749 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1750 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1751 f3e513ad Iustin Pop

1752 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1753 f3e513ad Iustin Pop

1754 f3e513ad Iustin Pop
    """
1755 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1756 a8459f1c Iustin Pop
    if result.failed:
1757 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1758 f3e513ad Iustin Pop
1759 f3e513ad Iustin Pop
  @classmethod
1760 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1761 a2cfdea2 Iustin Pop
    """Deactivate the device.
1762 a2cfdea2 Iustin Pop

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

1765 a2cfdea2 Iustin Pop
    """
1766 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1767 a2cfdea2 Iustin Pop
    if result.failed:
1768 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1769 33bc6f01 Iustin Pop
                  minor, result.output)
1770 a2cfdea2 Iustin Pop
1771 a2cfdea2 Iustin Pop
  def Shutdown(self):
1772 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1773 a2cfdea2 Iustin Pop

1774 a2cfdea2 Iustin Pop
    """
1775 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1776 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1777 746f7476 Iustin Pop
      return
1778 746f7476 Iustin Pop
    minor = self.minor
1779 a2cfdea2 Iustin Pop
    self.minor = None
1780 a2cfdea2 Iustin Pop
    self.dev_path = None
1781 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1782 a2cfdea2 Iustin Pop
1783 a2cfdea2 Iustin Pop
  def Remove(self):
1784 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1785 a2cfdea2 Iustin Pop

1786 a2cfdea2 Iustin Pop
    """
1787 0c6c04ec Iustin Pop
    self.Shutdown()
1788 a2cfdea2 Iustin Pop
1789 a2cfdea2 Iustin Pop
  @classmethod
1790 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1791 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1792 a2cfdea2 Iustin Pop

1793 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1794 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1795 a2cfdea2 Iustin Pop

1796 a2cfdea2 Iustin Pop
    """
1797 a2cfdea2 Iustin Pop
    if len(children) != 2:
1798 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1799 767d52d3 Iustin Pop
    # check that the minor is unused
1800 767d52d3 Iustin Pop
    aminor = unique_id[4]
1801 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1802 767d52d3 Iustin Pop
    if aminor in proc_info:
1803 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1804 767d52d3 Iustin Pop
      in_use = status.is_in_use
1805 767d52d3 Iustin Pop
    else:
1806 767d52d3 Iustin Pop
      in_use = False
1807 767d52d3 Iustin Pop
    if in_use:
1808 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1809 a2cfdea2 Iustin Pop
    meta = children[1]
1810 a2cfdea2 Iustin Pop
    meta.Assemble()
1811 a2cfdea2 Iustin Pop
    if not meta.Attach():
1812 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1813 33bc6f01 Iustin Pop
                  aminor, meta)
1814 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1815 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1816 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1817 a2cfdea2 Iustin Pop
1818 1005d816 Iustin Pop
  def Grow(self, amount):
1819 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1820 1005d816 Iustin Pop

1821 1005d816 Iustin Pop
    """
1822 1005d816 Iustin Pop
    if self.minor is None:
1823 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1824 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1825 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1826 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1827 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1828 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1829 1005d816 Iustin Pop
    if result.failed:
1830 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1831 1005d816 Iustin Pop
1832 a8083063 Iustin Pop
1833 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1834 6f695a2e Manuel Franceschini
  """File device.
1835 abdf0113 Iustin Pop

1836 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1837 6f695a2e Manuel Franceschini

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

1840 6f695a2e Manuel Franceschini
  """
1841 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1842 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1843 6f695a2e Manuel Franceschini

1844 6f695a2e Manuel Franceschini
    """
1845 6f695a2e Manuel Franceschini
    if children:
1846 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1847 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1848 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1849 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1850 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1851 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1852 ecb091e3 Iustin Pop
    self.Attach()
1853 6f695a2e Manuel Franceschini
1854 6f695a2e Manuel Franceschini
  def Assemble(self):
1855 6f695a2e Manuel Franceschini
    """Assemble the device.
1856 6f695a2e Manuel Franceschini

1857 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1858 6f695a2e Manuel Franceschini

1859 6f695a2e Manuel Franceschini
    """
1860 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1861 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1862 6f695a2e Manuel Franceschini
1863 6f695a2e Manuel Franceschini
  def Shutdown(self):
1864 6f695a2e Manuel Franceschini
    """Shutdown the device.
1865 6f695a2e Manuel Franceschini

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

1869 6f695a2e Manuel Franceschini
    """
1870 746f7476 Iustin Pop
    pass
1871 6f695a2e Manuel Franceschini
1872 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1873 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1874 6f695a2e Manuel Franceschini

1875 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1876 6f695a2e Manuel Franceschini

1877 6f695a2e Manuel Franceschini
    """
1878 6f695a2e Manuel Franceschini
    pass
1879 6f695a2e Manuel Franceschini
1880 6f695a2e Manuel Franceschini
  def Close(self):
1881 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1882 6f695a2e Manuel Franceschini

1883 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1884 6f695a2e Manuel Franceschini

1885 6f695a2e Manuel Franceschini
    """
1886 6f695a2e Manuel Franceschini
    pass
1887 6f695a2e Manuel Franceschini
1888 6f695a2e Manuel Franceschini
  def Remove(self):
1889 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1890 6f695a2e Manuel Franceschini

1891 c41eea6e Iustin Pop
    @rtype: boolean
1892 c41eea6e Iustin Pop
    @return: True if the removal was successful
1893 6f695a2e Manuel Franceschini

1894 6f695a2e Manuel Franceschini
    """
1895 6f695a2e Manuel Franceschini
    try:
1896 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1897 6f695a2e Manuel Franceschini
    except OSError, err:
1898 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1899 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1900 6f695a2e Manuel Franceschini
1901 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
1902 bbe4cc16 Iustin Pop
    """Renames the file.
1903 bbe4cc16 Iustin Pop

1904 bbe4cc16 Iustin Pop
    """
1905 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
1906 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
1907 bbe4cc16 Iustin Pop
1908 bbe4cc16 Iustin Pop
  def Grow(self, amount):
1909 bbe4cc16 Iustin Pop
    """Grow the file
1910 bbe4cc16 Iustin Pop

1911 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
1912 bbe4cc16 Iustin Pop

1913 bbe4cc16 Iustin Pop
    """
1914 91e2d9ec Guido Trotter
    # Check that the file exists
1915 91e2d9ec Guido Trotter
    self.Assemble()
1916 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
1917 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
1918 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
1919 91e2d9ec Guido Trotter
    try:
1920 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
1921 91e2d9ec Guido Trotter
      f.truncate(new_size)
1922 91e2d9ec Guido Trotter
      f.close()
1923 91e2d9ec Guido Trotter
    except EnvironmentError, err:
1924 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
1925 bbe4cc16 Iustin Pop
1926 6f695a2e Manuel Franceschini
  def Attach(self):
1927 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1928 6f695a2e Manuel Franceschini

1929 6f695a2e Manuel Franceschini
    Check if this file already exists.
1930 6f695a2e Manuel Franceschini

1931 c41eea6e Iustin Pop
    @rtype: boolean
1932 c41eea6e Iustin Pop
    @return: True if file exists
1933 6f695a2e Manuel Franceschini

1934 6f695a2e Manuel Franceschini
    """
1935 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1936 ecb091e3 Iustin Pop
    return self.attached
1937 6f695a2e Manuel Franceschini
1938 fcff3897 Iustin Pop
  def GetActualSize(self):
1939 fcff3897 Iustin Pop
    """Return the actual disk size.
1940 fcff3897 Iustin Pop

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

1943 fcff3897 Iustin Pop
    """
1944 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
1945 fcff3897 Iustin Pop
    try:
1946 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
1947 fcff3897 Iustin Pop
      return st.st_size
1948 fcff3897 Iustin Pop
    except OSError, err:
1949 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
1950 fcff3897 Iustin Pop
1951 6f695a2e Manuel Franceschini
  @classmethod
1952 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1953 6f695a2e Manuel Franceschini
    """Create a new file.
1954 6f695a2e Manuel Franceschini

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

1957 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
1958 c41eea6e Iustin Pop
    @return: an instance of FileStorage
1959 6f695a2e Manuel Franceschini

1960 6f695a2e Manuel Franceschini
    """
1961 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1962 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1963 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1964 6f695a2e Manuel Franceschini
    try:
1965 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
1966 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
1967 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1968 6f695a2e Manuel Franceschini
      f.close()
1969 cdeefd9b Guido Trotter
    except EnvironmentError, err:
1970 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
1971 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
1972 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
1973 6f695a2e Manuel Franceschini
1974 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
1975 6f695a2e Manuel Franceschini
1976 6f695a2e Manuel Franceschini
1977 a8083063 Iustin Pop
DEV_MAP = {
1978 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1979 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1980 a8083063 Iustin Pop
  }
1981 a8083063 Iustin Pop
1982 cb7c0198 Iustin Pop
if constants.ENABLE_FILE_STORAGE:
1983 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
1984 cb7c0198 Iustin Pop
1985 a8083063 Iustin Pop
1986 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
1987 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1988 a8083063 Iustin Pop

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

1992 a8083063 Iustin Pop
  """
1993 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1994 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1995 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1996 cb999543 Iustin Pop
  if not device.attached:
1997 a8083063 Iustin Pop
    return None
1998 ecb091e3 Iustin Pop
  return device
1999 a8083063 Iustin Pop
2000 a8083063 Iustin Pop
2001 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
2002 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2003 a8083063 Iustin Pop

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

2007 a8083063 Iustin Pop
  """
2008 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2009 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2010 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
2011 1063abd1 Iustin Pop
  device.Assemble()
2012 a8083063 Iustin Pop
  return device
2013 a8083063 Iustin Pop
2014 a8083063 Iustin Pop
2015 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2016 a8083063 Iustin Pop
  """Create a device.
2017 a8083063 Iustin Pop

2018 a8083063 Iustin Pop
  """
2019 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2020 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2021 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2022 a8083063 Iustin Pop
  return device