Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 92b61ec7

History | View | Annotate | Download (62.2 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Block device abstraction"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import re
25 a8083063 Iustin Pop
import time
26 a8083063 Iustin Pop
import errno
27 a2cfdea2 Iustin Pop
import pyparsing as pyp
28 6f695a2e Manuel Franceschini
import os
29 468c5f77 Iustin Pop
import logging
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
from ganeti import utils
32 a8083063 Iustin Pop
from ganeti import errors
33 fe96220b Iustin Pop
from ganeti import constants
34 96acbc09 Michael Hanselmann
from ganeti import objects
35 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 691744c4 Iustin Pop
    except (TypeError, 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 9122e60a Iustin Pop
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
796 9122e60a Iustin Pop
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
797 a8083063 Iustin Pop
798 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
799 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
800 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
801 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
802 a8083063 Iustin Pop
803 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
804 6b90c22e Iustin Pop
805 abdf0113 Iustin Pop
  @staticmethod
806 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
807 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
808 a8083063 Iustin Pop

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

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

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

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

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

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

892 a8083063 Iustin Pop
    """
893 abdf0113 Iustin Pop
    data = cls._GetProcData()
894 a8083063 Iustin Pop
895 abdf0113 Iustin Pop
    used_devs = {}
896 abdf0113 Iustin Pop
    for line in data:
897 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.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 691744c4 Iustin Pop
    except (TypeError, 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
    highest = None
1029 a2cfdea2 Iustin Pop
    for line in data:
1030 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1031 a2cfdea2 Iustin Pop
      if match:
1032 a2cfdea2 Iustin Pop
        return int(match.group(1))
1033 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1034 a2cfdea2 Iustin Pop
      if match:
1035 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1036 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1037 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1038 a2cfdea2 Iustin Pop
      return 0
1039 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1040 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1041 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1042 a2cfdea2 Iustin Pop
    return highest + 1
1043 a2cfdea2 Iustin Pop
1044 a2cfdea2 Iustin Pop
  @classmethod
1045 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1046 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1047 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

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

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

1209 a2cfdea2 Iustin Pop
    """
1210 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1211 f069addf Iustin Pop
            backend, meta, "0",
1212 f069addf Iustin Pop
            "-e", "detach",
1213 f069addf Iustin Pop
            "--create-device"]
1214 60bca04a Iustin Pop
    if size:
1215 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1216 89b70f39 Iustin Pop
    if not constants.DRBD_BARRIERS: # disable barriers, if configured so
1217 89b70f39 Iustin Pop
      version = cls._GetVersion()
1218 89b70f39 Iustin Pop
      # various DRBD versions support different disk barrier options;
1219 89b70f39 Iustin Pop
      # what we aim here is to revert back to the 'drain' method of
1220 89b70f39 Iustin Pop
      # disk flushes and to disable metadata barriers, in effect going
1221 89b70f39 Iustin Pop
      # back to pre-8.0.7 behaviour
1222 89b70f39 Iustin Pop
      vmaj = version['k_major']
1223 89b70f39 Iustin Pop
      vmin = version['k_minor']
1224 89b70f39 Iustin Pop
      vrel = version['k_point']
1225 89b70f39 Iustin Pop
      assert vmaj == 8
1226 89b70f39 Iustin Pop
      if vmin == 0: # 8.0.x
1227 89b70f39 Iustin Pop
        if vrel >= 12:
1228 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1229 89b70f39 Iustin Pop
      elif vmin == 2: # 8.2.x
1230 89b70f39 Iustin Pop
        if vrel >= 7:
1231 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1232 89b70f39 Iustin Pop
      elif vmaj >= 3: # 8.3.x or newer
1233 89b70f39 Iustin Pop
        args.extend(['-i', '-a', 'm'])
1234 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1235 a2cfdea2 Iustin Pop
    if result.failed:
1236 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1237 a2cfdea2 Iustin Pop
1238 a2cfdea2 Iustin Pop
  @classmethod
1239 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1240 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1241 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1242 a2cfdea2 Iustin Pop

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

1291 b00b95dd Iustin Pop
    """
1292 b00b95dd Iustin Pop
    if self.minor is None:
1293 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1294 82463074 Iustin Pop
                  self._aminor)
1295 b00b95dd Iustin Pop
    if len(devices) != 2:
1296 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1297 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1298 03ece5f3 Iustin Pop
    if "local_dev" in info:
1299 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1300 b00b95dd Iustin Pop
    backend, meta = devices
1301 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1302 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1303 b00b95dd Iustin Pop
    backend.Open()
1304 b00b95dd Iustin Pop
    meta.Open()
1305 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1306 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1307 b00b95dd Iustin Pop
1308 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1309 b00b95dd Iustin Pop
    self._children = devices
1310 b00b95dd Iustin Pop
1311 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1312 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1313 b00b95dd Iustin Pop

1314 b00b95dd Iustin Pop
    """
1315 b00b95dd Iustin Pop
    if self.minor is None:
1316 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1317 82463074 Iustin Pop
                  self._aminor)
