Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 52ecc063

History | View | Annotate | Download (88.5 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 0304f0ec Iustin Pop
# Copyright (C) 2006, 2007, 2010, 2011, 2012 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Block device abstraction"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import re
25 a8083063 Iustin Pop
import time
26 a8083063 Iustin Pop
import errno
27 ad1dd4c7 Andrea Spadaccini
import shlex
28 b6135bbc Apollon Oikonomopoulos
import stat
29 a2cfdea2 Iustin Pop
import pyparsing as pyp
30 6f695a2e Manuel Franceschini
import os
31 468c5f77 Iustin Pop
import logging
32 a8083063 Iustin Pop
33 a8083063 Iustin Pop
from ganeti import utils
34 a8083063 Iustin Pop
from ganeti import errors
35 fe96220b Iustin Pop
from ganeti import constants
36 96acbc09 Michael Hanselmann
from ganeti import objects
37 cea881e5 Michael Hanselmann
from ganeti import compat
38 a744b676 Manuel Franceschini
from ganeti import netutils
39 fbdac0d9 Michael Hanselmann
from ganeti import pathutils
40 a8083063 Iustin Pop
41 a8083063 Iustin Pop
42 310fbb64 Iustin Pop
# Size of reads in _CanReadDevice
43 310fbb64 Iustin Pop
_DEVICE_READ_SIZE = 128 * 1024
44 310fbb64 Iustin Pop
45 310fbb64 Iustin Pop
46 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
47 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
48 82463074 Iustin Pop

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

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

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

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

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

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

83 310fbb64 Iustin Pop
  """
84 310fbb64 Iustin Pop
  try:
85 310fbb64 Iustin Pop
    utils.ReadFile(path, size=_DEVICE_READ_SIZE)
86 310fbb64 Iustin Pop
    return True
87 1122eb25 Iustin Pop
  except EnvironmentError:
88 310fbb64 Iustin Pop
    logging.warning("Can't read from device %s", path, exc_info=True)
89 310fbb64 Iustin Pop
    return False
90 310fbb64 Iustin Pop
91 310fbb64 Iustin Pop
92 fbdac0d9 Michael Hanselmann
def _CheckFileStoragePath(path, allowed):
93 fbdac0d9 Michael Hanselmann
  """Checks if a path is in a list of allowed paths for file storage.
94 fbdac0d9 Michael Hanselmann

95 fbdac0d9 Michael Hanselmann
  @type path: string
96 fbdac0d9 Michael Hanselmann
  @param path: Path to check
97 fbdac0d9 Michael Hanselmann
  @type allowed: list
98 fbdac0d9 Michael Hanselmann
  @param allowed: List of allowed paths
99 fbdac0d9 Michael Hanselmann
  @raise errors.FileStoragePathError: If the path is not allowed
100 fbdac0d9 Michael Hanselmann

101 fbdac0d9 Michael Hanselmann
  """
102 fbdac0d9 Michael Hanselmann
  if not os.path.isabs(path):
103 fbdac0d9 Michael Hanselmann
    raise errors.FileStoragePathError("File storage path must be absolute,"
104 fbdac0d9 Michael Hanselmann
                                      " got '%s'" % path)
105 fbdac0d9 Michael Hanselmann
106 fbdac0d9 Michael Hanselmann
  for i in allowed:
107 fbdac0d9 Michael Hanselmann
    if not os.path.isabs(i):
108 fbdac0d9 Michael Hanselmann
      logging.info("Ignoring relative path '%s' for file storage", i)
109 fbdac0d9 Michael Hanselmann
      continue
110 fbdac0d9 Michael Hanselmann
111 fbdac0d9 Michael Hanselmann
    if utils.IsBelowDir(i, path):
112 fbdac0d9 Michael Hanselmann
      break
113 fbdac0d9 Michael Hanselmann
  else:
114 fbdac0d9 Michael Hanselmann
    raise errors.FileStoragePathError("Path '%s' is not acceptable for file"
115 fbdac0d9 Michael Hanselmann
                                      " storage" % path)
116 fbdac0d9 Michael Hanselmann
117 fbdac0d9 Michael Hanselmann
118 fbdac0d9 Michael Hanselmann
def LoadAllowedFileStoragePaths(filename):
119 fbdac0d9 Michael Hanselmann
  """Loads file containing allowed file storage paths.
120 fbdac0d9 Michael Hanselmann

121 fbdac0d9 Michael Hanselmann
  @rtype: list
122 fbdac0d9 Michael Hanselmann
  @return: List of allowed paths (can be an empty list)
123 fbdac0d9 Michael Hanselmann

124 fbdac0d9 Michael Hanselmann
  """
125 fbdac0d9 Michael Hanselmann
  try:
126 fbdac0d9 Michael Hanselmann
    contents = utils.ReadFile(filename)
127 fbdac0d9 Michael Hanselmann
  except EnvironmentError:
128 fbdac0d9 Michael Hanselmann
    return []
129 fbdac0d9 Michael Hanselmann
  else:
130 fbdac0d9 Michael Hanselmann
    return utils.FilterEmptyLinesAndComments(contents)
131 fbdac0d9 Michael Hanselmann
132 fbdac0d9 Michael Hanselmann
133 fbdac0d9 Michael Hanselmann
def CheckFileStoragePath(path, _filename=pathutils.FILE_STORAGE_PATHS_FILE):
134 fbdac0d9 Michael Hanselmann
  """Checks if a path is allowed for file storage.
135 fbdac0d9 Michael Hanselmann

136 fbdac0d9 Michael Hanselmann
  @type path: string
137 fbdac0d9 Michael Hanselmann
  @param path: Path to check
138 fbdac0d9 Michael Hanselmann
  @raise errors.FileStoragePathError: If the path is not allowed
139 fbdac0d9 Michael Hanselmann

140 fbdac0d9 Michael Hanselmann
  """
141 fbdac0d9 Michael Hanselmann
  _CheckFileStoragePath(path, LoadAllowedFileStoragePaths(_filename))
142 fbdac0d9 Michael Hanselmann
143 fbdac0d9 Michael Hanselmann
144 fbdac0d9 Michael Hanselmann
145 a8083063 Iustin Pop
class BlockDev(object):
146 a8083063 Iustin Pop
  """Block device abstract class.
147 a8083063 Iustin Pop

148 a8083063 Iustin Pop
  A block device can be in the following states:
149 a8083063 Iustin Pop
    - not existing on the system, and by `Create()` it goes into:
150 a8083063 Iustin Pop
    - existing but not setup/not active, and by `Assemble()` goes into:
151 a8083063 Iustin Pop
    - active read-write and by `Open()` it goes into
152 a8083063 Iustin Pop
    - online (=used, or ready for use)
153 a8083063 Iustin Pop

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

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

165 a8083063 Iustin Pop
  A block device is identified by three items:
166 a8083063 Iustin Pop
    - the /dev path of the device (dynamic)
167 a8083063 Iustin Pop
    - a unique ID of the device (static)
168 a8083063 Iustin Pop
    - it's major/minor pair (dynamic)
169 a8083063 Iustin Pop

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

176 a8083063 Iustin Pop
  You can get to a device in two ways:
177 a8083063 Iustin Pop
    - creating the (real) device, which returns you
178 abdf0113 Iustin Pop
      an attached instance (lvcreate)
179 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
180 a8083063 Iustin Pop

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

187 a8083063 Iustin Pop
  """
188 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
189 a8083063 Iustin Pop
    self._children = children
190 a8083063 Iustin Pop
    self.dev_path = None
191 a8083063 Iustin Pop
    self.unique_id = unique_id
192 a8083063 Iustin Pop
    self.major = None
193 a8083063 Iustin Pop
    self.minor = None
194 cb999543 Iustin Pop
    self.attached = False
195 464f8daf Iustin Pop
    self.size = size
196 94dcbdb0 Andrea Spadaccini
    self.params = params
197 a8083063 Iustin Pop
198 a8083063 Iustin Pop
  def Assemble(self):
199 a8083063 Iustin Pop
    """Assemble the device from its components.
200 a8083063 Iustin Pop

201 f87548b5 Iustin Pop
    Implementations of this method by child classes must ensure that:
202 f87548b5 Iustin Pop
      - after the device has been assembled, it knows its major/minor
203 f87548b5 Iustin Pop
        numbers; this allows other devices (usually parents) to probe
204 f87548b5 Iustin Pop
        correctly for their children
205 f87548b5 Iustin Pop
      - calling this method on an existing, in-use device is safe
206 f87548b5 Iustin Pop
      - if the device is already configured (and in an OK state),
207 f87548b5 Iustin Pop
        this method is idempotent
208 a8083063 Iustin Pop

209 a8083063 Iustin Pop
    """
210 1063abd1 Iustin Pop
    pass
211 a8083063 Iustin Pop
212 a8083063 Iustin Pop
  def Attach(self):
213 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
214 a8083063 Iustin Pop

215 a8083063 Iustin Pop
    """
216 a8083063 Iustin Pop
    raise NotImplementedError
217 a8083063 Iustin Pop
218 a8083063 Iustin Pop
  def Close(self):
219 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
220 a8083063 Iustin Pop

221 a8083063 Iustin Pop
    """
222 a8083063 Iustin Pop
    raise NotImplementedError
223 a8083063 Iustin Pop
224 a8083063 Iustin Pop
  @classmethod
225 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
226 a8083063 Iustin Pop
    """Create the device.
227 a8083063 Iustin Pop

228 a8083063 Iustin Pop
    If the device cannot be created, it will return None
229 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
230 a8083063 Iustin Pop

231 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
232 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
233 a8083063 Iustin Pop
    enough for both creation and assembly (later).
234 a8083063 Iustin Pop

235 a8083063 Iustin Pop
    """
236 a8083063 Iustin Pop
    raise NotImplementedError
237 a8083063 Iustin Pop
238 a8083063 Iustin Pop
  def Remove(self):
239 a8083063 Iustin Pop
    """Remove this device.
240 a8083063 Iustin Pop

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

245 a8083063 Iustin Pop
    """
246 a8083063 Iustin Pop
    raise NotImplementedError
247 a8083063 Iustin Pop
248 f3e513ad Iustin Pop
  def Rename(self, new_id):
249 f3e513ad Iustin Pop
    """Rename this device.
250 f3e513ad Iustin Pop

251 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
252 f3e513ad Iustin Pop

253 f3e513ad Iustin Pop
    """
254 f3e513ad Iustin Pop
    raise NotImplementedError
255 f3e513ad Iustin Pop
256 a8083063 Iustin Pop
  def Open(self, force=False):
257 a8083063 Iustin Pop
    """Make the device ready for use.
258 a8083063 Iustin Pop

259 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
260 a8083063 Iustin Pop
    devices need this.
261 a8083063 Iustin Pop

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

265 a8083063 Iustin Pop
    """
266 a8083063 Iustin Pop
    raise NotImplementedError
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
  def Shutdown(self):
269 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
270 a8083063 Iustin Pop

271 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
272 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
273 a8083063 Iustin Pop
    assembled after this call.
274 a8083063 Iustin Pop

275 a8083063 Iustin Pop
    """
276 a8083063 Iustin Pop
    raise NotImplementedError
277 a8083063 Iustin Pop
278 f2f57b6e Andrea Spadaccini
  def SetSyncParams(self, params):
279 f2f57b6e Andrea Spadaccini
    """Adjust the synchronization parameters of the mirror.
280 a8083063 Iustin Pop

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

283 f2f57b6e Andrea Spadaccini
    @param params: dictionary of LD level disk parameters related to the
284 f2f57b6e Andrea Spadaccini
    synchronization.
285 8584e922 Andrea Spadaccini
    @rtype: list
286 8584e922 Andrea Spadaccini
    @return: a list of error messages, emitted both by the current node and by
287 8584e922 Andrea Spadaccini
    children. An empty list means no errors.
288 f2f57b6e Andrea Spadaccini

289 a8083063 Iustin Pop
    """
290 8584e922 Andrea Spadaccini
    result = []
291 a8083063 Iustin Pop
    if self._children:
292 a8083063 Iustin Pop
      for child in self._children:
293 8584e922 Andrea Spadaccini
        result.extend(child.SetSyncParams(params))
294 a8083063 Iustin Pop
    return result
295 a8083063 Iustin Pop
296 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
297 a3fffcc6 René Nussbaumer
    """Pause/Resume the sync of the mirror.
298 a3fffcc6 René Nussbaumer

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

301 f2f57b6e Andrea Spadaccini
    @param pause: Whether to pause or resume
302 a3fffcc6 René Nussbaumer

303 a3fffcc6 René Nussbaumer
    """
304 a3fffcc6 René Nussbaumer
    result = True
305 a3fffcc6 René Nussbaumer
    if self._children:
306 a3fffcc6 René Nussbaumer
      for child in self._children:
307 a3fffcc6 René Nussbaumer
        result = result and child.PauseResumeSync(pause)
308 a3fffcc6 René Nussbaumer
    return result
309 a3fffcc6 René Nussbaumer
310 a8083063 Iustin Pop
  def GetSyncStatus(self):
311 a8083063 Iustin Pop
    """Returns the sync status of the device.
312 a8083063 Iustin Pop

313 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
314 a8083063 Iustin Pop
    status of the mirror.
315 a8083063 Iustin Pop

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

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

321 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
322 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
323 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
324 a8083063 Iustin Pop

325 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
326 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
327 0834c866 Iustin Pop
    return False (not degraded).
328 0834c866 Iustin Pop

329 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
330 c41eea6e Iustin Pop

331 a8083063 Iustin Pop
    """
332 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
333 96acbc09 Michael Hanselmann
                                  major=self.major,
334 96acbc09 Michael Hanselmann
                                  minor=self.minor,
335 96acbc09 Michael Hanselmann
                                  sync_percent=None,
336 96acbc09 Michael Hanselmann
                                  estimated_time=None,
337 96acbc09 Michael Hanselmann
                                  is_degraded=False,
338 f208978a Michael Hanselmann
                                  ldisk_status=constants.LDS_OKAY)
339 a8083063 Iustin Pop
340 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
341 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
342 a8083063 Iustin Pop

343 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
344 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
345 a8083063 Iustin Pop
    children.
346 a8083063 Iustin Pop

347 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
348 96acbc09 Michael Hanselmann

349 a8083063 Iustin Pop
    """
350 96acbc09 Michael Hanselmann
    status = self.GetSyncStatus()
351 96acbc09 Michael Hanselmann
352 96acbc09 Michael Hanselmann
    min_percent = status.sync_percent
353 96acbc09 Michael Hanselmann
    max_time = status.estimated_time
354 96acbc09 Michael Hanselmann
    is_degraded = status.is_degraded
355 f208978a Michael Hanselmann
    ldisk_status = status.ldisk_status
356 96acbc09 Michael Hanselmann
357 a8083063 Iustin Pop
    if self._children:
358 a8083063 Iustin Pop
      for child in self._children:
359 96acbc09 Michael Hanselmann
        child_status = child.GetSyncStatus()
360 96acbc09 Michael Hanselmann
361 a8083063 Iustin Pop
        if min_percent is None:
362 96acbc09 Michael Hanselmann
          min_percent = child_status.sync_percent
363 96acbc09 Michael Hanselmann
        elif child_status.sync_percent is not None:
364 96acbc09 Michael Hanselmann
          min_percent = min(min_percent, child_status.sync_percent)
365 96acbc09 Michael Hanselmann
366 a8083063 Iustin Pop
        if max_time is None:
367 96acbc09 Michael Hanselmann
          max_time = child_status.estimated_time
368 96acbc09 Michael Hanselmann
        elif child_status.estimated_time is not None:
369 96acbc09 Michael Hanselmann
          max_time = max(max_time, child_status.estimated_time)
370 96acbc09 Michael Hanselmann
371 96acbc09 Michael Hanselmann
        is_degraded = is_degraded or child_status.is_degraded
372 f208978a Michael Hanselmann
373 f208978a Michael Hanselmann
        if ldisk_status is None:
374 f208978a Michael Hanselmann
          ldisk_status = child_status.ldisk_status
375 f208978a Michael Hanselmann
        elif child_status.ldisk_status is not None:
376 f208978a Michael Hanselmann
          ldisk_status = max(ldisk_status, child_status.ldisk_status)
377 96acbc09 Michael Hanselmann
378 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
379 96acbc09 Michael Hanselmann
                                  major=self.major,
380 96acbc09 Michael Hanselmann
                                  minor=self.minor,
381 96acbc09 Michael Hanselmann
                                  sync_percent=min_percent,
382 96acbc09 Michael Hanselmann
                                  estimated_time=max_time,
383 96acbc09 Michael Hanselmann
                                  is_degraded=is_degraded,
384 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
385 a8083063 Iustin Pop
386 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
387 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
388 a0c3fea1 Michael Hanselmann

389 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
390 a0c3fea1 Michael Hanselmann

391 a0c3fea1 Michael Hanselmann
    """
