Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ b8291e00

History | View | Annotate | Download (86.6 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 0304f0ec Iustin Pop
# Copyright (C) 2006, 2007, 2010, 2011, 2012 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 ad1dd4c7 Andrea Spadaccini
import shlex
28 b6135bbc Apollon Oikonomopoulos
import stat
29 a2cfdea2 Iustin Pop
import pyparsing as pyp
30 6f695a2e Manuel Franceschini
import os
31 468c5f77 Iustin Pop
import logging
32 a8083063 Iustin Pop
33 a8083063 Iustin Pop
from ganeti import utils
34 a8083063 Iustin Pop
from ganeti import errors
35 fe96220b Iustin Pop
from ganeti import constants
36 96acbc09 Michael Hanselmann
from ganeti import objects
37 cea881e5 Michael Hanselmann
from ganeti import compat
38 a744b676 Manuel Franceschini
from ganeti import netutils
39 a8083063 Iustin Pop
40 a8083063 Iustin Pop
41 310fbb64 Iustin Pop
# Size of reads in _CanReadDevice
42 310fbb64 Iustin Pop
_DEVICE_READ_SIZE = 128 * 1024
43 310fbb64 Iustin Pop
44 310fbb64 Iustin Pop
45 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
46 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
47 82463074 Iustin Pop

48 82463074 Iustin Pop
  This is used in order to simplify the execution of cleanup or
49 82463074 Iustin Pop
  rollback functions.
50 82463074 Iustin Pop

51 82463074 Iustin Pop
  @rtype: boolean
52 82463074 Iustin Pop
  @return: True when fn didn't raise an exception, False otherwise
53 82463074 Iustin Pop

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

66 82463074 Iustin Pop
  @type msg: string
67 82463074 Iustin Pop
  @param msg: the text of the exception
68 82463074 Iustin Pop
  @raise errors.BlockDeviceError
69 82463074 Iustin Pop

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

80 310fbb64 Iustin Pop
  This tries to read the first 128k of the device.
81 310fbb64 Iustin Pop

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

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

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

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

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

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

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

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

133 a8083063 Iustin Pop
  """
134 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
135 a8083063 Iustin Pop
    self._children = children
136 a8083063 Iustin Pop
    self.dev_path = None
137 a8083063 Iustin Pop
    self.unique_id = unique_id
138 a8083063 Iustin Pop
    self.major = None
139 a8083063 Iustin Pop
    self.minor = None
140 cb999543 Iustin Pop
    self.attached = False
141 464f8daf Iustin Pop
    self.size = size
142 94dcbdb0 Andrea Spadaccini
    self.params = params
143 a8083063 Iustin Pop
144 a8083063 Iustin Pop
  def Assemble(self):
145 a8083063 Iustin Pop
    """Assemble the device from its components.
146 a8083063 Iustin Pop

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

155 a8083063 Iustin Pop
    """
156 1063abd1 Iustin Pop
    pass
157 a8083063 Iustin Pop
158 a8083063 Iustin Pop
  def Attach(self):
159 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
160 a8083063 Iustin Pop

161 a8083063 Iustin Pop
    """
162 a8083063 Iustin Pop
    raise NotImplementedError
163 a8083063 Iustin Pop
164 a8083063 Iustin Pop
  def Close(self):
165 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
166 a8083063 Iustin Pop

167 a8083063 Iustin Pop
    """
168 a8083063 Iustin Pop
    raise NotImplementedError
169 a8083063 Iustin Pop
170 a8083063 Iustin Pop
  @classmethod
171 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
172 a8083063 Iustin Pop
    """Create the device.
173 a8083063 Iustin Pop

174 a8083063 Iustin Pop
    If the device cannot be created, it will return None
175 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
176 a8083063 Iustin Pop

177 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
178 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
179 a8083063 Iustin Pop
    enough for both creation and assembly (later).
180 a8083063 Iustin Pop

181 a8083063 Iustin Pop
    """
182 a8083063 Iustin Pop
    raise NotImplementedError
183 a8083063 Iustin Pop
184 a8083063 Iustin Pop
  def Remove(self):
185 a8083063 Iustin Pop
    """Remove this device.
186 a8083063 Iustin Pop

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

191 a8083063 Iustin Pop
    """
192 a8083063 Iustin Pop
    raise NotImplementedError
193 a8083063 Iustin Pop
194 f3e513ad Iustin Pop
  def Rename(self, new_id):
195 f3e513ad Iustin Pop
    """Rename this device.
196 f3e513ad Iustin Pop

197 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
198 f3e513ad Iustin Pop

199 f3e513ad Iustin Pop
    """
200 f3e513ad Iustin Pop
    raise NotImplementedError
201 f3e513ad Iustin Pop
202 a8083063 Iustin Pop
  def Open(self, force=False):
203 a8083063 Iustin Pop
    """Make the device ready for use.
204 a8083063 Iustin Pop

205 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
206 a8083063 Iustin Pop
    devices need this.
207 a8083063 Iustin Pop

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

211 a8083063 Iustin Pop
    """
212 a8083063 Iustin Pop
    raise NotImplementedError
213 a8083063 Iustin Pop
214 a8083063 Iustin Pop
  def Shutdown(self):
215 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
216 a8083063 Iustin Pop

217 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
218 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
219 a8083063 Iustin Pop
    assembled after this call.
220 a8083063 Iustin Pop

221 a8083063 Iustin Pop
    """
222 a8083063 Iustin Pop
    raise NotImplementedError
223 a8083063 Iustin Pop
224 f2f57b6e Andrea Spadaccini
  def SetSyncParams(self, params):
225 f2f57b6e Andrea Spadaccini
    """Adjust the synchronization parameters of the mirror.
226 a8083063 Iustin Pop

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

229 f2f57b6e Andrea Spadaccini
    @param params: dictionary of LD level disk parameters related to the
230 f2f57b6e Andrea Spadaccini
    synchronization.
231 8584e922 Andrea Spadaccini
    @rtype: list
232 8584e922 Andrea Spadaccini
    @return: a list of error messages, emitted both by the current node and by
233 8584e922 Andrea Spadaccini
    children. An empty list means no errors.
234 f2f57b6e Andrea Spadaccini

235 a8083063 Iustin Pop
    """
236 8584e922 Andrea Spadaccini
    result = []
237 a8083063 Iustin Pop
    if self._children:
238 a8083063 Iustin Pop
      for child in self._children:
239 8584e922 Andrea Spadaccini
        result.extend(child.SetSyncParams(params))
240 a8083063 Iustin Pop
    return result
241 a8083063 Iustin Pop
242 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
243 a3fffcc6 René Nussbaumer
    """Pause/Resume the sync of the mirror.
244 a3fffcc6 René Nussbaumer

245 a3fffcc6 René Nussbaumer
    In case this is not a mirroring device, this is no-op.
246 a3fffcc6 René Nussbaumer

247 f2f57b6e Andrea Spadaccini
    @param pause: Whether to pause or resume
248 a3fffcc6 René Nussbaumer

249 a3fffcc6 René Nussbaumer
    """
250 a3fffcc6 René Nussbaumer
    result = True
251 a3fffcc6 René Nussbaumer
    if self._children:
252 a3fffcc6 René Nussbaumer
      for child in self._children:
253 a3fffcc6 René Nussbaumer
        result = result and child.PauseResumeSync(pause)
254 a3fffcc6 René Nussbaumer
    return result
255 a3fffcc6 René Nussbaumer
256 a8083063 Iustin Pop
  def GetSyncStatus(self):
257 a8083063 Iustin Pop
    """Returns the sync status of the device.
258 a8083063 Iustin Pop

259 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
260 a8083063 Iustin Pop
    status of the mirror.
261 a8083063 Iustin Pop

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

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

267 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
268 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
269 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
270 a8083063 Iustin Pop

271 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
272 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
273 0834c866 Iustin Pop
    return False (not degraded).
274 0834c866 Iustin Pop

275 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
276 c41eea6e Iustin Pop

277 a8083063 Iustin Pop
    """
278 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
279 96acbc09 Michael Hanselmann
                                  major=self.major,
280 96acbc09 Michael Hanselmann
                                  minor=self.minor,
281 96acbc09 Michael Hanselmann
                                  sync_percent=None,
282 96acbc09 Michael Hanselmann
                                  estimated_time=None,
283 96acbc09 Michael Hanselmann
                                  is_degraded=False,
284 f208978a Michael Hanselmann
                                  ldisk_status=constants.LDS_OKAY)
285 a8083063 Iustin Pop
286 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
287 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
288 a8083063 Iustin Pop

289 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
290 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
291 a8083063 Iustin Pop
    children.
292 a8083063 Iustin Pop

293 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
294 96acbc09 Michael Hanselmann

295 a8083063 Iustin Pop
    """
296 96acbc09 Michael Hanselmann
    status = self.GetSyncStatus()
297 96acbc09 Michael Hanselmann
298 96acbc09 Michael Hanselmann
    min_percent = status.sync_percent
299 96acbc09 Michael Hanselmann
    max_time = status.estimated_time
300 96acbc09 Michael Hanselmann
    is_degraded = status.is_degraded
301 f208978a Michael Hanselmann
    ldisk_status = status.ldisk_status
302 96acbc09 Michael Hanselmann
303 a8083063 Iustin Pop
    if self._children:
304 a8083063 Iustin Pop
      for child in self._children:
305 96acbc09 Michael Hanselmann
        child_status = child.GetSyncStatus()
306 96acbc09 Michael Hanselmann
307 a8083063 Iustin Pop
        if min_percent is None:
308 96acbc09 Michael Hanselmann
          min_percent = child_status.sync_percent
309 96acbc09 Michael Hanselmann
        elif child_status.sync_percent is not None:
310 96acbc09 Michael Hanselmann
          min_percent = min(min_percent, child_status.sync_percent)
311 96acbc09 Michael Hanselmann
312 a8083063 Iustin Pop
        if max_time is None:
313 96acbc09 Michael Hanselmann
          max_time = child_status.estimated_time
314 96acbc09 Michael Hanselmann
        elif child_status.estimated_time is not None:
315 96acbc09 Michael Hanselmann
          max_time = max(max_time, child_status.estimated_time)
316 96acbc09 Michael Hanselmann
317 96acbc09 Michael Hanselmann
        is_degraded = is_degraded or child_status.is_degraded
318 f208978a Michael Hanselmann
319 f208978a Michael Hanselmann
        if ldisk_status is None:
320 f208978a Michael Hanselmann
          ldisk_status = child_status.ldisk_status
321 f208978a Michael Hanselmann
        elif child_status.ldisk_status is not None:
322 f208978a Michael Hanselmann
          ldisk_status = max(ldisk_status, child_status.ldisk_status)
323 96acbc09 Michael Hanselmann
324 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
325 96acbc09 Michael Hanselmann
                                  major=self.major,
326 96acbc09 Michael Hanselmann
                                  minor=self.minor,
327 96acbc09 Michael Hanselmann
                                  sync_percent=min_percent,
328 96acbc09 Michael Hanselmann
                                  estimated_time=max_time,
329 96acbc09 Michael Hanselmann
                                  is_degraded=is_degraded,
330 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
331 a8083063 Iustin Pop
332 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
333 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
334 a0c3fea1 Michael Hanselmann

335 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
336 a0c3fea1 Michael Hanselmann

337 a0c3fea1 Michael Hanselmann
    """
338 a0c3fea1 Michael Hanselmann
    for child in self._children:
339 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
340 a0c3fea1 Michael Hanselmann
341 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
342 1005d816 Iustin Pop
    """Grow the block device.
343 1005d816 Iustin Pop

344 7fe23d47 Iustin Pop
    @type amount: integer
345 c41eea6e Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
346 7fe23d47 Iustin Pop
    @type dryrun: boolean
347 7fe23d47 Iustin Pop
    @param dryrun: whether to execute the operation in simulation mode
348 7fe23d47 Iustin Pop
        only, without actually increasing the size
349 1005d816 Iustin Pop

350 1005d816 Iustin Pop
    """
351 1005d816 Iustin Pop
    raise NotImplementedError
352 a0c3fea1 Michael Hanselmann
353 fcff3897 Iustin Pop
  def GetActualSize(self):
354 fcff3897 Iustin Pop
    """Return the actual disk size.
355 fcff3897 Iustin Pop

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

358 fcff3897 Iustin Pop
    """
359 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
360 fcff3897 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize64", self.dev_path])
361 fcff3897 Iustin Pop
    if result.failed:
362 fcff3897 Iustin Pop
      _ThrowError("blockdev failed (%s): %s",
363 fcff3897 Iustin Pop
                  result.fail_reason, result.output)
364 fcff3897 Iustin Pop
    try:
365 fcff3897 Iustin Pop
      sz = int(result.output.strip())
366 fcff3897 Iustin Pop
    except (ValueError, TypeError), err:
367 fcff3897 Iustin Pop
      _ThrowError("Failed to parse blockdev output: %s", str(err))
368 fcff3897 Iustin Pop
    return sz
369 fcff3897 Iustin Pop
370 a8083063 Iustin Pop
  def __repr__(self):
371 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
372 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
373 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
374 a8083063 Iustin Pop
375 a8083063 Iustin Pop
376 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
377 a8083063 Iustin Pop
  """Logical Volume block device.
378 a8083063 Iustin Pop

379 a8083063 Iustin Pop
  """
380 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
381 6136f8f0 Iustin Pop
  _INVALID_NAMES = frozenset([".", "..", "snapshot", "pvmove"])
382 6136f8f0 Iustin Pop
  _INVALID_SUBSTRINGS = frozenset(["_mlog", "_mimage"])
383 6136f8f0 Iustin Pop
384 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
385 a8083063 Iustin Pop
    """Attaches to a LV device.
386 a8083063 Iustin Pop

387 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
388 a8083063 Iustin Pop

389 a8083063 Iustin Pop
    """
390 94dcbdb0 Andrea Spadaccini
    super(LogicalVolume, self).__init__(unique_id, children, size, params)
391 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
392 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
393 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
394 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
395 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
396 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
397 99e8295c Iustin Pop
    self._degraded = True
398 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
399 a8083063 Iustin Pop
    self.Attach()
400 a8083063 Iustin Pop
401 a8083063 Iustin Pop
  @classmethod
402 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
403 a8083063 Iustin Pop
    """Create a new logical volume.
404 a8083063 Iustin Pop

405 a8083063 Iustin Pop
    """
406 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
407 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
408 6c626518 Iustin Pop
                                   str(unique_id))
409 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
410 6136f8f0 Iustin Pop
    cls._ValidateName(vg_name)
411 6136f8f0 Iustin Pop
    cls._ValidateName(lv_name)
412 2070598f Iustin Pop
    pvs_info = cls.GetPVInfo([vg_name])
413 a8083063 Iustin Pop
    if not pvs_info:
414 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", vg_name)
415 a8083063 Iustin Pop
    pvs_info.sort()
416 a8083063 Iustin Pop
    pvs_info.reverse()
417 5b7b5d49 Guido Trotter
418 e687ec01 Michael Hanselmann
    pvlist = [pv[1] for pv in pvs_info]
419 403f5172 Guido Trotter
    if compat.any(":" in v for v in pvlist):
420 01b6558a Iustin Pop
      _ThrowError("Some of your PVs have the invalid character ':' in their"
421 01b6558a Iustin Pop
                  " name, this is not supported - please filter them out"
422 01b6558a Iustin Pop
                  " in lvm.conf using either 'filter' or 'preferred_names'")
