Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 9fa2e150

History | View | Annotate | Download (61.5 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

351 a8083063 Iustin Pop
  """
352 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
353 a8083063 Iustin Pop
    """Attaches to a LV device.
354 a8083063 Iustin Pop

355 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
356 a8083063 Iustin Pop

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

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

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

416 c41eea6e Iustin Pop
    @rtype: list
417 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
418 098c0958 Michael Hanselmann

419 a8083063 Iustin Pop
    """
420 96c7a5b0 Iustin Pop
    sep = "|"
421 a8083063 Iustin Pop
    command = ["pvs", "--noheadings", "--nosuffix", "--units=m",
422 a8083063 Iustin Pop
               "-opv_name,vg_name,pv_free,pv_attr", "--unbuffered",
423 96c7a5b0 Iustin Pop
               "--separator=%s" % sep ]
424 a8083063 Iustin Pop
    result = utils.RunCmd(command)
425 a8083063 Iustin Pop
    if result.failed:
426 468c5f77 Iustin Pop
      logging.error("Can't get the PV information: %s - %s",
427 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
428 a8083063 Iustin Pop
      return None
429 a8083063 Iustin Pop
    data = []
430 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
431 96c7a5b0 Iustin Pop
      fields = line.strip().split(sep)
432 a8083063 Iustin Pop
      if len(fields) != 4:
433 468c5f77 Iustin Pop
        logging.error("Can't parse pvs output: line '%s'", line)
434 a8083063 Iustin Pop
        return None
435 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
436 2070598f Iustin Pop
      if filter_allocatable and fields[3][0] != 'a':
437 a8083063 Iustin Pop
        continue
438 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
439 2070598f Iustin Pop
      if vg_names and fields[1] not in vg_names:
440 2070598f Iustin Pop
        continue
441 2070598f Iustin Pop
      data.append((float(fields[2]), fields[0], fields[1]))
442 a8083063 Iustin Pop
443 a8083063 Iustin Pop
    return data
444 a8083063 Iustin Pop
445 a8083063 Iustin Pop
  def Remove(self):
446 a8083063 Iustin Pop
    """Remove this logical volume.
447 a8083063 Iustin Pop

448 a8083063 Iustin Pop
    """
449 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
450 a8083063 Iustin Pop
      # the LV does not exist
451 0c6c04ec Iustin Pop
      return
452 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
453 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
454 a8083063 Iustin Pop
    if result.failed:
455 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
456 a8083063 Iustin Pop
457 f3e513ad Iustin Pop
  def Rename(self, new_id):
458 f3e513ad Iustin Pop
    """Rename this logical volume.
459 f3e513ad Iustin Pop

460 f3e513ad Iustin Pop
    """
461 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
462 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
463 f3e513ad Iustin Pop
    new_vg, new_name = new_id
464 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
465 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
466 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
467 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
468 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
469 f3e513ad Iustin Pop
    if result.failed:
470 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
471 be345db0 Iustin Pop
    self._lv_name = new_name
472 be345db0 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
473 be345db0 Iustin Pop
474 a8083063 Iustin Pop
  def Attach(self):
475 a8083063 Iustin Pop
    """Attach to an existing LV.
476 a8083063 Iustin Pop

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

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

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

546 a8083063 Iustin Pop
    """
547 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
548 5574047a Iustin Pop
    if result.failed:
549 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
550 a8083063 Iustin Pop
551 a8083063 Iustin Pop
  def Shutdown(self):
552 a8083063 Iustin Pop
    """Shutdown the device.
553 a8083063 Iustin Pop

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

557 a8083063 Iustin Pop
    """
558 746f7476 Iustin Pop
    pass
559 a8083063 Iustin Pop
560 9db6dbce Iustin Pop
  def GetSyncStatus(self):
561 9db6dbce Iustin Pop
    """Returns the sync status of the device.
562 9db6dbce Iustin Pop

563 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
564 9db6dbce Iustin Pop
    status of the mirror.
565 9db6dbce Iustin Pop

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

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

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

579 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
580 c41eea6e Iustin Pop

581 9db6dbce Iustin Pop
    """
582 f208978a Michael Hanselmann
    if self._degraded:
583 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
584 f208978a Michael Hanselmann
    else:
585 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
586 f208978a Michael Hanselmann
587 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
588 96acbc09 Michael Hanselmann
                                  major=self.major,
589 96acbc09 Michael Hanselmann
                                  minor=self.minor,
590 96acbc09 Michael Hanselmann
                                  sync_percent=None,
591 96acbc09 Michael Hanselmann
                                  estimated_time=None,
592 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
593 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
594 9db6dbce Iustin Pop
595 a8083063 Iustin Pop
  def Open(self, force=False):
596 a8083063 Iustin Pop
    """Make the device ready for I/O.
597 a8083063 Iustin Pop

598 a8083063 Iustin Pop
    This is a no-op for the LV device type.
599 a8083063 Iustin Pop

600 a8083063 Iustin Pop
    """
601 fdbd668d Iustin Pop
    pass
602 a8083063 Iustin Pop
603 a8083063 Iustin Pop
  def Close(self):
604 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
605 a8083063 Iustin Pop

606 a8083063 Iustin Pop
    This is a no-op for the LV device type.
607 a8083063 Iustin Pop

608 a8083063 Iustin Pop
    """
609 fdbd668d Iustin Pop
    pass
610 a8083063 Iustin Pop
611 a8083063 Iustin Pop
  def Snapshot(self, size):
612 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
613 a8083063 Iustin Pop

614 a8083063 Iustin Pop
    """
615 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
616 a8083063 Iustin Pop
617 a8083063 Iustin Pop
    # remove existing snapshot if found
618 464f8daf Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None, size)
619 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
620 a8083063 Iustin Pop
621 2070598f Iustin Pop
    pvs_info = self.GetPVInfo([self._vg_name])
622 a8083063 Iustin Pop
    if not pvs_info:
623 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", self._vg_name)
624 a8083063 Iustin Pop
    pvs_info.sort()
625 a8083063 Iustin Pop
    pvs_info.reverse()
626 1122eb25 Iustin Pop
    free_size, _, _ = pvs_info[0]
627 a8083063 Iustin Pop
    if free_size < size:
628 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
629 82463074 Iustin Pop
                  " available %s", size, free_size)
630 a8083063 Iustin Pop
631 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
632 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
633 a8083063 Iustin Pop
    if result.failed:
634 82463074 Iustin Pop
      _ThrowError("command: %s error: %s - %s",
635 82463074 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
636 a8083063 Iustin Pop
637 a8083063 Iustin Pop
    return snap_name
638 a8083063 Iustin Pop
639 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
640 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
641 a0c3fea1 Michael Hanselmann

642 a0c3fea1 Michael Hanselmann
    """
643 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
644 a0c3fea1 Michael Hanselmann
645 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
646 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
647 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
648 a0c3fea1 Michael Hanselmann
649 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
650 a0c3fea1 Michael Hanselmann
    text = text[:128]
651 a0c3fea1 Michael Hanselmann
652 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
653 a0c3fea1 Michael Hanselmann
                           self.dev_path])
