Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 5e09a309

History | View | Annotate | Download (90.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 e398546b Iustin Pop
def _CheckResult(result):
79 e398546b Iustin Pop
  """Throws an error if the given result is a failed one.
80 e398546b Iustin Pop

81 e398546b Iustin Pop
  @param result: result from RunCmd
82 e398546b Iustin Pop

83 e398546b Iustin Pop
  """
84 e398546b Iustin Pop
  if result.failed:
85 e398546b Iustin Pop
    _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
86 e398546b Iustin Pop
                result.output)
87 e398546b Iustin Pop
88 e398546b Iustin Pop
89 310fbb64 Iustin Pop
def _CanReadDevice(path):
90 310fbb64 Iustin Pop
  """Check if we can read from the given device.
91 310fbb64 Iustin Pop

92 310fbb64 Iustin Pop
  This tries to read the first 128k of the device.
93 310fbb64 Iustin Pop

94 310fbb64 Iustin Pop
  """
95 310fbb64 Iustin Pop
  try:
96 310fbb64 Iustin Pop
    utils.ReadFile(path, size=_DEVICE_READ_SIZE)
97 310fbb64 Iustin Pop
    return True
98 1122eb25 Iustin Pop
  except EnvironmentError:
99 310fbb64 Iustin Pop
    logging.warning("Can't read from device %s", path, exc_info=True)
100 310fbb64 Iustin Pop
    return False
101 310fbb64 Iustin Pop
102 310fbb64 Iustin Pop
103 23e3c9b7 Michael Hanselmann
def _GetForbiddenFileStoragePaths():
104 23e3c9b7 Michael Hanselmann
  """Builds a list of path prefixes which shouldn't be used for file storage.
105 23e3c9b7 Michael Hanselmann

106 23e3c9b7 Michael Hanselmann
  @rtype: frozenset
107 23e3c9b7 Michael Hanselmann

108 23e3c9b7 Michael Hanselmann
  """
109 23e3c9b7 Michael Hanselmann
  paths = set([
110 23e3c9b7 Michael Hanselmann
    "/boot",
111 23e3c9b7 Michael Hanselmann
    "/dev",
112 23e3c9b7 Michael Hanselmann
    "/etc",
113 23e3c9b7 Michael Hanselmann
    "/home",
114 23e3c9b7 Michael Hanselmann
    "/proc",
115 23e3c9b7 Michael Hanselmann
    "/root",
116 23e3c9b7 Michael Hanselmann
    "/sys",
117 23e3c9b7 Michael Hanselmann
    ])
118 23e3c9b7 Michael Hanselmann
119 23e3c9b7 Michael Hanselmann
  for prefix in ["", "/usr", "/usr/local"]:
120 23e3c9b7 Michael Hanselmann
    paths.update(map(lambda s: "%s/%s" % (prefix, s),
121 23e3c9b7 Michael Hanselmann
                     ["bin", "lib", "lib32", "lib64", "sbin"]))
122 23e3c9b7 Michael Hanselmann
123 23e3c9b7 Michael Hanselmann
  return frozenset(map(os.path.normpath, paths))
124 23e3c9b7 Michael Hanselmann
125 23e3c9b7 Michael Hanselmann
126 23e3c9b7 Michael Hanselmann
def _ComputeWrongFileStoragePaths(paths,
127 23e3c9b7 Michael Hanselmann
                                  _forbidden=_GetForbiddenFileStoragePaths()):
128 23e3c9b7 Michael Hanselmann
  """Cross-checks a list of paths for prefixes considered bad.
129 23e3c9b7 Michael Hanselmann

130 23e3c9b7 Michael Hanselmann
  Some paths, e.g. "/bin", should not be used for file storage.
131 23e3c9b7 Michael Hanselmann

132 23e3c9b7 Michael Hanselmann
  @type paths: list
133 23e3c9b7 Michael Hanselmann
  @param paths: List of paths to be checked
134 23e3c9b7 Michael Hanselmann
  @rtype: list
135 23e3c9b7 Michael Hanselmann
  @return: Sorted list of paths for which the user should be warned
136 23e3c9b7 Michael Hanselmann

137 23e3c9b7 Michael Hanselmann
  """
138 23e3c9b7 Michael Hanselmann
  def _Check(path):
139 23e3c9b7 Michael Hanselmann
    return (not os.path.isabs(path) or
140 23e3c9b7 Michael Hanselmann
            path in _forbidden or
141 23e3c9b7 Michael Hanselmann
            filter(lambda p: utils.IsBelowDir(p, path), _forbidden))
142 23e3c9b7 Michael Hanselmann
143 23e3c9b7 Michael Hanselmann
  return utils.NiceSort(filter(_Check, map(os.path.normpath, paths)))
144 23e3c9b7 Michael Hanselmann
145 23e3c9b7 Michael Hanselmann
146 23e3c9b7 Michael Hanselmann
def ComputeWrongFileStoragePaths(_filename=pathutils.FILE_STORAGE_PATHS_FILE):
147 23e3c9b7 Michael Hanselmann
  """Returns a list of file storage paths whose prefix is considered bad.
148 23e3c9b7 Michael Hanselmann

149 23e3c9b7 Michael Hanselmann
  See L{_ComputeWrongFileStoragePaths}.
150 23e3c9b7 Michael Hanselmann

151 23e3c9b7 Michael Hanselmann
  """
152 23e3c9b7 Michael Hanselmann
  return _ComputeWrongFileStoragePaths(_LoadAllowedFileStoragePaths(_filename))
153 23e3c9b7 Michael Hanselmann
154 23e3c9b7 Michael Hanselmann
155 fbdac0d9 Michael Hanselmann
def _CheckFileStoragePath(path, allowed):
156 fbdac0d9 Michael Hanselmann
  """Checks if a path is in a list of allowed paths for file storage.
157 fbdac0d9 Michael Hanselmann

158 fbdac0d9 Michael Hanselmann
  @type path: string
159 fbdac0d9 Michael Hanselmann
  @param path: Path to check
160 fbdac0d9 Michael Hanselmann
  @type allowed: list
161 fbdac0d9 Michael Hanselmann
  @param allowed: List of allowed paths
162 fbdac0d9 Michael Hanselmann
  @raise errors.FileStoragePathError: If the path is not allowed
163 fbdac0d9 Michael Hanselmann

164 fbdac0d9 Michael Hanselmann
  """
165 fbdac0d9 Michael Hanselmann
  if not os.path.isabs(path):
166 fbdac0d9 Michael Hanselmann
    raise errors.FileStoragePathError("File storage path must be absolute,"
167 fbdac0d9 Michael Hanselmann
                                      " got '%s'" % path)
168 fbdac0d9 Michael Hanselmann
169 fbdac0d9 Michael Hanselmann
  for i in allowed:
170 fbdac0d9 Michael Hanselmann
    if not os.path.isabs(i):
171 fbdac0d9 Michael Hanselmann
      logging.info("Ignoring relative path '%s' for file storage", i)
172 fbdac0d9 Michael Hanselmann
      continue
173 fbdac0d9 Michael Hanselmann
174 fbdac0d9 Michael Hanselmann
    if utils.IsBelowDir(i, path):
175 fbdac0d9 Michael Hanselmann
      break
176 fbdac0d9 Michael Hanselmann
  else:
177 fbdac0d9 Michael Hanselmann
    raise errors.FileStoragePathError("Path '%s' is not acceptable for file"
178 fbdac0d9 Michael Hanselmann
                                      " storage" % path)
179 fbdac0d9 Michael Hanselmann
180 fbdac0d9 Michael Hanselmann
181 23e3c9b7 Michael Hanselmann
def _LoadAllowedFileStoragePaths(filename):
182 fbdac0d9 Michael Hanselmann
  """Loads file containing allowed file storage paths.
183 fbdac0d9 Michael Hanselmann

184 fbdac0d9 Michael Hanselmann
  @rtype: list
185 fbdac0d9 Michael Hanselmann
  @return: List of allowed paths (can be an empty list)
186 fbdac0d9 Michael Hanselmann

187 fbdac0d9 Michael Hanselmann
  """
188 fbdac0d9 Michael Hanselmann
  try:
189 fbdac0d9 Michael Hanselmann
    contents = utils.ReadFile(filename)
190 fbdac0d9 Michael Hanselmann
  except EnvironmentError:
191 fbdac0d9 Michael Hanselmann
    return []
192 fbdac0d9 Michael Hanselmann
  else:
193 fbdac0d9 Michael Hanselmann
    return utils.FilterEmptyLinesAndComments(contents)
194 fbdac0d9 Michael Hanselmann
195 fbdac0d9 Michael Hanselmann
196 fbdac0d9 Michael Hanselmann
def CheckFileStoragePath(path, _filename=pathutils.FILE_STORAGE_PATHS_FILE):
197 fbdac0d9 Michael Hanselmann
  """Checks if a path is allowed for file storage.
198 fbdac0d9 Michael Hanselmann

199 fbdac0d9 Michael Hanselmann
  @type path: string
200 fbdac0d9 Michael Hanselmann
  @param path: Path to check
201 fbdac0d9 Michael Hanselmann
  @raise errors.FileStoragePathError: If the path is not allowed
202 fbdac0d9 Michael Hanselmann

203 fbdac0d9 Michael Hanselmann
  """
204 23e3c9b7 Michael Hanselmann
  allowed = _LoadAllowedFileStoragePaths(_filename)
205 23e3c9b7 Michael Hanselmann
206 23e3c9b7 Michael Hanselmann
  if _ComputeWrongFileStoragePaths([path]):
207 23e3c9b7 Michael Hanselmann
    raise errors.FileStoragePathError("Path '%s' uses a forbidden prefix" %
208 23e3c9b7 Michael Hanselmann
                                      path)
209 23e3c9b7 Michael Hanselmann
210 23e3c9b7 Michael Hanselmann
  _CheckFileStoragePath(path, allowed)
211 fbdac0d9 Michael Hanselmann
212 fbdac0d9 Michael Hanselmann
213 a8083063 Iustin Pop
class BlockDev(object):
214 a8083063 Iustin Pop
  """Block device abstract class.
215 a8083063 Iustin Pop

216 a8083063 Iustin Pop
  A block device can be in the following states:
217 a8083063 Iustin Pop
    - not existing on the system, and by `Create()` it goes into:
218 a8083063 Iustin Pop
    - existing but not setup/not active, and by `Assemble()` goes into:
219 a8083063 Iustin Pop
    - active read-write and by `Open()` it goes into
220 a8083063 Iustin Pop
    - online (=used, or ready for use)
221 a8083063 Iustin Pop

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

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

233 a8083063 Iustin Pop
  A block device is identified by three items:
234 a8083063 Iustin Pop
    - the /dev path of the device (dynamic)
235 a8083063 Iustin Pop
    - a unique ID of the device (static)
236 a8083063 Iustin Pop
    - it's major/minor pair (dynamic)
237 a8083063 Iustin Pop

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

244 a8083063 Iustin Pop
  You can get to a device in two ways:
245 a8083063 Iustin Pop
    - creating the (real) device, which returns you
246 abdf0113 Iustin Pop
      an attached instance (lvcreate)
247 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
248 a8083063 Iustin Pop

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

255 a8083063 Iustin Pop
  """
256 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
257 a8083063 Iustin Pop
    self._children = children
258 a8083063 Iustin Pop
    self.dev_path = None
259 a8083063 Iustin Pop
    self.unique_id = unique_id
260 a8083063 Iustin Pop
    self.major = None
261 a8083063 Iustin Pop
    self.minor = None
262 cb999543 Iustin Pop
    self.attached = False
263 464f8daf Iustin Pop
    self.size = size
264 94dcbdb0 Andrea Spadaccini
    self.params = params
265 a8083063 Iustin Pop
266 a8083063 Iustin Pop
  def Assemble(self):
267 a8083063 Iustin Pop
    """Assemble the device from its components.
268 a8083063 Iustin Pop

269 f87548b5 Iustin Pop
    Implementations of this method by child classes must ensure that:
270 f87548b5 Iustin Pop
      - after the device has been assembled, it knows its major/minor
271 f87548b5 Iustin Pop
        numbers; this allows other devices (usually parents) to probe
272 f87548b5 Iustin Pop
        correctly for their children
273 f87548b5 Iustin Pop
      - calling this method on an existing, in-use device is safe
274 f87548b5 Iustin Pop
      - if the device is already configured (and in an OK state),
275 f87548b5 Iustin Pop
        this method is idempotent
276 a8083063 Iustin Pop

277 a8083063 Iustin Pop
    """
278 1063abd1 Iustin Pop
    pass
279 a8083063 Iustin Pop
280 a8083063 Iustin Pop
  def Attach(self):
281 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
282 a8083063 Iustin Pop

283 a8083063 Iustin Pop
    """
284 a8083063 Iustin Pop
    raise NotImplementedError
285 a8083063 Iustin Pop
286 a8083063 Iustin Pop
  def Close(self):
287 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
288 a8083063 Iustin Pop

289 a8083063 Iustin Pop
    """
290 a8083063 Iustin Pop
    raise NotImplementedError
291 a8083063 Iustin Pop
292 a8083063 Iustin Pop
  @classmethod
293 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
294 a8083063 Iustin Pop
    """Create the device.
295 a8083063 Iustin Pop

296 a8083063 Iustin Pop
    If the device cannot be created, it will return None
297 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
298 a8083063 Iustin Pop

299 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
300 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
301 a8083063 Iustin Pop
    enough for both creation and assembly (later).
302 a8083063 Iustin Pop

303 a8083063 Iustin Pop
    """
304 a8083063 Iustin Pop
    raise NotImplementedError
305 a8083063 Iustin Pop
306 a8083063 Iustin Pop
  def Remove(self):
307 a8083063 Iustin Pop
    """Remove this device.
308 a8083063 Iustin Pop

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

313 a8083063 Iustin Pop
    """
314 a8083063 Iustin Pop
    raise NotImplementedError
315 a8083063 Iustin Pop
316 f3e513ad Iustin Pop
  def Rename(self, new_id):
317 f3e513ad Iustin Pop
    """Rename this device.
318 f3e513ad Iustin Pop

319 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
320 f3e513ad Iustin Pop

321 f3e513ad Iustin Pop
    """
322 f3e513ad Iustin Pop
    raise NotImplementedError
323 f3e513ad Iustin Pop
324 a8083063 Iustin Pop
  def Open(self, force=False):
325 a8083063 Iustin Pop
    """Make the device ready for use.
326 a8083063 Iustin Pop

327 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
328 a8083063 Iustin Pop
    devices need this.
329 a8083063 Iustin Pop

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

333 a8083063 Iustin Pop
    """
334 a8083063 Iustin Pop
    raise NotImplementedError
335 a8083063 Iustin Pop
336 a8083063 Iustin Pop
  def Shutdown(self):
337 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
338 a8083063 Iustin Pop

339 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
340 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
341 a8083063 Iustin Pop
    assembled after this call.
342 a8083063 Iustin Pop

343 a8083063 Iustin Pop
    """
344 a8083063 Iustin Pop
    raise NotImplementedError
345 a8083063 Iustin Pop
346 f2f57b6e Andrea Spadaccini
  def SetSyncParams(self, params):
347 f2f57b6e Andrea Spadaccini
    """Adjust the synchronization parameters of the mirror.
348 a8083063 Iustin Pop

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

351 f2f57b6e Andrea Spadaccini
    @param params: dictionary of LD level disk parameters related to the
352 f2f57b6e Andrea Spadaccini
    synchronization.
353 8584e922 Andrea Spadaccini
    @rtype: list
354 8584e922 Andrea Spadaccini
    @return: a list of error messages, emitted both by the current node and by
355 8584e922 Andrea Spadaccini
    children. An empty list means no errors.
356 f2f57b6e Andrea Spadaccini

357 a8083063 Iustin Pop
    """
358 8584e922 Andrea Spadaccini
    result = []
359 a8083063 Iustin Pop
    if self._children:
360 a8083063 Iustin Pop
      for child in self._children:
361 8584e922 Andrea Spadaccini
        result.extend(child.SetSyncParams(params))
362 a8083063 Iustin Pop
    return result
363 a8083063 Iustin Pop
364 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
365 a3fffcc6 René Nussbaumer
    """Pause/Resume the sync of the mirror.
366 a3fffcc6 René Nussbaumer

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

369 f2f57b6e Andrea Spadaccini
    @param pause: Whether to pause or resume
370 a3fffcc6 René Nussbaumer

371 a3fffcc6 René Nussbaumer
    """
372 a3fffcc6 René Nussbaumer
    result = True
373 a3fffcc6 René Nussbaumer
    if self._children:
374 a3fffcc6 René Nussbaumer
      for child in self._children:
375 a3fffcc6 René Nussbaumer
        result = result and child.PauseResumeSync(pause)
376 a3fffcc6 René Nussbaumer
    return result
377 a3fffcc6 René Nussbaumer
378 a8083063 Iustin Pop
  def GetSyncStatus(self):
379 a8083063 Iustin Pop
    """Returns the sync status of the device.
