Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 3d16a983

History | View | Annotate | Download (64.2 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 403f5172 Guido Trotter
    if compat.any(":" in v for v in pvlist):
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 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
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 5a672c30 Manuel Franceschini
    lbracket = pyp.Literal("[").suppress()
1083 5a672c30 Manuel Franceschini
    rbracket = pyp.Literal("]").suppress()
1084 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1085 5a672c30 Manuel Franceschini
    colon = pyp.Literal(":").suppress()
1086 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1087 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1088 a2cfdea2 Iustin Pop
1089 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1090 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1091 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1092 a2cfdea2 Iustin Pop
1093 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1094 a2cfdea2 Iustin Pop
1095 a2cfdea2 Iustin Pop
    # value types
1096 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1097 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1098 5a672c30 Manuel Franceschini
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1099 5a672c30 Manuel Franceschini
                 pyp.Word(pyp.nums + ".") + colon + number)
1100 5a672c30 Manuel Franceschini
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
1101 5a672c30 Manuel Franceschini
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
1102 5a672c30 Manuel Franceschini
                 pyp.Optional(rbracket) + colon + number)
1103 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1104 5a672c30 Manuel Franceschini
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
1105 01e2ce3a Iustin Pop
    # device name, extended syntax
1106 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1107 a2cfdea2 Iustin Pop
1108 a2cfdea2 Iustin Pop
    # a statement
1109 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1110 5a672c30 Manuel Franceschini
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
1111 01e2ce3a Iustin Pop
                         device_value) +
1112 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1113 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1114 a2cfdea2 Iustin Pop
1115 a2cfdea2 Iustin Pop
    # an entire section
1116 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1117 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1118 a2cfdea2 Iustin Pop
1119 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1120 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1121 a2cfdea2 Iustin Pop
1122 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1123 a2cfdea2 Iustin Pop
1124 a2cfdea2 Iustin Pop
    return bnf
1125 a2cfdea2 Iustin Pop
1126 a2cfdea2 Iustin Pop
  @classmethod
1127 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1128 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1129 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

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

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

1332 b00b95dd Iustin Pop
    """
1333 b00b95dd Iustin Pop
    if self.minor is None:
1334 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1335 82463074 Iustin Pop
                  self._aminor)
1336 b00b95dd Iustin Pop
    if len(devices) != 2:
1337 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1338 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1339 03ece5f3 Iustin Pop
    if "local_dev" in info:
1340 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1341 b00b95dd Iustin Pop
    backend, meta = devices
1342 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1343 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1344 b00b95dd Iustin Pop
    backend.Open()
1345 b00b95dd Iustin Pop
    meta.Open()
1346 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1347 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1348 b00b95dd Iustin Pop
1349 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1350 b00b95dd Iustin Pop
    self._children = devices
1351 b00b95dd Iustin Pop
1352 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1353 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1354 b00b95dd Iustin Pop

1355 b00b95dd Iustin Pop
    """
1356 b00b95dd Iustin Pop
    if self.minor is None:
1357 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1358 82463074 Iustin Pop
                  self._aminor)
1359 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1360 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1361 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1362 03ece5f3 Iustin Pop
      return
1363 b00b95dd Iustin Pop
    if len(self._children) != 2:
1364 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1365 82463074 Iustin Pop
                  self._children)
1366 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1367 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1368 e739bd57 Iustin Pop
      return
1369 b00b95dd Iustin Pop
    if len(devices) != 2:
1370 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1371 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1372 e739bd57 Iustin Pop
      if dev != child.dev_path:
1373 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1374 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1375 b00b95dd Iustin Pop
1376 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1377 b00b95dd Iustin Pop
    self._children = []
1378 b00b95dd Iustin Pop
1379 7d585316 Iustin Pop
  @classmethod
1380 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1381 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1382 a2cfdea2 Iustin Pop

1383 7d585316 Iustin Pop
    This is the low-level implementation.
1384 7d585316 Iustin Pop

1385 7d585316 Iustin Pop
    @type minor: int
1386 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1387 7d585316 Iustin Pop
    @type kbytes: int
1388 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1389 7d585316 Iustin Pop
    @rtype: boolean
1390 7d585316 Iustin Pop
    @return: the success of the operation
