Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 0fbae49a

History | View | Annotate | Download (63.5 kB)

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

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

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

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

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

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

76 310fbb64 Iustin Pop
  This tries to read the first 128k of the device.
77 310fbb64 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

192 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
193 f3e513ad Iustin Pop

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

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

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

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

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

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

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

224 a8083063 Iustin Pop
    """
225 a8083063 Iustin Pop
    result = True
226 a8083063 Iustin Pop
    if self._children:
227 a8083063 Iustin Pop
      for child in self._children:
228 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
229 a8083063 Iustin Pop
    return result
230 a8083063 Iustin Pop
231 a8083063 Iustin Pop
  def GetSyncStatus(self):
232 a8083063 Iustin Pop
    """Returns the sync status of the device.
233 a8083063 Iustin Pop

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

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

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

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

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

250 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
251 c41eea6e Iustin Pop

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

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

268 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
269 96acbc09 Michael Hanselmann

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

311 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
312 a0c3fea1 Michael Hanselmann

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

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

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

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

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

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

359 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
360 a8083063 Iustin Pop

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

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

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

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

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

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

462 6136f8f0 Iustin Pop
    """
463 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
464 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
465 6136f8f0 Iustin Pop
        utils.any(cls._INVALID_SUBSTRINGS, lambda x: x in name)):
466 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
467 6136f8f0 Iustin Pop
468 a8083063 Iustin Pop
  def Remove(self):
469 a8083063 Iustin Pop
    """Remove this logical volume.
470 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

602 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
603 c41eea6e Iustin Pop

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

621 a8083063 Iustin Pop
    This is a no-op for the LV device type.
622 a8083063 Iustin Pop

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

629 a8083063 Iustin Pop
    This is a no-op for the LV device type.
630 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

849 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
850 c41eea6e Iustin Pop
        for that minor
851 a8083063 Iustin Pop

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

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

884 a8083063 Iustin Pop
    """
885 abdf0113 Iustin Pop
    proc_data = cls._GetProcData()
886 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
887 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
888 abdf0113 Iustin Pop
    if not version:
889 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
890 abdf0113 Iustin Pop
                                    first_line)
891 a8083063 Iustin Pop
892 abdf0113 Iustin Pop
    values = version.groups()
893 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
894 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
895 abdf0113 Iustin Pop
              'k_point': int(values[2]),
896 abdf0113 Iustin Pop
              'api': int(values[3]),
897 abdf0113 Iustin Pop
              'proto': int(values[4]),
898 abdf0113 Iustin Pop
             }
899 abdf0113 Iustin Pop
    if values[5] is not None:
900 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
901 a8083063 Iustin Pop
902 abdf0113 Iustin Pop
    return retval
903 abdf0113 Iustin Pop
904 abdf0113 Iustin Pop
  @staticmethod
905 abdf0113 Iustin Pop
  def _DevPath(minor):
906 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
907 a8083063 Iustin Pop

908 a8083063 Iustin Pop
    """
909 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
910 a8083063 Iustin Pop
911 abdf0113 Iustin Pop
  @classmethod
912 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
913 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
914 a8083063 Iustin Pop

915 a8083063 Iustin Pop
    """
916 abdf0113 Iustin Pop
    data = cls._GetProcData()
917 a8083063 Iustin Pop
918 abdf0113 Iustin Pop
    used_devs = {}
919 abdf0113 Iustin Pop
    for line in data:
920 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
921 abdf0113 Iustin Pop
      if not match:
922 abdf0113 Iustin Pop
        continue
923 abdf0113 Iustin Pop
      minor = int(match.group(1))
924 abdf0113 Iustin Pop
      state = match.group(2)
925 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
926 abdf0113 Iustin Pop
        continue
927 abdf0113 Iustin Pop
      used_devs[minor] = state, line
928 a8083063 Iustin Pop
929 abdf0113 Iustin Pop
    return used_devs
930 a8083063 Iustin Pop
931 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
932 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
933 0834c866 Iustin Pop

934 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
935 a8083063 Iustin Pop

936 a8083063 Iustin Pop
    """
937 abdf0113 Iustin Pop
    if minor is None:
938 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
939 cb999543 Iustin Pop
      self.attached = False
940 a8083063 Iustin Pop
    else:
941 abdf0113 Iustin Pop
      self.minor = minor
942 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
943 cb999543 Iustin Pop
      self.attached = True
944 a8083063 Iustin Pop
945 a8083063 Iustin Pop
  @staticmethod
946 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
947 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
948 a8083063 Iustin Pop

949 abdf0113 Iustin Pop
    This currently only check the size, which must be around
950 abdf0113 Iustin Pop
    128MiB.
951 a8083063 Iustin Pop