392 a0c3fea1 Michael Hanselmann
    for child in self._children:
393 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
394 a0c3fea1 Michael Hanselmann
395 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
396 1005d816 Iustin Pop
    """Grow the block device.
397 1005d816 Iustin Pop

398 7fe23d47 Iustin Pop
    @type amount: integer
399 c41eea6e Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
400 7fe23d47 Iustin Pop
    @type dryrun: boolean
401 7fe23d47 Iustin Pop
    @param dryrun: whether to execute the operation in simulation mode
402 7fe23d47 Iustin Pop
        only, without actually increasing the size
403 cad0723b Iustin Pop
    @param backingstore: whether to execute the operation on backing storage
404 cad0723b Iustin Pop
        only, or on "logical" storage only; e.g. DRBD is logical storage,
405 cad0723b Iustin Pop
        whereas LVM, file, RBD are backing storage
406 1005d816 Iustin Pop

407 1005d816 Iustin Pop
    """
408 1005d816 Iustin Pop
    raise NotImplementedError
409 a0c3fea1 Michael Hanselmann
410 fcff3897 Iustin Pop
  def GetActualSize(self):
411 fcff3897 Iustin Pop
    """Return the actual disk size.
412 fcff3897 Iustin Pop

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

415 fcff3897 Iustin Pop
    """
416 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
417 fcff3897 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize64", self.dev_path])
418 fcff3897 Iustin Pop
    if result.failed:
419 fcff3897 Iustin Pop
      _ThrowError("blockdev failed (%s): %s",
420 fcff3897 Iustin Pop
                  result.fail_reason, result.output)
421 fcff3897 Iustin Pop
    try:
422 fcff3897 Iustin Pop
      sz = int(result.output.strip())
423 fcff3897 Iustin Pop
    except (ValueError, TypeError), err:
424 fcff3897 Iustin Pop
      _ThrowError("Failed to parse blockdev output: %s", str(err))
425 fcff3897 Iustin Pop
    return sz
426 fcff3897 Iustin Pop
427 a8083063 Iustin Pop
  def __repr__(self):
428 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
429 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
430 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
431 a8083063 Iustin Pop
432 a8083063 Iustin Pop
433 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
434 a8083063 Iustin Pop
  """Logical Volume block device.
435 a8083063 Iustin Pop

436 a8083063 Iustin Pop
  """
437 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
438 6136f8f0 Iustin Pop
  _INVALID_NAMES = frozenset([".", "..", "snapshot", "pvmove"])
439 6136f8f0 Iustin Pop
  _INVALID_SUBSTRINGS = frozenset(["_mlog", "_mimage"])
440 6136f8f0 Iustin Pop
441 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
442 a8083063 Iustin Pop
    """Attaches to a LV device.
443 a8083063 Iustin Pop

444 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
445 a8083063 Iustin Pop

446 a8083063 Iustin Pop
    """
447 94dcbdb0 Andrea Spadaccini
    super(LogicalVolume, self).__init__(unique_id, children, size, params)
448 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
449 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
450 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
451 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
452 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
453 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
454 99e8295c Iustin Pop
    self._degraded = True
455 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
456 a8083063 Iustin Pop
    self.Attach()
457 a8083063 Iustin Pop
458 a8083063 Iustin Pop
  @classmethod
459 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
460 a8083063 Iustin Pop
    """Create a new logical volume.
461 a8083063 Iustin Pop

462 a8083063 Iustin Pop
    """
463 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
464 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
465 6c626518 Iustin Pop
                                   str(unique_id))
466 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
467 6136f8f0 Iustin Pop
    cls._ValidateName(vg_name)
468 6136f8f0 Iustin Pop
    cls._ValidateName(lv_name)
469 2070598f Iustin Pop
    pvs_info = cls.GetPVInfo([vg_name])
470 a8083063 Iustin Pop
    if not pvs_info:
471 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", vg_name)
472 a8083063 Iustin Pop
    pvs_info.sort()
473 a8083063 Iustin Pop
    pvs_info.reverse()
474 5b7b5d49 Guido Trotter
475 e687ec01 Michael Hanselmann
    pvlist = [pv[1] for pv in pvs_info]
476 403f5172 Guido Trotter
    if compat.any(":" in v for v in pvlist):
477 01b6558a Iustin Pop
      _ThrowError("Some of your PVs have the invalid character ':' in their"
478 01b6558a Iustin Pop
                  " name, this is not supported - please filter them out"
479 01b6558a Iustin Pop
                  " in lvm.conf using either 'filter' or 'preferred_names'")
480 e687ec01 Michael Hanselmann
    free_size = sum([pv[0] for pv in pvs_info])
481 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
482 ac00bf1b Andrea Spadaccini
    desired_stripes = params[constants.LDP_STRIPES]
483 43e11798 Andrea Spadaccini
    stripes = min(current_pvs, desired_stripes)
484 43e11798 Andrea Spadaccini
    if stripes < desired_stripes:
485 43e11798 Andrea Spadaccini
      logging.warning("Could not use %d stripes for VG %s, as only %d PVs are"
486 43e11798 Andrea Spadaccini
                      " available.", desired_stripes, vg_name, current_pvs)
487 5b7b5d49 Guido Trotter
488 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
489 5b7b5d49 Guido Trotter
    # calling the create function.
490 a8083063 Iustin Pop
    if free_size < size:
491 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
492 82463074 Iustin Pop
                  " available %s", size, free_size)
493 fecbe9d5 Iustin Pop
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
494 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
495 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
496 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
497 fecbe9d5 Iustin Pop
    # stripes
498 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
499 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
500 fecbe9d5 Iustin Pop
      if not result.failed:
501 fecbe9d5 Iustin Pop
        break
502 a8083063 Iustin Pop
    if result.failed:
503 82463074 Iustin Pop
      _ThrowError("LV create failed (%s): %s",
504 82463074 Iustin Pop
                  result.fail_reason, result.output)
505 94dcbdb0 Andrea Spadaccini
    return LogicalVolume(unique_id, children, size, params)
506 a8083063 Iustin Pop
507 a8083063 Iustin Pop
  @staticmethod
508 197478f2 René Nussbaumer
  def _GetVolumeInfo(lvm_cmd, fields):
509 197478f2 René Nussbaumer
    """Returns LVM Volumen infos using lvm_cmd
510 197478f2 René Nussbaumer

511 197478f2 René Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
512 197478f2 René Nussbaumer
    @param fields: Fields to return
513 197478f2 René Nussbaumer
    @return: A list of dicts each with the parsed fields
514 197478f2 René Nussbaumer

515 197478f2 René Nussbaumer
    """
516 197478f2 René Nussbaumer
    if not fields:
517 197478f2 René Nussbaumer
      raise errors.ProgrammerError("No fields specified")
518 197478f2 René Nussbaumer
519 197478f2 René Nussbaumer
    sep = "|"
520 197478f2 René Nussbaumer
    cmd = [lvm_cmd, "--noheadings", "--nosuffix", "--units=m", "--unbuffered",
521 197478f2 René Nussbaumer
           "--separator=%s" % sep, "-o%s" % ",".join(fields)]
522 197478f2 René Nussbaumer
523 197478f2 René Nussbaumer
    result = utils.RunCmd(cmd)
524 197478f2 René Nussbaumer
    if result.failed:
525 197478f2 René Nussbaumer
      raise errors.CommandError("Can't get the volume information: %s - %s" %
526 197478f2 René Nussbaumer
                                (result.fail_reason, result.output))
527 197478f2 René Nussbaumer
528 197478f2 René Nussbaumer
    data = []
529 197478f2 René Nussbaumer
    for line in result.stdout.splitlines():
530 197478f2 René Nussbaumer
      splitted_fields = line.strip().split(sep)
531 197478f2 René Nussbaumer
532 197478f2 René Nussbaumer
      if len(fields) != len(splitted_fields):
533 197478f2 René Nussbaumer
        raise errors.CommandError("Can't parse %s output: line '%s'" %
534 197478f2 René Nussbaumer
                                  (lvm_cmd, line))
535 197478f2 René Nussbaumer
536 197478f2 René Nussbaumer
      data.append(splitted_fields)
537 197478f2 René Nussbaumer
538 197478f2 René Nussbaumer
    return data
539 197478f2 René Nussbaumer
540 197478f2 René Nussbaumer
  @classmethod
541 197478f2 René Nussbaumer
  def GetPVInfo(cls, vg_names, filter_allocatable=True):
542 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
543 a8083063 Iustin Pop

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

547 c41eea6e Iustin Pop
    @rtype: list
548 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
549 098c0958 Michael Hanselmann

550 a8083063 Iustin Pop
    """
551 197478f2 René Nussbaumer
    try:
552 197478f2 René Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
553 197478f2 René Nussbaumer
                                        "pv_attr"])
554 197478f2 René Nussbaumer
    except errors.GenericError, err:
555 197478f2 René Nussbaumer
      logging.error("Can't get PV information: %s", err)
556 a8083063 Iustin Pop
      return None
557 197478f2 René Nussbaumer
558 a8083063 Iustin Pop
    data = []
559 197478f2 René Nussbaumer
    for pv_name, vg_name, pv_free, pv_attr in info:
560 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
561 197478f2 René Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
562 a8083063 Iustin Pop
        continue
563 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
564 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
565 2070598f Iustin Pop
        continue
566 197478f2 René Nussbaumer
      data.append((float(pv_free), pv_name, vg_name))
567 197478f2 René Nussbaumer
568 197478f2 René Nussbaumer
    return data
569 197478f2 René Nussbaumer
570 197478f2 René Nussbaumer
  @classmethod
571 197478f2 René Nussbaumer
  def GetVGInfo(cls, vg_names, filter_readonly=True):
572 197478f2 René Nussbaumer
    """Get the free space info for specific VGs.
573 197478f2 René Nussbaumer

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

577 197478f2 René Nussbaumer
    @rtype: list
578 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
579 673cd9c4 René Nussbaumer
             MiB
580 197478f2 René Nussbaumer

581 197478f2 René Nussbaumer
    """
582 197478f2 René Nussbaumer
    try:
583 673cd9c4 René Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
584 673cd9c4 René Nussbaumer
                                        "vg_size"])
585 197478f2 René Nussbaumer
    except errors.GenericError, err:
586 197478f2 René Nussbaumer
      logging.error("Can't get VG information: %s", err)
587 197478f2 René Nussbaumer
      return None
588 197478f2 René Nussbaumer
589 197478f2 René Nussbaumer
    data = []
590 673cd9c4 René Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
591 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not writable
592 197478f2 René Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
593 197478f2 René Nussbaumer
        continue
594 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
595 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
596 197478f2 René Nussbaumer
        continue
597 673cd9c4 René Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
598 a8083063 Iustin Pop
599 a8083063 Iustin Pop
    return data
600 a8083063 Iustin Pop
601 6136f8f0 Iustin Pop
  @classmethod
602 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
603 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
604 6136f8f0 Iustin Pop

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

609 6136f8f0 Iustin Pop
    """
610 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
611 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
612 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
613 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
614 6136f8f0 Iustin Pop
615 a8083063 Iustin Pop
  def Remove(self):
616 a8083063 Iustin Pop
    """Remove this logical volume.
617 a8083063 Iustin Pop

618 a8083063 Iustin Pop
    """
619 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
620 a8083063 Iustin Pop
      # the LV does not exist
621 0c6c04ec Iustin Pop
      return
622 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
623 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
624 a8083063 Iustin Pop
    if result.failed:
625 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
626 a8083063 Iustin Pop
627 f3e513ad Iustin Pop
  def Rename(self, new_id):
628 f3e513ad Iustin Pop
    """Rename this logical volume.
629 f3e513ad Iustin Pop

630 f3e513ad Iustin Pop
    """
631 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
632 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
633 f3e513ad Iustin Pop
    new_vg, new_name = new_id
634 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
635 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
636 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
637 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
638 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
639 f3e513ad Iustin Pop
    if result.failed:
640 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
641 be345db0 Iustin Pop
    self._lv_name = new_name
642 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
643 be345db0 Iustin Pop
644 a8083063 Iustin Pop
  def Attach(self):
645 a8083063 Iustin Pop
    """Attach to an existing LV.
646 a8083063 Iustin Pop

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

651 a8083063 Iustin Pop
    """
652 cb999543 Iustin Pop
    self.attached = False
653 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
654 38256320 Iustin Pop
                           "--units=m", "--nosuffix",
655 38256320 Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
656 38256320 Iustin Pop
                           "vg_extent_size,stripes", self.dev_path])
657 a8083063 Iustin Pop
    if result.failed:
658 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
659 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
660 a8083063 Iustin Pop
      return False
661 38256320 Iustin Pop
    # the output can (and will) have multiple lines for multi-segment
662 38256320 Iustin Pop
    # LVs, as the 'stripes' parameter is a segment one, so we take
663 38256320 Iustin Pop
    # only the last entry, which is the one we're interested in; note
664 38256320 Iustin Pop
    # that with LVM2 anyway the 'stripes' value must be constant
665 38256320 Iustin Pop
    # across segments, so this is a no-op actually
666 38256320 Iustin Pop
    out = result.stdout.splitlines()
667 38256320 Iustin Pop
    if not out: # totally empty result? splitlines() returns at least
668 38256320 Iustin Pop
                # one line for any non-empty string
669 38256320 Iustin Pop
      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
670 38256320 Iustin Pop
      return False
671 d0c8c01d Iustin Pop
    out = out[-1].strip().rstrip(",")
672 99e8295c Iustin Pop
    out = out.split(",")
673 38256320 Iustin Pop
    if len(out) != 5:
674 38256320 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
675 99e8295c Iustin Pop
      return False
676 99e8295c Iustin Pop
677 38256320 Iustin Pop
    status, major, minor, pe_size, stripes = out
678 0304f0ec Iustin Pop
    if len(status) < 6:
679 0304f0ec Iustin Pop
      logging.error("lvs lv_attr is not at least 6 characters (%s)", status)
680 99e8295c Iustin Pop
      return False
681 99e8295c Iustin Pop
682 99e8295c Iustin Pop
    try:
683 99e8295c Iustin Pop
      major = int(major)
684 99e8295c Iustin Pop
      minor = int(minor)
685 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
686 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
687 99e8295c Iustin Pop
688 38256320 Iustin Pop
    try:
689 38256320 Iustin Pop
      pe_size = int(float(pe_size))
690 38256320 Iustin Pop
    except (TypeError, ValueError), err:
691 38256320 Iustin Pop
      logging.error("Can't parse vg extent size: %s", err)
692 38256320 Iustin Pop
      return False
693 38256320 Iustin Pop
694 38256320 Iustin Pop
    try:
695 38256320 Iustin Pop
      stripes = int(stripes)
696 38256320 Iustin Pop
    except (TypeError, ValueError), err:
697 38256320 Iustin Pop
      logging.error("Can't parse the number of stripes: %s", err)
698 38256320 Iustin Pop
      return False
699 38256320 Iustin Pop
700 99e8295c Iustin Pop
    self.major = major
701 99e8295c Iustin Pop
    self.minor = minor
702 38256320 Iustin Pop
    self.pe_size = pe_size
703 38256320 Iustin Pop
    self.stripe_count = stripes
704 d0c8c01d Iustin Pop
    self._degraded = status[0] == "v" # virtual volume, i.e. doesn't backing
705 99e8295c Iustin Pop
                                      # storage
706 cb999543 Iustin Pop
    self.attached = True
707 99e8295c Iustin Pop
    return True
708 a8083063 Iustin Pop
709 a8083063 Iustin Pop
  def Assemble(self):
