Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 2674690b

History | View | Annotate | Download (70.5 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 b6135bbc Apollon Oikonomopoulos
import stat
28 a2cfdea2 Iustin Pop
import pyparsing as pyp
29 6f695a2e Manuel Franceschini
import os
30 468c5f77 Iustin Pop
import logging
31 a8083063 Iustin Pop
32 a8083063 Iustin Pop
from ganeti import utils
33 a8083063 Iustin Pop
from ganeti import errors
34 fe96220b Iustin Pop
from ganeti import constants
35 96acbc09 Michael Hanselmann
from ganeti import objects
36 cea881e5 Michael Hanselmann
from ganeti import compat
37 a744b676 Manuel Franceschini
from ganeti import netutils
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
40 310fbb64 Iustin Pop
# Size of reads in _CanReadDevice
41 310fbb64 Iustin Pop
_DEVICE_READ_SIZE = 128 * 1024
42 310fbb64 Iustin Pop
43 310fbb64 Iustin Pop
44 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
45 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
46 82463074 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

195 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
196 f3e513ad Iustin Pop

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

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

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

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

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

219 a8083063 Iustin Pop
    """
220 a8083063 Iustin Pop
    raise NotImplementedError
221 a8083063 Iustin Pop
222 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
223 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
224 a8083063 Iustin Pop

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

227 a8083063 Iustin Pop
    """
228 a8083063 Iustin Pop
    result = True
229 a8083063 Iustin Pop
    if self._children:
230 a8083063 Iustin Pop
      for child in self._children:
231 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
232 a8083063 Iustin Pop
    return result
233 a8083063 Iustin Pop
234 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
235 a3fffcc6 René Nussbaumer
    """Pause/Resume the sync of the mirror.
236 a3fffcc6 René Nussbaumer

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

239 a3fffcc6 René Nussbaumer
    @param pause: Wheater to pause or resume
240 a3fffcc6 René Nussbaumer

241 a3fffcc6 René Nussbaumer
    """
242 a3fffcc6 René Nussbaumer
    result = True
243 a3fffcc6 René Nussbaumer
    if self._children:
244 a3fffcc6 René Nussbaumer
      for child in self._children:
245 a3fffcc6 René Nussbaumer
        result = result and child.PauseResumeSync(pause)
246 a3fffcc6 René Nussbaumer
    return result
247 a3fffcc6 René Nussbaumer
248 a8083063 Iustin Pop
  def GetSyncStatus(self):
249 a8083063 Iustin Pop
    """Returns the sync status of the device.
250 a8083063 Iustin Pop

251 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
252 a8083063 Iustin Pop
    status of the mirror.
253 a8083063 Iustin Pop

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

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

259 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
260 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
261 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
262 a8083063 Iustin Pop

263 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
264 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
265 0834c866 Iustin Pop
    return False (not degraded).
266 0834c866 Iustin Pop

267 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
268 c41eea6e Iustin Pop

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

281 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
282 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
283 a8083063 Iustin Pop
    children.
284 a8083063 Iustin Pop

285 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
286 96acbc09 Michael Hanselmann

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

328 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
329 a0c3fea1 Michael Hanselmann

330 a0c3fea1 Michael Hanselmann
    """
331 a0c3fea1 Michael Hanselmann
    for child in self._children:
332 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
333 a0c3fea1 Michael Hanselmann
334 1005d816 Iustin Pop
  def Grow(self, amount):
335 1005d816 Iustin Pop
    """Grow the block device.
336 1005d816 Iustin Pop

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

339 1005d816 Iustin Pop
    """
340 1005d816 Iustin Pop
    raise NotImplementedError
341 a0c3fea1 Michael Hanselmann
342 fcff3897 Iustin Pop
  def GetActualSize(self):
343 fcff3897 Iustin Pop
    """Return the actual disk size.
344 fcff3897 Iustin Pop

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

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

368 a8083063 Iustin Pop
  """
369 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
370 6136f8f0 Iustin Pop
  _INVALID_NAMES = frozenset([".", "..", "snapshot", "pvmove"])
371 6136f8f0 Iustin Pop
  _INVALID_SUBSTRINGS = frozenset(["_mlog", "_mimage"])
372 6136f8f0 Iustin Pop
373 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
374 a8083063 Iustin Pop
    """Attaches to a LV device.
375 a8083063 Iustin Pop

376 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
377 a8083063 Iustin Pop

378 a8083063 Iustin Pop
    """
379 464f8daf Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children, size)
380 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
381 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
382 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
383 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
384 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
385 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
386 99e8295c Iustin Pop
    self._degraded = True
387 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
388 a8083063 Iustin Pop
    self.Attach()
389 a8083063 Iustin Pop
390 a8083063 Iustin Pop
  @classmethod
391 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
392 a8083063 Iustin Pop
    """Create a new logical volume.
393 a8083063 Iustin Pop

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

439 197478f2 René Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
440 197478f2 René Nussbaumer
    @param fields: Fields to return
441 197478f2 René Nussbaumer
    @return: A list of dicts each with the parsed fields
442 197478f2 René Nussbaumer

443 197478f2 René Nussbaumer
    """
444 197478f2 René Nussbaumer
    if not fields:
445 197478f2 René Nussbaumer
      raise errors.ProgrammerError("No fields specified")
446 197478f2 René Nussbaumer
447 197478f2 René Nussbaumer
    sep = "|"
448 197478f2 René Nussbaumer
    cmd = [lvm_cmd, "--noheadings", "--nosuffix", "--units=m", "--unbuffered",
449 197478f2 René Nussbaumer
           "--separator=%s" % sep, "-o%s" % ",".join(fields)]
450 197478f2 René Nussbaumer
451 197478f2 René Nussbaumer
    result = utils.RunCmd(cmd)
452 197478f2 René Nussbaumer
    if result.failed:
453 197478f2 René Nussbaumer
      raise errors.CommandError("Can't get the volume information: %s - %s" %
454 197478f2 René Nussbaumer
                                (result.fail_reason, result.output))
455 197478f2 René Nussbaumer
456 197478f2 René Nussbaumer
    data = []
457 197478f2 René Nussbaumer
    for line in result.stdout.splitlines():
458 197478f2 René Nussbaumer
      splitted_fields = line.strip().split(sep)
459 197478f2 René Nussbaumer
460 197478f2 René Nussbaumer
      if len(fields) != len(splitted_fields):
461 197478f2 René Nussbaumer
        raise errors.CommandError("Can't parse %s output: line '%s'" %
462 197478f2 René Nussbaumer
                                  (lvm_cmd, line))
463 197478f2 René Nussbaumer
464 197478f2 René Nussbaumer
      data.append(splitted_fields)
465 197478f2 René Nussbaumer
466 197478f2 René Nussbaumer
    return data
467 197478f2 René Nussbaumer
468 197478f2 René Nussbaumer
  @classmethod
469 197478f2 René Nussbaumer
  def GetPVInfo(cls, vg_names, filter_allocatable=True):
470 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
471 a8083063 Iustin Pop

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

475 c41eea6e Iustin Pop
    @rtype: list
476 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
477 098c0958 Michael Hanselmann

478 a8083063 Iustin Pop
    """
479 197478f2 René Nussbaumer
    try:
480 197478f2 René Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
481 197478f2 René Nussbaumer
                                        "pv_attr"])
482 197478f2 René Nussbaumer
    except errors.GenericError, err:
483 197478f2 René Nussbaumer
      logging.error("Can't get PV information: %s", err)
484 a8083063 Iustin Pop
      return None
485 197478f2 René Nussbaumer
486 a8083063 Iustin Pop
    data = []
487 197478f2 René Nussbaumer
    for pv_name, vg_name, pv_free, pv_attr in info:
488 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
489 197478f2 René Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
490 a8083063 Iustin Pop
        continue
491 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
492 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
493 2070598f Iustin Pop
        continue
494 197478f2 René Nussbaumer
      data.append((float(pv_free), pv_name, vg_name))
495 197478f2 René Nussbaumer
496 197478f2 René Nussbaumer
    return data
497 197478f2 René Nussbaumer
498 197478f2 René Nussbaumer
  @classmethod
499 197478f2 René Nussbaumer
  def GetVGInfo(cls, vg_names, filter_readonly=True):
500 197478f2 René Nussbaumer
    """Get the free space info for specific VGs.
501 197478f2 René Nussbaumer

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