1391 7d585316 Iustin Pop

1392 a2cfdea2 Iustin Pop
    """
1393 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1394 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1395 a2cfdea2 Iustin Pop
    if result.failed:
1396 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1397 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1398 7d585316 Iustin Pop
    return not result.failed
1399 7d585316 Iustin Pop
1400 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1401 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1402 7d585316 Iustin Pop

1403 7d585316 Iustin Pop
    @type kbytes: int
1404 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1405 7d585316 Iustin Pop
    @rtype: boolean
1406 7d585316 Iustin Pop
    @return: the success of the operation
1407 7d585316 Iustin Pop

1408 7d585316 Iustin Pop
    """
1409 7d585316 Iustin Pop
    if self.minor is None:
1410 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1411 7d585316 Iustin Pop
      return False
1412 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1413 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1414 a2cfdea2 Iustin Pop
1415 6b90c22e Iustin Pop
  def GetProcStatus(self):
1416 6b90c22e Iustin Pop
    """Return device data from /proc.
1417 6b90c22e Iustin Pop

1418 6b90c22e Iustin Pop
    """
1419 6b90c22e Iustin Pop
    if self.minor is None:
1420 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1421 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1422 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1423 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1424 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1425 6b90c22e Iustin Pop
1426 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1427 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1428 a2cfdea2 Iustin Pop

1429 a2cfdea2 Iustin Pop

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

1434 0834c866 Iustin Pop

1435 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1436 0834c866 Iustin Pop
    network not connected or local disk missing.
1437 0834c866 Iustin Pop

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

1441 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1442 c41eea6e Iustin Pop

1443 a2cfdea2 Iustin Pop
    """
1444 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1445 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1446 96acbc09 Michael Hanselmann
1447 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1448 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1449 f208978a Michael Hanselmann
1450 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1451 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1452 f208978a Michael Hanselmann
    elif stats.is_diskless:
1453 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1454 f208978a Michael Hanselmann
    else:
1455 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1456 96acbc09 Michael Hanselmann
1457 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1458 96acbc09 Michael Hanselmann
                                  major=self.major,
1459 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1460 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1461 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1462 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1463 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1464 a2cfdea2 Iustin Pop
1465 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1466 a2cfdea2 Iustin Pop
    """Make the local state primary.
1467 a2cfdea2 Iustin Pop

1468 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1469 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1470 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1471 f860ff4e Guido Trotter
    is mandatory.
1472 a2cfdea2 Iustin Pop

1473 a2cfdea2 Iustin Pop
    """
1474 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1475 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1476 a2cfdea2 Iustin Pop
      return False
1477 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1478 a2cfdea2 Iustin Pop
    if force:
1479 a2cfdea2 Iustin Pop
      cmd.append("-o")
1480 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1481 a2cfdea2 Iustin Pop
    if result.failed:
1482 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1483 82463074 Iustin Pop
                  result.output)
1484 a2cfdea2 Iustin Pop
1485 a2cfdea2 Iustin Pop
  def Close(self):
1486 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1487 a2cfdea2 Iustin Pop

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

1490 a2cfdea2 Iustin Pop
    """
1491 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1492 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1493 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1494 a2cfdea2 Iustin Pop
    if result.failed:
1495 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1496 82463074 Iustin Pop
                  self.minor, result.output)
1497 a2cfdea2 Iustin Pop
1498 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1499 cf8df3f3 Iustin Pop
    """Removes network configuration.
1500 cf8df3f3 Iustin Pop

1501 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1502 cf8df3f3 Iustin Pop

1503 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1504 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1505 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1506 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1507 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1508 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1509 cf8df3f3 Iustin Pop
    exception.
1510 cf8df3f3 Iustin Pop

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

1561 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1562 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1563 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1564 cf8df3f3 Iustin Pop

1565 cf8df3f3 Iustin Pop
    Args:
1566 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1567 cf8df3f3 Iustin Pop

1568 cf8df3f3 Iustin Pop
    """
1569 cf8df3f3 Iustin Pop
    if self.minor is None:
1570 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1571 cf8df3f3 Iustin Pop
1572 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1573 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1574 cf8df3f3 Iustin Pop
1575 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1576 cf8df3f3 Iustin Pop
1577 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1578 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1579 cf8df3f3 Iustin Pop
1580 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1581 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1582 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1583 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1584 cf8df3f3 Iustin Pop
1585 a2cfdea2 Iustin Pop
  def Attach(self):
