Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 3e77a36c

History | View | Annotate | Download (106.2 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 63c73073 Bernardo Dal Seno
import math
33 a8083063 Iustin Pop
34 a8083063 Iustin Pop
from ganeti import utils
35 a8083063 Iustin Pop
from ganeti import errors
36 fe96220b Iustin Pop
from ganeti import constants
37 96acbc09 Michael Hanselmann
from ganeti import objects
38 cea881e5 Michael Hanselmann
from ganeti import compat
39 a744b676 Manuel Franceschini
from ganeti import netutils
40 fbdac0d9 Michael Hanselmann
from ganeti import pathutils
41 a8083063 Iustin Pop
42 a8083063 Iustin Pop
43 310fbb64 Iustin Pop
# Size of reads in _CanReadDevice
44 310fbb64 Iustin Pop
_DEVICE_READ_SIZE = 128 * 1024
45 310fbb64 Iustin Pop
46 310fbb64 Iustin Pop
47 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
48 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
49 82463074 Iustin Pop

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

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

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

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

72 82463074 Iustin Pop
  """
73 82463074 Iustin Pop
  if args:
74 82463074 Iustin Pop
    msg = msg % args
75 82463074 Iustin Pop
  logging.error(msg)
76 82463074 Iustin Pop
  raise errors.BlockDeviceError(msg)
77 82463074 Iustin Pop
78 82463074 Iustin Pop
79 e398546b Iustin Pop
def _CheckResult(result):
80 e398546b Iustin Pop
  """Throws an error if the given result is a failed one.
81 e398546b Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

290 a8083063 Iustin Pop
    """
291 a8083063 Iustin Pop
    raise NotImplementedError
292 a8083063 Iustin Pop
293 a8083063 Iustin Pop
  @classmethod
294 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
295 a8083063 Iustin Pop
    """Create the device.
296 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

515 a8083063 Iustin Pop
    """
516 94dcbdb0 Andrea Spadaccini
    super(LogicalVolume, self).__init__(unique_id, children, size, params)
517 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
518 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
519 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
520 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
521 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
522 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
523 99e8295c Iustin Pop
    self._degraded = True
524 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
525 a8083063 Iustin Pop
    self.Attach()
526 a8083063 Iustin Pop
527 63c73073 Bernardo Dal Seno
  @staticmethod
528 63c73073 Bernardo Dal Seno
  def _GetStdPvSize(pvs_info):
529 63c73073 Bernardo Dal Seno
    """Return the the standard PV size (used with exclusive storage).
530 63c73073 Bernardo Dal Seno

531 63c73073 Bernardo Dal Seno
    @param pvs_info: list of objects.LvmPvInfo, cannot be empty
532 63c73073 Bernardo Dal Seno
    @rtype: float
533 63c73073 Bernardo Dal Seno
    @return: size in MiB
534 63c73073 Bernardo Dal Seno

535 63c73073 Bernardo Dal Seno
    """
536 63c73073 Bernardo Dal Seno
    assert len(pvs_info) > 0
537 63c73073 Bernardo Dal Seno
    smallest = min([pv.size for pv in pvs_info])
538 63c73073 Bernardo Dal Seno
    return smallest / (1 + constants.PART_MARGIN + constants.PART_RESERVED)
539 63c73073 Bernardo Dal Seno
540 63c73073 Bernardo Dal Seno
  @staticmethod
541 63c73073 Bernardo Dal Seno
  def _ComputeNumPvs(size, pvs_info):
542 63c73073 Bernardo Dal Seno
    """Compute the number of PVs needed for an LV (with exclusive storage).
543 63c73073 Bernardo Dal Seno

544 63c73073 Bernardo Dal Seno
    @type size: float
545 23d95cff Bernardo Dal Seno
    @param size: LV size in MiB
546 63c73073 Bernardo Dal Seno
    @param pvs_info: list of objects.LvmPvInfo, cannot be empty
547 63c73073 Bernardo Dal Seno
    @rtype: integer
548 63c73073 Bernardo Dal Seno
    @return: number of PVs needed
549 63c73073 Bernardo Dal Seno
    """
550 63c73073 Bernardo Dal Seno
    assert len(pvs_info) > 0
551 63c73073 Bernardo Dal Seno
    pv_size = float(LogicalVolume._GetStdPvSize(pvs_info))
552 63c73073 Bernardo Dal Seno
    return int(math.ceil(float(size) / pv_size))
553 63c73073 Bernardo Dal Seno
554 63c73073 Bernardo Dal Seno
  @staticmethod
555 63c73073 Bernardo Dal Seno
  def _GetEmptyPvNames(pvs_info, max_pvs=None):
556 63c73073 Bernardo Dal Seno
    """Return a list of empty PVs, by name.
557 63c73073 Bernardo Dal Seno

558 63c73073 Bernardo Dal Seno
    """
559 63c73073 Bernardo Dal Seno
    empty_pvs = filter(objects.LvmPvInfo.IsEmpty, pvs_info)
560 63c73073 Bernardo Dal Seno
    if max_pvs is not None:
561 63c73073 Bernardo Dal Seno
      empty_pvs = empty_pvs[:max_pvs]
562 63c73073 Bernardo Dal Seno
    return map((lambda pv: pv.name), empty_pvs)
563 63c73073 Bernardo Dal Seno
564 a8083063 Iustin Pop
  @classmethod
565 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
566 a8083063 Iustin Pop
    """Create a new logical volume.
567 a8083063 Iustin Pop

568 a8083063 Iustin Pop
    """
569 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
570 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
571 6c626518 Iustin Pop
                                   str(unique_id))
572 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
573 6136f8f0 Iustin Pop
    cls._ValidateName(vg_name)
574 6136f8f0 Iustin Pop
    cls._ValidateName(lv_name)
575 2070598f Iustin Pop
    pvs_info = cls.GetPVInfo([vg_name])
576 a8083063 Iustin Pop
    if not pvs_info:
577 63c73073 Bernardo Dal Seno
      if excl_stor:
578 63c73073 Bernardo Dal Seno
        msg = "No (empty) PVs found"
579 63c73073 Bernardo Dal Seno
      else:
580 63c73073 Bernardo Dal Seno
        msg = "Can't compute PV info for vg %s" % vg_name
581 63c73073 Bernardo Dal Seno
      _ThrowError(msg)
582 59726e15 Bernardo Dal Seno
    pvs_info.sort(key=(lambda pv: pv.free), reverse=True)
583 5b7b5d49 Guido Trotter
584 59726e15 Bernardo Dal Seno
    pvlist = [pv.name for pv in pvs_info]
585 403f5172 Guido Trotter
    if compat.any(":" in v for v in pvlist):
586 01b6558a Iustin Pop
      _ThrowError("Some of your PVs have the invalid character ':' in their"
587 01b6558a Iustin Pop
                  " name, this is not supported - please filter them out"
588 01b6558a Iustin Pop
                  " in lvm.conf using either 'filter' or 'preferred_names'")
589 63c73073 Bernardo Dal Seno
590 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
591 ac00bf1b Andrea Spadaccini
    desired_stripes = params[constants.LDP_STRIPES]
592 43e11798 Andrea Spadaccini
    stripes = min(current_pvs, desired_stripes)
593 63c73073 Bernardo Dal Seno
594 63c73073 Bernardo Dal Seno
    if excl_stor:
595 63c73073 Bernardo Dal Seno
      err_msgs = utils.LvmExclusiveCheckNodePvs(pvs_info)
596 63c73073 Bernardo Dal Seno
      if err_msgs:
597 63c73073 Bernardo Dal Seno
        for m in err_msgs:
598 63c73073 Bernardo Dal Seno
          logging.warning(m)
599 63c73073 Bernardo Dal Seno
      req_pvs = cls._ComputeNumPvs(size, pvs_info)
600 63c73073 Bernardo Dal Seno
      pvlist = cls._GetEmptyPvNames(pvs_info, req_pvs)
601 63c73073 Bernardo Dal Seno
      current_pvs = len(pvlist)
602 63c73073 Bernardo Dal Seno
      if current_pvs < req_pvs:
603 63c73073 Bernardo Dal Seno
        _ThrowError("Not enough empty PVs to create a disk of %d MB:"
604 63c73073 Bernardo Dal Seno
                    " %d available, %d needed", size, current_pvs, req_pvs)
605 63c73073 Bernardo Dal Seno
      assert current_pvs == len(pvlist)
606 63c73073 Bernardo Dal Seno
      if stripes > current_pvs:
607 63c73073 Bernardo Dal Seno
        # No warning issued for this, as it's no surprise
608 63c73073 Bernardo Dal Seno
        stripes = current_pvs
609 63c73073 Bernardo Dal Seno
610 63c73073 Bernardo Dal Seno
    else:
611 63c73073 Bernardo Dal Seno
      if stripes < desired_stripes:
612 63c73073 Bernardo Dal Seno
        logging.warning("Could not use %d stripes for VG %s, as only %d PVs are"
613 63c73073 Bernardo Dal Seno
                        " available.", desired_stripes, vg_name, current_pvs)
614 63c73073 Bernardo Dal Seno
      free_size = sum([pv.free for pv in pvs_info])
615 63c73073 Bernardo Dal Seno
      # The size constraint should have been checked from the master before
616 63c73073 Bernardo Dal Seno
      # calling the create function.
617 63c73073 Bernardo Dal Seno
      if free_size < size:
618 63c73073 Bernardo Dal Seno
        _ThrowError("Not enough free space: required %s,"
619 63c73073 Bernardo Dal Seno
                    " available %s", size, free_size)
620 63c73073 Bernardo Dal Seno
621 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
622 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
623 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
624 fecbe9d5 Iustin Pop
    # stripes
625 63c73073 Bernardo Dal Seno
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
626 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
627 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
628 fecbe9d5 Iustin Pop
      if not result.failed:
629 fecbe9d5 Iustin Pop
        break
630 a8083063 Iustin Pop
    if result.failed:
631 82463074 Iustin Pop
      _ThrowError("LV create failed (%s): %s",
632 82463074 Iustin Pop
                  result.fail_reason, result.output)
633 94dcbdb0 Andrea Spadaccini
    return LogicalVolume(unique_id, children, size, params)
634 a8083063 Iustin Pop
635 a8083063 Iustin Pop
  @staticmethod
636 197478f2 René Nussbaumer
  def _GetVolumeInfo(lvm_cmd, fields):
637 197478f2 René Nussbaumer
    """Returns LVM Volumen infos using lvm_cmd
638 197478f2 René Nussbaumer

639 197478f2 René Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
640 197478f2 René Nussbaumer
    @param fields: Fields to return
641 197478f2 René Nussbaumer
    @return: A list of dicts each with the parsed fields
642 197478f2 René Nussbaumer

643 197478f2 René Nussbaumer
    """
644 197478f2 René Nussbaumer
    if not fields:
645 197478f2 René Nussbaumer
      raise errors.ProgrammerError("No fields specified")
646 197478f2 René Nussbaumer
647 197478f2 René Nussbaumer
    sep = "|"
648 197478f2 René Nussbaumer
    cmd = [lvm_cmd, "--noheadings", "--nosuffix", "--units=m", "--unbuffered",
649 197478f2 René Nussbaumer
           "--separator=%s" % sep, "-o%s" % ",".join(fields)]
650 197478f2 René Nussbaumer
651 197478f2 René Nussbaumer
    result = utils.RunCmd(cmd)
652 197478f2 René Nussbaumer
    if result.failed:
653 197478f2 René Nussbaumer
      raise errors.CommandError("Can't get the volume information: %s - %s" %
654 197478f2 René Nussbaumer
                                (result.fail_reason, result.output))
655 197478f2 René Nussbaumer
656 197478f2 René Nussbaumer
    data = []
657 197478f2 René Nussbaumer
    for line in result.stdout.splitlines():
658 197478f2 René Nussbaumer
      splitted_fields = line.strip().split(sep)
659 197478f2 René Nussbaumer
660 197478f2 René Nussbaumer
      if len(fields) != len(splitted_fields):
661 197478f2 René Nussbaumer
        raise errors.CommandError("Can't parse %s output: line '%s'" %
662 197478f2 René Nussbaumer
                                  (lvm_cmd, line))
663 197478f2 René Nussbaumer
664 197478f2 René Nussbaumer
      data.append(splitted_fields)
665 197478f2 René Nussbaumer
666 197478f2 René Nussbaumer
    return data
667 197478f2 René Nussbaumer
668 197478f2 René Nussbaumer
  @classmethod
669 197478f2 René Nussbaumer
  def GetPVInfo(cls, vg_names, filter_allocatable=True):
670 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
671 a8083063 Iustin Pop

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

675 c41eea6e Iustin Pop
    @rtype: list
676 59726e15 Bernardo Dal Seno
    @return: list of objects.LvmPvInfo objects
677 098c0958 Michael Hanselmann

678 a8083063 Iustin Pop
    """
679 197478f2 René Nussbaumer
    try:
680 197478f2 René Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
681 59726e15 Bernardo Dal Seno
                                        "pv_attr", "pv_size"])
682 197478f2 René Nussbaumer
    except errors.GenericError, err:
683 197478f2 René Nussbaumer
      logging.error("Can't get PV information: %s", err)
684 a8083063 Iustin Pop
      return None
685 197478f2 René Nussbaumer
686 a8083063 Iustin Pop
    data = []
687 59726e15 Bernardo Dal Seno
    for (pv_name, vg_name, pv_free, pv_attr, pv_size) in info:
688 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
689 197478f2 René Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
690 a8083063 Iustin Pop
        continue
691 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
692 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
693 2070598f Iustin Pop
        continue
694 59726e15 Bernardo Dal Seno
      pvi = objects.LvmPvInfo(name=pv_name, vg_name=vg_name,
695 59726e15 Bernardo Dal Seno
                              size=float(pv_size), free=float(pv_free),
696 59726e15 Bernardo Dal Seno
                              attributes=pv_attr)
697 59726e15 Bernardo Dal Seno
      data.append(pvi)
698 197478f2 René Nussbaumer
699 197478f2 René Nussbaumer
    return data
700 197478f2 René Nussbaumer
701 197478f2 René Nussbaumer
  @classmethod
702 61481c52 Bernardo Dal Seno
  def _GetExclusiveStorageVgFree(cls, vg_name):
703 61481c52 Bernardo Dal Seno
    """Return the free disk space in the given VG, in exclusive storage mode.
704 61481c52 Bernardo Dal Seno

705 61481c52 Bernardo Dal Seno
    @type vg_name: string
706 61481c52 Bernardo Dal Seno
    @param vg_name: VG name
707 61481c52 Bernardo Dal Seno
    @rtype: float
708 61481c52 Bernardo Dal Seno
    @return: free space in MiB
709 61481c52 Bernardo Dal Seno
    """
710 61481c52 Bernardo Dal Seno
    pvs_info = cls.GetPVInfo([vg_name])
711 61481c52 Bernardo Dal Seno
    if not pvs_info:
712 61481c52 Bernardo Dal Seno
      return 0.0
713 61481c52 Bernardo Dal Seno
    pv_size = cls._GetStdPvSize(pvs_info)
714 61481c52 Bernardo Dal Seno
    num_pvs = len(cls._GetEmptyPvNames(pvs_info))
715 61481c52 Bernardo Dal Seno
    return pv_size * num_pvs
716 61481c52 Bernardo Dal Seno
717 61481c52 Bernardo Dal Seno
  @classmethod
718 1a3c5d4e Bernardo Dal Seno
  def GetVGInfo(cls, vg_names, excl_stor, filter_readonly=True):
719 197478f2 René Nussbaumer
    """Get the free space info for specific VGs.
720 197478f2 René Nussbaumer

721 197478f2 René Nussbaumer
    @param vg_names: list of volume group names, if empty all will be returned
722 1a3c5d4e Bernardo Dal Seno
    @param excl_stor: whether exclusive_storage is enabled
723 197478f2 René Nussbaumer
    @param filter_readonly: whether to skip over readonly VGs
724 197478f2 René Nussbaumer

725 197478f2 René Nussbaumer
    @rtype: list
726 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
727 673cd9c4 René Nussbaumer
             MiB
728 197478f2 René Nussbaumer

729 197478f2 René Nussbaumer
    """
730 197478f2 René Nussbaumer
    try:
731 673cd9c4 René Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
732 673cd9c4 René Nussbaumer
                                        "vg_size"])
733 197478f2 René Nussbaumer
    except errors.GenericError, err:
734 197478f2 René Nussbaumer
      logging.error("Can't get VG information: %s", err)
735 197478f2 René Nussbaumer
      return None
736 197478f2 René Nussbaumer
737 197478f2 René Nussbaumer
    data = []
738 673cd9c4 René Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
739 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not writable
740 197478f2 René Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
741 197478f2 René Nussbaumer
        continue
742 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
743 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
744 197478f2 René Nussbaumer
        continue
745 61481c52 Bernardo Dal Seno
      # Exclusive storage needs a different concept of free space
746 61481c52 Bernardo Dal Seno
      if excl_stor:
747 61481c52 Bernardo Dal Seno
        es_free = cls._GetExclusiveStorageVgFree(vg_name)
748 61481c52 Bernardo Dal Seno
        assert es_free <= vg_free
749 61481c52 Bernardo Dal Seno
        vg_free = es_free
750 673cd9c4 René Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
751 a8083063 Iustin Pop
752 a8083063 Iustin Pop
    return data
753 a8083063 Iustin Pop
754 6136f8f0 Iustin Pop
  @classmethod
755 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
756 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
757 6136f8f0 Iustin Pop

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

762 6136f8f0 Iustin Pop
    """
763 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
764 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
765 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
766 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
767 6136f8f0 Iustin Pop
768 a8083063 Iustin Pop
  def Remove(self):
769 a8083063 Iustin Pop
    """Remove this logical volume.
770 a8083063 Iustin Pop

771 a8083063 Iustin Pop
    """
772 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
773 a8083063 Iustin Pop
      # the LV does not exist
774 0c6c04ec Iustin Pop
      return
775 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
776 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
777 a8083063 Iustin Pop
    if result.failed:
778 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
779 a8083063 Iustin Pop
780 f3e513ad Iustin Pop
  def Rename(self, new_id):
781 f3e513ad Iustin Pop
    """Rename this logical volume.
782 f3e513ad Iustin Pop

783 f3e513ad Iustin Pop
    """
784 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
785 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
786 f3e513ad Iustin Pop
    new_vg, new_name = new_id
787 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
788 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
789 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
790 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
791 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
792 f3e513ad Iustin Pop
    if result.failed:
793 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
794 be345db0 Iustin Pop
    self._lv_name = new_name
795 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
796 be345db0 Iustin Pop
797 a8083063 Iustin Pop
  def Attach(self):
798 a8083063 Iustin Pop
    """Attach to an existing LV.