380 a8083063 Iustin Pop

381 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
382 a8083063 Iustin Pop
    status of the mirror.
383 a8083063 Iustin Pop

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

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

389 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
390 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
391 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
392 a8083063 Iustin Pop

393 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
394 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
395 0834c866 Iustin Pop
    return False (not degraded).
396 0834c866 Iustin Pop

397 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
398 c41eea6e Iustin Pop

399 a8083063 Iustin Pop
    """
400 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
401 96acbc09 Michael Hanselmann
                                  major=self.major,
402 96acbc09 Michael Hanselmann
                                  minor=self.minor,
403 96acbc09 Michael Hanselmann
                                  sync_percent=None,
404 96acbc09 Michael Hanselmann
                                  estimated_time=None,
405 96acbc09 Michael Hanselmann
                                  is_degraded=False,
406 f208978a Michael Hanselmann
                                  ldisk_status=constants.LDS_OKAY)
407 a8083063 Iustin Pop
408 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
409 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
410 a8083063 Iustin Pop

411 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
412 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
413 a8083063 Iustin Pop
    children.
414 a8083063 Iustin Pop

415 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
416 96acbc09 Michael Hanselmann

417 a8083063 Iustin Pop
    """
418 96acbc09 Michael Hanselmann
    status = self.GetSyncStatus()
419 96acbc09 Michael Hanselmann
420 96acbc09 Michael Hanselmann
    min_percent = status.sync_percent
421 96acbc09 Michael Hanselmann
    max_time = status.estimated_time
422 96acbc09 Michael Hanselmann
    is_degraded = status.is_degraded
423 f208978a Michael Hanselmann
    ldisk_status = status.ldisk_status
424 96acbc09 Michael Hanselmann
425 a8083063 Iustin Pop
    if self._children:
426 a8083063 Iustin Pop
      for child in self._children:
427 96acbc09 Michael Hanselmann
        child_status = child.GetSyncStatus()
428 96acbc09 Michael Hanselmann
429 a8083063 Iustin Pop
        if min_percent is None:
430 96acbc09 Michael Hanselmann
          min_percent = child_status.sync_percent
431 96acbc09 Michael Hanselmann
        elif child_status.sync_percent is not None:
432 96acbc09 Michael Hanselmann
          min_percent = min(min_percent, child_status.sync_percent)
433 96acbc09 Michael Hanselmann
434 a8083063 Iustin Pop
        if max_time is None:
435 96acbc09 Michael Hanselmann
          max_time = child_status.estimated_time
436 96acbc09 Michael Hanselmann
        elif child_status.estimated_time is not None:
437 96acbc09 Michael Hanselmann
          max_time = max(max_time, child_status.estimated_time)
438 96acbc09 Michael Hanselmann
439 96acbc09 Michael Hanselmann
        is_degraded = is_degraded or child_status.is_degraded
440 f208978a Michael Hanselmann
441 f208978a Michael Hanselmann
        if ldisk_status is None:
442 f208978a Michael Hanselmann
          ldisk_status = child_status.ldisk_status
443 f208978a Michael Hanselmann
        elif child_status.ldisk_status is not None:
444 f208978a Michael Hanselmann
          ldisk_status = max(ldisk_status, child_status.ldisk_status)
445 96acbc09 Michael Hanselmann
446 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
447 96acbc09 Michael Hanselmann
                                  major=self.major,
448 96acbc09 Michael Hanselmann
                                  minor=self.minor,
449 96acbc09 Michael Hanselmann
                                  sync_percent=min_percent,
450 96acbc09 Michael Hanselmann
                                  estimated_time=max_time,
451 96acbc09 Michael Hanselmann
                                  is_degraded=is_degraded,
452 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
453 a8083063 Iustin Pop
454 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
455 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
456 a0c3fea1 Michael Hanselmann

457 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
458 a0c3fea1 Michael Hanselmann

459 a0c3fea1 Michael Hanselmann
    """
460 a0c3fea1 Michael Hanselmann
    for child in self._children:
461 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
462 a0c3fea1 Michael Hanselmann
463 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
464 1005d816 Iustin Pop
    """Grow the block device.
465 1005d816 Iustin Pop

466 7fe23d47 Iustin Pop
    @type amount: integer
467 c41eea6e Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
468 7fe23d47 Iustin Pop
    @type dryrun: boolean
469 7fe23d47 Iustin Pop
    @param dryrun: whether to execute the operation in simulation mode
470 7fe23d47 Iustin Pop
        only, without actually increasing the size
471 cad0723b Iustin Pop
    @param backingstore: whether to execute the operation on backing storage
472 cad0723b Iustin Pop
        only, or on "logical" storage only; e.g. DRBD is logical storage,
473 cad0723b Iustin Pop
        whereas LVM, file, RBD are backing storage
474 1005d816 Iustin Pop

475 1005d816 Iustin Pop
    """
476 1005d816 Iustin Pop
    raise NotImplementedError
477 a0c3fea1 Michael Hanselmann
478 fcff3897 Iustin Pop
  def GetActualSize(self):
479 fcff3897 Iustin Pop
    """Return the actual disk size.
480 fcff3897 Iustin Pop

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

483 fcff3897 Iustin Pop
    """
484 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
485 fcff3897 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize64", self.dev_path])
486 fcff3897 Iustin Pop
    if result.failed:
487 fcff3897 Iustin Pop
      _ThrowError("blockdev failed (%s): %s",
488 fcff3897 Iustin Pop
                  result.fail_reason, result.output)
489 fcff3897 Iustin Pop
    try:
490 fcff3897 Iustin Pop
      sz = int(result.output.strip())
491 fcff3897 Iustin Pop
    except (ValueError, TypeError), err:
492 fcff3897 Iustin Pop
      _ThrowError("Failed to parse blockdev output: %s", str(err))
493 fcff3897 Iustin Pop
    return sz
494 fcff3897 Iustin Pop
495 a8083063 Iustin Pop
  def __repr__(self):
496 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
497 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
498 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
499 a8083063 Iustin Pop
500 a8083063 Iustin Pop
501 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
502 a8083063 Iustin Pop
  """Logical Volume block device.
503 a8083063 Iustin Pop

504 a8083063 Iustin Pop
  """
505 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
506 6136f8f0 Iustin Pop
  _INVALID_NAMES = frozenset([".", "..", "snapshot", "pvmove"])
507 6136f8f0 Iustin Pop
  _INVALID_SUBSTRINGS = frozenset(["_mlog", "_mimage"])
508 6136f8f0 Iustin Pop
509 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
510 a8083063 Iustin Pop
    """Attaches to a LV device.
511 a8083063 Iustin Pop

512 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
513 a8083063 Iustin Pop

514 a8083063 Iustin Pop
    """
515 94dcbdb0 Andrea Spadaccini
    super(LogicalVolume, self).__init__(unique_id, children, size, params)
516 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
517 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
518 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
519 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
520 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
521 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
522 99e8295c Iustin Pop
    self._degraded = True
523 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
524 a8083063 Iustin Pop
    self.Attach()
525 a8083063 Iustin Pop
526 a8083063 Iustin Pop
  @classmethod
527 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
528 a8083063 Iustin Pop
    """Create a new logical volume.
529 a8083063 Iustin Pop

530 a8083063 Iustin Pop
    """
531 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
532 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
533 6c626518 Iustin Pop
                                   str(unique_id))
534 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
535 6136f8f0 Iustin Pop
    cls._ValidateName(vg_name)
536 6136f8f0 Iustin Pop
    cls._ValidateName(lv_name)
537 2070598f Iustin Pop
    pvs_info = cls.GetPVInfo([vg_name])
538 a8083063 Iustin Pop
    if not pvs_info:
539 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", vg_name)
540 a8083063 Iustin Pop
    pvs_info.sort()
541 a8083063 Iustin Pop
    pvs_info.reverse()
542 5b7b5d49 Guido Trotter
543 e687ec01 Michael Hanselmann
    pvlist = [pv[1] for pv in pvs_info]
544 403f5172 Guido Trotter
    if compat.any(":" in v for v in pvlist):
545 01b6558a Iustin Pop
      _ThrowError("Some of your PVs have the invalid character ':' in their"
546 01b6558a Iustin Pop
                  " name, this is not supported - please filter them out"
547 01b6558a Iustin Pop
                  " in lvm.conf using either 'filter' or 'preferred_names'")
548 e687ec01 Michael Hanselmann
    free_size = sum([pv[0] for pv in pvs_info])
549 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
550 ac00bf1b Andrea Spadaccini
    desired_stripes = params[constants.LDP_STRIPES]
551 43e11798 Andrea Spadaccini
    stripes = min(current_pvs, desired_stripes)
552 43e11798 Andrea Spadaccini
    if stripes < desired_stripes:
553 43e11798 Andrea Spadaccini
      logging.warning("Could not use %d stripes for VG %s, as only %d PVs are"
554 43e11798 Andrea Spadaccini
                      " available.", desired_stripes, vg_name, current_pvs)
555 5b7b5d49 Guido Trotter
556 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
557 5b7b5d49 Guido Trotter
    # calling the create function.
558 a8083063 Iustin Pop
    if free_size < size:
559 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
560 82463074 Iustin Pop
                  " available %s", size, free_size)
561 fecbe9d5 Iustin Pop
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
562 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
563 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
564 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
565 fecbe9d5 Iustin Pop
    # stripes
566 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
567 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
568 fecbe9d5 Iustin Pop
      if not result.failed:
569 fecbe9d5 Iustin Pop
        break
570 a8083063 Iustin Pop
    if result.failed:
571 82463074 Iustin Pop
      _ThrowError("LV create failed (%s): %s",
572 82463074 Iustin Pop
                  result.fail_reason, result.output)
573 94dcbdb0 Andrea Spadaccini
    return LogicalVolume(unique_id, children, size, params)
574 a8083063 Iustin Pop
575 a8083063 Iustin Pop
  @staticmethod
576 197478f2 René Nussbaumer
  def _GetVolumeInfo(lvm_cmd, fields):
577 197478f2 René Nussbaumer
    """Returns LVM Volumen infos using lvm_cmd
578 197478f2 René Nussbaumer

579 197478f2 René Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
580 197478f2 René Nussbaumer
    @param fields: Fields to return
581 197478f2 René Nussbaumer
    @return: A list of dicts each with the parsed fields
582 197478f2 René Nussbaumer

583 197478f2 René Nussbaumer
    """
584 197478f2 René Nussbaumer
    if not fields:
585 197478f2 René Nussbaumer
      raise errors.ProgrammerError("No fields specified")
586 197478f2 René Nussbaumer
587 197478f2 René Nussbaumer
    sep = "|"
588 197478f2 René Nussbaumer
    cmd = [lvm_cmd, "--noheadings", "--nosuffix", "--units=m", "--unbuffered",
589 197478f2 René Nussbaumer
           "--separator=%s" % sep, "-o%s" % ",".join(fields)]
590 197478f2 René Nussbaumer
591 197478f2 René Nussbaumer
    result = utils.RunCmd(cmd)
592 197478f2 René Nussbaumer
    if result.failed:
593 197478f2 René Nussbaumer
      raise errors.CommandError("Can't get the volume information: %s - %s" %
594 197478f2 René Nussbaumer
                                (result.fail_reason, result.output))
595 197478f2 René Nussbaumer
596 197478f2 René Nussbaumer
    data = []
597 197478f2 René Nussbaumer
    for line in result.stdout.splitlines():
598 197478f2 René Nussbaumer
      splitted_fields = line.strip().split(sep)
599 197478f2 René Nussbaumer
600 197478f2 René Nussbaumer
      if len(fields) != len(splitted_fields):
601 197478f2 René Nussbaumer
        raise errors.CommandError("Can't parse %s output: line '%s'" %
602 197478f2 René Nussbaumer
                                  (lvm_cmd, line))
603 197478f2 René Nussbaumer
604 197478f2 René Nussbaumer
      data.append(splitted_fields)
605 197478f2 René Nussbaumer
606 197478f2 René Nussbaumer
    return data
607 197478f2 René Nussbaumer
608 197478f2 René Nussbaumer
  @classmethod
609 197478f2 René Nussbaumer
  def GetPVInfo(cls, vg_names, filter_allocatable=True):
610 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
611 a8083063 Iustin Pop

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

615 c41eea6e Iustin Pop
    @rtype: list
616 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
617 098c0958 Michael Hanselmann

618 a8083063 Iustin Pop
    """
619 197478f2 René Nussbaumer
    try:
620 197478f2 René Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
621 197478f2 René Nussbaumer
                                        "pv_attr"])
622 197478f2 René Nussbaumer
    except errors.GenericError, err:
623 197478f2 René Nussbaumer
      logging.error("Can't get PV information: %s", err)
624 a8083063 Iustin Pop
      return None
625 197478f2 René Nussbaumer
626 a8083063 Iustin Pop
    data = []
627 197478f2 René Nussbaumer
    for pv_name, vg_name, pv_free, pv_attr in info:
628 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
629 197478f2 René Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
630 a8083063 Iustin Pop
        continue
631 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
632 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
633 2070598f Iustin Pop
        continue
634 197478f2 René Nussbaumer
      data.append((float(pv_free), pv_name, vg_name))
635 197478f2 René Nussbaumer
636 197478f2 René Nussbaumer
    return data
637 197478f2 René Nussbaumer
638 197478f2 René Nussbaumer
  @classmethod
639 197478f2 René Nussbaumer
  def GetVGInfo(cls, vg_names, filter_readonly=True):
640 197478f2 René Nussbaumer
    """Get the free space info for specific VGs.
641 197478f2 René Nussbaumer

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

645 197478f2 René Nussbaumer
    @rtype: list
646 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
647 673cd9c4 René Nussbaumer
             MiB
648 197478f2 René Nussbaumer

649 197478f2 René Nussbaumer
    """
650 197478f2 René Nussbaumer
    try:
651 673cd9c4 René Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
652 673cd9c4 René Nussbaumer
                                        "vg_size"])
653 197478f2 René Nussbaumer
    except errors.GenericError, err:
654 197478f2 René Nussbaumer
      logging.error("Can't get VG information: %s", err)
655 197478f2 René Nussbaumer
      return None
656 197478f2 René Nussbaumer
657 197478f2 René Nussbaumer
    data = []
658 673cd9c4 René Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
659 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not writable
660 197478f2 René Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
661 197478f2 René Nussbaumer
        continue
662 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
663 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
664 197478f2 René Nussbaumer
        continue
665 673cd9c4 René Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
666 a8083063 Iustin Pop
667 a8083063 Iustin Pop
    return data
668 a8083063 Iustin Pop
669 6136f8f0 Iustin Pop
  @classmethod
670 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
671 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
672 6136f8f0 Iustin Pop

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

677 6136f8f0 Iustin Pop
    """
678 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
679 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
680 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
681 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
682 6136f8f0 Iustin Pop
683 a8083063 Iustin Pop
  def Remove(self):
684 a8083063 Iustin Pop
    """Remove this logical volume.
685 a8083063 Iustin Pop

686 a8083063 Iustin Pop
    """
687 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
688 a8083063 Iustin Pop
      # the LV does not exist
689 0c6c04ec Iustin Pop
      return
690 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
691 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
692 a8083063 Iustin Pop
    if result.failed:
693 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
694 a8083063 Iustin Pop
695 f3e513ad Iustin Pop
  def Rename(self, new_id):
696 f3e513ad Iustin Pop
    """Rename this logical volume.
697 f3e513ad Iustin Pop

698 f3e513ad Iustin Pop
    """
699 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
700 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
701 f3e513ad Iustin Pop
    new_vg, new_name = new_id
702 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
703 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
704 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
705 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
706 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
707 f3e513ad Iustin Pop
    if result.failed:
708 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
709 be345db0 Iustin Pop
    self._lv_name = new_name
710 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
711 be345db0 Iustin Pop
712 a8083063 Iustin Pop
  def Attach(self):
713 a8083063 Iustin Pop
    """Attach to an existing LV.
714 a8083063 Iustin Pop

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