952 a8083063 Iustin Pop
    """
953 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
954 abdf0113 Iustin Pop
    if result.failed:
955 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
956 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
957 a8083063 Iustin Pop
    try:
958 abdf0113 Iustin Pop
      sectors = int(result.stdout)
959 691744c4 Iustin Pop
    except (TypeError, ValueError):
960 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
961 abdf0113 Iustin Pop
    bytes = sectors * 512
962 abdf0113 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
963 9c793cfb Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
964 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
965 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
966 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
967 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
968 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
969 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
970 1dc10972 Iustin Pop
    if bytes > 1024 * 1024 * 1024:
971 9c793cfb Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
972 a8083063 Iustin Pop
973 abdf0113 Iustin Pop
  def Rename(self, new_id):
974 abdf0113 Iustin Pop
    """Rename a device.
975 a8083063 Iustin Pop

976 abdf0113 Iustin Pop
    This is not supported for drbd devices.
977 a8083063 Iustin Pop

978 a8083063 Iustin Pop
    """
979 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
980 a8083063 Iustin Pop
981 f3e513ad Iustin Pop
982 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
983 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
984 a2cfdea2 Iustin Pop

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

989 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
990 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
991 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
992 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
993 a2cfdea2 Iustin Pop

994 a2cfdea2 Iustin Pop
  """
995 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
996 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
997 a2cfdea2 Iustin Pop
998 cf8df3f3 Iustin Pop
  # timeout constants
999 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
1000 cf8df3f3 Iustin Pop
1001 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1002 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1003 fc1dc9d7 Iustin Pop
      children = []
1004 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
1005 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1006 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
1007 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1008 310fbb64 Iustin Pop
    (self._lhost, self._lport,
1009 310fbb64 Iustin Pop
     self._rhost, self._rport,
1010 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
1011 310fbb64 Iustin Pop
    if children:
1012 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
1013 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
1014 310fbb64 Iustin Pop
        children = []
1015 464f8daf Iustin Pop
    super(DRBD8, self).__init__(unique_id, children, size)
1016 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1017 c3f9340c Guido Trotter
    version = self._GetVersion()
1018 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
1019 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
1020 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
1021 82463074 Iustin Pop
                  version['k_major'], version['k_minor'])
1022 a2cfdea2 Iustin Pop
1023 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1024 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1025 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1026 ffa1c0dc Iustin Pop
                       (unique_id,))
1027 a2cfdea2 Iustin Pop
    self.Attach()
1028 a2cfdea2 Iustin Pop
1029 a2cfdea2 Iustin Pop
  @classmethod
1030 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1031 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1032 a2cfdea2 Iustin Pop

1033 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1034 a2cfdea2 Iustin Pop

1035 a2cfdea2 Iustin Pop
    """
1036 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1037 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1038 a2cfdea2 Iustin Pop
    if result.failed:
1039 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1040 a2cfdea2 Iustin Pop
1041 a2cfdea2 Iustin Pop
  @classmethod
1042 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1043 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1044 a2cfdea2 Iustin Pop

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

1048 a2cfdea2 Iustin Pop
    """
1049 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1050 a2cfdea2 Iustin Pop
1051 a2cfdea2 Iustin Pop
    highest = None
1052 a2cfdea2 Iustin Pop
    for line in data:
1053 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1054 a2cfdea2 Iustin Pop
      if match:
1055 a2cfdea2 Iustin Pop
        return int(match.group(1))
1056 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1057 a2cfdea2 Iustin Pop
      if match:
1058 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1059 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1060 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1061 a2cfdea2 Iustin Pop
      return 0
1062 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1063 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1064 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1065 a2cfdea2 Iustin Pop
    return highest + 1
1066 a2cfdea2 Iustin Pop
1067 a2cfdea2 Iustin Pop
  @classmethod
1068 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1069 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1070 a2cfdea2 Iustin Pop

1071 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1072 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1073 a2cfdea2 Iustin Pop

1074 a2cfdea2 Iustin Pop
    """
1075 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1076 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1077 a2cfdea2 Iustin Pop
1078 a2cfdea2 Iustin Pop
    # pyparsing setup
1079 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1080 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1081 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1082 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1083 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1084 a2cfdea2 Iustin Pop
1085 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1086 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1087 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1088 a2cfdea2 Iustin Pop
1089 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1090 a2cfdea2 Iustin Pop
1091 a2cfdea2 Iustin Pop
    # value types
1092 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1093 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1094 34e71fea Karsten Keil
    addr_type = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1095 34e71fea Karsten Keil
                 pyp.Optional(pyp.Literal("ipv6")).suppress())
1096 34e71fea Karsten Keil
    addr_port = (addr_type + pyp.Word(pyp.nums + '.') +
1097 34e71fea Karsten Keil
                 pyp.Literal(':').suppress() + number)
1098 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1099 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
1100 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
1101 01e2ce3a Iustin Pop
    # device name, extended syntax
1102 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1103 a2cfdea2 Iustin Pop
1104 a2cfdea2 Iustin Pop
    # a statement
1105 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1106 01e2ce3a Iustin Pop
            pyp.Optional(addr_port ^ value ^ quoted ^ meta_value ^
1107 01e2ce3a Iustin Pop
                         device_value) +
1108 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1109 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1110 a2cfdea2 Iustin Pop
1111 a2cfdea2 Iustin Pop
    # an entire section
1112 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1113 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1114 a2cfdea2 Iustin Pop
1115 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1116 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1117 a2cfdea2 Iustin Pop
1118 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1119 a2cfdea2 Iustin Pop
1120 a2cfdea2 Iustin Pop
    return bnf
1121 a2cfdea2 Iustin Pop
1122 a2cfdea2 Iustin Pop
  @classmethod
1123 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1124 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1125 a2cfdea2 Iustin Pop

1126 a2cfdea2 Iustin Pop
    """