1586 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1587 2d0c8319 Iustin Pop

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

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

1595 2d0c8319 Iustin Pop
    """
1596 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1597 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1598 2d0c8319 Iustin Pop
      minor = self._aminor
1599 2d0c8319 Iustin Pop
    else:
1600 2d0c8319 Iustin Pop
      minor = None
1601 2d0c8319 Iustin Pop
1602 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1603 2d0c8319 Iustin Pop
    return minor is not None
1604 2d0c8319 Iustin Pop
1605 2d0c8319 Iustin Pop
  def Assemble(self):
1606 2d0c8319 Iustin Pop
    """Assemble the drbd.
1607 2d0c8319 Iustin Pop

1608 2d0c8319 Iustin Pop
    Method:
1609 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1610 2d0c8319 Iustin Pop
        our config
1611 2d0c8319 Iustin Pop
      - if not, we create it from zero
1612 2d0c8319 Iustin Pop

1613 2d0c8319 Iustin Pop
    """
1614 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1615 2d0c8319 Iustin Pop
1616 2d0c8319 Iustin Pop
    self.Attach()
1617 2d0c8319 Iustin Pop
    if self.minor is None:
1618 2d0c8319 Iustin Pop
      # local device completely unconfigured
1619 1063abd1 Iustin Pop
      self._FastAssemble()
1620 2d0c8319 Iustin Pop
    else:
1621 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1622 2d0c8319 Iustin Pop
      # the device
1623 1063abd1 Iustin Pop
      self._SlowAssemble()
1624 2d0c8319 Iustin Pop
1625 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1626 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1627 a2cfdea2 Iustin Pop

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

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

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

1705 a2cfdea2 Iustin Pop
    """
1706 a1578d63 Iustin Pop
    minor = self._aminor
1707 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1708 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1709 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1710 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1711 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1712 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1713 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1714 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1715 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1716 a2cfdea2 Iustin Pop
1717 a2cfdea2 Iustin Pop
  @classmethod
1718 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1719 b00b95dd Iustin Pop
    """Detach from the local device.
1720 b00b95dd Iustin Pop

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

1724 b00b95dd Iustin Pop
    """
1725 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1726 b00b95dd Iustin Pop
    if result.failed:
1727 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1728 b00b95dd Iustin Pop
1729 b00b95dd Iustin Pop
  @classmethod
1730 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1731 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1732 f3e513ad Iustin Pop

1733 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1734 f3e513ad Iustin Pop

1735 f3e513ad Iustin Pop
    """
1736 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1737 a8459f1c Iustin Pop
    if result.failed:
1738 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1739 f3e513ad Iustin Pop
1740 f3e513ad Iustin Pop
  @classmethod
1741 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1742 a2cfdea2 Iustin Pop
    """Deactivate the device.
1743 a2cfdea2 Iustin Pop

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

1746 a2cfdea2 Iustin Pop
    """
1747 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1748 a2cfdea2 Iustin Pop
    if result.failed:
1749 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1750 33bc6f01 Iustin Pop
                  minor, result.output)
1751 a2cfdea2 Iustin Pop
1752 a2cfdea2 Iustin Pop
  def Shutdown(self):
1753 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1754 a2cfdea2 Iustin Pop

1755 a2cfdea2 Iustin Pop
    """
1756 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1757 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1758 746f7476 Iustin Pop
      return
1759 746f7476 Iustin Pop
    minor = self.minor
1760 a2cfdea2 Iustin Pop
    self.minor = None
1761 a2cfdea2 Iustin Pop
    self.dev_path = None
1762 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1763 a2cfdea2 Iustin Pop
1764 a2cfdea2 Iustin Pop
  def Remove(self):
1765 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1766 a2cfdea2 Iustin Pop

1767 a2cfdea2 Iustin Pop
    """
1768 0c6c04ec Iustin Pop
    self.Shutdown()
1769 a2cfdea2 Iustin Pop
1770 a2cfdea2 Iustin Pop
  @classmethod
1771 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1772 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1773 a2cfdea2 Iustin Pop

1774 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1775 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1776 a2cfdea2 Iustin Pop

1777 a2cfdea2 Iustin Pop
    """