423 e687ec01 Michael Hanselmann
    free_size = sum([pv[0] for pv in pvs_info])
424 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
425 ac00bf1b Andrea Spadaccini
    desired_stripes = params[constants.LDP_STRIPES]
426 43e11798 Andrea Spadaccini
    stripes = min(current_pvs, desired_stripes)
427 43e11798 Andrea Spadaccini
    if stripes < desired_stripes:
428 43e11798 Andrea Spadaccini
      logging.warning("Could not use %d stripes for VG %s, as only %d PVs are"
429 43e11798 Andrea Spadaccini
                      " available.", desired_stripes, vg_name, current_pvs)
430 5b7b5d49 Guido Trotter
431 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
432 5b7b5d49 Guido Trotter
    # calling the create function.
433 a8083063 Iustin Pop
    if free_size < size:
434 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
435 82463074 Iustin Pop
                  " available %s", size, free_size)
436 fecbe9d5 Iustin Pop
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
437 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
438 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
439 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
440 fecbe9d5 Iustin Pop
    # stripes
441 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
442 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
443 fecbe9d5 Iustin Pop
      if not result.failed:
444 fecbe9d5 Iustin Pop
        break
445 a8083063 Iustin Pop
    if result.failed:
446 82463074 Iustin Pop
      _ThrowError("LV create failed (%s): %s",
447 82463074 Iustin Pop
                  result.fail_reason, result.output)
448 94dcbdb0 Andrea Spadaccini
    return LogicalVolume(unique_id, children, size, params)
449 a8083063 Iustin Pop
450 a8083063 Iustin Pop
  @staticmethod
451 197478f2 René Nussbaumer
  def _GetVolumeInfo(lvm_cmd, fields):
452 197478f2 René Nussbaumer
    """Returns LVM Volumen infos using lvm_cmd
453 197478f2 René Nussbaumer

454 197478f2 René Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
455 197478f2 René Nussbaumer
    @param fields: Fields to return
456 197478f2 René Nussbaumer
    @return: A list of dicts each with the parsed fields
457 197478f2 René Nussbaumer

458 197478f2 René Nussbaumer
    """
459 197478f2 René Nussbaumer
    if not fields:
460 197478f2 René Nussbaumer
      raise errors.ProgrammerError("No fields specified")
461 197478f2 René Nussbaumer
462 197478f2 René Nussbaumer
    sep = "|"
463 197478f2 René Nussbaumer
    cmd = [lvm_cmd, "--noheadings", "--nosuffix", "--units=m", "--unbuffered",
464 197478f2 René Nussbaumer
           "--separator=%s" % sep, "-o%s" % ",".join(fields)]
465 197478f2 René Nussbaumer
466 197478f2 René Nussbaumer
    result = utils.RunCmd(cmd)
467 197478f2 René Nussbaumer
    if result.failed:
468 197478f2 René Nussbaumer
      raise errors.CommandError("Can't get the volume information: %s - %s" %
469 197478f2 René Nussbaumer
                                (result.fail_reason, result.output))
470 197478f2 René Nussbaumer
471 197478f2 René Nussbaumer
    data = []
472 197478f2 René Nussbaumer
    for line in result.stdout.splitlines():
473 197478f2 René Nussbaumer
      splitted_fields = line.strip().split(sep)
474 197478f2 René Nussbaumer
475 197478f2 René Nussbaumer
      if len(fields) != len(splitted_fields):
476 197478f2 René Nussbaumer
        raise errors.CommandError("Can't parse %s output: line '%s'" %
477 197478f2 René Nussbaumer
                                  (lvm_cmd, line))
478 197478f2 René Nussbaumer
479 197478f2 René Nussbaumer
      data.append(splitted_fields)
480 197478f2 René Nussbaumer
481 197478f2 René Nussbaumer
    return data
482 197478f2 René Nussbaumer
483 197478f2 René Nussbaumer
  @classmethod
484 197478f2 René Nussbaumer
  def GetPVInfo(cls, vg_names, filter_allocatable=True):
485 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
486 a8083063 Iustin Pop

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

490 c41eea6e Iustin Pop
    @rtype: list
491 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
492 098c0958 Michael Hanselmann

493 a8083063 Iustin Pop
    """
494 197478f2 René Nussbaumer
    try:
495 197478f2 René Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
496 197478f2 René Nussbaumer
                                        "pv_attr"])
497 197478f2 René Nussbaumer
    except errors.GenericError, err:
498 197478f2 René Nussbaumer
      logging.error("Can't get PV information: %s", err)
499 a8083063 Iustin Pop
      return None
500 197478f2 René Nussbaumer
501 a8083063 Iustin Pop
    data = []
502 197478f2 René Nussbaumer
    for pv_name, vg_name, pv_free, pv_attr in info:
503 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
504 197478f2 René Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
505 a8083063 Iustin Pop
        continue
506 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
507 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
508 2070598f Iustin Pop
        continue
509 197478f2 René Nussbaumer
      data.append((float(pv_free), pv_name, vg_name))
510 197478f2 René Nussbaumer
511 197478f2 René Nussbaumer
    return data
512 197478f2 René Nussbaumer
513 197478f2 René Nussbaumer
  @classmethod
514 197478f2 René Nussbaumer
  def GetVGInfo(cls, vg_names, filter_readonly=True):
515 197478f2 René Nussbaumer
    """Get the free space info for specific VGs.
516 197478f2 René Nussbaumer

517 197478f2 René Nussbaumer
    @param vg_names: list of volume group names, if empty all will be returned
518 197478f2 René Nussbaumer
    @param filter_readonly: whether to skip over readonly VGs
519 197478f2 René Nussbaumer

520 197478f2 René Nussbaumer
    @rtype: list
521 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
522 673cd9c4 René Nussbaumer
             MiB
523 197478f2 René Nussbaumer

524 197478f2 René Nussbaumer
    """
525 197478f2 René Nussbaumer
    try:
526 673cd9c4 René Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
527 673cd9c4 René Nussbaumer
                                        "vg_size"])
528 197478f2 René Nussbaumer
    except errors.GenericError, err:
529 197478f2 René Nussbaumer
      logging.error("Can't get VG information: %s", err)
530 197478f2 René Nussbaumer
      return None
531 197478f2 René Nussbaumer
532 197478f2 René Nussbaumer
    data = []
533 673cd9c4 René Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
534 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not writable
535 197478f2 René Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
536 197478f2 René Nussbaumer
        continue
537 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
538 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
539 197478f2 René Nussbaumer
        continue
540 673cd9c4 René Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
541 a8083063 Iustin Pop
542 a8083063 Iustin Pop
    return data
543 a8083063 Iustin Pop
544 6136f8f0 Iustin Pop
  @classmethod
545 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
546 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
547 6136f8f0 Iustin Pop

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

552 6136f8f0 Iustin Pop
    """
553 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
554 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
555 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
556 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
557 6136f8f0 Iustin Pop
558 a8083063 Iustin Pop
  def Remove(self):
559 a8083063 Iustin Pop
    """Remove this logical volume.
560 a8083063 Iustin Pop

561 a8083063 Iustin Pop
    """
562 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
563 a8083063 Iustin Pop
      # the LV does not exist
564 0c6c04ec Iustin Pop
      return
565 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
566 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
567 a8083063 Iustin Pop
    if result.failed:
568 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
569 a8083063 Iustin Pop
570 f3e513ad Iustin Pop
  def Rename(self, new_id):
571 f3e513ad Iustin Pop
    """Rename this logical volume.
572 f3e513ad Iustin Pop

573 f3e513ad Iustin Pop
    """
574 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
575 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
576 f3e513ad Iustin Pop
    new_vg, new_name = new_id
577 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
578 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
579 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
580 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
581 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
582 f3e513ad Iustin Pop
    if result.failed:
583 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
584 be345db0 Iustin Pop
    self._lv_name = new_name
585 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
586 be345db0 Iustin Pop
587 a8083063 Iustin Pop
  def Attach(self):
588 a8083063 Iustin Pop
    """Attach to an existing LV.
589 a8083063 Iustin Pop

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

594 a8083063 Iustin Pop
    """
595 cb999543 Iustin Pop
    self.attached = False
596 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
597 38256320 Iustin Pop
                           "--units=m", "--nosuffix",
598 38256320 Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
599 38256320 Iustin Pop
                           "vg_extent_size,stripes", self.dev_path])
600 a8083063 Iustin Pop
    if result.failed:
601 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
602 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
603 a8083063 Iustin Pop
      return False
604 38256320 Iustin Pop
    # the output can (and will) have multiple lines for multi-segment
605 38256320 Iustin Pop
    # LVs, as the 'stripes' parameter is a segment one, so we take
606 38256320 Iustin Pop
    # only the last entry, which is the one we're interested in; note
607 38256320 Iustin Pop
    # that with LVM2 anyway the 'stripes' value must be constant
608 38256320 Iustin Pop
    # across segments, so this is a no-op actually
609 38256320 Iustin Pop
    out = result.stdout.splitlines()
610 38256320 Iustin Pop
    if not out: # totally empty result? splitlines() returns at least
611 38256320 Iustin Pop
                # one line for any non-empty string
612 38256320 Iustin Pop
      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
613 38256320 Iustin Pop
      return False
614 d0c8c01d Iustin Pop
    out = out[-1].strip().rstrip(",")
615 99e8295c Iustin Pop
    out = out.split(",")
616 38256320 Iustin Pop
    if len(out) != 5:
617 38256320 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
618 99e8295c Iustin Pop
      return False
619 99e8295c Iustin Pop
620 38256320 Iustin Pop
    status, major, minor, pe_size, stripes = out
621 0304f0ec Iustin Pop
    if len(status) < 6:
622 0304f0ec Iustin Pop
      logging.error("lvs lv_attr is not at least 6 characters (%s)", status)
623 99e8295c Iustin Pop
      return False
624 99e8295c Iustin Pop
625 99e8295c Iustin Pop
    try:
626 99e8295c Iustin Pop
      major = int(major)
627 99e8295c Iustin Pop
      minor = int(minor)
628 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
629 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
630 99e8295c Iustin Pop
631 38256320 Iustin Pop
    try:
632 38256320 Iustin Pop
      pe_size = int(float(pe_size))
633 38256320 Iustin Pop
    except (TypeError, ValueError), err:
634 38256320 Iustin Pop
      logging.error("Can't parse vg extent size: %s", err)
635 38256320 Iustin Pop
      return False
636 38256320 Iustin Pop
637 38256320 Iustin Pop
    try:
638 38256320 Iustin Pop
      stripes = int(stripes)
639 38256320 Iustin Pop
    except (TypeError, ValueError), err:
640 38256320 Iustin Pop
      logging.error("Can't parse the number of stripes: %s", err)
641 38256320 Iustin Pop
      return False
642 38256320 Iustin Pop
643 99e8295c Iustin Pop
    self.major = major
644 99e8295c Iustin Pop
    self.minor = minor
645 38256320 Iustin Pop
    self.pe_size = pe_size
646 38256320 Iustin Pop
    self.stripe_count = stripes
647 d0c8c01d Iustin Pop
    self._degraded = status[0] == "v" # virtual volume, i.e. doesn't backing
648 99e8295c Iustin Pop
                                      # storage
649 cb999543 Iustin Pop
    self.attached = True
650 99e8295c Iustin Pop
    return True
651 a8083063 Iustin Pop
652 a8083063 Iustin Pop
  def Assemble(self):
653 a8083063 Iustin Pop
    """Assemble the device.
654 a8083063 Iustin Pop

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

659 a8083063 Iustin Pop
    """
660 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
661 5574047a Iustin Pop
    if result.failed:
662 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
663 a8083063 Iustin Pop
664 a8083063 Iustin Pop
  def Shutdown(self):
665 a8083063 Iustin Pop
    """Shutdown the device.
666 a8083063 Iustin Pop

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

670 a8083063 Iustin Pop
    """
671 746f7476 Iustin Pop
    pass
672 a8083063 Iustin Pop
673 9db6dbce Iustin Pop
  def GetSyncStatus(self):
674 9db6dbce Iustin Pop
    """Returns the sync status of the device.
675 9db6dbce Iustin Pop

676 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
677 9db6dbce Iustin Pop
    status of the mirror.
678 9db6dbce Iustin Pop

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

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

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

692 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
693 c41eea6e Iustin Pop

694 9db6dbce Iustin Pop
    """
695 f208978a Michael Hanselmann
    if self._degraded:
696 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
697 f208978a Michael Hanselmann
    else:
698 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
699 f208978a Michael Hanselmann
700 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
701 96acbc09 Michael Hanselmann
                                  major=self.major,
702 96acbc09 Michael Hanselmann
                                  minor=self.minor,
703 96acbc09 Michael Hanselmann
                                  sync_percent=None,
704 96acbc09 Michael Hanselmann
                                  estimated_time=None,
705 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
706 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
707 9db6dbce Iustin Pop
708 a8083063 Iustin Pop
  def Open(self, force=False):
709 a8083063 Iustin Pop
    """Make the device ready for I/O.
710 a8083063 Iustin Pop

711 a8083063 Iustin Pop
    This is a no-op for the LV device type.
712 a8083063 Iustin Pop

713 a8083063 Iustin Pop
    """
714 fdbd668d Iustin Pop
    pass
715 a8083063 Iustin Pop
716 a8083063 Iustin Pop
  def Close(self):
717 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
718 a8083063 Iustin Pop

719 a8083063 Iustin Pop
    This is a no-op for the LV device type.
720 a8083063 Iustin Pop

721 a8083063 Iustin Pop
    """
722 fdbd668d Iustin Pop
    pass
723 a8083063 Iustin Pop
724 a8083063 Iustin Pop
  def Snapshot(self, size):
725 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
726 a8083063 Iustin Pop

727 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
728 800ac399 Iustin Pop

729 a8083063 Iustin Pop
    """
730 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
731 a8083063 Iustin Pop
732 a8083063 Iustin Pop
    # remove existing snapshot if found
733 94dcbdb0 Andrea Spadaccini
    snap = LogicalVolume((self._vg_name, snap_name), None, size, self.params)
734 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
735 a8083063 Iustin Pop
736 197478f2 René Nussbaumer
    vg_info = self.GetVGInfo([self._vg_name])
737 197478f2 René Nussbaumer
    if not vg_info:
738 197478f2 René Nussbaumer
      _ThrowError("Can't compute VG info for vg %s", self._vg_name)
739 673cd9c4 René Nussbaumer
    free_size, _, _ = vg_info[0]
740 a8083063 Iustin Pop
    if free_size < size:
741 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
742 82463074 Iustin Pop
                  " available %s", size, free_size)
743 a8083063 Iustin Pop
744 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
745 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
746 a8083063 Iustin Pop
    if result.failed:
747 82463074 Iustin Pop
      _ThrowError("command: %s error: %s - %s",
748 82463074 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
749 a8083063 Iustin Pop
750 800ac399 Iustin Pop
    return (self._vg_name, snap_name)
751 a8083063 Iustin Pop
752 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
753 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
754 a0c3fea1 Michael Hanselmann

755 a0c3fea1 Michael Hanselmann
    """
756 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
757 a0c3fea1 Michael Hanselmann
758 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
759 d0c8c01d Iustin Pop
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
760 d0c8c01d Iustin Pop
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
761 a0c3fea1 Michael Hanselmann
762 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
763 a0c3fea1 Michael Hanselmann
    text = text[:128]
764 a0c3fea1 Michael Hanselmann
765 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
766 a0c3fea1 Michael Hanselmann
                           self.dev_path])
767 a0c3fea1 Michael Hanselmann
    if result.failed:
768 82463074 Iustin Pop
      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
769 82463074 Iustin Pop
                  result.output)
770 82463074 Iustin Pop
771 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
772 1005d816 Iustin Pop
    """Grow the logical volume.
773 1005d816 Iustin Pop

774 1005d816 Iustin Pop
    """
775 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
776 38256320 Iustin Pop
      if not self.Attach():
777 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
778 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
779 38256320 Iustin Pop
    rest = amount % full_stripe_size
780 38256320 Iustin Pop
    if rest != 0:
781 38256320 Iustin Pop
      amount += full_stripe_size - rest
782 7fe23d47 Iustin Pop
    cmd = ["lvextend", "-L", "+%dm" % amount]
783 7fe23d47 Iustin Pop
    if dryrun:
784 7fe23d47 Iustin Pop
      cmd.append("--test")
785 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
786 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
787 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
788 1005d816 Iustin Pop
    # supports 'cling'
789 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
790 7fe23d47 Iustin Pop
      result = utils.RunCmd(cmd + ["--alloc", alloc_policy, self.dev_path])
791 1005d816 Iustin Pop
      if not result.failed:
792 1005d816 Iustin Pop
        return
793 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
794 a0c3fea1 Michael Hanselmann
795 a0c3fea1 Michael Hanselmann
796 6b90c22e Iustin Pop
class DRBD8Status(object):
797 6b90c22e Iustin Pop
  """A DRBD status representation class.
798 6b90c22e Iustin Pop

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

801 6b90c22e Iustin Pop
  """
802 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
803 01e2ce3a Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
804 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
805 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
806 7a380ddf René Nussbaumer
                       # Due to a bug in drbd in the kernel, introduced in
807 7a380ddf René Nussbaumer
                       # commit 4b0715f096 (still unfixed as of 2011-08-22)
808 7a380ddf René Nussbaumer
                       "(?:\s|M)"
809 7a380ddf René Nussbaumer
                       "finish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
810 6b90c22e Iustin Pop
811 3c003d9d Iustin Pop
  CS_UNCONFIGURED = "Unconfigured"
812 3c003d9d Iustin Pop
  CS_STANDALONE = "StandAlone"
813 3c003d9d Iustin Pop
  CS_WFCONNECTION = "WFConnection"
814 3c003d9d Iustin Pop
  CS_WFREPORTPARAMS = "WFReportParams"
815 3c003d9d Iustin Pop
  CS_CONNECTED = "Connected"
816 3c003d9d Iustin Pop
  CS_STARTINGSYNCS = "StartingSyncS"
817 3c003d9d Iustin Pop
  CS_STARTINGSYNCT = "StartingSyncT"
818 3c003d9d Iustin Pop
  CS_WFBITMAPS = "WFBitMapS"
819 3c003d9d Iustin Pop
  CS_WFBITMAPT = "WFBitMapT"
820 3c003d9d Iustin Pop
  CS_WFSYNCUUID = "WFSyncUUID"
821 3c003d9d Iustin Pop
  CS_SYNCSOURCE = "SyncSource"
822 3c003d9d Iustin Pop
  CS_SYNCTARGET = "SyncTarget"
823 3c003d9d Iustin Pop
  CS_PAUSEDSYNCS = "PausedSyncS"
824 3c003d9d Iustin Pop
  CS_PAUSEDSYNCT = "PausedSyncT"
825 3c003d9d Iustin Pop
  CSET_SYNC = frozenset([
826 3c003d9d Iustin Pop
    CS_WFREPORTPARAMS,
827 3c003d9d Iustin Pop
    CS_STARTINGSYNCS,
828 3c003d9d Iustin Pop
    CS_STARTINGSYNCT,
829 3c003d9d Iustin Pop
    CS_WFBITMAPS,
830 3c003d9d Iustin Pop
    CS_WFBITMAPT,
831 3c003d9d Iustin Pop
    CS_WFSYNCUUID,
832 3c003d9d Iustin Pop
    CS_SYNCSOURCE,
833 3c003d9d Iustin Pop
    CS_SYNCTARGET,
834 3c003d9d Iustin Pop
    CS_PAUSEDSYNCS,
835 3c003d9d Iustin Pop
    CS_PAUSEDSYNCT,
836 3c003d9d Iustin Pop
    ])
837 3c003d9d Iustin Pop
838 3c003d9d Iustin Pop
  DS_DISKLESS = "Diskless"
839 3c003d9d Iustin Pop
  DS_ATTACHING = "Attaching" # transient state
840 3c003d9d Iustin Pop
  DS_FAILED = "Failed" # transient state, next: diskless
841 3c003d9d Iustin Pop
  DS_NEGOTIATING = "Negotiating" # transient state
842 3c003d9d Iustin Pop
  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
843 3c003d9d Iustin Pop
  DS_OUTDATED = "Outdated"
844 3c003d9d Iustin Pop
  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
845 3c003d9d Iustin Pop
  DS_CONSISTENT = "Consistent"
846 3c003d9d Iustin Pop
  DS_UPTODATE = "UpToDate" # normal state
847 3c003d9d Iustin Pop
848 3c003d9d Iustin Pop
  RO_PRIMARY = "Primary"
849 3c003d9d Iustin Pop
  RO_SECONDARY = "Secondary"
850 3c003d9d Iustin Pop
  RO_UNKNOWN = "Unknown"
851 3c003d9d Iustin Pop
852 6b90c22e Iustin Pop
  def __init__(self, procline):
853 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
854 767d52d3 Iustin Pop
    if u:
855 3c003d9d Iustin Pop
      self.cstatus = self.CS_UNCONFIGURED
856 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
857 767d52d3 Iustin Pop
    else:
858 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
859 767d52d3 Iustin Pop
      if not m:
860 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
861 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
862 767d52d3 Iustin Pop
      self.lrole = m.group(2)
863 767d52d3 Iustin Pop
      self.rrole = m.group(3)
864 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
865 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
866 767d52d3 Iustin Pop
867 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
868 6b90c22e Iustin Pop
869 3c003d9d Iustin Pop
    self.is_standalone = self.cstatus == self.CS_STANDALONE
870 3c003d9d Iustin Pop
    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
871 3c003d9d Iustin Pop
    self.is_connected = self.cstatus == self.CS_CONNECTED
872 3c003d9d Iustin Pop
    self.is_primary = self.lrole == self.RO_PRIMARY
873 3c003d9d Iustin Pop
    self.is_secondary = self.lrole == self.RO_SECONDARY
874 3c003d9d Iustin Pop
    self.peer_primary = self.rrole == self.RO_PRIMARY
875 3c003d9d Iustin Pop
    self.peer_secondary = self.rrole == self.RO_SECONDARY
876 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
877 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
878 6b90c22e Iustin Pop
879 3c003d9d Iustin Pop
    self.is_diskless = self.ldisk == self.DS_DISKLESS
880 3c003d9d Iustin Pop
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
881 6b90c22e Iustin Pop
882 3c003d9d Iustin Pop
    self.is_in_resync = self.cstatus in self.CSET_SYNC
883 3c003d9d Iustin Pop
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
884 6b93ec9d Iustin Pop
885 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
886 6b90c22e Iustin Pop
    if m:
887 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
888 6b90c22e Iustin Pop
      hours = int(m.group(2))
889 6b90c22e Iustin Pop
      minutes = int(m.group(3))
890 6b90c22e Iustin Pop
      seconds = int(m.group(4))
891 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
892 6b90c22e Iustin Pop
    else:
893 3c003d9d Iustin Pop
      # we have (in this if branch) no percent information, but if
894 3c003d9d Iustin Pop
      # we're resyncing we need to 'fake' a sync percent information,
895 3c003d9d Iustin Pop
      # as this is how cmdlib determines if it makes sense to wait for
896 3c003d9d Iustin Pop
      # resyncing or not
897 3c003d9d Iustin Pop
      if self.is_in_resync:
898 3c003d9d Iustin Pop
        self.sync_percent = 0
899 3c003d9d Iustin Pop
      else:
900 3c003d9d Iustin Pop
        self.sync_percent = None
901 6b90c22e Iustin Pop
      self.est_time = None
902 6b90c22e Iustin Pop
903 6b90c22e Iustin Pop
904 b459a848 Andrea Spadaccini
class BaseDRBD(BlockDev): # pylint: disable=W0223
905 0f7f32d9 Iustin Pop
  """Base DRBD class.
906 a8083063 Iustin Pop

907 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
908 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
909 0f7f32d9 Iustin Pop

910 abdf0113 Iustin Pop
  """
911 fcee765d Manuel Franceschini
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)(?:\.\d+)?"
912 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
913 9122e60a Iustin Pop
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
914 9122e60a Iustin Pop
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
915 a8083063 Iustin Pop
916 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
917 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
918 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
919 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
920 a8083063 Iustin Pop
921 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
922 549071a0 Luca Bigliardi
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
923 6b90c22e Iustin Pop
924 abdf0113 Iustin Pop
  @staticmethod
925 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
926 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
927 a8083063 Iustin Pop

928 a8083063 Iustin Pop
    """
929 abdf0113 Iustin Pop
    try:
930 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
931 f6eaed12 Iustin Pop
    except EnvironmentError, err:
932 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
933 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
934 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
935 f6eaed12 Iustin Pop
      else:
936 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
937 abdf0113 Iustin Pop
    if not data:
938 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
939 abdf0113 Iustin Pop
    return data
940 a8083063 Iustin Pop
941 9122e60a Iustin Pop
  @classmethod
942 9122e60a Iustin Pop
  def _MassageProcData(cls, data):
943 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
944 a8083063 Iustin Pop

945 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
946 c41eea6e Iustin Pop
        for that minor
947 a8083063 Iustin Pop

948 a8083063 Iustin Pop
    """
949 abdf0113 Iustin Pop
    results = {}
950 abdf0113 Iustin Pop
    old_minor = old_line = None
951 abdf0113 Iustin Pop
    for line in data:
952 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
953 67d101d4 Iustin Pop
        continue
954 9122e60a Iustin Pop
      lresult = cls._VALID_LINE_RE.match(line)
955 abdf0113 Iustin Pop
      if lresult is not None:
956 abdf0113 Iustin Pop
        if old_minor is not None:
957 abdf0113 Iustin Pop
          results[old_minor] = old_line
958 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
959 abdf0113 Iustin Pop
        old_line = line
960 abdf0113 Iustin Pop
      else:
961 abdf0113 Iustin Pop
        if old_minor is not None:
962 abdf0113 Iustin Pop
          old_line += " " + line.strip()
963 abdf0113 Iustin Pop
    # add last line
964 abdf0113 Iustin Pop
    if old_minor is not None:
965 abdf0113 Iustin Pop
      results[old_minor] = old_line
966 abdf0113 Iustin Pop
    return results
967 a8083063 Iustin Pop
968 abdf0113 Iustin Pop
  @classmethod
969 fcee765d Manuel Franceschini
  def _GetVersion(cls, proc_data):
970 abdf0113 Iustin Pop
    """Return the DRBD version.
971 a8083063 Iustin Pop

972 abdf0113 Iustin Pop
    This will return a dict with keys:
973 c41eea6e Iustin Pop
      - k_major
974 c41eea6e Iustin Pop
      - k_minor
975 c41eea6e Iustin Pop
      - k_point
976 c41eea6e Iustin Pop
      - api
977 c41eea6e Iustin Pop
      - proto
978 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
979 a8083063 Iustin Pop

980 a8083063 Iustin Pop
    """
981 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
982 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
983 abdf0113 Iustin Pop
    if not version:
984 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
985 abdf0113 Iustin Pop
                                    first_line)
986 a8083063 Iustin Pop
987 abdf0113 Iustin Pop
    values = version.groups()
988 d0c8c01d Iustin Pop
    retval = {"k_major": int(values[0]),
989 d0c8c01d Iustin Pop
              "k_minor": int(values[1]),
990 d0c8c01d Iustin Pop
              "k_point": int(values[2]),
991 d0c8c01d Iustin Pop
              "api": int(values[3]),
992 d0c8c01d Iustin Pop
              "proto": int(values[4]),
993 abdf0113 Iustin Pop
             }
994 abdf0113 Iustin Pop
    if values[5] is not None:
995 d0c8c01d Iustin Pop
      retval["proto2"] = values[5]
996 a8083063 Iustin Pop
997 abdf0113 Iustin Pop
    return retval
998 abdf0113 Iustin Pop
999 abdf0113 Iustin Pop
  @staticmethod
1000 549071a0 Luca Bigliardi
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
1001 549071a0 Luca Bigliardi
    """Returns DRBD usermode_helper currently set.
1002 549071a0 Luca Bigliardi

1003 549071a0 Luca Bigliardi
    """
1004 549071a0 Luca Bigliardi
    try:
1005 549071a0 Luca Bigliardi
      helper = utils.ReadFile(filename).splitlines()[0]
1006 549071a0 Luca Bigliardi
    except EnvironmentError, err:
1007 549071a0 Luca Bigliardi
      if err.errno == errno.ENOENT:
1008 549071a0 Luca Bigliardi
        _ThrowError("The file %s cannot be opened, check if the module"
1009 549071a0 Luca Bigliardi
                    " is loaded (%s)", filename, str(err))
1010 549071a0 Luca Bigliardi
      else:
1011 549071a0 Luca Bigliardi
        _ThrowError("Can't read DRBD helper file %s: %s", filename, str(err))
1012 549071a0 Luca Bigliardi
    if not helper:
1013 549071a0 Luca Bigliardi
      _ThrowError("Can't read any data from %s", filename)
1014 549071a0 Luca Bigliardi
    return helper
1015 549071a0 Luca Bigliardi
1016 549071a0 Luca Bigliardi
  @staticmethod
1017 abdf0113 Iustin Pop
  def _DevPath(minor):
1018 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
1019 a8083063 Iustin Pop

1020 a8083063 Iustin Pop
    """
1021 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
1022 a8083063 Iustin Pop
1023 abdf0113 Iustin Pop
  @classmethod
1024 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
1025 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
1026 a8083063 Iustin Pop

1027 a8083063 Iustin Pop
    """
1028 abdf0113 Iustin Pop
    data = cls._GetProcData()
1029 a8083063 Iustin Pop
1030 abdf0113 Iustin Pop
    used_devs = {}
1031 abdf0113 Iustin Pop
    for line in data:
1032 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1033 abdf0113 Iustin Pop
      if not match:
1034 abdf0113 Iustin Pop
        continue
1035 abdf0113 Iustin Pop
      minor = int(match.group(1))
1036 abdf0113 Iustin Pop
      state = match.group(2)
1037 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1038 abdf0113 Iustin Pop
        continue
1039 abdf0113 Iustin Pop
      used_devs[minor] = state, line
1040 a8083063 Iustin Pop
1041 abdf0113 Iustin Pop
    return used_devs
1042 a8083063 Iustin Pop
1043 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
1044 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
1045 0834c866 Iustin Pop

1046 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1047 a8083063 Iustin Pop

1048 a8083063 Iustin Pop
    """
1049 abdf0113 Iustin Pop
    if minor is None:
1050 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
1051 cb999543 Iustin Pop
      self.attached = False
1052 a8083063 Iustin Pop
    else:
1053 abdf0113 Iustin Pop
      self.minor = minor
1054 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
1055 cb999543 Iustin Pop
      self.attached = True
1056 a8083063 Iustin Pop
1057 a8083063 Iustin Pop
  @staticmethod
1058 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
1059 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
1060 a8083063 Iustin Pop

1061 3bc145d8 Bernardo Dal Seno
    This currently only checks the size, which must be around
1062 abdf0113 Iustin Pop
    128MiB.
1063 a8083063 Iustin Pop

1064 a8083063 Iustin Pop
    """
1065 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1066 abdf0113 Iustin Pop
    if result.failed:
1067 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
1068 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
1069 a8083063 Iustin Pop
    try:
1070 abdf0113 Iustin Pop
      sectors = int(result.stdout)
1071 691744c4 Iustin Pop
    except (TypeError, ValueError):
1072 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
1073 c04bc777 Iustin Pop
    num_bytes = sectors * 512
1074 c04bc777 Iustin Pop
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
1075 c04bc777 Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (num_bytes / 1024 / 1024))
1076 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
1077 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
1078 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
1079 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
1080 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
1081 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
1082 c04bc777 Iustin Pop
    if num_bytes > 1024 * 1024 * 1024:
1083 c04bc777 Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (num_bytes / 1024 / 1024))
1084 a8083063 Iustin Pop
1085 abdf0113 Iustin Pop
  def Rename(self, new_id):
1086 abdf0113 Iustin Pop
    """Rename a device.
1087 a8083063 Iustin Pop

1088 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1089 a8083063 Iustin Pop

1090 a8083063 Iustin Pop
    """
1091 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1092 a8083063 Iustin Pop
1093 f3e513ad Iustin Pop
1094 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1095 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1096 a2cfdea2 Iustin Pop

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

1101 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1102 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1103 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1104 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1105 a2cfdea2 Iustin Pop

1106 a2cfdea2 Iustin Pop
  """
1107 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1108 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1109 a2cfdea2 Iustin Pop
1110 cf8df3f3 Iustin Pop
  # timeout constants
1111 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
1112 cf8df3f3 Iustin Pop
1113 8a69b3a8 Andrea Spadaccini
  # command line options for barriers
1114 8a69b3a8 Andrea Spadaccini
  _DISABLE_DISK_OPTION = "--no-disk-barrier"  # -a
1115 8a69b3a8 Andrea Spadaccini
  _DISABLE_DRAIN_OPTION = "--no-disk-drain"   # -D
1116 8a69b3a8 Andrea Spadaccini
  _DISABLE_FLUSH_OPTION = "--no-disk-flushes" # -i
1117 8a69b3a8 Andrea Spadaccini
  _DISABLE_META_FLUSH_OPTION = "--no-md-flushes"  # -m
1118 8a69b3a8 Andrea Spadaccini
1119 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
1120 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1121 fc1dc9d7 Iustin Pop
      children = []
1122 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
1123 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1124 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
1125 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1126 310fbb64 Iustin Pop
    (self._lhost, self._lport,
1127 310fbb64 Iustin Pop
     self._rhost, self._rport,
1128 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
1129 310fbb64 Iustin Pop
    if children:
1130 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
1131 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
1132 310fbb64 Iustin Pop
        children = []
1133 94dcbdb0 Andrea Spadaccini
    super(DRBD8, self).__init__(unique_id, children, size, params)
1134 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1135 fcee765d Manuel Franceschini
    version = self._GetVersion(self._GetProcData())
1136 e687ec01 Michael Hanselmann
    if version["k_major"] != 8:
1137 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
1138 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
1139 d0c8c01d Iustin Pop
                  version["k_major"], version["k_minor"])
1140 a2cfdea2 Iustin Pop
1141 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1142 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1143 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1144 ffa1c0dc Iustin Pop
                       (unique_id,))
