Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ f2f57b6e

History | View | Annotate | Download (76.7 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 4b97f902 Apollon Oikonomopoulos
# Copyright (C) 2006, 2007, 2010, 2011 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 f2f57b6e Andrea Spadaccini

232 a8083063 Iustin Pop
    """
233 a8083063 Iustin Pop
    result = True
234 a8083063 Iustin Pop
    if self._children:
235 a8083063 Iustin Pop
      for child in self._children:
236 f2f57b6e Andrea Spadaccini
        result = result and child.SetSyncParams(params)
237 a8083063 Iustin Pop
    return result
238 a8083063 Iustin Pop
239 a3fffcc6 Renรฉ Nussbaumer
  def PauseResumeSync(self, pause):
240 a3fffcc6 Renรฉ Nussbaumer
    """Pause/Resume the sync of the mirror.
241 a3fffcc6 Renรฉ Nussbaumer

242 a3fffcc6 Renรฉ Nussbaumer
    In case this is not a mirroring device, this is no-op.
243 a3fffcc6 Renรฉ Nussbaumer

244 f2f57b6e Andrea Spadaccini
    @param pause: Whether to pause or resume
245 a3fffcc6 Renรฉ Nussbaumer

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

256 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
257 a8083063 Iustin Pop
    status of the mirror.
258 a8083063 Iustin Pop

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

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

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

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

272 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
273 c41eea6e Iustin Pop

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

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

290 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
291 96acbc09 Michael Hanselmann

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

332 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
333 a0c3fea1 Michael Hanselmann

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

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

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

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

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

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

384 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
385 a8083063 Iustin Pop

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

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

451 197478f2 Renรฉ Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
452 197478f2 Renรฉ Nussbaumer
    @param fields: Fields to return
453 197478f2 Renรฉ Nussbaumer
    @return: A list of dicts each with the parsed fields
454 197478f2 Renรฉ Nussbaumer

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

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

487 c41eea6e Iustin Pop
    @rtype: list
488 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
489 098c0958 Michael Hanselmann

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

514 197478f2 Renรฉ Nussbaumer
    @param vg_names: list of volume group names, if empty all will be returned
515 197478f2 Renรฉ Nussbaumer
    @param filter_readonly: whether to skip over readonly VGs
516 197478f2 Renรฉ Nussbaumer

517 197478f2 Renรฉ Nussbaumer
    @rtype: list
518 673cd9c4 Renรฉ Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
519 673cd9c4 Renรฉ Nussbaumer
             MiB
520 197478f2 Renรฉ Nussbaumer

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

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

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

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

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

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

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

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

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

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

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

673 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
674 9db6dbce Iustin Pop
    status of the mirror.
675 9db6dbce Iustin Pop

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

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

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

689 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
690 c41eea6e Iustin Pop

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

708 a8083063 Iustin Pop
    This is a no-op for the LV device type.
709 a8083063 Iustin Pop

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

716 a8083063 Iustin Pop
    This is a no-op for the LV device type.
717 a8083063 Iustin Pop

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

724 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
725 800ac399 Iustin Pop

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

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

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

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

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

904 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
905 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
906 0f7f32d9 Iustin Pop

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

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

942 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
943 c41eea6e Iustin Pop
        for that minor
944 a8083063 Iustin Pop

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

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

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

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

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

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

1043 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1044 a8083063 Iustin Pop

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

1058 abdf0113 Iustin Pop
    This currently only check the size, which must be around
1059 abdf0113 Iustin Pop
    128MiB.
1060 a8083063 Iustin Pop

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

1085 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1086 a8083063 Iustin Pop

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

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

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

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

1148 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1149 a2cfdea2 Iustin Pop

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

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

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

1197 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1198 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1199 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

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

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

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

1395 8a69b3a8 Andrea Spadaccini
    If the desired option is unsupported, raises errors.BlockDeviceError.
1396 8a69b3a8 Andrea Spadaccini

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

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

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

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

1581 7d585316 Iustin Pop
    This is the low-level implementation.
1582 7d585316 Iustin Pop

1583 7d585316 Iustin Pop
    @type minor: int
1584 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1585 f2f57b6e Andrea Spadaccini
    @type params: dict
1586 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1587 7d585316 Iustin Pop
    @rtype: boolean
1588 7d585316 Iustin Pop
    @return: the success of the operation
1589 7d585316 Iustin Pop

1590 a2cfdea2 Iustin Pop
    """
1591 f2f57b6e Andrea Spadaccini
1592 f2f57b6e Andrea Spadaccini
    args = ["drbdsetup", cls._DevPath(minor), "syncer"]
1593 f2f57b6e Andrea Spadaccini
    if params[constants.LDP_DYNAMIC_RESYNC]:
1594 f2f57b6e Andrea Spadaccini
      version = cls._GetVersion(cls._GetProcData())
1595 f2f57b6e Andrea Spadaccini
      vmin = version["k_minor"]
1596 f2f57b6e Andrea Spadaccini
      vrel = version["k_point"]
1597 f2f57b6e Andrea Spadaccini
1598 f2f57b6e Andrea Spadaccini
      # By definition we are using 8.x, so just check the rest of the version
1599 f2f57b6e Andrea Spadaccini
      # number
1600 f2f57b6e Andrea Spadaccini
      if vmin != 3 or vrel < 9:
1601 f2f57b6e Andrea Spadaccini
        logging.error("The current DRBD version (8.%d.%d) does not support the"
1602 f2f57b6e Andrea Spadaccini
                      " dynamic resync speed controller", vmin, vrel)
1603 f2f57b6e Andrea Spadaccini
        return False
1604 f2f57b6e Andrea Spadaccini
1605 f2f57b6e Andrea Spadaccini
      # add the c-* parameters to args
1606 f2f57b6e Andrea Spadaccini
      # TODO(spadaccio) use the actual parameters
1607 f2f57b6e Andrea Spadaccini
      args.extend(["--c-plan-ahead", "20"])
1608 f2f57b6e Andrea Spadaccini
1609 f2f57b6e Andrea Spadaccini
    else:
1610 f2f57b6e Andrea Spadaccini
      args.extend(["-r", "%d" % params[constants.LDP_RESYNC_RATE]])
1611 f2f57b6e Andrea Spadaccini
1612 f2f57b6e Andrea Spadaccini
    args.append("--create-device")
1613 f2f57b6e Andrea Spadaccini
    result = utils.RunCmd(args)
1614 a2cfdea2 Iustin Pop
    if result.failed:
1615 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1616 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1617 7d585316 Iustin Pop
    return not result.failed
1618 7d585316 Iustin Pop
1619 f2f57b6e Andrea Spadaccini
  def SetSyncParams(self, params):
1620 f2f57b6e Andrea Spadaccini
    """Set the synchronization parameters of the DRBD syncer.
1621 7d585316 Iustin Pop

1622 f2f57b6e Andrea Spadaccini
    @type params: dict
1623 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1624 7d585316 Iustin Pop
    @rtype: boolean
1625 7d585316 Iustin Pop
    @return: the success of the operation
1626 7d585316 Iustin Pop

1627 7d585316 Iustin Pop
    """
1628 7d585316 Iustin Pop
    if self.minor is None:
1629 f2f57b6e Andrea Spadaccini
      logging.info("Not attached during SetSyncParams")
1630 7d585316 Iustin Pop
      return False
1631 f2f57b6e Andrea Spadaccini
    children_result = super(DRBD8, self).SetSyncParams(params)
1632 f2f57b6e Andrea Spadaccini
    return self._SetMinorSyncParams(self.minor, params) and children_result
1633 a2cfdea2 Iustin Pop
1634 a3fffcc6 Renรฉ Nussbaumer
  def PauseResumeSync(self, pause):
1635 a3fffcc6 Renรฉ Nussbaumer
    """Pauses or resumes the sync of a DRBD device.
1636 a3fffcc6 Renรฉ Nussbaumer

1637 a3fffcc6 Renรฉ Nussbaumer
    @param pause: Wether to pause or resume
1638 a3fffcc6 Renรฉ Nussbaumer
    @return: the success of the operation
1639 a3fffcc6 Renรฉ Nussbaumer

1640 a3fffcc6 Renรฉ Nussbaumer
    """
1641 a3fffcc6 Renรฉ Nussbaumer
    if self.minor is None:
1642 a3fffcc6 Renรฉ Nussbaumer
      logging.info("Not attached during PauseSync")
1643 a3fffcc6 Renรฉ Nussbaumer
      return False
1644 a3fffcc6 Renรฉ Nussbaumer
1645 a3fffcc6 Renรฉ Nussbaumer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
1646 a3fffcc6 Renรฉ Nussbaumer
1647 a3fffcc6 Renรฉ Nussbaumer
    if pause:
1648 a3fffcc6 Renรฉ Nussbaumer
      cmd = "pause-sync"
1649 a3fffcc6 Renรฉ Nussbaumer
    else:
1650 a3fffcc6 Renรฉ Nussbaumer
      cmd = "resume-sync"
1651 a3fffcc6 Renรฉ Nussbaumer
1652 a3fffcc6 Renรฉ Nussbaumer
    result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
1653 a3fffcc6 Renรฉ Nussbaumer
    if result.failed:
1654 a3fffcc6 Renรฉ Nussbaumer
      logging.error("Can't %s: %s - %s", cmd,
1655 a3fffcc6 Renรฉ Nussbaumer
                    result.fail_reason, result.output)
1656 a3fffcc6 Renรฉ Nussbaumer
    return not result.failed and children_result
1657 a3fffcc6 Renรฉ Nussbaumer
1658 6b90c22e Iustin Pop
  def GetProcStatus(self):
1659 6b90c22e Iustin Pop
    """Return device data from /proc.
1660 6b90c22e Iustin Pop

1661 6b90c22e Iustin Pop
    """
1662 6b90c22e Iustin Pop
    if self.minor is None:
1663 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1664 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1665 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1666 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1667 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1668 6b90c22e Iustin Pop
1669 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1670 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1671 a2cfdea2 Iustin Pop

1672 a2cfdea2 Iustin Pop

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

1677 0834c866 Iustin Pop

1678 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1679 0834c866 Iustin Pop
    network not connected or local disk missing.
1680 0834c866 Iustin Pop

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

1684 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1685 c41eea6e Iustin Pop

1686 a2cfdea2 Iustin Pop
    """
1687 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1688 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1689 96acbc09 Michael Hanselmann
1690 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1691 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1692 f208978a Michael Hanselmann
1693 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1694 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1695 f208978a Michael Hanselmann
    elif stats.is_diskless:
1696 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1697 f208978a Michael Hanselmann
    else:
1698 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1699 96acbc09 Michael Hanselmann
1700 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1701 96acbc09 Michael Hanselmann
                                  major=self.major,
1702 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1703 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1704 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1705 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1706 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1707 a2cfdea2 Iustin Pop
1708 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1709 a2cfdea2 Iustin Pop
    """Make the local state primary.
1710 a2cfdea2 Iustin Pop

1711 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1712 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1713 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1714 f860ff4e Guido Trotter
    is mandatory.
1715 a2cfdea2 Iustin Pop

1716 a2cfdea2 Iustin Pop
    """
1717 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1718 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1719 a2cfdea2 Iustin Pop
      return False
1720 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1721 a2cfdea2 Iustin Pop
    if force:
1722 a2cfdea2 Iustin Pop
      cmd.append("-o")
1723 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1724 a2cfdea2 Iustin Pop
    if result.failed:
1725 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1726 82463074 Iustin Pop
                  result.output)