505 197478f2 René Nussbaumer
    @rtype: list
506 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
507 673cd9c4 René Nussbaumer
             MiB
508 197478f2 René Nussbaumer

509 197478f2 René Nussbaumer
    """
510 197478f2 René Nussbaumer
    try:
511 673cd9c4 René Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
512 673cd9c4 René Nussbaumer
                                        "vg_size"])
513 197478f2 René Nussbaumer
    except errors.GenericError, err:
514 197478f2 René Nussbaumer
      logging.error("Can't get VG information: %s", err)
515 197478f2 René Nussbaumer
      return None
516 197478f2 René Nussbaumer
517 197478f2 René Nussbaumer
    data = []
518 673cd9c4 René Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
519 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not writable
520 197478f2 René Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
521 197478f2 René Nussbaumer
        continue
522 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
523 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
524 197478f2 René Nussbaumer
        continue
525 673cd9c4 René Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
526 a8083063 Iustin Pop
527 a8083063 Iustin Pop
    return data
528 a8083063 Iustin Pop
529 6136f8f0 Iustin Pop
  @classmethod
530 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
531 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
532 6136f8f0 Iustin Pop

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

537 6136f8f0 Iustin Pop
    """
538 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
539 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
540 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
541 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
542 6136f8f0 Iustin Pop
543 a8083063 Iustin Pop
  def Remove(self):
544 a8083063 Iustin Pop
    """Remove this logical volume.
545 a8083063 Iustin Pop

546 a8083063 Iustin Pop
    """
547 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
548 a8083063 Iustin Pop
      # the LV does not exist
549 0c6c04ec Iustin Pop
      return
550 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
551 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
552 a8083063 Iustin Pop
    if result.failed:
553 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
554 a8083063 Iustin Pop
555 f3e513ad Iustin Pop
  def Rename(self, new_id):
556 f3e513ad Iustin Pop
    """Rename this logical volume.
557 f3e513ad Iustin Pop

558 f3e513ad Iustin Pop
    """
559 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
560 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
561 f3e513ad Iustin Pop
    new_vg, new_name = new_id
562 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
563 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
564 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
565 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
566 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
567 f3e513ad Iustin Pop
    if result.failed:
568 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
569 be345db0 Iustin Pop
    self._lv_name = new_name
570 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
571 be345db0 Iustin Pop
572 a8083063 Iustin Pop
  def Attach(self):
573 a8083063 Iustin Pop
    """Attach to an existing LV.
574 a8083063 Iustin Pop

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

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

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

644 a8083063 Iustin Pop
    """
645 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
646 5574047a Iustin Pop
    if result.failed:
647 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
648 a8083063 Iustin Pop
649 a8083063 Iustin Pop
  def Shutdown(self):
650 a8083063 Iustin Pop
    """Shutdown the device.
651 a8083063 Iustin Pop

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

655 a8083063 Iustin Pop
    """
656 746f7476 Iustin Pop
    pass
657 a8083063 Iustin Pop
658 9db6dbce Iustin Pop
  def GetSyncStatus(self):
659 9db6dbce Iustin Pop
    """Returns the sync status of the device.
660 9db6dbce Iustin Pop

661 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
662 9db6dbce Iustin Pop
    status of the mirror.
663 9db6dbce Iustin Pop

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

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

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

677 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
678 c41eea6e Iustin Pop

679 9db6dbce Iustin Pop
    """
680 f208978a Michael Hanselmann
    if self._degraded:
681 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
682 f208978a Michael Hanselmann
    else:
683 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
684 f208978a Michael Hanselmann
685 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
686 96acbc09 Michael Hanselmann
                                  major=self.major,
687 96acbc09 Michael Hanselmann
                                  minor=self.minor,
688 96acbc09 Michael Hanselmann
                                  sync_percent=None,
689 96acbc09 Michael Hanselmann
                                  estimated_time=None,
690 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
691 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
692 9db6dbce Iustin Pop
693 a8083063 Iustin Pop
  def Open(self, force=False):
694 a8083063 Iustin Pop
    """Make the device ready for I/O.
695 a8083063 Iustin Pop

696 a8083063 Iustin Pop
    This is a no-op for the LV device type.
697 a8083063 Iustin Pop

698 a8083063 Iustin Pop
    """
699 fdbd668d Iustin Pop
    pass
700 a8083063 Iustin Pop
701 a8083063 Iustin Pop
  def Close(self):
702 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
703 a8083063 Iustin Pop

704 a8083063 Iustin Pop
    This is a no-op for the LV device type.
705 a8083063 Iustin Pop

706 a8083063 Iustin Pop
    """
707 fdbd668d Iustin Pop
    pass
708 a8083063 Iustin Pop
709 a8083063 Iustin Pop
  def Snapshot(self, size):
710 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
711 a8083063 Iustin Pop

712 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
713 800ac399 Iustin Pop

714 a8083063 Iustin Pop
    """
715 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
716 a8083063 Iustin Pop
717 a8083063 Iustin Pop
    # remove existing snapshot if found
718 464f8daf Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None, size)
719 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
720 a8083063 Iustin Pop
721 197478f2 René Nussbaumer
    vg_info = self.GetVGInfo([self._vg_name])
722 197478f2 René Nussbaumer
    if not vg_info:
723 197478f2 René Nussbaumer
      _ThrowError("Can't compute VG info for vg %s", self._vg_name)
724 673cd9c4 René Nussbaumer
    free_size, _, _ = vg_info[0]
725 a8083063 Iustin Pop
    if free_size < size:
726 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
727 82463074 Iustin Pop
                  " available %s", size, free_size)
728 a8083063 Iustin Pop
729 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
730 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
731 a8083063 Iustin Pop
    if result.failed:
732 82463074 Iustin Pop
      _ThrowError("command: %s error: %s - %s",
733 82463074 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
734 a8083063 Iustin Pop
735 800ac399 Iustin Pop
    return (self._vg_name, snap_name)
736 a8083063 Iustin Pop
737 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
738 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
739 a0c3fea1 Michael Hanselmann

740 a0c3fea1 Michael Hanselmann
    """
741 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
742 a0c3fea1 Michael Hanselmann
743 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
744 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
745 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
746 a0c3fea1 Michael Hanselmann
747 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
748 a0c3fea1 Michael Hanselmann
    text = text[:128]
749 a0c3fea1 Michael Hanselmann
750 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
751 a0c3fea1 Michael Hanselmann
                           self.dev_path])
752 a0c3fea1 Michael Hanselmann
    if result.failed:
753 82463074 Iustin Pop
      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
754 82463074 Iustin Pop
                  result.output)
755 82463074 Iustin Pop
756 1005d816 Iustin Pop
  def Grow(self, amount):
757 1005d816 Iustin Pop
    """Grow the logical volume.
758 1005d816 Iustin Pop

759 1005d816 Iustin Pop
    """
760 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
761 38256320 Iustin Pop
      if not self.Attach():
762 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
763 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
764 38256320 Iustin Pop
    rest = amount % full_stripe_size
765 38256320 Iustin Pop
    if rest != 0:
766 38256320 Iustin Pop
      amount += full_stripe_size - rest
767 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
768 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
769 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
770 1005d816 Iustin Pop
    # supports 'cling'
771 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
772 1005d816 Iustin Pop
      result = utils.RunCmd(["lvextend", "--alloc", alloc_policy,
773 1005d816 Iustin Pop
                             "-L", "+%dm" % amount, self.dev_path])
774 1005d816 Iustin Pop
      if not result.failed:
775 1005d816 Iustin Pop
        return
776 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
777 a0c3fea1 Michael Hanselmann
778 a0c3fea1 Michael Hanselmann
779 6b90c22e Iustin Pop
class DRBD8Status(object):
780 6b90c22e Iustin Pop
  """A DRBD status representation class.
781 6b90c22e Iustin Pop

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

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

887 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
888 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
889 0f7f32d9 Iustin Pop

890 abdf0113 Iustin Pop
  """
891 fcee765d Manuel Franceschini
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)(?:\.\d+)?"
892 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
893 9122e60a Iustin Pop
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
894 9122e60a Iustin Pop
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
895 a8083063 Iustin Pop
896 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
897 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
898 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
899 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
900 a8083063 Iustin Pop
901 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
902 549071a0 Luca Bigliardi
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
903 6b90c22e Iustin Pop
904 abdf0113 Iustin Pop
  @staticmethod
905 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
906 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
907 a8083063 Iustin Pop

908 a8083063 Iustin Pop
    """
909 abdf0113 Iustin Pop
    try:
910 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
911 f6eaed12 Iustin Pop
    except EnvironmentError, err:
912 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
913 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
914 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
915 f6eaed12 Iustin Pop
      else:
916 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
917 abdf0113 Iustin Pop
    if not data:
918 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
919 abdf0113 Iustin Pop
    return data
920 a8083063 Iustin Pop
921 9122e60a Iustin Pop
  @classmethod
922 9122e60a Iustin Pop
  def _MassageProcData(cls, data):
923 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
924 a8083063 Iustin Pop

925 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
926 c41eea6e Iustin Pop
        for that minor
927 a8083063 Iustin Pop

928 a8083063 Iustin Pop
    """
929 abdf0113 Iustin Pop
    results = {}
930 abdf0113 Iustin Pop
    old_minor = old_line = None
931 abdf0113 Iustin Pop
    for line in data:
932 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
933 67d101d4 Iustin Pop
        continue
934 9122e60a Iustin Pop
      lresult = cls._VALID_LINE_RE.match(line)
935 abdf0113 Iustin Pop
      if lresult is not None:
936 abdf0113 Iustin Pop
        if old_minor is not None:
937 abdf0113 Iustin Pop
          results[old_minor] = old_line
938 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
939 abdf0113 Iustin Pop
        old_line = line
940 abdf0113 Iustin Pop
      else:
941 abdf0113 Iustin Pop
        if old_minor is not None:
942 abdf0113 Iustin Pop
          old_line += " " + line.strip()
943 abdf0113 Iustin Pop
    # add last line
944 abdf0113 Iustin Pop
    if old_minor is not None:
945 abdf0113 Iustin Pop
      results[old_minor] = old_line
946 abdf0113 Iustin Pop
    return results
947 a8083063 Iustin Pop
948 abdf0113 Iustin Pop
  @classmethod
949 fcee765d Manuel Franceschini
  def _GetVersion(cls, proc_data):
950 abdf0113 Iustin Pop
    """Return the DRBD version.
951 a8083063 Iustin Pop

952 abdf0113 Iustin Pop
    This will return a dict with keys:
953 c41eea6e Iustin Pop
      - k_major
954 c41eea6e Iustin Pop
      - k_minor
955 c41eea6e Iustin Pop
      - k_point
956 c41eea6e Iustin Pop
      - api
957 c41eea6e Iustin Pop
      - proto
958 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
959 a8083063 Iustin Pop

960 a8083063 Iustin Pop
    """
961 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
962 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
963 abdf0113 Iustin Pop
    if not version:
964 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
965 abdf0113 Iustin Pop
                                    first_line)
966 a8083063 Iustin Pop
967 abdf0113 Iustin Pop
    values = version.groups()
968 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
969 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
970 abdf0113 Iustin Pop
              'k_point': int(values[2]),
971 abdf0113 Iustin Pop
              'api': int(values[3]),
972 abdf0113 Iustin Pop
              'proto': int(values[4]),
973 abdf0113 Iustin Pop
             }
974 abdf0113 Iustin Pop
    if values[5] is not None:
975 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
976 a8083063 Iustin Pop
977 abdf0113 Iustin Pop
    return retval
978 abdf0113 Iustin Pop
979 abdf0113 Iustin Pop
  @staticmethod
980 549071a0 Luca Bigliardi
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
981 549071a0 Luca Bigliardi
    """Returns DRBD usermode_helper currently set.
982 549071a0 Luca Bigliardi

983 549071a0 Luca Bigliardi
    """
984 549071a0 Luca Bigliardi
    try:
985 549071a0 Luca Bigliardi
      helper = utils.ReadFile(filename).splitlines()[0]
986 549071a0 Luca Bigliardi
    except EnvironmentError, err:
987 549071a0 Luca Bigliardi
      if err.errno == errno.ENOENT:
988 549071a0 Luca Bigliardi
        _ThrowError("The file %s cannot be opened, check if the module"
989 549071a0 Luca Bigliardi
                    " is loaded (%s)", filename, str(err))
990 549071a0 Luca Bigliardi
      else:
991 549071a0 Luca Bigliardi
        _ThrowError("Can't read DRBD helper file %s: %s", filename, str(err))
992 549071a0 Luca Bigliardi
    if not helper:
993 549071a0 Luca Bigliardi
      _ThrowError("Can't read any data from %s", filename)
994 549071a0 Luca Bigliardi
    return helper
995 549071a0 Luca Bigliardi
996 549071a0 Luca Bigliardi
  @staticmethod
997 abdf0113 Iustin Pop
  def _DevPath(minor):
998 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
999 a8083063 Iustin Pop

1000 a8083063 Iustin Pop
    """
1001 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
1002 a8083063 Iustin Pop
1003 abdf0113 Iustin Pop
  @classmethod
1004 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
1005 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
1006 a8083063 Iustin Pop

1007 a8083063 Iustin Pop
    """
1008 abdf0113 Iustin Pop
    data = cls._GetProcData()
1009 a8083063 Iustin Pop
1010 abdf0113 Iustin Pop
    used_devs = {}
1011 abdf0113 Iustin Pop
    for line in data:
1012 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1013 abdf0113 Iustin Pop
      if not match:
1014 abdf0113 Iustin Pop
        continue
1015 abdf0113 Iustin Pop
      minor = int(match.group(1))
1016 abdf0113 Iustin Pop
      state = match.group(2)
1017 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1018 abdf0113 Iustin Pop
        continue
1019 abdf0113 Iustin Pop
      used_devs[minor] = state, line
1020 a8083063 Iustin Pop
1021 abdf0113 Iustin Pop
    return used_devs
1022 a8083063 Iustin Pop
1023 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
1024 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
1025 0834c866 Iustin Pop

1026 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1027 a8083063 Iustin Pop

1028 a8083063 Iustin Pop
    """
1029 abdf0113 Iustin Pop
    if minor is None:
1030 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
1031 cb999543 Iustin Pop
      self.attached = False
1032 a8083063 Iustin Pop
    else:
1033 abdf0113 Iustin Pop
      self.minor = minor
1034 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
1035 cb999543 Iustin Pop
      self.attached = True
1036 a8083063 Iustin Pop
1037 a8083063 Iustin Pop
  @staticmethod
1038 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
1039 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
1040 a8083063 Iustin Pop

1041 abdf0113 Iustin Pop
    This currently only check the size, which must be around
1042 abdf0113 Iustin Pop
    128MiB.
1043 a8083063 Iustin Pop

1044 a8083063 Iustin Pop
    """
1045 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1046 abdf0113 Iustin Pop
    if result.failed:
1047 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
1048 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
1049 a8083063 Iustin Pop
    try:
1050 abdf0113 Iustin Pop
      sectors = int(result.stdout)
1051 691744c4 Iustin Pop
    except (TypeError, ValueError):
1052 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
1053 c04bc777 Iustin Pop
    num_bytes = sectors * 512
1054 c04bc777 Iustin Pop
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
1055 c04bc777 Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (num_bytes / 1024 / 1024))
1056 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
1057 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
1058 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
1059 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
1060 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
1061 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
1062 c04bc777 Iustin Pop
    if num_bytes > 1024 * 1024 * 1024:
1063 c04bc777 Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (num_bytes / 1024 / 1024))
1064 a8083063 Iustin Pop
1065 abdf0113 Iustin Pop
  def Rename(self, new_id):
1066 abdf0113 Iustin Pop
    """Rename a device.
1067 a8083063 Iustin Pop

1068 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1069 a8083063 Iustin Pop

1070 a8083063 Iustin Pop
    """
1071 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1072 a8083063 Iustin Pop
1073 f3e513ad Iustin Pop
1074 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1075 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1076 a2cfdea2 Iustin Pop

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

1081 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1082 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1083 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1084 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1085 a2cfdea2 Iustin Pop

1086 a2cfdea2 Iustin Pop
  """
1087 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1088 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1089 a2cfdea2 Iustin Pop
1090 cf8df3f3 Iustin Pop
  # timeout constants
1091 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
1092 cf8df3f3 Iustin Pop
1093 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1094 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1095 fc1dc9d7 Iustin Pop
      children = []
1096 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
1097 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1098 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
1099 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1100 310fbb64 Iustin Pop
    (self._lhost, self._lport,
1101 310fbb64 Iustin Pop
     self._rhost, self._rport,
1102 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
1103 310fbb64 Iustin Pop
    if children:
1104 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
1105 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
1106 310fbb64 Iustin Pop
        children = []
1107 464f8daf Iustin Pop
    super(DRBD8, self).__init__(unique_id, children, size)
1108 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1109 fcee765d Manuel Franceschini
    version = self._GetVersion(self._GetProcData())
1110 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
1111 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
1112 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
1113 82463074 Iustin Pop
                  version['k_major'], version['k_minor'])
1114 a2cfdea2 Iustin Pop
1115 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1116 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1117 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1118 ffa1c0dc Iustin Pop
                       (unique_id,))