1145 a2cfdea2 Iustin Pop
    self.Attach()
1146 a2cfdea2 Iustin Pop
1147 a2cfdea2 Iustin Pop
  @classmethod
1148 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1149 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1150 a2cfdea2 Iustin Pop

1151 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1152 a2cfdea2 Iustin Pop

1153 a2cfdea2 Iustin Pop
    """
1154 18e4dee6 Iustin Pop
    # Zero the metadata first, in order to make sure drbdmeta doesn't
1155 18e4dee6 Iustin Pop
    # try to auto-detect existing filesystems or similar (see
1156 18e4dee6 Iustin Pop
    # http://code.google.com/p/ganeti/issues/detail?id=182); we only
1157 18e4dee6 Iustin Pop
    # care about the first 128MB of data in the device, even though it
1158 18e4dee6 Iustin Pop
    # can be bigger
1159 18e4dee6 Iustin Pop
    result = utils.RunCmd([constants.DD_CMD,
1160 18e4dee6 Iustin Pop
                           "if=/dev/zero", "of=%s" % dev_path,
1161 18e4dee6 Iustin Pop
                           "bs=1048576", "count=128", "oflag=direct"])
1162 18e4dee6 Iustin Pop
    if result.failed:
1163 18e4dee6 Iustin Pop
      _ThrowError("Can't wipe the meta device: %s", result.output)
1164 18e4dee6 Iustin Pop
1165 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1166 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1167 a2cfdea2 Iustin Pop
    if result.failed:
1168 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1169 a2cfdea2 Iustin Pop
1170 a2cfdea2 Iustin Pop
  @classmethod
1171 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1172 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1173 a2cfdea2 Iustin Pop

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

1177 a2cfdea2 Iustin Pop
    """
1178 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1179 a2cfdea2 Iustin Pop
1180 a2cfdea2 Iustin Pop
    highest = None
1181 a2cfdea2 Iustin Pop
    for line in data:
1182 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1183 a2cfdea2 Iustin Pop
      if match:
1184 a2cfdea2 Iustin Pop
        return int(match.group(1))
1185 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1186 a2cfdea2 Iustin Pop
      if match:
1187 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1188 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1189 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1190 a2cfdea2 Iustin Pop
      return 0
1191 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1192 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1193 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1194 a2cfdea2 Iustin Pop
    return highest + 1
1195 a2cfdea2 Iustin Pop
1196 a2cfdea2 Iustin Pop
  @classmethod
1197 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1198 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1199 a2cfdea2 Iustin Pop

1200 3bc145d8 Bernardo Dal Seno
    This will either create or return an already-created parser for the
1201 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1202 a2cfdea2 Iustin Pop

1203 a2cfdea2 Iustin Pop
    """
1204 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1205 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1206 a2cfdea2 Iustin Pop
1207 a2cfdea2 Iustin Pop
    # pyparsing setup
1208 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1209 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1210 5a672c30 Manuel Franceschini
    lbracket = pyp.Literal("[").suppress()
1211 5a672c30 Manuel Franceschini
    rbracket = pyp.Literal("]").suppress()
1212 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1213 5a672c30 Manuel Franceschini
    colon = pyp.Literal(":").suppress()
1214 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1215 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1216 a2cfdea2 Iustin Pop
1217 e687ec01 Michael Hanselmann
    comment = pyp.Literal("#") + pyp.Optional(pyp.restOfLine)
1218 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1219 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1220 a2cfdea2 Iustin Pop
1221 3ccb3a64 Michael Hanselmann
    keyword = pyp.Word(pyp.alphanums + "-")
1222 a2cfdea2 Iustin Pop
1223 a2cfdea2 Iustin Pop
    # value types
1224 3ccb3a64 Michael Hanselmann
    value = pyp.Word(pyp.alphanums + "_-/.:")
1225 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1226 5a672c30 Manuel Franceschini
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1227 5a672c30 Manuel Franceschini
                 pyp.Word(pyp.nums + ".") + colon + number)
1228 5a672c30 Manuel Franceschini
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
1229 5a672c30 Manuel Franceschini
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
1230 5a672c30 Manuel Franceschini
                 pyp.Optional(rbracket) + colon + number)
1231 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1232 5a672c30 Manuel Franceschini
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
1233 01e2ce3a Iustin Pop
    # device name, extended syntax
1234 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1235 a2cfdea2 Iustin Pop
1236 a2cfdea2 Iustin Pop
    # a statement
1237 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1238 5a672c30 Manuel Franceschini
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
1239 01e2ce3a Iustin Pop
                         device_value) +
1240 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1241 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1242 a2cfdea2 Iustin Pop
1243 a2cfdea2 Iustin Pop
    # an entire section
1244 d0c8c01d Iustin Pop
    section_name = pyp.Word(pyp.alphas + "_")
1245 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1246 a2cfdea2 Iustin Pop
1247 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1248 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1249 a2cfdea2 Iustin Pop
1250 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1251 a2cfdea2 Iustin Pop
1252 a2cfdea2 Iustin Pop
    return bnf
1253 a2cfdea2 Iustin Pop
1254 a2cfdea2 Iustin Pop
  @classmethod
1255 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1256 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1257 a2cfdea2 Iustin Pop

1258 a2cfdea2 Iustin Pop
    """
1259 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1260 a2cfdea2 Iustin Pop
    if result.failed:
1261 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1262 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1263 3840729d Iustin Pop
      return None
1264 3840729d Iustin Pop
    return result.stdout
1265 3840729d Iustin Pop
1266 3840729d Iustin Pop
  @classmethod
1267 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1268 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1269 3840729d Iustin Pop

1270 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1271 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1272 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1273 3840729d Iustin Pop
    by _GetShowData.
1274 3840729d Iustin Pop

1275 3840729d Iustin Pop
    """
1276 3840729d Iustin Pop
    data = {}
1277 a2cfdea2 Iustin Pop
    if not out:
1278 a2cfdea2 Iustin Pop
      return data
1279 a2cfdea2 Iustin Pop
1280 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1281 a2cfdea2 Iustin Pop
    # run pyparse
1282 a2cfdea2 Iustin Pop
1283 a2cfdea2 Iustin Pop
    try:
1284 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1285 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1286 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1287 a2cfdea2 Iustin Pop
1288 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1289 a2cfdea2 Iustin Pop
    for section in results:
1290 a2cfdea2 Iustin Pop
      sname = section[0]
1291 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1292 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1293 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1294 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1295 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1296 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1297 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1298 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1299 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1300 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1301 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1302 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1303 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1304 a2cfdea2 Iustin Pop
    return data
1305 a2cfdea2 Iustin Pop
1306 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1307 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1308 a2cfdea2 Iustin Pop

1309 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1310 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1311 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1312 a2cfdea2 Iustin Pop
    device.
1313 a2cfdea2 Iustin Pop

1314 a2cfdea2 Iustin Pop
    """
1315 b00b95dd Iustin Pop
    if self._children:
1316 b00b95dd Iustin Pop
      backend, meta = self._children
1317 b00b95dd Iustin Pop
    else:
1318 b00b95dd Iustin Pop
      backend = meta = None
1319 b00b95dd Iustin Pop
1320 a2cfdea2 Iustin Pop
    if backend is not None:
1321 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1322 a2cfdea2 Iustin Pop
    else:
1323 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1324 b00b95dd Iustin Pop
1325 a2cfdea2 Iustin Pop
    if meta is not None:
1326 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1327 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1328 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1329 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1330 a2cfdea2 Iustin Pop
    else:
1331 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1332 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1333 a2cfdea2 Iustin Pop
    return retval
1334 a2cfdea2 Iustin Pop
1335 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1336 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1337 a2cfdea2 Iustin Pop

1338 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1339 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1340 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1341 a2cfdea2 Iustin Pop
    device.
1342 a2cfdea2 Iustin Pop

1343 a2cfdea2 Iustin Pop
    """
1344 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1345 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1346 a2cfdea2 Iustin Pop
      return True
1347 a2cfdea2 Iustin Pop
1348 a2cfdea2 Iustin Pop
    if self._lhost is None:
1349 a2cfdea2 Iustin Pop
      return False
1350 a2cfdea2 Iustin Pop
1351 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1352 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1353 a2cfdea2 Iustin Pop
      return False
1354 a2cfdea2 Iustin Pop
1355 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1356 a2cfdea2 Iustin Pop
    retval = (retval and
1357 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1358 a2cfdea2 Iustin Pop
    return retval
1359 a2cfdea2 Iustin Pop
1360 8a69b3a8 Andrea Spadaccini
  def _AssembleLocal(self, minor, backend, meta, size):
1361 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1362 a2cfdea2 Iustin Pop

1363 a2cfdea2 Iustin Pop
    """
1364 8a69b3a8 Andrea Spadaccini
    args = ["drbdsetup", self._DevPath(minor), "disk",
1365 f069addf Iustin Pop
            backend, meta, "0",
1366 f069addf Iustin Pop
            "-e", "detach",
1367 f069addf Iustin Pop
            "--create-device"]
1368 60bca04a Iustin Pop
    if size:
1369 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1370 8a69b3a8 Andrea Spadaccini
1371 8a69b3a8 Andrea Spadaccini
    version = self._GetVersion(self._GetProcData())
1372 8a69b3a8 Andrea Spadaccini
    vmaj = version["k_major"]
1373 8a69b3a8 Andrea Spadaccini
    vmin = version["k_minor"]
1374 8a69b3a8 Andrea Spadaccini
    vrel = version["k_point"]
1375 8a69b3a8 Andrea Spadaccini
1376 8a69b3a8 Andrea Spadaccini
    barrier_args = \
1377 8a69b3a8 Andrea Spadaccini
      self._ComputeDiskBarrierArgs(vmaj, vmin, vrel,
1378 ac00bf1b Andrea Spadaccini
                                   self.params[constants.LDP_BARRIERS],
1379 ac00bf1b Andrea Spadaccini
                                   self.params[constants.LDP_NO_META_FLUSH])
1380 8a69b3a8 Andrea Spadaccini
    args.extend(barrier_args)
1381 8a69b3a8 Andrea Spadaccini
1382 ad1dd4c7 Andrea Spadaccini
    if self.params[constants.LDP_DISK_CUSTOM]:
1383 ad1dd4c7 Andrea Spadaccini
      args.extend(shlex.split(self.params[constants.LDP_DISK_CUSTOM]))
1384 ad1dd4c7 Andrea Spadaccini
1385 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1386 a2cfdea2 Iustin Pop
    if result.failed:
1387 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1388 a2cfdea2 Iustin Pop
1389 8a69b3a8 Andrea Spadaccini
  @classmethod
1390 8a69b3a8 Andrea Spadaccini
  def _ComputeDiskBarrierArgs(cls, vmaj, vmin, vrel, disabled_barriers,
1391 8a69b3a8 Andrea Spadaccini
      disable_meta_flush):
1392 8a69b3a8 Andrea Spadaccini
    """Compute the DRBD command line parameters for disk barriers
1393 8a69b3a8 Andrea Spadaccini

1394 8a69b3a8 Andrea Spadaccini
    Returns a list of the disk barrier parameters as requested via the
1395 8a69b3a8 Andrea Spadaccini
    disabled_barriers and disable_meta_flush arguments, and according to the
1396 8a69b3a8 Andrea Spadaccini
    supported ones in the DRBD version vmaj.vmin.vrel
1397 8a69b3a8 Andrea Spadaccini

1398 8a69b3a8 Andrea Spadaccini
    If the desired option is unsupported, raises errors.BlockDeviceError.
1399 8a69b3a8 Andrea Spadaccini

1400 8a69b3a8 Andrea Spadaccini
    """
1401 8a69b3a8 Andrea Spadaccini
    disabled_barriers_set = frozenset(disabled_barriers)
1402 8a69b3a8 Andrea Spadaccini
    if not disabled_barriers_set in constants.DRBD_VALID_BARRIER_OPT:
1403 8a69b3a8 Andrea Spadaccini
      raise errors.BlockDeviceError("%s is not a valid option set for DRBD"
1404 8a69b3a8 Andrea Spadaccini
                                    " barriers" % disabled_barriers)