1727 a2cfdea2 Iustin Pop
1728 a2cfdea2 Iustin Pop
  def Close(self):
1729 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1730 a2cfdea2 Iustin Pop

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

1733 a2cfdea2 Iustin Pop
    """
1734 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1735 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1736 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1737 a2cfdea2 Iustin Pop
    if result.failed:
1738 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1739 82463074 Iustin Pop
                  self.minor, result.output)
1740 a2cfdea2 Iustin Pop
1741 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1742 cf8df3f3 Iustin Pop
    """Removes network configuration.
1743 cf8df3f3 Iustin Pop

1744 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1745 cf8df3f3 Iustin Pop

1746 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1747 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1748 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1749 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1750 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1751 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1752 cf8df3f3 Iustin Pop
    exception.
1753 cf8df3f3 Iustin Pop

1754 cf8df3f3 Iustin Pop
    """
1755 cf8df3f3 Iustin Pop
    if self.minor is None:
1756 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1757 cf8df3f3 Iustin Pop
1758 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1759 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1760 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1761 cf8df3f3 Iustin Pop
1762 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1763 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1764 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1765 cf8df3f3 Iustin Pop
1766 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1767 def8e2f6 Michael Hanselmann
1768 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1769 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1770 def8e2f6 Michael Hanselmann
        return
1771 def8e2f6 Michael Hanselmann
1772 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1773 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1774 def8e2f6 Michael Hanselmann
      # forgotten
1775 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1776 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1777 def8e2f6 Michael Hanselmann
1778 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1779 def8e2f6 Michael Hanselmann
1780 def8e2f6 Michael Hanselmann
    # Keep start time
1781 def8e2f6 Michael Hanselmann
    start_time = time.time()
1782 def8e2f6 Michael Hanselmann
1783 def8e2f6 Michael Hanselmann
    try:
1784 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1785 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1786 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1787 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1788 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1789 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1790 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1791 cf8df3f3 Iustin Pop
      else:
1792 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1793 def8e2f6 Michael Hanselmann
1794 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1795 cf8df3f3 Iustin Pop
1796 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1797 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1798 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1799 82463074 Iustin Pop
                   self.minor, reconfig_time)
1800 cf8df3f3 Iustin Pop
1801 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1802 cf8df3f3 Iustin Pop
    """Reconnects the network.