710 a8083063 Iustin Pop
    """Assemble the device.
711 a8083063 Iustin Pop

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

716 a8083063 Iustin Pop
    """
717 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
718 5574047a Iustin Pop
    if result.failed:
719 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
720 a8083063 Iustin Pop
721 a8083063 Iustin Pop
  def Shutdown(self):
722 a8083063 Iustin Pop
    """Shutdown the device.
723 a8083063 Iustin Pop

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

727 a8083063 Iustin Pop
    """
728 746f7476 Iustin Pop
    pass
729 a8083063 Iustin Pop
730 9db6dbce Iustin Pop
  def GetSyncStatus(self):
731 9db6dbce Iustin Pop
    """Returns the sync status of the device.
732 9db6dbce Iustin Pop

733 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
734 9db6dbce Iustin Pop
    status of the mirror.
735 9db6dbce Iustin Pop

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

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

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

749 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
750 c41eea6e Iustin Pop

751 9db6dbce Iustin Pop
    """
752 f208978a Michael Hanselmann
    if self._degraded:
753 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
754 f208978a Michael Hanselmann
    else:
755 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
756 f208978a Michael Hanselmann
757 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
758 96acbc09 Michael Hanselmann
                                  major=self.major,
759 96acbc09 Michael Hanselmann
                                  minor=self.minor,
760 96acbc09 Michael Hanselmann
                                  sync_percent=None,
761 96acbc09 Michael Hanselmann
                                  estimated_time=None,
762 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
763 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
764 9db6dbce Iustin Pop
765 a8083063 Iustin Pop
  def Open(self, force=False):
766 a8083063 Iustin Pop
    """Make the device ready for I/O.
767 a8083063 Iustin Pop

768 a8083063 Iustin Pop
    This is a no-op for the LV device type.
769 a8083063 Iustin Pop

770 a8083063 Iustin Pop
    """
771 fdbd668d Iustin Pop
    pass
772 a8083063 Iustin Pop
773 a8083063 Iustin Pop
  def Close(self):
774 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
775 a8083063 Iustin Pop

776 a8083063 Iustin Pop
    This is a no-op for the LV device type.
777 a8083063 Iustin Pop

778 a8083063 Iustin Pop
    """
779 fdbd668d Iustin Pop
    pass
780 a8083063 Iustin Pop
781 a8083063 Iustin Pop
  def Snapshot(self, size):
782 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
783 a8083063 Iustin Pop

784 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
785 800ac399 Iustin Pop

786 a8083063 Iustin Pop
    """
787 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
788 a8083063 Iustin Pop
789 a8083063 Iustin Pop
    # remove existing snapshot if found
790 94dcbdb0 Andrea Spadaccini
    snap = LogicalVolume((self._vg_name, snap_name), None, size, self.params)
791 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
792 a8083063 Iustin Pop
793 197478f2 René Nussbaumer
    vg_info = self.GetVGInfo([self._vg_name])
794 197478f2 René Nussbaumer
    if not vg_info:
795 197478f2 René Nussbaumer
      _ThrowError("Can't compute VG info for vg %s", self._vg_name)
796 673cd9c4 René Nussbaumer
    free_size, _, _ = vg_info[0]
797 a8083063 Iustin Pop
    if free_size < size:
798 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
799 82463074 Iustin Pop
                  " available %s", size, free_size)
800 a8083063 Iustin Pop
801 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
802 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
803 a8083063 Iustin Pop
    if result.failed:
804 82463074 Iustin Pop
      _ThrowError("command: %s error: %s - %s",
805 82463074 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
806 a8083063 Iustin Pop
807 800ac399 Iustin Pop
    return (self._vg_name, snap_name)
808 a8083063 Iustin Pop
809 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
810 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
811 a0c3fea1 Michael Hanselmann

812 a0c3fea1 Michael Hanselmann
    """
813 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
814 a0c3fea1 Michael Hanselmann
815 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
816 d0c8c01d Iustin Pop
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
817 d0c8c01d Iustin Pop
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
818 a0c3fea1 Michael Hanselmann
819 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
820 a0c3fea1 Michael Hanselmann
    text = text[:128]
821 a0c3fea1 Michael Hanselmann
822 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
823 a0c3fea1 Michael Hanselmann
                           self.dev_path])
824 a0c3fea1 Michael Hanselmann
    if result.failed:
825 82463074 Iustin Pop
      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
826 82463074 Iustin Pop
                  result.output)
827 82463074 Iustin Pop
828 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
829 1005d816 Iustin Pop
    """Grow the logical volume.
830 1005d816 Iustin Pop

831 1005d816 Iustin Pop
    """
832 cad0723b Iustin Pop
    if not backingstore:
833 cad0723b Iustin Pop
      return
834 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
835 38256320 Iustin Pop
      if not self.Attach():
836 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
837 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
838 38256320 Iustin Pop
    rest = amount % full_stripe_size
839 38256320 Iustin Pop
    if rest != 0:
840 38256320 Iustin Pop
      amount += full_stripe_size - rest
841 7fe23d47 Iustin Pop
    cmd = ["lvextend", "-L", "+%dm" % amount]
842 7fe23d47 Iustin Pop
    if dryrun:
843 7fe23d47 Iustin Pop
      cmd.append("--test")
844 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
845 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
846 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
847 1005d816 Iustin Pop
    # supports 'cling'
848 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
849 7fe23d47 Iustin Pop
      result = utils.RunCmd(cmd + ["--alloc", alloc_policy, self.dev_path])
850 1005d816 Iustin Pop
      if not result.failed:
851 1005d816 Iustin Pop
        return
852 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
853 a0c3fea1 Michael Hanselmann
854 a0c3fea1 Michael Hanselmann
855 6b90c22e Iustin Pop
class DRBD8Status(object):
856 6b90c22e Iustin Pop
  """A DRBD status representation class.
857 6b90c22e Iustin Pop

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

860 6b90c22e Iustin Pop
  """
861 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
862 01e2ce3a Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
863 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
864 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
865 7a380ddf René Nussbaumer
                       # Due to a bug in drbd in the kernel, introduced in
866 7a380ddf René Nussbaumer
                       # commit 4b0715f096 (still unfixed as of 2011-08-22)
867 7a380ddf René Nussbaumer
                       "(?:\s|M)"
868 7a380ddf René Nussbaumer
                       "finish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
869 6b90c22e Iustin Pop
870 3c003d9d Iustin Pop
  CS_UNCONFIGURED = "Unconfigured"
871 3c003d9d Iustin Pop
  CS_STANDALONE = "StandAlone"
872 3c003d9d Iustin Pop
  CS_WFCONNECTION = "WFConnection"
873 3c003d9d Iustin Pop
  CS_WFREPORTPARAMS = "WFReportParams"
874 3c003d9d Iustin Pop
  CS_CONNECTED = "Connected"
875 3c003d9d Iustin Pop
  CS_STARTINGSYNCS = "StartingSyncS"
876 3c003d9d Iustin Pop
  CS_STARTINGSYNCT = "StartingSyncT"
877 3c003d9d Iustin Pop
  CS_WFBITMAPS = "WFBitMapS"
878 3c003d9d Iustin Pop
  CS_WFBITMAPT = "WFBitMapT"
879 3c003d9d Iustin Pop
  CS_WFSYNCUUID = "WFSyncUUID"
880 3c003d9d Iustin Pop
  CS_SYNCSOURCE = "SyncSource"
881 3c003d9d Iustin Pop
  CS_SYNCTARGET = "SyncTarget"
882 3c003d9d Iustin Pop
  CS_PAUSEDSYNCS = "PausedSyncS"
883 3c003d9d Iustin Pop
  CS_PAUSEDSYNCT = "PausedSyncT"
884 3c003d9d Iustin Pop
  CSET_SYNC = frozenset([
885 3c003d9d Iustin Pop
    CS_WFREPORTPARAMS,
886 3c003d9d Iustin Pop
    CS_STARTINGSYNCS,
887 3c003d9d Iustin Pop
    CS_STARTINGSYNCT,
888 3c003d9d Iustin Pop
    CS_WFBITMAPS,
889 3c003d9d Iustin Pop
    CS_WFBITMAPT,
890 3c003d9d Iustin Pop
    CS_WFSYNCUUID,
891 3c003d9d Iustin Pop
    CS_SYNCSOURCE,
892 3c003d9d Iustin Pop
    CS_SYNCTARGET,
893 3c003d9d Iustin Pop
    CS_PAUSEDSYNCS,
894 3c003d9d Iustin Pop
    CS_PAUSEDSYNCT,
895 3c003d9d Iustin Pop
    ])
896 3c003d9d Iustin Pop
897 3c003d9d Iustin Pop
  DS_DISKLESS = "Diskless"
898 3c003d9d Iustin Pop
  DS_ATTACHING = "Attaching" # transient state
899 3c003d9d Iustin Pop
  DS_FAILED = "Failed" # transient state, next: diskless
900 3c003d9d Iustin Pop
  DS_NEGOTIATING = "Negotiating" # transient state
901 3c003d9d Iustin Pop
  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
902 3c003d9d Iustin Pop
  DS_OUTDATED = "Outdated"
903 3c003d9d Iustin Pop
  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
904 3c003d9d Iustin Pop
  DS_CONSISTENT = "Consistent"
905 3c003d9d Iustin Pop
  DS_UPTODATE = "UpToDate" # normal state
906 3c003d9d Iustin Pop
907 3c003d9d Iustin Pop
  RO_PRIMARY = "Primary"
908 3c003d9d Iustin Pop
  RO_SECONDARY = "Secondary"
909 3c003d9d Iustin Pop
  RO_UNKNOWN = "Unknown"
910 3c003d9d Iustin Pop
911 6b90c22e Iustin Pop
  def __init__(self, procline):
912 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
913 767d52d3 Iustin Pop
    if u:
914 3c003d9d Iustin Pop
      self.cstatus = self.CS_UNCONFIGURED
915 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
916 767d52d3 Iustin Pop
    else:
917 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
918 767d52d3 Iustin Pop
      if not m:
919 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
920 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
921 767d52d3 Iustin Pop
      self.lrole = m.group(2)
922 767d52d3 Iustin Pop
      self.rrole = m.group(3)
923 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
924 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
925 767d52d3 Iustin Pop
926 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
927 6b90c22e Iustin Pop
928 3c003d9d Iustin Pop
    self.is_standalone = self.cstatus == self.CS_STANDALONE
929 3c003d9d Iustin Pop
    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
930 3c003d9d Iustin Pop
    self.is_connected = self.cstatus == self.CS_CONNECTED
931 3c003d9d Iustin Pop
    self.is_primary = self.lrole == self.RO_PRIMARY
932 3c003d9d Iustin Pop
    self.is_secondary = self.lrole == self.RO_SECONDARY
933 3c003d9d Iustin Pop
    self.peer_primary = self.rrole == self.RO_PRIMARY
934 3c003d9d Iustin Pop
    self.peer_secondary = self.rrole == self.RO_SECONDARY
935 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
936 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
937 6b90c22e Iustin Pop
938 3c003d9d Iustin Pop
    self.is_diskless = self.ldisk == self.DS_DISKLESS
939 3c003d9d Iustin Pop
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
940 6b90c22e Iustin Pop
941 3c003d9d Iustin Pop
    self.is_in_resync = self.cstatus in self.CSET_SYNC
942 3c003d9d Iustin Pop
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
943 6b93ec9d Iustin Pop
944 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
945 6b90c22e Iustin Pop
    if m:
946 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
947 6b90c22e Iustin Pop
      hours = int(m.group(2))
948 6b90c22e Iustin Pop
      minutes = int(m.group(3))
949 6b90c22e Iustin Pop
      seconds = int(m.group(4))
950 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
951 6b90c22e Iustin Pop
    else:
952 3c003d9d Iustin Pop
      # we have (in this if branch) no percent information, but if
953 3c003d9d Iustin Pop
      # we're resyncing we need to 'fake' a sync percent information,
954 3c003d9d Iustin Pop
      # as this is how cmdlib determines if it makes sense to wait for
955 3c003d9d Iustin Pop
      # resyncing or not
956 3c003d9d Iustin Pop
      if self.is_in_resync:
957 3c003d9d Iustin Pop
        self.sync_percent = 0
958 3c003d9d Iustin Pop
      else:
959 3c003d9d Iustin Pop
        self.sync_percent = None
960 6b90c22e Iustin Pop
      self.est_time = None
961 6b90c22e Iustin Pop
962 6b90c22e Iustin Pop
963 b459a848 Andrea Spadaccini
class BaseDRBD(BlockDev): # pylint: disable=W0223
964 0f7f32d9 Iustin Pop
  """Base DRBD class.
965 a8083063 Iustin Pop

966 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
967 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
968 0f7f32d9 Iustin Pop

969 abdf0113 Iustin Pop
  """
970 fcee765d Manuel Franceschini
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)(?:\.\d+)?"
971 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
972 9122e60a Iustin Pop
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
973 9122e60a Iustin Pop
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
974 a8083063 Iustin Pop
975 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
976 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
977 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
978 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
979 a8083063 Iustin Pop
980 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
981 549071a0 Luca Bigliardi
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
982 6b90c22e Iustin Pop
983 abdf0113 Iustin Pop
  @staticmethod
984 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
985 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
986 a8083063 Iustin Pop

987 a8083063 Iustin Pop
    """
988 abdf0113 Iustin Pop
    try:
989 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
990 f6eaed12 Iustin Pop
    except EnvironmentError, err:
991 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
992 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
993 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
994 f6eaed12 Iustin Pop
      else:
995 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
996 abdf0113 Iustin Pop
    if not data:
997 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
998 abdf0113 Iustin Pop
    return data
999 a8083063 Iustin Pop
1000 9122e60a Iustin Pop
  @classmethod
1001 9122e60a Iustin Pop
  def _MassageProcData(cls, data):
1002 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
1003 a8083063 Iustin Pop

1004 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
1005 c41eea6e Iustin Pop
        for that minor
1006 a8083063 Iustin Pop

1007 a8083063 Iustin Pop
    """
1008 abdf0113 Iustin Pop
    results = {}
1009 abdf0113 Iustin Pop
    old_minor = old_line = None
1010 abdf0113 Iustin Pop
    for line in data:
1011 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
1012 67d101d4 Iustin Pop
        continue
1013 9122e60a Iustin Pop
      lresult = cls._VALID_LINE_RE.match(line)
1014 abdf0113 Iustin Pop
      if lresult is not None:
1015 abdf0113 Iustin Pop
        if old_minor is not None:
1016 abdf0113 Iustin Pop
          results[old_minor] = old_line
1017 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
1018 abdf0113 Iustin Pop
        old_line = line
1019 abdf0113 Iustin Pop
      else:
1020 abdf0113 Iustin Pop
        if old_minor is not None:
1021 abdf0113 Iustin Pop
          old_line += " " + line.strip()
1022 abdf0113 Iustin Pop
    # add last line
1023 abdf0113 Iustin Pop
    if old_minor is not None:
1024 abdf0113 Iustin Pop
      results[old_minor] = old_line
1025 abdf0113 Iustin Pop
    return results
1026 a8083063 Iustin Pop
1027 abdf0113 Iustin Pop
  @classmethod
1028 fcee765d Manuel Franceschini
  def _GetVersion(cls, proc_data):
1029 abdf0113 Iustin Pop
    """Return the DRBD version.
1030 a8083063 Iustin Pop

1031 abdf0113 Iustin Pop
    This will return a dict with keys:
1032 c41eea6e Iustin Pop
      - k_major
1033 c41eea6e Iustin Pop
      - k_minor
1034 c41eea6e Iustin Pop
      - k_point
1035 c41eea6e Iustin Pop
      - api
1036 c41eea6e Iustin Pop
      - proto
1037 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
1038 a8083063 Iustin Pop

1039 a8083063 Iustin Pop
    """
1040 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
1041 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
1042 abdf0113 Iustin Pop
    if not version:
1043 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
1044 abdf0113 Iustin Pop
                                    first_line)
1045 a8083063 Iustin Pop
1046 abdf0113 Iustin Pop
    values = version.groups()
1047 5ae4945a Iustin Pop
    retval = {
1048 5ae4945a Iustin Pop
      "k_major": int(values[0]),
1049 5ae4945a Iustin Pop
      "k_minor": int(values[1]),
1050 5ae4945a Iustin Pop
      "k_point": int(values[2]),
1051 5ae4945a Iustin Pop
      "api": int(values[3]),
1052 5ae4945a Iustin Pop
      "proto": int(values[4]),
1053 5ae4945a Iustin Pop
      }
1054 abdf0113 Iustin Pop
    if values[5] is not None:
1055 d0c8c01d Iustin Pop
      retval["proto2"] = values[5]
1056 a8083063 Iustin Pop
1057 abdf0113 Iustin Pop
    return retval
1058 abdf0113 Iustin Pop
1059 abdf0113 Iustin Pop
  @staticmethod
1060 549071a0 Luca Bigliardi
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
1061 549071a0 Luca Bigliardi
    """Returns DRBD usermode_helper currently set.
1062 549071a0 Luca Bigliardi

1063 549071a0 Luca Bigliardi
    """
1064 549071a0 Luca Bigliardi
    try:
1065 549071a0 Luca Bigliardi
      helper = utils.ReadFile(filename).splitlines()[0]
1066 549071a0 Luca Bigliardi
    except EnvironmentError, err:
1067 549071a0 Luca Bigliardi
      if err.errno == errno.ENOENT:
1068 549071a0 Luca Bigliardi
        _ThrowError("The file %s cannot be opened, check if the module"
1069 549071a0 Luca Bigliardi
                    " is loaded (%s)", filename, str(err))
1070 549071a0 Luca Bigliardi
      else:
1071 549071a0 Luca Bigliardi
        _ThrowError("Can't read DRBD helper file %s: %s", filename, str(err))
1072 549071a0 Luca Bigliardi
    if not helper:
1073 549071a0 Luca Bigliardi
      _ThrowError("Can't read any data from %s", filename)
1074 549071a0 Luca Bigliardi
    return helper
1075 549071a0 Luca Bigliardi
1076 549071a0 Luca Bigliardi
  @staticmethod
1077 abdf0113 Iustin Pop
  def _DevPath(minor):
1078 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
1079 a8083063 Iustin Pop

1080 a8083063 Iustin Pop
    """
1081 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
1082 a8083063 Iustin Pop
1083 abdf0113 Iustin Pop
  @classmethod
1084 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
1085 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
1086 a8083063 Iustin Pop

1087 a8083063 Iustin Pop
    """
1088 abdf0113 Iustin Pop
    data = cls._GetProcData()
1089 a8083063 Iustin Pop
1090 abdf0113 Iustin Pop
    used_devs = {}
1091 abdf0113 Iustin Pop
    for line in data:
1092 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1093 abdf0113 Iustin Pop
      if not match:
1094 abdf0113 Iustin Pop
        continue
1095 abdf0113 Iustin Pop
      minor = int(match.group(1))
1096 abdf0113 Iustin Pop
      state = match.group(2)
1097 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1098 abdf0113 Iustin Pop
        continue
1099 abdf0113 Iustin Pop
      used_devs[minor] = state, line
1100 a8083063 Iustin Pop
1101 abdf0113 Iustin Pop
    return used_devs
1102 a8083063 Iustin Pop
1103 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
1104 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
1105 0834c866 Iustin Pop

1106 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1107 a8083063 Iustin Pop

1108 a8083063 Iustin Pop
    """
1109 abdf0113 Iustin Pop
    if minor is None:
1110 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
1111 cb999543 Iustin Pop
      self.attached = False
1112 a8083063 Iustin Pop
    else:
1113 abdf0113 Iustin Pop
      self.minor = minor
1114 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
1115 cb999543 Iustin Pop
      self.attached = True
1116 a8083063 Iustin Pop
1117 a8083063 Iustin Pop
  @staticmethod
1118 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
1119 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
1120 a8083063 Iustin Pop

1121 3bc145d8 Bernardo Dal Seno
    This currently only checks the size, which must be around
1122 abdf0113 Iustin Pop
    128MiB.
1123 a8083063 Iustin Pop

1124 a8083063 Iustin Pop
    """
1125 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1126 abdf0113 Iustin Pop
    if result.failed:
1127 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
1128 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
1129 a8083063 Iustin Pop
    try:
1130 abdf0113 Iustin Pop
      sectors = int(result.stdout)
1131 691744c4 Iustin Pop
    except (TypeError, ValueError):
1132 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
1133 c04bc777 Iustin Pop
    num_bytes = sectors * 512
1134 c04bc777 Iustin Pop
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
1135 c04bc777 Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (num_bytes / 1024 / 1024))
1136 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
1137 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
1138 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
1139 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
1140 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
1141 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
1142 c04bc777 Iustin Pop
    if num_bytes > 1024 * 1024 * 1024:
1143 c04bc777 Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (num_bytes / 1024 / 1024))
1144 a8083063 Iustin Pop
1145 abdf0113 Iustin Pop
  def Rename(self, new_id):
1146 abdf0113 Iustin Pop
    """Rename a device.
1147 a8083063 Iustin Pop

1148 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1149 a8083063 Iustin Pop

1150 a8083063 Iustin Pop
    """
1151 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1152 a8083063 Iustin Pop
1153 f3e513ad Iustin Pop
1154 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1155 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1156 a2cfdea2 Iustin Pop

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

1161 5c755a4d Iustin Pop
  The unique_id for the drbd device is a (local_ip, local_port,
1162 5c755a4d Iustin Pop
  remote_ip, remote_port, local_minor, secret) tuple, and it must have
1163 5c755a4d Iustin Pop
  two children: the data device and the meta_device. The meta device
1164 5c755a4d Iustin Pop
  is checked for valid size and is zeroed on create.
1165 a2cfdea2 Iustin Pop

1166 a2cfdea2 Iustin Pop
  """
1167 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1168 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1169 a2cfdea2 Iustin Pop
1170 cf8df3f3 Iustin Pop
  # timeout constants
1171 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
1172 cf8df3f3 Iustin Pop
1173 8a69b3a8 Andrea Spadaccini
  # command line options for barriers
1174 8a69b3a8 Andrea Spadaccini
  _DISABLE_DISK_OPTION = "--no-disk-barrier"  # -a
1175 8a69b3a8 Andrea Spadaccini
  _DISABLE_DRAIN_OPTION = "--no-disk-drain"   # -D
1176 8a69b3a8 Andrea Spadaccini
  _DISABLE_FLUSH_OPTION = "--no-disk-flushes" # -i
1177 8a69b3a8 Andrea Spadaccini
  _DISABLE_META_FLUSH_OPTION = "--no-md-flushes"  # -m
1178 8a69b3a8 Andrea Spadaccini
1179 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
1180 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1181 fc1dc9d7 Iustin Pop
      children = []
1182 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
1183 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1184 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
1185 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1186 310fbb64 Iustin Pop
    (self._lhost, self._lport,
1187 310fbb64 Iustin Pop
     self._rhost, self._rport,
1188 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
1189 310fbb64 Iustin Pop
    if children:
1190 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
1191 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
1192 310fbb64 Iustin Pop
        children = []
1193 94dcbdb0 Andrea Spadaccini
    super(DRBD8, self).__init__(unique_id, children, size, params)
1194 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1195 fcee765d Manuel Franceschini
    version = self._GetVersion(self._GetProcData())
1196 e687ec01 Michael Hanselmann
    if version["k_major"] != 8:
1197 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
1198 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
1199 d0c8c01d Iustin Pop
                  version["k_major"], version["k_minor"])
1200 a2cfdea2 Iustin Pop
1201 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1202 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1203 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1204 ffa1c0dc Iustin Pop
                       (unique_id,))