654 a0c3fea1 Michael Hanselmann
    if result.failed:
655 82463074 Iustin Pop
      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
656 82463074 Iustin Pop
                  result.output)
657 82463074 Iustin Pop
658 1005d816 Iustin Pop
  def Grow(self, amount):
659 1005d816 Iustin Pop
    """Grow the logical volume.
660 1005d816 Iustin Pop

661 1005d816 Iustin Pop
    """
662 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
663 38256320 Iustin Pop
      if not self.Attach():
664 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
665 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
666 38256320 Iustin Pop
    rest = amount % full_stripe_size
667 38256320 Iustin Pop
    if rest != 0:
668 38256320 Iustin Pop
      amount += full_stripe_size - rest
669 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
670 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
671 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
672 1005d816 Iustin Pop
    # supports 'cling'
673 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
674 1005d816 Iustin Pop
      result = utils.RunCmd(["lvextend", "--alloc", alloc_policy,
675 1005d816 Iustin Pop
                             "-L", "+%dm" % amount, self.dev_path])
676 1005d816 Iustin Pop
      if not result.failed:
677 1005d816 Iustin Pop
        return
678 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
679 a0c3fea1 Michael Hanselmann
680 a0c3fea1 Michael Hanselmann
681 6b90c22e Iustin Pop
class DRBD8Status(object):
682 6b90c22e Iustin Pop
  """A DRBD status representation class.
683 6b90c22e Iustin Pop

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

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

789 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
790 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
791 0f7f32d9 Iustin Pop

792 abdf0113 Iustin Pop
  """
793 abdf0113 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
794 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
795 a8083063 Iustin Pop
796 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
797 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
798 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
799 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
800 a8083063 Iustin Pop
801 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
802 6b90c22e Iustin Pop
803 abdf0113 Iustin Pop
  @staticmethod
804 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
805 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
806 a8083063 Iustin Pop

807 a8083063 Iustin Pop
    """
808 abdf0113 Iustin Pop
    try:
809 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
810 f6eaed12 Iustin Pop
    except EnvironmentError, err:
811 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
812 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
813 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
814 f6eaed12 Iustin Pop
      else:
815 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
816 abdf0113 Iustin Pop
    if not data:
817 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
818 abdf0113 Iustin Pop
    return data
819 a8083063 Iustin Pop
820 abdf0113 Iustin Pop
  @staticmethod
821 abdf0113 Iustin Pop
  def _MassageProcData(data):
822 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
823 a8083063 Iustin Pop

824 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
825 c41eea6e Iustin Pop
        for that minor
826 a8083063 Iustin Pop

827 a8083063 Iustin Pop
    """
828 abdf0113 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
829 abdf0113 Iustin Pop
    results = {}
830 abdf0113 Iustin Pop
    old_minor = old_line = None
831 abdf0113 Iustin Pop
    for line in data:
832 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
833 67d101d4 Iustin Pop
        continue
834 abdf0113 Iustin Pop
      lresult = lmatch.match(line)
835 abdf0113 Iustin Pop
      if lresult is not None:
836 abdf0113 Iustin Pop
        if old_minor is not None:
837 abdf0113 Iustin Pop
          results[old_minor] = old_line
838 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
839 abdf0113 Iustin Pop
        old_line = line
840 abdf0113 Iustin Pop
      else:
841 abdf0113 Iustin Pop
        if old_minor is not None:
842 abdf0113 Iustin Pop
          old_line += " " + line.strip()
843 abdf0113 Iustin Pop
    # add last line
844 abdf0113 Iustin Pop
    if old_minor is not None:
845 abdf0113 Iustin Pop
      results[old_minor] = old_line
846 abdf0113 Iustin Pop
    return results
847 a8083063 Iustin Pop
848 abdf0113 Iustin Pop
  @classmethod
849 abdf0113 Iustin Pop
  def _GetVersion(cls):
850 abdf0113 Iustin Pop
    """Return the DRBD version.
851 a8083063 Iustin Pop

852 abdf0113 Iustin Pop
    This will return a dict with keys:
853 c41eea6e Iustin Pop
      - k_major
854 c41eea6e Iustin Pop
      - k_minor
855 c41eea6e Iustin Pop
      - k_point
856 c41eea6e Iustin Pop
      - api
857 c41eea6e Iustin Pop
      - proto
858 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
859 a8083063 Iustin Pop

860 a8083063 Iustin Pop
    """
861 abdf0113 Iustin Pop
    proc_data = cls._GetProcData()
862 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
863 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
864 abdf0113 Iustin Pop
    if not version:
865 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
866 abdf0113 Iustin Pop
                                    first_line)
867 a8083063 Iustin Pop
868 abdf0113 Iustin Pop
    values = version.groups()
869 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
870 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
871 abdf0113 Iustin Pop
              'k_point': int(values[2]),
872 abdf0113 Iustin Pop
              'api': int(values[3]),