1318 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1319 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1320 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1321 03ece5f3 Iustin Pop
      return
1322 b00b95dd Iustin Pop
    if len(self._children) != 2:
1323 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1324 82463074 Iustin Pop
                  self._children)
1325 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1326 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1327 e739bd57 Iustin Pop
      return
1328 b00b95dd Iustin Pop
    if len(devices) != 2:
1329 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1330 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1331 e739bd57 Iustin Pop
      if dev != child.dev_path:
1332 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1333 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1334 b00b95dd Iustin Pop
1335 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1336 b00b95dd Iustin Pop
    self._children = []
1337 b00b95dd Iustin Pop
1338 7d585316 Iustin Pop
  @classmethod
1339 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1340 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1341 a2cfdea2 Iustin Pop

1342 7d585316 Iustin Pop
    This is the low-level implementation.
1343 7d585316 Iustin Pop

1344 7d585316 Iustin Pop
    @type minor: int
1345 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
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 a2cfdea2 Iustin Pop
    """
1352 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1353 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1354 a2cfdea2 Iustin Pop
    if result.failed:
1355 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1356 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1357 7d585316 Iustin Pop
    return not result.failed
1358 7d585316 Iustin Pop
1359 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1360 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1361 7d585316 Iustin Pop

1362 7d585316 Iustin Pop
    @type kbytes: int
1363 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1364 7d585316 Iustin Pop
    @rtype: boolean
1365 7d585316 Iustin Pop
    @return: the success of the operation
1366 7d585316 Iustin Pop

1367 7d585316 Iustin Pop
    """
1368 7d585316 Iustin Pop
    if self.minor is None:
1369 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1370 7d585316 Iustin Pop
      return False
1371 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1372 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1373 a2cfdea2 Iustin Pop
1374 6b90c22e Iustin Pop
  def GetProcStatus(self):
1375 6b90c22e Iustin Pop
    """Return device data from /proc.
1376 6b90c22e Iustin Pop

1377 6b90c22e Iustin Pop
    """
1378 6b90c22e Iustin Pop
    if self.minor is None:
1379 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1380 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1381 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1382 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1383 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1384 6b90c22e Iustin Pop
1385 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1386 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1387 a2cfdea2 Iustin Pop

1388 a2cfdea2 Iustin Pop

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

1393 0834c866 Iustin Pop

1394 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1395 0834c866 Iustin Pop
    network not connected or local disk missing.
1396 0834c866 Iustin Pop

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

1400 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1401 c41eea6e Iustin Pop

1402 a2cfdea2 Iustin Pop
    """
1403 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1404 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1405 96acbc09 Michael Hanselmann
1406 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1407 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1408 f208978a Michael Hanselmann
1409 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1410 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1411 f208978a Michael Hanselmann
    elif stats.is_diskless:
1412 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1413 f208978a Michael Hanselmann
    else:
1414 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1415 96acbc09 Michael Hanselmann
1416 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1417 96acbc09 Michael Hanselmann
                                  major=self.major,
1418 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1419 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1420 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1421 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1422 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1423 a2cfdea2 Iustin Pop
1424 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1425 a2cfdea2 Iustin Pop
    """Make the local state primary.
1426 a2cfdea2 Iustin Pop

1427 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1428 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1429 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1430 f860ff4e Guido Trotter
    is mandatory.
1431 a2cfdea2 Iustin Pop

1432 a2cfdea2 Iustin Pop
    """
1433 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1434 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1435 a2cfdea2 Iustin Pop
      return False
1436 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1437 a2cfdea2 Iustin Pop
    if force:
1438 a2cfdea2 Iustin Pop
      cmd.append("-o")
1439 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1440 a2cfdea2 Iustin Pop
    if result.failed:
1441 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1442 82463074 Iustin Pop
                  result.output)
1443 a2cfdea2 Iustin Pop
1444 a2cfdea2 Iustin Pop
  def Close(self):
1445 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1446 a2cfdea2 Iustin Pop

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

1449 a2cfdea2 Iustin Pop
    """