1205 a2cfdea2 Iustin Pop
    self.Attach()
1206 a2cfdea2 Iustin Pop
1207 a2cfdea2 Iustin Pop
  @classmethod
1208 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1209 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1210 a2cfdea2 Iustin Pop

1211 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1212 a2cfdea2 Iustin Pop

1213 a2cfdea2 Iustin Pop
    """
1214 18e4dee6 Iustin Pop
    # Zero the metadata first, in order to make sure drbdmeta doesn't
1215 18e4dee6 Iustin Pop
    # try to auto-detect existing filesystems or similar (see
1216 18e4dee6 Iustin Pop
    # http://code.google.com/p/ganeti/issues/detail?id=182); we only
1217 18e4dee6 Iustin Pop
    # care about the first 128MB of data in the device, even though it
1218 18e4dee6 Iustin Pop
    # can be bigger
1219 18e4dee6 Iustin Pop
    result = utils.RunCmd([constants.DD_CMD,
1220 18e4dee6 Iustin Pop
                           "if=/dev/zero", "of=%s" % dev_path,
1221 18e4dee6 Iustin Pop
                           "bs=1048576", "count=128", "oflag=direct"])
1222 18e4dee6 Iustin Pop
    if result.failed:
1223 18e4dee6 Iustin Pop
      _ThrowError("Can't wipe the meta device: %s", result.output)
1224 18e4dee6 Iustin Pop
1225 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1226 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1227 a2cfdea2 Iustin Pop
    if result.failed:
1228 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1229 a2cfdea2 Iustin Pop
1230 a2cfdea2 Iustin Pop
  @classmethod
1231 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1232 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1233 a2cfdea2 Iustin Pop

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

1237 a2cfdea2 Iustin Pop
    """
1238 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1239 a2cfdea2 Iustin Pop
1240 a2cfdea2 Iustin Pop
    highest = None
1241 a2cfdea2 Iustin Pop
    for line in data:
1242 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1243 a2cfdea2 Iustin Pop
      if match:
1244 a2cfdea2 Iustin Pop
        return int(match.group(1))
1245 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1246 a2cfdea2 Iustin Pop
      if match:
1247 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1248 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1249 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1250 a2cfdea2 Iustin Pop
      return 0
1251 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1252 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1253 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1254 a2cfdea2 Iustin Pop
    return highest + 1
1255 a2cfdea2 Iustin Pop
1256 a2cfdea2 Iustin Pop
  @classmethod
1257 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1258 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1259 a2cfdea2 Iustin Pop

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

1263 a2cfdea2 Iustin Pop
    """
1264 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1265 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1266 a2cfdea2 Iustin Pop
1267 a2cfdea2 Iustin Pop
    # pyparsing setup
1268 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1269 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1270 5a672c30 Manuel Franceschini
    lbracket = pyp.Literal("[").suppress()
1271 5a672c30 Manuel Franceschini
    rbracket = pyp.Literal("]").suppress()
1272 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1273 5a672c30 Manuel Franceschini
    colon = pyp.Literal(":").suppress()
1274 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1275 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1276 a2cfdea2 Iustin Pop
1277 e687ec01 Michael Hanselmann
    comment = pyp.Literal("#") + pyp.Optional(pyp.restOfLine)
1278 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1279 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1280 a2cfdea2 Iustin Pop
1281 3ccb3a64 Michael Hanselmann
    keyword = pyp.Word(pyp.alphanums + "-")
1282 a2cfdea2 Iustin Pop
1283 a2cfdea2 Iustin Pop
    # value types
1284 3ccb3a64 Michael Hanselmann
    value = pyp.Word(pyp.alphanums + "_-/.:")
1285 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1286 5a672c30 Manuel Franceschini
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1287 5a672c30 Manuel Franceschini
                 pyp.Word(pyp.nums + ".") + colon + number)
1288 5a672c30 Manuel Franceschini
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
1289 5a672c30 Manuel Franceschini
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
1290 5a672c30 Manuel Franceschini
                 pyp.Optional(rbracket) + colon + number)
1291 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1292 5a672c30 Manuel Franceschini
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
1293 01e2ce3a Iustin Pop
    # device name, extended syntax
1294 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1295 a2cfdea2 Iustin Pop
1296 a2cfdea2 Iustin Pop
    # a statement
1297 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1298 5a672c30 Manuel Franceschini
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
1299 01e2ce3a Iustin Pop
                         device_value) +
1300 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1301 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1302 a2cfdea2 Iustin Pop
1303 a2cfdea2 Iustin Pop
    # an entire section
1304 d0c8c01d Iustin Pop
    section_name = pyp.Word(pyp.alphas + "_")
1305 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1306 a2cfdea2 Iustin Pop
1307 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1308 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1309 a2cfdea2 Iustin Pop
1310 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1311 a2cfdea2 Iustin Pop
1312 a2cfdea2 Iustin Pop
    return bnf
1313 a2cfdea2 Iustin Pop
1314 a2cfdea2 Iustin Pop
  @classmethod
1315 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1316 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1317 a2cfdea2 Iustin Pop

1318 a2cfdea2 Iustin Pop
    """
1319 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1320 a2cfdea2 Iustin Pop
    if result.failed:
1321 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1322 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1323 3840729d Iustin Pop
      return None
1324 3840729d Iustin Pop
    return result.stdout
1325 3840729d Iustin Pop
1326 3840729d Iustin Pop
  @classmethod
1327 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1328 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1329 3840729d Iustin Pop

1330 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1331 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1332 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1333 3840729d Iustin Pop
    by _GetShowData.
1334 3840729d Iustin Pop

1335 3840729d Iustin Pop
    """
1336 3840729d Iustin Pop
    data = {}
1337 a2cfdea2 Iustin Pop
    if not out:
1338 a2cfdea2 Iustin Pop
      return data
1339 a2cfdea2 Iustin Pop
1340 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1341 a2cfdea2 Iustin Pop
    # run pyparse
1342 a2cfdea2 Iustin Pop
1343 a2cfdea2 Iustin Pop
    try:
1344 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1345 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1346 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1347 a2cfdea2 Iustin Pop
1348 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1349 a2cfdea2 Iustin Pop
    for section in results:
1350 a2cfdea2 Iustin Pop
      sname = section[0]
1351 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1352 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1353 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1354 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1355 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1356 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1357 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1358 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1359 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1360 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1361 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1362 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1363 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1364 a2cfdea2 Iustin Pop
    return data
1365 a2cfdea2 Iustin Pop
1366 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1367 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1368 a2cfdea2 Iustin Pop

1369 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1370 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1371 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1372 a2cfdea2 Iustin Pop
    device.
1373 a2cfdea2 Iustin Pop

1374 a2cfdea2 Iustin Pop
    """
1375 b00b95dd Iustin Pop
    if self._children:
1376 b00b95dd Iustin Pop
      backend, meta = self._children
1377 b00b95dd Iustin Pop
    else:
1378 b00b95dd Iustin Pop
      backend = meta = None
1379 b00b95dd Iustin Pop
1380 a2cfdea2 Iustin Pop
    if backend is not None:
1381 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1382 a2cfdea2 Iustin Pop
    else:
1383 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1384 b00b95dd Iustin Pop
1385 a2cfdea2 Iustin Pop
    if meta is not None:
1386 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1387 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1388 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1389 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1390 a2cfdea2 Iustin Pop
    else:
1391 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1392 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1393 a2cfdea2 Iustin Pop
    return retval
1394 a2cfdea2 Iustin Pop
1395 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1396 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1397 a2cfdea2 Iustin Pop

1398 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1399 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1400 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1401 a2cfdea2 Iustin Pop
    device.
1402 a2cfdea2 Iustin Pop

1403 a2cfdea2 Iustin Pop
    """
1404 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1405 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1406 a2cfdea2 Iustin Pop
      return True
1407 a2cfdea2 Iustin Pop
1408 a2cfdea2 Iustin Pop
    if self._lhost is None:
1409 a2cfdea2 Iustin Pop
      return False
1410 a2cfdea2 Iustin Pop
1411 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1412 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1413 a2cfdea2 Iustin Pop
      return False
1414 a2cfdea2 Iustin Pop
1415 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1416 a2cfdea2 Iustin Pop
    retval = (retval and
1417 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1418 a2cfdea2 Iustin Pop
    return retval
1419 a2cfdea2 Iustin Pop
1420 8a69b3a8 Andrea Spadaccini
  def _AssembleLocal(self, minor, backend, meta, size):
1421 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1422 a2cfdea2 Iustin Pop

1423 a2cfdea2 Iustin Pop
    """