1803 cf8df3f3 Iustin Pop

1804 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1805 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1806 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1807 cf8df3f3 Iustin Pop

1808 cf8df3f3 Iustin Pop
    Args:
1809 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1810 cf8df3f3 Iustin Pop

1811 cf8df3f3 Iustin Pop
    """
1812 cf8df3f3 Iustin Pop
    if self.minor is None:
1813 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1814 cf8df3f3 Iustin Pop
1815 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1816 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1817 cf8df3f3 Iustin Pop
1818 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1819 cf8df3f3 Iustin Pop
1820 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1821 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1822 cf8df3f3 Iustin Pop
1823 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1824 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1825 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1826 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1827 cf8df3f3 Iustin Pop
1828 a2cfdea2 Iustin Pop
  def Attach(self):
1829 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1830 2d0c8319 Iustin Pop

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

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

1838 2d0c8319 Iustin Pop
    """
1839 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1840 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1841 2d0c8319 Iustin Pop
      minor = self._aminor
1842 2d0c8319 Iustin Pop
    else:
1843 2d0c8319 Iustin Pop
      minor = None
1844 2d0c8319 Iustin Pop
1845 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1846 2d0c8319 Iustin Pop
    return minor is not None
1847 2d0c8319 Iustin Pop
1848 2d0c8319 Iustin Pop
  def Assemble(self):