1127 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1128 a2cfdea2 Iustin Pop
    if result.failed:
1129 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1130 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1131 3840729d Iustin Pop
      return None
1132 3840729d Iustin Pop
    return result.stdout
1133 3840729d Iustin Pop
1134 3840729d Iustin Pop
  @classmethod
1135 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1136 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1137 3840729d Iustin Pop

1138 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1139 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1140 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1141 3840729d Iustin Pop
    by _GetShowData.
1142 3840729d Iustin Pop

1143 3840729d Iustin Pop
    """
1144 3840729d Iustin Pop
    data = {}
1145 a2cfdea2 Iustin Pop
    if not out:
1146 a2cfdea2 Iustin Pop
      return data
1147 a2cfdea2 Iustin Pop
1148 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1149 a2cfdea2 Iustin Pop
    # run pyparse
1150 a2cfdea2 Iustin Pop
1151 a2cfdea2 Iustin Pop
    try:
1152 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1153 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1154 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1155 a2cfdea2 Iustin Pop
1156 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1157 a2cfdea2 Iustin Pop
    for section in results:
1158 a2cfdea2 Iustin Pop
      sname = section[0]
1159 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1160 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1161 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1162 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1163 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1164 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1165 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1166 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1167 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1168 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1169 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1170 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1171 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1172 a2cfdea2 Iustin Pop
    return data
1173 a2cfdea2 Iustin Pop
1174 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1175 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1176 a2cfdea2 Iustin Pop

1177 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1178 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1179 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1180 a2cfdea2 Iustin Pop
    device.
1181 a2cfdea2 Iustin Pop

1182 a2cfdea2 Iustin Pop
    """
1183 b00b95dd Iustin Pop
    if self._children:
1184 b00b95dd Iustin Pop
      backend, meta = self._children
1185 b00b95dd Iustin Pop
    else:
1186 b00b95dd Iustin Pop
      backend = meta = None
1187 b00b95dd Iustin Pop
1188 a2cfdea2 Iustin Pop
    if backend is not None:
1189 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1190 a2cfdea2 Iustin Pop
    else:
1191 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1192 b00b95dd Iustin Pop
1193 a2cfdea2 Iustin Pop
    if meta is not None:
1194 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1195 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1196 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1197 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1198 a2cfdea2 Iustin Pop
    else:
1199 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1200 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1201 a2cfdea2 Iustin Pop
    return retval
1202 a2cfdea2 Iustin Pop
1203 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1204 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1205 a2cfdea2 Iustin Pop

1206 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1207 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1208 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1209 a2cfdea2 Iustin Pop
    device.
1210 a2cfdea2 Iustin Pop

1211 a2cfdea2 Iustin Pop
    """
1212 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1213 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1214 a2cfdea2 Iustin Pop
      return True
1215 a2cfdea2 Iustin Pop
1216 a2cfdea2 Iustin Pop
    if self._lhost is None:
1217 a2cfdea2 Iustin Pop
      return False
1218 a2cfdea2 Iustin Pop
1219 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1220 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1221 a2cfdea2 Iustin Pop
      return False
1222 a2cfdea2 Iustin Pop
1223 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1224 a2cfdea2 Iustin Pop
    retval = (retval and
1225 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1226 a2cfdea2 Iustin Pop
    return retval
1227 a2cfdea2 Iustin Pop
1228 a2cfdea2 Iustin Pop
  @classmethod
1229 f069addf Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta, size):
1230 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1231 a2cfdea2 Iustin Pop

1232 a2cfdea2 Iustin Pop
    """
1233 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1234 f069addf Iustin Pop
            backend, meta, "0",
1235 f069addf Iustin Pop
            "-e", "detach",
1236 f069addf Iustin Pop
            "--create-device"]
1237 60bca04a Iustin Pop
    if size:
1238 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1239 89b70f39 Iustin Pop
    if not constants.DRBD_BARRIERS: # disable barriers, if configured so
1240 89b70f39 Iustin Pop
      version = cls._GetVersion()
1241 89b70f39 Iustin Pop
      # various DRBD versions support different disk barrier options;
1242 89b70f39 Iustin Pop
      # what we aim here is to revert back to the 'drain' method of
1243 89b70f39 Iustin Pop
      # disk flushes and to disable metadata barriers, in effect going
1244 89b70f39 Iustin Pop
      # back to pre-8.0.7 behaviour
1245 89b70f39 Iustin Pop
      vmaj = version['k_major']
1246 89b70f39 Iustin Pop
      vmin = version['k_minor']