1450 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1451 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1452 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1453 a2cfdea2 Iustin Pop
    if result.failed:
1454 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1455 82463074 Iustin Pop
                  self.minor, result.output)
1456 a2cfdea2 Iustin Pop
1457 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1458 cf8df3f3 Iustin Pop
    """Removes network configuration.
1459 cf8df3f3 Iustin Pop

1460 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1461 cf8df3f3 Iustin Pop

1462 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1463 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1464 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1465 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1466 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1467 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1468 cf8df3f3 Iustin Pop
    exception.
1469 cf8df3f3 Iustin Pop

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

1520 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1521 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1522 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1523 cf8df3f3 Iustin Pop

1524 cf8df3f3 Iustin Pop
    Args:
1525 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1526 cf8df3f3 Iustin Pop

1527 cf8df3f3 Iustin Pop
    """
1528 cf8df3f3 Iustin Pop
    if self.minor is None:
1529 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1530 cf8df3f3 Iustin Pop
1531 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1532 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1533 cf8df3f3 Iustin Pop
1534 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1535 cf8df3f3 Iustin Pop
1536 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1537 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1538 cf8df3f3 Iustin Pop
1539 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1540 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1541 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1542 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1543 cf8df3f3 Iustin Pop
1544 a2cfdea2 Iustin Pop
  def Attach(self):
1545 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1546 2d0c8319 Iustin Pop

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

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

1554 2d0c8319 Iustin Pop
    """
1555 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1556 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1557 2d0c8319 Iustin Pop
      minor = self._aminor
1558 2d0c8319 Iustin Pop
    else:
1559 2d0c8319 Iustin Pop
      minor = None
1560 2d0c8319 Iustin Pop
1561 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1562 2d0c8319 Iustin Pop
    return minor is not None
1563 2d0c8319 Iustin Pop
1564 2d0c8319 Iustin Pop
  def Assemble(self):
1565 2d0c8319 Iustin Pop
    """Assemble the drbd.
1566 2d0c8319 Iustin Pop

1567 2d0c8319 Iustin Pop
    Method:
1568 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1569 2d0c8319 Iustin Pop
        our config
1570 2d0c8319 Iustin Pop
      - if not, we create it from zero
1571 2d0c8319 Iustin Pop

1572 2d0c8319 Iustin Pop
    """
1573 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1574 2d0c8319 Iustin Pop
1575 2d0c8319 Iustin Pop
    self.Attach()
1576 2d0c8319 Iustin Pop
    if self.minor is None:
1577 2d0c8319 Iustin Pop
      # local device completely unconfigured
1578 1063abd1 Iustin Pop
      self._FastAssemble()
1579 2d0c8319 Iustin Pop
    else:
1580 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1581 2d0c8319 Iustin Pop
      # the device
1582 1063abd1 Iustin Pop
      self._SlowAssemble()
1583 2d0c8319 Iustin Pop
1584 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1585 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1586 a2cfdea2 Iustin Pop

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

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

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

1664 a2cfdea2 Iustin Pop
    """
1665 a1578d63 Iustin Pop
    minor = self._aminor
1666 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1667 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1668 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1669 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1670 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1671 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1672 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1673 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1674 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1675 a2cfdea2 Iustin Pop
1676 a2cfdea2 Iustin Pop
  @classmethod
1677 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1678 b00b95dd Iustin Pop
    """Detach from the local device.
1679 b00b95dd Iustin Pop

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

1683 b00b95dd Iustin Pop
    """
1684 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1685 b00b95dd Iustin Pop
    if result.failed:
1686 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1687 b00b95dd Iustin Pop
1688 b00b95dd Iustin Pop
  @classmethod
1689 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1690 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1691 f3e513ad Iustin Pop

1692 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1693 f3e513ad Iustin Pop

1694 f3e513ad Iustin Pop
    """
1695 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1696 a8459f1c Iustin Pop
    if result.failed:
1697 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1698 f3e513ad Iustin Pop
1699 f3e513ad Iustin Pop
  @classmethod
1700 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1701 a2cfdea2 Iustin Pop
    """Deactivate the device.
1702 a2cfdea2 Iustin Pop

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

1705 a2cfdea2 Iustin Pop
    """
1706 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1707 a2cfdea2 Iustin Pop
    if result.failed:
1708 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1709 33bc6f01 Iustin Pop
                  minor, result.output)
1710 a2cfdea2 Iustin Pop
1711 a2cfdea2 Iustin Pop
  def Shutdown(self):
1712 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1713 a2cfdea2 Iustin Pop

1714 a2cfdea2 Iustin Pop
    """