1849 2d0c8319 Iustin Pop
    """Assemble the drbd.
1850 2d0c8319 Iustin Pop

1851 2d0c8319 Iustin Pop
    Method:
1852 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1853 2d0c8319 Iustin Pop
        our config
1854 2d0c8319 Iustin Pop
      - if not, we create it from zero
1855 d529599f Andrea Spadaccini
      - anyway, set the device parameters
1856 2d0c8319 Iustin Pop

1857 2d0c8319 Iustin Pop
    """
1858 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1859 2d0c8319 Iustin Pop
1860 2d0c8319 Iustin Pop
    self.Attach()
1861 2d0c8319 Iustin Pop
    if self.minor is None:
1862 2d0c8319 Iustin Pop
      # local device completely unconfigured
1863 1063abd1 Iustin Pop
      self._FastAssemble()
1864 2d0c8319 Iustin Pop
    else:
1865 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1866 2d0c8319 Iustin Pop
      # the device
1867 1063abd1 Iustin Pop
      self._SlowAssemble()
1868 2d0c8319 Iustin Pop
1869 f2f57b6e Andrea Spadaccini
    self.SetSyncParams(self.params)
1870 d529599f Andrea Spadaccini
1871 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1872 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1873 a2cfdea2 Iustin Pop

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

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

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

1951 a2cfdea2 Iustin Pop
    """
1952 a1578d63 Iustin Pop
    minor = self._aminor
1953 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1954 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1955 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1956 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1957 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1958 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1959 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1960 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1961 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1962 a2cfdea2 Iustin Pop
1963 a2cfdea2 Iustin Pop
  @classmethod
1964 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1965 b00b95dd Iustin Pop
    """Detach from the local device.
1966 b00b95dd Iustin Pop

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

1970 b00b95dd Iustin Pop
    """
1971 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1972 b00b95dd Iustin Pop
    if result.failed:
1973 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1974 b00b95dd Iustin Pop
1975 b00b95dd Iustin Pop
  @classmethod
1976 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1977 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1978 f3e513ad Iustin Pop

1979 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1980 f3e513ad Iustin Pop

1981 f3e513ad Iustin Pop
    """
1982 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1983 a8459f1c Iustin Pop
    if result.failed:
1984 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1985 f3e513ad Iustin Pop
1986 f3e513ad Iustin Pop
  @classmethod