1424 8a69b3a8 Andrea Spadaccini
    args = ["drbdsetup", self._DevPath(minor), "disk",
1425 f069addf Iustin Pop
            backend, meta, "0",
1426 f069addf Iustin Pop
            "-e", "detach",
1427 f069addf Iustin Pop
            "--create-device"]
1428 60bca04a Iustin Pop
    if size:
1429 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1430 8a69b3a8 Andrea Spadaccini
1431 8a69b3a8 Andrea Spadaccini
    version = self._GetVersion(self._GetProcData())
1432 8a69b3a8 Andrea Spadaccini
    vmaj = version["k_major"]
1433 8a69b3a8 Andrea Spadaccini
    vmin = version["k_minor"]
1434 8a69b3a8 Andrea Spadaccini
    vrel = version["k_point"]
1435 8a69b3a8 Andrea Spadaccini
1436 8a69b3a8 Andrea Spadaccini
    barrier_args = \
1437 8a69b3a8 Andrea Spadaccini
      self._ComputeDiskBarrierArgs(vmaj, vmin, vrel,
1438 ac00bf1b Andrea Spadaccini
                                   self.params[constants.LDP_BARRIERS],
1439 ac00bf1b Andrea Spadaccini
                                   self.params[constants.LDP_NO_META_FLUSH])
1440 8a69b3a8 Andrea Spadaccini
    args.extend(barrier_args)
1441 8a69b3a8 Andrea Spadaccini
1442 ad1dd4c7 Andrea Spadaccini
    if self.params[constants.LDP_DISK_CUSTOM]:
1443 ad1dd4c7 Andrea Spadaccini
      args.extend(shlex.split(self.params[constants.LDP_DISK_CUSTOM]))
1444 ad1dd4c7 Andrea Spadaccini
1445 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1446 a2cfdea2 Iustin Pop
    if result.failed:
1447 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1448 a2cfdea2 Iustin Pop
1449 8a69b3a8 Andrea Spadaccini
  @classmethod
1450 8a69b3a8 Andrea Spadaccini
  def _ComputeDiskBarrierArgs(cls, vmaj, vmin, vrel, disabled_barriers,
1451 5ae4945a Iustin Pop
                              disable_meta_flush):
1452 8a69b3a8 Andrea Spadaccini
    """Compute the DRBD command line parameters for disk barriers
1453 8a69b3a8 Andrea Spadaccini

1454 8a69b3a8 Andrea Spadaccini
    Returns a list of the disk barrier parameters as requested via the
1455 8a69b3a8 Andrea Spadaccini
    disabled_barriers and disable_meta_flush arguments, and according to the
1456 8a69b3a8 Andrea Spadaccini
    supported ones in the DRBD version vmaj.vmin.vrel
1457 8a69b3a8 Andrea Spadaccini

1458 8a69b3a8 Andrea Spadaccini
    If the desired option is unsupported, raises errors.BlockDeviceError.
1459 8a69b3a8 Andrea Spadaccini

1460 8a69b3a8 Andrea Spadaccini
    """
1461 8a69b3a8 Andrea Spadaccini
    disabled_barriers_set = frozenset(disabled_barriers)
1462 8a69b3a8 Andrea Spadaccini
    if not disabled_barriers_set in constants.DRBD_VALID_BARRIER_OPT:
1463 8a69b3a8 Andrea Spadaccini
      raise errors.BlockDeviceError("%s is not a valid option set for DRBD"
1464 8a69b3a8 Andrea Spadaccini
                                    " barriers" % disabled_barriers)
1465 8a69b3a8 Andrea Spadaccini
1466 8a69b3a8 Andrea Spadaccini
    args = []
1467 8a69b3a8 Andrea Spadaccini
1468 8a69b3a8 Andrea Spadaccini
    # The following code assumes DRBD 8.x, with x < 4 and x != 1 (DRBD 8.1.x
1469 8a69b3a8 Andrea Spadaccini
    # does not exist)
1470 8a69b3a8 Andrea Spadaccini
    if not vmaj == 8 and vmin in (0, 2, 3):
1471 8a69b3a8 Andrea Spadaccini
      raise errors.BlockDeviceError("Unsupported DRBD version: %d.%d.%d" %
1472 8a69b3a8 Andrea Spadaccini
                                    (vmaj, vmin, vrel))
1473 8a69b3a8 Andrea Spadaccini
1474 8a69b3a8 Andrea Spadaccini
    def _AppendOrRaise(option, min_version):
1475 8a69b3a8 Andrea Spadaccini
      """Helper for DRBD options"""
1476 8a69b3a8 Andrea Spadaccini
      if min_version is not None and vrel >= min_version:
1477 8a69b3a8 Andrea Spadaccini
        args.append(option)
1478 8a69b3a8 Andrea Spadaccini
      else:
1479 8a69b3a8 Andrea Spadaccini
        raise errors.BlockDeviceError("Could not use the option %s as the"
1480 8a69b3a8 Andrea Spadaccini
                                      " DRBD version %d.%d.%d does not support"
1481 8a69b3a8 Andrea Spadaccini
                                      " it." % (option, vmaj, vmin, vrel))
1482 8a69b3a8 Andrea Spadaccini
1483 8a69b3a8 Andrea Spadaccini
    # the minimum version for each feature is encoded via pairs of (minor
1484 8a69b3a8 Andrea Spadaccini
    # version -> x) where x is version in which support for the option was
1485 8a69b3a8 Andrea Spadaccini
    # introduced.
1486 8a69b3a8 Andrea Spadaccini
    meta_flush_supported = disk_flush_supported = {
1487 8a69b3a8 Andrea Spadaccini
      0: 12,
1488 8a69b3a8 Andrea Spadaccini
      2: 7,
1489 8a69b3a8 Andrea Spadaccini
      3: 0,
1490 8a69b3a8 Andrea Spadaccini
      }
1491 8a69b3a8 Andrea Spadaccini
1492 8a69b3a8 Andrea Spadaccini
    disk_drain_supported = {
1493 8a69b3a8 Andrea Spadaccini
      2: 7,
1494 8a69b3a8 Andrea Spadaccini
      3: 0,
1495 8a69b3a8 Andrea Spadaccini
      }
1496 8a69b3a8 Andrea Spadaccini
1497 8a69b3a8 Andrea Spadaccini
    disk_barriers_supported = {
1498 8a69b3a8 Andrea Spadaccini
      3: 0,
1499 8a69b3a8 Andrea Spadaccini
      }
1500 8a69b3a8 Andrea Spadaccini
1501 8a69b3a8 Andrea Spadaccini
    # meta flushes
1502 8a69b3a8 Andrea Spadaccini
    if disable_meta_flush:
1503 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_META_FLUSH_OPTION,
1504 8a69b3a8 Andrea Spadaccini
                     meta_flush_supported.get(vmin, None))
1505 8a69b3a8 Andrea Spadaccini
1506 8a69b3a8 Andrea Spadaccini
    # disk flushes
1507 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set:
1508 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_FLUSH_OPTION,
1509 8a69b3a8 Andrea Spadaccini
                     disk_flush_supported.get(vmin, None))
1510 8a69b3a8 Andrea Spadaccini
1511 8a69b3a8 Andrea Spadaccini
    # disk drain
1512 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set:
1513 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_DRAIN_OPTION,
1514 8a69b3a8 Andrea Spadaccini
                     disk_drain_supported.get(vmin, None))
1515 8a69b3a8 Andrea Spadaccini
1516 8a69b3a8 Andrea Spadaccini
    # disk barriers
1517 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set:
1518 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_DISK_OPTION,
1519 8a69b3a8 Andrea Spadaccini
                     disk_barriers_supported.get(vmin, None))
1520 8a69b3a8 Andrea Spadaccini
1521 8a69b3a8 Andrea Spadaccini
    return args
1522 8a69b3a8 Andrea Spadaccini
1523 6e9814a1 Andrea Spadaccini
  def _AssembleNet(self, minor, net_info, protocol,
1524 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1525 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1526 a2cfdea2 Iustin Pop

1527 a2cfdea2 Iustin Pop
    """
1528 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1529 52857176 Iustin Pop
    if None in net_info:
1530 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1531 52857176 Iustin Pop
      # sure its shutdown
1532 6e9814a1 Andrea Spadaccini
      self._ShutdownNet(minor)
1533 1063abd1 Iustin Pop
      return
1534 52857176 Iustin Pop
1535 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1536 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1537 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1538 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1539 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1540 7d585316 Iustin Pop
    # about its peer.
1541 8584e922 Andrea Spadaccini
    sync_errors = self._SetMinorSyncParams(minor, self.params)
1542 8584e922 Andrea Spadaccini
    if sync_errors:
1543 8584e922 Andrea Spadaccini
      _ThrowError("drbd%d: can't set the synchronization parameters: %s" %
1544 8584e922 Andrea Spadaccini
                  (minor, utils.CommaJoin(sync_errors)))
1545 7d585316 Iustin Pop
1546 8b312c1d Manuel Franceschini
    if netutils.IP6Address.IsValid(lhost):
1547 8b312c1d Manuel Franceschini
      if not netutils.IP6Address.IsValid(rhost):
1548 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1549 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1550 5a672c30 Manuel Franceschini
      family = "ipv6"
1551 8b312c1d Manuel Franceschini
    elif netutils.IP4Address.IsValid(lhost):
1552 8b312c1d Manuel Franceschini
      if not netutils.IP4Address.IsValid(rhost):
1553 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1554 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1555 5a672c30 Manuel Franceschini
      family = "ipv4"
1556 5a672c30 Manuel Franceschini
    else:
1557 5a672c30 Manuel Franceschini
      _ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
1558 5a672c30 Manuel Franceschini
1559 6e9814a1 Andrea Spadaccini
    args = ["drbdsetup", self._DevPath(minor), "net",
1560 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, lhost, lport),
1561 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, rhost, rport), protocol,
1562 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1563 f38478b2 Iustin Pop
            "-B", "consensus",
1564 ab6cc81c Iustin Pop
            "--create-device",
1565 f38478b2 Iustin Pop
            ]
1566 a2cfdea2 Iustin Pop
    if dual_pri:
1567 a2cfdea2 Iustin Pop
      args.append("-m")
1568 a2cfdea2 Iustin Pop
    if hmac and secret:
1569 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1570 ad1dd4c7 Andrea Spadaccini
1571 ad1dd4c7 Andrea Spadaccini
    if self.params[constants.LDP_NET_CUSTOM]:
1572 ad1dd4c7 Andrea Spadaccini
      args.extend(shlex.split(self.params[constants.LDP_NET_CUSTOM]))
1573 ad1dd4c7 Andrea Spadaccini
1574 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1575 a2cfdea2 Iustin Pop
    if result.failed:
1576 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1577 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1578 a2cfdea2 Iustin Pop
1579 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1580 6e9814a1 Andrea Spadaccini
      info = self._GetDevInfo(self._GetShowData(minor))
1581 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1582 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1583 def8e2f6 Michael Hanselmann
1584 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1585 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1586 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1587 def8e2f6 Michael Hanselmann
1588 def8e2f6 Michael Hanselmann
    try:
1589 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1590 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1591 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1592 a2cfdea2 Iustin Pop
1593 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1594 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1595 b00b95dd Iustin Pop

1596 b00b95dd Iustin Pop
    """
1597 b00b95dd Iustin Pop
    if self.minor is None:
1598 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1599 82463074 Iustin Pop
                  self._aminor)
1600 b00b95dd Iustin Pop
    if len(devices) != 2:
1601 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1602 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1603 03ece5f3 Iustin Pop
    if "local_dev" in info:
1604 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1605 b00b95dd Iustin Pop
    backend, meta = devices
1606 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1607 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1608 b00b95dd Iustin Pop
    backend.Open()
1609 b00b95dd Iustin Pop
    meta.Open()
1610 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1611 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1612 b00b95dd Iustin Pop
1613 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1614 b00b95dd Iustin Pop
    self._children = devices
1615 b00b95dd Iustin Pop
1616 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1617 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1618 b00b95dd Iustin Pop

1619 b00b95dd Iustin Pop
    """
1620 b00b95dd Iustin Pop
    if self.minor is None:
1621 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1622 82463074 Iustin Pop
                  self._aminor)
1623 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1624 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1625 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1626 03ece5f3 Iustin Pop
      return
1627 b00b95dd Iustin Pop
    if len(self._children) != 2:
1628 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1629 82463074 Iustin Pop
                  self._children)
1630 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1631 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1632 e739bd57 Iustin Pop
      return
1633 b00b95dd Iustin Pop
    if len(devices) != 2:
1634 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1635 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1636 e739bd57 Iustin Pop
      if dev != child.dev_path:
1637 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1638 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1639 b00b95dd Iustin Pop
1640 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1641 b00b95dd Iustin Pop
    self._children = []
1642 b00b95dd Iustin Pop
1643 7d585316 Iustin Pop
  @classmethod
1644 f2f57b6e Andrea Spadaccini
  def _SetMinorSyncParams(cls, minor, params):
1645 f2f57b6e Andrea Spadaccini
    """Set the parameters of the DRBD syncer.
1646 a2cfdea2 Iustin Pop

1647 7d585316 Iustin Pop
    This is the low-level implementation.
1648 7d585316 Iustin Pop

1649 7d585316 Iustin Pop
    @type minor: int
1650 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1651 f2f57b6e Andrea Spadaccini
    @type params: dict
1652 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1653 8584e922 Andrea Spadaccini
    @rtype: list
1654 8584e922 Andrea Spadaccini
    @return: a list of error messages
1655 7d585316 Iustin Pop

1656 a2cfdea2 Iustin Pop
    """
1657 f2f57b6e Andrea Spadaccini
1658 f2f57b6e Andrea Spadaccini
    args = ["drbdsetup", cls._DevPath(minor), "syncer"]
1659 f2f57b6e Andrea Spadaccini
    if params[constants.LDP_DYNAMIC_RESYNC]:
1660 f2f57b6e Andrea Spadaccini
      version = cls._GetVersion(cls._GetProcData())
1661 f2f57b6e Andrea Spadaccini
      vmin = version["k_minor"]
1662 f2f57b6e Andrea Spadaccini
      vrel = version["k_point"]
1663 f2f57b6e Andrea Spadaccini
1664 f2f57b6e Andrea Spadaccini
      # By definition we are using 8.x, so just check the rest of the version
1665 f2f57b6e Andrea Spadaccini
      # number
1666 f2f57b6e Andrea Spadaccini
      if vmin != 3 or vrel < 9:
1667 8584e922 Andrea Spadaccini
        msg = ("The current DRBD version (8.%d.%d) does not support the "
1668 8584e922 Andrea Spadaccini
               "dynamic resync speed controller" % (vmin, vrel))
1669 8584e922 Andrea Spadaccini
        logging.error(msg)
1670 8584e922 Andrea Spadaccini
        return [msg]
1671 8584e922 Andrea Spadaccini
1672 8584e922 Andrea Spadaccini
      if params[constants.LDP_PLAN_AHEAD] == 0:
1673 8584e922 Andrea Spadaccini
        msg = ("A value of 0 for c-plan-ahead disables the dynamic sync speed"
1674 8584e922 Andrea Spadaccini
               " controller at DRBD level. If you want to disable it, please"
1675 8584e922 Andrea Spadaccini
               " set the dynamic-resync disk parameter to False.")
1676 8584e922 Andrea Spadaccini
        logging.error(msg)
1677 8584e922 Andrea Spadaccini
        return [msg]
1678 f2f57b6e Andrea Spadaccini
1679 f2f57b6e Andrea Spadaccini
      # add the c-* parameters to args
1680 8584e922 Andrea Spadaccini
      args.extend(["--c-plan-ahead", params[constants.LDP_PLAN_AHEAD],
1681 8584e922 Andrea Spadaccini
                   "--c-fill-target", params[constants.LDP_FILL_TARGET],
1682 8584e922 Andrea Spadaccini
                   "--c-delay-target", params[constants.LDP_DELAY_TARGET],
1683 8584e922 Andrea Spadaccini
                   "--c-max-rate", params[constants.LDP_MAX_RATE],
1684 8584e922 Andrea Spadaccini
                   "--c-min-rate", params[constants.LDP_MIN_RATE],
1685 5ae4945a Iustin Pop
                   ])
1686 f2f57b6e Andrea Spadaccini
1687 f2f57b6e Andrea Spadaccini
    else:
1688 f2f57b6e Andrea Spadaccini
      args.extend(["-r", "%d" % params[constants.LDP_RESYNC_RATE]])
1689 f2f57b6e Andrea Spadaccini
1690 f2f57b6e Andrea Spadaccini
    args.append("--create-device")
1691 f2f57b6e Andrea Spadaccini
    result = utils.RunCmd(args)
1692 a2cfdea2 Iustin Pop
    if result.failed:
1693 8584e922 Andrea Spadaccini
      msg = ("Can't change syncer rate: %s - %s" %
1694 8584e922 Andrea Spadaccini
             (result.fail_reason, result.output))
1695 8584e922 Andrea Spadaccini
      logging.error(msg)
1696 e0b57a5c Michael Hanselmann
      return [msg]
1697 8584e922 Andrea Spadaccini
1698 8584e922 Andrea Spadaccini
    return []
1699 7d585316 Iustin Pop
1700 f2f57b6e Andrea Spadaccini
  def SetSyncParams(self, params):
1701 f2f57b6e Andrea Spadaccini
    """Set the synchronization parameters of the DRBD syncer.