873 abdf0113 Iustin Pop
              'proto': int(values[4]),
874 abdf0113 Iustin Pop
             }
875 abdf0113 Iustin Pop
    if values[5] is not None:
876 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
877 a8083063 Iustin Pop
878 abdf0113 Iustin Pop
    return retval
879 abdf0113 Iustin Pop
880 abdf0113 Iustin Pop
  @staticmethod
881 abdf0113 Iustin Pop
  def _DevPath(minor):
882 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
883 a8083063 Iustin Pop

884 a8083063 Iustin Pop
    """
885 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
886 a8083063 Iustin Pop
887 abdf0113 Iustin Pop
  @classmethod
888 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
889 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
890 a8083063 Iustin Pop

891 a8083063 Iustin Pop
    """
892 abdf0113 Iustin Pop
    data = cls._GetProcData()
893 a8083063 Iustin Pop
894 abdf0113 Iustin Pop
    used_devs = {}
895 abdf0113 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
896 abdf0113 Iustin Pop
    for line in data:
897 abdf0113 Iustin Pop
      match = valid_line.match(line)
898 abdf0113 Iustin Pop
      if not match:
899 abdf0113 Iustin Pop
        continue
900 abdf0113 Iustin Pop
      minor = int(match.group(1))
901 abdf0113 Iustin Pop
      state = match.group(2)
902 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
903 abdf0113 Iustin Pop
        continue
904 abdf0113 Iustin Pop
      used_devs[minor] = state, line
905 a8083063 Iustin Pop
906 abdf0113 Iustin Pop
    return used_devs
907 a8083063 Iustin Pop
908 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
909 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
910 0834c866 Iustin Pop

911 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
912 a8083063 Iustin Pop

913 a8083063 Iustin Pop
    """
914 abdf0113 Iustin Pop
    if minor is None:
915 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
916 cb999543 Iustin Pop
      self.attached = False
917 a8083063 Iustin Pop
    else:
918 abdf0113 Iustin Pop
      self.minor = minor
919 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
920 cb999543 Iustin Pop
      self.attached = True
921 a8083063 Iustin Pop
922 a8083063 Iustin Pop
  @staticmethod
923 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
924 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
925 a8083063 Iustin Pop

926 abdf0113 Iustin Pop
    This currently only check the size, which must be around
927 abdf0113 Iustin Pop
    128MiB.
928 a8083063 Iustin Pop

929 a8083063 Iustin Pop
    """
930 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
931 abdf0113 Iustin Pop
    if result.failed:
932 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
933 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
934 a8083063 Iustin Pop
    try:
935 abdf0113 Iustin Pop
      sectors = int(result.stdout)
936 abdf0113 Iustin Pop
    except ValueError:
937 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
938 abdf0113 Iustin Pop
    bytes = sectors * 512
939 abdf0113 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
940 9c793cfb Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
941 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
942 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
943 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
944 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
945 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
946 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
947 1dc10972 Iustin Pop
    if bytes > 1024 * 1024 * 1024:
948 9c793cfb Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
949 a8083063 Iustin Pop
950 abdf0113 Iustin Pop
  def Rename(self, new_id):
951 abdf0113 Iustin Pop
    """Rename a device.
952 a8083063 Iustin Pop

953 abdf0113 Iustin Pop
    This is not supported for drbd devices.
954 a8083063 Iustin Pop

955 a8083063 Iustin Pop
    """
956 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
957 a8083063 Iustin Pop
958 f3e513ad Iustin Pop
959 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
960 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
961 a2cfdea2 Iustin Pop

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

966 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
967 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
968 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
969 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
970 a2cfdea2 Iustin Pop

971 a2cfdea2 Iustin Pop
  """
972 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
973 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
974 a2cfdea2 Iustin Pop
975 cf8df3f3 Iustin Pop
  # timeout constants
976 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
977 cf8df3f3 Iustin Pop
978 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
979 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
980 fc1dc9d7 Iustin Pop
      children = []
981 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
982 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
983 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
984 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
985 310fbb64 Iustin Pop
    (self._lhost, self._lport,
986 310fbb64 Iustin Pop
     self._rhost, self._rport,
987 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
988 310fbb64 Iustin Pop
    if children:
989 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
990 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
991 310fbb64 Iustin Pop
        children = []
992 464f8daf Iustin Pop
    super(DRBD8, self).__init__(unique_id, children, size)
993 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
994 c3f9340c Guido Trotter
    version = self._GetVersion()
995 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
996 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
997 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
998 82463074 Iustin Pop
                  version['k_major'], version['k_minor'])
999 a2cfdea2 Iustin Pop
1000 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1001 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1002 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1003 ffa1c0dc Iustin Pop
                       (unique_id,))
1004 a2cfdea2 Iustin Pop
    self.Attach()
1005 a2cfdea2 Iustin Pop
1006 a2cfdea2 Iustin Pop
  @classmethod
1007 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1008 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1009 a2cfdea2 Iustin Pop

1010 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1011 a2cfdea2 Iustin Pop

1012 a2cfdea2 Iustin Pop
    """
1013 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1014 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1015 a2cfdea2 Iustin Pop
    if result.failed:
1016 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1017 a2cfdea2 Iustin Pop
1018 a2cfdea2 Iustin Pop
  @classmethod
1019 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1020 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1021 a2cfdea2 Iustin Pop

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

1025 a2cfdea2 Iustin Pop
    """
1026 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1027 a2cfdea2 Iustin Pop
1028 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1029 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
1030 a2cfdea2 Iustin Pop
    highest = None
1031 a2cfdea2 Iustin Pop
    for line in data:
1032 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
1033 a2cfdea2 Iustin Pop
      if match:
1034 a2cfdea2 Iustin Pop
        return int(match.group(1))
1035 a2cfdea2 Iustin Pop
      match = used_line.match(line)
1036 a2cfdea2 Iustin Pop
      if match:
1037 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1038 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1039 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1040 a2cfdea2 Iustin Pop
      return 0
1041 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1042 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1043 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1044 a2cfdea2 Iustin Pop
    return highest + 1
1045 a2cfdea2 Iustin Pop
1046 a2cfdea2 Iustin Pop
  @classmethod
1047 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1048 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1049 a2cfdea2 Iustin Pop

1050 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1051 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1052 a2cfdea2 Iustin Pop

1053 a2cfdea2 Iustin Pop
    """