1987 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1988 a2cfdea2 Iustin Pop
    """Deactivate the device.
1989 a2cfdea2 Iustin Pop

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

1992 a2cfdea2 Iustin Pop
    """
1993 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1994 a2cfdea2 Iustin Pop
    if result.failed:
1995 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1996 33bc6f01 Iustin Pop
                  minor, result.output)
1997 a2cfdea2 Iustin Pop
1998 a2cfdea2 Iustin Pop
  def Shutdown(self):
1999 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2000 a2cfdea2 Iustin Pop

2001 a2cfdea2 Iustin Pop
    """
2002 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2003 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
2004 746f7476 Iustin Pop
      return
2005 746f7476 Iustin Pop
    minor = self.minor
2006 a2cfdea2 Iustin Pop
    self.minor = None
2007 a2cfdea2 Iustin Pop
    self.dev_path = None
2008 746f7476 Iustin Pop
    self._ShutdownAll(minor)
2009 a2cfdea2 Iustin Pop
2010 a2cfdea2 Iustin Pop
  def Remove(self):
2011 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2012 a2cfdea2 Iustin Pop

2013 a2cfdea2 Iustin Pop
    """
2014 0c6c04ec Iustin Pop
    self.Shutdown()
2015 a2cfdea2 Iustin Pop
2016 a2cfdea2 Iustin Pop
  @classmethod
2017 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2018 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2019 a2cfdea2 Iustin Pop

2020 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2021 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2022 a2cfdea2 Iustin Pop

2023 a2cfdea2 Iustin Pop
    """
2024 a2cfdea2 Iustin Pop
    if len(children) != 2:
2025 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2026 767d52d3 Iustin Pop
    # check that the minor is unused
2027 767d52d3 Iustin Pop
    aminor = unique_id[4]
2028 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
2029 767d52d3 Iustin Pop
    if aminor in proc_info:
2030 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
2031 767d52d3 Iustin Pop
      in_use = status.is_in_use
2032 767d52d3 Iustin Pop
    else:
2033 767d52d3 Iustin Pop
      in_use = False
2034 767d52d3 Iustin Pop
    if in_use:
2035 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
2036 a2cfdea2 Iustin Pop
    meta = children[1]
2037 a2cfdea2 Iustin Pop
    meta.Assemble()
2038 a2cfdea2 Iustin Pop
    if not meta.Attach():
2039 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
2040 33bc6f01 Iustin Pop
                  aminor, meta)
2041 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
2042 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
2043 94dcbdb0 Andrea Spadaccini
    return cls(unique_id, children, size, params)
2044 a2cfdea2 Iustin Pop
2045 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
2046 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
2047 1005d816 Iustin Pop

2048 1005d816 Iustin Pop
    """
2049 1005d816 Iustin Pop
    if self.minor is None:
2050 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
2051 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
2052 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
2053 7fe23d47 Iustin Pop
    self._children[0].Grow(amount, dryrun)
2054 7fe23d47 Iustin Pop
    if dryrun:
2055 7fe23d47 Iustin Pop
      # DRBD does not support dry-run mode, so we'll return here
2056 7fe23d47 Iustin Pop
      return
2057 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
2058 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
2059 1005d816 Iustin Pop
    if result.failed:
2060 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
2061 1005d816 Iustin Pop
2062 a8083063 Iustin Pop
2063 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
2064 6f695a2e Manuel Franceschini
  """File device.
2065 abdf0113 Iustin Pop

2066 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
2067 6f695a2e Manuel Franceschini

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

2070 6f695a2e Manuel Franceschini
  """
2071 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2072 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
2073 6f695a2e Manuel Franceschini

2074 6f695a2e Manuel Franceschini
    """
2075 6f695a2e Manuel Franceschini
    if children:
2076 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
2077 94dcbdb0 Andrea Spadaccini
    super(FileStorage, self).__init__(unique_id, children, size, params)
2078 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2079 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2080 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
2081 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
2082 ecb091e3 Iustin Pop
    self.Attach()
2083 6f695a2e Manuel Franceschini
2084 6f695a2e Manuel Franceschini
  def Assemble(self):
2085 6f695a2e Manuel Franceschini
    """Assemble the device.
2086 6f695a2e Manuel Franceschini

2087 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
2088 6f695a2e Manuel Franceschini