1119 a2cfdea2 Iustin Pop
    self.Attach()
1120 a2cfdea2 Iustin Pop
1121 a2cfdea2 Iustin Pop
  @classmethod
1122 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1123 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1124 a2cfdea2 Iustin Pop

1125 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1126 a2cfdea2 Iustin Pop

1127 a2cfdea2 Iustin Pop
    """
1128 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1129 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1130 a2cfdea2 Iustin Pop
    if result.failed:
1131 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1132 a2cfdea2 Iustin Pop
1133 a2cfdea2 Iustin Pop
  @classmethod
1134 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1135 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1136 a2cfdea2 Iustin Pop

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

1140 a2cfdea2 Iustin Pop
    """
1141 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1142 a2cfdea2 Iustin Pop
1143 a2cfdea2 Iustin Pop
    highest = None
1144 a2cfdea2 Iustin Pop
    for line in data:
1145 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1146 a2cfdea2 Iustin Pop
      if match:
1147 a2cfdea2 Iustin Pop
        return int(match.group(1))
1148 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1149 a2cfdea2 Iustin Pop
      if match:
1150 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1151 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1152 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1153 a2cfdea2 Iustin Pop
      return 0
1154 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1155 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1156 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1157 a2cfdea2 Iustin Pop
    return highest + 1
1158 a2cfdea2 Iustin Pop
1159 a2cfdea2 Iustin Pop
  @classmethod
1160 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1161 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1162 a2cfdea2 Iustin Pop

1163 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1164 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1165 a2cfdea2 Iustin Pop

1166 a2cfdea2 Iustin Pop
    """
1167 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1168 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1169 a2cfdea2 Iustin Pop
1170 a2cfdea2 Iustin Pop
    # pyparsing setup
1171 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1172 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1173 5a672c30 Manuel Franceschini
    lbracket = pyp.Literal("[").suppress()
1174 5a672c30 Manuel Franceschini
    rbracket = pyp.Literal("]").suppress()
1175 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1176 5a672c30 Manuel Franceschini
    colon = pyp.Literal(":").suppress()
1177 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1178 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1179 a2cfdea2 Iustin Pop
1180 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1181 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1182 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1183 a2cfdea2 Iustin Pop
1184 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1185 a2cfdea2 Iustin Pop
1186 a2cfdea2 Iustin Pop
    # value types
1187 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1188 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1189 5a672c30 Manuel Franceschini
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1190 5a672c30 Manuel Franceschini
                 pyp.Word(pyp.nums + ".") + colon + number)
1191 5a672c30 Manuel Franceschini
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
1192 5a672c30 Manuel Franceschini
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
1193 5a672c30 Manuel Franceschini
                 pyp.Optional(rbracket) + colon + number)
1194 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1195 5a672c30 Manuel Franceschini
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
1196 01e2ce3a Iustin Pop
    # device name, extended syntax
1197 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1198 a2cfdea2 Iustin Pop
1199 a2cfdea2 Iustin Pop
    # a statement
1200 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1201 5a672c30 Manuel Franceschini
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
1202 01e2ce3a Iustin Pop
                         device_value) +
1203 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1204 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1205 a2cfdea2 Iustin Pop
1206 a2cfdea2 Iustin Pop
    # an entire section
1207 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1208 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1209 a2cfdea2 Iustin Pop
1210 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1211 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1212 a2cfdea2 Iustin Pop
1213 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1214 a2cfdea2 Iustin Pop
1215 a2cfdea2 Iustin Pop
    return bnf
1216 a2cfdea2 Iustin Pop
1217 a2cfdea2 Iustin Pop
  @classmethod
1218 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1219 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1220 a2cfdea2 Iustin Pop

1221 a2cfdea2 Iustin Pop
    """
1222 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1223 a2cfdea2 Iustin Pop
    if result.failed:
1224 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1225 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1226 3840729d Iustin Pop
      return None
1227 3840729d Iustin Pop
    return result.stdout
1228 3840729d Iustin Pop
1229 3840729d Iustin Pop
  @classmethod
1230 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1231 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1232 3840729d Iustin Pop

1233 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1234 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1235 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1236 3840729d Iustin Pop
    by _GetShowData.
1237 3840729d Iustin Pop

1238 3840729d Iustin Pop
    """
1239 3840729d Iustin Pop
    data = {}
1240 a2cfdea2 Iustin Pop
    if not out:
1241 a2cfdea2 Iustin Pop
      return data
1242 a2cfdea2 Iustin Pop
1243 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1244 a2cfdea2 Iustin Pop
    # run pyparse
1245 a2cfdea2 Iustin Pop
1246 a2cfdea2 Iustin Pop
    try:
1247 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1248 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1249 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1250 a2cfdea2 Iustin Pop
1251 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1252 a2cfdea2 Iustin Pop
    for section in results:
1253 a2cfdea2 Iustin Pop
      sname = section[0]
1254 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1255 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1256 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1257 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1258 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1259 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1260 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1261 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1262 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1263 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1264 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1265 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1266 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1267 a2cfdea2 Iustin Pop
    return data
1268 a2cfdea2 Iustin Pop
1269 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1270 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1271 a2cfdea2 Iustin Pop

1272 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1273 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1274 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1275 a2cfdea2 Iustin Pop
    device.
1276 a2cfdea2 Iustin Pop

1277 a2cfdea2 Iustin Pop
    """
1278 b00b95dd Iustin Pop
    if self._children:
1279 b00b95dd Iustin Pop
      backend, meta = self._children
1280 b00b95dd Iustin Pop
    else:
1281 b00b95dd Iustin Pop
      backend = meta = None
1282 b00b95dd Iustin Pop
1283 a2cfdea2 Iustin Pop
    if backend is not None:
1284 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1285 a2cfdea2 Iustin Pop
    else:
1286 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1287 b00b95dd Iustin Pop
1288 a2cfdea2 Iustin Pop
    if meta is not None:
1289 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1290 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1291 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1292 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1293 a2cfdea2 Iustin Pop
    else:
1294 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1295 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1296 a2cfdea2 Iustin Pop
    return retval
1297 a2cfdea2 Iustin Pop
1298 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1299 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1300 a2cfdea2 Iustin Pop

1301 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1302 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1303 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1304 a2cfdea2 Iustin Pop
    device.
1305 a2cfdea2 Iustin Pop

1306 a2cfdea2 Iustin Pop
    """
1307 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1308 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1309 a2cfdea2 Iustin Pop
      return True
1310 a2cfdea2 Iustin Pop
1311 a2cfdea2 Iustin Pop
    if self._lhost is None:
1312 a2cfdea2 Iustin Pop
      return False
1313 a2cfdea2 Iustin Pop
1314 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1315 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1316 a2cfdea2 Iustin Pop
      return False
1317 a2cfdea2 Iustin Pop
1318 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1319 a2cfdea2 Iustin Pop
    retval = (retval and
1320 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1321 a2cfdea2 Iustin Pop
    return retval
1322 a2cfdea2 Iustin Pop
1323 a2cfdea2 Iustin Pop
  @classmethod
1324 f069addf Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta, size):
1325 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1326 a2cfdea2 Iustin Pop

1327 a2cfdea2 Iustin Pop
    """
1328 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1329 f069addf Iustin Pop
            backend, meta, "0",
1330 f069addf Iustin Pop
            "-e", "detach",
1331 f069addf Iustin Pop
            "--create-device"]
1332 60bca04a Iustin Pop
    if size:
1333 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1334 89b70f39 Iustin Pop
    if not constants.DRBD_BARRIERS: # disable barriers, if configured so
1335 fcee765d Manuel Franceschini
      version = cls._GetVersion(cls._GetProcData())