799 a8083063 Iustin Pop

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

804 a8083063 Iustin Pop
    """
805 cb999543 Iustin Pop
    self.attached = False
806 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
807 38256320 Iustin Pop
                           "--units=m", "--nosuffix",
808 38256320 Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
809 38256320 Iustin Pop
                           "vg_extent_size,stripes", self.dev_path])
810 a8083063 Iustin Pop
    if result.failed:
811 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
812 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
813 a8083063 Iustin Pop
      return False
814 38256320 Iustin Pop
    # the output can (and will) have multiple lines for multi-segment
815 38256320 Iustin Pop
    # LVs, as the 'stripes' parameter is a segment one, so we take
816 38256320 Iustin Pop
    # only the last entry, which is the one we're interested in; note
817 38256320 Iustin Pop
    # that with LVM2 anyway the 'stripes' value must be constant
818 38256320 Iustin Pop
    # across segments, so this is a no-op actually
819 38256320 Iustin Pop
    out = result.stdout.splitlines()
820 38256320 Iustin Pop
    if not out: # totally empty result? splitlines() returns at least
821 38256320 Iustin Pop
                # one line for any non-empty string
822 38256320 Iustin Pop
      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
823 38256320 Iustin Pop
      return False
824 d0c8c01d Iustin Pop
    out = out[-1].strip().rstrip(",")
825 99e8295c Iustin Pop
    out = out.split(",")
826 38256320 Iustin Pop
    if len(out) != 5:
827 38256320 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
828 99e8295c Iustin Pop
      return False
829 99e8295c Iustin Pop
830 38256320 Iustin Pop
    status, major, minor, pe_size, stripes = out
831 0304f0ec Iustin Pop
    if len(status) < 6:
832 0304f0ec Iustin Pop
      logging.error("lvs lv_attr is not at least 6 characters (%s)", status)
833 99e8295c Iustin Pop
      return False
834 99e8295c Iustin Pop
835 99e8295c Iustin Pop
    try:
836 99e8295c Iustin Pop
      major = int(major)
837 99e8295c Iustin Pop
      minor = int(minor)
838 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
839 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
840 99e8295c Iustin Pop
841 38256320 Iustin Pop
    try:
842 38256320 Iustin Pop
      pe_size = int(float(pe_size))
843 38256320 Iustin Pop
    except (TypeError, ValueError), err:
844 38256320 Iustin Pop
      logging.error("Can't parse vg extent size: %s", err)
845 38256320 Iustin Pop
      return False
846 38256320 Iustin Pop
847 38256320 Iustin Pop
    try:
848 38256320 Iustin Pop
      stripes = int(stripes)
849 38256320 Iustin Pop
    except (TypeError, ValueError), err:
850 38256320 Iustin Pop
      logging.error("Can't parse the number of stripes: %s", err)
851 38256320 Iustin Pop
      return False
852 38256320 Iustin Pop
853 99e8295c Iustin Pop
    self.major = major
854 99e8295c Iustin Pop
    self.minor = minor
855 38256320 Iustin Pop
    self.pe_size = pe_size
856 38256320 Iustin Pop
    self.stripe_count = stripes
857 d0c8c01d Iustin Pop
    self._degraded = status[0] == "v" # virtual volume, i.e. doesn't backing
858 99e8295c Iustin Pop
                                      # storage
859 cb999543 Iustin Pop
    self.attached = True
860 99e8295c Iustin Pop
    return True
861 a8083063 Iustin Pop
862 a8083063 Iustin Pop
  def Assemble(self):
863 a8083063 Iustin Pop
    """Assemble the device.
864 a8083063 Iustin Pop

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

869 a8083063 Iustin Pop
    """
870 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
871 5574047a Iustin Pop
    if result.failed:
872 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
873 a8083063 Iustin Pop
874 a8083063 Iustin Pop
  def Shutdown(self):
875 a8083063 Iustin Pop
    """Shutdown the device.
876 a8083063 Iustin Pop

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

880 a8083063 Iustin Pop
    """
881 746f7476 Iustin Pop
    pass
882 a8083063 Iustin Pop
883 9db6dbce Iustin Pop
  def GetSyncStatus(self):
884 9db6dbce Iustin Pop
    """Returns the sync status of the device.
885 9db6dbce Iustin Pop

886 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
887 9db6dbce Iustin Pop
    status of the mirror.
888 9db6dbce Iustin Pop

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

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

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

902 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
903 c41eea6e Iustin Pop

904 9db6dbce Iustin Pop
    """
905 f208978a Michael Hanselmann
    if self._degraded:
906 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
907 f208978a Michael Hanselmann
    else:
908 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
909 f208978a Michael Hanselmann
910 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
911 96acbc09 Michael Hanselmann
                                  major=self.major,
912 96acbc09 Michael Hanselmann
                                  minor=self.minor,
913 96acbc09 Michael Hanselmann
                                  sync_percent=None,
914 96acbc09 Michael Hanselmann
                                  estimated_time=None,
915 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
916 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
917 9db6dbce Iustin Pop
918 a8083063 Iustin Pop
  def Open(self, force=False):
919 a8083063 Iustin Pop
    """Make the device ready for I/O.
920 a8083063 Iustin Pop

921 a8083063 Iustin Pop
    This is a no-op for the LV device type.
922 a8083063 Iustin Pop

923 a8083063 Iustin Pop
    """
924 fdbd668d Iustin Pop
    pass
925 a8083063 Iustin Pop
926 a8083063 Iustin Pop
  def Close(self):
927 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
928 a8083063 Iustin Pop

929 a8083063 Iustin Pop
    This is a no-op for the LV device type.
930 a8083063 Iustin Pop

931 a8083063 Iustin Pop
    """
932 fdbd668d Iustin Pop
    pass
933 a8083063 Iustin Pop
934 a8083063 Iustin Pop
  def Snapshot(self, size):
935 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
936 a8083063 Iustin Pop

937 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
938 800ac399 Iustin Pop

939 a8083063 Iustin Pop
    """
940 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
941 a8083063 Iustin Pop
942 a8083063 Iustin Pop
    # remove existing snapshot if found
943 94dcbdb0 Andrea Spadaccini
    snap = LogicalVolume((self._vg_name, snap_name), None, size, self.params)
944 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
945 a8083063 Iustin Pop
946 1a3c5d4e Bernardo Dal Seno
    vg_info = self.GetVGInfo([self._vg_name], False)
947 197478f2 René Nussbaumer
    if not vg_info:
948 197478f2 René Nussbaumer
      _ThrowError("Can't compute VG info for vg %s", self._vg_name)
949 673cd9c4 René Nussbaumer
    free_size, _, _ = vg_info[0]
950 a8083063 Iustin Pop
    if free_size < size:
951 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
952 82463074 Iustin Pop
                  " available %s", size, free_size)
953 a8083063 Iustin Pop
954 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
955 e398546b Iustin Pop
                               "-n%s" % snap_name, self.dev_path]))
956 a8083063 Iustin Pop
957 800ac399 Iustin Pop
    return (self._vg_name, snap_name)
958 a8083063 Iustin Pop
959 a1556cfa Iustin Pop
  def _RemoveOldInfo(self):
960 a1556cfa Iustin Pop
    """Try to remove old tags from the lv.
961 a1556cfa Iustin Pop

962 a1556cfa Iustin Pop
    """
963 a1556cfa Iustin Pop
    result = utils.RunCmd(["lvs", "-o", "tags", "--noheadings", "--nosuffix",
964 a1556cfa Iustin Pop
                           self.dev_path])
965 a1556cfa Iustin Pop
    _CheckResult(result)
966 a1556cfa Iustin Pop
967 a1556cfa Iustin Pop
    raw_tags = result.stdout.strip()
968 a1556cfa Iustin Pop
    if raw_tags:
969 a1556cfa Iustin Pop
      for tag in raw_tags.split(","):
970 a1556cfa Iustin Pop
        _CheckResult(utils.RunCmd(["lvchange", "--deltag",
971 a1556cfa Iustin Pop
                                   tag.strip(), self.dev_path]))
972 a1556cfa Iustin Pop
973 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
974 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
975 a0c3fea1 Michael Hanselmann

976 a0c3fea1 Michael Hanselmann
    """
977 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
978 a0c3fea1 Michael Hanselmann
979 a1556cfa Iustin Pop
    self._RemoveOldInfo()
980 a1556cfa Iustin Pop
981 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
982 d0c8c01d Iustin Pop
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
983 d0c8c01d Iustin Pop
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
984 a0c3fea1 Michael Hanselmann
985 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
986 a0c3fea1 Michael Hanselmann
    text = text[:128]
987 a0c3fea1 Michael Hanselmann
988 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvchange", "--addtag", text, self.dev_path]))
989 82463074 Iustin Pop
990 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
991 1005d816 Iustin Pop
    """Grow the logical volume.
992 1005d816 Iustin Pop

993 1005d816 Iustin Pop
    """
994 cad0723b Iustin Pop
    if not backingstore:
995 cad0723b Iustin Pop
      return
996 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
997 38256320 Iustin Pop
      if not self.Attach():
998 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
999 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
1000 38256320 Iustin Pop
    rest = amount % full_stripe_size
1001 38256320 Iustin Pop
    if rest != 0:
1002 38256320 Iustin Pop
      amount += full_stripe_size - rest
1003 7fe23d47 Iustin Pop
    cmd = ["lvextend", "-L", "+%dm" % amount]
1004 7fe23d47 Iustin Pop
    if dryrun:
1005 7fe23d47 Iustin Pop
      cmd.append("--test")
1006 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
1007 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
1008 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
1009 1005d816 Iustin Pop
    # supports 'cling'
1010 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
1011 7fe23d47 Iustin Pop
      result = utils.RunCmd(cmd + ["--alloc", alloc_policy, self.dev_path])
1012 1005d816 Iustin Pop
      if not result.failed:
1013 1005d816 Iustin Pop
        return
1014 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
1015 a0c3fea1 Michael Hanselmann
1016 a0c3fea1 Michael Hanselmann
1017 6b90c22e Iustin Pop
class DRBD8Status(object):
1018 6b90c22e Iustin Pop
  """A DRBD status representation class.
1019 6b90c22e Iustin Pop

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

1022 6b90c22e Iustin Pop
  """
1023 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
1024 01e2ce3a Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
1025 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
1026 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
1027 7a380ddf René Nussbaumer
                       # Due to a bug in drbd in the kernel, introduced in
1028 7a380ddf René Nussbaumer
                       # commit 4b0715f096 (still unfixed as of 2011-08-22)
1029 7a380ddf René Nussbaumer
                       "(?:\s|M)"
1030 7a380ddf René Nussbaumer
                       "finish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
1031 6b90c22e Iustin Pop
1032 3c003d9d Iustin Pop
  CS_UNCONFIGURED = "Unconfigured"
1033 3c003d9d Iustin Pop
  CS_STANDALONE = "StandAlone"
1034 3c003d9d Iustin Pop
  CS_WFCONNECTION = "WFConnection"
1035 3c003d9d Iustin Pop
  CS_WFREPORTPARAMS = "WFReportParams"
1036 3c003d9d Iustin Pop
  CS_CONNECTED = "Connected"
1037 3c003d9d Iustin Pop
  CS_STARTINGSYNCS = "StartingSyncS"
1038 3c003d9d Iustin Pop
  CS_STARTINGSYNCT = "StartingSyncT"
1039 3c003d9d Iustin Pop
  CS_WFBITMAPS = "WFBitMapS"
1040 3c003d9d Iustin Pop
  CS_WFBITMAPT = "WFBitMapT"
1041 3c003d9d Iustin Pop
  CS_WFSYNCUUID = "WFSyncUUID"
1042 3c003d9d Iustin Pop
  CS_SYNCSOURCE = "SyncSource"
1043 3c003d9d Iustin Pop
  CS_SYNCTARGET = "SyncTarget"
1044 3c003d9d Iustin Pop
  CS_PAUSEDSYNCS = "PausedSyncS"
1045 3c003d9d Iustin Pop
  CS_PAUSEDSYNCT = "PausedSyncT"
1046 b8028dcf Michael Hanselmann
  CSET_SYNC = compat.UniqueFrozenset([
1047 3c003d9d Iustin Pop
    CS_WFREPORTPARAMS,
1048 3c003d9d Iustin Pop
    CS_STARTINGSYNCS,
1049 3c003d9d Iustin Pop
    CS_STARTINGSYNCT,
1050 3c003d9d Iustin Pop
    CS_WFBITMAPS,
1051 3c003d9d Iustin Pop
    CS_WFBITMAPT,
1052 3c003d9d Iustin Pop
    CS_WFSYNCUUID,
1053 3c003d9d Iustin Pop
    CS_SYNCSOURCE,
1054 3c003d9d Iustin Pop
    CS_SYNCTARGET,
1055 3c003d9d Iustin Pop
    CS_PAUSEDSYNCS,
1056 3c003d9d Iustin Pop
    CS_PAUSEDSYNCT,
1057 3c003d9d Iustin Pop
    ])
1058 3c003d9d Iustin Pop
1059 3c003d9d Iustin Pop
  DS_DISKLESS = "Diskless"
1060 3c003d9d Iustin Pop
  DS_ATTACHING = "Attaching" # transient state
1061 3c003d9d Iustin Pop
  DS_FAILED = "Failed" # transient state, next: diskless
1062 3c003d9d Iustin Pop
  DS_NEGOTIATING = "Negotiating" # transient state
1063 3c003d9d Iustin Pop
  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
1064 3c003d9d Iustin Pop
  DS_OUTDATED = "Outdated"
1065 3c003d9d Iustin Pop
  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
1066 3c003d9d Iustin Pop
  DS_CONSISTENT = "Consistent"
1067 3c003d9d Iustin Pop
  DS_UPTODATE = "UpToDate" # normal state
1068 3c003d9d Iustin Pop
1069 3c003d9d Iustin Pop
  RO_PRIMARY = "Primary"
1070 3c003d9d Iustin Pop
  RO_SECONDARY = "Secondary"
1071 3c003d9d Iustin Pop
  RO_UNKNOWN = "Unknown"
1072 3c003d9d Iustin Pop
1073 6b90c22e Iustin Pop
  def __init__(self, procline):
1074 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
1075 767d52d3 Iustin Pop
    if u:
1076 3c003d9d Iustin Pop
      self.cstatus = self.CS_UNCONFIGURED
1077 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
1078 767d52d3 Iustin Pop
    else:
1079 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
1080 767d52d3 Iustin Pop
      if not m:
1081 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
1082 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
1083 767d52d3 Iustin Pop
      self.lrole = m.group(2)
1084 767d52d3 Iustin Pop
      self.rrole = m.group(3)
1085 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
1086 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
1087 767d52d3 Iustin Pop
1088 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
1089 6b90c22e Iustin Pop
1090 3c003d9d Iustin Pop
    self.is_standalone = self.cstatus == self.CS_STANDALONE
1091 3c003d9d Iustin Pop
    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
1092 3c003d9d Iustin Pop
    self.is_connected = self.cstatus == self.CS_CONNECTED
1093 3c003d9d Iustin Pop
    self.is_primary = self.lrole == self.RO_PRIMARY
1094 3c003d9d Iustin Pop
    self.is_secondary = self.lrole == self.RO_SECONDARY
1095 3c003d9d Iustin Pop
    self.peer_primary = self.rrole == self.RO_PRIMARY
1096 3c003d9d Iustin Pop
    self.peer_secondary = self.rrole == self.RO_SECONDARY
1097 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
1098 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
1099 6b90c22e Iustin Pop
1100 3c003d9d Iustin Pop
    self.is_diskless = self.ldisk == self.DS_DISKLESS
1101 3c003d9d Iustin Pop
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
1102 6b90c22e Iustin Pop
1103 3c003d9d Iustin Pop
    self.is_in_resync = self.cstatus in self.CSET_SYNC
1104 3c003d9d Iustin Pop
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
1105 6b93ec9d Iustin Pop
1106 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
1107 6b90c22e Iustin Pop
    if m:
1108 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
1109 6b90c22e Iustin Pop
      hours = int(m.group(2))
1110 6b90c22e Iustin Pop
      minutes = int(m.group(3))
1111 6b90c22e Iustin Pop
      seconds = int(m.group(4))
1112 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
1113 6b90c22e Iustin Pop
    else:
1114 3c003d9d Iustin Pop
      # we have (in this if branch) no percent information, but if
1115 3c003d9d Iustin Pop
      # we're resyncing we need to 'fake' a sync percent information,
1116 3c003d9d Iustin Pop
      # as this is how cmdlib determines if it makes sense to wait for
1117 3c003d9d Iustin Pop
      # resyncing or not
1118 3c003d9d Iustin Pop
      if self.is_in_resync:
1119 3c003d9d Iustin Pop
        self.sync_percent = 0
1120 3c003d9d Iustin Pop
      else:
1121 3c003d9d Iustin Pop
        self.sync_percent = None
1122 6b90c22e Iustin Pop
      self.est_time = None
1123 6b90c22e Iustin Pop
1124 6b90c22e Iustin Pop
1125 b459a848 Andrea Spadaccini
class BaseDRBD(BlockDev): # pylint: disable=W0223
1126 0f7f32d9 Iustin Pop
  """Base DRBD class.
1127 a8083063 Iustin Pop

1128 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
1129 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
1130 0f7f32d9 Iustin Pop

1131 abdf0113 Iustin Pop
  """
1132 fcee765d Manuel Franceschini
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)(?:\.\d+)?"
1133 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
1134 9122e60a Iustin Pop
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
1135 9122e60a Iustin Pop
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
1136 a8083063 Iustin Pop
1137 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
1138 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
1139 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
1140 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
1141 a8083063 Iustin Pop
1142 61e062dd Michele Tartara
  _STATUS_FILE = constants.DRBD_STATUS_FILE
1143 549071a0 Luca Bigliardi
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
1144 6b90c22e Iustin Pop
1145 abdf0113 Iustin Pop
  @staticmethod
1146 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
1147 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
1148 a8083063 Iustin Pop

1149 a8083063 Iustin Pop
    """
1150 abdf0113 Iustin Pop
    try:
1151 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
1152 f6eaed12 Iustin Pop
    except EnvironmentError, err:
1153 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
1154 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
1155 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
1156 f6eaed12 Iustin Pop
      else:
1157 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
1158 abdf0113 Iustin Pop
    if not data:
1159 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
1160 abdf0113 Iustin Pop
    return data
1161 a8083063 Iustin Pop
1162 9122e60a Iustin Pop
  @classmethod
1163 9122e60a Iustin Pop
  def _MassageProcData(cls, data):
1164 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
1165 a8083063 Iustin Pop

1166 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
1167 c41eea6e Iustin Pop
        for that minor
1168 a8083063 Iustin Pop

1169 a8083063 Iustin Pop
    """