2089 6f695a2e Manuel Franceschini
    """
2090 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
2091 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
2092 6f695a2e Manuel Franceschini
2093 6f695a2e Manuel Franceschini
  def Shutdown(self):
2094 6f695a2e Manuel Franceschini
    """Shutdown the device.
2095 6f695a2e Manuel Franceschini

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

2099 6f695a2e Manuel Franceschini
    """
2100 746f7476 Iustin Pop
    pass
2101 6f695a2e Manuel Franceschini
2102 6f695a2e Manuel Franceschini
  def Open(self, force=False):
2103 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
2104 6f695a2e Manuel Franceschini

2105 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2106 6f695a2e Manuel Franceschini

2107 6f695a2e Manuel Franceschini
    """
2108 6f695a2e Manuel Franceschini
    pass
2109 6f695a2e Manuel Franceschini
2110 6f695a2e Manuel Franceschini
  def Close(self):
2111 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
2112 6f695a2e Manuel Franceschini

2113 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2114 6f695a2e Manuel Franceschini

2115 6f695a2e Manuel Franceschini
    """
2116 6f695a2e Manuel Franceschini
    pass
2117 6f695a2e Manuel Franceschini
2118 6f695a2e Manuel Franceschini
  def Remove(self):
2119 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
2120 6f695a2e Manuel Franceschini

2121 c41eea6e Iustin Pop
    @rtype: boolean
2122 c41eea6e Iustin Pop
    @return: True if the removal was successful
2123 6f695a2e Manuel Franceschini

2124 6f695a2e Manuel Franceschini
    """
2125 6f695a2e Manuel Franceschini
    try:
2126 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
2127 6f695a2e Manuel Franceschini
    except OSError, err:
2128 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
2129 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
2130 6f695a2e Manuel Franceschini
2131 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
2132 bbe4cc16 Iustin Pop
    """Renames the file.
2133 bbe4cc16 Iustin Pop

2134 bbe4cc16 Iustin Pop
    """
2135 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
2136 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
2137 bbe4cc16 Iustin Pop
2138 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
2139 bbe4cc16 Iustin Pop
    """Grow the file
2140 bbe4cc16 Iustin Pop

2141 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2142 bbe4cc16 Iustin Pop

2143 bbe4cc16 Iustin Pop
    """
2144 91e2d9ec Guido Trotter
    # Check that the file exists
2145 91e2d9ec Guido Trotter
    self.Assemble()
2146 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
2147 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
2148 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
2149 7fe23d47 Iustin Pop
    # We can't really simulate the growth
2150 7fe23d47 Iustin Pop
    if dryrun:
2151 7fe23d47 Iustin Pop
      return
2152 91e2d9ec Guido Trotter
    try:
2153 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
2154 91e2d9ec Guido Trotter
      f.truncate(new_size)
2155 91e2d9ec Guido Trotter
      f.close()
2156 91e2d9ec Guido Trotter
    except EnvironmentError, err:
2157 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
2158 bbe4cc16 Iustin Pop
2159 6f695a2e Manuel Franceschini
  def Attach(self):
2160 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2161 6f695a2e Manuel Franceschini

2162 6f695a2e Manuel Franceschini
    Check if this file already exists.
2163 6f695a2e Manuel Franceschini

2164 c41eea6e Iustin Pop
    @rtype: boolean
2165 c41eea6e Iustin Pop
    @return: True if file exists
2166 6f695a2e Manuel Franceschini

2167 6f695a2e Manuel Franceschini
    """
2168 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
2169 ecb091e3 Iustin Pop
    return self.attached
2170 6f695a2e Manuel Franceschini
2171 fcff3897 Iustin Pop
  def GetActualSize(self):
2172 fcff3897 Iustin Pop
    """Return the actual disk size.
2173 fcff3897 Iustin Pop

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

2176 fcff3897 Iustin Pop
    """
2177 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2178 fcff3897 Iustin Pop
    try:
2179 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2180 fcff3897 Iustin Pop
      return st.st_size
2181 fcff3897 Iustin Pop
    except OSError, err:
2182 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2183 fcff3897 Iustin Pop
2184 6f695a2e Manuel Franceschini
  @classmethod
2185 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2186 6f695a2e Manuel Franceschini
    """Create a new file.
2187 6f695a2e Manuel Franceschini

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

2190 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2191 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2192 6f695a2e Manuel Franceschini