1715 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1716 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1717 746f7476 Iustin Pop
      return
1718 746f7476 Iustin Pop
    minor = self.minor
1719 a2cfdea2 Iustin Pop
    self.minor = None
1720 a2cfdea2 Iustin Pop
    self.dev_path = None
1721 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1722 a2cfdea2 Iustin Pop
1723 a2cfdea2 Iustin Pop
  def Remove(self):
1724 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1725 a2cfdea2 Iustin Pop

1726 a2cfdea2 Iustin Pop
    """
1727 0c6c04ec Iustin Pop
    self.Shutdown()
1728 a2cfdea2 Iustin Pop
1729 a2cfdea2 Iustin Pop
  @classmethod
1730 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1731 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1732 a2cfdea2 Iustin Pop

1733 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1734 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1735 a2cfdea2 Iustin Pop

1736 a2cfdea2 Iustin Pop
    """
1737 a2cfdea2 Iustin Pop
    if len(children) != 2:
1738 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1739 767d52d3 Iustin Pop
    # check that the minor is unused
1740 767d52d3 Iustin Pop
    aminor = unique_id[4]
1741 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1742 767d52d3 Iustin Pop
    if aminor in proc_info:
1743 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1744 767d52d3 Iustin Pop
      in_use = status.is_in_use
1745 767d52d3 Iustin Pop
    else:
1746 767d52d3 Iustin Pop
      in_use = False
1747 767d52d3 Iustin Pop
    if in_use:
1748 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1749 a2cfdea2 Iustin Pop
    meta = children[1]
1750 a2cfdea2 Iustin Pop
    meta.Assemble()
1751 a2cfdea2 Iustin Pop
    if not meta.Attach():
1752 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1753 33bc6f01 Iustin Pop
                  aminor, meta)
1754 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1755 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1756 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1757 a2cfdea2 Iustin Pop
1758 1005d816 Iustin Pop
  def Grow(self, amount):
1759 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1760 1005d816 Iustin Pop

1761 1005d816 Iustin Pop
    """
1762 1005d816 Iustin Pop
    if self.minor is None:
1763 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1764 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1765 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1766 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1767 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1768 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1769 1005d816 Iustin Pop
    if result.failed:
1770 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1771 1005d816 Iustin Pop
1772 a8083063 Iustin Pop
1773 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1774 6f695a2e Manuel Franceschini
  """File device.
1775 abdf0113 Iustin Pop

1776 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1777 6f695a2e Manuel Franceschini

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

1780 6f695a2e Manuel Franceschini
  """
1781 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1782 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1783 6f695a2e Manuel Franceschini

1784 6f695a2e Manuel Franceschini
    """
1785 6f695a2e Manuel Franceschini
    if children:
1786 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1787 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1788 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1789 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1790 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1791 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1792 ecb091e3 Iustin Pop
    self.Attach()
1793 6f695a2e Manuel Franceschini
1794 6f695a2e Manuel Franceschini
  def Assemble(self):
1795 6f695a2e Manuel Franceschini
    """Assemble the device.
1796 6f695a2e Manuel Franceschini

1797 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1798 6f695a2e Manuel Franceschini

1799 6f695a2e Manuel Franceschini
    """
1800 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1801 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1802 6f695a2e Manuel Franceschini
1803 6f695a2e Manuel Franceschini
  def Shutdown(self):
1804 6f695a2e Manuel Franceschini
    """Shutdown the device.
1805 6f695a2e Manuel Franceschini

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

1809 6f695a2e Manuel Franceschini
    """
1810 746f7476 Iustin Pop
    pass
1811 6f695a2e Manuel Franceschini
1812 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1813 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1814 6f695a2e Manuel Franceschini

1815 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1816 6f695a2e Manuel Franceschini

1817 6f695a2e Manuel Franceschini
    """
1818 6f695a2e Manuel Franceschini
    pass
1819 6f695a2e Manuel Franceschini
1820 6f695a2e Manuel Franceschini
  def Close(self):
1821 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1822 6f695a2e Manuel Franceschini

1823 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1824 6f695a2e Manuel Franceschini

1825 6f695a2e Manuel Franceschini
    """
1826 6f695a2e Manuel Franceschini
    pass
1827 6f695a2e Manuel Franceschini
1828 6f695a2e Manuel Franceschini
  def Remove(self):
1829 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1830 6f695a2e Manuel Franceschini