1247 89b70f39 Iustin Pop
      vrel = version['k_point']
1248 89b70f39 Iustin Pop
      assert vmaj == 8
1249 89b70f39 Iustin Pop
      if vmin == 0: # 8.0.x
1250 89b70f39 Iustin Pop
        if vrel >= 12:
1251 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1252 89b70f39 Iustin Pop
      elif vmin == 2: # 8.2.x
1253 89b70f39 Iustin Pop
        if vrel >= 7:
1254 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1255 89b70f39 Iustin Pop
      elif vmaj >= 3: # 8.3.x or newer
1256 89b70f39 Iustin Pop
        args.extend(['-i', '-a', 'm'])
1257 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1258 a2cfdea2 Iustin Pop
    if result.failed:
1259 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1260 a2cfdea2 Iustin Pop
1261 a2cfdea2 Iustin Pop
  @classmethod
1262 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1263 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1264 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1265 a2cfdea2 Iustin Pop

1266 a2cfdea2 Iustin Pop
    """
1267 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1268 52857176 Iustin Pop
    if None in net_info:
1269 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1270 52857176 Iustin Pop
      # sure its shutdown
1271 1063abd1 Iustin Pop
      cls._ShutdownNet(minor)
1272 1063abd1 Iustin Pop
      return
1273 52857176 Iustin Pop
1274 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1275 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1276 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1277 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1278 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1279 7d585316 Iustin Pop
    # about its peer.
1280 7d585316 Iustin Pop
    cls._SetMinorSyncSpeed(minor, constants.SYNC_SPEED)
1281 7d585316 Iustin Pop
1282 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1283 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1284 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1285 f38478b2 Iustin Pop
            "-B", "consensus",
1286 ab6cc81c Iustin Pop
            "--create-device",
1287 f38478b2 Iustin Pop
            ]
1288 a2cfdea2 Iustin Pop
    if dual_pri:
1289 a2cfdea2 Iustin Pop
      args.append("-m")
1290 a2cfdea2 Iustin Pop
    if hmac and secret:
1291 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1292 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1293 a2cfdea2 Iustin Pop
    if result.failed:
1294 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1295 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1296 a2cfdea2 Iustin Pop
1297 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1298 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1299 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1300 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1301 def8e2f6 Michael Hanselmann
1302 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1303 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1304 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1305 def8e2f6 Michael Hanselmann
1306 def8e2f6 Michael Hanselmann
    try:
1307 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1308 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1309 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1310 a2cfdea2 Iustin Pop
1311 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1312 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1313 b00b95dd Iustin Pop

1314 b00b95dd Iustin Pop
    """
1315 b00b95dd Iustin Pop
    if self.minor is None:
1316 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1317 82463074 Iustin Pop
                  self._aminor)
1318 b00b95dd Iustin Pop
    if len(devices) != 2:
1319 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1320 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1321 03ece5f3 Iustin Pop
    if "local_dev" in info:
1322 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1323 b00b95dd Iustin Pop
    backend, meta = devices
1324 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1325 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1326 b00b95dd Iustin Pop
    backend.Open()
1327 b00b95dd Iustin Pop
    meta.Open()
1328 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1329 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1330 b00b95dd Iustin Pop
1331 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1332 b00b95dd Iustin Pop
    self._children = devices
1333 b00b95dd Iustin Pop
1334 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1335 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1336 b00b95dd Iustin Pop

1337 b00b95dd Iustin Pop
    """
1338 b00b95dd Iustin Pop
    if self.minor is None:
1339 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1340 82463074 Iustin Pop
                  self._aminor)
1341 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1342 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1343 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1344 03ece5f3 Iustin Pop
      return
1345 b00b95dd Iustin Pop
    if len(self._children) != 2:
1346 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1347 82463074 Iustin Pop
                  self._children)
1348 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1349 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1350 e739bd57 Iustin Pop
      return
1351 b00b95dd Iustin Pop
    if len(devices) != 2:
1352 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1353 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1354 e739bd57 Iustin Pop
      if dev != child.dev_path:
1355 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1356 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1357 b00b95dd Iustin Pop
1358 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1359 b00b95dd Iustin Pop
    self._children = []
1360 b00b95dd Iustin Pop
1361 7d585316 Iustin Pop
  @classmethod
1362 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1363 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1364 a2cfdea2 Iustin Pop

1365 7d585316 Iustin Pop
    This is the low-level implementation.
1366 7d585316 Iustin Pop

1367 7d585316 Iustin Pop
    @type minor: int
1368 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1369 7d585316 Iustin Pop
    @type kbytes: int
1370 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1371 7d585316 Iustin Pop
    @rtype: boolean
1372 7d585316 Iustin Pop
    @return: the success of the operation
1373 7d585316 Iustin Pop

1374 a2cfdea2 Iustin Pop
    """
1375 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1376 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1377 a2cfdea2 Iustin Pop
    if result.failed:
1378 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1379 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1380 7d585316 Iustin Pop
    return not result.failed