719 a8083063 Iustin Pop
    """
720 cb999543 Iustin Pop
    self.attached = False
721 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
722 38256320 Iustin Pop
                           "--units=m", "--nosuffix",
723 38256320 Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
724 38256320 Iustin Pop
                           "vg_extent_size,stripes", self.dev_path])
725 a8083063 Iustin Pop
    if result.failed:
726 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
727 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
728 a8083063 Iustin Pop
      return False
729 38256320 Iustin Pop
    # the output can (and will) have multiple lines for multi-segment
730 38256320 Iustin Pop
    # LVs, as the 'stripes' parameter is a segment one, so we take
731 38256320 Iustin Pop
    # only the last entry, which is the one we're interested in; note
732 38256320 Iustin Pop
    # that with LVM2 anyway the 'stripes' value must be constant
733 38256320 Iustin Pop
    # across segments, so this is a no-op actually
734 38256320 Iustin Pop
    out = result.stdout.splitlines()
735 38256320 Iustin Pop
    if not out: # totally empty result? splitlines() returns at least
736 38256320 Iustin Pop
                # one line for any non-empty string
737 38256320 Iustin Pop
      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
738 38256320 Iustin Pop
      return False
739 d0c8c01d Iustin Pop
    out = out[-1].strip().rstrip(",")
740 99e8295c Iustin Pop
    out = out.split(",")
741 38256320 Iustin Pop
    if len(out) != 5:
742 38256320 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
743 99e8295c Iustin Pop
      return False
744 99e8295c Iustin Pop
745 38256320 Iustin Pop
    status, major, minor, pe_size, stripes = out
746 0304f0ec Iustin Pop
    if len(status) < 6:
747 0304f0ec Iustin Pop
      logging.error("lvs lv_attr is not at least 6 characters (%s)", status)
748 99e8295c Iustin Pop
      return False
749 99e8295c Iustin Pop
750 99e8295c Iustin Pop
    try:
751 99e8295c Iustin Pop
      major = int(major)
752 99e8295c Iustin Pop
      minor = int(minor)
753 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
754 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
755 99e8295c Iustin Pop
756 38256320 Iustin Pop
    try:
757 38256320 Iustin Pop
      pe_size = int(float(pe_size))
758 38256320 Iustin Pop
    except (TypeError, ValueError), err:
759 38256320 Iustin Pop
      logging.error("Can't parse vg extent size: %s", err)
760 38256320 Iustin Pop
      return False
761 38256320 Iustin Pop
762 38256320 Iustin Pop
    try:
763 38256320 Iustin Pop
      stripes = int(stripes)
764 38256320 Iustin Pop
    except (TypeError, ValueError), err:
765 38256320 Iustin Pop
      logging.error("Can't parse the number of stripes: %s", err)
766 38256320 Iustin Pop
      return False
767 38256320 Iustin Pop
768 99e8295c Iustin Pop
    self.major = major
769 99e8295c Iustin Pop
    self.minor = minor
770 38256320 Iustin Pop
    self.pe_size = pe_size
771 38256320 Iustin Pop
    self.stripe_count = stripes
772 d0c8c01d Iustin Pop
    self._degraded = status[0] == "v" # virtual volume, i.e. doesn't backing
773 99e8295c Iustin Pop
                                      # storage
774 cb999543 Iustin Pop
    self.attached = True
775 99e8295c Iustin Pop
    return True
776 a8083063 Iustin Pop
777 a8083063 Iustin Pop
  def Assemble(self):
778 a8083063 Iustin Pop
    """Assemble the device.
779 a8083063 Iustin Pop

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

784 a8083063 Iustin Pop
    """
785 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
786 5574047a Iustin Pop
    if result.failed:
787 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
788 a8083063 Iustin Pop
789 a8083063 Iustin Pop
  def Shutdown(self):
790 a8083063 Iustin Pop
    """Shutdown the device.
791 a8083063 Iustin Pop

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

795 a8083063 Iustin Pop
    """
796 746f7476 Iustin Pop
    pass
797 a8083063 Iustin Pop
798 9db6dbce Iustin Pop
  def GetSyncStatus(self):
799 9db6dbce Iustin Pop
    """Returns the sync status of the device.
800 9db6dbce Iustin Pop

801 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
802 9db6dbce Iustin Pop
    status of the mirror.
803 9db6dbce Iustin Pop

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

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

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

817 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
818 c41eea6e Iustin Pop

819 9db6dbce Iustin Pop
    """
820 f208978a Michael Hanselmann
    if self._degraded:
821 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
822 f208978a Michael Hanselmann
    else:
823 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
824 f208978a Michael Hanselmann
825 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
826 96acbc09 Michael Hanselmann
                                  major=self.major,
827 96acbc09 Michael Hanselmann
                                  minor=self.minor,
828 96acbc09 Michael Hanselmann
                                  sync_percent=None,
829 96acbc09 Michael Hanselmann
                                  estimated_time=None,
830 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
831 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
832 9db6dbce Iustin Pop
833 a8083063 Iustin Pop
  def Open(self, force=False):
834 a8083063 Iustin Pop
    """Make the device ready for I/O.
835 a8083063 Iustin Pop

836 a8083063 Iustin Pop
    This is a no-op for the LV device type.
837 a8083063 Iustin Pop

838 a8083063 Iustin Pop
    """
839 fdbd668d Iustin Pop
    pass
840 a8083063 Iustin Pop
841 a8083063 Iustin Pop
  def Close(self):
842 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
843 a8083063 Iustin Pop

844 a8083063 Iustin Pop
    This is a no-op for the LV device type.
845 a8083063 Iustin Pop

846 a8083063 Iustin Pop
    """
847 fdbd668d Iustin Pop
    pass
848 a8083063 Iustin Pop
849 a8083063 Iustin Pop
  def Snapshot(self, size):
850 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
851 a8083063 Iustin Pop

852 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
853 800ac399 Iustin Pop

854 a8083063 Iustin Pop
    """
855 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
856 a8083063 Iustin Pop
857 a8083063 Iustin Pop
    # remove existing snapshot if found
858 94dcbdb0 Andrea Spadaccini
    snap = LogicalVolume((self._vg_name, snap_name), None, size, self.params)
859 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
860 a8083063 Iustin Pop
861 197478f2 René Nussbaumer
    vg_info = self.GetVGInfo([self._vg_name])
862 197478f2 René Nussbaumer
    if not vg_info:
863 197478f2 René Nussbaumer
      _ThrowError("Can't compute VG info for vg %s", self._vg_name)
864 673cd9c4 René Nussbaumer
    free_size, _, _ = vg_info[0]
865 a8083063 Iustin Pop
    if free_size < size:
866 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
867 82463074 Iustin Pop
                  " available %s", size, free_size)
868 a8083063 Iustin Pop
869 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
870 e398546b Iustin Pop
                               "-n%s" % snap_name, self.dev_path]))
871 a8083063 Iustin Pop
872 800ac399 Iustin Pop
    return (self._vg_name, snap_name)
873 a8083063 Iustin Pop
874 a1556cfa Iustin Pop
  def _RemoveOldInfo(self):
875 a1556cfa Iustin Pop
    """Try to remove old tags from the lv.
876 a1556cfa Iustin Pop

877 a1556cfa Iustin Pop
    """
878 a1556cfa Iustin Pop
    result = utils.RunCmd(["lvs", "-o", "tags", "--noheadings", "--nosuffix",
879 a1556cfa Iustin Pop
                           self.dev_path])
880 a1556cfa Iustin Pop
    _CheckResult(result)
881 a1556cfa Iustin Pop
882 a1556cfa Iustin Pop
    raw_tags = result.stdout.strip()
883 a1556cfa Iustin Pop
    if raw_tags:
884 a1556cfa Iustin Pop
      for tag in raw_tags.split(","):
885 a1556cfa Iustin Pop
        _CheckResult(utils.RunCmd(["lvchange", "--deltag",
886 a1556cfa Iustin Pop
                                   tag.strip(), self.dev_path]))
887 a1556cfa Iustin Pop
888 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
889 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
890 a0c3fea1 Michael Hanselmann

891 a0c3fea1 Michael Hanselmann
    """
892 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
893 a0c3fea1 Michael Hanselmann
894 a1556cfa Iustin Pop
    self._RemoveOldInfo()
895 a1556cfa Iustin Pop
896 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
897 d0c8c01d Iustin Pop
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
898 d0c8c01d Iustin Pop
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
899 a0c3fea1 Michael Hanselmann
900 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
901 a0c3fea1 Michael Hanselmann
    text = text[:128]
902 a0c3fea1 Michael Hanselmann
903 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvchange", "--addtag", text, self.dev_path]))
904 82463074 Iustin Pop
905 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
906 1005d816 Iustin Pop
    """Grow the logical volume.
907 1005d816 Iustin Pop

908 1005d816 Iustin Pop
    """
909 cad0723b Iustin Pop
    if not backingstore:
910 cad0723b Iustin Pop
      return
911 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
912 38256320 Iustin Pop
      if not self.Attach():
913 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
914 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
915 38256320 Iustin Pop
    rest = amount % full_stripe_size
916 38256320 Iustin Pop
    if rest != 0:
917 38256320 Iustin Pop
      amount += full_stripe_size - rest
918 7fe23d47 Iustin Pop
    cmd = ["lvextend", "-L", "+%dm" % amount]
919 7fe23d47 Iustin Pop
    if dryrun:
920 7fe23d47 Iustin Pop
      cmd.append("--test")
921 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
922 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
923 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
924 1005d816 Iustin Pop
    # supports 'cling'
925 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
926 7fe23d47 Iustin Pop
      result = utils.RunCmd(cmd + ["--alloc", alloc_policy, self.dev_path])
927 1005d816 Iustin Pop
      if not result.failed:
928 1005d816 Iustin Pop
        return
929 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
930 a0c3fea1 Michael Hanselmann
931 a0c3fea1 Michael Hanselmann
932 6b90c22e Iustin Pop
class DRBD8Status(object):
933 6b90c22e Iustin Pop
  """A DRBD status representation class.
934 6b90c22e Iustin Pop

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

937 6b90c22e Iustin Pop
  """
938 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
939 01e2ce3a Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
940 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
941 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
942 7a380ddf René Nussbaumer
                       # Due to a bug in drbd in the kernel, introduced in
943 7a380ddf René Nussbaumer
                       # commit 4b0715f096 (still unfixed as of 2011-08-22)
944 7a380ddf René Nussbaumer
                       "(?:\s|M)"
945 7a380ddf René Nussbaumer
                       "finish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
946 6b90c22e Iustin Pop
947 3c003d9d Iustin Pop
  CS_UNCONFIGURED = "Unconfigured"
948 3c003d9d Iustin Pop
  CS_STANDALONE = "StandAlone"
949 3c003d9d Iustin Pop
  CS_WFCONNECTION = "WFConnection"
950 3c003d9d Iustin Pop
  CS_WFREPORTPARAMS = "WFReportParams"
951 3c003d9d Iustin Pop
  CS_CONNECTED = "Connected"
952 3c003d9d Iustin Pop
  CS_STARTINGSYNCS = "StartingSyncS"
953 3c003d9d Iustin Pop
  CS_STARTINGSYNCT = "StartingSyncT"
954 3c003d9d Iustin Pop
  CS_WFBITMAPS = "WFBitMapS"
955 3c003d9d Iustin Pop
  CS_WFBITMAPT = "WFBitMapT"
956 3c003d9d Iustin Pop
  CS_WFSYNCUUID = "WFSyncUUID"
957 3c003d9d Iustin Pop
  CS_SYNCSOURCE = "SyncSource"
958 3c003d9d Iustin Pop
  CS_SYNCTARGET = "SyncTarget"
959 3c003d9d Iustin Pop
  CS_PAUSEDSYNCS = "PausedSyncS"
960 3c003d9d Iustin Pop
  CS_PAUSEDSYNCT = "PausedSyncT"
961 3c003d9d Iustin Pop
  CSET_SYNC = frozenset([
962 3c003d9d Iustin Pop
    CS_WFREPORTPARAMS,
963 3c003d9d Iustin Pop
    CS_STARTINGSYNCS,
964 3c003d9d Iustin Pop
    CS_STARTINGSYNCT,
965 3c003d9d Iustin Pop
    CS_WFBITMAPS,
966 3c003d9d Iustin Pop
    CS_WFBITMAPT,
967 3c003d9d Iustin Pop
    CS_WFSYNCUUID,
968 3c003d9d Iustin Pop
    CS_SYNCSOURCE,
969 3c003d9d Iustin Pop
    CS_SYNCTARGET,
970 3c003d9d Iustin Pop
    CS_PAUSEDSYNCS,
971 3c003d9d Iustin Pop
    CS_PAUSEDSYNCT,
972 3c003d9d Iustin Pop
    ])
973 3c003d9d Iustin Pop
974 3c003d9d Iustin Pop
  DS_DISKLESS = "Diskless"
975 3c003d9d Iustin Pop
  DS_ATTACHING = "Attaching" # transient state
976 3c003d9d Iustin Pop
  DS_FAILED = "Failed" # transient state, next: diskless
977 3c003d9d Iustin Pop
  DS_NEGOTIATING = "Negotiating" # transient state
978 3c003d9d Iustin Pop
  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
979 3c003d9d Iustin Pop
  DS_OUTDATED = "Outdated"
980 3c003d9d Iustin Pop
  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
981 3c003d9d Iustin Pop
  DS_CONSISTENT = "Consistent"
982 3c003d9d Iustin Pop
  DS_UPTODATE = "UpToDate" # normal state
983 3c003d9d Iustin Pop
984 3c003d9d Iustin Pop
  RO_PRIMARY = "Primary"
985 3c003d9d Iustin Pop
  RO_SECONDARY = "Secondary"
986 3c003d9d Iustin Pop
  RO_UNKNOWN = "Unknown"
987 3c003d9d Iustin Pop
988 6b90c22e Iustin Pop
  def __init__(self, procline):
989 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
990 767d52d3 Iustin Pop
    if u:
991 3c003d9d Iustin Pop
      self.cstatus = self.CS_UNCONFIGURED
992 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
993 767d52d3 Iustin Pop
    else:
994 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
995 767d52d3 Iustin Pop
      if not m:
996 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
997 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
998 767d52d3 Iustin Pop
      self.lrole = m.group(2)
999 767d52d3 Iustin Pop
      self.rrole = m.group(3)
1000 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
1001 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
1002 767d52d3 Iustin Pop
1003 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
1004 6b90c22e Iustin Pop
1005 3c003d9d Iustin Pop
    self.is_standalone = self.cstatus == self.CS_STANDALONE
1006 3c003d9d Iustin Pop
    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
1007 3c003d9d Iustin Pop
    self.is_connected = self.cstatus == self.CS_CONNECTED
1008 3c003d9d Iustin Pop
    self.is_primary = self.lrole == self.RO_PRIMARY
1009 3c003d9d Iustin Pop
    self.is_secondary = self.lrole == self.RO_SECONDARY
1010 3c003d9d Iustin Pop
    self.peer_primary = self.rrole == self.RO_PRIMARY
1011 3c003d9d Iustin Pop
    self.peer_secondary = self.rrole == self.RO_SECONDARY
1012 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
1013 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
1014 6b90c22e Iustin Pop
1015 3c003d9d Iustin Pop
    self.is_diskless = self.ldisk == self.DS_DISKLESS
1016 3c003d9d Iustin Pop
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
1017 6b90c22e Iustin Pop
1018 3c003d9d Iustin Pop
    self.is_in_resync = self.cstatus in self.CSET_SYNC
1019 3c003d9d Iustin Pop
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
1020 6b93ec9d Iustin Pop
1021 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
1022 6b90c22e Iustin Pop
    if m:
1023 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
1024 6b90c22e Iustin Pop
      hours = int(m.group(2))
1025 6b90c22e Iustin Pop
      minutes = int(m.group(3))
1026 6b90c22e Iustin Pop
      seconds = int(m.group(4))
1027 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
1028 6b90c22e Iustin Pop
    else:
1029 3c003d9d Iustin Pop
      # we have (in this if branch) no percent information, but if
1030 3c003d9d Iustin Pop
      # we're resyncing we need to 'fake' a sync percent information,
1031 3c003d9d Iustin Pop
      # as this is how cmdlib determines if it makes sense to wait for
1032 3c003d9d Iustin Pop
      # resyncing or not
1033 3c003d9d Iustin Pop
      if self.is_in_resync:
1034 3c003d9d Iustin Pop
        self.sync_percent = 0
1035 3c003d9d Iustin Pop
      else:
1036 3c003d9d Iustin Pop
        self.sync_percent = None
1037 6b90c22e Iustin Pop
      self.est_time = None
1038 6b90c22e Iustin Pop
1039 6b90c22e Iustin Pop
1040 b459a848 Andrea Spadaccini
class BaseDRBD(BlockDev): # pylint: disable=W0223
1041 0f7f32d9 Iustin Pop
  """Base DRBD class.
1042 a8083063 Iustin Pop

1043 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
1044 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
1045 0f7f32d9 Iustin Pop

1046 abdf0113 Iustin Pop
  """
1047 fcee765d Manuel Franceschini
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)(?:\.\d+)?"
1048 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
1049 9122e60a Iustin Pop
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
1050 9122e60a Iustin Pop
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
1051 a8083063 Iustin Pop
1052 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
1053 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
1054 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
1055 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
1056 a8083063 Iustin Pop
1057 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
1058 549071a0 Luca Bigliardi
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
1059 6b90c22e Iustin Pop
1060 abdf0113 Iustin Pop
  @staticmethod
1061 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
1062 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
1063 a8083063 Iustin Pop