2193 6f695a2e Manuel Franceschini
    """
2194 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2195 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2196 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2197 6f695a2e Manuel Franceschini
    try:
2198 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
2199 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
2200 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2201 6f695a2e Manuel Franceschini
      f.close()
2202 cdeefd9b Guido Trotter
    except EnvironmentError, err:
2203 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
2204 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
2205 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
2206 6f695a2e Manuel Franceschini
2207 94dcbdb0 Andrea Spadaccini
    return FileStorage(unique_id, children, size, params)
2208 6f695a2e Manuel Franceschini
2209 6f695a2e Manuel Franceschini
2210 b6135bbc Apollon Oikonomopoulos
class PersistentBlockDevice(BlockDev):
2211 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
2212 b6135bbc Apollon Oikonomopoulos

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

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

2219 b6135bbc Apollon Oikonomopoulos
  """
2220 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2221 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
2222 b6135bbc Apollon Oikonomopoulos

2223 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2224 b6135bbc Apollon Oikonomopoulos

2225 b6135bbc Apollon Oikonomopoulos
    """
2226 94dcbdb0 Andrea Spadaccini
    super(PersistentBlockDevice, self).__init__(unique_id, children, size,
2227 94dcbdb0 Andrea Spadaccini
                                                params)
2228 b6135bbc Apollon Oikonomopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2229 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2230 b6135bbc Apollon Oikonomopoulos
    self.dev_path = unique_id[1]
2231 d0c8c01d Iustin Pop
    if not os.path.realpath(self.dev_path).startswith("/dev/"):
2232 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Full path '%s' lies outside /dev" %
2233 b6135bbc Apollon Oikonomopoulos
                              os.path.realpath(self.dev_path))
2234 b6135bbc Apollon Oikonomopoulos
    # TODO: this is just a safety guard checking that we only deal with devices
2235 b6135bbc Apollon Oikonomopoulos
    # we know how to handle. In the future this will be integrated with
2236 b6135bbc Apollon Oikonomopoulos
    # external storage backends and possible values will probably be collected
2237 b6135bbc Apollon Oikonomopoulos
    # from the cluster configuration.
2238 b6135bbc Apollon Oikonomopoulos
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
2239 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Got persistent block device of invalid type: %s" %
2240 b6135bbc Apollon Oikonomopoulos
                       unique_id[0])
2241 b6135bbc Apollon Oikonomopoulos
2242 b6135bbc Apollon Oikonomopoulos
    self.major = self.minor = None
2243 b6135bbc Apollon Oikonomopoulos
    self.Attach()
2244 b6135bbc Apollon Oikonomopoulos
2245 b6135bbc Apollon Oikonomopoulos
  @classmethod
2246 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2247 b6135bbc Apollon Oikonomopoulos
    """Create a new device
2248 b6135bbc Apollon Oikonomopoulos

2249 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2250 b6135bbc Apollon Oikonomopoulos

2251 b6135bbc Apollon Oikonomopoulos
    """
2252 94dcbdb0 Andrea Spadaccini
    return PersistentBlockDevice(unique_id, children, 0, params)
2253 b6135bbc Apollon Oikonomopoulos
2254 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
2255 b6135bbc Apollon Oikonomopoulos
    """Remove a device
2256 b6135bbc Apollon Oikonomopoulos

2257 b6135bbc Apollon Oikonomopoulos
    This is a noop
2258 b6135bbc Apollon Oikonomopoulos

2259 b6135bbc Apollon Oikonomopoulos
    """
2260 b6135bbc Apollon Oikonomopoulos
    pass
2261 b6135bbc Apollon Oikonomopoulos
2262 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
2263 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
2264 b6135bbc Apollon Oikonomopoulos

2265 b6135bbc Apollon Oikonomopoulos
    """
2266 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Rename is not supported for PersistentBlockDev storage")
2267 b6135bbc Apollon Oikonomopoulos
2268 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
2269 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
2270 b6135bbc Apollon Oikonomopoulos

2271 b6135bbc Apollon Oikonomopoulos

2272 b6135bbc Apollon Oikonomopoulos
    """
2273 b6135bbc Apollon Oikonomopoulos
    self.attached = False
2274 b6135bbc Apollon Oikonomopoulos
    try:
2275 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
2276 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
2277 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2278 b6135bbc Apollon Oikonomopoulos
      return False
2279 b6135bbc Apollon Oikonomopoulos
2280 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
2281 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
2282 b6135bbc Apollon Oikonomopoulos
      return False
2283 b6135bbc Apollon Oikonomopoulos
2284 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
2285 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
2286 b6135bbc Apollon Oikonomopoulos
    self.attached = True
2287 b6135bbc Apollon Oikonomopoulos
2288 b6135bbc Apollon Oikonomopoulos
    return True
2289 b6135bbc Apollon Oikonomopoulos
2290 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
2291 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
2292 b6135bbc Apollon Oikonomopoulos

2293 b6135bbc Apollon Oikonomopoulos
    """