1054 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1055 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1056 a2cfdea2 Iustin Pop
1057 a2cfdea2 Iustin Pop
    # pyparsing setup
1058 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1059 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1060 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1061 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1062 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1063 a2cfdea2 Iustin Pop
1064 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1065 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1066 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1067 a2cfdea2 Iustin Pop
1068 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1069 a2cfdea2 Iustin Pop
1070 a2cfdea2 Iustin Pop
    # value types
1071 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1072 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1073 34e71fea Karsten Keil
    addr_type = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1074 34e71fea Karsten Keil
                 pyp.Optional(pyp.Literal("ipv6")).suppress())
1075 34e71fea Karsten Keil
    addr_port = (addr_type + pyp.Word(pyp.nums + '.') +
1076 34e71fea Karsten Keil
                 pyp.Literal(':').suppress() + number)
1077 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1078 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
1079 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
1080 01e2ce3a Iustin Pop
    # device name, extended syntax
1081 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1082 a2cfdea2 Iustin Pop
1083 a2cfdea2 Iustin Pop
    # a statement
1084 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1085 01e2ce3a Iustin Pop
            pyp.Optional(addr_port ^ value ^ quoted ^ meta_value ^
1086 01e2ce3a Iustin Pop
                         device_value) +
1087 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1088 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1089 a2cfdea2 Iustin Pop
1090 a2cfdea2 Iustin Pop
    # an entire section
1091 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1092 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1093 a2cfdea2 Iustin Pop
1094 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1095 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1096 a2cfdea2 Iustin Pop
1097 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1098 a2cfdea2 Iustin Pop
1099 a2cfdea2 Iustin Pop
    return bnf
1100 a2cfdea2 Iustin Pop
1101 a2cfdea2 Iustin Pop
  @classmethod
1102 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1103 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1104 a2cfdea2 Iustin Pop

1105 a2cfdea2 Iustin Pop
    """
1106 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1107 a2cfdea2 Iustin Pop
    if result.failed:
1108 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1109 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1110 3840729d Iustin Pop
      return None
1111 3840729d Iustin Pop
    return result.stdout
1112 3840729d Iustin Pop
1113 3840729d Iustin Pop
  @classmethod
1114 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1115 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1116 3840729d Iustin Pop

1117 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1118 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1119 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1120 3840729d Iustin Pop
    by _GetShowData.
1121 3840729d Iustin Pop

1122 3840729d Iustin Pop
    """
1123 3840729d Iustin Pop
    data = {}
1124 a2cfdea2 Iustin Pop
    if not out:
1125 a2cfdea2 Iustin Pop
      return data
1126 a2cfdea2 Iustin Pop
1127 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1128 a2cfdea2 Iustin Pop
    # run pyparse
1129 a2cfdea2 Iustin Pop
1130 a2cfdea2 Iustin Pop
    try:
1131 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1132 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1133 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1134 a2cfdea2 Iustin Pop
1135 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1136 a2cfdea2 Iustin Pop
    for section in results:
1137 a2cfdea2 Iustin Pop
      sname = section[0]
1138 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1139 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1140 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1141 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1142 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1143 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1144 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1145 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1146 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1147 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1148 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1149 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1150 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1151 a2cfdea2 Iustin Pop
    return data
1152 a2cfdea2 Iustin Pop
1153 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1154 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1155 a2cfdea2 Iustin Pop

1156 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1157 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1158 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1159 a2cfdea2 Iustin Pop
    device.
1160 a2cfdea2 Iustin Pop

1161 a2cfdea2 Iustin Pop
    """
1162 b00b95dd Iustin Pop
    if self._children:
1163 b00b95dd Iustin Pop
      backend, meta = self._children
1164 b00b95dd Iustin Pop
    else:
1165 b00b95dd Iustin Pop
      backend = meta = None
1166 b00b95dd Iustin Pop
1167 a2cfdea2 Iustin Pop
    if backend is not None:
1168 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1169 a2cfdea2 Iustin Pop
    else:
1170 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1171 b00b95dd Iustin Pop
1172 a2cfdea2 Iustin Pop
    if meta is not None:
1173 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1174 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1175 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1176 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1177 a2cfdea2 Iustin Pop
    else:
1178 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1179 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1180 a2cfdea2 Iustin Pop
    return retval
1181 a2cfdea2 Iustin Pop
1182 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1183 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1184 a2cfdea2 Iustin Pop

1185 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1186 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1187 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1188 a2cfdea2 Iustin Pop
    device.
1189 a2cfdea2 Iustin Pop

1190 a2cfdea2 Iustin Pop
    """
1191 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1192 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1193 a2cfdea2 Iustin Pop
      return True
1194 a2cfdea2 Iustin Pop
1195 a2cfdea2 Iustin Pop
    if self._lhost is None:
1196 a2cfdea2 Iustin Pop
      return False
1197 a2cfdea2 Iustin Pop
1198 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1199 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1200 a2cfdea2 Iustin Pop
      return False
1201 a2cfdea2 Iustin Pop
1202 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1203 a2cfdea2 Iustin Pop
    retval = (retval and
1204 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1205 a2cfdea2 Iustin Pop
    return retval
1206 a2cfdea2 Iustin Pop
1207 a2cfdea2 Iustin Pop
  @classmethod
1208 f069addf Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta, size):
1209 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1210 a2cfdea2 Iustin Pop