1064 a8083063 Iustin Pop
    """
1065 abdf0113 Iustin Pop
    try:
1066 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
1067 f6eaed12 Iustin Pop
    except EnvironmentError, err:
1068 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
1069 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
1070 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
1071 f6eaed12 Iustin Pop
      else:
1072 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
1073 abdf0113 Iustin Pop
    if not data:
1074 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
1075 abdf0113 Iustin Pop
    return data
1076 a8083063 Iustin Pop
1077 9122e60a Iustin Pop
  @classmethod
1078 9122e60a Iustin Pop
  def _MassageProcData(cls, data):
1079 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
1080 a8083063 Iustin Pop

1081 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
1082 c41eea6e Iustin Pop
        for that minor
1083 a8083063 Iustin Pop

1084 a8083063 Iustin Pop
    """
1085 abdf0113 Iustin Pop
    results = {}
1086 abdf0113 Iustin Pop
    old_minor = old_line = None
1087 abdf0113 Iustin Pop
    for line in data:
1088 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
1089 67d101d4 Iustin Pop
        continue
1090 9122e60a Iustin Pop
      lresult = cls._VALID_LINE_RE.match(line)
1091 abdf0113 Iustin Pop
      if lresult is not None:
1092 abdf0113 Iustin Pop
        if old_minor is not None:
1093 abdf0113 Iustin Pop
          results[old_minor] = old_line
1094 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
1095 abdf0113 Iustin Pop
        old_line = line
1096 abdf0113 Iustin Pop
      else:
1097 abdf0113 Iustin Pop
        if old_minor is not None:
1098 abdf0113 Iustin Pop
          old_line += " " + line.strip()
1099 abdf0113 Iustin Pop
    # add last line
1100 abdf0113 Iustin Pop
    if old_minor is not None:
1101 abdf0113 Iustin Pop
      results[old_minor] = old_line
1102 abdf0113 Iustin Pop
    return results
1103 a8083063 Iustin Pop
1104 abdf0113 Iustin Pop
  @classmethod
1105 fcee765d Manuel Franceschini
  def _GetVersion(cls, proc_data):
1106 abdf0113 Iustin Pop
    """Return the DRBD version.
1107 a8083063 Iustin Pop

1108 abdf0113 Iustin Pop
    This will return a dict with keys:
1109 c41eea6e Iustin Pop
      - k_major
1110 c41eea6e Iustin Pop
      - k_minor
1111 c41eea6e Iustin Pop
      - k_point
1112 c41eea6e Iustin Pop
      - api
1113 c41eea6e Iustin Pop
      - proto
1114 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
1115 a8083063 Iustin Pop

1116 a8083063 Iustin Pop
    """
1117 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
1118 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
1119 abdf0113 Iustin Pop
    if not version:
1120 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
1121 abdf0113 Iustin Pop
                                    first_line)
1122 a8083063 Iustin Pop
1123 abdf0113 Iustin Pop
    values = version.groups()
1124 5ae4945a Iustin Pop
    retval = {
1125 5ae4945a Iustin Pop
      "k_major": int(values[0]),
1126 5ae4945a Iustin Pop
      "k_minor": int(values[1]),
1127 5ae4945a Iustin Pop
      "k_point": int(values[2]),
1128 5ae4945a Iustin Pop
      "api": int(values[3]),
1129 5ae4945a Iustin Pop
      "proto": int(values[4]),
1130 5ae4945a Iustin Pop
      }
1131 abdf0113 Iustin Pop
    if values[5] is not None:
1132 d0c8c01d Iustin Pop
      retval["proto2"] = values[5]
1133 a8083063 Iustin Pop
1134 abdf0113 Iustin Pop
    return retval
1135 abdf0113 Iustin Pop
1136 abdf0113 Iustin Pop
  @staticmethod
1137 549071a0 Luca Bigliardi
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
1138 549071a0 Luca Bigliardi
    """Returns DRBD usermode_helper currently set.
1139 549071a0 Luca Bigliardi

1140 549071a0 Luca Bigliardi
    """
1141 549071a0 Luca Bigliardi
    try:
1142 549071a0 Luca Bigliardi
      helper = utils.ReadFile(filename).splitlines()[0]
1143 549071a0 Luca Bigliardi
    except EnvironmentError, err:
1144 549071a0 Luca Bigliardi
      if err.errno == errno.ENOENT:
1145 549071a0 Luca Bigliardi
        _ThrowError("The file %s cannot be opened, check if the module"
1146 549071a0 Luca Bigliardi
                    " is loaded (%s)", filename, str(err))
1147 549071a0 Luca Bigliardi
      else:
1148 549071a0 Luca Bigliardi
        _ThrowError("Can't read DRBD helper file %s: %s", filename, str(err))
1149 549071a0 Luca Bigliardi
    if not helper:
1150 549071a0 Luca Bigliardi
      _ThrowError("Can't read any data from %s", filename)
1151 549071a0 Luca Bigliardi
    return helper
1152 549071a0 Luca Bigliardi
1153 549071a0 Luca Bigliardi
  @staticmethod
1154 abdf0113 Iustin Pop
  def _DevPath(minor):
1155 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
1156 a8083063 Iustin Pop

1157 a8083063 Iustin Pop
    """
1158 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
1159 a8083063 Iustin Pop
1160 abdf0113 Iustin Pop
  @classmethod
1161 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
1162 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
1163 a8083063 Iustin Pop

1164 a8083063 Iustin Pop
    """
1165 abdf0113 Iustin Pop
    data = cls._GetProcData()
1166 a8083063 Iustin Pop
1167 abdf0113 Iustin Pop
    used_devs = {}
1168 abdf0113 Iustin Pop
    for line in data:
1169 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1170 abdf0113 Iustin Pop
      if not match:
1171 abdf0113 Iustin Pop
        continue
1172 abdf0113 Iustin Pop
      minor = int(match.group(1))
1173 abdf0113 Iustin Pop
      state = match.group(2)
1174 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1175 abdf0113 Iustin Pop
        continue
1176 abdf0113 Iustin Pop
      used_devs[minor] = state, line
1177 a8083063 Iustin Pop
1178 abdf0113 Iustin Pop
    return used_devs
1179 a8083063 Iustin Pop
1180 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
1181 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
1182 0834c866 Iustin Pop

1183 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1184 a8083063 Iustin Pop

1185 a8083063 Iustin Pop
    """
1186 abdf0113 Iustin Pop
    if minor is None:
1187 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
1188 cb999543 Iustin Pop
      self.attached = False
1189 a8083063 Iustin Pop
    else:
1190 abdf0113 Iustin Pop
      self.minor = minor
1191 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
1192 cb999543 Iustin Pop
      self.attached = True
1193 a8083063 Iustin Pop
1194 a8083063 Iustin Pop
  @staticmethod
1195 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
1196 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
1197 a8083063 Iustin Pop

1198 3bc145d8 Bernardo Dal Seno
    This currently only checks the size, which must be around
1199 abdf0113 Iustin Pop
    128MiB.
1200 a8083063 Iustin Pop

1201 a8083063 Iustin Pop
    """
1202 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1203 abdf0113 Iustin Pop
    if result.failed:
1204 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
1205 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
1206 a8083063 Iustin Pop
    try:
1207 abdf0113 Iustin Pop
      sectors = int(result.stdout)
1208 691744c4 Iustin Pop
    except (TypeError, ValueError):
1209 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
1210 c04bc777 Iustin Pop
    num_bytes = sectors * 512
1211 c04bc777 Iustin Pop
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
1212 c04bc777 Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (num_bytes / 1024 / 1024))
1213 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
1214 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
1215 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
1216 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
1217 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
1218 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
1219 c04bc777 Iustin Pop
    if num_bytes > 1024 * 1024 * 1024:
1220 c04bc777 Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (num_bytes / 1024 / 1024))
1221 a8083063 Iustin Pop
1222 abdf0113 Iustin Pop
  def Rename(self, new_id):
1223 abdf0113 Iustin Pop
    """Rename a device.
1224 a8083063 Iustin Pop

1225 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1226 a8083063 Iustin Pop

1227 a8083063 Iustin Pop
    """
1228 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1229 a8083063 Iustin Pop
1230 f3e513ad Iustin Pop
1231 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1232 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1233 a2cfdea2 Iustin Pop

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

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

1243 a2cfdea2 Iustin Pop
  """
1244 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1245 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1246 a2cfdea2 Iustin Pop
1247 cf8df3f3 Iustin Pop
  # timeout constants
1248 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
1249 cf8df3f3 Iustin Pop
1250 8a69b3a8 Andrea Spadaccini
  # command line options for barriers
1251 8a69b3a8 Andrea Spadaccini
  _DISABLE_DISK_OPTION = "--no-disk-barrier"  # -a
1252 8a69b3a8 Andrea Spadaccini
  _DISABLE_DRAIN_OPTION = "--no-disk-drain"   # -D
1253 8a69b3a8 Andrea Spadaccini
  _DISABLE_FLUSH_OPTION = "--no-disk-flushes" # -i
1254 8a69b3a8 Andrea Spadaccini
  _DISABLE_META_FLUSH_OPTION = "--no-md-flushes"  # -m
1255 8a69b3a8 Andrea Spadaccini
1256 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
1257 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1258 fc1dc9d7 Iustin Pop
      children = []
1259 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
1260 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1261 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
1262 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1263 310fbb64 Iustin Pop
    (self._lhost, self._lport,
1264 310fbb64 Iustin Pop
     self._rhost, self._rport,
1265 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
1266 310fbb64 Iustin Pop
    if children:
1267 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
1268 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
1269 310fbb64 Iustin Pop
        children = []
1270 94dcbdb0 Andrea Spadaccini
    super(DRBD8, self).__init__(unique_id, children, size, params)
1271 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1272 fcee765d Manuel Franceschini
    version = self._GetVersion(self._GetProcData())
1273 e687ec01 Michael Hanselmann
    if version["k_major"] != 8:
1274 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
1275 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
1276 d0c8c01d Iustin Pop
                  version["k_major"], version["k_minor"])
1277 a2cfdea2 Iustin Pop
1278 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1279 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1280 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1281 ffa1c0dc Iustin Pop
                       (unique_id,))
1282 a2cfdea2 Iustin Pop
    self.Attach()
1283 a2cfdea2 Iustin Pop
1284 a2cfdea2 Iustin Pop
  @classmethod
1285 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1286 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1287 a2cfdea2 Iustin Pop

1288 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1289 a2cfdea2 Iustin Pop

1290 a2cfdea2 Iustin Pop
    """
1291 18e4dee6 Iustin Pop
    # Zero the metadata first, in order to make sure drbdmeta doesn't
1292 18e4dee6 Iustin Pop
    # try to auto-detect existing filesystems or similar (see
1293 18e4dee6 Iustin Pop
    # http://code.google.com/p/ganeti/issues/detail?id=182); we only
1294 18e4dee6 Iustin Pop
    # care about the first 128MB of data in the device, even though it
1295 18e4dee6 Iustin Pop
    # can be bigger
1296 18e4dee6 Iustin Pop
    result = utils.RunCmd([constants.DD_CMD,
1297 18e4dee6 Iustin Pop
                           "if=/dev/zero", "of=%s" % dev_path,
1298 18e4dee6 Iustin Pop
                           "bs=1048576", "count=128", "oflag=direct"])
1299 18e4dee6 Iustin Pop
    if result.failed:
1300 18e4dee6 Iustin Pop
      _ThrowError("Can't wipe the meta device: %s", result.output)
1301 18e4dee6 Iustin Pop
1302 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1303 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1304 a2cfdea2 Iustin Pop
    if result.failed:
1305 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1306 a2cfdea2 Iustin Pop
1307 a2cfdea2 Iustin Pop
  @classmethod
1308 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1309 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1310 a2cfdea2 Iustin Pop

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

1314 a2cfdea2 Iustin Pop
    """
1315 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1316 a2cfdea2 Iustin Pop
1317 a2cfdea2 Iustin Pop
    highest = None
1318 a2cfdea2 Iustin Pop
    for line in data:
1319 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1320 a2cfdea2 Iustin Pop
      if match:
1321 a2cfdea2 Iustin Pop
        return int(match.group(1))
1322 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1323 a2cfdea2 Iustin Pop
      if match:
1324 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1325 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1326 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1327 a2cfdea2 Iustin Pop
      return 0
1328 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1329 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1330 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1331 a2cfdea2 Iustin Pop
    return highest + 1
1332 a2cfdea2 Iustin Pop
1333 a2cfdea2 Iustin Pop
  @classmethod
1334 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1335 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1336 a2cfdea2 Iustin Pop

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

1340 a2cfdea2 Iustin Pop
    """
1341 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1342 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1343 a2cfdea2 Iustin Pop
1344 a2cfdea2 Iustin Pop
    # pyparsing setup
1345 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1346 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1347 5a672c30 Manuel Franceschini
    lbracket = pyp.Literal("[").suppress()
1348 5a672c30 Manuel Franceschini
    rbracket = pyp.Literal("]").suppress()
1349 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1350 5a672c30 Manuel Franceschini
    colon = pyp.Literal(":").suppress()
1351 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1352 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1353 a2cfdea2 Iustin Pop
1354 e687ec01 Michael Hanselmann
    comment = pyp.Literal("#") + pyp.Optional(pyp.restOfLine)
1355 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1356 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1357 a2cfdea2 Iustin Pop
1358 3ccb3a64 Michael Hanselmann
    keyword = pyp.Word(pyp.alphanums + "-")
1359 a2cfdea2 Iustin Pop
1360 a2cfdea2 Iustin Pop
    # value types
1361 3ccb3a64 Michael Hanselmann
    value = pyp.Word(pyp.alphanums + "_-/.:")
1362 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1363 5a672c30 Manuel Franceschini
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1364 5a672c30 Manuel Franceschini
                 pyp.Word(pyp.nums + ".") + colon + number)
1365 5a672c30 Manuel Franceschini
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
1366 5a672c30 Manuel Franceschini
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
1367 5a672c30 Manuel Franceschini
                 pyp.Optional(rbracket) + colon + number)
1368 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1369 5a672c30 Manuel Franceschini
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
1370 01e2ce3a Iustin Pop
    # device name, extended syntax
1371 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1372 a2cfdea2 Iustin Pop
1373 a2cfdea2 Iustin Pop
    # a statement
1374 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1375 5a672c30 Manuel Franceschini
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
1376 01e2ce3a Iustin Pop
                         device_value) +
1377 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1378 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1379 a2cfdea2 Iustin Pop
1380 a2cfdea2 Iustin Pop
    # an entire section
1381 d0c8c01d Iustin Pop
    section_name = pyp.Word(pyp.alphas + "_")
1382 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1383 a2cfdea2 Iustin Pop
1384 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1385 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1386 a2cfdea2 Iustin Pop
1387 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1388 a2cfdea2 Iustin Pop
1389 a2cfdea2 Iustin Pop
    return bnf
1390 a2cfdea2 Iustin Pop
1391 a2cfdea2 Iustin Pop
  @classmethod
1392 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1393 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1394 a2cfdea2 Iustin Pop

1395 a2cfdea2 Iustin Pop
    """
1396 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1397 a2cfdea2 Iustin Pop
    if result.failed:
1398 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1399 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1400 3840729d Iustin Pop
      return None
1401 3840729d Iustin Pop
    return result.stdout
1402 3840729d Iustin Pop
1403 3840729d Iustin Pop
  @classmethod
1404 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1405 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1406 3840729d Iustin Pop

1407 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1408 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1409 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1410 3840729d Iustin Pop
    by _GetShowData.
1411 3840729d Iustin Pop

1412 3840729d Iustin Pop
    """
1413 3840729d Iustin Pop
    data = {}
1414 a2cfdea2 Iustin Pop
    if not out:
1415 a2cfdea2 Iustin Pop
      return data
1416 a2cfdea2 Iustin Pop
1417 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1418 a2cfdea2 Iustin Pop
    # run pyparse
1419 a2cfdea2 Iustin Pop
1420 a2cfdea2 Iustin Pop
    try:
1421 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1422 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1423 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1424 a2cfdea2 Iustin Pop
1425 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1426 a2cfdea2 Iustin Pop
    for section in results:
1427 a2cfdea2 Iustin Pop
      sname = section[0]
1428 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1429 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1430 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1431 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1432 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1433 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1434 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1435 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1436 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1437 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1438 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1439 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1440 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1441 a2cfdea2 Iustin Pop
    return data
1442 a2cfdea2 Iustin Pop
1443 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1444 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1445 a2cfdea2 Iustin Pop

1446 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1447 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1448 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1449 a2cfdea2 Iustin Pop
    device.
1450 a2cfdea2 Iustin Pop