1778 a2cfdea2 Iustin Pop
    if len(children) != 2:
1779 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1780 767d52d3 Iustin Pop
    # check that the minor is unused
1781 767d52d3 Iustin Pop
    aminor = unique_id[4]
1782 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1783 767d52d3 Iustin Pop
    if aminor in proc_info:
1784 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1785 767d52d3 Iustin Pop
      in_use = status.is_in_use
1786 767d52d3 Iustin Pop
    else:
1787 767d52d3 Iustin Pop
      in_use = False
1788 767d52d3 Iustin Pop
    if in_use:
1789 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1790 a2cfdea2 Iustin Pop
    meta = children[1]
1791 a2cfdea2 Iustin Pop
    meta.Assemble()
1792 a2cfdea2 Iustin Pop
    if not meta.Attach():
1793 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1794 33bc6f01 Iustin Pop
                  aminor, meta)
1795 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1796 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1797 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1798 a2cfdea2 Iustin Pop
1799 1005d816 Iustin Pop
  def Grow(self, amount):
1800 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1801 1005d816 Iustin Pop

1802 1005d816 Iustin Pop
    """
1803 1005d816 Iustin Pop
    if self.minor is None:
1804 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1805 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1806 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1807 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1808 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1809 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1810 1005d816 Iustin Pop
    if result.failed:
1811 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1812 1005d816 Iustin Pop
1813 a8083063 Iustin Pop
1814 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1815 6f695a2e Manuel Franceschini
  """File device.
1816 abdf0113 Iustin Pop

1817 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1818 6f695a2e Manuel Franceschini

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

1821 6f695a2e Manuel Franceschini
  """
1822 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1823 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1824 6f695a2e Manuel Franceschini

1825 6f695a2e Manuel Franceschini
    """
1826 6f695a2e Manuel Franceschini
    if children:
1827 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1828 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1829 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1830 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1831 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1832 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1833 ecb091e3 Iustin Pop
    self.Attach()
1834 6f695a2e Manuel Franceschini
1835 6f695a2e Manuel Franceschini
  def Assemble(self):
1836 6f695a2e Manuel Franceschini
    """Assemble the device.
1837 6f695a2e Manuel Franceschini

1838 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1839 6f695a2e Manuel Franceschini

1840 6f695a2e Manuel Franceschini
    """
1841 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1842 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1843 6f695a2e Manuel Franceschini
1844 6f695a2e Manuel Franceschini
  def Shutdown(self):
1845 6f695a2e Manuel Franceschini
    """Shutdown the device.
1846 6f695a2e Manuel Franceschini

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

1850 6f695a2e Manuel Franceschini
    """
1851 746f7476 Iustin Pop
    pass
1852 6f695a2e Manuel Franceschini
1853 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1854 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1855 6f695a2e Manuel Franceschini

1856 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1857 6f695a2e Manuel Franceschini

1858 6f695a2e Manuel Franceschini
    """
1859 6f695a2e Manuel Franceschini
    pass
1860 6f695a2e Manuel Franceschini
1861 6f695a2e Manuel Franceschini
  def Close(self):
1862 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1863 6f695a2e Manuel Franceschini

1864 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1865 6f695a2e Manuel Franceschini

1866 6f695a2e Manuel Franceschini
    """
1867 6f695a2e Manuel Franceschini
    pass
1868 6f695a2e Manuel Franceschini
1869 6f695a2e Manuel Franceschini
  def Remove(self):
1870 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1871 6f695a2e Manuel Franceschini

1872 c41eea6e Iustin Pop
    @rtype: boolean
1873 c41eea6e Iustin Pop
    @return: True if the removal was successful
1874 6f695a2e Manuel Franceschini

1875 6f695a2e Manuel Franceschini
    """
1876 6f695a2e Manuel Franceschini
    try:
1877 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1878 6f695a2e Manuel Franceschini
    except OSError, err:
1879 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1880 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1881 6f695a2e Manuel Franceschini
1882 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
1883 bbe4cc16 Iustin Pop
    """Renames the file.
1884 bbe4cc16 Iustin Pop