1381 7d585316 Iustin Pop
1382 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1383 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1384 7d585316 Iustin Pop

1385 7d585316 Iustin Pop
    @type kbytes: int
1386 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1387 7d585316 Iustin Pop
    @rtype: boolean
1388 7d585316 Iustin Pop
    @return: the success of the operation
1389 7d585316 Iustin Pop

1390 7d585316 Iustin Pop
    """
1391 7d585316 Iustin Pop
    if self.minor is None:
1392 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1393 7d585316 Iustin Pop
      return False
1394 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1395 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1396 a2cfdea2 Iustin Pop
1397 6b90c22e Iustin Pop
  def GetProcStatus(self):
1398 6b90c22e Iustin Pop
    """Return device data from /proc.
1399 6b90c22e Iustin Pop

1400 6b90c22e Iustin Pop
    """
1401 6b90c22e Iustin Pop
    if self.minor is None:
1402 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1403 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1404 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1405 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1406 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1407 6b90c22e Iustin Pop
1408 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1409 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1410 a2cfdea2 Iustin Pop

1411 a2cfdea2 Iustin Pop

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

1416 0834c866 Iustin Pop

1417 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1418 0834c866 Iustin Pop
    network not connected or local disk missing.
1419 0834c866 Iustin Pop

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

1423 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1424 c41eea6e Iustin Pop

1425 a2cfdea2 Iustin Pop
    """
1426 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1427 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1428 96acbc09 Michael Hanselmann
1429 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1430 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1431 f208978a Michael Hanselmann
1432 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1433 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1434 f208978a Michael Hanselmann
    elif stats.is_diskless:
1435 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1436 f208978a Michael Hanselmann
    else:
1437 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1438 96acbc09 Michael Hanselmann
1439 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1440 96acbc09 Michael Hanselmann
                                  major=self.major,
1441 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1442 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1443 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1444 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1445 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1446 a2cfdea2 Iustin Pop
1447 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1448 a2cfdea2 Iustin Pop
    """Make the local state primary.
1449 a2cfdea2 Iustin Pop

1450 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1451 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1452 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1453 f860ff4e Guido Trotter
    is mandatory.
1454 a2cfdea2 Iustin Pop

1455 a2cfdea2 Iustin Pop
    """
1456 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1457 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1458 a2cfdea2 Iustin Pop
      return False
1459 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1460 a2cfdea2 Iustin Pop
    if force:
1461 a2cfdea2 Iustin Pop
      cmd.append("-o")
1462 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1463 a2cfdea2 Iustin Pop
    if result.failed:
1464 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1465 82463074 Iustin Pop
                  result.output)
1466 a2cfdea2 Iustin Pop
1467 a2cfdea2 Iustin Pop
  def Close(self):
1468 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1469 a2cfdea2 Iustin Pop

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

1472 a2cfdea2 Iustin Pop
    """
1473 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1474 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1475 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1476 a2cfdea2 Iustin Pop
    if result.failed:
1477 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1478 82463074 Iustin Pop
                  self.minor, result.output)
1479 a2cfdea2 Iustin Pop
1480 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1481 cf8df3f3 Iustin Pop
    """Removes network configuration.
1482 cf8df3f3 Iustin Pop

1483 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1484 cf8df3f3 Iustin Pop

1485 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1486 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1487 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1488 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1489 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1490 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1491 cf8df3f3 Iustin Pop
    exception.
1492 cf8df3f3 Iustin Pop

1493 cf8df3f3 Iustin Pop
    """
1494 cf8df3f3 Iustin Pop
    if self.minor is None:
1495 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1496 cf8df3f3 Iustin Pop
1497 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1498 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1499 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1500 cf8df3f3 Iustin Pop
1501 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1502 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1503 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1504 cf8df3f3 Iustin Pop
1505 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1506 def8e2f6 Michael Hanselmann
1507 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1508 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1509 def8e2f6 Michael Hanselmann
        return
1510 def8e2f6 Michael Hanselmann
1511 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1512 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1513 def8e2f6 Michael Hanselmann
      # forgotten
1514 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1515 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1516 def8e2f6 Michael Hanselmann
1517 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1518 def8e2f6 Michael Hanselmann
1519 def8e2f6 Michael Hanselmann
    # Keep start time
1520 def8e2f6 Michael Hanselmann
    start_time = time.time()
1521 def8e2f6 Michael Hanselmann
1522 def8e2f6 Michael Hanselmann
    try:
1523 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1524 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1525 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1526 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1527 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1528 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1529 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1530 cf8df3f3 Iustin Pop
      else:
1531 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1532 def8e2f6 Michael Hanselmann
1533 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1534 cf8df3f3 Iustin Pop
1535 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1536 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1537 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1538 82463074 Iustin Pop
                   self.minor, reconfig_time)
1539 cf8df3f3 Iustin Pop
1540 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1541 cf8df3f3 Iustin Pop
    """Reconnects the network.
1542 cf8df3f3 Iustin Pop

1543 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1544 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1545 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1546 cf8df3f3 Iustin Pop