1451 a2cfdea2 Iustin Pop
    """
1452 b00b95dd Iustin Pop
    if self._children:
1453 b00b95dd Iustin Pop
      backend, meta = self._children
1454 b00b95dd Iustin Pop
    else:
1455 b00b95dd Iustin Pop
      backend = meta = None
1456 b00b95dd Iustin Pop
1457 a2cfdea2 Iustin Pop
    if backend is not None:
1458 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1459 a2cfdea2 Iustin Pop
    else:
1460 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1461 b00b95dd Iustin Pop
1462 a2cfdea2 Iustin Pop
    if meta is not None:
1463 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1464 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1465 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1466 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1467 a2cfdea2 Iustin Pop
    else:
1468 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1469 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1470 a2cfdea2 Iustin Pop
    return retval
1471 a2cfdea2 Iustin Pop
1472 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1473 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1474 a2cfdea2 Iustin Pop

1475 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1476 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1477 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1478 a2cfdea2 Iustin Pop
    device.
1479 a2cfdea2 Iustin Pop

1480 a2cfdea2 Iustin Pop
    """
1481 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1482 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1483 a2cfdea2 Iustin Pop
      return True
1484 a2cfdea2 Iustin Pop
1485 a2cfdea2 Iustin Pop
    if self._lhost is None:
1486 a2cfdea2 Iustin Pop
      return False
1487 a2cfdea2 Iustin Pop
1488 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1489 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1490 a2cfdea2 Iustin Pop
      return False
1491 a2cfdea2 Iustin Pop
1492 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1493 a2cfdea2 Iustin Pop
    retval = (retval and
1494 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1495 a2cfdea2 Iustin Pop
    return retval
1496 a2cfdea2 Iustin Pop
1497 8a69b3a8 Andrea Spadaccini
  def _AssembleLocal(self, minor, backend, meta, size):
1498 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1499 a2cfdea2 Iustin Pop

1500 a2cfdea2 Iustin Pop
    """
1501 8a69b3a8 Andrea Spadaccini
    args = ["drbdsetup", self._DevPath(minor), "disk",
1502 f069addf Iustin Pop
            backend, meta, "0",
1503 f069addf Iustin Pop
            "-e", "detach",
1504 f069addf Iustin Pop
            "--create-device"]
1505 60bca04a Iustin Pop
    if size:
1506 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1507 8a69b3a8 Andrea Spadaccini
1508 8a69b3a8 Andrea Spadaccini
    version = self._GetVersion(self._GetProcData())
1509 8a69b3a8 Andrea Spadaccini
    vmaj = version["k_major"]
1510 8a69b3a8 Andrea Spadaccini
    vmin = version["k_minor"]
1511 8a69b3a8 Andrea Spadaccini
    vrel = version["k_point"]
1512 8a69b3a8 Andrea Spadaccini
1513 8a69b3a8 Andrea Spadaccini
    barrier_args = \
1514 8a69b3a8 Andrea Spadaccini
      self._ComputeDiskBarrierArgs(vmaj, vmin, vrel,
1515 ac00bf1b Andrea Spadaccini
                                   self.params[constants.LDP_BARRIERS],
1516 ac00bf1b Andrea Spadaccini
                                   self.params[constants.LDP_NO_META_FLUSH])
1517 8a69b3a8 Andrea Spadaccini
    args.extend(barrier_args)
1518 8a69b3a8 Andrea Spadaccini
1519 ad1dd4c7 Andrea Spadaccini
    if self.params[constants.LDP_DISK_CUSTOM]:
1520 ad1dd4c7 Andrea Spadaccini
      args.extend(shlex.split(self.params[constants.LDP_DISK_CUSTOM]))
1521 ad1dd4c7 Andrea Spadaccini
1522 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1523 a2cfdea2 Iustin Pop
    if result.failed:
1524 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1525 a2cfdea2 Iustin Pop
1526 8a69b3a8 Andrea Spadaccini
  @classmethod
1527 8a69b3a8 Andrea Spadaccini
  def _ComputeDiskBarrierArgs(cls, vmaj, vmin, vrel, disabled_barriers,
1528 5ae4945a Iustin Pop
                              disable_meta_flush):
1529 8a69b3a8 Andrea Spadaccini
    """Compute the DRBD command line parameters for disk barriers
1530 8a69b3a8 Andrea Spadaccini

1531 8a69b3a8 Andrea Spadaccini
    Returns a list of the disk barrier parameters as requested via the
1532 8a69b3a8 Andrea Spadaccini
    disabled_barriers and disable_meta_flush arguments, and according to the
1533 8a69b3a8 Andrea Spadaccini
    supported ones in the DRBD version vmaj.vmin.vrel
1534 8a69b3a8 Andrea Spadaccini

1535 8a69b3a8 Andrea Spadaccini
    If the desired option is unsupported, raises errors.BlockDeviceError.
1536 8a69b3a8 Andrea Spadaccini

1537 8a69b3a8 Andrea Spadaccini
    """
1538 8a69b3a8 Andrea Spadaccini
    disabled_barriers_set = frozenset(disabled_barriers)
1539 8a69b3a8 Andrea Spadaccini
    if not disabled_barriers_set in constants.DRBD_VALID_BARRIER_OPT:
1540 8a69b3a8 Andrea Spadaccini
      raise errors.BlockDeviceError("%s is not a valid option set for DRBD"
1541 8a69b3a8 Andrea Spadaccini
                                    " barriers" % disabled_barriers)
1542 8a69b3a8 Andrea Spadaccini
1543 8a69b3a8 Andrea Spadaccini
    args = []
1544 8a69b3a8 Andrea Spadaccini
1545 8a69b3a8 Andrea Spadaccini
    # The following code assumes DRBD 8.x, with x < 4 and x != 1 (DRBD 8.1.x
1546 8a69b3a8 Andrea Spadaccini
    # does not exist)
1547 8a69b3a8 Andrea Spadaccini
    if not vmaj == 8 and vmin in (0, 2, 3):
1548 8a69b3a8 Andrea Spadaccini
      raise errors.BlockDeviceError("Unsupported DRBD version: %d.%d.%d" %
1549 8a69b3a8 Andrea Spadaccini
                                    (vmaj, vmin, vrel))
1550 8a69b3a8 Andrea Spadaccini
1551 8a69b3a8 Andrea Spadaccini
    def _AppendOrRaise(option, min_version):
1552 8a69b3a8 Andrea Spadaccini
      """Helper for DRBD options"""
1553 8a69b3a8 Andrea Spadaccini
      if min_version is not None and vrel >= min_version:
1554 8a69b3a8 Andrea Spadaccini
        args.append(option)
1555 8a69b3a8 Andrea Spadaccini
      else:
1556 8a69b3a8 Andrea Spadaccini
        raise errors.BlockDeviceError("Could not use the option %s as the"
1557 8a69b3a8 Andrea Spadaccini
                                      " DRBD version %d.%d.%d does not support"
1558 8a69b3a8 Andrea Spadaccini
                                      " it." % (option, vmaj, vmin, vrel))
1559 8a69b3a8 Andrea Spadaccini
1560 8a69b3a8 Andrea Spadaccini
    # the minimum version for each feature is encoded via pairs of (minor
1561 8a69b3a8 Andrea Spadaccini
    # version -> x) where x is version in which support for the option was
1562 8a69b3a8 Andrea Spadaccini
    # introduced.
1563 8a69b3a8 Andrea Spadaccini
    meta_flush_supported = disk_flush_supported = {
1564 8a69b3a8 Andrea Spadaccini
      0: 12,
1565 8a69b3a8 Andrea Spadaccini
      2: 7,
1566 8a69b3a8 Andrea Spadaccini
      3: 0,
1567 8a69b3a8 Andrea Spadaccini
      }
1568 8a69b3a8 Andrea Spadaccini
1569 8a69b3a8 Andrea Spadaccini
    disk_drain_supported = {
1570 8a69b3a8 Andrea Spadaccini
      2: 7,
1571 8a69b3a8 Andrea Spadaccini
      3: 0,
1572 8a69b3a8 Andrea Spadaccini
      }
1573 8a69b3a8 Andrea Spadaccini
1574 8a69b3a8 Andrea Spadaccini
    disk_barriers_supported = {
1575 8a69b3a8 Andrea Spadaccini
      3: 0,
1576 8a69b3a8 Andrea Spadaccini
      }
1577 8a69b3a8 Andrea Spadaccini
1578 8a69b3a8 Andrea Spadaccini
    # meta flushes
1579 8a69b3a8 Andrea Spadaccini
    if disable_meta_flush:
1580 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_META_FLUSH_OPTION,
1581 8a69b3a8 Andrea Spadaccini
                     meta_flush_supported.get(vmin, None))
1582 8a69b3a8 Andrea Spadaccini
1583 8a69b3a8 Andrea Spadaccini
    # disk flushes
1584 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set:
1585 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_FLUSH_OPTION,
1586 8a69b3a8 Andrea Spadaccini
                     disk_flush_supported.get(vmin, None))
1587 8a69b3a8 Andrea Spadaccini
1588 8a69b3a8 Andrea Spadaccini
    # disk drain
1589 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set:
1590 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_DRAIN_OPTION,
1591 8a69b3a8 Andrea Spadaccini
                     disk_drain_supported.get(vmin, None))
1592 8a69b3a8 Andrea Spadaccini
1593 8a69b3a8 Andrea Spadaccini
    # disk barriers
1594 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set:
1595 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_DISK_OPTION,
1596 8a69b3a8 Andrea Spadaccini
                     disk_barriers_supported.get(vmin, None))
1597 8a69b3a8 Andrea Spadaccini
1598 8a69b3a8 Andrea Spadaccini
    return args
1599 8a69b3a8 Andrea Spadaccini
1600 6e9814a1 Andrea Spadaccini
  def _AssembleNet(self, minor, net_info, protocol,
1601 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1602 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1603 a2cfdea2 Iustin Pop

1604 a2cfdea2 Iustin Pop
    """
1605 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1606 52857176 Iustin Pop
    if None in net_info:
1607 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1608 52857176 Iustin Pop
      # sure its shutdown
1609 6e9814a1 Andrea Spadaccini
      self._ShutdownNet(minor)
1610 1063abd1 Iustin Pop
      return
1611 52857176 Iustin Pop
1612 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1613 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1614 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1615 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1616 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1617 7d585316 Iustin Pop
    # about its peer.
1618 8584e922 Andrea Spadaccini
    sync_errors = self._SetMinorSyncParams(minor, self.params)
1619 8584e922 Andrea Spadaccini
    if sync_errors:
1620 8584e922 Andrea Spadaccini
      _ThrowError("drbd%d: can't set the synchronization parameters: %s" %
1621 8584e922 Andrea Spadaccini
                  (minor, utils.CommaJoin(sync_errors)))
1622 7d585316 Iustin Pop
1623 8b312c1d Manuel Franceschini
    if netutils.IP6Address.IsValid(lhost):
1624 8b312c1d Manuel Franceschini
      if not netutils.IP6Address.IsValid(rhost):
1625 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1626 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1627 5a672c30 Manuel Franceschini
      family = "ipv6"
1628 8b312c1d Manuel Franceschini
    elif netutils.IP4Address.IsValid(lhost):
1629 8b312c1d Manuel Franceschini
      if not netutils.IP4Address.IsValid(rhost):
1630 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1631 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1632 5a672c30 Manuel Franceschini
      family = "ipv4"
1633 5a672c30 Manuel Franceschini
    else:
1634 5a672c30 Manuel Franceschini
      _ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
1635 5a672c30 Manuel Franceschini
1636 6e9814a1 Andrea Spadaccini
    args = ["drbdsetup", self._DevPath(minor), "net",
1637 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, lhost, lport),
1638 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, rhost, rport), protocol,
1639 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1640 f38478b2 Iustin Pop
            "-B", "consensus",
1641 ab6cc81c Iustin Pop
            "--create-device",
1642 f38478b2 Iustin Pop
            ]
1643 a2cfdea2 Iustin Pop
    if dual_pri:
1644 a2cfdea2 Iustin Pop
      args.append("-m")
1645 a2cfdea2 Iustin Pop
    if hmac and secret:
1646 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1647 ad1dd4c7 Andrea Spadaccini
1648 ad1dd4c7 Andrea Spadaccini
    if self.params[constants.LDP_NET_CUSTOM]:
1649 ad1dd4c7 Andrea Spadaccini
      args.extend(shlex.split(self.params[constants.LDP_NET_CUSTOM]))
1650 ad1dd4c7 Andrea Spadaccini
1651 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1652 a2cfdea2 Iustin Pop
    if result.failed:
1653 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1654 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1655 a2cfdea2 Iustin Pop
1656 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1657 6e9814a1 Andrea Spadaccini
      info = self._GetDevInfo(self._GetShowData(minor))
1658 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1659 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1660 def8e2f6 Michael Hanselmann
1661 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1662 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1663 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1664 def8e2f6 Michael Hanselmann
1665 def8e2f6 Michael Hanselmann
    try:
1666 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1667 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1668 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1669 a2cfdea2 Iustin Pop
1670 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1671 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1672 b00b95dd Iustin Pop

1673 b00b95dd Iustin Pop
    """
1674 b00b95dd Iustin Pop
    if self.minor is None:
1675 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1676 82463074 Iustin Pop
                  self._aminor)
1677 b00b95dd Iustin Pop
    if len(devices) != 2:
1678 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1679 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1680 03ece5f3 Iustin Pop
    if "local_dev" in info:
1681 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1682 b00b95dd Iustin Pop
    backend, meta = devices
1683 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1684 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1685 b00b95dd Iustin Pop
    backend.Open()
1686 b00b95dd Iustin Pop
    meta.Open()
1687 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1688 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1689 b00b95dd Iustin Pop
1690 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1691 b00b95dd Iustin Pop
    self._children = devices
1692 b00b95dd Iustin Pop
1693 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1694 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1695 b00b95dd Iustin Pop

1696 b00b95dd Iustin Pop
    """
1697 b00b95dd Iustin Pop
    if self.minor is None:
1698 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1699 82463074 Iustin Pop
                  self._aminor)
1700 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1701 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1702 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1703 03ece5f3 Iustin Pop
      return
1704 b00b95dd Iustin Pop
    if len(self._children) != 2:
1705 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1706 82463074 Iustin Pop
                  self._children)
1707 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1708 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1709 e739bd57 Iustin Pop
      return
1710 b00b95dd Iustin Pop
    if len(devices) != 2:
1711 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1712 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1713 e739bd57 Iustin Pop
      if dev != child.dev_path:
1714 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1715 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1716 b00b95dd Iustin Pop
1717 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1718 b00b95dd Iustin Pop
    self._children = []
1719 b00b95dd Iustin Pop
1720 7d585316 Iustin Pop
  @classmethod
1721 f2f57b6e Andrea Spadaccini
  def _SetMinorSyncParams(cls, minor, params):
1722 f2f57b6e Andrea Spadaccini
    """Set the parameters of the DRBD syncer.
1723 a2cfdea2 Iustin Pop

1724 7d585316 Iustin Pop
    This is the low-level implementation.
1725 7d585316 Iustin Pop

1726 7d585316 Iustin Pop
    @type minor: int
1727 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1728 f2f57b6e Andrea Spadaccini
    @type params: dict
1729 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1730 8584e922 Andrea Spadaccini
    @rtype: list
1731 8584e922 Andrea Spadaccini
    @return: a list of error messages
1732 7d585316 Iustin Pop

1733 a2cfdea2 Iustin Pop
    """
1734 f2f57b6e Andrea Spadaccini
1735 f2f57b6e Andrea Spadaccini
    args = ["drbdsetup", cls._DevPath(minor), "syncer"]
1736 f2f57b6e Andrea Spadaccini
    if params[constants.LDP_DYNAMIC_RESYNC]:
1737 f2f57b6e Andrea Spadaccini
      version = cls._GetVersion(cls._GetProcData())
1738 f2f57b6e Andrea Spadaccini
      vmin = version["k_minor"]
1739 f2f57b6e Andrea Spadaccini
      vrel = version["k_point"]
1740 f2f57b6e Andrea Spadaccini
1741 f2f57b6e Andrea Spadaccini
      # By definition we are using 8.x, so just check the rest of the version
1742 f2f57b6e Andrea Spadaccini
      # number
1743 f2f57b6e Andrea Spadaccini
      if vmin != 3 or vrel < 9:
1744 8584e922 Andrea Spadaccini
        msg = ("The current DRBD version (8.%d.%d) does not support the "
1745 8584e922 Andrea Spadaccini
               "dynamic resync speed controller" % (vmin, vrel))
1746 8584e922 Andrea Spadaccini
        logging.error(msg)
1747 8584e922 Andrea Spadaccini
        return [msg]
1748 8584e922 Andrea Spadaccini
1749 8584e922 Andrea Spadaccini
      if params[constants.LDP_PLAN_AHEAD] == 0:
1750 8584e922 Andrea Spadaccini
        msg = ("A value of 0 for c-plan-ahead disables the dynamic sync speed"
1751 8584e922 Andrea Spadaccini
               " controller at DRBD level. If you want to disable it, please"
1752 8584e922 Andrea Spadaccini
               " set the dynamic-resync disk parameter to False.")
1753 8584e922 Andrea Spadaccini
        logging.error(msg)
1754 8584e922 Andrea Spadaccini
        return [msg]
1755 f2f57b6e Andrea Spadaccini
1756 f2f57b6e Andrea Spadaccini
      # add the c-* parameters to args
1757 8584e922 Andrea Spadaccini
      args.extend(["--c-plan-ahead", params[constants.LDP_PLAN_AHEAD],
1758 8584e922 Andrea Spadaccini
                   "--c-fill-target", params[constants.LDP_FILL_TARGET],
1759 8584e922 Andrea Spadaccini
                   "--c-delay-target", params[constants.LDP_DELAY_TARGET],
1760 8584e922 Andrea Spadaccini
                   "--c-max-rate", params[constants.LDP_MAX_RATE],
1761 8584e922 Andrea Spadaccini
                   "--c-min-rate", params[constants.LDP_MIN_RATE],
1762 5ae4945a Iustin Pop
                   ])
1763 f2f57b6e Andrea Spadaccini
1764 f2f57b6e Andrea Spadaccini
    else:
1765 f2f57b6e Andrea Spadaccini
      args.extend(["-r", "%d" % params[constants.LDP_RESYNC_RATE]])
1766 f2f57b6e Andrea Spadaccini
1767 f2f57b6e Andrea Spadaccini
    args.append("--create-device")
1768 f2f57b6e Andrea Spadaccini
    result = utils.RunCmd(args)
1769 a2cfdea2 Iustin Pop
    if result.failed:
1770 8584e922 Andrea Spadaccini
      msg = ("Can't change syncer rate: %s - %s" %
1771 8584e922 Andrea Spadaccini
             (result.fail_reason, result.output))
1772 8584e922 Andrea Spadaccini
      logging.error(msg)
1773 e0b57a5c Michael Hanselmann
      return [msg]
1774 8584e922 Andrea Spadaccini
1775 8584e922 Andrea Spadaccini
    return []
1776 7d585316 Iustin Pop
1777 f2f57b6e Andrea Spadaccini
  def SetSyncParams(self, params):
1778 f2f57b6e Andrea Spadaccini
    """Set the synchronization parameters of the DRBD syncer.
1779 7d585316 Iustin Pop

1780 f2f57b6e Andrea Spadaccini
    @type params: dict
1781 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1782 8584e922 Andrea Spadaccini
    @rtype: list
1783 8584e922 Andrea Spadaccini
    @return: a list of error messages, emitted both by the current node and by
1784 8584e922 Andrea Spadaccini
    children. An empty list means no errors
1785 7d585316 Iustin Pop

1786 7d585316 Iustin Pop
    """
1787 7d585316 Iustin Pop
    if self.minor is None:
1788 8584e922 Andrea Spadaccini
      err = "Not attached during SetSyncParams"
1789 8584e922 Andrea Spadaccini
      logging.info(err)
1790 8584e922 Andrea Spadaccini
      return [err]
1791 8584e922 Andrea Spadaccini
1792 f2f57b6e Andrea Spadaccini
    children_result = super(DRBD8, self).SetSyncParams(params)
1793 8584e922 Andrea Spadaccini
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
1794 8584e922 Andrea Spadaccini
    return children_result
1795 a2cfdea2 Iustin Pop
1796 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
1797 a3fffcc6 René Nussbaumer
    """Pauses or resumes the sync of a DRBD device.
1798 a3fffcc6 René Nussbaumer

1799 a3fffcc6 René Nussbaumer
    @param pause: Wether to pause or resume
1800 a3fffcc6 René Nussbaumer
    @return: the success of the operation
1801 a3fffcc6 René Nussbaumer

1802 a3fffcc6 René Nussbaumer
    """
1803 a3fffcc6 René Nussbaumer
    if self.minor is None:
1804 a3fffcc6 René Nussbaumer
      logging.info("Not attached during PauseSync")
1805 a3fffcc6 René Nussbaumer
      return False
1806 a3fffcc6 René Nussbaumer
1807 a3fffcc6 René Nussbaumer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
1808 a3fffcc6 René Nussbaumer
1809 a3fffcc6 René Nussbaumer
    if pause:
1810 a3fffcc6 René Nussbaumer
      cmd = "pause-sync"
1811 a3fffcc6 René Nussbaumer
    else:
1812 a3fffcc6 René Nussbaumer
      cmd = "resume-sync"
1813 a3fffcc6 René Nussbaumer
1814 a3fffcc6 René Nussbaumer
    result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
1815 a3fffcc6 René Nussbaumer
    if result.failed:
1816 a3fffcc6 René Nussbaumer
      logging.error("Can't %s: %s - %s", cmd,
1817 a3fffcc6 René Nussbaumer
                    result.fail_reason, result.output)
1818 a3fffcc6 René Nussbaumer
    return not result.failed and children_result
1819 a3fffcc6 René Nussbaumer
1820 6b90c22e Iustin Pop
  def GetProcStatus(self):
1821 6b90c22e Iustin Pop
    """Return device data from /proc.
1822 6b90c22e Iustin Pop

1823 6b90c22e Iustin Pop
    """
1824 6b90c22e Iustin Pop
    if self.minor is None:
1825 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1826 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1827 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1828 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1829 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1830 6b90c22e Iustin Pop
1831 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1832 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1833 a2cfdea2 Iustin Pop

1834 a2cfdea2 Iustin Pop

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

1839 0834c866 Iustin Pop

1840 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1841 0834c866 Iustin Pop
    network not connected or local disk missing.
1842 0834c866 Iustin Pop

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

1846 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1847 c41eea6e Iustin Pop

1848 a2cfdea2 Iustin Pop
    """
1849 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1850 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1851 96acbc09 Michael Hanselmann
1852 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1853 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1854 f208978a Michael Hanselmann
1855 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1856 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1857 f208978a Michael Hanselmann
    elif stats.is_diskless:
1858 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1859 f208978a Michael Hanselmann
    else:
1860 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1861 96acbc09 Michael Hanselmann
1862 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1863 96acbc09 Michael Hanselmann
                                  major=self.major,
1864 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1865 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1866 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1867 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1868 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1869 a2cfdea2 Iustin Pop
1870 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1871 a2cfdea2 Iustin Pop
    """Make the local state primary.
1872 a2cfdea2 Iustin Pop

1873 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1874 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1875 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1876 f860ff4e Guido Trotter
    is mandatory.
1877 a2cfdea2 Iustin Pop

1878 a2cfdea2 Iustin Pop
    """
1879 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1880 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1881 a2cfdea2 Iustin Pop
      return False
1882 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1883 a2cfdea2 Iustin Pop
    if force:
1884 a2cfdea2 Iustin Pop
      cmd.append("-o")
1885 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1886 a2cfdea2 Iustin Pop
    if result.failed:
1887 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1888 82463074 Iustin Pop
                  result.output)
1889 a2cfdea2 Iustin Pop
1890 a2cfdea2 Iustin Pop
  def Close(self):
1891 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1892 a2cfdea2 Iustin Pop

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

1895 a2cfdea2 Iustin Pop
    """
1896 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1897 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1898 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1899 a2cfdea2 Iustin Pop
    if result.failed:
1900 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1901 82463074 Iustin Pop
                  self.minor, result.output)
1902 a2cfdea2 Iustin Pop
1903 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1904 cf8df3f3 Iustin Pop
    """Removes network configuration.
1905 cf8df3f3 Iustin Pop

1906 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1907 cf8df3f3 Iustin Pop

1908 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1909 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1910 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1911 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1912 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1913 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1914 cf8df3f3 Iustin Pop
    exception.
1915 cf8df3f3 Iustin Pop

1916 cf8df3f3 Iustin Pop
    """
1917 cf8df3f3 Iustin Pop
    if self.minor is None:
1918 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1919 cf8df3f3 Iustin Pop
1920 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1921 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1922 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1923 cf8df3f3 Iustin Pop
1924 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
1925 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
1926 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
1927 cf8df3f3 Iustin Pop
1928 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
1929 def8e2f6 Michael Hanselmann
1930 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
1931 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
1932 def8e2f6 Michael Hanselmann
        return
1933 def8e2f6 Michael Hanselmann
1934 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
1935 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
1936 def8e2f6 Michael Hanselmann
      # forgotten
1937 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
1938 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
1939 def8e2f6 Michael Hanselmann
1940 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
1941 def8e2f6 Michael Hanselmann
1942 def8e2f6 Michael Hanselmann
    # Keep start time
1943 def8e2f6 Michael Hanselmann
    start_time = time.time()
1944 def8e2f6 Michael Hanselmann
1945 def8e2f6 Michael Hanselmann
    try:
1946 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
1947 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
1948 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
1949 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1950 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
1951 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1952 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1953 cf8df3f3 Iustin Pop
      else:
1954 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1955 def8e2f6 Michael Hanselmann
1956 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1957 cf8df3f3 Iustin Pop
1958 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
1959 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
1960 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1961 82463074 Iustin Pop
                   self.minor, reconfig_time)
1962 cf8df3f3 Iustin Pop
1963 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1964 cf8df3f3 Iustin Pop
    """Reconnects the network.
1965 cf8df3f3 Iustin Pop

1966 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1967 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1968 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1969 cf8df3f3 Iustin Pop

1970 cf8df3f3 Iustin Pop
    Args:
1971 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1972 cf8df3f3 Iustin Pop

1973 cf8df3f3 Iustin Pop
    """
1974 cf8df3f3 Iustin Pop
    if self.minor is None:
1975 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1976 cf8df3f3 Iustin Pop
1977 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1978 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1979 cf8df3f3 Iustin Pop
1980 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1981 cf8df3f3 Iustin Pop
1982 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1983 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1984 cf8df3f3 Iustin Pop
1985 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1986 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1987 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1988 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1989 cf8df3f3 Iustin Pop
1990 a2cfdea2 Iustin Pop
  def Attach(self):
1991 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1992 2d0c8319 Iustin Pop

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

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

2000 2d0c8319 Iustin Pop
    """
2001 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
2002 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
2003 2d0c8319 Iustin Pop
      minor = self._aminor
2004 2d0c8319 Iustin Pop
    else:
2005 2d0c8319 Iustin Pop
      minor = None
2006 2d0c8319 Iustin Pop
2007 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
2008 2d0c8319 Iustin Pop
    return minor is not None
2009 2d0c8319 Iustin Pop
2010 2d0c8319 Iustin Pop
  def Assemble(self):
2011 2d0c8319 Iustin Pop
    """Assemble the drbd.
2012 2d0c8319 Iustin Pop

2013 2d0c8319 Iustin Pop
    Method:
2014 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
2015 2d0c8319 Iustin Pop
        our config
2016 2d0c8319 Iustin Pop
      - if not, we create it from zero
2017 d529599f Andrea Spadaccini
      - anyway, set the device parameters
2018 2d0c8319 Iustin Pop

2019 2d0c8319 Iustin Pop
    """
2020 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
2021 2d0c8319 Iustin Pop
2022 2d0c8319 Iustin Pop
    self.Attach()
2023 2d0c8319 Iustin Pop
    if self.minor is None:
2024 2d0c8319 Iustin Pop
      # local device completely unconfigured
2025 1063abd1 Iustin Pop
      self._FastAssemble()
2026 2d0c8319 Iustin Pop
    else:
2027 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
2028 2d0c8319 Iustin Pop
      # the device
2029 1063abd1 Iustin Pop
      self._SlowAssemble()
2030 2d0c8319 Iustin Pop
2031 8584e922 Andrea Spadaccini
    sync_errors = self.SetSyncParams(self.params)
2032 8584e922 Andrea Spadaccini
    if sync_errors:
2033 8584e922 Andrea Spadaccini
      _ThrowError("drbd%d: can't set the synchronization parameters: %s" %
2034 8584e922 Andrea Spadaccini
                  (self.minor, utils.CommaJoin(sync_errors)))
2035 d529599f Andrea Spadaccini
2036 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
2037 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
2038 a2cfdea2 Iustin Pop

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

2043 a2cfdea2 Iustin Pop
    """
2044 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
2045 b459a848 Andrea Spadaccini
    # pylint: disable=W0631
2046 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
2047 a1578d63 Iustin Pop
    for minor in (self._aminor,):
2048 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
2049 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
2050 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
2051 1063abd1 Iustin Pop
2052 a2cfdea2 Iustin Pop
      if match_l and match_r:
2053 1063abd1 Iustin Pop
        # everything matches
2054 a2cfdea2 Iustin Pop
        break
2055 1063abd1 Iustin Pop
2056 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
2057 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
2058 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
2059 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2060 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2061 1063abd1 Iustin Pop
          break
2062 1063abd1 Iustin Pop
        else:
2063 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
2064 1063abd1 Iustin Pop
                      " show' disagrees", minor)
2065 1063abd1 Iustin Pop
2066 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
2067 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
2068 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
2069 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
2070 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2071 1063abd1 Iustin Pop
          break
2072 1063abd1 Iustin Pop
        else:
2073 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
2074 1063abd1 Iustin Pop
                      " show' disagrees", minor)
2075 bf25af3b Iustin Pop
2076 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
2077 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
2078 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
2079 bf25af3b Iustin Pop
      # configuration
2080 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
2081 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
2082 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
2083 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
2084 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
2085 9cdbe77f Iustin Pop
        # reconnect to our correct one
2086 1063abd1 Iustin Pop
        try:
2087 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
2088 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
2089 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
2090 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
2091 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
2092 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
2093 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
2094 9cdbe77f Iustin Pop
        # None)
2095 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
2096 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2097 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2098 9cdbe77f Iustin Pop
          break
2099 1063abd1 Iustin Pop
        else:
2100 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
2101 1063abd1 Iustin Pop
                      " show' disagrees", minor)
2102 9cdbe77f Iustin Pop
2103 a2cfdea2 Iustin Pop
    else:
2104 a2cfdea2 Iustin Pop
      minor = None
2105 a2cfdea2 Iustin Pop
2106 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2107 1063abd1 Iustin Pop
    if minor is None:
2108 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
2109 1063abd1 Iustin Pop
                  self._aminor)
2110 a2cfdea2 Iustin Pop
2111 2d0c8319 Iustin Pop
  def _FastAssemble(self):
2112 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
2113 a2cfdea2 Iustin Pop

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

2116 a2cfdea2 Iustin Pop
    """
2117 a1578d63 Iustin Pop
    minor = self._aminor
2118 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
2119 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
2120 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
2121 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
2122 1063abd1 Iustin Pop
      self._AssembleNet(minor,
2123 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
2124 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
2125 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2126 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2127 a2cfdea2 Iustin Pop
2128 a2cfdea2 Iustin Pop
  @classmethod
2129 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
2130 b00b95dd Iustin Pop
    """Detach from the local device.
2131 b00b95dd Iustin Pop

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

2135 b00b95dd Iustin Pop
    """
2136 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2137 b00b95dd Iustin Pop
    if result.failed:
2138 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
2139 b00b95dd Iustin Pop
2140 b00b95dd Iustin Pop
  @classmethod
2141 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2142 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2143 f3e513ad Iustin Pop

2144 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2145 f3e513ad Iustin Pop

2146 f3e513ad Iustin Pop
    """
2147 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2148 a8459f1c Iustin Pop
    if result.failed:
2149 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
2150 f3e513ad Iustin Pop
2151 f3e513ad Iustin Pop
  @classmethod
2152 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2153 a2cfdea2 Iustin Pop
    """Deactivate the device.
2154 a2cfdea2 Iustin Pop

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

2157 a2cfdea2 Iustin Pop
    """
2158 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2159 a2cfdea2 Iustin Pop
    if result.failed:
2160 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
2161 33bc6f01 Iustin Pop
                  minor, result.output)
2162 a2cfdea2 Iustin Pop
2163 a2cfdea2 Iustin Pop
  def Shutdown(self):
2164 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2165 a2cfdea2 Iustin Pop

2166 a2cfdea2 Iustin Pop
    """
2167 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2168 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
2169 746f7476 Iustin Pop
      return
2170 746f7476 Iustin Pop
    minor = self.minor
2171 a2cfdea2 Iustin Pop
    self.minor = None
2172 a2cfdea2 Iustin Pop
    self.dev_path = None
2173 746f7476 Iustin Pop
    self._ShutdownAll(minor)
2174 a2cfdea2 Iustin Pop
2175 a2cfdea2 Iustin Pop
  def Remove(self):
2176 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2177 a2cfdea2 Iustin Pop

2178 a2cfdea2 Iustin Pop
    """
2179 0c6c04ec Iustin Pop
    self.Shutdown()
2180 a2cfdea2 Iustin Pop
2181 a2cfdea2 Iustin Pop
  @classmethod
2182 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2183 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2184 a2cfdea2 Iustin Pop