1211 a2cfdea2 Iustin Pop
    """
1212 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1213 f069addf Iustin Pop
            backend, meta, "0",
1214 f069addf Iustin Pop
            "-e", "detach",
1215 f069addf Iustin Pop
            "--create-device"]
1216 60bca04a Iustin Pop
    if size:
1217 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1218 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1219 a2cfdea2 Iustin Pop
    if result.failed:
1220 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1221 a2cfdea2 Iustin Pop
1222 a2cfdea2 Iustin Pop
  @classmethod
1223 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1224 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1225 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1226 a2cfdea2 Iustin Pop

1227 a2cfdea2 Iustin Pop
    """
1228 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1229 52857176 Iustin Pop
    if None in net_info:
1230 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1231 52857176 Iustin Pop
      # sure its shutdown
1232 1063abd1 Iustin Pop
      cls._ShutdownNet(minor)
1233 1063abd1 Iustin Pop
      return
1234 52857176 Iustin Pop
1235 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1236 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1237 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1238 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1239 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1240 7d585316 Iustin Pop
    # about its peer.
1241 7d585316 Iustin Pop
    cls._SetMinorSyncSpeed(minor, constants.SYNC_SPEED)
1242 7d585316 Iustin Pop
1243 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1244 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1245 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1246 f38478b2 Iustin Pop
            "-B", "consensus",
1247 ab6cc81c Iustin Pop
            "--create-device",
1248 f38478b2 Iustin Pop
            ]
1249 a2cfdea2 Iustin Pop
    if dual_pri:
1250 a2cfdea2 Iustin Pop
      args.append("-m")
1251 a2cfdea2 Iustin Pop
    if hmac and secret:
1252 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1253 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1254 a2cfdea2 Iustin Pop
    if result.failed:
1255 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1256 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1257 a2cfdea2 Iustin Pop
1258 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1259 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1260 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1261 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1262 def8e2f6 Michael Hanselmann
1263 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1264 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1265 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1266 def8e2f6 Michael Hanselmann
1267 def8e2f6 Michael Hanselmann
    try:
1268 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1269 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1270 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1271 a2cfdea2 Iustin Pop
1272 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1273 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1274 b00b95dd Iustin Pop

1275 b00b95dd Iustin Pop
    """
1276 b00b95dd Iustin Pop
    if self.minor is None:
1277 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1278 82463074 Iustin Pop
                  self._aminor)
1279 b00b95dd Iustin Pop
    if len(devices) != 2:
1280 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1281 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1282 03ece5f3 Iustin Pop
    if "local_dev" in info:
1283 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1284 b00b95dd Iustin Pop
    backend, meta = devices
1285 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1286 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1287 b00b95dd Iustin Pop
    backend.Open()
1288 b00b95dd Iustin Pop
    meta.Open()
1289 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1290 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1291 b00b95dd Iustin Pop
1292 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1293 b00b95dd Iustin Pop
    self._children = devices
1294 b00b95dd Iustin Pop
1295 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1296 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1297 b00b95dd Iustin Pop

1298 b00b95dd Iustin Pop
    """
1299 b00b95dd Iustin Pop
    if self.minor is None:
1300 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1301 82463074 Iustin Pop
                  self._aminor)
1302 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1303 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1304 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1305 03ece5f3 Iustin Pop
      return
1306 b00b95dd Iustin Pop
    if len(self._children) != 2:
1307 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1308 82463074 Iustin Pop
                  self._children)
1309 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1310 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1311 e739bd57 Iustin Pop
      return
1312 b00b95dd Iustin Pop
    if len(devices) != 2:
1313 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1314 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1315 e739bd57 Iustin Pop
      if dev != child.dev_path:
1316 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1317 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1318 b00b95dd Iustin Pop
1319 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1320 b00b95dd Iustin Pop
    self._children = []
1321 b00b95dd Iustin Pop
1322 7d585316 Iustin Pop
  @classmethod
1323 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1324 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1325 a2cfdea2 Iustin Pop

1326 7d585316 Iustin Pop
    This is the low-level implementation.
1327 7d585316 Iustin Pop

1328 7d585316 Iustin Pop
    @type minor: int
1329 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1330 7d585316 Iustin Pop
    @type kbytes: int
1331 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1332 7d585316 Iustin Pop
    @rtype: boolean
1333 7d585316 Iustin Pop
    @return: the success of the operation
1334 7d585316 Iustin Pop

1335 a2cfdea2 Iustin Pop
    """
1336 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1337 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1338 a2cfdea2 Iustin Pop
    if result.failed:
1339 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1340 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1341 7d585316 Iustin Pop
    return not result.failed
1342 7d585316 Iustin Pop
1343 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1344 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1345 7d585316 Iustin Pop

1346 7d585316 Iustin Pop
    @type kbytes: int
1347 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1348 7d585316 Iustin Pop
    @rtype: boolean
1349 7d585316 Iustin Pop
    @return: the success of the operation
1350 7d585316 Iustin Pop

1351 7d585316 Iustin Pop
    """
1352 7d585316 Iustin Pop
    if self.minor is None:
1353 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1354 7d585316 Iustin Pop
      return False
1355 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1356 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1357 a2cfdea2 Iustin Pop
1358 6b90c22e Iustin Pop
  def GetProcStatus(self):
1359 6b90c22e Iustin Pop
    """Return device data from /proc.
1360 6b90c22e Iustin Pop

1361 6b90c22e Iustin Pop
    """
1362 6b90c22e Iustin Pop
    if self.minor is None:
1363 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1364 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1365 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1366 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1367 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1368 6b90c22e Iustin Pop
1369 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1370 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1371 a2cfdea2 Iustin Pop

1372 a2cfdea2 Iustin Pop

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

1377 0834c866 Iustin Pop

1378 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1379 0834c866 Iustin Pop
    network not connected or local disk missing.
1380 0834c866 Iustin Pop

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

1384 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1385 c41eea6e Iustin Pop

1386 a2cfdea2 Iustin Pop
    """
1387 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1388 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1389 96acbc09 Michael Hanselmann
1390 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1391 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1392 f208978a Michael Hanselmann
1393 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1394 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1395 f208978a Michael Hanselmann
    elif stats.is_diskless:
1396 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1397 f208978a Michael Hanselmann
    else:
1398 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1399 96acbc09 Michael Hanselmann
1400 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1401 96acbc09 Michael Hanselmann
                                  major=self.major,
1402 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1403 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1404 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1405 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1406 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1407 a2cfdea2 Iustin Pop
1408 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1409 a2cfdea2 Iustin Pop
    """Make the local state primary.