1702 7d585316 Iustin Pop

1703 f2f57b6e Andrea Spadaccini
    @type params: dict
1704 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1705 8584e922 Andrea Spadaccini
    @rtype: list
1706 8584e922 Andrea Spadaccini
    @return: a list of error messages, emitted both by the current node and by
1707 8584e922 Andrea Spadaccini
    children. An empty list means no errors
1708 7d585316 Iustin Pop

1709 7d585316 Iustin Pop
    """
1710 7d585316 Iustin Pop
    if self.minor is None:
1711 8584e922 Andrea Spadaccini
      err = "Not attached during SetSyncParams"
1712 8584e922 Andrea Spadaccini
      logging.info(err)
1713 8584e922 Andrea Spadaccini
      return [err]
1714 8584e922 Andrea Spadaccini
1715 f2f57b6e Andrea Spadaccini
    children_result = super(DRBD8, self).SetSyncParams(params)
1716 8584e922 Andrea Spadaccini
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
1717 8584e922 Andrea Spadaccini
    return children_result
1718 a2cfdea2 Iustin Pop
1719 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
1720 a3fffcc6 René Nussbaumer
    """Pauses or resumes the sync of a DRBD device.
1721 a3fffcc6 René Nussbaumer

1722 a3fffcc6 René Nussbaumer
    @param pause: Wether to pause or resume
1723 a3fffcc6 René Nussbaumer
    @return: the success of the operation
1724 a3fffcc6 René Nussbaumer

1725 a3fffcc6 René Nussbaumer
    """
1726 a3fffcc6 René Nussbaumer
    if self.minor is None:
1727 a3fffcc6 René Nussbaumer
      logging.info("Not attached during PauseSync")
1728 a3fffcc6 René Nussbaumer
      return False
1729 a3fffcc6 René Nussbaumer
1730 a3fffcc6 René Nussbaumer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
1731 a3fffcc6 René Nussbaumer
1732 a3fffcc6 René Nussbaumer
    if pause:
1733 a3fffcc6 René Nussbaumer
      cmd = "pause-sync"
1734 a3fffcc6 René Nussbaumer
    else:
1735 a3fffcc6 René Nussbaumer
      cmd = "resume-sync"
1736 a3fffcc6 René Nussbaumer
1737 a3fffcc6 René Nussbaumer
    result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
1738 a3fffcc6 René Nussbaumer
    if result.failed:
1739 a3fffcc6 René Nussbaumer
      logging.error("Can't %s: %s - %s", cmd,
1740 a3fffcc6 René Nussbaumer
                    result.fail_reason, result.output)
1741 a3fffcc6 René Nussbaumer
    return not result.failed and children_result
1742 a3fffcc6 René Nussbaumer
1743 6b90c22e Iustin Pop
  def GetProcStatus(self):
1744 6b90c22e Iustin Pop
    """Return device data from /proc.
1745 6b90c22e Iustin Pop

1746 6b90c22e Iustin Pop
    """
1747 6b90c22e Iustin Pop
    if self.minor is None:
1748 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1749 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1750 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1751 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1752 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1753 6b90c22e Iustin Pop
1754 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1755 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1756 a2cfdea2 Iustin Pop

1757 a2cfdea2 Iustin Pop

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

1762 0834c866 Iustin Pop

1763 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1764 0834c866 Iustin Pop
    network not connected or local disk missing.
1765 0834c866 Iustin Pop

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

1769 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1770 c41eea6e Iustin Pop

1771 a2cfdea2 Iustin Pop
    """
1772 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1773 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1774 96acbc09 Michael Hanselmann
1775 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1776 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1777 f208978a Michael Hanselmann
1778 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1779 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1780 f208978a Michael Hanselmann
    elif stats.is_diskless:
1781 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1782 f208978a Michael Hanselmann
    else:
1783 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1784 96acbc09 Michael Hanselmann
1785 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1786 96acbc09 Michael Hanselmann
                                  major=self.major,
1787 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1788 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1789 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1790 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1791 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1792 a2cfdea2 Iustin Pop
1793 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1794 a2cfdea2 Iustin Pop
    """Make the local state primary.
1795 a2cfdea2 Iustin Pop

1796 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1797 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1798 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1799 f860ff4e Guido Trotter
    is mandatory.
1800 a2cfdea2 Iustin Pop

1801 a2cfdea2 Iustin Pop
    """
1802 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1803 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1804 a2cfdea2 Iustin Pop
      return False
1805 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1806 a2cfdea2 Iustin Pop
    if force:
1807 a2cfdea2 Iustin Pop
      cmd.append("-o")
1808 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1809 a2cfdea2 Iustin Pop
    if result.failed:
1810 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1811 82463074 Iustin Pop
                  result.output)
1812 a2cfdea2 Iustin Pop
1813 a2cfdea2 Iustin Pop
  def Close(self):
1814 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1815 a2cfdea2 Iustin Pop

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

1818 a2cfdea2 Iustin Pop
    """
1819 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1820 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1821 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1822 a2cfdea2 Iustin Pop
    if result.failed:
1823 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1824 82463074 Iustin Pop
                  self.minor, result.output)
1825 a2cfdea2 Iustin Pop
1826 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1827 cf8df3f3 Iustin Pop
    """Removes network configuration.
1828 cf8df3f3 Iustin Pop

1829 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1830 cf8df3f3 Iustin Pop

1831 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1832 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1833 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1834 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1835 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1836 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1837 cf8df3f3 Iustin Pop
    exception.
1838 cf8df3f3 Iustin Pop

1839 cf8df3f3 Iustin Pop
    """
1840 cf8df3f3 Iustin Pop
    if self.minor is None:
1841 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1842 cf8df3f3 Iustin Pop
1843 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1844 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1845 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1846 cf8df3f3 Iustin Pop
1847 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1848 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1849 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1850 cf8df3f3 Iustin Pop
1851 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1852 def8e2f6 Michael Hanselmann
1853 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1854 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1855 def8e2f6 Michael Hanselmann
        return
1856 def8e2f6 Michael Hanselmann
1857 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1858 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1859 def8e2f6 Michael Hanselmann
      # forgotten
1860 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1861 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1862 def8e2f6 Michael Hanselmann
1863 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1864 def8e2f6 Michael Hanselmann
1865 def8e2f6 Michael Hanselmann
    # Keep start time
1866 def8e2f6 Michael Hanselmann
    start_time = time.time()
1867 def8e2f6 Michael Hanselmann
1868 def8e2f6 Michael Hanselmann
    try:
1869 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1870 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1871 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1872 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1873 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1874 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1875 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1876 cf8df3f3 Iustin Pop
      else:
1877 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1878 def8e2f6 Michael Hanselmann
1879 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1880 cf8df3f3 Iustin Pop
1881 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1882 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1883 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1884 82463074 Iustin Pop
                   self.minor, reconfig_time)
1885 cf8df3f3 Iustin Pop
1886 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1887 cf8df3f3 Iustin Pop
    """Reconnects the network.
1888 cf8df3f3 Iustin Pop

1889 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1890 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1891 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1892 cf8df3f3 Iustin Pop

1893 cf8df3f3 Iustin Pop
    Args:
1894 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1895 cf8df3f3 Iustin Pop

1896 cf8df3f3 Iustin Pop
    """
1897 cf8df3f3 Iustin Pop
    if self.minor is None:
1898 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1899 cf8df3f3 Iustin Pop
1900 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1901 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1902 cf8df3f3 Iustin Pop
1903 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1904 cf8df3f3 Iustin Pop
1905 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1906 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1907 cf8df3f3 Iustin Pop
1908 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1909 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1910 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1911 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1912 cf8df3f3 Iustin Pop
1913 a2cfdea2 Iustin Pop
  def Attach(self):
1914 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1915 2d0c8319 Iustin Pop

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

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

1923 2d0c8319 Iustin Pop
    """
1924 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1925 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1926 2d0c8319 Iustin Pop
      minor = self._aminor
1927 2d0c8319 Iustin Pop
    else:
1928 2d0c8319 Iustin Pop
      minor = None
1929 2d0c8319 Iustin Pop
1930 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1931 2d0c8319 Iustin Pop
    return minor is not None
1932 2d0c8319 Iustin Pop
1933 2d0c8319 Iustin Pop
  def Assemble(self):
1934 2d0c8319 Iustin Pop
    """Assemble the drbd.
1935 2d0c8319 Iustin Pop

1936 2d0c8319 Iustin Pop
    Method:
1937 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1938 2d0c8319 Iustin Pop
        our config
1939 2d0c8319 Iustin Pop
      - if not, we create it from zero
1940 d529599f Andrea Spadaccini
      - anyway, set the device parameters
1941 2d0c8319 Iustin Pop

1942 2d0c8319 Iustin Pop
    """
1943 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1944 2d0c8319 Iustin Pop
1945 2d0c8319 Iustin Pop
    self.Attach()
1946 2d0c8319 Iustin Pop
    if self.minor is None:
1947 2d0c8319 Iustin Pop
      # local device completely unconfigured
1948 1063abd1 Iustin Pop
      self._FastAssemble()
1949 2d0c8319 Iustin Pop
    else:
1950 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1951 2d0c8319 Iustin Pop
      # the device
1952 1063abd1 Iustin Pop
      self._SlowAssemble()
1953 2d0c8319 Iustin Pop
1954 8584e922 Andrea Spadaccini
    sync_errors = self.SetSyncParams(self.params)
1955 8584e922 Andrea Spadaccini
    if sync_errors:
1956 8584e922 Andrea Spadaccini
      _ThrowError("drbd%d: can't set the synchronization parameters: %s" %
1957 8584e922 Andrea Spadaccini
                  (self.minor, utils.CommaJoin(sync_errors)))
1958 d529599f Andrea Spadaccini
1959 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1960 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1961 a2cfdea2 Iustin Pop

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

1966 a2cfdea2 Iustin Pop
    """
1967 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
1968 b459a848 Andrea Spadaccini
    # pylint: disable=W0631
1969 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1970 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1971 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1972 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1973 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1974 1063abd1 Iustin Pop
1975 a2cfdea2 Iustin Pop
      if match_l and match_r:
1976 1063abd1 Iustin Pop
        # everything matches
1977 a2cfdea2 Iustin Pop
        break
1978 1063abd1 Iustin Pop
1979 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1980 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1981 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1982 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1983 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1984 1063abd1 Iustin Pop
          break
1985 1063abd1 Iustin Pop
        else:
1986 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1987 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1988 1063abd1 Iustin Pop
1989 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1990 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1991 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1992 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1993 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1994 1063abd1 Iustin Pop
          break
1995 1063abd1 Iustin Pop
        else:
1996 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1997 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1998 bf25af3b Iustin Pop
1999 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
2000 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
2001 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
2002 bf25af3b Iustin Pop
      # configuration
2003 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
2004 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
2005 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
2006 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
2007 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
2008 9cdbe77f Iustin Pop
        # reconnect to our correct one
2009 1063abd1 Iustin Pop
        try:
2010 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
2011 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
2012 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
2013 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
2014 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
2015 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
2016 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
2017 9cdbe77f Iustin Pop
        # None)
2018 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
2019 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2020 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2021 9cdbe77f Iustin Pop
          break
2022 1063abd1 Iustin Pop
        else:
2023 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
2024 1063abd1 Iustin Pop
                      " show' disagrees", minor)
2025 9cdbe77f Iustin Pop
2026 a2cfdea2 Iustin Pop
    else:
2027 a2cfdea2 Iustin Pop
      minor = None
2028 a2cfdea2 Iustin Pop
2029 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2030 1063abd1 Iustin Pop
    if minor is None:
2031 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
2032 1063abd1 Iustin Pop
                  self._aminor)
2033 a2cfdea2 Iustin Pop
2034 2d0c8319 Iustin Pop
  def _FastAssemble(self):
2035 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
2036 a2cfdea2 Iustin Pop

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

2039 a2cfdea2 Iustin Pop
    """
2040 a1578d63 Iustin Pop
    minor = self._aminor
2041 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
2042 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
2043 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
2044 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
2045 1063abd1 Iustin Pop
      self._AssembleNet(minor,
2046 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
2047 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
2048 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2049 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2050 a2cfdea2 Iustin Pop
2051 a2cfdea2 Iustin Pop
  @classmethod
2052 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
2053 b00b95dd Iustin Pop
    """Detach from the local device.
2054 b00b95dd Iustin Pop

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

2058 b00b95dd Iustin Pop
    """
2059 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2060 b00b95dd Iustin Pop
    if result.failed:
2061 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
2062 b00b95dd Iustin Pop
2063 b00b95dd Iustin Pop
  @classmethod
2064 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2065 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2066 f3e513ad Iustin Pop

2067 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2068 f3e513ad Iustin Pop

2069 f3e513ad Iustin Pop
    """
2070 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2071 a8459f1c Iustin Pop
    if result.failed:
2072 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
2073 f3e513ad Iustin Pop
2074 f3e513ad Iustin Pop
  @classmethod
2075 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2076 a2cfdea2 Iustin Pop
    """Deactivate the device.
2077 a2cfdea2 Iustin Pop

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

2080 a2cfdea2 Iustin Pop
    """
2081 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2082 a2cfdea2 Iustin Pop
    if result.failed:
2083 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
2084 33bc6f01 Iustin Pop
                  minor, result.output)
2085 a2cfdea2 Iustin Pop
2086 a2cfdea2 Iustin Pop
  def Shutdown(self):
2087 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2088 a2cfdea2 Iustin Pop

2089 a2cfdea2 Iustin Pop
    """
2090 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2091 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
2092 746f7476 Iustin Pop
      return
2093 746f7476 Iustin Pop
    minor = self.minor
2094 a2cfdea2 Iustin Pop
    self.minor = None
2095 a2cfdea2 Iustin Pop
    self.dev_path = None
2096 746f7476 Iustin Pop
    self._ShutdownAll(minor)
2097 a2cfdea2 Iustin Pop
2098 a2cfdea2 Iustin Pop
  def Remove(self):
2099 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2100 a2cfdea2 Iustin Pop

2101 a2cfdea2 Iustin Pop
    """
2102 0c6c04ec Iustin Pop
    self.Shutdown()
2103 a2cfdea2 Iustin Pop
2104 a2cfdea2 Iustin Pop
  @classmethod
2105 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2106 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2107 a2cfdea2 Iustin Pop

2108 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2109 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2110 a2cfdea2 Iustin Pop

2111 a2cfdea2 Iustin Pop
    """
2112 a2cfdea2 Iustin Pop
    if len(children) != 2:
2113 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2114 767d52d3 Iustin Pop
    # check that the minor is unused
2115 767d52d3 Iustin Pop
    aminor = unique_id[4]
2116 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
2117 767d52d3 Iustin Pop
    if aminor in proc_info:
2118 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
2119 767d52d3 Iustin Pop
      in_use = status.is_in_use
2120 767d52d3 Iustin Pop
    else:
2121 767d52d3 Iustin Pop
      in_use = False
2122 767d52d3 Iustin Pop
    if in_use:
2123 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
2124 a2cfdea2 Iustin Pop
    meta = children[1]
2125 a2cfdea2 Iustin Pop
    meta.Assemble()
2126 a2cfdea2 Iustin Pop
    if not meta.Attach():
2127 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
2128 33bc6f01 Iustin Pop
                  aminor, meta)
