Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ adb6d685

History | View | Annotate | Download (63.6 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1412 a2cfdea2 Iustin Pop

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

1417 0834c866 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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