1336 89b70f39 Iustin Pop
      # various DRBD versions support different disk barrier options;
1337 89b70f39 Iustin Pop
      # what we aim here is to revert back to the 'drain' method of
1338 89b70f39 Iustin Pop
      # disk flushes and to disable metadata barriers, in effect going
1339 89b70f39 Iustin Pop
      # back to pre-8.0.7 behaviour
1340 89b70f39 Iustin Pop
      vmaj = version['k_major']
1341 89b70f39 Iustin Pop
      vmin = version['k_minor']
1342 89b70f39 Iustin Pop
      vrel = version['k_point']
1343 89b70f39 Iustin Pop
      assert vmaj == 8
1344 89b70f39 Iustin Pop
      if vmin == 0: # 8.0.x
1345 89b70f39 Iustin Pop
        if vrel >= 12:
1346 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1347 89b70f39 Iustin Pop
      elif vmin == 2: # 8.2.x
1348 89b70f39 Iustin Pop
        if vrel >= 7:
1349 89b70f39 Iustin Pop
          args.extend(['-i', '-m'])
1350 89b70f39 Iustin Pop
      elif vmaj >= 3: # 8.3.x or newer
1351 89b70f39 Iustin Pop
        args.extend(['-i', '-a', 'm'])
1352 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1353 a2cfdea2 Iustin Pop
    if result.failed:
1354 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1355 a2cfdea2 Iustin Pop
1356 a2cfdea2 Iustin Pop
  @classmethod
1357 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1358 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1359 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1360 a2cfdea2 Iustin Pop

1361 a2cfdea2 Iustin Pop
    """
1362 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1363 52857176 Iustin Pop
    if None in net_info:
1364 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1365 52857176 Iustin Pop
      # sure its shutdown
1366 1063abd1 Iustin Pop
      cls._ShutdownNet(minor)
1367 1063abd1 Iustin Pop
      return
1368 52857176 Iustin Pop
1369 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1370 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1371 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1372 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1373 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1374 7d585316 Iustin Pop
    # about its peer.
1375 7d585316 Iustin Pop
    cls._SetMinorSyncSpeed(minor, constants.SYNC_SPEED)
1376 7d585316 Iustin Pop
1377 8b312c1d Manuel Franceschini
    if netutils.IP6Address.IsValid(lhost):
1378 8b312c1d Manuel Franceschini
      if not netutils.IP6Address.IsValid(rhost):
1379 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1380 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1381 5a672c30 Manuel Franceschini
      family = "ipv6"
1382 8b312c1d Manuel Franceschini
    elif netutils.IP4Address.IsValid(lhost):
1383 8b312c1d Manuel Franceschini
      if not netutils.IP4Address.IsValid(rhost):
1384 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1385 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1386 5a672c30 Manuel Franceschini
      family = "ipv4"
1387 5a672c30 Manuel Franceschini
    else:
1388 5a672c30 Manuel Franceschini
      _ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
1389 5a672c30 Manuel Franceschini
1390 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1391 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, lhost, lport),
1392 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, rhost, rport), protocol,
1393 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1394 f38478b2 Iustin Pop
            "-B", "consensus",
1395 ab6cc81c Iustin Pop
            "--create-device",
1396 f38478b2 Iustin Pop
            ]
1397 a2cfdea2 Iustin Pop
    if dual_pri:
1398 a2cfdea2 Iustin Pop
      args.append("-m")
1399 a2cfdea2 Iustin Pop
    if hmac and secret:
1400 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1401 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1402 a2cfdea2 Iustin Pop
    if result.failed:
1403 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1404 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1405 a2cfdea2 Iustin Pop
1406 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1407 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1408 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1409 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1410 def8e2f6 Michael Hanselmann
1411 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1412 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1413 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1414 def8e2f6 Michael Hanselmann
1415 def8e2f6 Michael Hanselmann
    try:
1416 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1417 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1418 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1419 a2cfdea2 Iustin Pop
1420 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1421 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1422 b00b95dd Iustin Pop

1423 b00b95dd Iustin Pop
    """
1424 b00b95dd Iustin Pop
    if self.minor is None:
1425 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1426 82463074 Iustin Pop
                  self._aminor)
1427 b00b95dd Iustin Pop
    if len(devices) != 2:
1428 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1429 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1430 03ece5f3 Iustin Pop
    if "local_dev" in info:
1431 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1432 b00b95dd Iustin Pop
    backend, meta = devices
1433 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1434 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1435 b00b95dd Iustin Pop
    backend.Open()
1436 b00b95dd Iustin Pop
    meta.Open()
1437 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1438 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1439 b00b95dd Iustin Pop
1440 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1441 b00b95dd Iustin Pop
    self._children = devices
1442 b00b95dd Iustin Pop
1443 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1444 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1445 b00b95dd Iustin Pop

1446 b00b95dd Iustin Pop
    """
1447 b00b95dd Iustin Pop
    if self.minor is None:
1448 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1449 82463074 Iustin Pop
                  self._aminor)
1450 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1451 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1452 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1453 03ece5f3 Iustin Pop
      return
1454 b00b95dd Iustin Pop
    if len(self._children) != 2:
1455 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1456 82463074 Iustin Pop
                  self._children)
1457 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1458 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1459 e739bd57 Iustin Pop
      return
1460 b00b95dd Iustin Pop
    if len(devices) != 2:
1461 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1462 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1463 e739bd57 Iustin Pop
      if dev != child.dev_path:
1464 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1465 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1466 b00b95dd Iustin Pop
1467 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1468 b00b95dd Iustin Pop
    self._children = []
1469 b00b95dd Iustin Pop
1470 7d585316 Iustin Pop
  @classmethod
1471 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1472 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1473 a2cfdea2 Iustin Pop

1474 7d585316 Iustin Pop
    This is the low-level implementation.
1475 7d585316 Iustin Pop

1476 7d585316 Iustin Pop
    @type minor: int
1477 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1478 7d585316 Iustin Pop
    @type kbytes: int
1479 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1480 7d585316 Iustin Pop
    @rtype: boolean
1481 7d585316 Iustin Pop
    @return: the success of the operation
1482 7d585316 Iustin Pop

1483 a2cfdea2 Iustin Pop
    """
1484 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1485 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1486 a2cfdea2 Iustin Pop
    if result.failed:
1487 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1488 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1489 7d585316 Iustin Pop
    return not result.failed
1490 7d585316 Iustin Pop
1491 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1492 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1493 7d585316 Iustin Pop

1494 7d585316 Iustin Pop
    @type kbytes: int
1495 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1496 7d585316 Iustin Pop
    @rtype: boolean
1497 7d585316 Iustin Pop
    @return: the success of the operation
1498 7d585316 Iustin Pop

1499 7d585316 Iustin Pop
    """
1500 7d585316 Iustin Pop
    if self.minor is None:
1501 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1502 7d585316 Iustin Pop
      return False
1503 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1504 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1505 a2cfdea2 Iustin Pop
1506 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
1507 a3fffcc6 René Nussbaumer
    """Pauses or resumes the sync of a DRBD device.
1508 a3fffcc6 René Nussbaumer

1509 a3fffcc6 René Nussbaumer
    @param pause: Wether to pause or resume
1510 a3fffcc6 René Nussbaumer
    @return: the success of the operation
1511 a3fffcc6 René Nussbaumer

1512 a3fffcc6 René Nussbaumer
    """
1513 a3fffcc6 René Nussbaumer
    if self.minor is None:
1514 a3fffcc6 René Nussbaumer
      logging.info("Not attached during PauseSync")
1515 a3fffcc6 René Nussbaumer
      return False
1516 a3fffcc6 René Nussbaumer
1517 a3fffcc6 René Nussbaumer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
1518 a3fffcc6 René Nussbaumer
1519 a3fffcc6 René Nussbaumer
    if pause:
1520 a3fffcc6 René Nussbaumer
      cmd = "pause-sync"
1521 a3fffcc6 René Nussbaumer
    else:
1522 a3fffcc6 René Nussbaumer
      cmd = "resume-sync"
1523 a3fffcc6 René Nussbaumer
1524 a3fffcc6 René Nussbaumer
    result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
1525 a3fffcc6 René Nussbaumer
    if result.failed:
1526 a3fffcc6 René Nussbaumer
      logging.error("Can't %s: %s - %s", cmd,
1527 a3fffcc6 René Nussbaumer
                    result.fail_reason, result.output)
1528 a3fffcc6 René Nussbaumer
    return not result.failed and children_result
1529 a3fffcc6 René Nussbaumer
1530 6b90c22e Iustin Pop
  def GetProcStatus(self):
1531 6b90c22e Iustin Pop
    """Return device data from /proc.