2129 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
2130 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
2131 94dcbdb0 Andrea Spadaccini
    return cls(unique_id, children, size, params)
2132 a2cfdea2 Iustin Pop
2133 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2134 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
2135 1005d816 Iustin Pop

2136 1005d816 Iustin Pop
    """
2137 1005d816 Iustin Pop
    if self.minor is None:
2138 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
2139 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
2140 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
2141 cad0723b Iustin Pop
    self._children[0].Grow(amount, dryrun, backingstore)
2142 cad0723b Iustin Pop
    if dryrun or backingstore:
2143 cad0723b Iustin Pop
      # DRBD does not support dry-run mode and is not backing storage,
2144 cad0723b Iustin Pop
      # so we'll return here
2145 7fe23d47 Iustin Pop
      return
2146 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
2147 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
2148 1005d816 Iustin Pop
    if result.failed:
2149 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
2150 1005d816 Iustin Pop
2151 a8083063 Iustin Pop
2152 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
2153 6f695a2e Manuel Franceschini
  """File device.
2154 abdf0113 Iustin Pop

2155 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
2156 6f695a2e Manuel Franceschini

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

2159 6f695a2e Manuel Franceschini
  """
2160 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2161 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
2162 6f695a2e Manuel Franceschini

2163 6f695a2e Manuel Franceschini
    """
2164 6f695a2e Manuel Franceschini
    if children:
2165 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
2166 94dcbdb0 Andrea Spadaccini
    super(FileStorage, self).__init__(unique_id, children, size, params)
2167 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2168 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2169 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
2170 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
2171 ecb091e3 Iustin Pop
    self.Attach()
2172 6f695a2e Manuel Franceschini
2173 6f695a2e Manuel Franceschini
  def Assemble(self):
2174 6f695a2e Manuel Franceschini
    """Assemble the device.
2175 6f695a2e Manuel Franceschini

2176 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
2177 6f695a2e Manuel Franceschini

2178 6f695a2e Manuel Franceschini
    """
2179 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
2180 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
2181 6f695a2e Manuel Franceschini
2182 6f695a2e Manuel Franceschini
  def Shutdown(self):
2183 6f695a2e Manuel Franceschini
    """Shutdown the device.
2184 6f695a2e Manuel Franceschini

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

2188 6f695a2e Manuel Franceschini
    """
2189 746f7476 Iustin Pop
    pass
2190 6f695a2e Manuel Franceschini
2191 6f695a2e Manuel Franceschini
  def Open(self, force=False):
2192 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
2193 6f695a2e Manuel Franceschini

2194 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2195 6f695a2e Manuel Franceschini

2196 6f695a2e Manuel Franceschini
    """
2197 6f695a2e Manuel Franceschini
    pass
2198 6f695a2e Manuel Franceschini
2199 6f695a2e Manuel Franceschini
  def Close(self):
2200 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
2201 6f695a2e Manuel Franceschini

2202 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2203 6f695a2e Manuel Franceschini

2204 6f695a2e Manuel Franceschini
    """
2205 6f695a2e Manuel Franceschini
    pass
2206 6f695a2e Manuel Franceschini
2207 6f695a2e Manuel Franceschini
  def Remove(self):
2208 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
2209 6f695a2e Manuel Franceschini

2210 c41eea6e Iustin Pop
    @rtype: boolean
2211 c41eea6e Iustin Pop
    @return: True if the removal was successful
2212 6f695a2e Manuel Franceschini

2213 6f695a2e Manuel Franceschini
    """
2214 6f695a2e Manuel Franceschini
    try:
2215 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
2216 6f695a2e Manuel Franceschini
    except OSError, err:
2217 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
2218 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
2219 6f695a2e Manuel Franceschini
2220 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
2221 bbe4cc16 Iustin Pop
    """Renames the file.
2222 bbe4cc16 Iustin Pop

2223 bbe4cc16 Iustin Pop
    """
2224 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
2225 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
2226 bbe4cc16 Iustin Pop
2227 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2228 bbe4cc16 Iustin Pop
    """Grow the file
2229 bbe4cc16 Iustin Pop

2230 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2231 bbe4cc16 Iustin Pop

2232 bbe4cc16 Iustin Pop
    """
2233 cad0723b Iustin Pop
    if not backingstore:
2234 cad0723b Iustin Pop
      return
2235 91e2d9ec Guido Trotter
    # Check that the file exists
2236 91e2d9ec Guido Trotter
    self.Assemble()
2237 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
2238 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
2239 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
2240 7fe23d47 Iustin Pop
    # We can't really simulate the growth
2241 7fe23d47 Iustin Pop
    if dryrun:
2242 7fe23d47 Iustin Pop
      return
2243 91e2d9ec Guido Trotter
    try:
2244 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
2245 91e2d9ec Guido Trotter
      f.truncate(new_size)
2246 91e2d9ec Guido Trotter
      f.close()
2247 91e2d9ec Guido Trotter
    except EnvironmentError, err:
2248 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
2249 bbe4cc16 Iustin Pop
2250 6f695a2e Manuel Franceschini
  def Attach(self):
2251 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2252 6f695a2e Manuel Franceschini

2253 6f695a2e Manuel Franceschini
    Check if this file already exists.
2254 6f695a2e Manuel Franceschini

2255 c41eea6e Iustin Pop
    @rtype: boolean
2256 c41eea6e Iustin Pop
    @return: True if file exists
2257 6f695a2e Manuel Franceschini

2258 6f695a2e Manuel Franceschini
    """
2259 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
2260 ecb091e3 Iustin Pop
    return self.attached
2261 6f695a2e Manuel Franceschini
2262 fcff3897 Iustin Pop
  def GetActualSize(self):
2263 fcff3897 Iustin Pop
    """Return the actual disk size.
2264 fcff3897 Iustin Pop

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

2267 fcff3897 Iustin Pop
    """
2268 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2269 fcff3897 Iustin Pop
    try:
2270 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2271 fcff3897 Iustin Pop
      return st.st_size
2272 fcff3897 Iustin Pop
    except OSError, err:
2273 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2274 fcff3897 Iustin Pop
2275 6f695a2e Manuel Franceschini
  @classmethod
2276 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2277 6f695a2e Manuel Franceschini
    """Create a new file.
2278 6f695a2e Manuel Franceschini

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

2281 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2282 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2283 6f695a2e Manuel Franceschini

2284 6f695a2e Manuel Franceschini
    """
2285 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2286 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2287 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2288 6f695a2e Manuel Franceschini
    try:
2289 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
2290 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
2291 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2292 6f695a2e Manuel Franceschini
      f.close()
2293 cdeefd9b Guido Trotter
    except EnvironmentError, err:
2294 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
2295 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
2296 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
2297 6f695a2e Manuel Franceschini
2298 94dcbdb0 Andrea Spadaccini
    return FileStorage(unique_id, children, size, params)
2299 6f695a2e Manuel Franceschini
2300 6f695a2e Manuel Franceschini
2301 b6135bbc Apollon Oikonomopoulos
class PersistentBlockDevice(BlockDev):
2302 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
2303 b6135bbc Apollon Oikonomopoulos

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

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

2310 b6135bbc Apollon Oikonomopoulos
  """
2311 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2312 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
2313 b6135bbc Apollon Oikonomopoulos

2314 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2315 b6135bbc Apollon Oikonomopoulos

2316 b6135bbc Apollon Oikonomopoulos
    """
2317 94dcbdb0 Andrea Spadaccini
    super(PersistentBlockDevice, self).__init__(unique_id, children, size,
2318 94dcbdb0 Andrea Spadaccini
                                                params)
2319 b6135bbc Apollon Oikonomopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2320 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2321 b6135bbc Apollon Oikonomopoulos
    self.dev_path = unique_id[1]
2322 d0c8c01d Iustin Pop
    if not os.path.realpath(self.dev_path).startswith("/dev/"):
2323 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Full path '%s' lies outside /dev" %
2324 b6135bbc Apollon Oikonomopoulos
                              os.path.realpath(self.dev_path))
2325 b6135bbc Apollon Oikonomopoulos
    # TODO: this is just a safety guard checking that we only deal with devices
2326 b6135bbc Apollon Oikonomopoulos
    # we know how to handle. In the future this will be integrated with
2327 b6135bbc Apollon Oikonomopoulos
    # external storage backends and possible values will probably be collected
2328 b6135bbc Apollon Oikonomopoulos
    # from the cluster configuration.
2329 b6135bbc Apollon Oikonomopoulos
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
2330 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Got persistent block device of invalid type: %s" %
2331 b6135bbc Apollon Oikonomopoulos
                       unique_id[0])
2332 b6135bbc Apollon Oikonomopoulos
2333 b6135bbc Apollon Oikonomopoulos
    self.major = self.minor = None
2334 b6135bbc Apollon Oikonomopoulos
    self.Attach()
2335 b6135bbc Apollon Oikonomopoulos
2336 b6135bbc Apollon Oikonomopoulos
  @classmethod
2337 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2338 b6135bbc Apollon Oikonomopoulos
    """Create a new device
2339 b6135bbc Apollon Oikonomopoulos

2340 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2341 b6135bbc Apollon Oikonomopoulos

2342 b6135bbc Apollon Oikonomopoulos
    """
2343 94dcbdb0 Andrea Spadaccini
    return PersistentBlockDevice(unique_id, children, 0, params)
2344 b6135bbc Apollon Oikonomopoulos
2345 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
2346 b6135bbc Apollon Oikonomopoulos
    """Remove a device
2347 b6135bbc Apollon Oikonomopoulos

2348 b6135bbc Apollon Oikonomopoulos
    This is a noop
2349 b6135bbc Apollon Oikonomopoulos

2350 b6135bbc Apollon Oikonomopoulos
    """
2351 b6135bbc Apollon Oikonomopoulos
    pass
2352 b6135bbc Apollon Oikonomopoulos
2353 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
2354 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
2355 b6135bbc Apollon Oikonomopoulos

2356 b6135bbc Apollon Oikonomopoulos
    """
2357 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Rename is not supported for PersistentBlockDev storage")
2358 b6135bbc Apollon Oikonomopoulos
2359 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
2360 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
2361 b6135bbc Apollon Oikonomopoulos

2362 b6135bbc Apollon Oikonomopoulos

2363 b6135bbc Apollon Oikonomopoulos
    """
2364 b6135bbc Apollon Oikonomopoulos
    self.attached = False
2365 b6135bbc Apollon Oikonomopoulos
    try:
2366 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
2367 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
2368 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2369 b6135bbc Apollon Oikonomopoulos
      return False
2370 b6135bbc Apollon Oikonomopoulos
2371 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
2372 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
2373 b6135bbc Apollon Oikonomopoulos
      return False
2374 b6135bbc Apollon Oikonomopoulos
2375 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
2376 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
2377 b6135bbc Apollon Oikonomopoulos
    self.attached = True
2378 b6135bbc Apollon Oikonomopoulos
2379 b6135bbc Apollon Oikonomopoulos
    return True
2380 b6135bbc Apollon Oikonomopoulos
2381 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
2382 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
2383 b6135bbc Apollon Oikonomopoulos

2384 b6135bbc Apollon Oikonomopoulos
    """
2385 b6135bbc Apollon Oikonomopoulos
    pass
2386 b6135bbc Apollon Oikonomopoulos
2387 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
2388 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
2389 b6135bbc Apollon Oikonomopoulos

2390 b6135bbc Apollon Oikonomopoulos
    """
2391 b6135bbc Apollon Oikonomopoulos
    pass
2392 b6135bbc Apollon Oikonomopoulos
2393 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
2394 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
2395 b6135bbc Apollon Oikonomopoulos

2396 b6135bbc Apollon Oikonomopoulos
    """
2397 b6135bbc Apollon Oikonomopoulos
    pass
2398 b6135bbc Apollon Oikonomopoulos
2399 b6135bbc Apollon Oikonomopoulos
  def Close(self):
2400 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
2401 b6135bbc Apollon Oikonomopoulos

2402 b6135bbc Apollon Oikonomopoulos
    """
2403 b6135bbc Apollon Oikonomopoulos
    pass
2404 b6135bbc Apollon Oikonomopoulos
2405 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2406 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
2407 b6135bbc Apollon Oikonomopoulos

2408 b6135bbc Apollon Oikonomopoulos
    """
2409 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2410 b6135bbc Apollon Oikonomopoulos
2411 b6135bbc Apollon Oikonomopoulos
2412 7181fba0 Constantinos Venetsanopoulos
class RADOSBlockDevice(BlockDev):
2413 7181fba0 Constantinos Venetsanopoulos
  """A RADOS Block Device (rbd).
2414 7181fba0 Constantinos Venetsanopoulos

2415 7181fba0 Constantinos Venetsanopoulos
  This class implements the RADOS Block Device for the backend. You need
2416 7181fba0 Constantinos Venetsanopoulos
  the rbd kernel driver, the RADOS Tools and a working RADOS cluster for
2417 7181fba0 Constantinos Venetsanopoulos
  this to be functional.
2418 7181fba0 Constantinos Venetsanopoulos

2419 7181fba0 Constantinos Venetsanopoulos
  """
2420 7181fba0 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
2421 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
2422 7181fba0 Constantinos Venetsanopoulos

2423 7181fba0 Constantinos Venetsanopoulos
    """
2424 7181fba0 Constantinos Venetsanopoulos
    super(RADOSBlockDevice, self).__init__(unique_id, children, size, params)
2425 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2426 7181fba0 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2427 7181fba0 Constantinos Venetsanopoulos
2428 7181fba0 Constantinos Venetsanopoulos
    self.driver, self.rbd_name = unique_id
2429 7181fba0 Constantinos Venetsanopoulos
2430 7181fba0 Constantinos Venetsanopoulos
    self.major = self.minor = None
2431 7181fba0 Constantinos Venetsanopoulos
    self.Attach()
2432 7181fba0 Constantinos Venetsanopoulos
2433 7181fba0 Constantinos Venetsanopoulos
  @classmethod
2434 7181fba0 Constantinos Venetsanopoulos
  def Create(cls, unique_id, children, size, params):
2435 7181fba0 Constantinos Venetsanopoulos
    """Create a new rbd device.
2436 7181fba0 Constantinos Venetsanopoulos

2437 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
2438 7181fba0 Constantinos Venetsanopoulos

2439 7181fba0 Constantinos Venetsanopoulos
    """
2440 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2441 7181fba0 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
2442 7181fba0 Constantinos Venetsanopoulos
                                   str(unique_id))
2443 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = params[constants.LDP_POOL]
2444 7181fba0 Constantinos Venetsanopoulos
    rbd_name = unique_id[1]
2445 7181fba0 Constantinos Venetsanopoulos
2446 7181fba0 Constantinos Venetsanopoulos
    # Provision a new rbd volume (Image) inside the RADOS cluster.
2447 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "create", "-p", rbd_pool,
2448 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % size]
2449 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2450 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2451 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd creation failed (%s): %s",
2452 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2453 7181fba0 Constantinos Venetsanopoulos
2454 7181fba0 Constantinos Venetsanopoulos
    return RADOSBlockDevice(unique_id, children, size, params)
2455 7181fba0 Constantinos Venetsanopoulos
2456 7181fba0 Constantinos Venetsanopoulos
  def Remove(self):
2457 7181fba0 Constantinos Venetsanopoulos
    """Remove the rbd device.
2458 7181fba0 Constantinos Venetsanopoulos

2459 7181fba0 Constantinos Venetsanopoulos
    """
2460 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
2461 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
2462 7181fba0 Constantinos Venetsanopoulos
2463 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2464 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
2465 7181fba0 Constantinos Venetsanopoulos
      return
2466 7181fba0 Constantinos Venetsanopoulos
2467 7181fba0 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
2468 7181fba0 Constantinos Venetsanopoulos
    self.Shutdown()
2469 7181fba0 Constantinos Venetsanopoulos
2470 7181fba0 Constantinos Venetsanopoulos
    # Remove the actual Volume (Image) from the RADOS cluster.