1547 cf8df3f3 Iustin Pop
    Args:
1548 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1549 cf8df3f3 Iustin Pop

1550 cf8df3f3 Iustin Pop
    """
1551 cf8df3f3 Iustin Pop
    if self.minor is None:
1552 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1553 cf8df3f3 Iustin Pop
1554 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1555 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1556 cf8df3f3 Iustin Pop
1557 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1558 cf8df3f3 Iustin Pop
1559 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1560 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1561 cf8df3f3 Iustin Pop
1562 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1563 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1564 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1565 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1566 cf8df3f3 Iustin Pop
1567 a2cfdea2 Iustin Pop
  def Attach(self):
1568 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1569 2d0c8319 Iustin Pop

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

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

1577 2d0c8319 Iustin Pop
    """
1578 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1579 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1580 2d0c8319 Iustin Pop
      minor = self._aminor
1581 2d0c8319 Iustin Pop
    else:
1582 2d0c8319 Iustin Pop
      minor = None
1583 2d0c8319 Iustin Pop
1584 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1585 2d0c8319 Iustin Pop
    return minor is not None
1586 2d0c8319 Iustin Pop
1587 2d0c8319 Iustin Pop
  def Assemble(self):
1588 2d0c8319 Iustin Pop
    """Assemble the drbd.
1589 2d0c8319 Iustin Pop

1590 2d0c8319 Iustin Pop
    Method:
1591 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1592 2d0c8319 Iustin Pop
        our config
1593 2d0c8319 Iustin Pop
      - if not, we create it from zero
1594 2d0c8319 Iustin Pop

1595 2d0c8319 Iustin Pop
    """
1596 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1597 2d0c8319 Iustin Pop
1598 2d0c8319 Iustin Pop
    self.Attach()
1599 2d0c8319 Iustin Pop
    if self.minor is None:
1600 2d0c8319 Iustin Pop
      # local device completely unconfigured
1601 1063abd1 Iustin Pop
      self._FastAssemble()
1602 2d0c8319 Iustin Pop
    else:
1603 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1604 2d0c8319 Iustin Pop
      # the device
1605 1063abd1 Iustin Pop
      self._SlowAssemble()
1606 2d0c8319 Iustin Pop
1607 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1608 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1609 a2cfdea2 Iustin Pop

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

1614 a2cfdea2 Iustin Pop
    """
1615 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
1616 527a15ac Iustin Pop
    # pylint: disable-msg=W0631
1617 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1618 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1619 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1620 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1621 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1622 1063abd1 Iustin Pop
1623 a2cfdea2 Iustin Pop
      if match_l and match_r:
1624 1063abd1 Iustin Pop
        # everything matches
1625 a2cfdea2 Iustin Pop
        break
1626 1063abd1 Iustin Pop
1627 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1628 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1629 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1630 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1631 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1632 1063abd1 Iustin Pop
          break
1633 1063abd1 Iustin Pop
        else:
1634 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1635 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1636 1063abd1 Iustin Pop
1637 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1638 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1639 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1640 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1641 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1642 1063abd1 Iustin Pop
          break
1643 1063abd1 Iustin Pop
        else:
1644 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1645 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1646 bf25af3b Iustin Pop
1647 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1648 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1649 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1650 bf25af3b Iustin Pop
      # configuration
1651 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1652 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1653 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1654 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1655 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1656 9cdbe77f Iustin Pop
        # reconnect to our correct one
1657 1063abd1 Iustin Pop
        try:
1658 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1659 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1660 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1661 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1662 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1663 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1664 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1665 9cdbe77f Iustin Pop
        # None)
1666 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1667 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1668 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1669 9cdbe77f Iustin Pop
          break
1670 1063abd1 Iustin Pop
        else:
1671 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1672 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1673 9cdbe77f Iustin Pop
1674 a2cfdea2 Iustin Pop
    else:
1675 a2cfdea2 Iustin Pop
      minor = None
1676 a2cfdea2 Iustin Pop
1677 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1678 1063abd1 Iustin Pop
    if minor is None:
1679 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1680 1063abd1 Iustin Pop
                  self._aminor)
1681 a2cfdea2 Iustin Pop
1682 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1683 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1684 a2cfdea2 Iustin Pop

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

1687 a2cfdea2 Iustin Pop
    """
1688 a1578d63 Iustin Pop
    minor = self._aminor
1689 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1690 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1691 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1692 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1693 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1694 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1695 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1696 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1697 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1698 a2cfdea2 Iustin Pop
1699 a2cfdea2 Iustin Pop
  @classmethod
1700 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1701 b00b95dd Iustin Pop
    """Detach from the local device.
1702 b00b95dd Iustin Pop

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

1706 b00b95dd Iustin Pop
    """
1707 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1708 b00b95dd Iustin Pop
    if result.failed:
1709 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1710 b00b95dd Iustin Pop
1711 b00b95dd Iustin Pop
  @classmethod
1712 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1713 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1714 f3e513ad Iustin Pop

1715 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1716 f3e513ad Iustin Pop