1532 6b90c22e Iustin Pop

1533 6b90c22e Iustin Pop
    """
1534 6b90c22e Iustin Pop
    if self.minor is None:
1535 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1536 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1537 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1538 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1539 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1540 6b90c22e Iustin Pop
1541 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1542 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1543 a2cfdea2 Iustin Pop

1544 a2cfdea2 Iustin Pop

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

1549 0834c866 Iustin Pop

1550 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1551 0834c866 Iustin Pop
    network not connected or local disk missing.
1552 0834c866 Iustin Pop

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

1556 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1557 c41eea6e Iustin Pop

1558 a2cfdea2 Iustin Pop
    """
1559 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1560 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1561 96acbc09 Michael Hanselmann
1562 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1563 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1564 f208978a Michael Hanselmann
1565 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1566 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1567 f208978a Michael Hanselmann
    elif stats.is_diskless:
1568 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1569 f208978a Michael Hanselmann
    else:
1570 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1571 96acbc09 Michael Hanselmann
1572 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1573 96acbc09 Michael Hanselmann
                                  major=self.major,
1574 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1575 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1576 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1577 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1578 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1579 a2cfdea2 Iustin Pop
1580 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1581 a2cfdea2 Iustin Pop
    """Make the local state primary.
1582 a2cfdea2 Iustin Pop

1583 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1584 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1585 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1586 f860ff4e Guido Trotter
    is mandatory.
1587 a2cfdea2 Iustin Pop

1588 a2cfdea2 Iustin Pop
    """
1589 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1590 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1591 a2cfdea2 Iustin Pop
      return False
1592 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1593 a2cfdea2 Iustin Pop
    if force:
1594 a2cfdea2 Iustin Pop
      cmd.append("-o")
1595 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1596 a2cfdea2 Iustin Pop
    if result.failed:
1597 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1598 82463074 Iustin Pop
                  result.output)
1599 a2cfdea2 Iustin Pop
1600 a2cfdea2 Iustin Pop
  def Close(self):
1601 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1602 a2cfdea2 Iustin Pop

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

1605 a2cfdea2 Iustin Pop
    """
1606 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1607 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1608 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1609 a2cfdea2 Iustin Pop
    if result.failed:
1610 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1611 82463074 Iustin Pop
                  self.minor, result.output)
1612 a2cfdea2 Iustin Pop
1613 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1614 cf8df3f3 Iustin Pop
    """Removes network configuration.
1615 cf8df3f3 Iustin Pop

1616 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1617 cf8df3f3 Iustin Pop

1618 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1619 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1620 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1621 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1622 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1623 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1624 cf8df3f3 Iustin Pop
    exception.
1625 cf8df3f3 Iustin Pop

1626 cf8df3f3 Iustin Pop
    """
1627 cf8df3f3 Iustin Pop
    if self.minor is None:
1628 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1629 cf8df3f3 Iustin Pop
1630 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1631 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1632 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1633 cf8df3f3 Iustin Pop
1634 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1635 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1636 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1637 cf8df3f3 Iustin Pop
1638 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1639 def8e2f6 Michael Hanselmann
1640 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1641 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1642 def8e2f6 Michael Hanselmann
        return
1643 def8e2f6 Michael Hanselmann
1644 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1645 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1646 def8e2f6 Michael Hanselmann
      # forgotten
1647 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1648 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1649 def8e2f6 Michael Hanselmann
1650 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1651 def8e2f6 Michael Hanselmann
1652 def8e2f6 Michael Hanselmann
    # Keep start time
1653 def8e2f6 Michael Hanselmann
    start_time = time.time()
1654 def8e2f6 Michael Hanselmann
1655 def8e2f6 Michael Hanselmann
    try:
1656 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1657 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1658 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1659 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1660 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1661 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1662 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1663 cf8df3f3 Iustin Pop
      else:
1664 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1665 def8e2f6 Michael Hanselmann
1666 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1667 cf8df3f3 Iustin Pop
1668 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1669 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1670 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1671 82463074 Iustin Pop
                   self.minor, reconfig_time)
1672 cf8df3f3 Iustin Pop
1673 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1674 cf8df3f3 Iustin Pop
    """Reconnects the network.
1675 cf8df3f3 Iustin Pop

1676 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1677 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1678 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1679 cf8df3f3 Iustin Pop

1680 cf8df3f3 Iustin Pop
    Args:
1681 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1682 cf8df3f3 Iustin Pop

1683 cf8df3f3 Iustin Pop
    """
1684 cf8df3f3 Iustin Pop
    if self.minor is None:
1685 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1686 cf8df3f3 Iustin Pop
1687 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1688 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1689 cf8df3f3 Iustin Pop
1690 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1691 cf8df3f3 Iustin Pop
1692 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1693 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1694 cf8df3f3 Iustin Pop
1695 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1696 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1697 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1698 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1699 cf8df3f3 Iustin Pop
1700 a2cfdea2 Iustin Pop
  def Attach(self):
1701 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1702 2d0c8319 Iustin Pop

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

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

1710 2d0c8319 Iustin Pop
    """
1711 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1712 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1713 2d0c8319 Iustin Pop
      minor = self._aminor
1714 2d0c8319 Iustin Pop
    else:
1715 2d0c8319 Iustin Pop
      minor = None
1716 2d0c8319 Iustin Pop
1717 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1718 2d0c8319 Iustin Pop
    return minor is not None
1719 2d0c8319 Iustin Pop
1720 2d0c8319 Iustin Pop
  def Assemble(self):
1721 2d0c8319 Iustin Pop
    """Assemble the drbd.
1722 2d0c8319 Iustin Pop

1723 2d0c8319 Iustin Pop
    Method:
1724 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1725 2d0c8319 Iustin Pop
        our config
1726 2d0c8319 Iustin Pop
      - if not, we create it from zero
1727 2d0c8319 Iustin Pop

1728 2d0c8319 Iustin Pop
    """
1729 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1730 2d0c8319 Iustin Pop
1731 2d0c8319 Iustin Pop
    self.Attach()
1732 2d0c8319 Iustin Pop
    if self.minor is None:
1733 2d0c8319 Iustin Pop
      # local device completely unconfigured
1734 1063abd1 Iustin Pop
      self._FastAssemble()
1735 2d0c8319 Iustin Pop
    else:
1736 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1737 2d0c8319 Iustin Pop
      # the device
1738 1063abd1 Iustin Pop
      self._SlowAssemble()
1739 2d0c8319 Iustin Pop
1740 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1741 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1742 a2cfdea2 Iustin Pop

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

1747 a2cfdea2 Iustin Pop
    """
1748 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
1749 527a15ac Iustin Pop
    # pylint: disable-msg=W0631
1750 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1751 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1752 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1753 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1754 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1755 1063abd1 Iustin Pop
1756 a2cfdea2 Iustin Pop
      if match_l and match_r:
1757 1063abd1 Iustin Pop
        # everything matches
1758 a2cfdea2 Iustin Pop
        break
1759 1063abd1 Iustin Pop
1760 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1761 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1762 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1763 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1764 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1765 1063abd1 Iustin Pop
          break
1766 1063abd1 Iustin Pop
        else:
1767 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1768 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1769 1063abd1 Iustin Pop
1770 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1771 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1772 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1773 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1774 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1775 1063abd1 Iustin Pop
          break
1776 1063abd1 Iustin Pop
        else:
1777 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1778 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1779 bf25af3b Iustin Pop
1780 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1781 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1782 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1783 bf25af3b Iustin Pop
      # configuration
1784 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1785 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1786 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1787 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1788 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1789 9cdbe77f Iustin Pop
        # reconnect to our correct one
1790 1063abd1 Iustin Pop
        try:
1791 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1792 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1793 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1794 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1795 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1796 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1797 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1798 9cdbe77f Iustin Pop
        # None)
1799 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1800 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1801 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1802 9cdbe77f Iustin Pop
          break
1803 1063abd1 Iustin Pop
        else:
1804 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1805 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1806 9cdbe77f Iustin Pop
1807 a2cfdea2 Iustin Pop
    else:
1808 a2cfdea2 Iustin Pop
      minor = None
1809 a2cfdea2 Iustin Pop
1810 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1811 1063abd1 Iustin Pop
    if minor is None:
1812 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1813 1063abd1 Iustin Pop
                  self._aminor)
1814 a2cfdea2 Iustin Pop
1815 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1816 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1817 a2cfdea2 Iustin Pop

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

1820 a2cfdea2 Iustin Pop
    """