1405 8a69b3a8 Andrea Spadaccini
1406 8a69b3a8 Andrea Spadaccini
    args = []
1407 8a69b3a8 Andrea Spadaccini
1408 8a69b3a8 Andrea Spadaccini
    # The following code assumes DRBD 8.x, with x < 4 and x != 1 (DRBD 8.1.x
1409 8a69b3a8 Andrea Spadaccini
    # does not exist)
1410 8a69b3a8 Andrea Spadaccini
    if not vmaj == 8 and vmin in (0, 2, 3):
1411 8a69b3a8 Andrea Spadaccini
      raise errors.BlockDeviceError("Unsupported DRBD version: %d.%d.%d" %
1412 8a69b3a8 Andrea Spadaccini
                                    (vmaj, vmin, vrel))
1413 8a69b3a8 Andrea Spadaccini
1414 8a69b3a8 Andrea Spadaccini
    def _AppendOrRaise(option, min_version):
1415 8a69b3a8 Andrea Spadaccini
      """Helper for DRBD options"""
1416 8a69b3a8 Andrea Spadaccini
      if min_version is not None and vrel >= min_version:
1417 8a69b3a8 Andrea Spadaccini
        args.append(option)
1418 8a69b3a8 Andrea Spadaccini
      else:
1419 8a69b3a8 Andrea Spadaccini
        raise errors.BlockDeviceError("Could not use the option %s as the"
1420 8a69b3a8 Andrea Spadaccini
                                      " DRBD version %d.%d.%d does not support"
1421 8a69b3a8 Andrea Spadaccini
                                      " it." % (option, vmaj, vmin, vrel))
1422 8a69b3a8 Andrea Spadaccini
1423 8a69b3a8 Andrea Spadaccini
    # the minimum version for each feature is encoded via pairs of (minor
1424 8a69b3a8 Andrea Spadaccini
    # version -> x) where x is version in which support for the option was
1425 8a69b3a8 Andrea Spadaccini
    # introduced.
1426 8a69b3a8 Andrea Spadaccini
    meta_flush_supported = disk_flush_supported = {
1427 8a69b3a8 Andrea Spadaccini
      0: 12,
1428 8a69b3a8 Andrea Spadaccini
      2: 7,
1429 8a69b3a8 Andrea Spadaccini
      3: 0,
1430 8a69b3a8 Andrea Spadaccini
      }
1431 8a69b3a8 Andrea Spadaccini
1432 8a69b3a8 Andrea Spadaccini
    disk_drain_supported = {
1433 8a69b3a8 Andrea Spadaccini
      2: 7,
1434 8a69b3a8 Andrea Spadaccini
      3: 0,
1435 8a69b3a8 Andrea Spadaccini
      }
1436 8a69b3a8 Andrea Spadaccini
1437 8a69b3a8 Andrea Spadaccini
    disk_barriers_supported = {
1438 8a69b3a8 Andrea Spadaccini
      3: 0,
1439 8a69b3a8 Andrea Spadaccini
      }
1440 8a69b3a8 Andrea Spadaccini
1441 8a69b3a8 Andrea Spadaccini
    # meta flushes
1442 8a69b3a8 Andrea Spadaccini
    if disable_meta_flush:
1443 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_META_FLUSH_OPTION,
1444 8a69b3a8 Andrea Spadaccini
                     meta_flush_supported.get(vmin, None))
1445 8a69b3a8 Andrea Spadaccini
1446 8a69b3a8 Andrea Spadaccini
    # disk flushes
1447 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set:
1448 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_FLUSH_OPTION,
1449 8a69b3a8 Andrea Spadaccini
                     disk_flush_supported.get(vmin, None))
1450 8a69b3a8 Andrea Spadaccini
1451 8a69b3a8 Andrea Spadaccini
    # disk drain
1452 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set:
1453 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_DRAIN_OPTION,
1454 8a69b3a8 Andrea Spadaccini
                     disk_drain_supported.get(vmin, None))
1455 8a69b3a8 Andrea Spadaccini
1456 8a69b3a8 Andrea Spadaccini
    # disk barriers
1457 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set:
1458 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_DISK_OPTION,
1459 8a69b3a8 Andrea Spadaccini
                     disk_barriers_supported.get(vmin, None))
1460 8a69b3a8 Andrea Spadaccini
1461 8a69b3a8 Andrea Spadaccini
    return args
1462 8a69b3a8 Andrea Spadaccini
1463 6e9814a1 Andrea Spadaccini
  def _AssembleNet(self, minor, net_info, protocol,
1464 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1465 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1466 a2cfdea2 Iustin Pop

1467 a2cfdea2 Iustin Pop
    """
1468 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1469 52857176 Iustin Pop
    if None in net_info:
1470 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1471 52857176 Iustin Pop
      # sure its shutdown
1472 6e9814a1 Andrea Spadaccini
      self._ShutdownNet(minor)
1473 1063abd1 Iustin Pop
      return
1474 52857176 Iustin Pop
1475 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1476 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1477 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1478 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1479 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1480 7d585316 Iustin Pop
    # about its peer.
1481 8584e922 Andrea Spadaccini
    sync_errors = self._SetMinorSyncParams(minor, self.params)
1482 8584e922 Andrea Spadaccini
    if sync_errors:
1483 8584e922 Andrea Spadaccini
      _ThrowError("drbd%d: can't set the synchronization parameters: %s" %
1484 8584e922 Andrea Spadaccini
                  (minor, utils.CommaJoin(sync_errors)))
1485 7d585316 Iustin Pop
1486 8b312c1d Manuel Franceschini
    if netutils.IP6Address.IsValid(lhost):
1487 8b312c1d Manuel Franceschini
      if not netutils.IP6Address.IsValid(rhost):
1488 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1489 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1490 5a672c30 Manuel Franceschini
      family = "ipv6"
1491 8b312c1d Manuel Franceschini
    elif netutils.IP4Address.IsValid(lhost):
1492 8b312c1d Manuel Franceschini
      if not netutils.IP4Address.IsValid(rhost):
1493 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1494 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1495 5a672c30 Manuel Franceschini
      family = "ipv4"
1496 5a672c30 Manuel Franceschini
    else:
1497 5a672c30 Manuel Franceschini
      _ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
1498 5a672c30 Manuel Franceschini
1499 6e9814a1 Andrea Spadaccini
    args = ["drbdsetup", self._DevPath(minor), "net",
1500 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, lhost, lport),
1501 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, rhost, rport), protocol,
1502 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1503 f38478b2 Iustin Pop
            "-B", "consensus",
1504 ab6cc81c Iustin Pop
            "--create-device",
1505 f38478b2 Iustin Pop
            ]
1506 a2cfdea2 Iustin Pop
    if dual_pri:
1507 a2cfdea2 Iustin Pop
      args.append("-m")
1508 a2cfdea2 Iustin Pop
    if hmac and secret:
1509 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1510 ad1dd4c7 Andrea Spadaccini
1511 ad1dd4c7 Andrea Spadaccini
    if self.params[constants.LDP_NET_CUSTOM]:
1512 ad1dd4c7 Andrea Spadaccini
      args.extend(shlex.split(self.params[constants.LDP_NET_CUSTOM]))
1513 ad1dd4c7 Andrea Spadaccini
1514 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1515 a2cfdea2 Iustin Pop
    if result.failed:
1516 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1517 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1518 a2cfdea2 Iustin Pop
1519 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1520 6e9814a1 Andrea Spadaccini
      info = self._GetDevInfo(self._GetShowData(minor))
1521 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1522 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1523 def8e2f6 Michael Hanselmann
1524 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1525 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1526 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1527 def8e2f6 Michael Hanselmann
1528 def8e2f6 Michael Hanselmann
    try:
1529 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1530 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1531 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1532 a2cfdea2 Iustin Pop
1533 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1534 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1535 b00b95dd Iustin Pop

1536 b00b95dd Iustin Pop
    """
1537 b00b95dd Iustin Pop
    if self.minor is None:
1538 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1539 82463074 Iustin Pop
                  self._aminor)
1540 b00b95dd Iustin Pop
    if len(devices) != 2:
1541 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1542 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1543 03ece5f3 Iustin Pop
    if "local_dev" in info:
1544 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1545 b00b95dd Iustin Pop
    backend, meta = devices
1546 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1547 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1548 b00b95dd Iustin Pop
    backend.Open()
1549 b00b95dd Iustin Pop
    meta.Open()
1550 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1551 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1552 b00b95dd Iustin Pop
1553 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1554 b00b95dd Iustin Pop
    self._children = devices
1555 b00b95dd Iustin Pop
1556 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1557 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1558 b00b95dd Iustin Pop

1559 b00b95dd Iustin Pop
    """
1560 b00b95dd Iustin Pop
    if self.minor is None:
1561 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1562 82463074 Iustin Pop
                  self._aminor)
1563 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1564 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1565 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1566 03ece5f3 Iustin Pop
      return
1567 b00b95dd Iustin Pop
    if len(self._children) != 2:
1568 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1569 82463074 Iustin Pop
                  self._children)
1570 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1571 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1572 e739bd57 Iustin Pop
      return
1573 b00b95dd Iustin Pop
    if len(devices) != 2:
1574 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1575 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1576 e739bd57 Iustin Pop
      if dev != child.dev_path:
1577 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1578 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1579 b00b95dd Iustin Pop
1580 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1581 b00b95dd Iustin Pop
    self._children = []
1582 b00b95dd Iustin Pop
1583 7d585316 Iustin Pop
  @classmethod
1584 f2f57b6e Andrea Spadaccini
  def _SetMinorSyncParams(cls, minor, params):
1585 f2f57b6e Andrea Spadaccini
    """Set the parameters of the DRBD syncer.
1586 a2cfdea2 Iustin Pop

1587 7d585316 Iustin Pop
    This is the low-level implementation.
1588 7d585316 Iustin Pop

1589 7d585316 Iustin Pop
    @type minor: int
1590 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1591 f2f57b6e Andrea Spadaccini
    @type params: dict
1592 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1593 8584e922 Andrea Spadaccini
    @rtype: list
1594 8584e922 Andrea Spadaccini
    @return: a list of error messages
1595 7d585316 Iustin Pop

1596 a2cfdea2 Iustin Pop
    """
1597 f2f57b6e Andrea Spadaccini
1598 f2f57b6e Andrea Spadaccini
    args = ["drbdsetup", cls._DevPath(minor), "syncer"]
1599 f2f57b6e Andrea Spadaccini
    if params[constants.LDP_DYNAMIC_RESYNC]:
1600 f2f57b6e Andrea Spadaccini
      version = cls._GetVersion(cls._GetProcData())
1601 f2f57b6e Andrea Spadaccini
      vmin = version["k_minor"]
1602 f2f57b6e Andrea Spadaccini
      vrel = version["k_point"]
1603 f2f57b6e Andrea Spadaccini
1604 f2f57b6e Andrea Spadaccini
      # By definition we are using 8.x, so just check the rest of the version
1605 f2f57b6e Andrea Spadaccini
      # number
1606 f2f57b6e Andrea Spadaccini
      if vmin != 3 or vrel < 9:
1607 8584e922 Andrea Spadaccini
        msg = ("The current DRBD version (8.%d.%d) does not support the "
1608 8584e922 Andrea Spadaccini
               "dynamic resync speed controller" % (vmin, vrel))
1609 8584e922 Andrea Spadaccini
        logging.error(msg)
1610 8584e922 Andrea Spadaccini
        return [msg]
1611 8584e922 Andrea Spadaccini
1612 8584e922 Andrea Spadaccini
      if params[constants.LDP_PLAN_AHEAD] == 0:
1613 8584e922 Andrea Spadaccini
        msg = ("A value of 0 for c-plan-ahead disables the dynamic sync speed"
1614 8584e922 Andrea Spadaccini
               " controller at DRBD level. If you want to disable it, please"
1615 8584e922 Andrea Spadaccini
               " set the dynamic-resync disk parameter to False.")
1616 8584e922 Andrea Spadaccini
        logging.error(msg)
1617 8584e922 Andrea Spadaccini
        return [msg]
1618 f2f57b6e Andrea Spadaccini
1619 f2f57b6e Andrea Spadaccini
      # add the c-* parameters to args
1620 8584e922 Andrea Spadaccini
      args.extend(["--c-plan-ahead", params[constants.LDP_PLAN_AHEAD],
1621 8584e922 Andrea Spadaccini
                   "--c-fill-target", params[constants.LDP_FILL_TARGET],
1622 8584e922 Andrea Spadaccini
                   "--c-delay-target", params[constants.LDP_DELAY_TARGET],
1623 8584e922 Andrea Spadaccini
                   "--c-max-rate", params[constants.LDP_MAX_RATE],
1624 8584e922 Andrea Spadaccini
                   "--c-min-rate", params[constants.LDP_MIN_RATE],
1625 8584e922 Andrea Spadaccini
                  ])
1626 f2f57b6e Andrea Spadaccini
1627 f2f57b6e Andrea Spadaccini
    else:
1628 f2f57b6e Andrea Spadaccini
      args.extend(["-r", "%d" % params[constants.LDP_RESYNC_RATE]])
1629 f2f57b6e Andrea Spadaccini
1630 f2f57b6e Andrea Spadaccini
    args.append("--create-device")
1631 f2f57b6e Andrea Spadaccini
    result = utils.RunCmd(args)
1632 a2cfdea2 Iustin Pop
    if result.failed:
1633 8584e922 Andrea Spadaccini
      msg = ("Can't change syncer rate: %s - %s" %
1634 8584e922 Andrea Spadaccini
             (result.fail_reason, result.output))
1635 8584e922 Andrea Spadaccini
      logging.error(msg)
1636 e0b57a5c Michael Hanselmann
      return [msg]
1637 8584e922 Andrea Spadaccini
1638 8584e922 Andrea Spadaccini
    return []
1639 7d585316 Iustin Pop
1640 f2f57b6e Andrea Spadaccini
  def SetSyncParams(self, params):
1641 f2f57b6e Andrea Spadaccini
    """Set the synchronization parameters of the DRBD syncer.
1642 7d585316 Iustin Pop

1643 f2f57b6e Andrea Spadaccini
    @type params: dict
1644 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1645 8584e922 Andrea Spadaccini
    @rtype: list
1646 8584e922 Andrea Spadaccini
    @return: a list of error messages, emitted both by the current node and by
1647 8584e922 Andrea Spadaccini
    children. An empty list means no errors
1648 7d585316 Iustin Pop

1649 7d585316 Iustin Pop
    """
1650 7d585316 Iustin Pop
    if self.minor is None:
1651 8584e922 Andrea Spadaccini
      err = "Not attached during SetSyncParams"
1652 8584e922 Andrea Spadaccini
      logging.info(err)
1653 8584e922 Andrea Spadaccini
      return [err]
1654 8584e922 Andrea Spadaccini
1655 f2f57b6e Andrea Spadaccini
    children_result = super(DRBD8, self).SetSyncParams(params)
1656 8584e922 Andrea Spadaccini
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
1657 8584e922 Andrea Spadaccini
    return children_result
1658 a2cfdea2 Iustin Pop
1659 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
1660 a3fffcc6 René Nussbaumer
    """Pauses or resumes the sync of a DRBD device.
1661 a3fffcc6 René Nussbaumer

1662 a3fffcc6 René Nussbaumer
    @param pause: Wether to pause or resume
1663 a3fffcc6 René Nussbaumer
    @return: the success of the operation