1717 f3e513ad Iustin Pop
    """
1718 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1719 a8459f1c Iustin Pop
    if result.failed:
1720 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1721 f3e513ad Iustin Pop
1722 f3e513ad Iustin Pop
  @classmethod
1723 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1724 a2cfdea2 Iustin Pop
    """Deactivate the device.
1725 a2cfdea2 Iustin Pop

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

1728 a2cfdea2 Iustin Pop
    """
1729 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1730 a2cfdea2 Iustin Pop
    if result.failed:
1731 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1732 33bc6f01 Iustin Pop
                  minor, result.output)
1733 a2cfdea2 Iustin Pop
1734 a2cfdea2 Iustin Pop
  def Shutdown(self):
1735 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1736 a2cfdea2 Iustin Pop

1737 a2cfdea2 Iustin Pop
    """
1738 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1739 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1740 746f7476 Iustin Pop
      return
1741 746f7476 Iustin Pop
    minor = self.minor
1742 a2cfdea2 Iustin Pop
    self.minor = None
1743 a2cfdea2 Iustin Pop
    self.dev_path = None
1744 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1745 a2cfdea2 Iustin Pop
1746 a2cfdea2 Iustin Pop
  def Remove(self):
1747 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1748 a2cfdea2 Iustin Pop

1749 a2cfdea2 Iustin Pop
    """
1750 0c6c04ec Iustin Pop
    self.Shutdown()
1751 a2cfdea2 Iustin Pop
1752 a2cfdea2 Iustin Pop
  @classmethod
1753 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1754 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1755 a2cfdea2 Iustin Pop

1756 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1757 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1758 a2cfdea2 Iustin Pop

1759 a2cfdea2 Iustin Pop
    """
1760 a2cfdea2 Iustin Pop
    if len(children) != 2:
1761 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1762 767d52d3 Iustin Pop
    # check that the minor is unused
1763 767d52d3 Iustin Pop
    aminor = unique_id[4]
1764 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1765 767d52d3 Iustin Pop
    if aminor in proc_info:
1766 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1767 767d52d3 Iustin Pop
      in_use = status.is_in_use
1768 767d52d3 Iustin Pop
    else:
1769 767d52d3 Iustin Pop
      in_use = False
1770 767d52d3 Iustin Pop
    if in_use:
1771 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1772 a2cfdea2 Iustin Pop
    meta = children[1]
1773 a2cfdea2 Iustin Pop
    meta.Assemble()
1774 a2cfdea2 Iustin Pop
    if not meta.Attach():
1775 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1776 33bc6f01 Iustin Pop
                  aminor, meta)
1777 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1778 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1779 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1780 a2cfdea2 Iustin Pop
1781 1005d816 Iustin Pop
  def Grow(self, amount):
1782 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1783 1005d816 Iustin Pop

1784 1005d816 Iustin Pop
    """
1785 1005d816 Iustin Pop
    if self.minor is None:
1786 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1787 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1788 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1789 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1790 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1791 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1792 1005d816 Iustin Pop
    if result.failed:
1793 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1794 1005d816 Iustin Pop
1795 a8083063 Iustin Pop
1796 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1797 6f695a2e Manuel Franceschini
  """File device.
1798 abdf0113 Iustin Pop

1799 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1800 6f695a2e Manuel Franceschini

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

1803 6f695a2e Manuel Franceschini
  """
1804 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1805 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1806 6f695a2e Manuel Franceschini

1807 6f695a2e Manuel Franceschini
    """
1808 6f695a2e Manuel Franceschini
    if children:
1809 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1810 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1811 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1812 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1813 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1814 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1815 ecb091e3 Iustin Pop
    self.Attach()
1816 6f695a2e Manuel Franceschini
1817 6f695a2e Manuel Franceschini
  def Assemble(self):
1818 6f695a2e Manuel Franceschini
    """Assemble the device.
1819 6f695a2e Manuel Franceschini

1820 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1821 6f695a2e Manuel Franceschini

1822 6f695a2e Manuel Franceschini
    """
1823 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1824 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1825 6f695a2e Manuel Franceschini
1826 6f695a2e Manuel Franceschini
  def Shutdown(self):
1827 6f695a2e Manuel Franceschini
    """Shutdown the device.
1828 6f695a2e Manuel Franceschini

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

1832 6f695a2e Manuel Franceschini
    """
1833 746f7476 Iustin Pop
    pass
1834 6f695a2e Manuel Franceschini
1835 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1836 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1837 6f695a2e Manuel Franceschini

1838 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1839 6f695a2e Manuel Franceschini

1840 6f695a2e Manuel Franceschini
    """
1841 6f695a2e Manuel Franceschini
    pass
1842 6f695a2e Manuel Franceschini
1843 6f695a2e Manuel Franceschini
  def Close(self):
1844 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1845 6f695a2e Manuel Franceschini

1846 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1847 6f695a2e Manuel Franceschini

1848 6f695a2e Manuel Franceschini
    """
1849 6f695a2e Manuel Franceschini
    pass