1821 a1578d63 Iustin Pop
    minor = self._aminor
1822 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1823 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1824 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1825 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1826 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1827 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1828 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1829 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1830 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1831 a2cfdea2 Iustin Pop
1832 a2cfdea2 Iustin Pop
  @classmethod
1833 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1834 b00b95dd Iustin Pop
    """Detach from the local device.
1835 b00b95dd Iustin Pop

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

1839 b00b95dd Iustin Pop
    """
1840 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1841 b00b95dd Iustin Pop
    if result.failed:
1842 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1843 b00b95dd Iustin Pop
1844 b00b95dd Iustin Pop
  @classmethod
1845 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1846 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1847 f3e513ad Iustin Pop

1848 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1849 f3e513ad Iustin Pop

1850 f3e513ad Iustin Pop
    """
1851 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1852 a8459f1c Iustin Pop
    if result.failed:
1853 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1854 f3e513ad Iustin Pop
1855 f3e513ad Iustin Pop
  @classmethod
1856 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1857 a2cfdea2 Iustin Pop
    """Deactivate the device.
1858 a2cfdea2 Iustin Pop

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

1861 a2cfdea2 Iustin Pop
    """
1862 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1863 a2cfdea2 Iustin Pop
    if result.failed:
1864 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1865 33bc6f01 Iustin Pop
                  minor, result.output)
1866 a2cfdea2 Iustin Pop
1867 a2cfdea2 Iustin Pop
  def Shutdown(self):
1868 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1869 a2cfdea2 Iustin Pop

1870 a2cfdea2 Iustin Pop
    """
1871 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1872 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1873 746f7476 Iustin Pop
      return
1874 746f7476 Iustin Pop
    minor = self.minor
1875 a2cfdea2 Iustin Pop
    self.minor = None
1876 a2cfdea2 Iustin Pop
    self.dev_path = None
1877 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1878 a2cfdea2 Iustin Pop
1879 a2cfdea2 Iustin Pop
  def Remove(self):
1880 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1881 a2cfdea2 Iustin Pop

1882 a2cfdea2 Iustin Pop
    """
1883 0c6c04ec Iustin Pop
    self.Shutdown()
1884 a2cfdea2 Iustin Pop
1885 a2cfdea2 Iustin Pop
  @classmethod
1886 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1887 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1888 a2cfdea2 Iustin Pop

1889 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1890 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1891 a2cfdea2 Iustin Pop

1892 a2cfdea2 Iustin Pop
    """
1893 a2cfdea2 Iustin Pop
    if len(children) != 2:
1894 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1895 767d52d3 Iustin Pop
    # check that the minor is unused
1896 767d52d3 Iustin Pop
    aminor = unique_id[4]
1897 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1898 767d52d3 Iustin Pop
    if aminor in proc_info:
1899 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1900 767d52d3 Iustin Pop
      in_use = status.is_in_use
1901 767d52d3 Iustin Pop
    else:
1902 767d52d3 Iustin Pop
      in_use = False
1903 767d52d3 Iustin Pop
    if in_use:
1904 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1905 a2cfdea2 Iustin Pop
    meta = children[1]
1906 a2cfdea2 Iustin Pop
    meta.Assemble()
1907 a2cfdea2 Iustin Pop
    if not meta.Attach():
1908 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1909 33bc6f01 Iustin Pop
                  aminor, meta)
1910 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1911 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1912 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1913 a2cfdea2 Iustin Pop
1914 1005d816 Iustin Pop
  def Grow(self, amount):
1915 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1916 1005d816 Iustin Pop

1917 1005d816 Iustin Pop
    """
1918 1005d816 Iustin Pop
    if self.minor is None:
1919 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1920 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1921 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1922 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1923 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1924 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1925 1005d816 Iustin Pop
    if result.failed:
1926 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1927 1005d816 Iustin Pop
1928 a8083063 Iustin Pop
1929 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1930 6f695a2e Manuel Franceschini
  """File device.
1931 abdf0113 Iustin Pop

1932 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1933 6f695a2e Manuel Franceschini

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

1936 6f695a2e Manuel Franceschini
  """
1937 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1938 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1939 6f695a2e Manuel Franceschini

1940 6f695a2e Manuel Franceschini
    """
1941 6f695a2e Manuel Franceschini
    if children:
1942 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1943 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1944 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1945 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1946 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1947 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1948 ecb091e3 Iustin Pop
    self.Attach()
1949 6f695a2e Manuel Franceschini
1950 6f695a2e Manuel Franceschini
  def Assemble(self):
1951 6f695a2e Manuel Franceschini
    """Assemble the device.
1952 6f695a2e Manuel Franceschini

1953 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1954 6f695a2e Manuel Franceschini

1955 6f695a2e Manuel Franceschini
    """
1956 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1957 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1958 6f695a2e Manuel Franceschini
1959 6f695a2e Manuel Franceschini
  def Shutdown(self):
1960 6f695a2e Manuel Franceschini
    """Shutdown the device.
1961 6f695a2e Manuel Franceschini

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

1965 6f695a2e Manuel Franceschini
    """
1966 746f7476 Iustin Pop
    pass
1967 6f695a2e Manuel Franceschini
1968 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1969 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1970 6f695a2e Manuel Franceschini

1971 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1972 6f695a2e Manuel Franceschini

1973 6f695a2e Manuel Franceschini
    """
1974 6f695a2e Manuel Franceschini
    pass
1975 6f695a2e Manuel Franceschini
1976 6f695a2e Manuel Franceschini
  def Close(self):
1977 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1978 6f695a2e Manuel Franceschini

1979 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1980 6f695a2e Manuel Franceschini

1981 6f695a2e Manuel Franceschini
    """
1982 6f695a2e Manuel Franceschini
    pass
1983 6f695a2e Manuel Franceschini
1984 6f695a2e Manuel Franceschini
  def Remove(self):
1985 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1986 6f695a2e Manuel Franceschini

1987 c41eea6e Iustin Pop
    @rtype: boolean
1988 c41eea6e Iustin Pop
    @return: True if the removal was successful
1989 6f695a2e Manuel Franceschini

1990 6f695a2e Manuel Franceschini
    """
1991 6f695a2e Manuel Franceschini
    try:
1992 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1993 6f695a2e Manuel Franceschini
    except OSError, err:
1994 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1995 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1996 6f695a2e Manuel Franceschini
1997 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
1998 bbe4cc16 Iustin Pop
    """Renames the file.
1999 bbe4cc16 Iustin Pop

2000 bbe4cc16 Iustin Pop
    """
2001 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
2002 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
2003 bbe4cc16 Iustin Pop
2004 bbe4cc16 Iustin Pop
  def Grow(self, amount):
2005 bbe4cc16 Iustin Pop
    """Grow the file
2006 bbe4cc16 Iustin Pop

2007 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2008 bbe4cc16 Iustin Pop

2009 bbe4cc16 Iustin Pop
    """
2010 91e2d9ec Guido Trotter
    # Check that the file exists
2011 91e2d9ec Guido Trotter
    self.Assemble()
2012 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
2013 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
2014 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
2015 91e2d9ec Guido Trotter
    try:
2016 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
2017 91e2d9ec Guido Trotter
      f.truncate(new_size)
2018 91e2d9ec Guido Trotter
      f.close()
2019 91e2d9ec Guido Trotter
    except EnvironmentError, err:
2020 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
2021 bbe4cc16 Iustin Pop
2022 6f695a2e Manuel Franceschini
  def Attach(self):
2023 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2024 6f695a2e Manuel Franceschini

2025 6f695a2e Manuel Franceschini
    Check if this file already exists.
2026 6f695a2e Manuel Franceschini

2027 c41eea6e Iustin Pop
    @rtype: boolean
2028 c41eea6e Iustin Pop
    @return: True if file exists
2029 6f695a2e Manuel Franceschini

2030 6f695a2e Manuel Franceschini
    """
2031 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
2032 ecb091e3 Iustin Pop
    return self.attached