1410 a2cfdea2 Iustin Pop

1411 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1412 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1413 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1414 f860ff4e Guido Trotter
    is mandatory.
1415 a2cfdea2 Iustin Pop

1416 a2cfdea2 Iustin Pop
    """
1417 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1418 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1419 a2cfdea2 Iustin Pop
      return False
1420 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1421 a2cfdea2 Iustin Pop
    if force:
1422 a2cfdea2 Iustin Pop
      cmd.append("-o")
1423 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1424 a2cfdea2 Iustin Pop
    if result.failed:
1425 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1426 82463074 Iustin Pop
                  result.output)
1427 a2cfdea2 Iustin Pop
1428 a2cfdea2 Iustin Pop
  def Close(self):
1429 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1430 a2cfdea2 Iustin Pop

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

1433 a2cfdea2 Iustin Pop
    """
1434 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1435 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1436 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1437 a2cfdea2 Iustin Pop
    if result.failed:
1438 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1439 82463074 Iustin Pop
                  self.minor, result.output)
1440 a2cfdea2 Iustin Pop
1441 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1442 cf8df3f3 Iustin Pop
    """Removes network configuration.
1443 cf8df3f3 Iustin Pop

1444 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1445 cf8df3f3 Iustin Pop

1446 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1447 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1448 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1449 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1450 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1451 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1452 cf8df3f3 Iustin Pop
    exception.
1453 cf8df3f3 Iustin Pop

1454 cf8df3f3 Iustin Pop
    """
1455 cf8df3f3 Iustin Pop
    if self.minor is None:
1456 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1457 cf8df3f3 Iustin Pop
1458 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1459 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1460 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1461 cf8df3f3 Iustin Pop
1462 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1463 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1464 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1465 cf8df3f3 Iustin Pop
1466 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1467 def8e2f6 Michael Hanselmann
1468 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1469 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1470 def8e2f6 Michael Hanselmann
        return
1471 def8e2f6 Michael Hanselmann
1472 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1473 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1474 def8e2f6 Michael Hanselmann
      # forgotten
1475 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1476 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1477 def8e2f6 Michael Hanselmann
1478 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1479 def8e2f6 Michael Hanselmann
1480 def8e2f6 Michael Hanselmann
    # Keep start time
1481 def8e2f6 Michael Hanselmann
    start_time = time.time()
1482 def8e2f6 Michael Hanselmann
1483 def8e2f6 Michael Hanselmann
    try:
1484 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1485 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1486 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1487 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1488 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1489 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1490 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1491 cf8df3f3 Iustin Pop
      else:
1492 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1493 def8e2f6 Michael Hanselmann
1494 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1495 cf8df3f3 Iustin Pop
1496 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1497 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1498 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1499 82463074 Iustin Pop
                   self.minor, reconfig_time)
1500 cf8df3f3 Iustin Pop
1501 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1502 cf8df3f3 Iustin Pop
    """Reconnects the network.
1503 cf8df3f3 Iustin Pop

1504 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1505 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1506 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1507 cf8df3f3 Iustin Pop

1508 cf8df3f3 Iustin Pop
    Args:
1509 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1510 cf8df3f3 Iustin Pop

1511 cf8df3f3 Iustin Pop
    """
1512 cf8df3f3 Iustin Pop
    if self.minor is None:
1513 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", 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: missing network info in AttachNet()", self.minor)
1517 cf8df3f3 Iustin Pop
1518 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1519 cf8df3f3 Iustin Pop
1520 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1521 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1522 cf8df3f3 Iustin Pop
1523 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1524 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1525 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1526 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1527 cf8df3f3 Iustin Pop
1528 a2cfdea2 Iustin Pop
  def Attach(self):
1529 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1530 2d0c8319 Iustin Pop

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

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

1538 2d0c8319 Iustin Pop
    """
1539 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1540 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1541 2d0c8319 Iustin Pop
      minor = self._aminor
1542 2d0c8319 Iustin Pop
    else:
1543 2d0c8319 Iustin Pop
      minor = None
1544 2d0c8319 Iustin Pop
1545 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1546 2d0c8319 Iustin Pop
    return minor is not None
1547 2d0c8319 Iustin Pop
1548 2d0c8319 Iustin Pop
  def Assemble(self):
1549 2d0c8319 Iustin Pop
    """Assemble the drbd.
1550 2d0c8319 Iustin Pop

1551 2d0c8319 Iustin Pop
    Method:
1552 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1553 2d0c8319 Iustin Pop
        our config
1554 2d0c8319 Iustin Pop
      - if not, we create it from zero
1555 2d0c8319 Iustin Pop

1556 2d0c8319 Iustin Pop
    """
1557 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1558 2d0c8319 Iustin Pop
1559 2d0c8319 Iustin Pop
    self.Attach()
1560 2d0c8319 Iustin Pop
    if self.minor is None:
1561 2d0c8319 Iustin Pop
      # local device completely unconfigured
1562 1063abd1 Iustin Pop
      self._FastAssemble()
1563 2d0c8319 Iustin Pop
    else:
1564 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1565 2d0c8319 Iustin Pop
      # the device
1566 1063abd1 Iustin Pop
      self._SlowAssemble()
1567 2d0c8319 Iustin Pop
1568 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1569 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1570 a2cfdea2 Iustin Pop

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

1575 a2cfdea2 Iustin Pop
    """
1576 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
1577 527a15ac Iustin Pop
    # pylint: disable-msg=W0631
1578 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1579 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1580 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1581 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1582 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1583 1063abd1 Iustin Pop
1584 a2cfdea2 Iustin Pop
      if match_l and match_r:
1585 1063abd1 Iustin Pop
        # everything matches
1586 a2cfdea2 Iustin Pop
        break