1664 a3fffcc6 René Nussbaumer

1665 a3fffcc6 René Nussbaumer
    """
1666 a3fffcc6 René Nussbaumer
    if self.minor is None:
1667 a3fffcc6 René Nussbaumer
      logging.info("Not attached during PauseSync")
1668 a3fffcc6 René Nussbaumer
      return False
1669 a3fffcc6 René Nussbaumer
1670 a3fffcc6 René Nussbaumer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
1671 a3fffcc6 René Nussbaumer
1672 a3fffcc6 René Nussbaumer
    if pause:
1673 a3fffcc6 René Nussbaumer
      cmd = "pause-sync"
1674 a3fffcc6 René Nussbaumer
    else:
1675 a3fffcc6 René Nussbaumer
      cmd = "resume-sync"
1676 a3fffcc6 René Nussbaumer
1677 a3fffcc6 René Nussbaumer
    result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
1678 a3fffcc6 René Nussbaumer
    if result.failed:
1679 a3fffcc6 René Nussbaumer
      logging.error("Can't %s: %s - %s", cmd,
1680 a3fffcc6 René Nussbaumer
                    result.fail_reason, result.output)
1681 a3fffcc6 René Nussbaumer
    return not result.failed and children_result
1682 a3fffcc6 René Nussbaumer
1683 6b90c22e Iustin Pop
  def GetProcStatus(self):
1684 6b90c22e Iustin Pop
    """Return device data from /proc.
1685 6b90c22e Iustin Pop

1686 6b90c22e Iustin Pop
    """
1687 6b90c22e Iustin Pop
    if self.minor is None:
1688 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1689 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1690 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1691 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1692 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1693 6b90c22e Iustin Pop
1694 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1695 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1696 a2cfdea2 Iustin Pop

1697 a2cfdea2 Iustin Pop

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

1702 0834c866 Iustin Pop

1703 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1704 0834c866 Iustin Pop
    network not connected or local disk missing.
1705 0834c866 Iustin Pop

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

1709 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1710 c41eea6e Iustin Pop

1711 a2cfdea2 Iustin Pop
    """
1712 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1713 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1714 96acbc09 Michael Hanselmann
1715 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1716 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1717 f208978a Michael Hanselmann
1718 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1719 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1720 f208978a Michael Hanselmann
    elif stats.is_diskless:
1721 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1722 f208978a Michael Hanselmann
    else:
1723 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1724 96acbc09 Michael Hanselmann
1725 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1726 96acbc09 Michael Hanselmann
                                  major=self.major,
1727 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1728 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1729 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1730 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1731 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1732 a2cfdea2 Iustin Pop
1733 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1734 a2cfdea2 Iustin Pop
    """Make the local state primary.
1735 a2cfdea2 Iustin Pop

1736 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1737 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1738 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1739 f860ff4e Guido Trotter
    is mandatory.
1740 a2cfdea2 Iustin Pop

1741 a2cfdea2 Iustin Pop
    """
1742 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1743 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1744 a2cfdea2 Iustin Pop
      return False
1745 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1746 a2cfdea2 Iustin Pop
    if force:
1747 a2cfdea2 Iustin Pop
      cmd.append("-o")
1748 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1749 a2cfdea2 Iustin Pop
    if result.failed:
1750 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1751 82463074 Iustin Pop
                  result.output)
1752 a2cfdea2 Iustin Pop
1753 a2cfdea2 Iustin Pop
  def Close(self):
1754 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1755 a2cfdea2 Iustin Pop

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

1758 a2cfdea2 Iustin Pop
    """
1759 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1760 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1761 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1762 a2cfdea2 Iustin Pop
    if result.failed:
1763 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1764 82463074 Iustin Pop
                  self.minor, result.output)
1765 a2cfdea2 Iustin Pop
1766 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1767 cf8df3f3 Iustin Pop
    """Removes network configuration.
1768 cf8df3f3 Iustin Pop

1769 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1770 cf8df3f3 Iustin Pop

1771 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1772 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1773 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1774 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1775 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1776 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1777 cf8df3f3 Iustin Pop
    exception.
1778 cf8df3f3 Iustin Pop

1779 cf8df3f3 Iustin Pop
    """
1780 cf8df3f3 Iustin Pop
    if self.minor is None:
1781 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1782 cf8df3f3 Iustin Pop
1783 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1784 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1785 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1786 cf8df3f3 Iustin Pop
1787 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1788 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1789 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1790 cf8df3f3 Iustin Pop
1791 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1792 def8e2f6 Michael Hanselmann
1793 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1794 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1795 def8e2f6 Michael Hanselmann
        return
1796 def8e2f6 Michael Hanselmann
1797 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1798 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1799 def8e2f6 Michael Hanselmann
      # forgotten
1800 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1801 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1802 def8e2f6 Michael Hanselmann
1803 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1804 def8e2f6 Michael Hanselmann
1805 def8e2f6 Michael Hanselmann
    # Keep start time
1806 def8e2f6 Michael Hanselmann
    start_time = time.time()
1807 def8e2f6 Michael Hanselmann
1808 def8e2f6 Michael Hanselmann
    try:
1809 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1810 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1811 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1812 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1813 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1814 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1815 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1816 cf8df3f3 Iustin Pop
      else:
1817 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1818 def8e2f6 Michael Hanselmann
1819 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1820 cf8df3f3 Iustin Pop
1821 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1822 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1823 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1824 82463074 Iustin Pop
                   self.minor, reconfig_time)
1825 cf8df3f3 Iustin Pop
1826 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1827 cf8df3f3 Iustin Pop
    """Reconnects the network.
1828 cf8df3f3 Iustin Pop

1829 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1830 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1831 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1832 cf8df3f3 Iustin Pop

1833 cf8df3f3 Iustin Pop
    Args:
1834 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1835 cf8df3f3 Iustin Pop

1836 cf8df3f3 Iustin Pop
    """
1837 cf8df3f3 Iustin Pop
    if self.minor is None:
1838 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1839 cf8df3f3 Iustin Pop
1840 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1841 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1842 cf8df3f3 Iustin Pop
1843 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1844 cf8df3f3 Iustin Pop
1845 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1846 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1847 cf8df3f3 Iustin Pop
1848 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1849 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1850 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1851 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1852 cf8df3f3 Iustin Pop
1853 a2cfdea2 Iustin Pop
  def Attach(self):
1854 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1855 2d0c8319 Iustin Pop

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

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

1863 2d0c8319 Iustin Pop
    """
1864 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1865 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1866 2d0c8319 Iustin Pop
      minor = self._aminor
1867 2d0c8319 Iustin Pop
    else:
1868 2d0c8319 Iustin Pop
      minor = None
1869 2d0c8319 Iustin Pop
1870 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1871 2d0c8319 Iustin Pop
    return minor is not None
1872 2d0c8319 Iustin Pop
1873 2d0c8319 Iustin Pop
  def Assemble(self):
1874 2d0c8319 Iustin Pop
    """Assemble the drbd.
1875 2d0c8319 Iustin Pop

1876 2d0c8319 Iustin Pop
    Method:
1877 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1878 2d0c8319 Iustin Pop
        our config
1879 2d0c8319 Iustin Pop
      - if not, we create it from zero
1880 d529599f Andrea Spadaccini
      - anyway, set the device parameters
1881 2d0c8319 Iustin Pop

1882 2d0c8319 Iustin Pop
    """
1883 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1884 2d0c8319 Iustin Pop
1885 2d0c8319 Iustin Pop
    self.Attach()
1886 2d0c8319 Iustin Pop
    if self.minor is None:
1887 2d0c8319 Iustin Pop
      # local device completely unconfigured
1888 1063abd1 Iustin Pop
      self._FastAssemble()
1889 2d0c8319 Iustin Pop
    else:
1890 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1891 2d0c8319 Iustin Pop
      # the device
1892 1063abd1 Iustin Pop
      self._SlowAssemble()
1893 2d0c8319 Iustin Pop
1894 8584e922 Andrea Spadaccini
    sync_errors = self.SetSyncParams(self.params)
1895 8584e922 Andrea Spadaccini
    if sync_errors:
1896 8584e922 Andrea Spadaccini
      _ThrowError("drbd%d: can't set the synchronization parameters: %s" %
1897 8584e922 Andrea Spadaccini
                  (self.minor, utils.CommaJoin(sync_errors)))
1898 d529599f Andrea Spadaccini
1899 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1900 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1901 a2cfdea2 Iustin Pop

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

1906 a2cfdea2 Iustin Pop
    """
1907 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
1908 b459a848 Andrea Spadaccini
    # pylint: disable=W0631
1909 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1910 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1911 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1912 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1913 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1914 1063abd1 Iustin Pop
1915 a2cfdea2 Iustin Pop
      if match_l and match_r:
1916 1063abd1 Iustin Pop
        # everything matches
1917 a2cfdea2 Iustin Pop
        break
1918 1063abd1 Iustin Pop
1919 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1920 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1921 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1922 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1923 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1924 1063abd1 Iustin Pop
          break
1925 1063abd1 Iustin Pop
        else:
1926 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1927 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1928 1063abd1 Iustin Pop
1929 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1930 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1931 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1932 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1933 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1934 1063abd1 Iustin Pop
          break
1935 1063abd1 Iustin Pop
        else:
1936 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1937 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1938 bf25af3b Iustin Pop
1939 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1940 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1941 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1942 bf25af3b Iustin Pop
      # configuration
1943 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1944 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1945 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1946 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1947 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1948 9cdbe77f Iustin Pop
        # reconnect to our correct one
1949 1063abd1 Iustin Pop
        try:
1950 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1951 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1952 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1953 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1954 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1955 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1956 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1957 9cdbe77f Iustin Pop
        # None)
1958 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1959 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1960 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1961 9cdbe77f Iustin Pop
          break
1962 1063abd1 Iustin Pop
        else:
1963 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1964 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1965 9cdbe77f Iustin Pop
1966 a2cfdea2 Iustin Pop
    else:
1967 a2cfdea2 Iustin Pop
      minor = None
1968 a2cfdea2 Iustin Pop
1969 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1970 1063abd1 Iustin Pop
    if minor is None:
1971 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1972 1063abd1 Iustin Pop
                  self._aminor)
1973 a2cfdea2 Iustin Pop
1974 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1975 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1976 a2cfdea2 Iustin Pop

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

1979 a2cfdea2 Iustin Pop
    """
1980 a1578d63 Iustin Pop
    minor = self._aminor
1981 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1982 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1983 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1984 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1985 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1986 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1987 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1988 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1989 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1990 a2cfdea2 Iustin Pop
1991 a2cfdea2 Iustin Pop
  @classmethod
1992 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1993 b00b95dd Iustin Pop
    """Detach from the local device.
1994 b00b95dd Iustin Pop

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

1998 b00b95dd Iustin Pop
    """
1999 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2000 b00b95dd Iustin Pop
    if result.failed:
2001 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
2002 b00b95dd Iustin Pop
2003 b00b95dd Iustin Pop
  @classmethod
2004 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2005 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2006 f3e513ad Iustin Pop

2007 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2008 f3e513ad Iustin Pop

2009 f3e513ad Iustin Pop
    """
2010 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2011 a8459f1c Iustin Pop
    if result.failed:
2012 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
2013 f3e513ad Iustin Pop
2014 f3e513ad Iustin Pop
  @classmethod
2015 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2016 a2cfdea2 Iustin Pop
    """Deactivate the device.
2017 a2cfdea2 Iustin Pop

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

2020 a2cfdea2 Iustin Pop
    """
2021 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2022 a2cfdea2 Iustin Pop
    if result.failed:
2023 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
2024 33bc6f01 Iustin Pop
                  minor, result.output)
2025 a2cfdea2 Iustin Pop
2026 a2cfdea2 Iustin Pop
  def Shutdown(self):
2027 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2028 a2cfdea2 Iustin Pop

2029 a2cfdea2 Iustin Pop
    """
2030 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2031 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
2032 746f7476 Iustin Pop
      return
2033 746f7476 Iustin Pop
    minor = self.minor
2034 a2cfdea2 Iustin Pop
    self.minor = None
2035 a2cfdea2 Iustin Pop
    self.dev_path = None
2036 746f7476 Iustin Pop
    self._ShutdownAll(minor)
2037 a2cfdea2 Iustin Pop
2038 a2cfdea2 Iustin Pop
  def Remove(self):
2039 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2040 a2cfdea2 Iustin Pop

2041 a2cfdea2 Iustin Pop
    """
2042 0c6c04ec Iustin Pop
    self.Shutdown()
2043 a2cfdea2 Iustin Pop
2044 a2cfdea2 Iustin Pop
  @classmethod
2045 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2046 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2047 a2cfdea2 Iustin Pop

2048 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2049 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2050 a2cfdea2 Iustin Pop

2051 a2cfdea2 Iustin Pop
    """
2052 a2cfdea2 Iustin Pop
    if len(children) != 2:
2053 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2054 767d52d3 Iustin Pop
    # check that the minor is unused
2055 767d52d3 Iustin Pop
    aminor = unique_id[4]
2056 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
2057 767d52d3 Iustin Pop
    if aminor in proc_info:
2058 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
2059 767d52d3 Iustin Pop
      in_use = status.is_in_use
2060 767d52d3 Iustin Pop
    else:
2061 767d52d3 Iustin Pop
      in_use = False
2062 767d52d3 Iustin Pop
    if in_use:
2063 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
2064 a2cfdea2 Iustin Pop
    meta = children[1]
2065 a2cfdea2 Iustin Pop
    meta.Assemble()
2066 a2cfdea2 Iustin Pop
    if not meta.Attach():
2067 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
2068 33bc6f01 Iustin Pop
                  aminor, meta)
2069 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
2070 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
2071 94dcbdb0 Andrea Spadaccini
    return cls(unique_id, children, size, params)
2072 a2cfdea2 Iustin Pop
2073 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
2074 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
2075 1005d816 Iustin Pop

2076 1005d816 Iustin Pop
    """
2077 1005d816 Iustin Pop
    if self.minor is None:
2078 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
2079 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
2080 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
2081 7fe23d47 Iustin Pop
    self._children[0].Grow(amount, dryrun)
2082 7fe23d47 Iustin Pop
    if dryrun:
2083 7fe23d47 Iustin Pop
      # DRBD does not support dry-run mode, so we'll return here
2084 7fe23d47 Iustin Pop
      return
2085 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
2086 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
2087 1005d816 Iustin Pop
    if result.failed:
2088 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
2089 1005d816 Iustin Pop
2090 a8083063 Iustin Pop
2091 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
2092 6f695a2e Manuel Franceschini
  """File device.
2093 abdf0113 Iustin Pop

2094 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
2095 6f695a2e Manuel Franceschini

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

2098 6f695a2e Manuel Franceschini
  """
2099 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2100 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
2101 6f695a2e Manuel Franceschini

2102 6f695a2e Manuel Franceschini
    """
2103 6f695a2e Manuel Franceschini
    if children:
2104 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
2105 94dcbdb0 Andrea Spadaccini
    super(FileStorage, self).__init__(unique_id, children, size, params)
2106 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2107 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2108 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
2109 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
2110 ecb091e3 Iustin Pop
    self.Attach()
2111 6f695a2e Manuel Franceschini
2112 6f695a2e Manuel Franceschini
  def Assemble(self):
2113 6f695a2e Manuel Franceschini
    """Assemble the device.
2114 6f695a2e Manuel Franceschini

2115 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
2116 6f695a2e Manuel Franceschini

2117 6f695a2e Manuel Franceschini
    """