2033 6f695a2e Manuel Franceschini
2034 fcff3897 Iustin Pop
  def GetActualSize(self):
2035 fcff3897 Iustin Pop
    """Return the actual disk size.
2036 fcff3897 Iustin Pop

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

2039 fcff3897 Iustin Pop
    """
2040 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2041 fcff3897 Iustin Pop
    try:
2042 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2043 fcff3897 Iustin Pop
      return st.st_size
2044 fcff3897 Iustin Pop
    except OSError, err:
2045 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2046 fcff3897 Iustin Pop
2047 6f695a2e Manuel Franceschini
  @classmethod
2048 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
2049 6f695a2e Manuel Franceschini
    """Create a new file.
2050 6f695a2e Manuel Franceschini

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

2053 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2054 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2055 6f695a2e Manuel Franceschini

2056 6f695a2e Manuel Franceschini
    """
2057 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2058 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2059 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2060 6f695a2e Manuel Franceschini
    try:
2061 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
2062 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
2063 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2064 6f695a2e Manuel Franceschini
      f.close()
2065 cdeefd9b Guido Trotter
    except EnvironmentError, err:
2066 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
2067 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
2068 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
2069 6f695a2e Manuel Franceschini
2070 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
2071 6f695a2e Manuel Franceschini
2072 6f695a2e Manuel Franceschini
2073 b6135bbc Apollon Oikonomopoulos
class PersistentBlockDevice(BlockDev):
2074 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
2075 b6135bbc Apollon Oikonomopoulos

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

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

2082 b6135bbc Apollon Oikonomopoulos
  """
2083 b6135bbc Apollon Oikonomopoulos
  def __init__(self, unique_id, children, size):
2084 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
2085 b6135bbc Apollon Oikonomopoulos

2086 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2087 b6135bbc Apollon Oikonomopoulos

2088 b6135bbc Apollon Oikonomopoulos
    """
2089 b6135bbc Apollon Oikonomopoulos
    super(PersistentBlockDevice, self).__init__(unique_id, children, size)
2090 b6135bbc Apollon Oikonomopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2091 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2092 b6135bbc Apollon Oikonomopoulos
    self.dev_path = unique_id[1]
2093 b6135bbc Apollon Oikonomopoulos
    if not os.path.realpath(self.dev_path).startswith('/dev/'):
2094 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Full path '%s' lies outside /dev" %
2095 b6135bbc Apollon Oikonomopoulos
                              os.path.realpath(self.dev_path))
2096 b6135bbc Apollon Oikonomopoulos
    # TODO: this is just a safety guard checking that we only deal with devices
2097 b6135bbc Apollon Oikonomopoulos
    # we know how to handle. In the future this will be integrated with
2098 b6135bbc Apollon Oikonomopoulos
    # external storage backends and possible values will probably be collected
2099 b6135bbc Apollon Oikonomopoulos
    # from the cluster configuration.
2100 b6135bbc Apollon Oikonomopoulos
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
2101 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Got persistent block device of invalid type: %s" %
2102 b6135bbc Apollon Oikonomopoulos
                       unique_id[0])
2103 b6135bbc Apollon Oikonomopoulos
2104 b6135bbc Apollon Oikonomopoulos
    self.major = self.minor = None
2105 b6135bbc Apollon Oikonomopoulos
    self.Attach()
2106 b6135bbc Apollon Oikonomopoulos
2107 b6135bbc Apollon Oikonomopoulos
  @classmethod
2108 b6135bbc Apollon Oikonomopoulos
  def Create(cls, unique_id, children, size):
2109 b6135bbc Apollon Oikonomopoulos
    """Create a new device
2110 b6135bbc Apollon Oikonomopoulos

2111 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2112 b6135bbc Apollon Oikonomopoulos

2113 b6135bbc Apollon Oikonomopoulos
    """
2114 b6135bbc Apollon Oikonomopoulos
    return PersistentBlockDevice(unique_id, children, 0)
2115 b6135bbc Apollon Oikonomopoulos
2116 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
2117 b6135bbc Apollon Oikonomopoulos
    """Remove a device
2118 b6135bbc Apollon Oikonomopoulos

2119 b6135bbc Apollon Oikonomopoulos
    This is a noop
2120 b6135bbc Apollon Oikonomopoulos

2121 b6135bbc Apollon Oikonomopoulos
    """
2122 b6135bbc Apollon Oikonomopoulos
    pass
2123 b6135bbc Apollon Oikonomopoulos
2124 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
2125 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
2126 b6135bbc Apollon Oikonomopoulos

2127 b6135bbc Apollon Oikonomopoulos
    """
2128 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Rename is not supported for PersistentBlockDev storage")
2129 b6135bbc Apollon Oikonomopoulos
2130 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
2131 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
2132 b6135bbc Apollon Oikonomopoulos

2133 b6135bbc Apollon Oikonomopoulos

2134 b6135bbc Apollon Oikonomopoulos
    """
2135 b6135bbc Apollon Oikonomopoulos
    self.attached = False
2136 b6135bbc Apollon Oikonomopoulos
    try:
2137 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
2138 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
2139 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2140 b6135bbc Apollon Oikonomopoulos
      return False
2141 b6135bbc Apollon Oikonomopoulos
2142 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
2143 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
2144 b6135bbc Apollon Oikonomopoulos
      return False
2145 b6135bbc Apollon Oikonomopoulos
2146 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
2147 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
2148 b6135bbc Apollon Oikonomopoulos
    self.attached = True
2149 b6135bbc Apollon Oikonomopoulos
2150 b6135bbc Apollon Oikonomopoulos
    return True
2151 b6135bbc Apollon Oikonomopoulos
2152 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
2153 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
2154 b6135bbc Apollon Oikonomopoulos

2155 b6135bbc Apollon Oikonomopoulos
    """
2156 b6135bbc Apollon Oikonomopoulos
    pass
2157 b6135bbc Apollon Oikonomopoulos
2158 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
2159 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
2160 b6135bbc Apollon Oikonomopoulos

2161 b6135bbc Apollon Oikonomopoulos
    """
2162 b6135bbc Apollon Oikonomopoulos
    pass
2163 b6135bbc Apollon Oikonomopoulos
2164 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
2165 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
2166 b6135bbc Apollon Oikonomopoulos

2167 b6135bbc Apollon Oikonomopoulos
    """
2168 b6135bbc Apollon Oikonomopoulos
    pass
2169 b6135bbc Apollon Oikonomopoulos
2170 b6135bbc Apollon Oikonomopoulos
  def Close(self):
2171 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
2172 b6135bbc Apollon Oikonomopoulos

2173 b6135bbc Apollon Oikonomopoulos
    """
2174 b6135bbc Apollon Oikonomopoulos
    pass
2175 b6135bbc Apollon Oikonomopoulos
2176 b6135bbc Apollon Oikonomopoulos
  def Grow(self, amount):
2177 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
2178 b6135bbc Apollon Oikonomopoulos

2179 b6135bbc Apollon Oikonomopoulos
    """
2180 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2181 b6135bbc Apollon Oikonomopoulos
2182 b6135bbc Apollon Oikonomopoulos
2183 a8083063 Iustin Pop
DEV_MAP = {
2184 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2185 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2186 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
2187 a8083063 Iustin Pop
  }
2188 a8083063 Iustin Pop
2189 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
2190 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
2191 cb7c0198 Iustin Pop
2192 a8083063 Iustin Pop
2193 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
2194 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2195 a8083063 Iustin Pop

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

2199 a8083063 Iustin Pop
  """
2200 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2201 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2202 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
2203 cb999543 Iustin Pop
  if not device.attached:
2204 a8083063 Iustin Pop
    return None
2205 ecb091e3 Iustin Pop
  return device
2206 a8083063 Iustin Pop
2207 a8083063 Iustin Pop
2208 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
2209 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2210 a8083063 Iustin Pop

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

2214 a8083063 Iustin Pop
  """
2215 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2216 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2217 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
2218 1063abd1 Iustin Pop
  device.Assemble()
2219 a8083063 Iustin Pop
  return device
2220 a8083063 Iustin Pop
2221 a8083063 Iustin Pop
2222 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2223 a8083063 Iustin Pop
  """Create a device.
2224 a8083063 Iustin Pop

2225 a8083063 Iustin Pop
  """
2226 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2227 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2228 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2229 a8083063 Iustin Pop
  return device