1170 abdf0113 Iustin Pop
    results = {}
1171 abdf0113 Iustin Pop
    old_minor = old_line = None
1172 abdf0113 Iustin Pop
    for line in data:
1173 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
1174 67d101d4 Iustin Pop
        continue
1175 9122e60a Iustin Pop
      lresult = cls._VALID_LINE_RE.match(line)
1176 abdf0113 Iustin Pop
      if lresult is not None:
1177 abdf0113 Iustin Pop
        if old_minor is not None:
1178 abdf0113 Iustin Pop
          results[old_minor] = old_line
1179 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
1180 abdf0113 Iustin Pop
        old_line = line
1181 abdf0113 Iustin Pop
      else:
1182 abdf0113 Iustin Pop
        if old_minor is not None:
1183 abdf0113 Iustin Pop
          old_line += " " + line.strip()
1184 abdf0113 Iustin Pop
    # add last line
1185 abdf0113 Iustin Pop
    if old_minor is not None:
1186 abdf0113 Iustin Pop
      results[old_minor] = old_line
1187 abdf0113 Iustin Pop
    return results
1188 a8083063 Iustin Pop
1189 abdf0113 Iustin Pop
  @classmethod
1190 fcee765d Manuel Franceschini
  def _GetVersion(cls, proc_data):
1191 abdf0113 Iustin Pop
    """Return the DRBD version.
1192 a8083063 Iustin Pop

1193 abdf0113 Iustin Pop
    This will return a dict with keys:
1194 c41eea6e Iustin Pop
      - k_major
1195 c41eea6e Iustin Pop
      - k_minor
1196 c41eea6e Iustin Pop
      - k_point
1197 c41eea6e Iustin Pop
      - api
1198 c41eea6e Iustin Pop
      - proto
1199 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
1200 a8083063 Iustin Pop

1201 a8083063 Iustin Pop
    """
1202 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
1203 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
1204 abdf0113 Iustin Pop
    if not version:
1205 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
1206 abdf0113 Iustin Pop
                                    first_line)
1207 a8083063 Iustin Pop
1208 abdf0113 Iustin Pop
    values = version.groups()
1209 5ae4945a Iustin Pop
    retval = {
1210 5ae4945a Iustin Pop
      "k_major": int(values[0]),
1211 5ae4945a Iustin Pop
      "k_minor": int(values[1]),
1212 5ae4945a Iustin Pop
      "k_point": int(values[2]),
1213 5ae4945a Iustin Pop
      "api": int(values[3]),
1214 5ae4945a Iustin Pop
      "proto": int(values[4]),
1215 5ae4945a Iustin Pop
      }
1216 abdf0113 Iustin Pop
    if values[5] is not None:
1217 d0c8c01d Iustin Pop
      retval["proto2"] = values[5]
1218 a8083063 Iustin Pop
1219 abdf0113 Iustin Pop
    return retval
1220 abdf0113 Iustin Pop
1221 abdf0113 Iustin Pop
  @staticmethod
1222 549071a0 Luca Bigliardi
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
1223 549071a0 Luca Bigliardi
    """Returns DRBD usermode_helper currently set.
1224 549071a0 Luca Bigliardi

1225 549071a0 Luca Bigliardi
    """
1226 549071a0 Luca Bigliardi
    try:
1227 549071a0 Luca Bigliardi
      helper = utils.ReadFile(filename).splitlines()[0]
1228 549071a0 Luca Bigliardi
    except EnvironmentError, err:
1229 549071a0 Luca Bigliardi
      if err.errno == errno.ENOENT:
1230 549071a0 Luca Bigliardi
        _ThrowError("The file %s cannot be opened, check if the module"
1231 549071a0 Luca Bigliardi
                    " is loaded (%s)", filename, str(err))
1232 549071a0 Luca Bigliardi
      else:
1233 549071a0 Luca Bigliardi
        _ThrowError("Can't read DRBD helper file %s: %s", filename, str(err))
1234 549071a0 Luca Bigliardi
    if not helper:
1235 549071a0 Luca Bigliardi
      _ThrowError("Can't read any data from %s", filename)
1236 549071a0 Luca Bigliardi
    return helper
1237 549071a0 Luca Bigliardi
1238 549071a0 Luca Bigliardi
  @staticmethod
1239 abdf0113 Iustin Pop
  def _DevPath(minor):
1240 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
1241 a8083063 Iustin Pop

1242 a8083063 Iustin Pop
    """
1243 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
1244 a8083063 Iustin Pop
1245 abdf0113 Iustin Pop
  @classmethod
1246 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
1247 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
1248 a8083063 Iustin Pop

1249 a8083063 Iustin Pop
    """
1250 abdf0113 Iustin Pop
    data = cls._GetProcData()
1251 a8083063 Iustin Pop
1252 abdf0113 Iustin Pop
    used_devs = {}
1253 abdf0113 Iustin Pop
    for line in data:
1254 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1255 abdf0113 Iustin Pop
      if not match:
1256 abdf0113 Iustin Pop
        continue
1257 abdf0113 Iustin Pop
      minor = int(match.group(1))
1258 abdf0113 Iustin Pop
      state = match.group(2)
1259 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1260 abdf0113 Iustin Pop
        continue
1261 abdf0113 Iustin Pop
      used_devs[minor] = state, line
1262 a8083063 Iustin Pop
1263 abdf0113 Iustin Pop
    return used_devs
1264 a8083063 Iustin Pop
1265 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
1266 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
1267 0834c866 Iustin Pop

1268 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1269 a8083063 Iustin Pop

1270 a8083063 Iustin Pop
    """
1271 abdf0113 Iustin Pop
    if minor is None:
1272 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
1273 cb999543 Iustin Pop
      self.attached = False
1274 a8083063 Iustin Pop
    else:
1275 abdf0113 Iustin Pop
      self.minor = minor
1276 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
1277 cb999543 Iustin Pop
      self.attached = True
1278 a8083063 Iustin Pop
1279 a8083063 Iustin Pop
  @staticmethod
1280 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
1281 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
1282 a8083063 Iustin Pop

1283 3bc145d8 Bernardo Dal Seno
    This currently only checks the size, which must be around
1284 abdf0113 Iustin Pop
    128MiB.
1285 a8083063 Iustin Pop

1286 a8083063 Iustin Pop
    """
1287 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1288 abdf0113 Iustin Pop
    if result.failed:
1289 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
1290 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
1291 a8083063 Iustin Pop
    try:
1292 abdf0113 Iustin Pop
      sectors = int(result.stdout)
1293 691744c4 Iustin Pop
    except (TypeError, ValueError):
1294 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
1295 c04bc777 Iustin Pop
    num_bytes = sectors * 512
1296 c04bc777 Iustin Pop
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
1297 c04bc777 Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (num_bytes / 1024 / 1024))
1298 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
1299 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
1300 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
1301 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
1302 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
1303 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
1304 c04bc777 Iustin Pop
    if num_bytes > 1024 * 1024 * 1024:
1305 c04bc777 Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (num_bytes / 1024 / 1024))
1306 a8083063 Iustin Pop
1307 abdf0113 Iustin Pop
  def Rename(self, new_id):
1308 abdf0113 Iustin Pop
    """Rename a device.
1309 a8083063 Iustin Pop

1310 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1311 a8083063 Iustin Pop

1312 a8083063 Iustin Pop
    """
1313 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1314 a8083063 Iustin Pop
1315 f3e513ad Iustin Pop
1316 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1317 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1318 a2cfdea2 Iustin Pop

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

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

1328 a2cfdea2 Iustin Pop
  """
1329 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1330 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1331 a2cfdea2 Iustin Pop
1332 cf8df3f3 Iustin Pop
  # timeout constants
1333 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
1334 cf8df3f3 Iustin Pop
1335 8a69b3a8 Andrea Spadaccini
  # command line options for barriers
1336 8a69b3a8 Andrea Spadaccini
  _DISABLE_DISK_OPTION = "--no-disk-barrier"  # -a
1337 8a69b3a8 Andrea Spadaccini
  _DISABLE_DRAIN_OPTION = "--no-disk-drain"   # -D
1338 8a69b3a8 Andrea Spadaccini
  _DISABLE_FLUSH_OPTION = "--no-disk-flushes" # -i
1339 8a69b3a8 Andrea Spadaccini
  _DISABLE_META_FLUSH_OPTION = "--no-md-flushes"  # -m
1340 8a69b3a8 Andrea Spadaccini
1341 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
1342 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1343 fc1dc9d7 Iustin Pop
      children = []
1344 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
1345 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1346 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
1347 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1348 310fbb64 Iustin Pop
    (self._lhost, self._lport,
1349 310fbb64 Iustin Pop
     self._rhost, self._rport,
1350 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
1351 310fbb64 Iustin Pop
    if children:
1352 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
1353 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
1354 310fbb64 Iustin Pop
        children = []
1355 94dcbdb0 Andrea Spadaccini
    super(DRBD8, self).__init__(unique_id, children, size, params)
1356 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1357 fcee765d Manuel Franceschini
    version = self._GetVersion(self._GetProcData())
1358 e687ec01 Michael Hanselmann
    if version["k_major"] != 8:
1359 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
1360 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
1361 d0c8c01d Iustin Pop
                  version["k_major"], version["k_minor"])
1362 a2cfdea2 Iustin Pop
1363 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1364 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1365 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1366 ffa1c0dc Iustin Pop
                       (unique_id,))
1367 a2cfdea2 Iustin Pop
    self.Attach()
1368 a2cfdea2 Iustin Pop
1369 a2cfdea2 Iustin Pop
  @classmethod
1370 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1371 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1372 a2cfdea2 Iustin Pop

1373 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1374 a2cfdea2 Iustin Pop

1375 a2cfdea2 Iustin Pop
    """
1376 18e4dee6 Iustin Pop
    # Zero the metadata first, in order to make sure drbdmeta doesn't
1377 18e4dee6 Iustin Pop
    # try to auto-detect existing filesystems or similar (see
1378 18e4dee6 Iustin Pop
    # http://code.google.com/p/ganeti/issues/detail?id=182); we only
1379 18e4dee6 Iustin Pop
    # care about the first 128MB of data in the device, even though it
1380 18e4dee6 Iustin Pop
    # can be bigger
1381 18e4dee6 Iustin Pop
    result = utils.RunCmd([constants.DD_CMD,
1382 18e4dee6 Iustin Pop
                           "if=/dev/zero", "of=%s" % dev_path,
1383 18e4dee6 Iustin Pop
                           "bs=1048576", "count=128", "oflag=direct"])
1384 18e4dee6 Iustin Pop
    if result.failed:
1385 18e4dee6 Iustin Pop
      _ThrowError("Can't wipe the meta device: %s", result.output)
1386 18e4dee6 Iustin Pop
1387 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1388 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1389 a2cfdea2 Iustin Pop
    if result.failed:
1390 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1391 a2cfdea2 Iustin Pop
1392 a2cfdea2 Iustin Pop
  @classmethod
1393 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1394 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1395 a2cfdea2 Iustin Pop

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

1399 a2cfdea2 Iustin Pop
    """
1400 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1401 a2cfdea2 Iustin Pop
1402 a2cfdea2 Iustin Pop
    highest = None
1403 a2cfdea2 Iustin Pop
    for line in data:
1404 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1405 a2cfdea2 Iustin Pop
      if match:
1406 a2cfdea2 Iustin Pop
        return int(match.group(1))
1407 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1408 a2cfdea2 Iustin Pop
      if match:
1409 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1410 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1411 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1412 a2cfdea2 Iustin Pop
      return 0
1413 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1414 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1415 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1416 a2cfdea2 Iustin Pop
    return highest + 1
1417 a2cfdea2 Iustin Pop
1418 a2cfdea2 Iustin Pop
  @classmethod
1419 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1420 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1421 a2cfdea2 Iustin Pop

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

1425 a2cfdea2 Iustin Pop
    """
1426 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1427 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1428 a2cfdea2 Iustin Pop
1429 a2cfdea2 Iustin Pop
    # pyparsing setup
1430 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1431 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1432 5a672c30 Manuel Franceschini
    lbracket = pyp.Literal("[").suppress()
1433 5a672c30 Manuel Franceschini
    rbracket = pyp.Literal("]").suppress()
1434 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1435 5a672c30 Manuel Franceschini
    colon = pyp.Literal(":").suppress()
1436 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1437 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1438 a2cfdea2 Iustin Pop
1439 e687ec01 Michael Hanselmann
    comment = pyp.Literal("#") + pyp.Optional(pyp.restOfLine)
1440 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1441 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1442 a2cfdea2 Iustin Pop
1443 3ccb3a64 Michael Hanselmann
    keyword = pyp.Word(pyp.alphanums + "-")
1444 a2cfdea2 Iustin Pop
1445 a2cfdea2 Iustin Pop
    # value types
1446 3ccb3a64 Michael Hanselmann
    value = pyp.Word(pyp.alphanums + "_-/.:")
1447 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1448 5a672c30 Manuel Franceschini
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1449 5a672c30 Manuel Franceschini
                 pyp.Word(pyp.nums + ".") + colon + number)
1450 5a672c30 Manuel Franceschini
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
1451 5a672c30 Manuel Franceschini
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
1452 5a672c30 Manuel Franceschini
                 pyp.Optional(rbracket) + colon + number)
1453 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1454 5a672c30 Manuel Franceschini
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
1455 01e2ce3a Iustin Pop
    # device name, extended syntax
1456 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1457 a2cfdea2 Iustin Pop
1458 a2cfdea2 Iustin Pop
    # a statement
1459 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1460 5a672c30 Manuel Franceschini
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
1461 01e2ce3a Iustin Pop
                         device_value) +
1462 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1463 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1464 a2cfdea2 Iustin Pop
1465 a2cfdea2 Iustin Pop
    # an entire section
1466 d0c8c01d Iustin Pop
    section_name = pyp.Word(pyp.alphas + "_")
1467 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1468 a2cfdea2 Iustin Pop
1469 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1470 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1471 a2cfdea2 Iustin Pop
1472 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1473 a2cfdea2 Iustin Pop
1474 a2cfdea2 Iustin Pop
    return bnf
1475 a2cfdea2 Iustin Pop
1476 a2cfdea2 Iustin Pop
  @classmethod
1477 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1478 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1479 a2cfdea2 Iustin Pop

1480 a2cfdea2 Iustin Pop
    """
1481 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1482 a2cfdea2 Iustin Pop
    if result.failed:
1483 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1484 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1485 3840729d Iustin Pop
      return None
1486 3840729d Iustin Pop
    return result.stdout
1487 3840729d Iustin Pop
1488 3840729d Iustin Pop
  @classmethod
1489 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1490 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1491 3840729d Iustin Pop

1492 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1493 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1494 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1495 3840729d Iustin Pop
    by _GetShowData.
1496 3840729d Iustin Pop

1497 3840729d Iustin Pop
    """
1498 3840729d Iustin Pop
    data = {}
1499 a2cfdea2 Iustin Pop
    if not out:
1500 a2cfdea2 Iustin Pop
      return data
1501 a2cfdea2 Iustin Pop
1502 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1503 a2cfdea2 Iustin Pop
    # run pyparse
1504 a2cfdea2 Iustin Pop
1505 a2cfdea2 Iustin Pop
    try:
1506 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1507 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1508 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1509 a2cfdea2 Iustin Pop
1510 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1511 a2cfdea2 Iustin Pop
    for section in results:
1512 a2cfdea2 Iustin Pop
      sname = section[0]
1513 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1514 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1515 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1516 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1517 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1518 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1519 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1520 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1521 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1522 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1523 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1524 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1525 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1526 a2cfdea2 Iustin Pop
    return data
1527 a2cfdea2 Iustin Pop
1528 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1529 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1530 a2cfdea2 Iustin Pop

1531 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1532 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1533 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1534 a2cfdea2 Iustin Pop
    device.
1535 a2cfdea2 Iustin Pop

1536 a2cfdea2 Iustin Pop
    """
1537 b00b95dd Iustin Pop
    if self._children:
1538 b00b95dd Iustin Pop
      backend, meta = self._children
1539 b00b95dd Iustin Pop
    else:
1540 b00b95dd Iustin Pop
      backend = meta = None
1541 b00b95dd Iustin Pop
1542 a2cfdea2 Iustin Pop
    if backend is not None:
1543 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1544 a2cfdea2 Iustin Pop
    else:
1545 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1546 b00b95dd Iustin Pop
1547 a2cfdea2 Iustin Pop
    if meta is not None:
1548 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1549 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1550 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1551 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1552 a2cfdea2 Iustin Pop
    else:
1553 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1554 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1555 a2cfdea2 Iustin Pop
    return retval
1556 a2cfdea2 Iustin Pop
1557 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1558 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1559 a2cfdea2 Iustin Pop

1560 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1561 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1562 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1563 a2cfdea2 Iustin Pop
    device.
1564 a2cfdea2 Iustin Pop

1565 a2cfdea2 Iustin Pop
    """
1566 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1567 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1568 a2cfdea2 Iustin Pop
      return True
1569 a2cfdea2 Iustin Pop
1570 a2cfdea2 Iustin Pop
    if self._lhost is None:
1571 a2cfdea2 Iustin Pop
      return False
1572 a2cfdea2 Iustin Pop
1573 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1574 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1575 a2cfdea2 Iustin Pop
      return False
