Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 8062638d

History | View | Annotate | Download (65 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 fcee765d Manuel Franceschini
# Copyright (C) 2006, 2007, 2010 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Block device abstraction"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import re
25 a8083063 Iustin Pop
import time
26 a8083063 Iustin Pop
import errno
27 a2cfdea2 Iustin Pop
import pyparsing as pyp
28 6f695a2e Manuel Franceschini
import os
29 468c5f77 Iustin Pop
import logging
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
from ganeti import utils
32 a8083063 Iustin Pop
from ganeti import errors
33 fe96220b Iustin Pop
from ganeti import constants
34 96acbc09 Michael Hanselmann
from ganeti import objects
35 cea881e5 Michael Hanselmann
from ganeti import compat
36 a744b676 Manuel Franceschini
from ganeti import netutils
37 a8083063 Iustin Pop
38 a8083063 Iustin Pop
39 310fbb64 Iustin Pop
# Size of reads in _CanReadDevice
40 310fbb64 Iustin Pop
_DEVICE_READ_SIZE = 128 * 1024
41 310fbb64 Iustin Pop
42 310fbb64 Iustin Pop
43 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
44 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
45 82463074 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

226 a8083063 Iustin Pop
    """
227 a8083063 Iustin Pop
    result = True
228 a8083063 Iustin Pop
    if self._children:
229 a8083063 Iustin Pop
      for child in self._children:
230 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
231 a8083063 Iustin Pop
    return result
232 a8083063 Iustin Pop
233 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 fcee765d Manuel Franceschini
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\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 fcee765d Manuel Franceschini
  def _GetVersion(cls, proc_data):
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
    first_line = proc_data[0].strip()
889 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
890 abdf0113 Iustin Pop
    if not version:
891 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
892 abdf0113 Iustin Pop
                                    first_line)
893 a8083063 Iustin Pop
894 abdf0113 Iustin Pop
    values = version.groups()
895 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
896 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
897 abdf0113 Iustin Pop
              'k_point': int(values[2]),
898 abdf0113 Iustin Pop
              'api': int(values[3]),
899 abdf0113 Iustin Pop
              'proto': int(values[4]),
900 abdf0113 Iustin Pop
             }
901 abdf0113 Iustin Pop
    if values[5] is not None:
902 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
903 a8083063 Iustin Pop
904 abdf0113 Iustin Pop
    return retval
905 abdf0113 Iustin Pop
906 abdf0113 Iustin Pop
  @staticmethod
907 549071a0 Luca Bigliardi
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
908 549071a0 Luca Bigliardi
    """Returns DRBD usermode_helper currently set.
909 549071a0 Luca Bigliardi

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1447 a2cfdea2 Iustin Pop

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

1452 0834c866 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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