2185 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2186 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2187 a2cfdea2 Iustin Pop

2188 a2cfdea2 Iustin Pop
    """
2189 a2cfdea2 Iustin Pop
    if len(children) != 2:
2190 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2191 767d52d3 Iustin Pop
    # check that the minor is unused
2192 767d52d3 Iustin Pop
    aminor = unique_id[4]
2193 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
2194 767d52d3 Iustin Pop
    if aminor in proc_info:
2195 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
2196 767d52d3 Iustin Pop
      in_use = status.is_in_use
2197 767d52d3 Iustin Pop
    else:
2198 767d52d3 Iustin Pop
      in_use = False
2199 767d52d3 Iustin Pop
    if in_use:
2200 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
2201 a2cfdea2 Iustin Pop
    meta = children[1]
2202 a2cfdea2 Iustin Pop
    meta.Assemble()
2203 a2cfdea2 Iustin Pop
    if not meta.Attach():
2204 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
2205 33bc6f01 Iustin Pop
                  aminor, meta)
2206 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
2207 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
2208 94dcbdb0 Andrea Spadaccini
    return cls(unique_id, children, size, params)
2209 a2cfdea2 Iustin Pop
2210 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2211 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
2212 1005d816 Iustin Pop

2213 1005d816 Iustin Pop
    """
2214 1005d816 Iustin Pop
    if self.minor is None:
2215 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
2216 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
2217 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
2218 cad0723b Iustin Pop
    self._children[0].Grow(amount, dryrun, backingstore)
2219 cad0723b Iustin Pop
    if dryrun or backingstore:
2220 cad0723b Iustin Pop
      # DRBD does not support dry-run mode and is not backing storage,
2221 cad0723b Iustin Pop
      # so we'll return here
2222 7fe23d47 Iustin Pop
      return
2223 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
2224 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
2225 1005d816 Iustin Pop
    if result.failed:
2226 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
2227 1005d816 Iustin Pop
2228 a8083063 Iustin Pop
2229 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
2230 6f695a2e Manuel Franceschini
  """File device.
2231 abdf0113 Iustin Pop

2232 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
2233 6f695a2e Manuel Franceschini

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

2236 6f695a2e Manuel Franceschini
  """
2237 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2238 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
2239 6f695a2e Manuel Franceschini

2240 6f695a2e Manuel Franceschini
    """
2241 6f695a2e Manuel Franceschini
    if children:
2242 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
2243 94dcbdb0 Andrea Spadaccini
    super(FileStorage, self).__init__(unique_id, children, size, params)
2244 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2245 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2246 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
2247 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
2248 5e09a309 Michael Hanselmann
2249 5e09a309 Michael Hanselmann
    CheckFileStoragePath(self.dev_path)
2250 5e09a309 Michael Hanselmann
2251 ecb091e3 Iustin Pop
    self.Attach()
2252 6f695a2e Manuel Franceschini
2253 6f695a2e Manuel Franceschini
  def Assemble(self):
2254 6f695a2e Manuel Franceschini
    """Assemble the device.
2255 6f695a2e Manuel Franceschini

2256 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
2257 6f695a2e Manuel Franceschini

2258 6f695a2e Manuel Franceschini
    """
2259 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
2260 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
2261 6f695a2e Manuel Franceschini
2262 6f695a2e Manuel Franceschini
  def Shutdown(self):
2263 6f695a2e Manuel Franceschini
    """Shutdown the device.
2264 6f695a2e Manuel Franceschini

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

2268 6f695a2e Manuel Franceschini
    """
2269 746f7476 Iustin Pop
    pass
2270 6f695a2e Manuel Franceschini
2271 6f695a2e Manuel Franceschini
  def Open(self, force=False):
2272 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
2273 6f695a2e Manuel Franceschini

2274 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2275 6f695a2e Manuel Franceschini

2276 6f695a2e Manuel Franceschini
    """
2277 6f695a2e Manuel Franceschini
    pass
2278 6f695a2e Manuel Franceschini
2279 6f695a2e Manuel Franceschini
  def Close(self):
2280 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
2281 6f695a2e Manuel Franceschini

2282 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2283 6f695a2e Manuel Franceschini

2284 6f695a2e Manuel Franceschini
    """
2285 6f695a2e Manuel Franceschini
    pass
2286 6f695a2e Manuel Franceschini
2287 6f695a2e Manuel Franceschini
  def Remove(self):
2288 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
2289 6f695a2e Manuel Franceschini

2290 c41eea6e Iustin Pop
    @rtype: boolean
2291 c41eea6e Iustin Pop
    @return: True if the removal was successful
2292 6f695a2e Manuel Franceschini

2293 6f695a2e Manuel Franceschini
    """
2294 6f695a2e Manuel Franceschini
    try:
2295 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
2296 6f695a2e Manuel Franceschini
    except OSError, err:
2297 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
2298 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
2299 6f695a2e Manuel Franceschini
2300 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
2301 bbe4cc16 Iustin Pop
    """Renames the file.
2302 bbe4cc16 Iustin Pop

2303 bbe4cc16 Iustin Pop
    """
2304 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
2305 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
2306 bbe4cc16 Iustin Pop
2307 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2308 bbe4cc16 Iustin Pop
    """Grow the file
2309 bbe4cc16 Iustin Pop

2310 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2311 bbe4cc16 Iustin Pop

2312 bbe4cc16 Iustin Pop
    """
2313 cad0723b Iustin Pop
    if not backingstore:
2314 cad0723b Iustin Pop
      return
2315 91e2d9ec Guido Trotter
    # Check that the file exists
2316 91e2d9ec Guido Trotter
    self.Assemble()
2317 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
2318 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
2319 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
2320 7fe23d47 Iustin Pop
    # We can't really simulate the growth
2321 7fe23d47 Iustin Pop
    if dryrun:
2322 7fe23d47 Iustin Pop
      return
2323 91e2d9ec Guido Trotter
    try:
2324 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
2325 91e2d9ec Guido Trotter
      f.truncate(new_size)
2326 91e2d9ec Guido Trotter
      f.close()
2327 91e2d9ec Guido Trotter
    except EnvironmentError, err:
2328 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
2329 bbe4cc16 Iustin Pop
2330 6f695a2e Manuel Franceschini
  def Attach(self):
2331 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2332 6f695a2e Manuel Franceschini

2333 6f695a2e Manuel Franceschini
    Check if this file already exists.
2334 6f695a2e Manuel Franceschini

2335 c41eea6e Iustin Pop
    @rtype: boolean
2336 c41eea6e Iustin Pop
    @return: True if file exists
2337 6f695a2e Manuel Franceschini

2338 6f695a2e Manuel Franceschini
    """
2339 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
2340 ecb091e3 Iustin Pop
    return self.attached
2341 6f695a2e Manuel Franceschini
2342 fcff3897 Iustin Pop
  def GetActualSize(self):
2343 fcff3897 Iustin Pop
    """Return the actual disk size.
2344 fcff3897 Iustin Pop

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

2347 fcff3897 Iustin Pop
    """
2348 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2349 fcff3897 Iustin Pop
    try:
2350 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2351 fcff3897 Iustin Pop
      return st.st_size
2352 fcff3897 Iustin Pop
    except OSError, err:
2353 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2354 fcff3897 Iustin Pop
2355 6f695a2e Manuel Franceschini
  @classmethod
2356 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2357 6f695a2e Manuel Franceschini
    """Create a new file.
2358 6f695a2e Manuel Franceschini

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

2361 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2362 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2363 6f695a2e Manuel Franceschini

2364 6f695a2e Manuel Franceschini
    """
2365 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2366 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2367 5e09a309 Michael Hanselmann
2368 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2369 5e09a309 Michael Hanselmann
2370 5e09a309 Michael Hanselmann
    CheckFileStoragePath(dev_path)
2371 5e09a309 Michael Hanselmann
2372 6f695a2e Manuel Franceschini
    try:
2373 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
2374 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
2375 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2376 6f695a2e Manuel Franceschini
      f.close()
2377 cdeefd9b Guido Trotter
    except EnvironmentError, err:
2378 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
2379 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
2380 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
2381 6f695a2e Manuel Franceschini
2382 94dcbdb0 Andrea Spadaccini
    return FileStorage(unique_id, children, size, params)
2383 6f695a2e Manuel Franceschini
2384 6f695a2e Manuel Franceschini
2385 b6135bbc Apollon Oikonomopoulos
class PersistentBlockDevice(BlockDev):
2386 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
2387 b6135bbc Apollon Oikonomopoulos

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

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

2394 b6135bbc Apollon Oikonomopoulos
  """
2395 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2396 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
2397 b6135bbc Apollon Oikonomopoulos

2398 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2399 b6135bbc Apollon Oikonomopoulos

2400 b6135bbc Apollon Oikonomopoulos
    """
2401 94dcbdb0 Andrea Spadaccini
    super(PersistentBlockDevice, self).__init__(unique_id, children, size,
2402 94dcbdb0 Andrea Spadaccini
                                                params)
2403 b6135bbc Apollon Oikonomopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2404 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2405 b6135bbc Apollon Oikonomopoulos
    self.dev_path = unique_id[1]
2406 d0c8c01d Iustin Pop
    if not os.path.realpath(self.dev_path).startswith("/dev/"):
2407 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Full path '%s' lies outside /dev" %
2408 b6135bbc Apollon Oikonomopoulos
                              os.path.realpath(self.dev_path))
2409 b6135bbc Apollon Oikonomopoulos
    # TODO: this is just a safety guard checking that we only deal with devices
2410 b6135bbc Apollon Oikonomopoulos
    # we know how to handle. In the future this will be integrated with
2411 b6135bbc Apollon Oikonomopoulos
    # external storage backends and possible values will probably be collected
2412 b6135bbc Apollon Oikonomopoulos
    # from the cluster configuration.
2413 b6135bbc Apollon Oikonomopoulos
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
2414 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Got persistent block device of invalid type: %s" %
2415 b6135bbc Apollon Oikonomopoulos
                       unique_id[0])
2416 b6135bbc Apollon Oikonomopoulos
2417 b6135bbc Apollon Oikonomopoulos
    self.major = self.minor = None
2418 b6135bbc Apollon Oikonomopoulos
    self.Attach()
2419 b6135bbc Apollon Oikonomopoulos
2420 b6135bbc Apollon Oikonomopoulos
  @classmethod
2421 94dcbdb0 Andrea Spadaccini
  def Create(cls, unique_id, children, size, params):
2422 b6135bbc Apollon Oikonomopoulos
    """Create a new device
2423 b6135bbc Apollon Oikonomopoulos

2424 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2425 b6135bbc Apollon Oikonomopoulos

2426 b6135bbc Apollon Oikonomopoulos
    """
2427 94dcbdb0 Andrea Spadaccini
    return PersistentBlockDevice(unique_id, children, 0, params)
2428 b6135bbc Apollon Oikonomopoulos
2429 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
2430 b6135bbc Apollon Oikonomopoulos
    """Remove a device
2431 b6135bbc Apollon Oikonomopoulos

2432 b6135bbc Apollon Oikonomopoulos
    This is a noop
2433 b6135bbc Apollon Oikonomopoulos

2434 b6135bbc Apollon Oikonomopoulos
    """
2435 b6135bbc Apollon Oikonomopoulos
    pass
2436 b6135bbc Apollon Oikonomopoulos
2437 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
2438 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
2439 b6135bbc Apollon Oikonomopoulos

2440 b6135bbc Apollon Oikonomopoulos
    """
2441 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Rename is not supported for PersistentBlockDev storage")
2442 b6135bbc Apollon Oikonomopoulos
2443 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
2444 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
2445 b6135bbc Apollon Oikonomopoulos

2446 b6135bbc Apollon Oikonomopoulos

2447 b6135bbc Apollon Oikonomopoulos
    """
2448 b6135bbc Apollon Oikonomopoulos
    self.attached = False
2449 b6135bbc Apollon Oikonomopoulos
    try:
2450 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
2451 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
2452 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2453 b6135bbc Apollon Oikonomopoulos
      return False
2454 b6135bbc Apollon Oikonomopoulos
2455 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
2456 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
2457 b6135bbc Apollon Oikonomopoulos
      return False
2458 b6135bbc Apollon Oikonomopoulos
2459 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
2460 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
2461 b6135bbc Apollon Oikonomopoulos
    self.attached = True
2462 b6135bbc Apollon Oikonomopoulos
2463 b6135bbc Apollon Oikonomopoulos
    return True
2464 b6135bbc Apollon Oikonomopoulos
2465 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
2466 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
2467 b6135bbc Apollon Oikonomopoulos

2468 b6135bbc Apollon Oikonomopoulos
    """
2469 b6135bbc Apollon Oikonomopoulos
    pass
2470 b6135bbc Apollon Oikonomopoulos
2471 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
2472 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
2473 b6135bbc Apollon Oikonomopoulos

2474 b6135bbc Apollon Oikonomopoulos
    """
2475 b6135bbc Apollon Oikonomopoulos
    pass
2476 b6135bbc Apollon Oikonomopoulos
2477 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
2478 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
2479 b6135bbc Apollon Oikonomopoulos

2480 b6135bbc Apollon Oikonomopoulos
    """
2481 b6135bbc Apollon Oikonomopoulos
    pass
2482 b6135bbc Apollon Oikonomopoulos
2483 b6135bbc Apollon Oikonomopoulos
  def Close(self):
2484 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
2485 b6135bbc Apollon Oikonomopoulos

2486 b6135bbc Apollon Oikonomopoulos
    """
2487 b6135bbc Apollon Oikonomopoulos
    pass
2488 b6135bbc Apollon Oikonomopoulos
2489 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2490 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
2491 b6135bbc Apollon Oikonomopoulos

2492 b6135bbc Apollon Oikonomopoulos
    """
2493 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2494 b6135bbc Apollon Oikonomopoulos
2495 b6135bbc Apollon Oikonomopoulos
2496 7181fba0 Constantinos Venetsanopoulos
class RADOSBlockDevice(BlockDev):
2497 7181fba0 Constantinos Venetsanopoulos
  """A RADOS Block Device (rbd).
2498 7181fba0 Constantinos Venetsanopoulos

2499 7181fba0 Constantinos Venetsanopoulos
  This class implements the RADOS Block Device for the backend. You need
2500 7181fba0 Constantinos Venetsanopoulos
  the rbd kernel driver, the RADOS Tools and a working RADOS cluster for
2501 7181fba0 Constantinos Venetsanopoulos
  this to be functional.
2502 7181fba0 Constantinos Venetsanopoulos

2503 7181fba0 Constantinos Venetsanopoulos
  """
2504 7181fba0 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
2505 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
2506 7181fba0 Constantinos Venetsanopoulos

2507 7181fba0 Constantinos Venetsanopoulos
    """
2508 7181fba0 Constantinos Venetsanopoulos
    super(RADOSBlockDevice, self).__init__(unique_id, children, size, params)
2509 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2510 7181fba0 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2511 7181fba0 Constantinos Venetsanopoulos
2512 7181fba0 Constantinos Venetsanopoulos
    self.driver, self.rbd_name = unique_id
2513 7181fba0 Constantinos Venetsanopoulos
2514 7181fba0 Constantinos Venetsanopoulos
    self.major = self.minor = None
2515 7181fba0 Constantinos Venetsanopoulos
    self.Attach()
2516 7181fba0 Constantinos Venetsanopoulos
2517 7181fba0 Constantinos Venetsanopoulos
  @classmethod
2518 7181fba0 Constantinos Venetsanopoulos
  def Create(cls, unique_id, children, size, params):
2519 7181fba0 Constantinos Venetsanopoulos
    """Create a new rbd device.
2520 7181fba0 Constantinos Venetsanopoulos

2521 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
2522 7181fba0 Constantinos Venetsanopoulos

2523 7181fba0 Constantinos Venetsanopoulos
    """
2524 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2525 7181fba0 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
2526 7181fba0 Constantinos Venetsanopoulos
                                   str(unique_id))
2527 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = params[constants.LDP_POOL]
2528 7181fba0 Constantinos Venetsanopoulos
    rbd_name = unique_id[1]
2529 7181fba0 Constantinos Venetsanopoulos
2530 7181fba0 Constantinos Venetsanopoulos
    # Provision a new rbd volume (Image) inside the RADOS cluster.
2531 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "create", "-p", rbd_pool,
2532 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % size]
2533 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2534 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2535 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd creation failed (%s): %s",
2536 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2537 7181fba0 Constantinos Venetsanopoulos
2538 7181fba0 Constantinos Venetsanopoulos
    return RADOSBlockDevice(unique_id, children, size, params)
2539 7181fba0 Constantinos Venetsanopoulos
2540 7181fba0 Constantinos Venetsanopoulos
  def Remove(self):
2541 7181fba0 Constantinos Venetsanopoulos
    """Remove the rbd device.