1850 6f695a2e Manuel Franceschini
1851 6f695a2e Manuel Franceschini
  def Remove(self):
1852 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1853 6f695a2e Manuel Franceschini

1854 c41eea6e Iustin Pop
    @rtype: boolean
1855 c41eea6e Iustin Pop
    @return: True if the removal was successful
1856 6f695a2e Manuel Franceschini

1857 6f695a2e Manuel Franceschini
    """
1858 6f695a2e Manuel Franceschini
    try:
1859 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1860 6f695a2e Manuel Franceschini
    except OSError, err:
1861 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1862 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1863 6f695a2e Manuel Franceschini
1864 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
1865 bbe4cc16 Iustin Pop
    """Renames the file.
1866 bbe4cc16 Iustin Pop

1867 bbe4cc16 Iustin Pop
    """
1868 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
1869 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
1870 bbe4cc16 Iustin Pop
1871 bbe4cc16 Iustin Pop
  def Grow(self, amount):
1872 bbe4cc16 Iustin Pop
    """Grow the file
1873 bbe4cc16 Iustin Pop

1874 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
1875 bbe4cc16 Iustin Pop

1876 bbe4cc16 Iustin Pop
    """
1877 91e2d9ec Guido Trotter
    # Check that the file exists
1878 91e2d9ec Guido Trotter
    self.Assemble()
1879 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
1880 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
1881 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
1882 91e2d9ec Guido Trotter
    try:
1883 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
1884 91e2d9ec Guido Trotter
      f.truncate(new_size)
1885 91e2d9ec Guido Trotter
      f.close()
1886 91e2d9ec Guido Trotter
    except EnvironmentError, err:
1887 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
1888 bbe4cc16 Iustin Pop
1889 6f695a2e Manuel Franceschini
  def Attach(self):
1890 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1891 6f695a2e Manuel Franceschini

1892 6f695a2e Manuel Franceschini
    Check if this file already exists.
1893 6f695a2e Manuel Franceschini

1894 c41eea6e Iustin Pop
    @rtype: boolean
1895 c41eea6e Iustin Pop
    @return: True if file exists
1896 6f695a2e Manuel Franceschini

1897 6f695a2e Manuel Franceschini
    """
1898 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1899 ecb091e3 Iustin Pop
    return self.attached
1900 6f695a2e Manuel Franceschini
1901 fcff3897 Iustin Pop
  def GetActualSize(self):
1902 fcff3897 Iustin Pop
    """Return the actual disk size.
1903 fcff3897 Iustin Pop

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

1906 fcff3897 Iustin Pop
    """
1907 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
1908 fcff3897 Iustin Pop
    try:
1909 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
1910 fcff3897 Iustin Pop
      return st.st_size
1911 fcff3897 Iustin Pop
    except OSError, err:
1912 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
1913 fcff3897 Iustin Pop
1914 6f695a2e Manuel Franceschini
  @classmethod
1915 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1916 6f695a2e Manuel Franceschini
    """Create a new file.
1917 6f695a2e Manuel Franceschini

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

1920 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
1921 c41eea6e Iustin Pop
    @return: an instance of FileStorage
1922 6f695a2e Manuel Franceschini

1923 6f695a2e Manuel Franceschini
    """
1924 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1925 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1926 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1927 6f695a2e Manuel Franceschini
    try:
1928 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
1929 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
1930 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1931 6f695a2e Manuel Franceschini
      f.close()
1932 cdeefd9b Guido Trotter
    except EnvironmentError, err:
1933 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
1934 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
1935 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
1936 6f695a2e Manuel Franceschini
1937 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
1938 6f695a2e Manuel Franceschini
1939 6f695a2e Manuel Franceschini
1940 a8083063 Iustin Pop
DEV_MAP = {
1941 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1942 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1943 a8083063 Iustin Pop
  }
1944 a8083063 Iustin Pop
1945 cb7c0198 Iustin Pop
if constants.ENABLE_FILE_STORAGE:
1946 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
1947 cb7c0198 Iustin Pop
1948 a8083063 Iustin Pop
1949 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
1950 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1951 a8083063 Iustin Pop

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

1955 a8083063 Iustin Pop
  """
1956 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1957 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1958 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1959 cb999543 Iustin Pop
  if not device.attached:
1960 a8083063 Iustin Pop
    return None
1961 ecb091e3 Iustin Pop
  return device
1962 a8083063 Iustin Pop
1963 a8083063 Iustin Pop
1964 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
1965 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1966 a8083063 Iustin Pop

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

1970 a8083063 Iustin Pop
  """
1971 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1972 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1973 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1974 1063abd1 Iustin Pop
  device.Assemble()
1975 a8083063 Iustin Pop
  return device
1976 a8083063 Iustin Pop
1977 a8083063 Iustin Pop
1978 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1979 a8083063 Iustin Pop
  """Create a device.
1980 a8083063 Iustin Pop

1981 a8083063 Iustin Pop
  """
1982 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1983 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1984 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1985 a8083063 Iustin Pop
  return device