Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ d23a2a9d

History | View | Annotate | Download (70.9 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 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
335 1005d816 Iustin Pop
    """Grow the block device.
336 1005d816 Iustin Pop

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

343 1005d816 Iustin Pop
    """
344 1005d816 Iustin Pop
    raise NotImplementedError
345 a0c3fea1 Michael Hanselmann
346 fcff3897 Iustin Pop
  def GetActualSize(self):
347 fcff3897 Iustin Pop
    """Return the actual disk size.
348 fcff3897 Iustin Pop

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

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

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

380 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
381 a8083063 Iustin Pop

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

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

443 197478f2 René Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
444 197478f2 René Nussbaumer
    @param fields: Fields to return
445 197478f2 René Nussbaumer
    @return: A list of dicts each with the parsed fields
446 197478f2 René Nussbaumer

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

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

479 c41eea6e Iustin Pop
    @rtype: list
480 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
481 098c0958 Michael Hanselmann

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

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

509 197478f2 René Nussbaumer
    @rtype: list
510 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
511 673cd9c4 René Nussbaumer
             MiB
512 197478f2 René Nussbaumer

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

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

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

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

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

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

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

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

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

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

659 a8083063 Iustin Pop
    """
660 746f7476 Iustin Pop
    pass
661 a8083063 Iustin Pop
662 9db6dbce Iustin Pop
  def GetSyncStatus(self):
663 9db6dbce Iustin Pop
    """Returns the sync status of the device.
664 9db6dbce Iustin Pop

665 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
666 9db6dbce Iustin Pop
    status of the mirror.
667 9db6dbce Iustin Pop

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

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

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

681 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
682 c41eea6e Iustin Pop

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

700 a8083063 Iustin Pop
    This is a no-op for the LV device type.
701 a8083063 Iustin Pop

702 a8083063 Iustin Pop
    """
703 fdbd668d Iustin Pop
    pass
704 a8083063 Iustin Pop
705 a8083063 Iustin Pop
  def Close(self):
706 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
707 a8083063 Iustin Pop

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

710 a8083063 Iustin Pop
    """
711 fdbd668d Iustin Pop
    pass
712 a8083063 Iustin Pop
713 a8083063 Iustin Pop
  def Snapshot(self, size):
714 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
715 a8083063 Iustin Pop

716 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
717 800ac399 Iustin Pop

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

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

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

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

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

893 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
894 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
895 0f7f32d9 Iustin Pop

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

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

931 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
932 c41eea6e Iustin Pop
        for that minor
933 a8083063 Iustin Pop

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

958 abdf0113 Iustin Pop
    This will return a dict with keys:
959 c41eea6e Iustin Pop
      - k_major
960 c41eea6e Iustin Pop
      - k_minor
961 c41eea6e Iustin Pop
      - k_point
962 c41eea6e Iustin Pop
      - api
963 c41eea6e Iustin Pop
      - proto
964 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
965 a8083063 Iustin Pop

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

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

1006 a8083063 Iustin Pop
    """
1007 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
1008 a8083063 Iustin Pop
1009 abdf0113 Iustin Pop
  @classmethod
1010 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
1011 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
1012 a8083063 Iustin Pop

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

1032 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1033 a8083063 Iustin Pop

1034 a8083063 Iustin Pop
    """
1035 abdf0113 Iustin Pop
    if minor is None:
1036 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
1037 cb999543 Iustin Pop
      self.attached = False
1038 a8083063 Iustin Pop
    else:
1039 abdf0113 Iustin Pop
      self.minor = minor
1040 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
1041 cb999543 Iustin Pop
      self.attached = True
1042 a8083063 Iustin Pop
1043 a8083063 Iustin Pop
  @staticmethod
1044 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
1045 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
1046 a8083063 Iustin Pop

1047 abdf0113 Iustin Pop
    This currently only check the size, which must be around
1048 abdf0113 Iustin Pop
    128MiB.
1049 a8083063 Iustin Pop

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

1074 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1075 a8083063 Iustin Pop

1076 a8083063 Iustin Pop
    """
1077 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1078 a8083063 Iustin Pop
1079 f3e513ad Iustin Pop
1080 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1081 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1082 a2cfdea2 Iustin Pop

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

1087 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1088 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1089 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1090 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1091 a2cfdea2 Iustin Pop

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

1131 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1132 a2cfdea2 Iustin Pop

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

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

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

1169 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1170 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1171 a2cfdea2 Iustin Pop

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

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

1239 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1240 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1241 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1242 3840729d Iustin Pop
    by _GetShowData.
1243 3840729d Iustin Pop

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

1278 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1279 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1280 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1281 a2cfdea2 Iustin Pop
    device.
1282 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

1480 7d585316 Iustin Pop
    This is the low-level implementation.
1481 7d585316 Iustin Pop

1482 7d585316 Iustin Pop
    @type minor: int
1483 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1484 7d585316 Iustin Pop
    @type kbytes: int
1485 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1486 7d585316 Iustin Pop
    @rtype: boolean
1487 7d585316 Iustin Pop
    @return: the success of the operation
1488 7d585316 Iustin Pop

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

1500 7d585316 Iustin Pop
    @type kbytes: int
1501 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1502 7d585316 Iustin Pop
    @rtype: boolean
1503 7d585316 Iustin Pop
    @return: the success of the operation
1504 7d585316 Iustin Pop

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

1515 a3fffcc6 René Nussbaumer
    @param pause: Wether to pause or resume
1516 a3fffcc6 René Nussbaumer
    @return: the success of the operation
1517 a3fffcc6 René Nussbaumer

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

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

1550 a2cfdea2 Iustin Pop

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

1555 0834c866 Iustin Pop

1556 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1557 0834c866 Iustin Pop
    network not connected or local disk missing.
1558 0834c866 Iustin Pop

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

1562 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1563 c41eea6e Iustin Pop

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

1589 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1590 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1591 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1592 f860ff4e Guido Trotter
    is mandatory.
1593 a2cfdea2 Iustin Pop

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

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

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

1622 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1623 cf8df3f3 Iustin Pop

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

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

1682 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1683 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1684 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1685 cf8df3f3 Iustin Pop

1686 cf8df3f3 Iustin Pop
    Args:
1687 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1688 cf8df3f3 Iustin Pop

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

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

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

1716 2d0c8319 Iustin Pop
    """
1717 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1718 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1719 2d0c8319 Iustin Pop
      minor = self._aminor
1720 2d0c8319 Iustin Pop
    else:
1721 2d0c8319 Iustin Pop
      minor = None
1722 2d0c8319 Iustin Pop
1723 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1724 2d0c8319 Iustin Pop
    return minor is not None
1725 2d0c8319 Iustin Pop
1726 2d0c8319 Iustin Pop
  def Assemble(self):
1727 2d0c8319 Iustin Pop
    """Assemble the drbd.
1728 2d0c8319 Iustin Pop

1729 2d0c8319 Iustin Pop
    Method:
1730 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1731 2d0c8319 Iustin Pop
        our config
1732 2d0c8319 Iustin Pop
      - if not, we create it from zero
1733 2d0c8319 Iustin Pop

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

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

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

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

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

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

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

1854 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1855 f3e513ad Iustin Pop

1856 f3e513ad Iustin Pop
    """
1857 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1858 a8459f1c Iustin Pop
    if result.failed:
1859 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1860 f3e513ad Iustin Pop
1861 f3e513ad Iustin Pop
  @classmethod
1862 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1863 a2cfdea2 Iustin Pop
    """Deactivate the device.
1864 a2cfdea2 Iustin Pop

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

1867 a2cfdea2 Iustin Pop
    """
1868 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1869 a2cfdea2 Iustin Pop
    if result.failed:
1870 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1871 33bc6f01 Iustin Pop
                  minor, result.output)
1872 a2cfdea2 Iustin Pop
1873 a2cfdea2 Iustin Pop
  def Shutdown(self):
1874 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1875 a2cfdea2 Iustin Pop

1876 a2cfdea2 Iustin Pop
    """
1877 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1878 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1879 746f7476 Iustin Pop
      return
1880 746f7476 Iustin Pop
    minor = self.minor
1881 a2cfdea2 Iustin Pop
    self.minor = None
1882 a2cfdea2 Iustin Pop
    self.dev_path = None
1883 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1884 a2cfdea2 Iustin Pop
1885 a2cfdea2 Iustin Pop
  def Remove(self):
1886 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1887 a2cfdea2 Iustin Pop

1888 a2cfdea2 Iustin Pop
    """
1889 0c6c04ec Iustin Pop
    self.Shutdown()
1890 a2cfdea2 Iustin Pop
1891 a2cfdea2 Iustin Pop
  @classmethod
1892 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1893 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1894 a2cfdea2 Iustin Pop

1895 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1896 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1897 a2cfdea2 Iustin Pop

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

1923 1005d816 Iustin Pop
    """
1924 1005d816 Iustin Pop
    if self.minor is None:
1925 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1926 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1927 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1928 7fe23d47 Iustin Pop
    self._children[0].Grow(amount, dryrun)
1929 7fe23d47 Iustin Pop
    if dryrun:
1930 7fe23d47 Iustin Pop
      # DRBD does not support dry-run mode, so we'll return here
1931 7fe23d47 Iustin Pop
      return
1932 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1933 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1934 1005d816 Iustin Pop
    if result.failed:
1935 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1936 1005d816 Iustin Pop
1937 a8083063 Iustin Pop
1938 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1939 6f695a2e Manuel Franceschini
  """File device.
1940 abdf0113 Iustin Pop

1941 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1942 6f695a2e Manuel Franceschini

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

1945 6f695a2e Manuel Franceschini
  """
1946 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1947 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1948 6f695a2e Manuel Franceschini

1949 6f695a2e Manuel Franceschini
    """
1950 6f695a2e Manuel Franceschini
    if children:
1951 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1952 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1953 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1954 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1955 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1956 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1957 ecb091e3 Iustin Pop
    self.Attach()
1958 6f695a2e Manuel Franceschini
1959 6f695a2e Manuel Franceschini
  def Assemble(self):
1960 6f695a2e Manuel Franceschini
    """Assemble the device.
1961 6f695a2e Manuel Franceschini

1962 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1963 6f695a2e Manuel Franceschini

1964 6f695a2e Manuel Franceschini
    """
1965 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1966 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1967 6f695a2e Manuel Franceschini
1968 6f695a2e Manuel Franceschini
  def Shutdown(self):
1969 6f695a2e Manuel Franceschini
    """Shutdown the device.
1970 6f695a2e Manuel Franceschini

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

1974 6f695a2e Manuel Franceschini
    """
1975 746f7476 Iustin Pop
    pass
1976 6f695a2e Manuel Franceschini
1977 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1978 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1979 6f695a2e Manuel Franceschini

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

1982 6f695a2e Manuel Franceschini
    """
1983 6f695a2e Manuel Franceschini
    pass
1984 6f695a2e Manuel Franceschini
1985 6f695a2e Manuel Franceschini
  def Close(self):
1986 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1987 6f695a2e Manuel Franceschini

1988 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1989 6f695a2e Manuel Franceschini

1990 6f695a2e Manuel Franceschini
    """
1991 6f695a2e Manuel Franceschini
    pass
1992 6f695a2e Manuel Franceschini
1993 6f695a2e Manuel Franceschini
  def Remove(self):
1994 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1995 6f695a2e Manuel Franceschini

1996 c41eea6e Iustin Pop
    @rtype: boolean
1997 c41eea6e Iustin Pop
    @return: True if the removal was successful
1998 6f695a2e Manuel Franceschini

1999 6f695a2e Manuel Franceschini
    """
2000 6f695a2e Manuel Franceschini
    try:
2001 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
2002 6f695a2e Manuel Franceschini
    except OSError, err:
2003 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
2004 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
2005 6f695a2e Manuel Franceschini
2006 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
2007 bbe4cc16 Iustin Pop
    """Renames the file.
2008 bbe4cc16 Iustin Pop

2009 bbe4cc16 Iustin Pop
    """
2010 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
2011 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
2012 bbe4cc16 Iustin Pop
2013 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
2014 bbe4cc16 Iustin Pop
    """Grow the file
2015 bbe4cc16 Iustin Pop

2016 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2017 bbe4cc16 Iustin Pop

2018 bbe4cc16 Iustin Pop
    """
2019 91e2d9ec Guido Trotter
    # Check that the file exists
2020 91e2d9ec Guido Trotter
    self.Assemble()
2021 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
2022 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
2023 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
2024 7fe23d47 Iustin Pop
    # We can't really simulate the growth
2025 7fe23d47 Iustin Pop
    if dryrun:
2026 7fe23d47 Iustin Pop
      return
2027 91e2d9ec Guido Trotter
    try:
2028 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
2029 91e2d9ec Guido Trotter
      f.truncate(new_size)
2030 91e2d9ec Guido Trotter
      f.close()
2031 91e2d9ec Guido Trotter
    except EnvironmentError, err:
2032 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
2033 bbe4cc16 Iustin Pop
2034 6f695a2e Manuel Franceschini
  def Attach(self):
2035 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2036 6f695a2e Manuel Franceschini

2037 6f695a2e Manuel Franceschini
    Check if this file already exists.
2038 6f695a2e Manuel Franceschini

2039 c41eea6e Iustin Pop
    @rtype: boolean
2040 c41eea6e Iustin Pop
    @return: True if file exists
2041 6f695a2e Manuel Franceschini

2042 6f695a2e Manuel Franceschini
    """
2043 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
2044 ecb091e3 Iustin Pop
    return self.attached
2045 6f695a2e Manuel Franceschini
2046 fcff3897 Iustin Pop
  def GetActualSize(self):
2047 fcff3897 Iustin Pop
    """Return the actual disk size.
2048 fcff3897 Iustin Pop

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

2051 fcff3897 Iustin Pop
    """
2052 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2053 fcff3897 Iustin Pop
    try:
2054 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2055 fcff3897 Iustin Pop
      return st.st_size
2056 fcff3897 Iustin Pop
    except OSError, err:
2057 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2058 fcff3897 Iustin Pop
2059 6f695a2e Manuel Franceschini
  @classmethod
2060 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
2061 6f695a2e Manuel Franceschini
    """Create a new file.
2062 6f695a2e Manuel Franceschini

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

2065 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2066 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2067 6f695a2e Manuel Franceschini

2068 6f695a2e Manuel Franceschini
    """
2069 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2070 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2071 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2072 6f695a2e Manuel Franceschini
    try:
2073 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
2074 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
2075 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2076 6f695a2e Manuel Franceschini
      f.close()
2077 cdeefd9b Guido Trotter
    except EnvironmentError, err:
2078 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
2079 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
2080 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
2081 6f695a2e Manuel Franceschini
2082 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
2083 6f695a2e Manuel Franceschini
2084 6f695a2e Manuel Franceschini
2085 b6135bbc Apollon Oikonomopoulos
class PersistentBlockDevice(BlockDev):
2086 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
2087 b6135bbc Apollon Oikonomopoulos

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

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

2094 b6135bbc Apollon Oikonomopoulos
  """
2095 b6135bbc Apollon Oikonomopoulos
  def __init__(self, unique_id, children, size):
2096 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
2097 b6135bbc Apollon Oikonomopoulos

2098 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2099 b6135bbc Apollon Oikonomopoulos

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

2123 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2124 b6135bbc Apollon Oikonomopoulos

2125 b6135bbc Apollon Oikonomopoulos
    """
2126 b6135bbc Apollon Oikonomopoulos
    return PersistentBlockDevice(unique_id, children, 0)
2127 b6135bbc Apollon Oikonomopoulos
2128 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
2129 b6135bbc Apollon Oikonomopoulos
    """Remove a device
2130 b6135bbc Apollon Oikonomopoulos

2131 b6135bbc Apollon Oikonomopoulos
    This is a noop
2132 b6135bbc Apollon Oikonomopoulos

2133 b6135bbc Apollon Oikonomopoulos
    """
2134 b6135bbc Apollon Oikonomopoulos
    pass
2135 b6135bbc Apollon Oikonomopoulos
2136 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
2137 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
2138 b6135bbc Apollon Oikonomopoulos

2139 b6135bbc Apollon Oikonomopoulos
    """
2140 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Rename is not supported for PersistentBlockDev storage")
2141 b6135bbc Apollon Oikonomopoulos
2142 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
2143 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
2144 b6135bbc Apollon Oikonomopoulos

2145 b6135bbc Apollon Oikonomopoulos

2146 b6135bbc Apollon Oikonomopoulos
    """
2147 b6135bbc Apollon Oikonomopoulos
    self.attached = False
2148 b6135bbc Apollon Oikonomopoulos
    try:
2149 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
2150 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
2151 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2152 b6135bbc Apollon Oikonomopoulos
      return False
2153 b6135bbc Apollon Oikonomopoulos
2154 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
2155 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
2156 b6135bbc Apollon Oikonomopoulos
      return False
2157 b6135bbc Apollon Oikonomopoulos
2158 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
2159 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
2160 b6135bbc Apollon Oikonomopoulos
    self.attached = True
2161 b6135bbc Apollon Oikonomopoulos
2162 b6135bbc Apollon Oikonomopoulos
    return True
2163 b6135bbc Apollon Oikonomopoulos
2164 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
2165 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
2166 b6135bbc Apollon Oikonomopoulos

2167 b6135bbc Apollon Oikonomopoulos
    """
2168 b6135bbc Apollon Oikonomopoulos
    pass
2169 b6135bbc Apollon Oikonomopoulos
2170 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
2171 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
2172 b6135bbc Apollon Oikonomopoulos

2173 b6135bbc Apollon Oikonomopoulos
    """
2174 b6135bbc Apollon Oikonomopoulos
    pass
2175 b6135bbc Apollon Oikonomopoulos
2176 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
2177 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
2178 b6135bbc Apollon Oikonomopoulos

2179 b6135bbc Apollon Oikonomopoulos
    """
2180 b6135bbc Apollon Oikonomopoulos
    pass
2181 b6135bbc Apollon Oikonomopoulos
2182 b6135bbc Apollon Oikonomopoulos
  def Close(self):
2183 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
2184 b6135bbc Apollon Oikonomopoulos

2185 b6135bbc Apollon Oikonomopoulos
    """
2186 b6135bbc Apollon Oikonomopoulos
    pass
2187 b6135bbc Apollon Oikonomopoulos
2188 7fe23d47 Iustin Pop
  def Grow(self, amount, dryrun):
2189 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
2190 b6135bbc Apollon Oikonomopoulos

2191 b6135bbc Apollon Oikonomopoulos
    """
2192 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2193 b6135bbc Apollon Oikonomopoulos
2194 b6135bbc Apollon Oikonomopoulos
2195 a8083063 Iustin Pop
DEV_MAP = {
2196 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2197 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2198 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
2199 a8083063 Iustin Pop
  }
2200 a8083063 Iustin Pop
2201 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
2202 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
2203 cb7c0198 Iustin Pop
2204 a8083063 Iustin Pop
2205 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
2206 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2207 a8083063 Iustin Pop

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

2211 a8083063 Iustin Pop
  """
2212 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2213 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2214 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
2215 cb999543 Iustin Pop
  if not device.attached:
2216 a8083063 Iustin Pop
    return None
2217 ecb091e3 Iustin Pop
  return device
2218 a8083063 Iustin Pop
2219 a8083063 Iustin Pop
2220 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
2221 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2222 a8083063 Iustin Pop

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

2226 a8083063 Iustin Pop
  """
2227 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2228 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2229 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
2230 1063abd1 Iustin Pop
  device.Assemble()
2231 a8083063 Iustin Pop
  return device
2232 a8083063 Iustin Pop
2233 a8083063 Iustin Pop
2234 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2235 a8083063 Iustin Pop
  """Create a device.
2236 a8083063 Iustin Pop

2237 a8083063 Iustin Pop
  """
2238 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2239 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2240 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2241 a8083063 Iustin Pop
  return device