1576 a2cfdea2 Iustin Pop
1577 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1578 a2cfdea2 Iustin Pop
    retval = (retval and
1579 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1580 a2cfdea2 Iustin Pop
    return retval
1581 a2cfdea2 Iustin Pop
1582 8a69b3a8 Andrea Spadaccini
  def _AssembleLocal(self, minor, backend, meta, size):
1583 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1584 a2cfdea2 Iustin Pop

1585 a2cfdea2 Iustin Pop
    """
1586 8a69b3a8 Andrea Spadaccini
    args = ["drbdsetup", self._DevPath(minor), "disk",
1587 f069addf Iustin Pop
            backend, meta, "0",
1588 f069addf Iustin Pop
            "-e", "detach",
1589 f069addf Iustin Pop
            "--create-device"]
1590 60bca04a Iustin Pop
    if size:
1591 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1592 8a69b3a8 Andrea Spadaccini
1593 8a69b3a8 Andrea Spadaccini
    version = self._GetVersion(self._GetProcData())
1594 8a69b3a8 Andrea Spadaccini
    vmaj = version["k_major"]
1595 8a69b3a8 Andrea Spadaccini
    vmin = version["k_minor"]
1596 8a69b3a8 Andrea Spadaccini
    vrel = version["k_point"]
1597 8a69b3a8 Andrea Spadaccini
1598 8a69b3a8 Andrea Spadaccini
    barrier_args = \
1599 8a69b3a8 Andrea Spadaccini
      self._ComputeDiskBarrierArgs(vmaj, vmin, vrel,
1600 ac00bf1b Andrea Spadaccini
                                   self.params[constants.LDP_BARRIERS],
1601 ac00bf1b Andrea Spadaccini
                                   self.params[constants.LDP_NO_META_FLUSH])
1602 8a69b3a8 Andrea Spadaccini
    args.extend(barrier_args)
1603 8a69b3a8 Andrea Spadaccini
1604 ad1dd4c7 Andrea Spadaccini
    if self.params[constants.LDP_DISK_CUSTOM]:
1605 ad1dd4c7 Andrea Spadaccini
      args.extend(shlex.split(self.params[constants.LDP_DISK_CUSTOM]))
1606 ad1dd4c7 Andrea Spadaccini
1607 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1608 a2cfdea2 Iustin Pop
    if result.failed:
1609 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1610 a2cfdea2 Iustin Pop
1611 8a69b3a8 Andrea Spadaccini
  @classmethod
1612 8a69b3a8 Andrea Spadaccini
  def _ComputeDiskBarrierArgs(cls, vmaj, vmin, vrel, disabled_barriers,
1613 5ae4945a Iustin Pop
                              disable_meta_flush):
1614 8a69b3a8 Andrea Spadaccini
    """Compute the DRBD command line parameters for disk barriers
1615 8a69b3a8 Andrea Spadaccini

1616 8a69b3a8 Andrea Spadaccini
    Returns a list of the disk barrier parameters as requested via the
1617 8a69b3a8 Andrea Spadaccini
    disabled_barriers and disable_meta_flush arguments, and according to the
1618 8a69b3a8 Andrea Spadaccini
    supported ones in the DRBD version vmaj.vmin.vrel
1619 8a69b3a8 Andrea Spadaccini

1620 8a69b3a8 Andrea Spadaccini
    If the desired option is unsupported, raises errors.BlockDeviceError.
1621 8a69b3a8 Andrea Spadaccini

1622 8a69b3a8 Andrea Spadaccini
    """
1623 8a69b3a8 Andrea Spadaccini
    disabled_barriers_set = frozenset(disabled_barriers)
1624 8a69b3a8 Andrea Spadaccini
    if not disabled_barriers_set in constants.DRBD_VALID_BARRIER_OPT:
1625 8a69b3a8 Andrea Spadaccini
      raise errors.BlockDeviceError("%s is not a valid option set for DRBD"
1626 8a69b3a8 Andrea Spadaccini
                                    " barriers" % disabled_barriers)
1627 8a69b3a8 Andrea Spadaccini
1628 8a69b3a8 Andrea Spadaccini
    args = []
1629 8a69b3a8 Andrea Spadaccini
1630 8a69b3a8 Andrea Spadaccini
    # The following code assumes DRBD 8.x, with x < 4 and x != 1 (DRBD 8.1.x
1631 8a69b3a8 Andrea Spadaccini
    # does not exist)
1632 8a69b3a8 Andrea Spadaccini
    if not vmaj == 8 and vmin in (0, 2, 3):
1633 8a69b3a8 Andrea Spadaccini
      raise errors.BlockDeviceError("Unsupported DRBD version: %d.%d.%d" %
1634 8a69b3a8 Andrea Spadaccini
                                    (vmaj, vmin, vrel))
1635 8a69b3a8 Andrea Spadaccini
1636 8a69b3a8 Andrea Spadaccini
    def _AppendOrRaise(option, min_version):
1637 8a69b3a8 Andrea Spadaccini
      """Helper for DRBD options"""
1638 8a69b3a8 Andrea Spadaccini
      if min_version is not None and vrel >= min_version:
1639 8a69b3a8 Andrea Spadaccini
        args.append(option)
1640 8a69b3a8 Andrea Spadaccini
      else:
1641 8a69b3a8 Andrea Spadaccini
        raise errors.BlockDeviceError("Could not use the option %s as the"
1642 8a69b3a8 Andrea Spadaccini
                                      " DRBD version %d.%d.%d does not support"
1643 8a69b3a8 Andrea Spadaccini
                                      " it." % (option, vmaj, vmin, vrel))
1644 8a69b3a8 Andrea Spadaccini
1645 8a69b3a8 Andrea Spadaccini
    # the minimum version for each feature is encoded via pairs of (minor
1646 8a69b3a8 Andrea Spadaccini
    # version -> x) where x is version in which support for the option was
1647 8a69b3a8 Andrea Spadaccini
    # introduced.
1648 8a69b3a8 Andrea Spadaccini
    meta_flush_supported = disk_flush_supported = {
1649 8a69b3a8 Andrea Spadaccini
      0: 12,
1650 8a69b3a8 Andrea Spadaccini
      2: 7,
1651 8a69b3a8 Andrea Spadaccini
      3: 0,
1652 8a69b3a8 Andrea Spadaccini
      }
1653 8a69b3a8 Andrea Spadaccini
1654 8a69b3a8 Andrea Spadaccini
    disk_drain_supported = {
1655 8a69b3a8 Andrea Spadaccini
      2: 7,
1656 8a69b3a8 Andrea Spadaccini
      3: 0,
1657 8a69b3a8 Andrea Spadaccini
      }
1658 8a69b3a8 Andrea Spadaccini
1659 8a69b3a8 Andrea Spadaccini
    disk_barriers_supported = {
1660 8a69b3a8 Andrea Spadaccini
      3: 0,
1661 8a69b3a8 Andrea Spadaccini
      }
1662 8a69b3a8 Andrea Spadaccini
1663 8a69b3a8 Andrea Spadaccini
    # meta flushes
1664 8a69b3a8 Andrea Spadaccini
    if disable_meta_flush:
1665 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_META_FLUSH_OPTION,
1666 8a69b3a8 Andrea Spadaccini
                     meta_flush_supported.get(vmin, None))
1667 8a69b3a8 Andrea Spadaccini
1668 8a69b3a8 Andrea Spadaccini
    # disk flushes
1669 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set:
1670 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_FLUSH_OPTION,
1671 8a69b3a8 Andrea Spadaccini
                     disk_flush_supported.get(vmin, None))
1672 8a69b3a8 Andrea Spadaccini
1673 8a69b3a8 Andrea Spadaccini
    # disk drain
1674 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set:
1675 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_DRAIN_OPTION,
1676 8a69b3a8 Andrea Spadaccini
                     disk_drain_supported.get(vmin, None))
1677 8a69b3a8 Andrea Spadaccini
1678 8a69b3a8 Andrea Spadaccini
    # disk barriers
1679 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set:
1680 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_DISK_OPTION,
1681 8a69b3a8 Andrea Spadaccini
                     disk_barriers_supported.get(vmin, None))
1682 8a69b3a8 Andrea Spadaccini
1683 8a69b3a8 Andrea Spadaccini
    return args
1684 8a69b3a8 Andrea Spadaccini
1685 6e9814a1 Andrea Spadaccini
  def _AssembleNet(self, minor, net_info, protocol,
1686 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1687 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1688 a2cfdea2 Iustin Pop

1689 a2cfdea2 Iustin Pop
    """
1690 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1691 52857176 Iustin Pop
    if None in net_info:
1692 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1693 52857176 Iustin Pop
      # sure its shutdown
1694 6e9814a1 Andrea Spadaccini
      self._ShutdownNet(minor)
1695 1063abd1 Iustin Pop
      return
1696 52857176 Iustin Pop
1697 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1698 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1699 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1700 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1701 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1702 7d585316 Iustin Pop
    # about its peer.
1703 8584e922 Andrea Spadaccini
    sync_errors = self._SetMinorSyncParams(minor, self.params)
1704 8584e922 Andrea Spadaccini
    if sync_errors:
1705 8584e922 Andrea Spadaccini
      _ThrowError("drbd%d: can't set the synchronization parameters: %s" %
1706 8584e922 Andrea Spadaccini
                  (minor, utils.CommaJoin(sync_errors)))
1707 7d585316 Iustin Pop
1708 8b312c1d Manuel Franceschini
    if netutils.IP6Address.IsValid(lhost):
1709 8b312c1d Manuel Franceschini
      if not netutils.IP6Address.IsValid(rhost):
1710 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1711 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1712 5a672c30 Manuel Franceschini
      family = "ipv6"
1713 8b312c1d Manuel Franceschini
    elif netutils.IP4Address.IsValid(lhost):
1714 8b312c1d Manuel Franceschini
      if not netutils.IP4Address.IsValid(rhost):
1715 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1716 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1717 5a672c30 Manuel Franceschini
      family = "ipv4"
1718 5a672c30 Manuel Franceschini
    else:
1719 5a672c30 Manuel Franceschini
      _ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
1720 5a672c30 Manuel Franceschini
1721 6e9814a1 Andrea Spadaccini
    args = ["drbdsetup", self._DevPath(minor), "net",
1722 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, lhost, lport),
1723 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, rhost, rport), protocol,
1724 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1725 f38478b2 Iustin Pop
            "-B", "consensus",
1726 ab6cc81c Iustin Pop
            "--create-device",
1727 f38478b2 Iustin Pop
            ]
1728 a2cfdea2 Iustin Pop
    if dual_pri:
1729 a2cfdea2 Iustin Pop
      args.append("-m")
1730 a2cfdea2 Iustin Pop
    if hmac and secret:
1731 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1732 ad1dd4c7 Andrea Spadaccini
1733 ad1dd4c7 Andrea Spadaccini
    if self.params[constants.LDP_NET_CUSTOM]:
1734 ad1dd4c7 Andrea Spadaccini
      args.extend(shlex.split(self.params[constants.LDP_NET_CUSTOM]))
1735 ad1dd4c7 Andrea Spadaccini
1736 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1737 a2cfdea2 Iustin Pop
    if result.failed:
1738 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1739 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1740 a2cfdea2 Iustin Pop
1741 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1742 6e9814a1 Andrea Spadaccini
      info = self._GetDevInfo(self._GetShowData(minor))
1743 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1744 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1745 def8e2f6 Michael Hanselmann
1746 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1747 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1748 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1749 def8e2f6 Michael Hanselmann
1750 def8e2f6 Michael Hanselmann
    try:
1751 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1752 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1753 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1754 a2cfdea2 Iustin Pop
1755 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1756 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1757 b00b95dd Iustin Pop

1758 b00b95dd Iustin Pop
    """
1759 b00b95dd Iustin Pop
    if self.minor is None:
1760 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1761 82463074 Iustin Pop
                  self._aminor)
1762 b00b95dd Iustin Pop
    if len(devices) != 2:
1763 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1764 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1765 03ece5f3 Iustin Pop
    if "local_dev" in info:
1766 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1767 b00b95dd Iustin Pop
    backend, meta = devices
1768 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1769 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1770 b00b95dd Iustin Pop
    backend.Open()
1771 b00b95dd Iustin Pop
    meta.Open()
1772 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1773 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1774 b00b95dd Iustin Pop
1775 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1776 b00b95dd Iustin Pop
    self._children = devices
1777 b00b95dd Iustin Pop
1778 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1779 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1780 b00b95dd Iustin Pop

1781 b00b95dd Iustin Pop
    """
1782 b00b95dd Iustin Pop
    if self.minor is None:
1783 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1784 82463074 Iustin Pop
                  self._aminor)
1785 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1786 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1787 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1788 03ece5f3 Iustin Pop
      return
1789 b00b95dd Iustin Pop
    if len(self._children) != 2:
1790 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1791 82463074 Iustin Pop
                  self._children)
1792 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1793 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1794 e739bd57 Iustin Pop
      return
1795 b00b95dd Iustin Pop
    if len(devices) != 2:
1796 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1797 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1798 e739bd57 Iustin Pop
      if dev != child.dev_path:
1799 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1800 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1801 b00b95dd Iustin Pop
1802 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1803 b00b95dd Iustin Pop
    self._children = []
1804 b00b95dd Iustin Pop
1805 7d585316 Iustin Pop
  @classmethod
1806 f2f57b6e Andrea Spadaccini
  def _SetMinorSyncParams(cls, minor, params):
1807 f2f57b6e Andrea Spadaccini
    """Set the parameters of the DRBD syncer.
1808 a2cfdea2 Iustin Pop

1809 7d585316 Iustin Pop
    This is the low-level implementation.
1810 7d585316 Iustin Pop

1811 7d585316 Iustin Pop
    @type minor: int
1812 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1813 f2f57b6e Andrea Spadaccini
    @type params: dict
1814 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1815 8584e922 Andrea Spadaccini
    @rtype: list
1816 8584e922 Andrea Spadaccini
    @return: a list of error messages
1817 7d585316 Iustin Pop

1818 a2cfdea2 Iustin Pop
    """
1819 f2f57b6e Andrea Spadaccini
1820 f2f57b6e Andrea Spadaccini
    args = ["drbdsetup", cls._DevPath(minor), "syncer"]
1821 f2f57b6e Andrea Spadaccini
    if params[constants.LDP_DYNAMIC_RESYNC]:
1822 f2f57b6e Andrea Spadaccini
      version = cls._GetVersion(cls._GetProcData())
1823 f2f57b6e Andrea Spadaccini
      vmin = version["k_minor"]
1824 f2f57b6e Andrea Spadaccini
      vrel = version["k_point"]
1825 f2f57b6e Andrea Spadaccini
1826 f2f57b6e Andrea Spadaccini
      # By definition we are using 8.x, so just check the rest of the version
1827 f2f57b6e Andrea Spadaccini
      # number
1828 f2f57b6e Andrea Spadaccini
      if vmin != 3 or vrel < 9:
1829 8584e922 Andrea Spadaccini
        msg = ("The current DRBD version (8.%d.%d) does not support the "
1830 8584e922 Andrea Spadaccini
               "dynamic resync speed controller" % (vmin, vrel))
1831 8584e922 Andrea Spadaccini
        logging.error(msg)
1832 8584e922 Andrea Spadaccini
        return [msg]
1833 8584e922 Andrea Spadaccini
1834 8584e922 Andrea Spadaccini
      if params[constants.LDP_PLAN_AHEAD] == 0:
1835 8584e922 Andrea Spadaccini
        msg = ("A value of 0 for c-plan-ahead disables the dynamic sync speed"
1836 8584e922 Andrea Spadaccini
               " controller at DRBD level. If you want to disable it, please"
1837 8584e922 Andrea Spadaccini
               " set the dynamic-resync disk parameter to False.")
1838 8584e922 Andrea Spadaccini
        logging.error(msg)
1839 8584e922 Andrea Spadaccini
        return [msg]
1840 f2f57b6e Andrea Spadaccini
1841 f2f57b6e Andrea Spadaccini
      # add the c-* parameters to args
1842 8584e922 Andrea Spadaccini
      args.extend(["--c-plan-ahead", params[constants.LDP_PLAN_AHEAD],
1843 8584e922 Andrea Spadaccini
                   "--c-fill-target", params[constants.LDP_FILL_TARGET],
1844 8584e922 Andrea Spadaccini
                   "--c-delay-target", params[constants.LDP_DELAY_TARGET],
1845 8584e922 Andrea Spadaccini
                   "--c-max-rate", params[constants.LDP_MAX_RATE],
1846 8584e922 Andrea Spadaccini
                   "--c-min-rate", params[constants.LDP_MIN_RATE],
1847 5ae4945a Iustin Pop
                   ])
1848 f2f57b6e Andrea Spadaccini
1849 f2f57b6e Andrea Spadaccini
    else:
1850 f2f57b6e Andrea Spadaccini
      args.extend(["-r", "%d" % params[constants.LDP_RESYNC_RATE]])
1851 f2f57b6e Andrea Spadaccini
1852 f2f57b6e Andrea Spadaccini
    args.append("--create-device")
1853 f2f57b6e Andrea Spadaccini
    result = utils.RunCmd(args)
1854 a2cfdea2 Iustin Pop
    if result.failed:
1855 8584e922 Andrea Spadaccini
      msg = ("Can't change syncer rate: %s - %s" %
1856 8584e922 Andrea Spadaccini
             (result.fail_reason, result.output))
1857 8584e922 Andrea Spadaccini
      logging.error(msg)
1858 e0b57a5c Michael Hanselmann
      return [msg]
1859 8584e922 Andrea Spadaccini
1860 8584e922 Andrea Spadaccini
    return []
1861 7d585316 Iustin Pop
1862 f2f57b6e Andrea Spadaccini
  def SetSyncParams(self, params):
1863 f2f57b6e Andrea Spadaccini
    """Set the synchronization parameters of the DRBD syncer.
1864 7d585316 Iustin Pop

1865 f2f57b6e Andrea Spadaccini
    @type params: dict
1866 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1867 8584e922 Andrea Spadaccini
    @rtype: list
1868 8584e922 Andrea Spadaccini
    @return: a list of error messages, emitted both by the current node and by
1869 8584e922 Andrea Spadaccini
    children. An empty list means no errors
1870 7d585316 Iustin Pop

1871 7d585316 Iustin Pop
    """
1872 7d585316 Iustin Pop
    if self.minor is None:
1873 8584e922 Andrea Spadaccini
      err = "Not attached during SetSyncParams"
1874 8584e922 Andrea Spadaccini
      logging.info(err)
1875 8584e922 Andrea Spadaccini
      return [err]
1876 8584e922 Andrea Spadaccini
1877 f2f57b6e Andrea Spadaccini
    children_result = super(DRBD8, self).SetSyncParams(params)
1878 8584e922 Andrea Spadaccini
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
1879 8584e922 Andrea Spadaccini
    return children_result
1880 a2cfdea2 Iustin Pop
1881 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
1882 a3fffcc6 René Nussbaumer
    """Pauses or resumes the sync of a DRBD device.
1883 a3fffcc6 René Nussbaumer

1884 a3fffcc6 René Nussbaumer
    @param pause: Wether to pause or resume
1885 a3fffcc6 René Nussbaumer
    @return: the success of the operation
1886 a3fffcc6 René Nussbaumer

1887 a3fffcc6 René Nussbaumer
    """
1888 a3fffcc6 René Nussbaumer
    if self.minor is None:
1889 a3fffcc6 René Nussbaumer
      logging.info("Not attached during PauseSync")
1890 a3fffcc6 René Nussbaumer
      return False
1891 a3fffcc6 René Nussbaumer
1892 a3fffcc6 René Nussbaumer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
1893 a3fffcc6 René Nussbaumer
1894 a3fffcc6 René Nussbaumer
    if pause:
1895 a3fffcc6 René Nussbaumer
      cmd = "pause-sync"
1896 a3fffcc6 René Nussbaumer
    else:
1897 a3fffcc6 René Nussbaumer
      cmd = "resume-sync"
1898 a3fffcc6 René Nussbaumer
1899 a3fffcc6 René Nussbaumer
    result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
1900 a3fffcc6 René Nussbaumer
    if result.failed:
1901 a3fffcc6 René Nussbaumer
      logging.error("Can't %s: %s - %s", cmd,
1902 a3fffcc6 René Nussbaumer
                    result.fail_reason, result.output)
1903 a3fffcc6 René Nussbaumer
    return not result.failed and children_result
1904 a3fffcc6 René Nussbaumer
1905 6b90c22e Iustin Pop
  def GetProcStatus(self):
1906 6b90c22e Iustin Pop
    """Return device data from /proc.
1907 6b90c22e Iustin Pop

1908 6b90c22e Iustin Pop
    """
1909 6b90c22e Iustin Pop
    if self.minor is None:
1910 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1911 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1912 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1913 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1914 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1915 6b90c22e Iustin Pop
1916 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1917 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1918 a2cfdea2 Iustin Pop

1919 a2cfdea2 Iustin Pop

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

1924 0834c866 Iustin Pop

1925 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1926 0834c866 Iustin Pop
    network not connected or local disk missing.
1927 0834c866 Iustin Pop

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

1931 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1932 c41eea6e Iustin Pop

1933 a2cfdea2 Iustin Pop
    """
1934 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1935 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1936 96acbc09 Michael Hanselmann
1937 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1938 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1939 f208978a Michael Hanselmann
1940 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1941 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1942 f208978a Michael Hanselmann
    elif stats.is_diskless:
1943 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1944 f208978a Michael Hanselmann
    else:
1945 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1946 96acbc09 Michael Hanselmann
1947 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1948 96acbc09 Michael Hanselmann
                                  major=self.major,
1949 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1950 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1951 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1952 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1953 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1954 a2cfdea2 Iustin Pop
1955 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1956 a2cfdea2 Iustin Pop
    """Make the local state primary.
1957 a2cfdea2 Iustin Pop

1958 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1959 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1960 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1961 f860ff4e Guido Trotter
    is mandatory.
1962 a2cfdea2 Iustin Pop

1963 a2cfdea2 Iustin Pop
    """
1964 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1965 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1966 a2cfdea2 Iustin Pop
      return False
1967 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1968 a2cfdea2 Iustin Pop
    if force:
1969 a2cfdea2 Iustin Pop
      cmd.append("-o")
1970 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1971 a2cfdea2 Iustin Pop
    if result.failed:
1972 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1973 82463074 Iustin Pop
                  result.output)
1974 a2cfdea2 Iustin Pop
1975 a2cfdea2 Iustin Pop
  def Close(self):
1976 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1977 a2cfdea2 Iustin Pop

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

1980 a2cfdea2 Iustin Pop
    """
1981 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1982 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1983 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1984 a2cfdea2 Iustin Pop
    if result.failed:
1985 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1986 82463074 Iustin Pop
                  self.minor, result.output)
1987 a2cfdea2 Iustin Pop
1988 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1989 cf8df3f3 Iustin Pop
    """Removes network configuration.
1990 cf8df3f3 Iustin Pop

1991 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1992 cf8df3f3 Iustin Pop

1993 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1994 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1995 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1996 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1997 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1998 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1999 cf8df3f3 Iustin Pop
    exception.
2000 cf8df3f3 Iustin Pop

2001 cf8df3f3 Iustin Pop
    """
2002 cf8df3f3 Iustin Pop
    if self.minor is None:
2003 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
2004 cf8df3f3 Iustin Pop
2005 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
2006 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
2007 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
2008 cf8df3f3 Iustin Pop
2009 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
2010 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
2011 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
2012 cf8df3f3 Iustin Pop
2013 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
2014 def8e2f6 Michael Hanselmann
2015 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
2016 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
2017 def8e2f6 Michael Hanselmann
        return
2018 def8e2f6 Michael Hanselmann
2019 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
2020 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
2021 def8e2f6 Michael Hanselmann
      # forgotten
2022 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
2023 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
2024 def8e2f6 Michael Hanselmann
2025 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
2026 def8e2f6 Michael Hanselmann
2027 def8e2f6 Michael Hanselmann
    # Keep start time
2028 def8e2f6 Michael Hanselmann
    start_time = time.time()
2029 def8e2f6 Michael Hanselmann
2030 def8e2f6 Michael Hanselmann
    try:
2031 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
2032 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
2033 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
2034 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
2035 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
2036 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
2037 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
2038 cf8df3f3 Iustin Pop
      else:
2039 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
2040 def8e2f6 Michael Hanselmann
2041 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
2042 cf8df3f3 Iustin Pop
2043 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
2044 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
2045 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
2046 82463074 Iustin Pop
                   self.minor, reconfig_time)
2047 cf8df3f3 Iustin Pop
2048 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
2049 cf8df3f3 Iustin Pop
    """Reconnects the network.
2050 cf8df3f3 Iustin Pop

2051 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
2052 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
2053 cf8df3f3 Iustin Pop
    but have valid network configuration data.
2054 cf8df3f3 Iustin Pop

2055 cf8df3f3 Iustin Pop
    Args:
2056 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
2057 cf8df3f3 Iustin Pop

2058 cf8df3f3 Iustin Pop
    """
2059 cf8df3f3 Iustin Pop
    if self.minor is None:
2060 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
2061 cf8df3f3 Iustin Pop
2062 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
2063 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
2064 cf8df3f3 Iustin Pop
2065 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
2066 cf8df3f3 Iustin Pop
2067 cf8df3f3 Iustin Pop
    if not status.is_standalone:
2068 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
2069 cf8df3f3 Iustin Pop
2070 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
2071 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
2072 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
2073 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2074 cf8df3f3 Iustin Pop
2075 a2cfdea2 Iustin Pop
  def Attach(self):
2076 2d0c8319 Iustin Pop
    """Check if our minor is configured.
2077 2d0c8319 Iustin Pop

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

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

2085 2d0c8319 Iustin Pop
    """
2086 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
2087 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
2088 2d0c8319 Iustin Pop
      minor = self._aminor
2089 2d0c8319 Iustin Pop
    else:
2090 2d0c8319 Iustin Pop
      minor = None
2091 2d0c8319 Iustin Pop
2092 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
2093 2d0c8319 Iustin Pop
    return minor is not None
2094 2d0c8319 Iustin Pop
2095 2d0c8319 Iustin Pop
  def Assemble(self):
2096 2d0c8319 Iustin Pop
    """Assemble the drbd.
2097 2d0c8319 Iustin Pop

2098 2d0c8319 Iustin Pop
    Method:
2099 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
2100 2d0c8319 Iustin Pop
        our config
2101 2d0c8319 Iustin Pop
      - if not, we create it from zero
2102 d529599f Andrea Spadaccini
      - anyway, set the device parameters
2103 2d0c8319 Iustin Pop

2104 2d0c8319 Iustin Pop
    """
2105 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
2106 2d0c8319 Iustin Pop
2107 2d0c8319 Iustin Pop
    self.Attach()
2108 2d0c8319 Iustin Pop
    if self.minor is None:
2109 2d0c8319 Iustin Pop
      # local device completely unconfigured
2110 1063abd1 Iustin Pop
      self._FastAssemble()
2111 2d0c8319 Iustin Pop
    else:
2112 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
2113 2d0c8319 Iustin Pop
      # the device
2114 1063abd1 Iustin Pop
      self._SlowAssemble()
2115 2d0c8319 Iustin Pop
2116 8584e922 Andrea Spadaccini
    sync_errors = self.SetSyncParams(self.params)
2117 8584e922 Andrea Spadaccini
    if sync_errors:
2118 8584e922 Andrea Spadaccini
      _ThrowError("drbd%d: can't set the synchronization parameters: %s" %
2119 8584e922 Andrea Spadaccini
                  (self.minor, utils.CommaJoin(sync_errors)))
2120 d529599f Andrea Spadaccini
2121 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
2122 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
2123 a2cfdea2 Iustin Pop

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

2128 a2cfdea2 Iustin Pop
    """
2129 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
2130 b459a848 Andrea Spadaccini
    # pylint: disable=W0631
2131 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
2132 a1578d63 Iustin Pop
    for minor in (self._aminor,):
2133 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
2134 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
2135 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
2136 1063abd1 Iustin Pop
2137 a2cfdea2 Iustin Pop
      if match_l and match_r:
2138 1063abd1 Iustin Pop
        # everything matches
2139 a2cfdea2 Iustin Pop
        break
2140 1063abd1 Iustin Pop
2141 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
2142 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
2143 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
2144 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2145 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2146 1063abd1 Iustin Pop
          break
2147 1063abd1 Iustin Pop
        else:
2148 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
2149 1063abd1 Iustin Pop
                      " show' disagrees", minor)
2150 1063abd1 Iustin Pop
2151 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
2152 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
2153 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
2154 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
2155 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2156 1063abd1 Iustin Pop
          break
2157 1063abd1 Iustin Pop
        else:
2158 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
2159 1063abd1 Iustin Pop
                      " show' disagrees", minor)
2160 bf25af3b Iustin Pop
2161 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
2162 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
2163 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
2164 bf25af3b Iustin Pop
      # configuration
2165 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
2166 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
2167 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
2168 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
2169 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
2170 9cdbe77f Iustin Pop
        # reconnect to our correct one
2171 1063abd1 Iustin Pop
        try:
2172 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
2173 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
2174 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
2175 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
2176 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
2177 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
2178 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
2179 9cdbe77f Iustin Pop
        # None)
2180 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
2181 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2182 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2183 9cdbe77f Iustin Pop
          break
2184 1063abd1 Iustin Pop
        else:
2185 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
2186 1063abd1 Iustin Pop
                      " show' disagrees", minor)
2187 9cdbe77f Iustin Pop
2188 a2cfdea2 Iustin Pop
    else:
2189 a2cfdea2 Iustin Pop
      minor = None
2190 a2cfdea2 Iustin Pop
2191 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2192 1063abd1 Iustin Pop
    if minor is None:
2193 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
2194 1063abd1 Iustin Pop
                  self._aminor)
2195 a2cfdea2 Iustin Pop
2196 2d0c8319 Iustin Pop
  def _FastAssemble(self):
2197 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
2198 a2cfdea2 Iustin Pop

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

2201 a2cfdea2 Iustin Pop
    """
2202 a1578d63 Iustin Pop
    minor = self._aminor
2203 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
2204 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
2205 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
2206 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
2207 1063abd1 Iustin Pop
      self._AssembleNet(minor,
2208 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
2209 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
2210 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2211 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2212 a2cfdea2 Iustin Pop
2213 a2cfdea2 Iustin Pop
  @classmethod
2214 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
2215 b00b95dd Iustin Pop
    """Detach from the local device.
2216 b00b95dd Iustin Pop

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

2220 b00b95dd Iustin Pop
    """
2221 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2222 b00b95dd Iustin Pop
    if result.failed:
2223 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
2224 b00b95dd Iustin Pop
2225 b00b95dd Iustin Pop
  @classmethod
2226 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2227 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2228 f3e513ad Iustin Pop

2229 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2230 f3e513ad Iustin Pop

2231 f3e513ad Iustin Pop
    """
2232 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2233 a8459f1c Iustin Pop
    if result.failed:
2234 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
2235 f3e513ad Iustin Pop
2236 f3e513ad Iustin Pop
  @classmethod
2237 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2238 a2cfdea2 Iustin Pop
    """Deactivate the device.
2239 a2cfdea2 Iustin Pop

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

2242 a2cfdea2 Iustin Pop
    """
2243 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2244 a2cfdea2 Iustin Pop
    if result.failed:
2245 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
2246 33bc6f01 Iustin Pop
                  minor, result.output)
2247 a2cfdea2 Iustin Pop
2248 a2cfdea2 Iustin Pop
  def Shutdown(self):
2249 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2250 a2cfdea2 Iustin Pop

2251 a2cfdea2 Iustin Pop
    """
2252 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2253 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
2254 746f7476 Iustin Pop
      return
2255 746f7476 Iustin Pop
    minor = self.minor
2256 a2cfdea2 Iustin Pop
    self.minor = None
2257 a2cfdea2 Iustin Pop
    self.dev_path = None
2258 746f7476 Iustin Pop
    self._ShutdownAll(minor)
2259 a2cfdea2 Iustin Pop
2260 a2cfdea2 Iustin Pop
  def Remove(self):
2261 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2262 a2cfdea2 Iustin Pop

2263 a2cfdea2 Iustin Pop
    """
2264 0c6c04ec Iustin Pop
    self.Shutdown()
2265 a2cfdea2 Iustin Pop
2266 a2cfdea2 Iustin Pop
  @classmethod
2267 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2268 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2269 a2cfdea2 Iustin Pop

2270 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2271 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2272 a2cfdea2 Iustin Pop

2273 a2cfdea2 Iustin Pop
    """
2274 a2cfdea2 Iustin Pop
    if len(children) != 2:
2275 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2276 ee1478e5 Bernardo Dal Seno
    if excl_stor:
2277 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("DRBD device requested with"
2278 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
2279 767d52d3 Iustin Pop
    # check that the minor is unused
2280 767d52d3 Iustin Pop
    aminor = unique_id[4]
2281 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
2282 767d52d3 Iustin Pop
    if aminor in proc_info:
2283 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
2284 767d52d3 Iustin Pop
      in_use = status.is_in_use
2285 767d52d3 Iustin Pop
    else:
2286 767d52d3 Iustin Pop
      in_use = False
2287 767d52d3 Iustin Pop
    if in_use:
2288 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
2289 a2cfdea2 Iustin Pop
    meta = children[1]
2290 a2cfdea2 Iustin Pop
    meta.Assemble()
2291 a2cfdea2 Iustin Pop
    if not meta.Attach():
2292 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
2293 33bc6f01 Iustin Pop
                  aminor, meta)
2294 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
2295 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
2296 94dcbdb0 Andrea Spadaccini
    return cls(unique_id, children, size, params)
2297 a2cfdea2 Iustin Pop
2298 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2299 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
2300 1005d816 Iustin Pop

2301 1005d816 Iustin Pop
    """
2302 1005d816 Iustin Pop
    if self.minor is None:
2303 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
2304 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
2305 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
2306 cad0723b Iustin Pop
    self._children[0].Grow(amount, dryrun, backingstore)
2307 cad0723b Iustin Pop
    if dryrun or backingstore:
2308 cad0723b Iustin Pop
      # DRBD does not support dry-run mode and is not backing storage,
2309 cad0723b Iustin Pop
      # so we'll return here
2310 7fe23d47 Iustin Pop
      return
2311 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
2312 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
2313 1005d816 Iustin Pop
    if result.failed:
2314 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
2315 1005d816 Iustin Pop
2316 a8083063 Iustin Pop
2317 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
2318 6f695a2e Manuel Franceschini
  """File device.
2319 abdf0113 Iustin Pop

2320 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
2321 6f695a2e Manuel Franceschini

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

2324 6f695a2e Manuel Franceschini
  """
2325 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2326 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
2327 6f695a2e Manuel Franceschini

2328 6f695a2e Manuel Franceschini
    """
2329 6f695a2e Manuel Franceschini
    if children:
2330 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
2331 94dcbdb0 Andrea Spadaccini
    super(FileStorage, self).__init__(unique_id, children, size, params)
2332 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2333 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2334 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
2335 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
2336 5e09a309 Michael Hanselmann
2337 5e09a309 Michael Hanselmann
    CheckFileStoragePath(self.dev_path)
2338 5e09a309 Michael Hanselmann
2339 ecb091e3 Iustin Pop
    self.Attach()
2340 6f695a2e Manuel Franceschini
2341 6f695a2e Manuel Franceschini
  def Assemble(self):
2342 6f695a2e Manuel Franceschini
    """Assemble the device.
2343 6f695a2e Manuel Franceschini

2344 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
2345 6f695a2e Manuel Franceschini

2346 6f695a2e Manuel Franceschini
    """
2347 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
2348 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
2349 6f695a2e Manuel Franceschini
2350 6f695a2e Manuel Franceschini
  def Shutdown(self):
2351 6f695a2e Manuel Franceschini
    """Shutdown the device.
2352 6f695a2e Manuel Franceschini

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

2356 6f695a2e Manuel Franceschini
    """
2357 746f7476 Iustin Pop
    pass
2358 6f695a2e Manuel Franceschini
2359 6f695a2e Manuel Franceschini
  def Open(self, force=False):
2360 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
2361 6f695a2e Manuel Franceschini

2362 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2363 6f695a2e Manuel Franceschini

2364 6f695a2e Manuel Franceschini
    """
2365 6f695a2e Manuel Franceschini
    pass
2366 6f695a2e Manuel Franceschini
2367 6f695a2e Manuel Franceschini
  def Close(self):
2368 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
2369 6f695a2e Manuel Franceschini

2370 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2371 6f695a2e Manuel Franceschini

2372 6f695a2e Manuel Franceschini
    """
2373 6f695a2e Manuel Franceschini
    pass
2374 6f695a2e Manuel Franceschini
2375 6f695a2e Manuel Franceschini
  def Remove(self):
2376 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
2377 6f695a2e Manuel Franceschini

2378 c41eea6e Iustin Pop
    @rtype: boolean
2379 c41eea6e Iustin Pop
    @return: True if the removal was successful
2380 6f695a2e Manuel Franceschini

2381 6f695a2e Manuel Franceschini
    """
2382 6f695a2e Manuel Franceschini
    try:
2383 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
2384 6f695a2e Manuel Franceschini
    except OSError, err:
2385 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
2386 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
2387 6f695a2e Manuel Franceschini
2388 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
2389 bbe4cc16 Iustin Pop
    """Renames the file.
2390 bbe4cc16 Iustin Pop

2391 bbe4cc16 Iustin Pop
    """
2392 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
2393 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
2394 bbe4cc16 Iustin Pop
2395 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2396 bbe4cc16 Iustin Pop
    """Grow the file
2397 bbe4cc16 Iustin Pop

2398 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2399 bbe4cc16 Iustin Pop

2400 bbe4cc16 Iustin Pop
    """
2401 cad0723b Iustin Pop
    if not backingstore:
2402 cad0723b Iustin Pop
      return
2403 91e2d9ec Guido Trotter
    # Check that the file exists
2404 91e2d9ec Guido Trotter
    self.Assemble()
2405 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
2406 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
2407 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
2408 7fe23d47 Iustin Pop
    # We can't really simulate the growth
2409 7fe23d47 Iustin Pop
    if dryrun:
2410 7fe23d47 Iustin Pop
      return
2411 91e2d9ec Guido Trotter
    try:
2412 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
2413 91e2d9ec Guido Trotter
      f.truncate(new_size)
2414 91e2d9ec Guido Trotter
      f.close()
2415 91e2d9ec Guido Trotter
    except EnvironmentError, err:
2416 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
2417 bbe4cc16 Iustin Pop
2418 6f695a2e Manuel Franceschini
  def Attach(self):
2419 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2420 6f695a2e Manuel Franceschini

2421 6f695a2e Manuel Franceschini
    Check if this file already exists.
2422 6f695a2e Manuel Franceschini

2423 c41eea6e Iustin Pop
    @rtype: boolean
2424 c41eea6e Iustin Pop
    @return: True if file exists
2425 6f695a2e Manuel Franceschini

2426 6f695a2e Manuel Franceschini
    """
2427 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
2428 ecb091e3 Iustin Pop
    return self.attached
2429 6f695a2e Manuel Franceschini
2430 fcff3897 Iustin Pop
  def GetActualSize(self):
2431 fcff3897 Iustin Pop
    """Return the actual disk size.
2432 fcff3897 Iustin Pop

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

2435 fcff3897 Iustin Pop
    """
2436 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2437 fcff3897 Iustin Pop
    try:
2438 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2439 fcff3897 Iustin Pop
      return st.st_size
2440 fcff3897 Iustin Pop
    except OSError, err:
2441 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2442 fcff3897 Iustin Pop
2443 6f695a2e Manuel Franceschini
  @classmethod
2444 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2445 6f695a2e Manuel Franceschini
    """Create a new file.
2446 6f695a2e Manuel Franceschini

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

2449 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2450 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2451 6f695a2e Manuel Franceschini

2452 6f695a2e Manuel Franceschini
    """
2453 ee1478e5 Bernardo Dal Seno
    if excl_stor:
2454 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("FileStorage device requested with"
2455 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
2456 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2457 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2458 5e09a309 Michael Hanselmann
2459 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2460 5e09a309 Michael Hanselmann
2461 5e09a309 Michael Hanselmann
    CheckFileStoragePath(dev_path)
2462 5e09a309 Michael Hanselmann
2463 6f695a2e Manuel Franceschini
    try:
2464 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
2465 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
2466 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2467 6f695a2e Manuel Franceschini
      f.close()
2468 cdeefd9b Guido Trotter
    except EnvironmentError, err:
2469 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
2470 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
2471 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
2472 6f695a2e Manuel Franceschini
2473 94dcbdb0 Andrea Spadaccini
    return FileStorage(unique_id, children, size, params)
2474 6f695a2e Manuel Franceschini
2475 6f695a2e Manuel Franceschini
2476 b6135bbc Apollon Oikonomopoulos
class PersistentBlockDevice(BlockDev):
2477 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
2478 b6135bbc Apollon Oikonomopoulos

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

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

2485 b6135bbc Apollon Oikonomopoulos
  """
2486 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2487 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
2488 b6135bbc Apollon Oikonomopoulos

2489 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2490 b6135bbc Apollon Oikonomopoulos

2491 b6135bbc Apollon Oikonomopoulos
    """
2492 94dcbdb0 Andrea Spadaccini
    super(PersistentBlockDevice, self).__init__(unique_id, children, size,
2493 94dcbdb0 Andrea Spadaccini
                                                params)
2494 b6135bbc Apollon Oikonomopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2495 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2496 b6135bbc Apollon Oikonomopoulos
    self.dev_path = unique_id[1]
2497 d0c8c01d Iustin Pop
    if not os.path.realpath(self.dev_path).startswith("/dev/"):
2498 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Full path '%s' lies outside /dev" %
2499 b6135bbc Apollon Oikonomopoulos
                              os.path.realpath(self.dev_path))
2500 b6135bbc Apollon Oikonomopoulos
    # TODO: this is just a safety guard checking that we only deal with devices
2501 b6135bbc Apollon Oikonomopoulos
    # we know how to handle. In the future this will be integrated with
2502 b6135bbc Apollon Oikonomopoulos
    # external storage backends and possible values will probably be collected
2503 b6135bbc Apollon Oikonomopoulos
    # from the cluster configuration.
2504 b6135bbc Apollon Oikonomopoulos
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
2505 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Got persistent block device of invalid type: %s" %
2506 b6135bbc Apollon Oikonomopoulos
                       unique_id[0])