1587 1063abd1 Iustin Pop
1588 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1589 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1590 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1591 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1592 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1593 1063abd1 Iustin Pop
          break
1594 1063abd1 Iustin Pop
        else:
1595 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1596 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1597 1063abd1 Iustin Pop
1598 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1599 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1600 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1601 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1602 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1603 1063abd1 Iustin Pop
          break
1604 1063abd1 Iustin Pop
        else:
1605 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1606 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1607 bf25af3b Iustin Pop
1608 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1609 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1610 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1611 bf25af3b Iustin Pop
      # configuration
1612 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1613 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1614 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1615 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1616 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1617 9cdbe77f Iustin Pop
        # reconnect to our correct one
1618 1063abd1 Iustin Pop
        try:
1619 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1620 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1621 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1622 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1623 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1624 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1625 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1626 9cdbe77f Iustin Pop
        # None)
1627 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1628 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1629 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1630 9cdbe77f Iustin Pop
          break
1631 1063abd1 Iustin Pop
        else:
1632 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1633 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1634 9cdbe77f Iustin Pop
1635 a2cfdea2 Iustin Pop
    else:
1636 a2cfdea2 Iustin Pop
      minor = None
1637 a2cfdea2 Iustin Pop
1638 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1639 1063abd1 Iustin Pop
    if minor is None:
1640 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1641 1063abd1 Iustin Pop
                  self._aminor)
1642 a2cfdea2 Iustin Pop
1643 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1644 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1645 a2cfdea2 Iustin Pop

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

1648 a2cfdea2 Iustin Pop
    """
1649 a1578d63 Iustin Pop
    minor = self._aminor
1650 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1651 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1652 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1653 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1654 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1655 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1656 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1657 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1658 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1659 a2cfdea2 Iustin Pop
1660 a2cfdea2 Iustin Pop
  @classmethod
1661 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1662 b00b95dd Iustin Pop
    """Detach from the local device.
1663 b00b95dd Iustin Pop

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

1667 b00b95dd Iustin Pop
    """
1668 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1669 b00b95dd Iustin Pop
    if result.failed:
1670 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1671 b00b95dd Iustin Pop
1672 b00b95dd Iustin Pop
  @classmethod
1673 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1674 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1675 f3e513ad Iustin Pop

1676 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1677 f3e513ad Iustin Pop

1678 f3e513ad Iustin Pop
    """
1679 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1680 a8459f1c Iustin Pop
    if result.failed:
1681 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1682 f3e513ad Iustin Pop
1683 f3e513ad Iustin Pop
  @classmethod
1684 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1685 a2cfdea2 Iustin Pop
    """Deactivate the device.
1686 a2cfdea2 Iustin Pop

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

1689 a2cfdea2 Iustin Pop
    """
1690 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1691 a2cfdea2 Iustin Pop
    if result.failed:
1692 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1693 33bc6f01 Iustin Pop
                  minor, result.output)
1694 a2cfdea2 Iustin Pop
1695 a2cfdea2 Iustin Pop
  def Shutdown(self):
1696 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1697 a2cfdea2 Iustin Pop

1698 a2cfdea2 Iustin Pop
    """
1699 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1700 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1701 746f7476 Iustin Pop
      return
1702 746f7476 Iustin Pop
    minor = self.minor
1703 a2cfdea2 Iustin Pop
    self.minor = None
1704 a2cfdea2 Iustin Pop
    self.dev_path = None
1705 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1706 a2cfdea2 Iustin Pop
1707 a2cfdea2 Iustin Pop
  def Remove(self):
1708 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1709 a2cfdea2 Iustin Pop

1710 a2cfdea2 Iustin Pop
    """
1711 0c6c04ec Iustin Pop
    self.Shutdown()
1712 a2cfdea2 Iustin Pop
1713 a2cfdea2 Iustin Pop
  @classmethod
1714 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1715 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1716 a2cfdea2 Iustin Pop

1717 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1718 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1719 a2cfdea2 Iustin Pop

1720 a2cfdea2 Iustin Pop
    """
1721 a2cfdea2 Iustin Pop
    if len(children) != 2:
1722 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1723 767d52d3 Iustin Pop
    # check that the minor is unused
1724 767d52d3 Iustin Pop
    aminor = unique_id[4]
1725 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1726 767d52d3 Iustin Pop
    if aminor in proc_info:
1727 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1728 767d52d3 Iustin Pop
      in_use = status.is_in_use
1729 767d52d3 Iustin Pop
    else:
1730 767d52d3 Iustin Pop
      in_use = False
1731 767d52d3 Iustin Pop
    if in_use:
1732 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1733 a2cfdea2 Iustin Pop
    meta = children[1]
1734 a2cfdea2 Iustin Pop
    meta.Assemble()
1735 a2cfdea2 Iustin Pop
    if not meta.Attach():
1736 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1737 33bc6f01 Iustin Pop
                  aminor, meta)
1738 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1739 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1740 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1741 a2cfdea2 Iustin Pop
1742 1005d816 Iustin Pop
  def Grow(self, amount):
1743 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1744 1005d816 Iustin Pop

1745 1005d816 Iustin Pop
    """
1746 1005d816 Iustin Pop
    if self.minor is None:
1747 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1748 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1749 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1750 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1751 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1752 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1753 1005d816 Iustin Pop
    if result.failed:
1754 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1755 1005d816 Iustin Pop
1756 a8083063 Iustin Pop
1757 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1758 6f695a2e Manuel Franceschini
  """File device.
1759 abdf0113 Iustin Pop

1760 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1761 6f695a2e Manuel Franceschini

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

1764 6f695a2e Manuel Franceschini
  """
1765 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1766 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1767 6f695a2e Manuel Franceschini

1768 6f695a2e Manuel Franceschini
    """
1769 6f695a2e Manuel Franceschini
    if children:
1770 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1771 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1772 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1773 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1774 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1775 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1776 ecb091e3 Iustin Pop
    self.Attach()
1777 6f695a2e Manuel Franceschini
1778 6f695a2e Manuel Franceschini
  def Assemble(self):
1779 6f695a2e Manuel Franceschini
    """Assemble the device.