2471 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "rm", "-p", rbd_pool, rbd_name]
2472 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2473 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2474 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Can't remove Volume from cluster with rbd rm: %s - %s",
2475 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2476 7181fba0 Constantinos Venetsanopoulos
2477 7181fba0 Constantinos Venetsanopoulos
  def Rename(self, new_id):
2478 7181fba0 Constantinos Venetsanopoulos
    """Rename this device.
2479 7181fba0 Constantinos Venetsanopoulos

2480 7181fba0 Constantinos Venetsanopoulos
    """
2481 7181fba0 Constantinos Venetsanopoulos
    pass
2482 7181fba0 Constantinos Venetsanopoulos
2483 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
2484 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
2485 7181fba0 Constantinos Venetsanopoulos

2486 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
2487 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
2488 7181fba0 Constantinos Venetsanopoulos

2489 7181fba0 Constantinos Venetsanopoulos
    """
2490 7181fba0 Constantinos Venetsanopoulos
    self.attached = False
2491 7181fba0 Constantinos Venetsanopoulos
2492 7181fba0 Constantinos Venetsanopoulos
    # Map the rbd volume to a block device under /dev
2493 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = self._MapVolumeToBlockdev(self.unique_id)
2494 7181fba0 Constantinos Venetsanopoulos
2495 7181fba0 Constantinos Venetsanopoulos
    try:
2496 7181fba0 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
2497 7181fba0 Constantinos Venetsanopoulos
    except OSError, err:
2498 7181fba0 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2499 7181fba0 Constantinos Venetsanopoulos
      return False
2500 7181fba0 Constantinos Venetsanopoulos
2501 7181fba0 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
2502 7181fba0 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
2503 7181fba0 Constantinos Venetsanopoulos
      return False
2504 7181fba0 Constantinos Venetsanopoulos
2505 7181fba0 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
2506 7181fba0 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
2507 7181fba0 Constantinos Venetsanopoulos
    self.attached = True
2508 7181fba0 Constantinos Venetsanopoulos
2509 7181fba0 Constantinos Venetsanopoulos
    return True
2510 7181fba0 Constantinos Venetsanopoulos
2511 7181fba0 Constantinos Venetsanopoulos
  def _MapVolumeToBlockdev(self, unique_id):
2512 7181fba0 Constantinos Venetsanopoulos
    """Maps existing rbd volumes to block devices.
2513 7181fba0 Constantinos Venetsanopoulos

2514 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
2515 7181fba0 Constantinos Venetsanopoulos

2516 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
2517 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
2518 7181fba0 Constantinos Venetsanopoulos

2519 7181fba0 Constantinos Venetsanopoulos
    """
2520 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2521 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2522 7181fba0 Constantinos Venetsanopoulos
2523 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2524 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2525 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2526 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2527 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped failed (%s): %s",
2528 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2529 7181fba0 Constantinos Venetsanopoulos
2530 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2531 7181fba0 Constantinos Venetsanopoulos
2532 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2533 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Return it.
2534 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2535 7181fba0 Constantinos Venetsanopoulos
2536 7181fba0 Constantinos Venetsanopoulos
    # The mapping doesn't exist. Create it.
2537 7181fba0 Constantinos Venetsanopoulos
    map_cmd = [constants.RBD_CMD, "map", "-p", pool, name]
2538 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(map_cmd)
2539 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2540 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map failed (%s): %s",
2541 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2542 7181fba0 Constantinos Venetsanopoulos
2543 7181fba0 Constantinos Venetsanopoulos
    # Find the corresponding rbd device.
2544 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2545 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2546 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2547 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map succeeded, but showmapped failed (%s): %s",
2548 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2549 7181fba0 Constantinos Venetsanopoulos
2550 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2551 7181fba0 Constantinos Venetsanopoulos
2552 7181fba0 Constantinos Venetsanopoulos
    if not rbd_dev:
2553 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map succeeded, but could not find the rbd block"
2554 7181fba0 Constantinos Venetsanopoulos
                  " device in output of showmapped, for volume: %s", name)
2555 7181fba0 Constantinos Venetsanopoulos
2556 7181fba0 Constantinos Venetsanopoulos
    # The device was successfully mapped. Return it.
2557 7181fba0 Constantinos Venetsanopoulos
    return rbd_dev
2558 7181fba0 Constantinos Venetsanopoulos
2559 7181fba0 Constantinos Venetsanopoulos
  @staticmethod
2560 7181fba0 Constantinos Venetsanopoulos
  def _ParseRbdShowmappedOutput(output, volume_name):
2561 7181fba0 Constantinos Venetsanopoulos
    """Parse the output of `rbd showmapped'.
2562 7181fba0 Constantinos Venetsanopoulos

2563 7181fba0 Constantinos Venetsanopoulos
    This method parses the output of `rbd showmapped' and returns
2564 7181fba0 Constantinos Venetsanopoulos
    the rbd block device path (e.g. /dev/rbd0) that matches the
2565 7181fba0 Constantinos Venetsanopoulos
    given rbd volume.
2566 7181fba0 Constantinos Venetsanopoulos

2567 7181fba0 Constantinos Venetsanopoulos
    @type output: string
2568 7181fba0 Constantinos Venetsanopoulos
    @param output: the whole output of `rbd showmapped'
2569 7181fba0 Constantinos Venetsanopoulos
    @type volume_name: string
2570 7181fba0 Constantinos Venetsanopoulos
    @param volume_name: the name of the volume whose device we search for
2571 7181fba0 Constantinos Venetsanopoulos
    @rtype: string or None
2572 7181fba0 Constantinos Venetsanopoulos
    @return: block device path if the volume is mapped, else None
2573 7181fba0 Constantinos Venetsanopoulos

2574 7181fba0 Constantinos Venetsanopoulos
    """
2575 7181fba0 Constantinos Venetsanopoulos
    allfields = 5
2576 7181fba0 Constantinos Venetsanopoulos
    volumefield = 2
2577 7181fba0 Constantinos Venetsanopoulos
    devicefield = 4
2578 7181fba0 Constantinos Venetsanopoulos
2579 7181fba0 Constantinos Venetsanopoulos
    field_sep = "\t"
2580 7181fba0 Constantinos Venetsanopoulos
2581 7181fba0 Constantinos Venetsanopoulos
    lines = output.splitlines()
2582 7181fba0 Constantinos Venetsanopoulos
    splitted_lines = map(lambda l: l.split(field_sep), lines)
2583 7181fba0 Constantinos Venetsanopoulos
2584 7181fba0 Constantinos Venetsanopoulos
    # Check empty output.
2585 7181fba0 Constantinos Venetsanopoulos
    if not splitted_lines:
2586 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped returned empty output")
2587 7181fba0 Constantinos Venetsanopoulos
2588 7181fba0 Constantinos Venetsanopoulos
    # Check showmapped header line, to determine number of fields.
2589 7181fba0 Constantinos Venetsanopoulos
    field_cnt = len(splitted_lines[0])
2590 7181fba0 Constantinos Venetsanopoulos
    if field_cnt != allfields:
2591 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Cannot parse rbd showmapped output because its format"
2592 7181fba0 Constantinos Venetsanopoulos
                  " seems to have changed; expected %s fields, found %s",
2593 7181fba0 Constantinos Venetsanopoulos
                  allfields, field_cnt)
2594 7181fba0 Constantinos Venetsanopoulos
2595 7181fba0 Constantinos Venetsanopoulos
    matched_lines = \
2596 7181fba0 Constantinos Venetsanopoulos
      filter(lambda l: len(l) == allfields and l[volumefield] == volume_name,
2597 7181fba0 Constantinos Venetsanopoulos
             splitted_lines)
2598 7181fba0 Constantinos Venetsanopoulos
2599 7181fba0 Constantinos Venetsanopoulos
    if len(matched_lines) > 1:
2600 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("The rbd volume %s is mapped more than once."
2601 7181fba0 Constantinos Venetsanopoulos
                  " This shouldn't happen, try to unmap the extra"
2602 7181fba0 Constantinos Venetsanopoulos
                  " devices manually.", volume_name)
2603 7181fba0 Constantinos Venetsanopoulos
2604 7181fba0 Constantinos Venetsanopoulos
    if matched_lines:
2605 7181fba0 Constantinos Venetsanopoulos
      # rbd block device found. Return it.
2606 7181fba0 Constantinos Venetsanopoulos
      rbd_dev = matched_lines[0][devicefield]
2607 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2608 7181fba0 Constantinos Venetsanopoulos
2609 7181fba0 Constantinos Venetsanopoulos
    # The given volume is not mapped.
2610 7181fba0 Constantinos Venetsanopoulos
    return None
2611 7181fba0 Constantinos Venetsanopoulos
2612 7181fba0 Constantinos Venetsanopoulos
  def Assemble(self):
2613 7181fba0 Constantinos Venetsanopoulos
    """Assemble the device.
2614 7181fba0 Constantinos Venetsanopoulos

2615 7181fba0 Constantinos Venetsanopoulos
    """
2616 7181fba0 Constantinos Venetsanopoulos
    pass
2617 7181fba0 Constantinos Venetsanopoulos
2618 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
2619 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
2620 7181fba0 Constantinos Venetsanopoulos

2621 7181fba0 Constantinos Venetsanopoulos
    """
2622 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2623 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
2624 7181fba0 Constantinos Venetsanopoulos
      return
2625 7181fba0 Constantinos Venetsanopoulos
2626 7181fba0 Constantinos Venetsanopoulos
    # Unmap the block device from the Volume.
2627 7181fba0 Constantinos Venetsanopoulos
    self._UnmapVolumeFromBlockdev(self.unique_id)
2628 7181fba0 Constantinos Venetsanopoulos
2629 7181fba0 Constantinos Venetsanopoulos
    self.minor = None
2630 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = None
2631 7181fba0 Constantinos Venetsanopoulos
2632 7181fba0 Constantinos Venetsanopoulos
  def _UnmapVolumeFromBlockdev(self, unique_id):
2633 7181fba0 Constantinos Venetsanopoulos
    """Unmaps the rbd device from the Volume it is mapped.
2634 7181fba0 Constantinos Venetsanopoulos

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

2638 7181fba0 Constantinos Venetsanopoulos
    """
2639 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2640 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2641 7181fba0 Constantinos Venetsanopoulos
2642 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2643 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2644 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2645 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2646 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped failed [during unmap](%s): %s",
2647 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2648 7181fba0 Constantinos Venetsanopoulos
2649 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2650 7181fba0 Constantinos Venetsanopoulos
2651 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2652 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Unmap the rbd device.
2653 7181fba0 Constantinos Venetsanopoulos
      unmap_cmd = [constants.RBD_CMD, "unmap", "%s" % rbd_dev]
2654 7181fba0 Constantinos Venetsanopoulos
      result = utils.RunCmd(unmap_cmd)
2655 7181fba0 Constantinos Venetsanopoulos
      if result.failed:
2656 7181fba0 Constantinos Venetsanopoulos
        _ThrowError("rbd unmap failed (%s): %s",
2657 7181fba0 Constantinos Venetsanopoulos
                    result.fail_reason, result.output)
2658 7181fba0 Constantinos Venetsanopoulos
2659 7181fba0 Constantinos Venetsanopoulos
  def Open(self, force=False):
2660 7181fba0 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
2661 7181fba0 Constantinos Venetsanopoulos

2662 7181fba0 Constantinos Venetsanopoulos
    """
2663 7181fba0 Constantinos Venetsanopoulos
    pass
2664 7181fba0 Constantinos Venetsanopoulos
2665 7181fba0 Constantinos Venetsanopoulos
  def Close(self):
2666 7181fba0 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
2667 7181fba0 Constantinos Venetsanopoulos

2668 7181fba0 Constantinos Venetsanopoulos
    """
2669 7181fba0 Constantinos Venetsanopoulos
    pass
2670 7181fba0 Constantinos Venetsanopoulos
2671 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2672 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
2673 7181fba0 Constantinos Venetsanopoulos

2674 7181fba0 Constantinos Venetsanopoulos
    @type amount: integer
2675 7181fba0 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
2676 7181fba0 Constantinos Venetsanopoulos
    @type dryrun: boolean
2677 7181fba0 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
2678 7181fba0 Constantinos Venetsanopoulos
        only, without actually increasing the size
2679 7181fba0 Constantinos Venetsanopoulos

2680 7181fba0 Constantinos Venetsanopoulos
    """
2681 cad0723b Iustin Pop
    if not backingstore:
2682 cad0723b Iustin Pop
      return
2683 7181fba0 Constantinos Venetsanopoulos
    if not self.Attach():
2684 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Can't attach to rbd device during Grow()")
2685 7181fba0 Constantinos Venetsanopoulos
2686 7181fba0 Constantinos Venetsanopoulos
    if dryrun:
2687 7181fba0 Constantinos Venetsanopoulos
      # the rbd tool does not support dry runs of resize operations.
2688 7181fba0 Constantinos Venetsanopoulos
      # Since rbd volumes are thinly provisioned, we assume
2689 7181fba0 Constantinos Venetsanopoulos
      # there is always enough free space for the operation.
2690 7181fba0 Constantinos Venetsanopoulos
      return
2691 7181fba0 Constantinos Venetsanopoulos
2692 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
2693 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
2694 7181fba0 Constantinos Venetsanopoulos
    new_size = self.size + amount
2695 7181fba0 Constantinos Venetsanopoulos
2696 7181fba0 Constantinos Venetsanopoulos
    # Resize the rbd volume (Image) inside the RADOS cluster.
2697 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "resize", "-p", rbd_pool,
2698 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % new_size]
2699 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2700 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2701 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd resize failed (%s): %s",
2702 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2703 7181fba0 Constantinos Venetsanopoulos
2704 7181fba0 Constantinos Venetsanopoulos
2705 a8083063 Iustin Pop
DEV_MAP = {
2706 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2707 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2708 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
2709 7181fba0 Constantinos Venetsanopoulos
  constants.LD_RBD: RADOSBlockDevice,
2710 a8083063 Iustin Pop
  }
2711 a8083063 Iustin Pop
2712 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
2713 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
2714 cb7c0198 Iustin Pop
2715 a8083063 Iustin Pop
2716 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
2717 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
2718 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2719 94dcbdb0 Andrea Spadaccini
2720 94dcbdb0 Andrea Spadaccini
2721 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
2722 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
2723 5ff82cc9 René Nussbaumer

2724 5ff82cc9 René Nussbaumer
  """
2725 5ff82cc9 René Nussbaumer
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
2726 5ff82cc9 René Nussbaumer
  if missing:
2727 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
2728 5ff82cc9 René Nussbaumer
                                 missing)
2729 5ff82cc9 René Nussbaumer
2730 5ff82cc9 René Nussbaumer
2731 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
2732 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2733 a8083063 Iustin Pop

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

2737 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2738 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
2739 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2740 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2741 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2742 94dcbdb0 Andrea Spadaccini

2743 a8083063 Iustin Pop
  """
2744 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2745 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
2746 c7c6606d René Nussbaumer
                                  disk.params)
2747 cb999543 Iustin Pop
  if not device.attached:
2748 a8083063 Iustin Pop
    return None
2749 ecb091e3 Iustin Pop
  return device
2750 a8083063 Iustin Pop
2751 a8083063 Iustin Pop
2752 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
2753 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2754 a8083063 Iustin Pop

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

2758 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2759 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
2760 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2761 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2762 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2763 94dcbdb0 Andrea Spadaccini

2764 a8083063 Iustin Pop
  """
2765 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2766 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
2767 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
2768 c7c6606d René Nussbaumer
                                  disk.params)
2769 1063abd1 Iustin Pop
  device.Assemble()
2770 a8083063 Iustin Pop
  return device
2771 a8083063 Iustin Pop
2772 a8083063 Iustin Pop
2773 94dcbdb0 Andrea Spadaccini
def Create(disk, children):
2774 a8083063 Iustin Pop
  """Create a device.
2775 a8083063 Iustin Pop

2776 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2777 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
2778 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2779 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2780 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2781 94dcbdb0 Andrea Spadaccini

2782 a8083063 Iustin Pop
  """
2783 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2784 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
2785 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
2786 c7c6606d René Nussbaumer
                                         disk.params)
2787 a8083063 Iustin Pop
  return device