2294 b6135bbc Apollon Oikonomopoulos
    pass
2295 b6135bbc Apollon Oikonomopoulos
2296 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
2297 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
2298 b6135bbc Apollon Oikonomopoulos

2299 b6135bbc Apollon Oikonomopoulos
    """
2300 b6135bbc Apollon Oikonomopoulos
    pass
2301 b6135bbc Apollon Oikonomopoulos
2302 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
2303 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
2304 b6135bbc Apollon Oikonomopoulos

2305 b6135bbc Apollon Oikonomopoulos
    """
2306 b6135bbc Apollon Oikonomopoulos
    pass
2307 b6135bbc Apollon Oikonomopoulos
2308 b6135bbc Apollon Oikonomopoulos
  def Close(self):
2309 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
2310 b6135bbc Apollon Oikonomopoulos

2311 b6135bbc Apollon Oikonomopoulos
    """
2312 b6135bbc Apollon Oikonomopoulos
    pass
2313 b6135bbc Apollon Oikonomopoulos
2314 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
2315 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
2316 b6135bbc Apollon Oikonomopoulos

2317 b6135bbc Apollon Oikonomopoulos
    """
2318 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2319 b6135bbc Apollon Oikonomopoulos
2320 b6135bbc Apollon Oikonomopoulos
2321 a8083063 Iustin Pop
DEV_MAP = {
2322 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2323 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2324 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
2325 a8083063 Iustin Pop
  }
2326 a8083063 Iustin Pop
2327 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
2328 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
2329 cb7c0198 Iustin Pop
2330 a8083063 Iustin Pop
2331 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
2332 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
2333 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2334 94dcbdb0 Andrea Spadaccini
2335 94dcbdb0 Andrea Spadaccini
2336 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
2337 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2338 a8083063 Iustin Pop

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

2342 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2343 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
2344 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2345 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2346 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2347 94dcbdb0 Andrea Spadaccini

2348 a8083063 Iustin Pop
  """
2349 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2350 94dcbdb0 Andrea Spadaccini
  dev_params = objects.FillDict(constants.DISK_LD_DEFAULTS[disk.dev_type],
2351 94dcbdb0 Andrea Spadaccini
                                disk.params)
2352 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
2353 94dcbdb0 Andrea Spadaccini
                                  dev_params)
2354 cb999543 Iustin Pop
  if not device.attached:
2355 a8083063 Iustin Pop
    return None
2356 ecb091e3 Iustin Pop
  return device
2357 a8083063 Iustin Pop
2358 a8083063 Iustin Pop
2359 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
2360 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2361 a8083063 Iustin Pop

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

2365 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2366 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
2367 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2368 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2369 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2370 94dcbdb0 Andrea Spadaccini

2371 a8083063 Iustin Pop
  """
2372 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2373 94dcbdb0 Andrea Spadaccini
  dev_params = objects.FillDict(constants.DISK_LD_DEFAULTS[disk.dev_type],
2374 94dcbdb0 Andrea Spadaccini
                                disk.params)
2375 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
2376 94dcbdb0 Andrea Spadaccini
                                  dev_params)
2377 1063abd1 Iustin Pop
  device.Assemble()
2378 a8083063 Iustin Pop
  return device
2379 a8083063 Iustin Pop
2380 a8083063 Iustin Pop
2381 94dcbdb0 Andrea Spadaccini
def Create(disk, children):
2382 a8083063 Iustin Pop
  """Create a device.
2383 a8083063 Iustin Pop

2384 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2385 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
2386 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2387 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2388 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2389 94dcbdb0 Andrea Spadaccini

2390 a8083063 Iustin Pop
  """
2391 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2392 94dcbdb0 Andrea Spadaccini
  dev_params = objects.FillDict(constants.DISK_LD_DEFAULTS[disk.dev_type],
2393 94dcbdb0 Andrea Spadaccini
                                disk.params)
2394 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
2395 94dcbdb0 Andrea Spadaccini
                                         dev_params)
2396 a8083063 Iustin Pop
  return device