1780 6f695a2e Manuel Franceschini

1781 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1782 6f695a2e Manuel Franceschini

1783 6f695a2e Manuel Franceschini
    """
1784 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1785 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1786 6f695a2e Manuel Franceschini
1787 6f695a2e Manuel Franceschini
  def Shutdown(self):
1788 6f695a2e Manuel Franceschini
    """Shutdown the device.
1789 6f695a2e Manuel Franceschini

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

1793 6f695a2e Manuel Franceschini
    """
1794 746f7476 Iustin Pop
    pass
1795 6f695a2e Manuel Franceschini
1796 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1797 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1798 6f695a2e Manuel Franceschini

1799 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1800 6f695a2e Manuel Franceschini

1801 6f695a2e Manuel Franceschini
    """
1802 6f695a2e Manuel Franceschini
    pass
1803 6f695a2e Manuel Franceschini
1804 6f695a2e Manuel Franceschini
  def Close(self):
1805 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1806 6f695a2e Manuel Franceschini

1807 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1808 6f695a2e Manuel Franceschini

1809 6f695a2e Manuel Franceschini
    """
1810 6f695a2e Manuel Franceschini
    pass
1811 6f695a2e Manuel Franceschini
1812 6f695a2e Manuel Franceschini
  def Remove(self):
1813 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1814 6f695a2e Manuel Franceschini

1815 c41eea6e Iustin Pop
    @rtype: boolean
1816 c41eea6e Iustin Pop
    @return: True if the removal was successful
1817 6f695a2e Manuel Franceschini

1818 6f695a2e Manuel Franceschini
    """
1819 6f695a2e Manuel Franceschini
    try:
1820 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1821 6f695a2e Manuel Franceschini
    except OSError, err:
1822 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1823 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1824 6f695a2e Manuel Franceschini
1825 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
1826 bbe4cc16 Iustin Pop
    """Renames the file.
1827 bbe4cc16 Iustin Pop

1828 bbe4cc16 Iustin Pop
    """
1829 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
1830 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
1831 bbe4cc16 Iustin Pop
1832 bbe4cc16 Iustin Pop
  def Grow(self, amount):
1833 bbe4cc16 Iustin Pop
    """Grow the file
1834 bbe4cc16 Iustin Pop

1835 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
1836 bbe4cc16 Iustin Pop

1837 bbe4cc16 Iustin Pop
    """
1838 bbe4cc16 Iustin Pop
    # TODO: implement grow for file-based storage
1839 bbe4cc16 Iustin Pop
    _ThrowError("Grow not supported for file-based storage")
1840 bbe4cc16 Iustin Pop
1841 6f695a2e Manuel Franceschini
  def Attach(self):
1842 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1843 6f695a2e Manuel Franceschini

1844 6f695a2e Manuel Franceschini
    Check if this file already exists.
1845 6f695a2e Manuel Franceschini

1846 c41eea6e Iustin Pop
    @rtype: boolean
1847 c41eea6e Iustin Pop
    @return: True if file exists
1848 6f695a2e Manuel Franceschini

1849 6f695a2e Manuel Franceschini
    """
1850 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1851 ecb091e3 Iustin Pop
    return self.attached
1852 6f695a2e Manuel Franceschini
1853 fcff3897 Iustin Pop
  def GetActualSize(self):
1854 fcff3897 Iustin Pop
    """Return the actual disk size.
1855 fcff3897 Iustin Pop

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

1858 fcff3897 Iustin Pop
    """
1859 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
1860 fcff3897 Iustin Pop
    try:
1861 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
1862 fcff3897 Iustin Pop
      return st.st_size
1863 fcff3897 Iustin Pop
    except OSError, err:
1864 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
1865 fcff3897 Iustin Pop
1866 6f695a2e Manuel Franceschini
  @classmethod
1867 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1868 6f695a2e Manuel Franceschini
    """Create a new file.
1869 6f695a2e Manuel Franceschini

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

1872 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
1873 c41eea6e Iustin Pop
    @return: an instance of FileStorage
1874 6f695a2e Manuel Franceschini

1875 6f695a2e Manuel Franceschini
    """
1876 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1877 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1878 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1879 aed77cea Guido Trotter
    if os.path.exists(dev_path):
1880 aed77cea Guido Trotter
      _ThrowError("File already existing: %s", dev_path)
1881 6f695a2e Manuel Franceschini
    try:
1882 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
1883 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1884 6f695a2e Manuel Franceschini
      f.close()
1885 6c626518 Iustin Pop
    except IOError, err:
1886 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
1887 6f695a2e Manuel Franceschini
1888 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
1889 6f695a2e Manuel Franceschini
1890 6f695a2e Manuel Franceschini
1891 a8083063 Iustin Pop
DEV_MAP = {
1892 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1893 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1894 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
1895 a8083063 Iustin Pop
  }
1896 a8083063 Iustin Pop
1897 a8083063 Iustin Pop
1898 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
1899 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1900 a8083063 Iustin Pop

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

1904 a8083063 Iustin Pop
  """
1905 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1906 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1907 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1908 cb999543 Iustin Pop
  if not device.attached:
1909 a8083063 Iustin Pop
    return None
1910 ecb091e3 Iustin Pop
  return device
1911 a8083063 Iustin Pop
1912 a8083063 Iustin Pop
1913 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
1914 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1915 a8083063 Iustin Pop

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

1919 a8083063 Iustin Pop
  """
1920 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1921 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1922 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1923 1063abd1 Iustin Pop
  device.Assemble()
1924 a8083063 Iustin Pop
  return device
1925 a8083063 Iustin Pop
1926 a8083063 Iustin Pop
1927 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1928 a8083063 Iustin Pop
  """Create a device.
1929 a8083063 Iustin Pop

1930 a8083063 Iustin Pop
  """
1931 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1932 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1933 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1934 a8083063 Iustin Pop
  return device