2507 b6135bbc Apollon Oikonomopoulos
2508 b6135bbc Apollon Oikonomopoulos
    self.major = self.minor = None
2509 b6135bbc Apollon Oikonomopoulos
    self.Attach()
2510 b6135bbc Apollon Oikonomopoulos
2511 b6135bbc Apollon Oikonomopoulos
  @classmethod
2512 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2513 b6135bbc Apollon Oikonomopoulos
    """Create a new device
2514 b6135bbc Apollon Oikonomopoulos

2515 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2516 b6135bbc Apollon Oikonomopoulos

2517 b6135bbc Apollon Oikonomopoulos
    """
2518 ee1478e5 Bernardo Dal Seno
    if excl_stor:
2519 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("Persistent block device requested with"
2520 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
2521 94dcbdb0 Andrea Spadaccini
    return PersistentBlockDevice(unique_id, children, 0, params)
2522 b6135bbc Apollon Oikonomopoulos
2523 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
2524 b6135bbc Apollon Oikonomopoulos
    """Remove a device
2525 b6135bbc Apollon Oikonomopoulos

2526 b6135bbc Apollon Oikonomopoulos
    This is a noop
2527 b6135bbc Apollon Oikonomopoulos

2528 b6135bbc Apollon Oikonomopoulos
    """
2529 b6135bbc Apollon Oikonomopoulos
    pass
2530 b6135bbc Apollon Oikonomopoulos
2531 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
2532 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
2533 b6135bbc Apollon Oikonomopoulos

2534 b6135bbc Apollon Oikonomopoulos
    """
2535 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Rename is not supported for PersistentBlockDev storage")
2536 b6135bbc Apollon Oikonomopoulos
2537 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
2538 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
2539 b6135bbc Apollon Oikonomopoulos

2540 b6135bbc Apollon Oikonomopoulos

2541 b6135bbc Apollon Oikonomopoulos
    """
2542 b6135bbc Apollon Oikonomopoulos
    self.attached = False
2543 b6135bbc Apollon Oikonomopoulos
    try:
2544 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
2545 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
2546 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2547 b6135bbc Apollon Oikonomopoulos
      return False
2548 b6135bbc Apollon Oikonomopoulos
2549 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
2550 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
2551 b6135bbc Apollon Oikonomopoulos
      return False
2552 b6135bbc Apollon Oikonomopoulos
2553 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
2554 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
2555 b6135bbc Apollon Oikonomopoulos
    self.attached = True
2556 b6135bbc Apollon Oikonomopoulos
2557 b6135bbc Apollon Oikonomopoulos
    return True
2558 b6135bbc Apollon Oikonomopoulos
2559 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
2560 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
2561 b6135bbc Apollon Oikonomopoulos

2562 b6135bbc Apollon Oikonomopoulos
    """
2563 b6135bbc Apollon Oikonomopoulos
    pass
2564 b6135bbc Apollon Oikonomopoulos
2565 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
2566 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
2567 b6135bbc Apollon Oikonomopoulos

2568 b6135bbc Apollon Oikonomopoulos
    """
2569 b6135bbc Apollon Oikonomopoulos
    pass
2570 b6135bbc Apollon Oikonomopoulos
2571 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
2572 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
2573 b6135bbc Apollon Oikonomopoulos

2574 b6135bbc Apollon Oikonomopoulos
    """
2575 b6135bbc Apollon Oikonomopoulos
    pass
2576 b6135bbc Apollon Oikonomopoulos
2577 b6135bbc Apollon Oikonomopoulos
  def Close(self):
2578 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
2579 b6135bbc Apollon Oikonomopoulos

2580 b6135bbc Apollon Oikonomopoulos
    """
2581 b6135bbc Apollon Oikonomopoulos
    pass
2582 b6135bbc Apollon Oikonomopoulos
2583 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2584 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
2585 b6135bbc Apollon Oikonomopoulos

2586 b6135bbc Apollon Oikonomopoulos
    """
2587 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2588 b6135bbc Apollon Oikonomopoulos
2589 b6135bbc Apollon Oikonomopoulos
2590 7181fba0 Constantinos Venetsanopoulos
class RADOSBlockDevice(BlockDev):
2591 7181fba0 Constantinos Venetsanopoulos
  """A RADOS Block Device (rbd).
2592 7181fba0 Constantinos Venetsanopoulos

2593 7181fba0 Constantinos Venetsanopoulos
  This class implements the RADOS Block Device for the backend. You need
2594 7181fba0 Constantinos Venetsanopoulos
  the rbd kernel driver, the RADOS Tools and a working RADOS cluster for
2595 7181fba0 Constantinos Venetsanopoulos
  this to be functional.
2596 7181fba0 Constantinos Venetsanopoulos

2597 7181fba0 Constantinos Venetsanopoulos
  """
2598 7181fba0 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
2599 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
2600 7181fba0 Constantinos Venetsanopoulos

2601 7181fba0 Constantinos Venetsanopoulos
    """
2602 7181fba0 Constantinos Venetsanopoulos
    super(RADOSBlockDevice, self).__init__(unique_id, children, size, params)
2603 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2604 7181fba0 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2605 7181fba0 Constantinos Venetsanopoulos
2606 7181fba0 Constantinos Venetsanopoulos
    self.driver, self.rbd_name = unique_id
2607 7181fba0 Constantinos Venetsanopoulos
2608 7181fba0 Constantinos Venetsanopoulos
    self.major = self.minor = None
2609 7181fba0 Constantinos Venetsanopoulos
    self.Attach()
2610 7181fba0 Constantinos Venetsanopoulos
2611 7181fba0 Constantinos Venetsanopoulos
  @classmethod
2612 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2613 7181fba0 Constantinos Venetsanopoulos
    """Create a new rbd device.
2614 7181fba0 Constantinos Venetsanopoulos

2615 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
2616 7181fba0 Constantinos Venetsanopoulos

2617 7181fba0 Constantinos Venetsanopoulos
    """
2618 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2619 7181fba0 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
2620 7181fba0 Constantinos Venetsanopoulos
                                   str(unique_id))
2621 ee1478e5 Bernardo Dal Seno
    if excl_stor:
2622 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("RBD device requested with"
2623 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
2624 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = params[constants.LDP_POOL]
2625 7181fba0 Constantinos Venetsanopoulos
    rbd_name = unique_id[1]
2626 7181fba0 Constantinos Venetsanopoulos
2627 7181fba0 Constantinos Venetsanopoulos
    # Provision a new rbd volume (Image) inside the RADOS cluster.
2628 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "create", "-p", rbd_pool,
2629 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % size]
2630 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2631 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2632 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd creation failed (%s): %s",
2633 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2634 7181fba0 Constantinos Venetsanopoulos
2635 7181fba0 Constantinos Venetsanopoulos
    return RADOSBlockDevice(unique_id, children, size, params)
2636 7181fba0 Constantinos Venetsanopoulos
2637 7181fba0 Constantinos Venetsanopoulos
  def Remove(self):
2638 7181fba0 Constantinos Venetsanopoulos
    """Remove the rbd device.
2639 7181fba0 Constantinos Venetsanopoulos

2640 7181fba0 Constantinos Venetsanopoulos
    """
2641 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
2642 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
2643 7181fba0 Constantinos Venetsanopoulos
2644 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2645 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
2646 7181fba0 Constantinos Venetsanopoulos
      return
2647 7181fba0 Constantinos Venetsanopoulos
2648 7181fba0 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
2649 7181fba0 Constantinos Venetsanopoulos
    self.Shutdown()
2650 7181fba0 Constantinos Venetsanopoulos
2651 7181fba0 Constantinos Venetsanopoulos
    # Remove the actual Volume (Image) from the RADOS cluster.
2652 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "rm", "-p", rbd_pool, rbd_name]
2653 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2654 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2655 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Can't remove Volume from cluster with rbd rm: %s - %s",
2656 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2657 7181fba0 Constantinos Venetsanopoulos
2658 7181fba0 Constantinos Venetsanopoulos
  def Rename(self, new_id):
2659 7181fba0 Constantinos Venetsanopoulos
    """Rename this device.
2660 7181fba0 Constantinos Venetsanopoulos

2661 7181fba0 Constantinos Venetsanopoulos
    """
2662 7181fba0 Constantinos Venetsanopoulos
    pass
2663 7181fba0 Constantinos Venetsanopoulos
2664 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
2665 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
2666 7181fba0 Constantinos Venetsanopoulos

2667 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
2668 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
2669 7181fba0 Constantinos Venetsanopoulos

2670 7181fba0 Constantinos Venetsanopoulos
    """
2671 7181fba0 Constantinos Venetsanopoulos
    self.attached = False
2672 7181fba0 Constantinos Venetsanopoulos
2673 7181fba0 Constantinos Venetsanopoulos
    # Map the rbd volume to a block device under /dev
2674 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = self._MapVolumeToBlockdev(self.unique_id)
2675 7181fba0 Constantinos Venetsanopoulos
2676 7181fba0 Constantinos Venetsanopoulos
    try:
2677 7181fba0 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
2678 7181fba0 Constantinos Venetsanopoulos
    except OSError, err:
2679 7181fba0 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2680 7181fba0 Constantinos Venetsanopoulos
      return False
2681 7181fba0 Constantinos Venetsanopoulos
2682 7181fba0 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
2683 7181fba0 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
2684 7181fba0 Constantinos Venetsanopoulos
      return False
2685 7181fba0 Constantinos Venetsanopoulos
2686 7181fba0 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
2687 7181fba0 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
2688 7181fba0 Constantinos Venetsanopoulos
    self.attached = True
2689 7181fba0 Constantinos Venetsanopoulos
2690 7181fba0 Constantinos Venetsanopoulos
    return True
2691 7181fba0 Constantinos Venetsanopoulos
2692 7181fba0 Constantinos Venetsanopoulos
  def _MapVolumeToBlockdev(self, unique_id):
2693 7181fba0 Constantinos Venetsanopoulos
    """Maps existing rbd volumes to block devices.
2694 7181fba0 Constantinos Venetsanopoulos

2695 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
2696 7181fba0 Constantinos Venetsanopoulos

2697 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
2698 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
2699 7181fba0 Constantinos Venetsanopoulos

2700 7181fba0 Constantinos Venetsanopoulos
    """
2701 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2702 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2703 7181fba0 Constantinos Venetsanopoulos
2704 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2705 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2706 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2707 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2708 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped failed (%s): %s",
2709 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2710 7181fba0 Constantinos Venetsanopoulos
2711 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2712 7181fba0 Constantinos Venetsanopoulos
2713 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2714 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Return it.
2715 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2716 7181fba0 Constantinos Venetsanopoulos
2717 7181fba0 Constantinos Venetsanopoulos
    # The mapping doesn't exist. Create it.
2718 7181fba0 Constantinos Venetsanopoulos
    map_cmd = [constants.RBD_CMD, "map", "-p", pool, name]
2719 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(map_cmd)
2720 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2721 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map failed (%s): %s",
2722 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2723 7181fba0 Constantinos Venetsanopoulos
2724 7181fba0 Constantinos Venetsanopoulos
    # Find the corresponding rbd device.
2725 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2726 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2727 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2728 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map succeeded, but showmapped failed (%s): %s",
2729 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2730 7181fba0 Constantinos Venetsanopoulos
2731 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2732 7181fba0 Constantinos Venetsanopoulos
2733 7181fba0 Constantinos Venetsanopoulos
    if not rbd_dev:
2734 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map succeeded, but could not find the rbd block"
2735 7181fba0 Constantinos Venetsanopoulos
                  " device in output of showmapped, for volume: %s", name)
2736 7181fba0 Constantinos Venetsanopoulos
2737 7181fba0 Constantinos Venetsanopoulos
    # The device was successfully mapped. Return it.
2738 7181fba0 Constantinos Venetsanopoulos
    return rbd_dev
2739 7181fba0 Constantinos Venetsanopoulos
2740 7181fba0 Constantinos Venetsanopoulos
  @staticmethod
2741 7181fba0 Constantinos Venetsanopoulos
  def _ParseRbdShowmappedOutput(output, volume_name):
2742 7181fba0 Constantinos Venetsanopoulos
    """Parse the output of `rbd showmapped'.
2743 7181fba0 Constantinos Venetsanopoulos

2744 7181fba0 Constantinos Venetsanopoulos
    This method parses the output of `rbd showmapped' and returns
2745 7181fba0 Constantinos Venetsanopoulos
    the rbd block device path (e.g. /dev/rbd0) that matches the
2746 7181fba0 Constantinos Venetsanopoulos
    given rbd volume.
2747 7181fba0 Constantinos Venetsanopoulos

2748 7181fba0 Constantinos Venetsanopoulos
    @type output: string