2118 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
2119 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
2120 6f695a2e Manuel Franceschini
2121 6f695a2e Manuel Franceschini
  def Shutdown(self):
2122 6f695a2e Manuel Franceschini
    """Shutdown the device.
2123 6f695a2e Manuel Franceschini

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

2127 6f695a2e Manuel Franceschini
    """
2128 746f7476 Iustin Pop
    pass
2129 6f695a2e Manuel Franceschini
2130 6f695a2e Manuel Franceschini
  def Open(self, force=False):
2131 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
2132 6f695a2e Manuel Franceschini

2133 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2134 6f695a2e Manuel Franceschini

2135 6f695a2e Manuel Franceschini
    """
2136 6f695a2e Manuel Franceschini
    pass
2137 6f695a2e Manuel Franceschini
2138 6f695a2e Manuel Franceschini
  def Close(self):
2139 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
2140 6f695a2e Manuel Franceschini

2141 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2142 6f695a2e Manuel Franceschini

2143 6f695a2e Manuel Franceschini
    """
2144 6f695a2e Manuel Franceschini
    pass
2145 6f695a2e Manuel Franceschini
2146 6f695a2e Manuel Franceschini
  def Remove(self):
2147 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
2148 6f695a2e Manuel Franceschini

2149 c41eea6e Iustin Pop
    @rtype: boolean
2150 c41eea6e Iustin Pop
    @return: True if the removal was successful
2151 6f695a2e Manuel Franceschini

2152 6f695a2e Manuel Franceschini
    """
2153 6f695a2e Manuel Franceschini
    try:
2154 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
2155 6f695a2e Manuel Franceschini
    except OSError, err:
2156 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
2157 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
2158 6f695a2e Manuel Franceschini
2159 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
2160 bbe4cc16 Iustin Pop
    """Renames the file.
2161 bbe4cc16 Iustin Pop

2162 bbe4cc16 Iustin Pop
    """
2163 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
2164 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
2165 bbe4cc16 Iustin Pop
2166 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
2167 bbe4cc16 Iustin Pop
    """Grow the file
2168 bbe4cc16 Iustin Pop

2169 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2170 bbe4cc16 Iustin Pop

2171 bbe4cc16 Iustin Pop
    """
2172 91e2d9ec Guido Trotter
    # Check that the file exists
2173 91e2d9ec Guido Trotter
    self.Assemble()
2174 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
2175 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
2176 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
2177 7fe23d47 Iustin Pop
    # We can't really simulate the growth
2178 7fe23d47 Iustin Pop
    if dryrun:
2179 7fe23d47 Iustin Pop
      return
2180 91e2d9ec Guido Trotter
    try:
2181 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
2182 91e2d9ec Guido Trotter
      f.truncate(new_size)
2183 91e2d9ec Guido Trotter
      f.close()
2184 91e2d9ec Guido Trotter
    except EnvironmentError, err:
2185 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
2186 bbe4cc16 Iustin Pop
2187 6f695a2e Manuel Franceschini
  def Attach(self):
2188 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2189 6f695a2e Manuel Franceschini

2190 6f695a2e Manuel Franceschini
    Check if this file already exists.
2191 6f695a2e Manuel Franceschini

2192 c41eea6e Iustin Pop
    @rtype: boolean
2193 c41eea6e Iustin Pop
    @return: True if file exists
2194 6f695a2e Manuel Franceschini

2195 6f695a2e Manuel Franceschini
    """
2196 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
2197 ecb091e3 Iustin Pop
    return self.attached
2198 6f695a2e Manuel Franceschini
2199 fcff3897 Iustin Pop
  def GetActualSize(self):
2200 fcff3897 Iustin Pop
    """Return the actual disk size.
2201 fcff3897 Iustin Pop

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

2204 fcff3897 Iustin Pop
    """
2205 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2206 fcff3897 Iustin Pop
    try:
2207 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2208 fcff3897 Iustin Pop
      return st.st_size
2209 fcff3897 Iustin Pop
    except OSError, err:
2210 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2211 fcff3897 Iustin Pop
2212 6f695a2e Manuel Franceschini
  @classmethod
2213 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2214 6f695a2e Manuel Franceschini
    """Create a new file.
2215 6f695a2e Manuel Franceschini

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

2218 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2219 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2220 6f695a2e Manuel Franceschini

2221 6f695a2e Manuel Franceschini
    """
2222 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2223 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2224 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2225 6f695a2e Manuel Franceschini
    try:
2226 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
2227 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
2228 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2229 6f695a2e Manuel Franceschini
      f.close()
2230 cdeefd9b Guido Trotter
    except EnvironmentError, err:
2231 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
2232 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
2233 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
2234 6f695a2e Manuel Franceschini
2235 94dcbdb0 Andrea Spadaccini
    return FileStorage(unique_id, children, size, params)
2236 6f695a2e Manuel Franceschini
2237 6f695a2e Manuel Franceschini
2238 b6135bbc Apollon Oikonomopoulos
class PersistentBlockDevice(BlockDev):
2239 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
2240 b6135bbc Apollon Oikonomopoulos

2241 b6135bbc Apollon Oikonomopoulos
  May be either directly attached, or exposed through DM (e.g. dm-multipath).
2242 b6135bbc Apollon Oikonomopoulos
  udev helpers are probably required to give persistent, human-friendly
2243 b6135bbc Apollon Oikonomopoulos
  names.
2244 b6135bbc Apollon Oikonomopoulos

2245 b6135bbc Apollon Oikonomopoulos
  For the time being, pathnames are required to lie under /dev.
2246 b6135bbc Apollon Oikonomopoulos

2247 b6135bbc Apollon Oikonomopoulos
  """
2248 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2249 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
2250 b6135bbc Apollon Oikonomopoulos

2251 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2252 b6135bbc Apollon Oikonomopoulos

2253 b6135bbc Apollon Oikonomopoulos
    """
2254 94dcbdb0 Andrea Spadaccini
    super(PersistentBlockDevice, self).__init__(unique_id, children, size,
2255 94dcbdb0 Andrea Spadaccini
                                                params)
2256 b6135bbc Apollon Oikonomopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2257 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2258 b6135bbc Apollon Oikonomopoulos
    self.dev_path = unique_id[1]
2259 d0c8c01d Iustin Pop
    if not os.path.realpath(self.dev_path).startswith("/dev/"):
2260 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Full path '%s' lies outside /dev" %
2261 b6135bbc Apollon Oikonomopoulos
                              os.path.realpath(self.dev_path))
2262 b6135bbc Apollon Oikonomopoulos
    # TODO: this is just a safety guard checking that we only deal with devices
2263 b6135bbc Apollon Oikonomopoulos
    # we know how to handle. In the future this will be integrated with
2264 b6135bbc Apollon Oikonomopoulos
    # external storage backends and possible values will probably be collected
2265 b6135bbc Apollon Oikonomopoulos
    # from the cluster configuration.
2266 b6135bbc Apollon Oikonomopoulos
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
2267 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Got persistent block device of invalid type: %s" %
2268 b6135bbc Apollon Oikonomopoulos
                       unique_id[0])
2269 b6135bbc Apollon Oikonomopoulos
2270 b6135bbc Apollon Oikonomopoulos
    self.major = self.minor = None
2271 b6135bbc Apollon Oikonomopoulos
    self.Attach()
2272 b6135bbc Apollon Oikonomopoulos
2273 b6135bbc Apollon Oikonomopoulos
  @classmethod
2274 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2275 b6135bbc Apollon Oikonomopoulos
    """Create a new device
2276 b6135bbc Apollon Oikonomopoulos

2277 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2278 b6135bbc Apollon Oikonomopoulos

2279 b6135bbc Apollon Oikonomopoulos
    """
2280 94dcbdb0 Andrea Spadaccini
    return PersistentBlockDevice(unique_id, children, 0, params)
2281 b6135bbc Apollon Oikonomopoulos
2282 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
2283 b6135bbc Apollon Oikonomopoulos
    """Remove a device
2284 b6135bbc Apollon Oikonomopoulos

2285 b6135bbc Apollon Oikonomopoulos
    This is a noop
2286 b6135bbc Apollon Oikonomopoulos

2287 b6135bbc Apollon Oikonomopoulos
    """
2288 b6135bbc Apollon Oikonomopoulos
    pass
2289 b6135bbc Apollon Oikonomopoulos
2290 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
2291 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
2292 b6135bbc Apollon Oikonomopoulos

2293 b6135bbc Apollon Oikonomopoulos
    """
2294 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Rename is not supported for PersistentBlockDev storage")
2295 b6135bbc Apollon Oikonomopoulos
2296 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
2297 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
2298 b6135bbc Apollon Oikonomopoulos

2299 b6135bbc Apollon Oikonomopoulos

2300 b6135bbc Apollon Oikonomopoulos
    """
2301 b6135bbc Apollon Oikonomopoulos
    self.attached = False
2302 b6135bbc Apollon Oikonomopoulos
    try:
2303 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
2304 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
2305 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2306 b6135bbc Apollon Oikonomopoulos
      return False
2307 b6135bbc Apollon Oikonomopoulos
2308 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
2309 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
2310 b6135bbc Apollon Oikonomopoulos
      return False
2311 b6135bbc Apollon Oikonomopoulos
2312 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
2313 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
2314 b6135bbc Apollon Oikonomopoulos
    self.attached = True
2315 b6135bbc Apollon Oikonomopoulos
2316 b6135bbc Apollon Oikonomopoulos
    return True
2317 b6135bbc Apollon Oikonomopoulos
2318 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
2319 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
2320 b6135bbc Apollon Oikonomopoulos

2321 b6135bbc Apollon Oikonomopoulos
    """
2322 b6135bbc Apollon Oikonomopoulos
    pass
2323 b6135bbc Apollon Oikonomopoulos
2324 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
2325 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
2326 b6135bbc Apollon Oikonomopoulos

2327 b6135bbc Apollon Oikonomopoulos
    """
2328 b6135bbc Apollon Oikonomopoulos
    pass
2329 b6135bbc Apollon Oikonomopoulos
2330 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
2331 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
2332 b6135bbc Apollon Oikonomopoulos

2333 b6135bbc Apollon Oikonomopoulos
    """
2334 b6135bbc Apollon Oikonomopoulos
    pass
2335 b6135bbc Apollon Oikonomopoulos
2336 b6135bbc Apollon Oikonomopoulos
  def Close(self):
2337 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
2338 b6135bbc Apollon Oikonomopoulos

2339 b6135bbc Apollon Oikonomopoulos
    """
2340 b6135bbc Apollon Oikonomopoulos
    pass
2341 b6135bbc Apollon Oikonomopoulos
2342 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
2343 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
2344 b6135bbc Apollon Oikonomopoulos

2345 b6135bbc Apollon Oikonomopoulos
    """
2346 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2347 b6135bbc Apollon Oikonomopoulos
2348 b6135bbc Apollon Oikonomopoulos
2349 7181fba0 Constantinos Venetsanopoulos
class RADOSBlockDevice(BlockDev):
2350 7181fba0 Constantinos Venetsanopoulos
  """A RADOS Block Device (rbd).
2351 7181fba0 Constantinos Venetsanopoulos

2352 7181fba0 Constantinos Venetsanopoulos
  This class implements the RADOS Block Device for the backend. You need
2353 7181fba0 Constantinos Venetsanopoulos
  the rbd kernel driver, the RADOS Tools and a working RADOS cluster for
2354 7181fba0 Constantinos Venetsanopoulos
  this to be functional.
2355 7181fba0 Constantinos Venetsanopoulos

2356 7181fba0 Constantinos Venetsanopoulos
  """
2357 7181fba0 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
2358 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
2359 7181fba0 Constantinos Venetsanopoulos

2360 7181fba0 Constantinos Venetsanopoulos
    """
2361 7181fba0 Constantinos Venetsanopoulos
    super(RADOSBlockDevice, self).__init__(unique_id, children, size, params)
2362 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2363 7181fba0 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2364 7181fba0 Constantinos Venetsanopoulos
2365 7181fba0 Constantinos Venetsanopoulos
    self.driver, self.rbd_name = unique_id
2366 7181fba0 Constantinos Venetsanopoulos
2367 7181fba0 Constantinos Venetsanopoulos
    self.major = self.minor = None
2368 7181fba0 Constantinos Venetsanopoulos
    self.Attach()
2369 7181fba0 Constantinos Venetsanopoulos
2370 7181fba0 Constantinos Venetsanopoulos
  @classmethod
2371 7181fba0 Constantinos Venetsanopoulos
  def Create(cls, unique_id, children, size, params):
2372 7181fba0 Constantinos Venetsanopoulos
    """Create a new rbd device.
2373 7181fba0 Constantinos Venetsanopoulos

2374 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
2375 7181fba0 Constantinos Venetsanopoulos

2376 7181fba0 Constantinos Venetsanopoulos
    """
2377 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2378 7181fba0 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
2379 7181fba0 Constantinos Venetsanopoulos
                                   str(unique_id))
2380 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = params[constants.LDP_POOL]
2381 7181fba0 Constantinos Venetsanopoulos
    rbd_name = unique_id[1]
2382 7181fba0 Constantinos Venetsanopoulos
2383 7181fba0 Constantinos Venetsanopoulos
    # Provision a new rbd volume (Image) inside the RADOS cluster.
2384 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "create", "-p", rbd_pool,
2385 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % size]
2386 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2387 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2388 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd creation failed (%s): %s",
2389 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2390 7181fba0 Constantinos Venetsanopoulos
2391 7181fba0 Constantinos Venetsanopoulos
    return RADOSBlockDevice(unique_id, children, size, params)
2392 7181fba0 Constantinos Venetsanopoulos
2393 7181fba0 Constantinos Venetsanopoulos
  def Remove(self):
2394 7181fba0 Constantinos Venetsanopoulos
    """Remove the rbd device.
2395 7181fba0 Constantinos Venetsanopoulos

2396 7181fba0 Constantinos Venetsanopoulos
    """
2397 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
2398 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
2399 7181fba0 Constantinos Venetsanopoulos
2400 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2401 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
2402 7181fba0 Constantinos Venetsanopoulos
      return
2403 7181fba0 Constantinos Venetsanopoulos
2404 7181fba0 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
2405 7181fba0 Constantinos Venetsanopoulos
    self.Shutdown()
2406 7181fba0 Constantinos Venetsanopoulos
2407 7181fba0 Constantinos Venetsanopoulos
    # Remove the actual Volume (Image) from the RADOS cluster.
2408 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "rm", "-p", rbd_pool, rbd_name]
2409 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2410 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2411 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Can't remove Volume from cluster with rbd rm: %s - %s",
2412 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2413 7181fba0 Constantinos Venetsanopoulos
2414 7181fba0 Constantinos Venetsanopoulos
  def Rename(self, new_id):
2415 7181fba0 Constantinos Venetsanopoulos
    """Rename this device.
2416 7181fba0 Constantinos Venetsanopoulos

2417 7181fba0 Constantinos Venetsanopoulos
    """
2418 7181fba0 Constantinos Venetsanopoulos
    pass
2419 7181fba0 Constantinos Venetsanopoulos
2420 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
2421 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
2422 7181fba0 Constantinos Venetsanopoulos

2423 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
2424 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
2425 7181fba0 Constantinos Venetsanopoulos