2542 7181fba0 Constantinos Venetsanopoulos

2543 7181fba0 Constantinos Venetsanopoulos
    """
2544 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
2545 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
2546 7181fba0 Constantinos Venetsanopoulos
2547 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2548 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
2549 7181fba0 Constantinos Venetsanopoulos
      return
2550 7181fba0 Constantinos Venetsanopoulos
2551 7181fba0 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
2552 7181fba0 Constantinos Venetsanopoulos
    self.Shutdown()
2553 7181fba0 Constantinos Venetsanopoulos
2554 7181fba0 Constantinos Venetsanopoulos
    # Remove the actual Volume (Image) from the RADOS cluster.
2555 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "rm", "-p", rbd_pool, rbd_name]
2556 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2557 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2558 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Can't remove Volume from cluster with rbd rm: %s - %s",
2559 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2560 7181fba0 Constantinos Venetsanopoulos
2561 7181fba0 Constantinos Venetsanopoulos
  def Rename(self, new_id):
2562 7181fba0 Constantinos Venetsanopoulos
    """Rename this device.
2563 7181fba0 Constantinos Venetsanopoulos

2564 7181fba0 Constantinos Venetsanopoulos
    """
2565 7181fba0 Constantinos Venetsanopoulos
    pass
2566 7181fba0 Constantinos Venetsanopoulos
2567 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
2568 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
2569 7181fba0 Constantinos Venetsanopoulos

2570 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
2571 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
2572 7181fba0 Constantinos Venetsanopoulos

2573 7181fba0 Constantinos Venetsanopoulos
    """
2574 7181fba0 Constantinos Venetsanopoulos
    self.attached = False
2575 7181fba0 Constantinos Venetsanopoulos
2576 7181fba0 Constantinos Venetsanopoulos
    # Map the rbd volume to a block device under /dev
2577 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = self._MapVolumeToBlockdev(self.unique_id)
2578 7181fba0 Constantinos Venetsanopoulos
2579 7181fba0 Constantinos Venetsanopoulos
    try:
2580 7181fba0 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
2581 7181fba0 Constantinos Venetsanopoulos
    except OSError, err:
2582 7181fba0 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2583 7181fba0 Constantinos Venetsanopoulos
      return False
2584 7181fba0 Constantinos Venetsanopoulos
2585 7181fba0 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
2586 7181fba0 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
2587 7181fba0 Constantinos Venetsanopoulos
      return False
2588 7181fba0 Constantinos Venetsanopoulos
2589 7181fba0 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
2590 7181fba0 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
2591 7181fba0 Constantinos Venetsanopoulos
    self.attached = True
2592 7181fba0 Constantinos Venetsanopoulos
2593 7181fba0 Constantinos Venetsanopoulos
    return True
2594 7181fba0 Constantinos Venetsanopoulos
2595 7181fba0 Constantinos Venetsanopoulos
  def _MapVolumeToBlockdev(self, unique_id):
2596 7181fba0 Constantinos Venetsanopoulos
    """Maps existing rbd volumes to block devices.
2597 7181fba0 Constantinos Venetsanopoulos

2598 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
2599 7181fba0 Constantinos Venetsanopoulos

2600 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
2601 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
2602 7181fba0 Constantinos Venetsanopoulos

2603 7181fba0 Constantinos Venetsanopoulos
    """
2604 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2605 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2606 7181fba0 Constantinos Venetsanopoulos
2607 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2608 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2609 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2610 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2611 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped failed (%s): %s",
2612 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2613 7181fba0 Constantinos Venetsanopoulos
2614 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2615 7181fba0 Constantinos Venetsanopoulos
2616 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2617 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Return it.
2618 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2619 7181fba0 Constantinos Venetsanopoulos
2620 7181fba0 Constantinos Venetsanopoulos
    # The mapping doesn't exist. Create it.
2621 7181fba0 Constantinos Venetsanopoulos
    map_cmd = [constants.RBD_CMD, "map", "-p", pool, name]
2622 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(map_cmd)
2623 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2624 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map failed (%s): %s",
2625 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2626 7181fba0 Constantinos Venetsanopoulos
2627 7181fba0 Constantinos Venetsanopoulos
    # Find the corresponding rbd device.
2628 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2629 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2630 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2631 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map succeeded, but showmapped failed (%s): %s",
2632 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2633 7181fba0 Constantinos Venetsanopoulos
2634 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2635 7181fba0 Constantinos Venetsanopoulos
2636 7181fba0 Constantinos Venetsanopoulos
    if not rbd_dev:
2637 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map succeeded, but could not find the rbd block"
2638 7181fba0 Constantinos Venetsanopoulos
                  " device in output of showmapped, for volume: %s", name)
2639 7181fba0 Constantinos Venetsanopoulos
2640 7181fba0 Constantinos Venetsanopoulos
    # The device was successfully mapped. Return it.
2641 7181fba0 Constantinos Venetsanopoulos
    return rbd_dev
2642 7181fba0 Constantinos Venetsanopoulos
2643 7181fba0 Constantinos Venetsanopoulos
  @staticmethod
2644 7181fba0 Constantinos Venetsanopoulos
  def _ParseRbdShowmappedOutput(output, volume_name):
2645 7181fba0 Constantinos Venetsanopoulos
    """Parse the output of `rbd showmapped'.
2646 7181fba0 Constantinos Venetsanopoulos

2647 7181fba0 Constantinos Venetsanopoulos
    This method parses the output of `rbd showmapped' and returns
2648 7181fba0 Constantinos Venetsanopoulos
    the rbd block device path (e.g. /dev/rbd0) that matches the
2649 7181fba0 Constantinos Venetsanopoulos
    given rbd volume.
2650 7181fba0 Constantinos Venetsanopoulos

2651 7181fba0 Constantinos Venetsanopoulos
    @type output: string
2652 7181fba0 Constantinos Venetsanopoulos
    @param output: the whole output of `rbd showmapped'
2653 7181fba0 Constantinos Venetsanopoulos
    @type volume_name: string
2654 7181fba0 Constantinos Venetsanopoulos
    @param volume_name: the name of the volume whose device we search for
2655 7181fba0 Constantinos Venetsanopoulos
    @rtype: string or None
2656 7181fba0 Constantinos Venetsanopoulos
    @return: block device path if the volume is mapped, else None
2657 7181fba0 Constantinos Venetsanopoulos

2658 7181fba0 Constantinos Venetsanopoulos
    """
2659 7181fba0 Constantinos Venetsanopoulos
    allfields = 5
2660 7181fba0 Constantinos Venetsanopoulos
    volumefield = 2
2661 7181fba0 Constantinos Venetsanopoulos
    devicefield = 4
2662 7181fba0 Constantinos Venetsanopoulos
2663 7181fba0 Constantinos Venetsanopoulos
    field_sep = "\t"
2664 7181fba0 Constantinos Venetsanopoulos
2665 7181fba0 Constantinos Venetsanopoulos
    lines = output.splitlines()
2666 7181fba0 Constantinos Venetsanopoulos
    splitted_lines = map(lambda l: l.split(field_sep), lines)
2667 7181fba0 Constantinos Venetsanopoulos
2668 7181fba0 Constantinos Venetsanopoulos
    # Check empty output.
2669 7181fba0 Constantinos Venetsanopoulos
    if not splitted_lines:
2670 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped returned empty output")
2671 7181fba0 Constantinos Venetsanopoulos
2672 7181fba0 Constantinos Venetsanopoulos
    # Check showmapped header line, to determine number of fields.
2673 7181fba0 Constantinos Venetsanopoulos
    field_cnt = len(splitted_lines[0])
2674 7181fba0 Constantinos Venetsanopoulos
    if field_cnt != allfields:
2675 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Cannot parse rbd showmapped output because its format"
2676 7181fba0 Constantinos Venetsanopoulos
                  " seems to have changed; expected %s fields, found %s",
2677 7181fba0 Constantinos Venetsanopoulos
                  allfields, field_cnt)
2678 7181fba0 Constantinos Venetsanopoulos
2679 7181fba0 Constantinos Venetsanopoulos
    matched_lines = \
2680 7181fba0 Constantinos Venetsanopoulos
      filter(lambda l: len(l) == allfields and l[volumefield] == volume_name,
2681 7181fba0 Constantinos Venetsanopoulos
             splitted_lines)
2682 7181fba0 Constantinos Venetsanopoulos
2683 7181fba0 Constantinos Venetsanopoulos
    if len(matched_lines) > 1:
2684 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("The rbd volume %s is mapped more than once."
2685 7181fba0 Constantinos Venetsanopoulos
                  " This shouldn't happen, try to unmap the extra"
2686 7181fba0 Constantinos Venetsanopoulos
                  " devices manually.", volume_name)
2687 7181fba0 Constantinos Venetsanopoulos
2688 7181fba0 Constantinos Venetsanopoulos
    if matched_lines:
2689 7181fba0 Constantinos Venetsanopoulos
      # rbd block device found. Return it.
2690 7181fba0 Constantinos Venetsanopoulos
      rbd_dev = matched_lines[0][devicefield]
2691 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2692 7181fba0 Constantinos Venetsanopoulos
2693 7181fba0 Constantinos Venetsanopoulos
    # The given volume is not mapped.
2694 7181fba0 Constantinos Venetsanopoulos
    return None
2695 7181fba0 Constantinos Venetsanopoulos
2696 7181fba0 Constantinos Venetsanopoulos
  def Assemble(self):
2697 7181fba0 Constantinos Venetsanopoulos
    """Assemble the device.
2698 7181fba0 Constantinos Venetsanopoulos

2699 7181fba0 Constantinos Venetsanopoulos
    """
2700 7181fba0 Constantinos Venetsanopoulos
    pass
2701 7181fba0 Constantinos Venetsanopoulos
2702 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
2703 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
2704 7181fba0 Constantinos Venetsanopoulos

2705 7181fba0 Constantinos Venetsanopoulos
    """
2706 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2707 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
2708 7181fba0 Constantinos Venetsanopoulos
      return
2709 7181fba0 Constantinos Venetsanopoulos
2710 7181fba0 Constantinos Venetsanopoulos
    # Unmap the block device from the Volume.
2711 7181fba0 Constantinos Venetsanopoulos
    self._UnmapVolumeFromBlockdev(self.unique_id)
2712 7181fba0 Constantinos Venetsanopoulos
2713 7181fba0 Constantinos Venetsanopoulos
    self.minor = None
2714 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = None
2715 7181fba0 Constantinos Venetsanopoulos
2716 7181fba0 Constantinos Venetsanopoulos
  def _UnmapVolumeFromBlockdev(self, unique_id):
2717 7181fba0 Constantinos Venetsanopoulos
    """Unmaps the rbd device from the Volume it is mapped.
2718 7181fba0 Constantinos Venetsanopoulos

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

2722 7181fba0 Constantinos Venetsanopoulos
    """
2723 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2724 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2725 7181fba0 Constantinos Venetsanopoulos
2726 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2727 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2728 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2729 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2730 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped failed [during unmap](%s): %s",
2731 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2732 7181fba0 Constantinos Venetsanopoulos
2733 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2734 7181fba0 Constantinos Venetsanopoulos
2735 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2736 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Unmap the rbd device.
2737 7181fba0 Constantinos Venetsanopoulos
      unmap_cmd = [constants.RBD_CMD, "unmap", "%s" % rbd_dev]
2738 7181fba0 Constantinos Venetsanopoulos
      result = utils.RunCmd(unmap_cmd)
2739 7181fba0 Constantinos Venetsanopoulos
      if result.failed:
2740 7181fba0 Constantinos Venetsanopoulos
        _ThrowError("rbd unmap failed (%s): %s",
2741 7181fba0 Constantinos Venetsanopoulos
                    result.fail_reason, result.output)
2742 7181fba0 Constantinos Venetsanopoulos
2743 7181fba0 Constantinos Venetsanopoulos
  def Open(self, force=False):
2744 7181fba0 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
2745 7181fba0 Constantinos Venetsanopoulos

2746 7181fba0 Constantinos Venetsanopoulos
    """
2747 7181fba0 Constantinos Venetsanopoulos
    pass
2748 7181fba0 Constantinos Venetsanopoulos
2749 7181fba0 Constantinos Venetsanopoulos
  def Close(self):
2750 7181fba0 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
2751 7181fba0 Constantinos Venetsanopoulos

2752 7181fba0 Constantinos Venetsanopoulos
    """
2753 7181fba0 Constantinos Venetsanopoulos
    pass
2754 7181fba0 Constantinos Venetsanopoulos
2755 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2756 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
2757 7181fba0 Constantinos Venetsanopoulos

2758 7181fba0 Constantinos Venetsanopoulos
    @type amount: integer
2759 7181fba0 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
2760 7181fba0 Constantinos Venetsanopoulos
    @type dryrun: boolean
2761 7181fba0 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
2762 7181fba0 Constantinos Venetsanopoulos
        only, without actually increasing the size
2763 7181fba0 Constantinos Venetsanopoulos

2764 7181fba0 Constantinos Venetsanopoulos
    """
2765 cad0723b Iustin Pop
    if not backingstore:
2766 cad0723b Iustin Pop
      return
2767 7181fba0 Constantinos Venetsanopoulos
    if not self.Attach():
2768 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Can't attach to rbd device during Grow()")
2769 7181fba0 Constantinos Venetsanopoulos
2770 7181fba0 Constantinos Venetsanopoulos
    if dryrun:
2771 7181fba0 Constantinos Venetsanopoulos
      # the rbd tool does not support dry runs of resize operations.
2772 7181fba0 Constantinos Venetsanopoulos
      # Since rbd volumes are thinly provisioned, we assume
2773 7181fba0 Constantinos Venetsanopoulos
      # there is always enough free space for the operation.
2774 7181fba0 Constantinos Venetsanopoulos
      return
2775 7181fba0 Constantinos Venetsanopoulos
2776 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
2777 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
2778 7181fba0 Constantinos Venetsanopoulos
    new_size = self.size + amount
2779 7181fba0 Constantinos Venetsanopoulos
2780 7181fba0 Constantinos Venetsanopoulos
    # Resize the rbd volume (Image) inside the RADOS cluster.
2781 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "resize", "-p", rbd_pool,
2782 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % new_size]
2783 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2784 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2785 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd resize failed (%s): %s",
2786 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2787 7181fba0 Constantinos Venetsanopoulos
2788 7181fba0 Constantinos Venetsanopoulos
2789 a8083063 Iustin Pop
DEV_MAP = {
2790 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2791 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2792 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
2793 7181fba0 Constantinos Venetsanopoulos
  constants.LD_RBD: RADOSBlockDevice,
2794 a8083063 Iustin Pop
  }
2795 a8083063 Iustin Pop
2796 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
2797 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
2798 cb7c0198 Iustin Pop
2799 a8083063 Iustin Pop
2800 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
2801 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
2802 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2803 94dcbdb0 Andrea Spadaccini
2804 94dcbdb0 Andrea Spadaccini
2805 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
2806 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
2807 5ff82cc9 René Nussbaumer

2808 5ff82cc9 René Nussbaumer
  """
2809 5ff82cc9 René Nussbaumer
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
2810 5ff82cc9 René Nussbaumer
  if missing:
2811 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
2812 5ff82cc9 René Nussbaumer
                                 missing)
2813 5ff82cc9 René Nussbaumer
2814 5ff82cc9 René Nussbaumer
2815 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
2816 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2817 a8083063 Iustin Pop

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

2821 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2822 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
2823 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2824 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2825 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2826 94dcbdb0 Andrea Spadaccini

2827 a8083063 Iustin Pop
  """
2828 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2829 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
2830 c7c6606d René Nussbaumer
                                  disk.params)
2831 cb999543 Iustin Pop
  if not device.attached:
2832 a8083063 Iustin Pop
    return None
2833 ecb091e3 Iustin Pop
  return device
2834 a8083063 Iustin Pop
2835 a8083063 Iustin Pop
2836 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
2837 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2838 a8083063 Iustin Pop

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

2842 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2843 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
2844 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2845 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2846 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2847 94dcbdb0 Andrea Spadaccini

2848 a8083063 Iustin Pop
  """
2849 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2850 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
2851 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
2852 c7c6606d René Nussbaumer
                                  disk.params)
2853 1063abd1 Iustin Pop
  device.Assemble()
2854 a8083063 Iustin Pop
  return device
2855 a8083063 Iustin Pop
2856 a8083063 Iustin Pop
2857 94dcbdb0 Andrea Spadaccini
def Create(disk, children):
2858 a8083063 Iustin Pop
  """Create a device.
2859 a8083063 Iustin Pop

2860 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
2861 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
2862 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
2863 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
2864 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
2865 94dcbdb0 Andrea Spadaccini

2866 a8083063 Iustin Pop
  """
2867 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
2868 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
2869 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
2870 c7c6606d René Nussbaumer
                                         disk.params)
2871 a8083063 Iustin Pop
  return device