2749 7181fba0 Constantinos Venetsanopoulos
    @param output: the whole output of `rbd showmapped'
2750 7181fba0 Constantinos Venetsanopoulos
    @type volume_name: string
2751 7181fba0 Constantinos Venetsanopoulos
    @param volume_name: the name of the volume whose device we search for
2752 7181fba0 Constantinos Venetsanopoulos
    @rtype: string or None
2753 7181fba0 Constantinos Venetsanopoulos
    @return: block device path if the volume is mapped, else None
2754 7181fba0 Constantinos Venetsanopoulos

2755 7181fba0 Constantinos Venetsanopoulos
    """
2756 7181fba0 Constantinos Venetsanopoulos
    allfields = 5
2757 7181fba0 Constantinos Venetsanopoulos
    volumefield = 2
2758 7181fba0 Constantinos Venetsanopoulos
    devicefield = 4
2759 7181fba0 Constantinos Venetsanopoulos
2760 7181fba0 Constantinos Venetsanopoulos
    field_sep = "\t"
2761 7181fba0 Constantinos Venetsanopoulos
2762 7181fba0 Constantinos Venetsanopoulos
    lines = output.splitlines()
2763 7181fba0 Constantinos Venetsanopoulos
    splitted_lines = map(lambda l: l.split(field_sep), lines)
2764 7181fba0 Constantinos Venetsanopoulos
2765 7181fba0 Constantinos Venetsanopoulos
    # Check empty output.
2766 7181fba0 Constantinos Venetsanopoulos
    if not splitted_lines:
2767 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped returned empty output")
2768 7181fba0 Constantinos Venetsanopoulos
2769 7181fba0 Constantinos Venetsanopoulos
    # Check showmapped header line, to determine number of fields.
2770 7181fba0 Constantinos Venetsanopoulos
    field_cnt = len(splitted_lines[0])
2771 7181fba0 Constantinos Venetsanopoulos
    if field_cnt != allfields:
2772 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Cannot parse rbd showmapped output because its format"
2773 7181fba0 Constantinos Venetsanopoulos
                  " seems to have changed; expected %s fields, found %s",
2774 7181fba0 Constantinos Venetsanopoulos
                  allfields, field_cnt)
2775 7181fba0 Constantinos Venetsanopoulos
2776 7181fba0 Constantinos Venetsanopoulos
    matched_lines = \
2777 7181fba0 Constantinos Venetsanopoulos
      filter(lambda l: len(l) == allfields and l[volumefield] == volume_name,
2778 7181fba0 Constantinos Venetsanopoulos
             splitted_lines)
2779 7181fba0 Constantinos Venetsanopoulos
2780 7181fba0 Constantinos Venetsanopoulos
    if len(matched_lines) > 1:
2781 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("The rbd volume %s is mapped more than once."
2782 7181fba0 Constantinos Venetsanopoulos
                  " This shouldn't happen, try to unmap the extra"
2783 7181fba0 Constantinos Venetsanopoulos
                  " devices manually.", volume_name)
2784 7181fba0 Constantinos Venetsanopoulos
2785 7181fba0 Constantinos Venetsanopoulos
    if matched_lines:
2786 7181fba0 Constantinos Venetsanopoulos
      # rbd block device found. Return it.
2787 7181fba0 Constantinos Venetsanopoulos
      rbd_dev = matched_lines[0][devicefield]
2788 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2789 7181fba0 Constantinos Venetsanopoulos
2790 7181fba0 Constantinos Venetsanopoulos
    # The given volume is not mapped.
2791 7181fba0 Constantinos Venetsanopoulos
    return None
2792 7181fba0 Constantinos Venetsanopoulos
2793 7181fba0 Constantinos Venetsanopoulos
  def Assemble(self):
2794 7181fba0 Constantinos Venetsanopoulos
    """Assemble the device.
2795 7181fba0 Constantinos Venetsanopoulos

2796 7181fba0 Constantinos Venetsanopoulos
    """
2797 7181fba0 Constantinos Venetsanopoulos
    pass
2798 7181fba0 Constantinos Venetsanopoulos
2799 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
2800 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
2801 7181fba0 Constantinos Venetsanopoulos

2802 7181fba0 Constantinos Venetsanopoulos
    """
2803 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2804 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
2805 7181fba0 Constantinos Venetsanopoulos
      return
2806 7181fba0 Constantinos Venetsanopoulos
2807 7181fba0 Constantinos Venetsanopoulos
    # Unmap the block device from the Volume.
2808 7181fba0 Constantinos Venetsanopoulos
    self._UnmapVolumeFromBlockdev(self.unique_id)
2809 7181fba0 Constantinos Venetsanopoulos
2810 7181fba0 Constantinos Venetsanopoulos
    self.minor = None
2811 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = None
2812 7181fba0 Constantinos Venetsanopoulos
2813 7181fba0 Constantinos Venetsanopoulos
  def _UnmapVolumeFromBlockdev(self, unique_id):
2814 7181fba0 Constantinos Venetsanopoulos
    """Unmaps the rbd device from the Volume it is mapped.
2815 7181fba0 Constantinos Venetsanopoulos

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

2819 7181fba0 Constantinos Venetsanopoulos
    """
2820 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2821 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2822 7181fba0 Constantinos Venetsanopoulos
2823 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2824 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2825 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2826 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2827 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped failed [during unmap](%s): %s",
2828 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2829 7181fba0 Constantinos Venetsanopoulos
2830 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2831 7181fba0 Constantinos Venetsanopoulos
2832 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2833 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Unmap the rbd device.
2834 7181fba0 Constantinos Venetsanopoulos
      unmap_cmd = [constants.RBD_CMD, "unmap", "%s" % rbd_dev]
2835 7181fba0 Constantinos Venetsanopoulos
      result = utils.RunCmd(unmap_cmd)
2836 7181fba0 Constantinos Venetsanopoulos
      if result.failed:
2837 7181fba0 Constantinos Venetsanopoulos
        _ThrowError("rbd unmap failed (%s): %s",
2838 7181fba0 Constantinos Venetsanopoulos
                    result.fail_reason, result.output)
2839 7181fba0 Constantinos Venetsanopoulos
2840 7181fba0 Constantinos Venetsanopoulos
  def Open(self, force=False):
2841 7181fba0 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
2842 7181fba0 Constantinos Venetsanopoulos

2843 7181fba0 Constantinos Venetsanopoulos
    """
2844 7181fba0 Constantinos Venetsanopoulos
    pass
2845 7181fba0 Constantinos Venetsanopoulos
2846 7181fba0 Constantinos Venetsanopoulos
  def Close(self):
2847 7181fba0 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
2848 7181fba0 Constantinos Venetsanopoulos

2849 7181fba0 Constantinos Venetsanopoulos
    """
2850 7181fba0 Constantinos Venetsanopoulos
    pass
2851 7181fba0 Constantinos Venetsanopoulos
2852 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2853 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
2854 7181fba0 Constantinos Venetsanopoulos

2855 7181fba0 Constantinos Venetsanopoulos
    @type amount: integer
2856 7181fba0 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
2857 7181fba0 Constantinos Venetsanopoulos
    @type dryrun: boolean
2858 7181fba0 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
2859 7181fba0 Constantinos Venetsanopoulos
        only, without actually increasing the size
2860 7181fba0 Constantinos Venetsanopoulos

2861 7181fba0 Constantinos Venetsanopoulos
    """
2862 cad0723b Iustin Pop
    if not backingstore:
2863 cad0723b Iustin Pop
      return
2864 7181fba0 Constantinos Venetsanopoulos
    if not self.Attach():
2865 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Can't attach to rbd device during Grow()")
2866 7181fba0 Constantinos Venetsanopoulos
2867 7181fba0 Constantinos Venetsanopoulos
    if dryrun:
2868 7181fba0 Constantinos Venetsanopoulos
      # the rbd tool does not support dry runs of resize operations.
2869 7181fba0 Constantinos Venetsanopoulos
      # Since rbd volumes are thinly provisioned, we assume
2870 7181fba0 Constantinos Venetsanopoulos
      # there is always enough free space for the operation.
2871 7181fba0 Constantinos Venetsanopoulos
      return
2872 7181fba0 Constantinos Venetsanopoulos
2873 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
2874 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
2875 7181fba0 Constantinos Venetsanopoulos
    new_size = self.size + amount
2876 7181fba0 Constantinos Venetsanopoulos
2877 7181fba0 Constantinos Venetsanopoulos
    # Resize the rbd volume (Image) inside the RADOS cluster.
2878 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "resize", "-p", rbd_pool,
2879 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % new_size]
2880 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2881 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2882 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd resize failed (%s): %s",
2883 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2884 7181fba0 Constantinos Venetsanopoulos
2885 7181fba0 Constantinos Venetsanopoulos
2886 376631d1 Constantinos Venetsanopoulos
class ExtStorageDevice(BlockDev):
2887 376631d1 Constantinos Venetsanopoulos
  """A block device provided by an ExtStorage Provider.
2888 376631d1 Constantinos Venetsanopoulos

2889 376631d1 Constantinos Venetsanopoulos
  This class implements the External Storage Interface, which means
2890 376631d1 Constantinos Venetsanopoulos
  handling of the externally provided block devices.
2891 376631d1 Constantinos Venetsanopoulos

2892 376631d1 Constantinos Venetsanopoulos
  """
2893 376631d1 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
2894 376631d1 Constantinos Venetsanopoulos
    """Attaches to an extstorage block device.
2895 376631d1 Constantinos Venetsanopoulos

2896 376631d1 Constantinos Venetsanopoulos
    """
2897 376631d1 Constantinos Venetsanopoulos
    super(ExtStorageDevice, self).__init__(unique_id, children, size, params)
2898 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2899 376631d1 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2900 376631d1 Constantinos Venetsanopoulos
2901 376631d1 Constantinos Venetsanopoulos
    self.driver, self.vol_name = unique_id
2902 938adc87 Constantinos Venetsanopoulos
    self.ext_params = params
2903 376631d1 Constantinos Venetsanopoulos
2904 376631d1 Constantinos Venetsanopoulos
    self.major = self.minor = None
2905 376631d1 Constantinos Venetsanopoulos
    self.Attach()
2906 376631d1 Constantinos Venetsanopoulos
2907 376631d1 Constantinos Venetsanopoulos
  @classmethod
2908 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2909 376631d1 Constantinos Venetsanopoulos
    """Create a new extstorage device.
2910 376631d1 Constantinos Venetsanopoulos

2911 376631d1 Constantinos Venetsanopoulos
    Provision a new volume using an extstorage provider, which will
2912 376631d1 Constantinos Venetsanopoulos
    then be mapped to a block device.
2913 376631d1 Constantinos Venetsanopoulos

2914 376631d1 Constantinos Venetsanopoulos
    """
2915 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2916 376631d1 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
2917 376631d1 Constantinos Venetsanopoulos
                                   str(unique_id))
2918 ee1478e5 Bernardo Dal Seno
    if excl_stor:
2919 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("extstorage device requested with"
2920 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
2921 376631d1 Constantinos Venetsanopoulos
2922 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's create script,
2923 376631d1 Constantinos Venetsanopoulos
    # to provision a new Volume inside the External Storage
2924 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id,
2925 938adc87 Constantinos Venetsanopoulos
                      params, str(size))
2926 376631d1 Constantinos Venetsanopoulos
2927 376631d1 Constantinos Venetsanopoulos
    return ExtStorageDevice(unique_id, children, size, params)
2928 376631d1 Constantinos Venetsanopoulos
2929 376631d1 Constantinos Venetsanopoulos
  def Remove(self):
2930 376631d1 Constantinos Venetsanopoulos
    """Remove the extstorage device.
2931 376631d1 Constantinos Venetsanopoulos

2932 376631d1 Constantinos Venetsanopoulos
    """
2933 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2934 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
2935 376631d1 Constantinos Venetsanopoulos
      return
2936 376631d1 Constantinos Venetsanopoulos
2937 376631d1 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
2938 376631d1 Constantinos Venetsanopoulos
    self.Shutdown()
2939 376631d1 Constantinos Venetsanopoulos
2940 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's remove script,
2941 376631d1 Constantinos Venetsanopoulos
    # to remove the Volume from the External Storage
2942 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id,
2943 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
2944 376631d1 Constantinos Venetsanopoulos
2945 376631d1 Constantinos Venetsanopoulos
  def Rename(self, new_id):
2946 376631d1 Constantinos Venetsanopoulos
    """Rename this device.
2947 376631d1 Constantinos Venetsanopoulos

2948 376631d1 Constantinos Venetsanopoulos
    """
2949 376631d1 Constantinos Venetsanopoulos
    pass
2950 376631d1 Constantinos Venetsanopoulos
2951 376631d1 Constantinos Venetsanopoulos
  def Attach(self):
2952 376631d1 Constantinos Venetsanopoulos
    """Attach to an existing extstorage device.
2953 376631d1 Constantinos Venetsanopoulos

2954 376631d1 Constantinos Venetsanopoulos
    This method maps the extstorage volume that matches our name with
2955 376631d1 Constantinos Venetsanopoulos
    a corresponding block device and then attaches to this device.
2956 376631d1 Constantinos Venetsanopoulos

2957 376631d1 Constantinos Venetsanopoulos
    """
2958 376631d1 Constantinos Venetsanopoulos
    self.attached = False
2959 376631d1 Constantinos Venetsanopoulos
2960 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's attach script,
2961 376631d1 Constantinos Venetsanopoulos
    # to attach an existing Volume to a block device under /dev
2962 376631d1 Constantinos Venetsanopoulos
    self.dev_path = _ExtStorageAction(constants.ES_ACTION_ATTACH,
2963 938adc87 Constantinos Venetsanopoulos
                                      self.unique_id, self.ext_params)
2964 376631d1 Constantinos Venetsanopoulos
2965 376631d1 Constantinos Venetsanopoulos
    try:
2966 376631d1 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
2967 376631d1 Constantinos Venetsanopoulos
    except OSError, err:
2968 376631d1 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2969 376631d1 Constantinos Venetsanopoulos
      return False
2970 376631d1 Constantinos Venetsanopoulos
2971 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
2972 376631d1 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
2973 376631d1 Constantinos Venetsanopoulos
      return False
2974 376631d1 Constantinos Venetsanopoulos
2975 376631d1 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
2976 376631d1 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
2977 376631d1 Constantinos Venetsanopoulos
    self.attached = True
2978 376631d1 Constantinos Venetsanopoulos
2979 376631d1 Constantinos Venetsanopoulos
    return True
2980 376631d1 Constantinos Venetsanopoulos
2981 376631d1 Constantinos Venetsanopoulos
  def Assemble(self):
2982 376631d1 Constantinos Venetsanopoulos
    """Assemble the device.
2983 376631d1 Constantinos Venetsanopoulos

2984 376631d1 Constantinos Venetsanopoulos
    """
2985 376631d1 Constantinos Venetsanopoulos
    pass
2986 376631d1 Constantinos Venetsanopoulos
2987 376631d1 Constantinos Venetsanopoulos
  def Shutdown(self):
2988 376631d1 Constantinos Venetsanopoulos
    """Shutdown the device.
2989 376631d1 Constantinos Venetsanopoulos

2990 376631d1 Constantinos Venetsanopoulos
    """
2991 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2992 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
2993 376631d1 Constantinos Venetsanopoulos
      return
2994 376631d1 Constantinos Venetsanopoulos
2995 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's detach script,
2996 376631d1 Constantinos Venetsanopoulos
    # to detach an existing Volume from it's block device under /dev
2997 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id,
2998 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
2999 376631d1 Constantinos Venetsanopoulos
3000 376631d1 Constantinos Venetsanopoulos
    self.minor = None
3001 376631d1 Constantinos Venetsanopoulos
    self.dev_path = None
3002 376631d1 Constantinos Venetsanopoulos
3003 376631d1 Constantinos Venetsanopoulos
  def Open(self, force=False):
3004 376631d1 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
3005 376631d1 Constantinos Venetsanopoulos

3006 376631d1 Constantinos Venetsanopoulos
    """
3007 376631d1 Constantinos Venetsanopoulos
    pass
3008 376631d1 Constantinos Venetsanopoulos
3009 376631d1 Constantinos Venetsanopoulos
  def Close(self):
3010 376631d1 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
3011 376631d1 Constantinos Venetsanopoulos

3012 376631d1 Constantinos Venetsanopoulos
    """
3013 376631d1 Constantinos Venetsanopoulos
    pass
3014 376631d1 Constantinos Venetsanopoulos
3015 376631d1 Constantinos Venetsanopoulos
  def Grow(self, amount, dryrun, backingstore):
3016 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
3017 376631d1 Constantinos Venetsanopoulos

3018 376631d1 Constantinos Venetsanopoulos
    @type amount: integer
3019 376631d1 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
3020 376631d1 Constantinos Venetsanopoulos
    @type dryrun: boolean
3021 376631d1 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
3022 376631d1 Constantinos Venetsanopoulos
        only, without actually increasing the size
3023 376631d1 Constantinos Venetsanopoulos

3024 376631d1 Constantinos Venetsanopoulos
    """
3025 376631d1 Constantinos Venetsanopoulos
    if not backingstore:
3026 376631d1 Constantinos Venetsanopoulos
      return
3027 376631d1 Constantinos Venetsanopoulos
    if not self.Attach():
3028 376631d1 Constantinos Venetsanopoulos
      _ThrowError("Can't attach to extstorage device during Grow()")
3029 376631d1 Constantinos Venetsanopoulos
3030 376631d1 Constantinos Venetsanopoulos
    if dryrun:
3031 376631d1 Constantinos Venetsanopoulos
      # we do not support dry runs of resize operations for now.
3032 376631d1 Constantinos Venetsanopoulos
      return
3033 376631d1 Constantinos Venetsanopoulos
3034 376631d1 Constantinos Venetsanopoulos
    new_size = self.size + amount
3035 376631d1 Constantinos Venetsanopoulos
3036 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's grow script,
3037 376631d1 Constantinos Venetsanopoulos
    # to grow an existing Volume inside the External Storage
3038 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_GROW, self.unique_id,
3039 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, str(self.size), grow=str(new_size))
3040 376631d1 Constantinos Venetsanopoulos
3041 376631d1 Constantinos Venetsanopoulos
  def SetInfo(self, text):
3042 376631d1 Constantinos Venetsanopoulos
    """Update metadata with info text.
3043 376631d1 Constantinos Venetsanopoulos

3044 376631d1 Constantinos Venetsanopoulos
    """
3045 376631d1 Constantinos Venetsanopoulos
    # Replace invalid characters
3046 376631d1 Constantinos Venetsanopoulos
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
3047 376631d1 Constantinos Venetsanopoulos
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
3048 376631d1 Constantinos Venetsanopoulos
3049 376631d1 Constantinos Venetsanopoulos
    # Only up to 128 characters are allowed
3050 376631d1 Constantinos Venetsanopoulos
    text = text[:128]
3051 376631d1 Constantinos Venetsanopoulos
3052 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's setinfo script,
3053 376631d1 Constantinos Venetsanopoulos
    # to set metadata for an existing Volume inside the External Storage
3054 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
3055 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, metadata=text)
3056 376631d1 Constantinos Venetsanopoulos
3057 376631d1 Constantinos Venetsanopoulos
3058 938adc87 Constantinos Venetsanopoulos
def _ExtStorageAction(action, unique_id, ext_params,
3059 938adc87 Constantinos Venetsanopoulos
                      size=None, grow=None, metadata=None):
3060 376631d1 Constantinos Venetsanopoulos
  """Take an External Storage action.
3061 376631d1 Constantinos Venetsanopoulos

3062 376631d1 Constantinos Venetsanopoulos
  Take an External Storage action concerning or affecting
3063 376631d1 Constantinos Venetsanopoulos
  a specific Volume inside the External Storage.
3064 376631d1 Constantinos Venetsanopoulos

3065 376631d1 Constantinos Venetsanopoulos
  @type action: string
3066 376631d1 Constantinos Venetsanopoulos
  @param action: which action to perform. One of:
3067 376631d1 Constantinos Venetsanopoulos
                 create / remove / grow / attach / detach
3068 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
3069 376631d1 Constantinos Venetsanopoulos
  @param unique_id: a tuple containing the type of ExtStorage (driver)
3070 376631d1 Constantinos Venetsanopoulos
                    and the Volume name
3071 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
3072 938adc87 Constantinos Venetsanopoulos
  @param ext_params: ExtStorage parameters
3073 376631d1 Constantinos Venetsanopoulos
  @type size: integer
3074 376631d1 Constantinos Venetsanopoulos
  @param size: the size of the Volume in mebibytes
3075 376631d1 Constantinos Venetsanopoulos
  @type grow: integer
3076 376631d1 Constantinos Venetsanopoulos
  @param grow: the new size in mebibytes (after grow)
3077 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
3078 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume, for use by the provider
3079 376631d1 Constantinos Venetsanopoulos
  @rtype: None or a block device path (during attach)
3080 376631d1 Constantinos Venetsanopoulos

3081 376631d1 Constantinos Venetsanopoulos
  """
3082 376631d1 Constantinos Venetsanopoulos
  driver, vol_name = unique_id
3083 376631d1 Constantinos Venetsanopoulos
3084 376631d1 Constantinos Venetsanopoulos
  # Create an External Storage instance of type `driver'