2426 7181fba0 Constantinos Venetsanopoulos
    """
2427 7181fba0 Constantinos Venetsanopoulos
    self.attached = False
2428 7181fba0 Constantinos Venetsanopoulos
2429 7181fba0 Constantinos Venetsanopoulos
    # Map the rbd volume to a block device under /dev
2430 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = self._MapVolumeToBlockdev(self.unique_id)
2431 7181fba0 Constantinos Venetsanopoulos
2432 7181fba0 Constantinos Venetsanopoulos
    try:
2433 7181fba0 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
2434 7181fba0 Constantinos Venetsanopoulos
    except OSError, err:
2435 7181fba0 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2436 7181fba0 Constantinos Venetsanopoulos
      return False
2437 7181fba0 Constantinos Venetsanopoulos
2438 7181fba0 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
2439 7181fba0 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
2440 7181fba0 Constantinos Venetsanopoulos
      return False
2441 7181fba0 Constantinos Venetsanopoulos
2442 7181fba0 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
2443 7181fba0 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
2444 7181fba0 Constantinos Venetsanopoulos
    self.attached = True
2445 7181fba0 Constantinos Venetsanopoulos
2446 7181fba0 Constantinos Venetsanopoulos
    return True
2447 7181fba0 Constantinos Venetsanopoulos
2448 7181fba0 Constantinos Venetsanopoulos
  def _MapVolumeToBlockdev(self, unique_id):
2449 7181fba0 Constantinos Venetsanopoulos
    """Maps existing rbd volumes to block devices.
2450 7181fba0 Constantinos Venetsanopoulos

2451 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
2452 7181fba0 Constantinos Venetsanopoulos

2453 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
2454 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
2455 7181fba0 Constantinos Venetsanopoulos

2456 7181fba0 Constantinos Venetsanopoulos
    """
2457 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2458 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2459 7181fba0 Constantinos Venetsanopoulos
2460 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2461 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2462 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2463 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2464 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped failed (%s): %s",
2465 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2466 7181fba0 Constantinos Venetsanopoulos
2467 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2468 7181fba0 Constantinos Venetsanopoulos
2469 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2470 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Return it.
2471 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2472 7181fba0 Constantinos Venetsanopoulos
2473 7181fba0 Constantinos Venetsanopoulos
    # The mapping doesn't exist. Create it.
2474 7181fba0 Constantinos Venetsanopoulos
    map_cmd = [constants.RBD_CMD, "map", "-p", pool, name]
2475 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(map_cmd)
2476 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2477 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map failed (%s): %s",
2478 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2479 7181fba0 Constantinos Venetsanopoulos
2480 7181fba0 Constantinos Venetsanopoulos
    # Find the corresponding rbd device.
2481 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2482 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2483 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2484 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map succeeded, but showmapped failed (%s): %s",
2485 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2486 7181fba0 Constantinos Venetsanopoulos
2487 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2488 7181fba0 Constantinos Venetsanopoulos
2489 7181fba0 Constantinos Venetsanopoulos
    if not rbd_dev:
2490 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map succeeded, but could not find the rbd block"
2491 7181fba0 Constantinos Venetsanopoulos
                  " device in output of showmapped, for volume: %s", name)
2492 7181fba0 Constantinos Venetsanopoulos
2493 7181fba0 Constantinos Venetsanopoulos
    # The device was successfully mapped. Return it.
2494 7181fba0 Constantinos Venetsanopoulos
    return rbd_dev
2495 7181fba0 Constantinos Venetsanopoulos
2496 7181fba0 Constantinos Venetsanopoulos
  @staticmethod
2497 7181fba0 Constantinos Venetsanopoulos
  def _ParseRbdShowmappedOutput(output, volume_name):
2498 7181fba0 Constantinos Venetsanopoulos
    """Parse the output of `rbd showmapped'.
2499 7181fba0 Constantinos Venetsanopoulos

2500 7181fba0 Constantinos Venetsanopoulos
    This method parses the output of `rbd showmapped' and returns
2501 7181fba0 Constantinos Venetsanopoulos
    the rbd block device path (e.g. /dev/rbd0) that matches the
2502 7181fba0 Constantinos Venetsanopoulos
    given rbd volume.
2503 7181fba0 Constantinos Venetsanopoulos

2504 7181fba0 Constantinos Venetsanopoulos
    @type output: string
2505 7181fba0 Constantinos Venetsanopoulos
    @param output: the whole output of `rbd showmapped'
2506 7181fba0 Constantinos Venetsanopoulos
    @type volume_name: string
2507 7181fba0 Constantinos Venetsanopoulos
    @param volume_name: the name of the volume whose device we search for
2508 7181fba0 Constantinos Venetsanopoulos
    @rtype: string or None
2509 7181fba0 Constantinos Venetsanopoulos
    @return: block device path if the volume is mapped, else None
2510 7181fba0 Constantinos Venetsanopoulos

2511 7181fba0 Constantinos Venetsanopoulos
    """
2512 7181fba0 Constantinos Venetsanopoulos
    allfields = 5
2513 7181fba0 Constantinos Venetsanopoulos
    volumefield = 2
2514 7181fba0 Constantinos Venetsanopoulos
    devicefield = 4
2515 7181fba0 Constantinos Venetsanopoulos
2516 7181fba0 Constantinos Venetsanopoulos
    field_sep = "\t"
2517 7181fba0 Constantinos Venetsanopoulos
2518 7181fba0 Constantinos Venetsanopoulos
    lines = output.splitlines()
2519 7181fba0 Constantinos Venetsanopoulos
    splitted_lines = map(lambda l: l.split(field_sep), lines)
2520 7181fba0 Constantinos Venetsanopoulos
2521 7181fba0 Constantinos Venetsanopoulos
    # Check empty output.
2522 7181fba0 Constantinos Venetsanopoulos
    if not splitted_lines:
2523 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped returned empty output")
2524 7181fba0 Constantinos Venetsanopoulos
2525 7181fba0 Constantinos Venetsanopoulos
    # Check showmapped header line, to determine number of fields.
2526 7181fba0 Constantinos Venetsanopoulos
    field_cnt = len(splitted_lines[0])
2527 7181fba0 Constantinos Venetsanopoulos
    if field_cnt != allfields:
2528 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Cannot parse rbd showmapped output because its format"
2529 7181fba0 Constantinos Venetsanopoulos
                  " seems to have changed; expected %s fields, found %s",
2530 7181fba0 Constantinos Venetsanopoulos
                  allfields, field_cnt)
2531 7181fba0 Constantinos Venetsanopoulos
2532 7181fba0 Constantinos Venetsanopoulos
    matched_lines = \
2533 7181fba0 Constantinos Venetsanopoulos
      filter(lambda l: len(l) == allfields and l[volumefield] == volume_name,
2534 7181fba0 Constantinos Venetsanopoulos
             splitted_lines)
2535 7181fba0 Constantinos Venetsanopoulos
2536 7181fba0 Constantinos Venetsanopoulos
    if len(matched_lines) > 1:
2537 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("The rbd volume %s is mapped more than once."
2538 7181fba0 Constantinos Venetsanopoulos
                  " This shouldn't happen, try to unmap the extra"
2539 7181fba0 Constantinos Venetsanopoulos
                  " devices manually.", volume_name)
2540 7181fba0 Constantinos Venetsanopoulos
2541 7181fba0 Constantinos Venetsanopoulos
    if matched_lines:
2542 7181fba0 Constantinos Venetsanopoulos
      # rbd block device found. Return it.
2543 7181fba0 Constantinos Venetsanopoulos
      rbd_dev = matched_lines[0][devicefield]
2544 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2545 7181fba0 Constantinos Venetsanopoulos
2546 7181fba0 Constantinos Venetsanopoulos
    # The given volume is not mapped.
2547 7181fba0 Constantinos Venetsanopoulos
    return None
2548 7181fba0 Constantinos Venetsanopoulos
2549 7181fba0 Constantinos Venetsanopoulos
  def Assemble(self):
2550 7181fba0 Constantinos Venetsanopoulos
    """Assemble the device.
2551 7181fba0 Constantinos Venetsanopoulos

2552 7181fba0 Constantinos Venetsanopoulos
    """
2553 7181fba0 Constantinos Venetsanopoulos
    pass
2554 7181fba0 Constantinos Venetsanopoulos
2555 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
2556 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
2557 7181fba0 Constantinos Venetsanopoulos

2558 7181fba0 Constantinos Venetsanopoulos
    """
2559 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2560 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
2561 7181fba0 Constantinos Venetsanopoulos
      return
2562 7181fba0 Constantinos Venetsanopoulos
2563 7181fba0 Constantinos Venetsanopoulos
    # Unmap the block device from the Volume.
2564 7181fba0 Constantinos Venetsanopoulos
    self._UnmapVolumeFromBlockdev(self.unique_id)
2565 7181fba0 Constantinos Venetsanopoulos
2566 7181fba0 Constantinos Venetsanopoulos
    self.minor = None
2567 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = None
2568 7181fba0 Constantinos Venetsanopoulos
2569 7181fba0 Constantinos Venetsanopoulos
  def _UnmapVolumeFromBlockdev(self, unique_id):
2570 7181fba0 Constantinos Venetsanopoulos
    """Unmaps the rbd device from the Volume it is mapped.
2571 7181fba0 Constantinos Venetsanopoulos

2572 7181fba0 Constantinos Venetsanopoulos
    Unmaps the rbd device from the Volume it was previously mapped to.
2573 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the Volume isn't mapped.
2574 7181fba0 Constantinos Venetsanopoulos

2575 7181fba0 Constantinos Venetsanopoulos
    """
2576 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2577 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2578 7181fba0 Constantinos Venetsanopoulos
2579 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2580 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2581 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2582 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2583 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped failed [during unmap](%s): %s",
2584 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2585 7181fba0 Constantinos Venetsanopoulos
2586 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2587 7181fba0 Constantinos Venetsanopoulos
2588 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2589 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Unmap the rbd device.
2590 7181fba0 Constantinos Venetsanopoulos
      unmap_cmd = [constants.RBD_CMD, "unmap", "%s" % rbd_dev]
2591 7181fba0 Constantinos Venetsanopoulos
      result = utils.RunCmd(unmap_cmd)
2592 7181fba0 Constantinos Venetsanopoulos
      if result.failed:
2593 7181fba0 Constantinos Venetsanopoulos
        _ThrowError("rbd unmap failed (%s): %s",
2594 7181fba0 Constantinos Venetsanopoulos
                    result.fail_reason, result.output)
2595 7181fba0 Constantinos Venetsanopoulos
2596 7181fba0 Constantinos Venetsanopoulos
  def Open(self, force=False):
2597 7181fba0 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
2598 7181fba0 Constantinos Venetsanopoulos

2599 7181fba0 Constantinos Venetsanopoulos
    """
2600 7181fba0 Constantinos Venetsanopoulos
    pass
2601 7181fba0 Constantinos Venetsanopoulos
2602 7181fba0 Constantinos Venetsanopoulos
  def Close(self):
2603 7181fba0 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
2604 7181fba0 Constantinos Venetsanopoulos

2605 7181fba0 Constantinos Venetsanopoulos
    """
2606 7181fba0 Constantinos Venetsanopoulos
    pass
2607 7181fba0 Constantinos Venetsanopoulos
2608 7181fba0 Constantinos Venetsanopoulos
  def Grow(self, amount, dryrun):
2609 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
2610 7181fba0 Constantinos Venetsanopoulos

2611 7181fba0 Constantinos Venetsanopoulos
    @type amount: integer
2612 7181fba0 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
2613 7181fba0 Constantinos Venetsanopoulos
    @type dryrun: boolean
2614 7181fba0 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
2615 7181fba0 Constantinos Venetsanopoulos
        only, without actually increasing the size
2616 7181fba0 Constantinos Venetsanopoulos

2617 7181fba0 Constantinos Venetsanopoulos
    """
2618 7181fba0 Constantinos Venetsanopoulos
    if not self.Attach():
2619 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Can't attach to rbd device during Grow()")
2620 7181fba0 Constantinos Venetsanopoulos
2621 7181fba0 Constantinos Venetsanopoulos
    if dryrun:
2622 7181fba0 Constantinos Venetsanopoulos
      # the rbd tool does not support dry runs of resize operations.
2623 7181fba0 Constantinos Venetsanopoulos
      # Since rbd volumes are thinly provisioned, we assume
2624 7181fba0 Constantinos Venetsanopoulos
      # there is always enough free space for the operation.
2625 7181fba0 Constantinos Venetsanopoulos
      return
2626 7181fba0 Constantinos Venetsanopoulos
2627 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
2628 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
2629 7181fba0 Constantinos Venetsanopoulos
    new_size = self.size + amount
2630 7181fba0 Constantinos Venetsanopoulos
2631 7181fba0 Constantinos Venetsanopoulos
    # Resize the rbd volume (Image) inside the RADOS cluster.
2632 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "resize", "-p", rbd_pool,
2633 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % new_size]
2634 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2635 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2636 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd resize failed (%s): %s",
2637 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2638 7181fba0 Constantinos Venetsanopoulos
2639 7181fba0 Constantinos Venetsanopoulos
2640 a8083063 Iustin Pop
DEV_MAP = {
2641 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2642 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2643 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
2644 7181fba0 Constantinos Venetsanopoulos
  constants.LD_RBD: RADOSBlockDevice,
2645 a8083063 Iustin Pop
  }
2646 a8083063 Iustin Pop
2647 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
2648 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
2649 cb7c0198 Iustin Pop
2650 a8083063 Iustin Pop
2651 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
2652 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
2653 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2654 94dcbdb0 Andrea Spadaccini
2655 94dcbdb0 Andrea Spadaccini
2656 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
2657 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
2658 5ff82cc9 René Nussbaumer

2659 5ff82cc9 René Nussbaumer
  """
2660 5ff82cc9 René Nussbaumer
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
2661 5ff82cc9 René Nussbaumer
  if missing:
2662 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
2663 5ff82cc9 René Nussbaumer
                                 missing)
2664 5ff82cc9 René Nussbaumer
2665 5ff82cc9 René Nussbaumer
2666 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
2667 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2668 a8083063 Iustin Pop

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

2672 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2673 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
2674 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2675 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2676 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2677 94dcbdb0 Andrea Spadaccini

2678 a8083063 Iustin Pop
  """
2679 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2680 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
2681 c7c6606d René Nussbaumer
                                  disk.params)
2682 cb999543 Iustin Pop
  if not device.attached:
2683 a8083063 Iustin Pop
    return None
2684 ecb091e3 Iustin Pop
  return device
2685 a8083063 Iustin Pop
2686 a8083063 Iustin Pop
2687 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
2688 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2689 a8083063 Iustin Pop

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

2693 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2694 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
2695 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2696 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2697 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2698 94dcbdb0 Andrea Spadaccini

2699 a8083063 Iustin Pop
  """
2700 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2701 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
2702 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
2703 c7c6606d René Nussbaumer
                                  disk.params)
2704 1063abd1 Iustin Pop
  device.Assemble()
2705 a8083063 Iustin Pop
  return device
2706 a8083063 Iustin Pop
2707 a8083063 Iustin Pop
2708 94dcbdb0 Andrea Spadaccini
def Create(disk, children):
2709 a8083063 Iustin Pop
  """Create a device.
2710 a8083063 Iustin Pop

2711 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2712 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
2713 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2714 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2715 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2716 94dcbdb0 Andrea Spadaccini

2717 a8083063 Iustin Pop
  """
2718 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2719 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
2720 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
2721 c7c6606d René Nussbaumer
                                         disk.params)
2722 a8083063 Iustin Pop
  return device