1831 c41eea6e Iustin Pop
    @rtype: boolean
1832 c41eea6e Iustin Pop
    @return: True if the removal was successful
1833 6f695a2e Manuel Franceschini

1834 6f695a2e Manuel Franceschini
    """
1835 6f695a2e Manuel Franceschini
    try:
1836 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1837 6f695a2e Manuel Franceschini
    except OSError, err:
1838 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1839 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1840 6f695a2e Manuel Franceschini
1841 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
1842 bbe4cc16 Iustin Pop
    """Renames the file.
1843 bbe4cc16 Iustin Pop

1844 bbe4cc16 Iustin Pop
    """
1845 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
1846 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
1847 bbe4cc16 Iustin Pop
1848 bbe4cc16 Iustin Pop
  def Grow(self, amount):
1849 bbe4cc16 Iustin Pop
    """Grow the file
1850 bbe4cc16 Iustin Pop

1851 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
1852 bbe4cc16 Iustin Pop

1853 bbe4cc16 Iustin Pop
    """
1854 bbe4cc16 Iustin Pop
    # TODO: implement grow for file-based storage
1855 bbe4cc16 Iustin Pop
    _ThrowError("Grow not supported for file-based storage")
1856 bbe4cc16 Iustin Pop
1857 6f695a2e Manuel Franceschini
  def Attach(self):
1858 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1859 6f695a2e Manuel Franceschini

1860 6f695a2e Manuel Franceschini
    Check if this file already exists.
1861 6f695a2e Manuel Franceschini

1862 c41eea6e Iustin Pop
    @rtype: boolean
1863 c41eea6e Iustin Pop
    @return: True if file exists
1864 6f695a2e Manuel Franceschini

1865 6f695a2e Manuel Franceschini
    """
1866 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1867 ecb091e3 Iustin Pop
    return self.attached
1868 6f695a2e Manuel Franceschini
1869 fcff3897 Iustin Pop
  def GetActualSize(self):
1870 fcff3897 Iustin Pop
    """Return the actual disk size.
1871 fcff3897 Iustin Pop

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

1874 fcff3897 Iustin Pop
    """
1875 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
1876 fcff3897 Iustin Pop
    try:
1877 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
1878 fcff3897 Iustin Pop
      return st.st_size
1879 fcff3897 Iustin Pop
    except OSError, err:
1880 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
1881 fcff3897 Iustin Pop
1882 6f695a2e Manuel Franceschini
  @classmethod
1883 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1884 6f695a2e Manuel Franceschini
    """Create a new file.
1885 6f695a2e Manuel Franceschini

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

1888 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
1889 c41eea6e Iustin Pop
    @return: an instance of FileStorage
1890 6f695a2e Manuel Franceschini

1891 6f695a2e Manuel Franceschini
    """
1892 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1893 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1894 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1895 aed77cea Guido Trotter
    if os.path.exists(dev_path):
1896 aed77cea Guido Trotter
      _ThrowError("File already existing: %s", dev_path)
1897 6f695a2e Manuel Franceschini
    try:
1898 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
1899 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1900 6f695a2e Manuel Franceschini
      f.close()
1901 6c626518 Iustin Pop
    except IOError, err:
1902 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
1903 6f695a2e Manuel Franceschini
1904 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
1905 6f695a2e Manuel Franceschini
1906 6f695a2e Manuel Franceschini
1907 a8083063 Iustin Pop
DEV_MAP = {
1908 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1909 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1910 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
1911 a8083063 Iustin Pop
  }
1912 a8083063 Iustin Pop
1913 a8083063 Iustin Pop
1914 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
1915 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1916 a8083063 Iustin Pop

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

1920 a8083063 Iustin Pop
  """
1921 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1922 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1923 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1924 cb999543 Iustin Pop
  if not device.attached:
1925 a8083063 Iustin Pop
    return None
1926 ecb091e3 Iustin Pop
  return device
1927 a8083063 Iustin Pop
1928 a8083063 Iustin Pop
1929 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
1930 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1931 a8083063 Iustin Pop

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

1935 a8083063 Iustin Pop
  """
1936 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1937 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1938 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1939 1063abd1 Iustin Pop
  device.Assemble()
1940 a8083063 Iustin Pop
  return device
1941 a8083063 Iustin Pop
1942 a8083063 Iustin Pop
1943 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1944 a8083063 Iustin Pop
  """Create a device.
1945 a8083063 Iustin Pop

1946 a8083063 Iustin Pop
  """
1947 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1948 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1949 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1950 a8083063 Iustin Pop
  return device