3085 376631d1 Constantinos Venetsanopoulos
  status, inst_es = ExtStorageFromDisk(driver)
3086 376631d1 Constantinos Venetsanopoulos
  if not status:
3087 376631d1 Constantinos Venetsanopoulos
    _ThrowError("%s" % inst_es)
3088 376631d1 Constantinos Venetsanopoulos
3089 376631d1 Constantinos Venetsanopoulos
  # Create the basic environment for the driver's scripts
3090 938adc87 Constantinos Venetsanopoulos
  create_env = _ExtStorageEnvironment(unique_id, ext_params, size,
3091 938adc87 Constantinos Venetsanopoulos
                                      grow, metadata)
3092 376631d1 Constantinos Venetsanopoulos
3093 376631d1 Constantinos Venetsanopoulos
  # Do not use log file for action `attach' as we need
3094 376631d1 Constantinos Venetsanopoulos
  # to get the output from RunResult
3095 376631d1 Constantinos Venetsanopoulos
  # TODO: find a way to have a log file for attach too
3096 376631d1 Constantinos Venetsanopoulos
  logfile = None
3097 376631d1 Constantinos Venetsanopoulos
  if action is not constants.ES_ACTION_ATTACH:
3098 376631d1 Constantinos Venetsanopoulos
    logfile = _VolumeLogName(action, driver, vol_name)
3099 376631d1 Constantinos Venetsanopoulos
3100 376631d1 Constantinos Venetsanopoulos
  # Make sure the given action results in a valid script
3101 376631d1 Constantinos Venetsanopoulos
  if action not in constants.ES_SCRIPTS:
3102 376631d1 Constantinos Venetsanopoulos
    _ThrowError("Action '%s' doesn't result in a valid ExtStorage script" %
3103 376631d1 Constantinos Venetsanopoulos
                action)
3104 376631d1 Constantinos Venetsanopoulos
3105 376631d1 Constantinos Venetsanopoulos
  # Find out which external script to run according the given action
3106 376631d1 Constantinos Venetsanopoulos
  script_name = action + "_script"
3107 376631d1 Constantinos Venetsanopoulos
  script = getattr(inst_es, script_name)
3108 376631d1 Constantinos Venetsanopoulos
3109 376631d1 Constantinos Venetsanopoulos
  # Run the external script
3110 376631d1 Constantinos Venetsanopoulos
  result = utils.RunCmd([script], env=create_env,
3111 376631d1 Constantinos Venetsanopoulos
                        cwd=inst_es.path, output=logfile,)
3112 376631d1 Constantinos Venetsanopoulos
  if result.failed:
3113 376631d1 Constantinos Venetsanopoulos
    logging.error("External storage's %s command '%s' returned"
3114 376631d1 Constantinos Venetsanopoulos
                  " error: %s, logfile: %s, output: %s",
3115 376631d1 Constantinos Venetsanopoulos
                  action, result.cmd, result.fail_reason,
3116 376631d1 Constantinos Venetsanopoulos
                  logfile, result.output)
3117 376631d1 Constantinos Venetsanopoulos
3118 376631d1 Constantinos Venetsanopoulos
    # If logfile is 'None' (during attach), it breaks TailFile
3119 376631d1 Constantinos Venetsanopoulos
    # TODO: have a log file for attach too
3120 376631d1 Constantinos Venetsanopoulos
    if action is not constants.ES_ACTION_ATTACH:
3121 376631d1 Constantinos Venetsanopoulos
      lines = [utils.SafeEncode(val)
3122 376631d1 Constantinos Venetsanopoulos
               for val in utils.TailFile(logfile, lines=20)]
3123 376631d1 Constantinos Venetsanopoulos
    else:
3124 376631d1 Constantinos Venetsanopoulos
      lines = result.output[-20:]
3125 376631d1 Constantinos Venetsanopoulos
3126 376631d1 Constantinos Venetsanopoulos
    _ThrowError("External storage's %s script failed (%s), last"
3127 376631d1 Constantinos Venetsanopoulos
                " lines of output:\n%s",
3128 376631d1 Constantinos Venetsanopoulos
                action, result.fail_reason, "\n".join(lines))
3129 376631d1 Constantinos Venetsanopoulos
3130 376631d1 Constantinos Venetsanopoulos
  if action == constants.ES_ACTION_ATTACH:
3131 376631d1 Constantinos Venetsanopoulos
    return result.stdout
3132 376631d1 Constantinos Venetsanopoulos
3133 376631d1 Constantinos Venetsanopoulos
3134 376631d1 Constantinos Venetsanopoulos
def ExtStorageFromDisk(name, base_dir=None):
3135 376631d1 Constantinos Venetsanopoulos
  """Create an ExtStorage instance from disk.
3136 376631d1 Constantinos Venetsanopoulos

3137 376631d1 Constantinos Venetsanopoulos
  This function will return an ExtStorage instance
3138 376631d1 Constantinos Venetsanopoulos
  if the given name is a valid ExtStorage name.
3139 376631d1 Constantinos Venetsanopoulos

3140 376631d1 Constantinos Venetsanopoulos
  @type base_dir: string
3141 376631d1 Constantinos Venetsanopoulos
  @keyword base_dir: Base directory containing ExtStorage installations.
3142 376631d1 Constantinos Venetsanopoulos
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
3143 376631d1 Constantinos Venetsanopoulos
  @rtype: tuple
3144 376631d1 Constantinos Venetsanopoulos
  @return: True and the ExtStorage instance if we find a valid one, or
3145 376631d1 Constantinos Venetsanopoulos
      False and the diagnose message on error
3146 376631d1 Constantinos Venetsanopoulos

3147 376631d1 Constantinos Venetsanopoulos
  """
3148 376631d1 Constantinos Venetsanopoulos
  if base_dir is None:
3149 376631d1 Constantinos Venetsanopoulos
    es_base_dir = pathutils.ES_SEARCH_PATH
3150 376631d1 Constantinos Venetsanopoulos
  else:
3151 376631d1 Constantinos Venetsanopoulos
    es_base_dir = [base_dir]
3152 376631d1 Constantinos Venetsanopoulos
3153 376631d1 Constantinos Venetsanopoulos
  es_dir = utils.FindFile(name, es_base_dir, os.path.isdir)
3154 376631d1 Constantinos Venetsanopoulos
3155 376631d1 Constantinos Venetsanopoulos
  if es_dir is None:
3156 376631d1 Constantinos Venetsanopoulos
    return False, ("Directory for External Storage Provider %s not"
3157 376631d1 Constantinos Venetsanopoulos
                   " found in search path" % name)
3158 376631d1 Constantinos Venetsanopoulos
3159 376631d1 Constantinos Venetsanopoulos
  # ES Files dictionary, we will populate it with the absolute path
3160 376631d1 Constantinos Venetsanopoulos
  # names; if the value is True, then it is a required file, otherwise
3161 376631d1 Constantinos Venetsanopoulos
  # an optional one
3162 376631d1 Constantinos Venetsanopoulos
  es_files = dict.fromkeys(constants.ES_SCRIPTS, True)
3163 376631d1 Constantinos Venetsanopoulos
3164 938adc87 Constantinos Venetsanopoulos
  es_files[constants.ES_PARAMETERS_FILE] = True
3165 938adc87 Constantinos Venetsanopoulos
3166 938adc87 Constantinos Venetsanopoulos
  for (filename, _) in es_files.items():
3167 376631d1 Constantinos Venetsanopoulos
    es_files[filename] = utils.PathJoin(es_dir, filename)
3168 376631d1 Constantinos Venetsanopoulos
3169 376631d1 Constantinos Venetsanopoulos
    try:
3170 376631d1 Constantinos Venetsanopoulos
      st = os.stat(es_files[filename])
3171 376631d1 Constantinos Venetsanopoulos
    except EnvironmentError, err:
3172 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is missing (%s)" %
3173 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir, utils.ErrnoOrStr(err)))
3174 376631d1 Constantinos Venetsanopoulos
3175 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
3176 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is not a regular file" %
3177 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir))
3178 376631d1 Constantinos Venetsanopoulos
3179 376631d1 Constantinos Venetsanopoulos
    if filename in constants.ES_SCRIPTS:
3180 376631d1 Constantinos Venetsanopoulos
      if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
3181 376631d1 Constantinos Venetsanopoulos
        return False, ("File '%s' under path '%s' is not executable" %
3182 376631d1 Constantinos Venetsanopoulos
                       (filename, es_dir))
3183 376631d1 Constantinos Venetsanopoulos
3184 938adc87 Constantinos Venetsanopoulos
  parameters = []
3185 938adc87 Constantinos Venetsanopoulos
  if constants.ES_PARAMETERS_FILE in es_files:
3186 938adc87 Constantinos Venetsanopoulos
    parameters_file = es_files[constants.ES_PARAMETERS_FILE]
3187 938adc87 Constantinos Venetsanopoulos
    try:
3188 938adc87 Constantinos Venetsanopoulos
      parameters = utils.ReadFile(parameters_file).splitlines()
3189 938adc87 Constantinos Venetsanopoulos
    except EnvironmentError, err:
3190 938adc87 Constantinos Venetsanopoulos
      return False, ("Error while reading the EXT parameters file at %s: %s" %
3191 938adc87 Constantinos Venetsanopoulos
                     (parameters_file, utils.ErrnoOrStr(err)))
3192 938adc87 Constantinos Venetsanopoulos
    parameters = [v.split(None, 1) for v in parameters]
3193 938adc87 Constantinos Venetsanopoulos
3194 376631d1 Constantinos Venetsanopoulos
  es_obj = \
3195 376631d1 Constantinos Venetsanopoulos
    objects.ExtStorage(name=name, path=es_dir,
3196 376631d1 Constantinos Venetsanopoulos
                       create_script=es_files[constants.ES_SCRIPT_CREATE],
3197 376631d1 Constantinos Venetsanopoulos
                       remove_script=es_files[constants.ES_SCRIPT_REMOVE],
3198 376631d1 Constantinos Venetsanopoulos
                       grow_script=es_files[constants.ES_SCRIPT_GROW],
3199 376631d1 Constantinos Venetsanopoulos
                       attach_script=es_files[constants.ES_SCRIPT_ATTACH],
3200 376631d1 Constantinos Venetsanopoulos
                       detach_script=es_files[constants.ES_SCRIPT_DETACH],
3201 938adc87 Constantinos Venetsanopoulos
                       setinfo_script=es_files[constants.ES_SCRIPT_SETINFO],
3202 938adc87 Constantinos Venetsanopoulos
                       verify_script=es_files[constants.ES_SCRIPT_VERIFY],
3203 938adc87 Constantinos Venetsanopoulos
                       supported_parameters=parameters)
3204 376631d1 Constantinos Venetsanopoulos
  return True, es_obj
3205 376631d1 Constantinos Venetsanopoulos
3206 376631d1 Constantinos Venetsanopoulos
3207 938adc87 Constantinos Venetsanopoulos
def _ExtStorageEnvironment(unique_id, ext_params,
3208 938adc87 Constantinos Venetsanopoulos
                           size=None, grow=None, metadata=None):
3209 376631d1 Constantinos Venetsanopoulos
  """Calculate the environment for an External Storage script.
3210 376631d1 Constantinos Venetsanopoulos

3211 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
3212 376631d1 Constantinos Venetsanopoulos
  @param unique_id: ExtStorage pool and name of the Volume
3213 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
3214 938adc87 Constantinos Venetsanopoulos
  @param ext_params: the EXT parameters
3215 376631d1 Constantinos Venetsanopoulos
  @type size: string
3216 376631d1 Constantinos Venetsanopoulos
  @param size: size of the Volume (in mebibytes)
3217 376631d1 Constantinos Venetsanopoulos
  @type grow: string
3218 376631d1 Constantinos Venetsanopoulos
  @param grow: new size of Volume after grow (in mebibytes)
3219 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
3220 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume
3221 376631d1 Constantinos Venetsanopoulos
  @rtype: dict
3222 376631d1 Constantinos Venetsanopoulos
  @return: dict of environment variables
3223 376631d1 Constantinos Venetsanopoulos

3224 376631d1 Constantinos Venetsanopoulos
  """
3225 376631d1 Constantinos Venetsanopoulos
  vol_name = unique_id[1]
3226 376631d1 Constantinos Venetsanopoulos
3227 376631d1 Constantinos Venetsanopoulos
  result = {}
3228 376631d1 Constantinos Venetsanopoulos
  result["VOL_NAME"] = vol_name
3229 376631d1 Constantinos Venetsanopoulos
3230 938adc87 Constantinos Venetsanopoulos
  # EXT params
3231 938adc87 Constantinos Venetsanopoulos
  for pname, pvalue in ext_params.items():
3232 938adc87 Constantinos Venetsanopoulos
    result["EXTP_%s" % pname.upper()] = str(pvalue)
3233 938adc87 Constantinos Venetsanopoulos
3234 376631d1 Constantinos Venetsanopoulos
  if size is not None:
3235 376631d1 Constantinos Venetsanopoulos
    result["VOL_SIZE"] = size
3236 376631d1 Constantinos Venetsanopoulos
3237 376631d1 Constantinos Venetsanopoulos
  if grow is not None:
3238 376631d1 Constantinos Venetsanopoulos
    result["VOL_NEW_SIZE"] = grow
3239 376631d1 Constantinos Venetsanopoulos
3240 376631d1 Constantinos Venetsanopoulos
  if metadata is not None:
3241 376631d1 Constantinos Venetsanopoulos
    result["VOL_METADATA"] = metadata
3242 376631d1 Constantinos Venetsanopoulos
3243 376631d1 Constantinos Venetsanopoulos
  return result
3244 376631d1 Constantinos Venetsanopoulos
3245 376631d1 Constantinos Venetsanopoulos
3246 376631d1 Constantinos Venetsanopoulos
def _VolumeLogName(kind, es_name, volume):
3247 376631d1 Constantinos Venetsanopoulos
  """Compute the ExtStorage log filename for a given Volume and operation.
3248 376631d1 Constantinos Venetsanopoulos

3249 376631d1 Constantinos Venetsanopoulos
  @type kind: string
3250 376631d1 Constantinos Venetsanopoulos
  @param kind: the operation type (e.g. create, remove etc.)
3251 376631d1 Constantinos Venetsanopoulos
  @type es_name: string
3252 376631d1 Constantinos Venetsanopoulos
  @param es_name: the ExtStorage name
3253 376631d1 Constantinos Venetsanopoulos
  @type volume: string
3254 376631d1 Constantinos Venetsanopoulos
  @param volume: the name of the Volume inside the External Storage
3255 376631d1 Constantinos Venetsanopoulos

3256 376631d1 Constantinos Venetsanopoulos
  """
3257 376631d1 Constantinos Venetsanopoulos
  # Check if the extstorage log dir is a valid dir
3258 376631d1 Constantinos Venetsanopoulos
  if not os.path.isdir(pathutils.LOG_ES_DIR):
3259 376631d1 Constantinos Venetsanopoulos
    _ThrowError("Cannot find log directory: %s", pathutils.LOG_ES_DIR)
3260 376631d1 Constantinos Venetsanopoulos
3261 376631d1 Constantinos Venetsanopoulos
  # TODO: Use tempfile.mkstemp to create unique filename
3262 376631d1 Constantinos Venetsanopoulos
  base = ("%s-%s-%s-%s.log" %
3263 376631d1 Constantinos Venetsanopoulos
          (kind, es_name, volume, utils.TimestampForFilename()))
3264 376631d1 Constantinos Venetsanopoulos
  return utils.PathJoin(pathutils.LOG_ES_DIR, base)
3265 376631d1 Constantinos Venetsanopoulos
3266 376631d1 Constantinos Venetsanopoulos
3267 a8083063 Iustin Pop
DEV_MAP = {
3268 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
3269 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
3270 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
3271 7181fba0 Constantinos Venetsanopoulos
  constants.LD_RBD: RADOSBlockDevice,
3272 376631d1 Constantinos Venetsanopoulos
  constants.LD_EXT: ExtStorageDevice,
3273 a8083063 Iustin Pop
  }
3274 a8083063 Iustin Pop
3275 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
3276 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
3277 cb7c0198 Iustin Pop
3278 a8083063 Iustin Pop
3279 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
3280 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
3281 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
3282 94dcbdb0 Andrea Spadaccini
3283 94dcbdb0 Andrea Spadaccini
3284 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
3285 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
3286 5ff82cc9 René Nussbaumer

3287 5ff82cc9 René Nussbaumer
  """
3288 5ff82cc9 René Nussbaumer
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
3289 5ff82cc9 René Nussbaumer
  if missing:
3290 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
3291 5ff82cc9 René Nussbaumer
                                 missing)
3292 5ff82cc9 René Nussbaumer
3293 5ff82cc9 René Nussbaumer
3294 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
3295 a8083063 Iustin Pop
  """Search for an existing, assembled device.
3296 a8083063 Iustin Pop

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

3300 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
3301 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
3302 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
3303 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
3304 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
3305 94dcbdb0 Andrea Spadaccini

3306 a8083063 Iustin Pop
  """
3307 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3308 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
3309 c7c6606d René Nussbaumer
                                  disk.params)
3310 cb999543 Iustin Pop
  if not device.attached:
3311 a8083063 Iustin Pop
    return None
3312 ecb091e3 Iustin Pop
  return device
3313 a8083063 Iustin Pop
3314 a8083063 Iustin Pop
3315 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
3316 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
3317 a8083063 Iustin Pop

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

3321 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
3322 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
3323 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
3324 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
3325 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
3326 94dcbdb0 Andrea Spadaccini

3327 a8083063 Iustin Pop
  """
3328 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3329 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
3330 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
3331 c7c6606d René Nussbaumer
                                  disk.params)
3332 1063abd1 Iustin Pop
  device.Assemble()
3333 a8083063 Iustin Pop
  return device
3334 a8083063 Iustin Pop
3335 a8083063 Iustin Pop
3336 ee1478e5 Bernardo Dal Seno
def Create(disk, children, excl_stor):
3337 a8083063 Iustin Pop
  """Create a device.
3338 a8083063 Iustin Pop

3339 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
3340 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
3341 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
3342 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
3343 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
3344 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
3345 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
3346 94dcbdb0 Andrea Spadaccini

3347 a8083063 Iustin Pop
  """
3348 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3349 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
3350 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
3351 ee1478e5 Bernardo Dal Seno
                                         disk.params, excl_stor)
3352 a8083063 Iustin Pop
  return device