1885 bbe4cc16 Iustin Pop
    """
1886 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
1887 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
1888 bbe4cc16 Iustin Pop
1889 bbe4cc16 Iustin Pop
  def Grow(self, amount):
1890 bbe4cc16 Iustin Pop
    """Grow the file
1891 bbe4cc16 Iustin Pop

1892 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
1893 bbe4cc16 Iustin Pop

1894 bbe4cc16 Iustin Pop
    """
1895 91e2d9ec Guido Trotter
    # Check that the file exists
1896 91e2d9ec Guido Trotter
    self.Assemble()
1897 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
1898 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
1899 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
1900 91e2d9ec Guido Trotter
    try:
1901 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
1902 91e2d9ec Guido Trotter
      f.truncate(new_size)
1903 91e2d9ec Guido Trotter
      f.close()
1904 91e2d9ec Guido Trotter
    except EnvironmentError, err:
1905 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
1906 bbe4cc16 Iustin Pop
1907 6f695a2e Manuel Franceschini
  def Attach(self):
1908 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1909 6f695a2e Manuel Franceschini

1910 6f695a2e Manuel Franceschini
    Check if this file already exists.
1911 6f695a2e Manuel Franceschini

1912 c41eea6e Iustin Pop
    @rtype: boolean
1913 c41eea6e Iustin Pop
    @return: True if file exists
1914 6f695a2e Manuel Franceschini

1915 6f695a2e Manuel Franceschini
    """
1916 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1917 ecb091e3 Iustin Pop
    return self.attached
1918 6f695a2e Manuel Franceschini
1919 fcff3897 Iustin Pop
  def GetActualSize(self):
1920 fcff3897 Iustin Pop
    """Return the actual disk size.
1921 fcff3897 Iustin Pop

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

1924 fcff3897 Iustin Pop
    """
1925 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
1926 fcff3897 Iustin Pop
    try:
1927 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
1928 fcff3897 Iustin Pop
      return st.st_size
1929 fcff3897 Iustin Pop
    except OSError, err:
1930 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
1931 fcff3897 Iustin Pop
1932 6f695a2e Manuel Franceschini
  @classmethod
1933 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1934 6f695a2e Manuel Franceschini
    """Create a new file.
1935 6f695a2e Manuel Franceschini

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

1938 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
1939 c41eea6e Iustin Pop
    @return: an instance of FileStorage
1940 6f695a2e Manuel Franceschini

1941 6f695a2e Manuel Franceschini
    """
1942 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1943 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1944 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1945 6f695a2e Manuel Franceschini
    try:
1946 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
1947 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
1948 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1949 6f695a2e Manuel Franceschini
      f.close()
1950 cdeefd9b Guido Trotter
    except EnvironmentError, err:
1951 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
1952 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
1953 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
1954 6f695a2e Manuel Franceschini
1955 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
1956 6f695a2e Manuel Franceschini
1957 6f695a2e Manuel Franceschini
1958 a8083063 Iustin Pop
DEV_MAP = {
1959 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1960 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1961 a8083063 Iustin Pop
  }
1962 a8083063 Iustin Pop
1963 cb7c0198 Iustin Pop
if constants.ENABLE_FILE_STORAGE:
1964 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
1965 cb7c0198 Iustin Pop
1966 a8083063 Iustin Pop
1967 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
1968 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1969 a8083063 Iustin Pop

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

1973 a8083063 Iustin Pop
  """
1974 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1975 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1976 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1977 cb999543 Iustin Pop
  if not device.attached:
1978 a8083063 Iustin Pop
    return None
1979 ecb091e3 Iustin Pop
  return device
1980 a8083063 Iustin Pop
1981 a8083063 Iustin Pop
1982 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
1983 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1984 a8083063 Iustin Pop

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

1988 a8083063 Iustin Pop
  """
1989 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1990 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1991 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1992 1063abd1 Iustin Pop
  device.Assemble()
1993 a8083063 Iustin Pop
  return device
1994 a8083063 Iustin Pop
1995 a8083063 Iustin Pop
1996 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1997 a8083063 Iustin Pop
  """Create a device.
1998 a8083063 Iustin Pop

1999 a8083063 Iustin Pop
  """
2000 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2001 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2002 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2003 a8083063 Iustin Pop
  return device