Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 2bd9ec7c

History | View | Annotate | Download (109.2 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 5454737c Iustin Pop
# Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 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 bdecfea2 Stratos Psomadakis
from ganeti import serializer
42 a8083063 Iustin Pop
43 a8083063 Iustin Pop
44 310fbb64 Iustin Pop
# Size of reads in _CanReadDevice
45 310fbb64 Iustin Pop
_DEVICE_READ_SIZE = 128 * 1024
46 310fbb64 Iustin Pop
47 310fbb64 Iustin Pop
48 bdecfea2 Stratos Psomadakis
class RbdShowmappedJsonError(Exception):
49 bdecfea2 Stratos Psomadakis
  """`rbd showmmapped' JSON formatting error Exception class.
50 bdecfea2 Stratos Psomadakis

51 bdecfea2 Stratos Psomadakis
  """
52 bdecfea2 Stratos Psomadakis
  pass
53 bdecfea2 Stratos Psomadakis
54 bdecfea2 Stratos Psomadakis
55 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
56 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
57 82463074 Iustin Pop

58 82463074 Iustin Pop
  This is used in order to simplify the execution of cleanup or
59 82463074 Iustin Pop
  rollback functions.
60 82463074 Iustin Pop

61 82463074 Iustin Pop
  @rtype: boolean
62 82463074 Iustin Pop
  @return: True when fn didn't raise an exception, False otherwise
63 82463074 Iustin Pop

64 82463074 Iustin Pop
  """
65 82463074 Iustin Pop
  try:
66 82463074 Iustin Pop
    fn(*args, **kwargs)
67 82463074 Iustin Pop
    return True
68 82463074 Iustin Pop
  except errors.BlockDeviceError, err:
69 099c52ad Iustin Pop
    logging.warning("Caught BlockDeviceError but ignoring: %s", str(err))
70 82463074 Iustin Pop
    return False
71 82463074 Iustin Pop
72 82463074 Iustin Pop
73 82463074 Iustin Pop
def _ThrowError(msg, *args):
74 82463074 Iustin Pop
  """Log an error to the node daemon and the raise an exception.
75 82463074 Iustin Pop

76 82463074 Iustin Pop
  @type msg: string
77 82463074 Iustin Pop
  @param msg: the text of the exception
78 82463074 Iustin Pop
  @raise errors.BlockDeviceError
79 82463074 Iustin Pop

80 82463074 Iustin Pop
  """
81 82463074 Iustin Pop
  if args:
82 82463074 Iustin Pop
    msg = msg % args
83 82463074 Iustin Pop
  logging.error(msg)
84 82463074 Iustin Pop
  raise errors.BlockDeviceError(msg)
85 82463074 Iustin Pop
86 82463074 Iustin Pop
87 e398546b Iustin Pop
def _CheckResult(result):
88 e398546b Iustin Pop
  """Throws an error if the given result is a failed one.
89 e398546b Iustin Pop

90 e398546b Iustin Pop
  @param result: result from RunCmd
91 e398546b Iustin Pop

92 e398546b Iustin Pop
  """
93 e398546b Iustin Pop
  if result.failed:
94 e398546b Iustin Pop
    _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
95 e398546b Iustin Pop
                result.output)
96 e398546b Iustin Pop
97 e398546b Iustin Pop
98 310fbb64 Iustin Pop
def _CanReadDevice(path):
99 310fbb64 Iustin Pop
  """Check if we can read from the given device.
100 310fbb64 Iustin Pop

101 310fbb64 Iustin Pop
  This tries to read the first 128k of the device.
102 310fbb64 Iustin Pop

103 310fbb64 Iustin Pop
  """
104 310fbb64 Iustin Pop
  try:
105 310fbb64 Iustin Pop
    utils.ReadFile(path, size=_DEVICE_READ_SIZE)
106 310fbb64 Iustin Pop
    return True
107 1122eb25 Iustin Pop
  except EnvironmentError:
108 310fbb64 Iustin Pop
    logging.warning("Can't read from device %s", path, exc_info=True)
109 310fbb64 Iustin Pop
    return False
110 310fbb64 Iustin Pop
111 310fbb64 Iustin Pop
112 23e3c9b7 Michael Hanselmann
def _GetForbiddenFileStoragePaths():
113 23e3c9b7 Michael Hanselmann
  """Builds a list of path prefixes which shouldn't be used for file storage.
114 23e3c9b7 Michael Hanselmann

115 23e3c9b7 Michael Hanselmann
  @rtype: frozenset
116 23e3c9b7 Michael Hanselmann

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

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

141 23e3c9b7 Michael Hanselmann
  @type paths: list
142 23e3c9b7 Michael Hanselmann
  @param paths: List of paths to be checked
143 23e3c9b7 Michael Hanselmann
  @rtype: list
144 23e3c9b7 Michael Hanselmann
  @return: Sorted list of paths for which the user should be warned
145 23e3c9b7 Michael Hanselmann

146 23e3c9b7 Michael Hanselmann
  """
147 23e3c9b7 Michael Hanselmann
  def _Check(path):
148 23e3c9b7 Michael Hanselmann
    return (not os.path.isabs(path) or
149 23e3c9b7 Michael Hanselmann
            path in _forbidden or
150 23e3c9b7 Michael Hanselmann
            filter(lambda p: utils.IsBelowDir(p, path), _forbidden))
151 23e3c9b7 Michael Hanselmann
152 23e3c9b7 Michael Hanselmann
  return utils.NiceSort(filter(_Check, map(os.path.normpath, paths)))
153 23e3c9b7 Michael Hanselmann
154 23e3c9b7 Michael Hanselmann
155 23e3c9b7 Michael Hanselmann
def ComputeWrongFileStoragePaths(_filename=pathutils.FILE_STORAGE_PATHS_FILE):
156 23e3c9b7 Michael Hanselmann
  """Returns a list of file storage paths whose prefix is considered bad.
157 23e3c9b7 Michael Hanselmann

158 23e3c9b7 Michael Hanselmann
  See L{_ComputeWrongFileStoragePaths}.
159 23e3c9b7 Michael Hanselmann

160 23e3c9b7 Michael Hanselmann
  """
161 23e3c9b7 Michael Hanselmann
  return _ComputeWrongFileStoragePaths(_LoadAllowedFileStoragePaths(_filename))
162 23e3c9b7 Michael Hanselmann
163 23e3c9b7 Michael Hanselmann
164 fbdac0d9 Michael Hanselmann
def _CheckFileStoragePath(path, allowed):
165 fbdac0d9 Michael Hanselmann
  """Checks if a path is in a list of allowed paths for file storage.
166 fbdac0d9 Michael Hanselmann

167 fbdac0d9 Michael Hanselmann
  @type path: string
168 fbdac0d9 Michael Hanselmann
  @param path: Path to check
169 fbdac0d9 Michael Hanselmann
  @type allowed: list
170 fbdac0d9 Michael Hanselmann
  @param allowed: List of allowed paths
171 fbdac0d9 Michael Hanselmann
  @raise errors.FileStoragePathError: If the path is not allowed
172 fbdac0d9 Michael Hanselmann

173 fbdac0d9 Michael Hanselmann
  """
174 fbdac0d9 Michael Hanselmann
  if not os.path.isabs(path):
175 fbdac0d9 Michael Hanselmann
    raise errors.FileStoragePathError("File storage path must be absolute,"
176 fbdac0d9 Michael Hanselmann
                                      " got '%s'" % path)
177 fbdac0d9 Michael Hanselmann
178 fbdac0d9 Michael Hanselmann
  for i in allowed:
179 fbdac0d9 Michael Hanselmann
    if not os.path.isabs(i):
180 fbdac0d9 Michael Hanselmann
      logging.info("Ignoring relative path '%s' for file storage", i)
181 fbdac0d9 Michael Hanselmann
      continue
182 fbdac0d9 Michael Hanselmann
183 fbdac0d9 Michael Hanselmann
    if utils.IsBelowDir(i, path):
184 fbdac0d9 Michael Hanselmann
      break
185 fbdac0d9 Michael Hanselmann
  else:
186 fbdac0d9 Michael Hanselmann
    raise errors.FileStoragePathError("Path '%s' is not acceptable for file"
187 fbdac0d9 Michael Hanselmann
                                      " storage" % path)
188 fbdac0d9 Michael Hanselmann
189 fbdac0d9 Michael Hanselmann
190 23e3c9b7 Michael Hanselmann
def _LoadAllowedFileStoragePaths(filename):
191 fbdac0d9 Michael Hanselmann
  """Loads file containing allowed file storage paths.
192 fbdac0d9 Michael Hanselmann

193 fbdac0d9 Michael Hanselmann
  @rtype: list
194 fbdac0d9 Michael Hanselmann
  @return: List of allowed paths (can be an empty list)
195 fbdac0d9 Michael Hanselmann

196 fbdac0d9 Michael Hanselmann
  """
197 fbdac0d9 Michael Hanselmann
  try:
198 fbdac0d9 Michael Hanselmann
    contents = utils.ReadFile(filename)
199 fbdac0d9 Michael Hanselmann
  except EnvironmentError:
200 fbdac0d9 Michael Hanselmann
    return []
201 fbdac0d9 Michael Hanselmann
  else:
202 fbdac0d9 Michael Hanselmann
    return utils.FilterEmptyLinesAndComments(contents)
203 fbdac0d9 Michael Hanselmann
204 fbdac0d9 Michael Hanselmann
205 fbdac0d9 Michael Hanselmann
def CheckFileStoragePath(path, _filename=pathutils.FILE_STORAGE_PATHS_FILE):
206 fbdac0d9 Michael Hanselmann
  """Checks if a path is allowed for file storage.
207 fbdac0d9 Michael Hanselmann

208 fbdac0d9 Michael Hanselmann
  @type path: string
209 fbdac0d9 Michael Hanselmann
  @param path: Path to check
210 fbdac0d9 Michael Hanselmann
  @raise errors.FileStoragePathError: If the path is not allowed
211 fbdac0d9 Michael Hanselmann

212 fbdac0d9 Michael Hanselmann
  """
213 23e3c9b7 Michael Hanselmann
  allowed = _LoadAllowedFileStoragePaths(_filename)
214 23e3c9b7 Michael Hanselmann
215 23e3c9b7 Michael Hanselmann
  if _ComputeWrongFileStoragePaths([path]):
216 23e3c9b7 Michael Hanselmann
    raise errors.FileStoragePathError("Path '%s' uses a forbidden prefix" %
217 23e3c9b7 Michael Hanselmann
                                      path)
218 23e3c9b7 Michael Hanselmann
219 23e3c9b7 Michael Hanselmann
  _CheckFileStoragePath(path, allowed)
220 fbdac0d9 Michael Hanselmann
221 fbdac0d9 Michael Hanselmann
222 a8083063 Iustin Pop
class BlockDev(object):
223 a8083063 Iustin Pop
  """Block device abstract class.
224 a8083063 Iustin Pop

225 a8083063 Iustin Pop
  A block device can be in the following states:
226 a8083063 Iustin Pop
    - not existing on the system, and by `Create()` it goes into:
227 a8083063 Iustin Pop
    - existing but not setup/not active, and by `Assemble()` goes into:
228 a8083063 Iustin Pop
    - active read-write and by `Open()` it goes into
229 a8083063 Iustin Pop
    - online (=used, or ready for use)
230 a8083063 Iustin Pop

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

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

242 a8083063 Iustin Pop
  A block device is identified by three items:
243 a8083063 Iustin Pop
    - the /dev path of the device (dynamic)
244 a8083063 Iustin Pop
    - a unique ID of the device (static)
245 a8083063 Iustin Pop
    - it's major/minor pair (dynamic)
246 a8083063 Iustin Pop

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

253 a8083063 Iustin Pop
  You can get to a device in two ways:
254 a8083063 Iustin Pop
    - creating the (real) device, which returns you
255 abdf0113 Iustin Pop
      an attached instance (lvcreate)
256 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
257 a8083063 Iustin Pop

258 5454737c Iustin Pop
  The second point, the attachment to a device, is different
259 a8083063 Iustin Pop
  depending on whether the device is assembled or not. At init() time,
260 a8083063 Iustin Pop
  we search for a device with the same unique_id as us. If found,
261 a8083063 Iustin Pop
  good. It also means that the device is already assembled. If not,
262 a8083063 Iustin Pop
  after assembly we'll have our correct major/minor.
263 a8083063 Iustin Pop

264 a8083063 Iustin Pop
  """
265 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
266 a8083063 Iustin Pop
    self._children = children
267 a8083063 Iustin Pop
    self.dev_path = None
268 a8083063 Iustin Pop
    self.unique_id = unique_id
269 a8083063 Iustin Pop
    self.major = None
270 a8083063 Iustin Pop
    self.minor = None
271 cb999543 Iustin Pop
    self.attached = False
272 464f8daf Iustin Pop
    self.size = size
273 94dcbdb0 Andrea Spadaccini
    self.params = params
274 a8083063 Iustin Pop
275 a8083063 Iustin Pop
  def Assemble(self):
276 a8083063 Iustin Pop
    """Assemble the device from its components.
277 a8083063 Iustin Pop

278 f87548b5 Iustin Pop
    Implementations of this method by child classes must ensure that:
279 f87548b5 Iustin Pop
      - after the device has been assembled, it knows its major/minor
280 f87548b5 Iustin Pop
        numbers; this allows other devices (usually parents) to probe
281 f87548b5 Iustin Pop
        correctly for their children
282 f87548b5 Iustin Pop
      - calling this method on an existing, in-use device is safe
283 f87548b5 Iustin Pop
      - if the device is already configured (and in an OK state),
284 f87548b5 Iustin Pop
        this method is idempotent
285 a8083063 Iustin Pop

286 a8083063 Iustin Pop
    """
287 1063abd1 Iustin Pop
    pass
288 a8083063 Iustin Pop
289 a8083063 Iustin Pop
  def Attach(self):
290 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
291 a8083063 Iustin Pop

292 a8083063 Iustin Pop
    """
293 a8083063 Iustin Pop
    raise NotImplementedError
294 a8083063 Iustin Pop
295 a8083063 Iustin Pop
  def Close(self):
296 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
297 a8083063 Iustin Pop

298 a8083063 Iustin Pop
    """
299 a8083063 Iustin Pop
    raise NotImplementedError
300 a8083063 Iustin Pop
301 a8083063 Iustin Pop
  @classmethod
302 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
303 a8083063 Iustin Pop
    """Create the device.
304 a8083063 Iustin Pop

305 a8083063 Iustin Pop
    If the device cannot be created, it will return None
306 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
307 a8083063 Iustin Pop

308 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
309 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
310 a8083063 Iustin Pop
    enough for both creation and assembly (later).
311 a8083063 Iustin Pop

312 a8083063 Iustin Pop
    """
313 a8083063 Iustin Pop
    raise NotImplementedError
314 a8083063 Iustin Pop
315 a8083063 Iustin Pop
  def Remove(self):
316 a8083063 Iustin Pop
    """Remove this device.
317 a8083063 Iustin Pop

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

322 a8083063 Iustin Pop
    """
323 a8083063 Iustin Pop
    raise NotImplementedError
324 a8083063 Iustin Pop
325 f3e513ad Iustin Pop
  def Rename(self, new_id):
326 f3e513ad Iustin Pop
    """Rename this device.
327 f3e513ad Iustin Pop

328 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
329 f3e513ad Iustin Pop

330 f3e513ad Iustin Pop
    """
331 f3e513ad Iustin Pop
    raise NotImplementedError
332 f3e513ad Iustin Pop
333 a8083063 Iustin Pop
  def Open(self, force=False):
334 a8083063 Iustin Pop
    """Make the device ready for use.
335 a8083063 Iustin Pop

336 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
337 a8083063 Iustin Pop
    devices need this.
338 a8083063 Iustin Pop

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

342 a8083063 Iustin Pop
    """
343 a8083063 Iustin Pop
    raise NotImplementedError
344 a8083063 Iustin Pop
345 a8083063 Iustin Pop
  def Shutdown(self):
346 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
347 a8083063 Iustin Pop

348 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
349 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
350 a8083063 Iustin Pop
    assembled after this call.
351 a8083063 Iustin Pop

352 a8083063 Iustin Pop
    """
353 a8083063 Iustin Pop
    raise NotImplementedError
354 a8083063 Iustin Pop
355 f2f57b6e Andrea Spadaccini
  def SetSyncParams(self, params):
356 f2f57b6e Andrea Spadaccini
    """Adjust the synchronization parameters of the mirror.
357 a8083063 Iustin Pop

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

360 f2f57b6e Andrea Spadaccini
    @param params: dictionary of LD level disk parameters related to the
361 f2f57b6e Andrea Spadaccini
    synchronization.
362 8584e922 Andrea Spadaccini
    @rtype: list
363 8584e922 Andrea Spadaccini
    @return: a list of error messages, emitted both by the current node and by
364 8584e922 Andrea Spadaccini
    children. An empty list means no errors.
365 f2f57b6e Andrea Spadaccini

366 a8083063 Iustin Pop
    """
367 8584e922 Andrea Spadaccini
    result = []
368 a8083063 Iustin Pop
    if self._children:
369 a8083063 Iustin Pop
      for child in self._children:
370 8584e922 Andrea Spadaccini
        result.extend(child.SetSyncParams(params))
371 a8083063 Iustin Pop
    return result
372 a8083063 Iustin Pop
373 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
374 a3fffcc6 René Nussbaumer
    """Pause/Resume the sync of the mirror.
375 a3fffcc6 René Nussbaumer

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

378 f2f57b6e Andrea Spadaccini
    @param pause: Whether to pause or resume
379 a3fffcc6 René Nussbaumer

380 a3fffcc6 René Nussbaumer
    """
381 a3fffcc6 René Nussbaumer
    result = True
382 a3fffcc6 René Nussbaumer
    if self._children:
383 a3fffcc6 René Nussbaumer
      for child in self._children:
384 a3fffcc6 René Nussbaumer
        result = result and child.PauseResumeSync(pause)
385 a3fffcc6 René Nussbaumer
    return result
386 a3fffcc6 René Nussbaumer
387 a8083063 Iustin Pop
  def GetSyncStatus(self):
388 a8083063 Iustin Pop
    """Returns the sync status of the device.
389 a8083063 Iustin Pop

390 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
391 a8083063 Iustin Pop
    status of the mirror.
392 a8083063 Iustin Pop

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

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

398 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
399 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
400 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
401 a8083063 Iustin Pop

402 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
403 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
404 0834c866 Iustin Pop
    return False (not degraded).
405 0834c866 Iustin Pop

406 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
407 c41eea6e Iustin Pop

408 a8083063 Iustin Pop
    """
409 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
410 96acbc09 Michael Hanselmann
                                  major=self.major,
411 96acbc09 Michael Hanselmann
                                  minor=self.minor,
412 96acbc09 Michael Hanselmann
                                  sync_percent=None,
413 96acbc09 Michael Hanselmann
                                  estimated_time=None,
414 96acbc09 Michael Hanselmann
                                  is_degraded=False,
415 f208978a Michael Hanselmann
                                  ldisk_status=constants.LDS_OKAY)
416 a8083063 Iustin Pop
417 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
418 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
419 a8083063 Iustin Pop

420 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
421 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
422 a8083063 Iustin Pop
    children.
423 a8083063 Iustin Pop

424 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
425 96acbc09 Michael Hanselmann

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

466 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
467 a0c3fea1 Michael Hanselmann

468 a0c3fea1 Michael Hanselmann
    """
469 a0c3fea1 Michael Hanselmann
    for child in self._children:
470 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
471 a0c3fea1 Michael Hanselmann
472 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
473 1005d816 Iustin Pop
    """Grow the block device.
474 1005d816 Iustin Pop

475 7fe23d47 Iustin Pop
    @type amount: integer
476 c41eea6e Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
477 7fe23d47 Iustin Pop
    @type dryrun: boolean
478 7fe23d47 Iustin Pop
    @param dryrun: whether to execute the operation in simulation mode
479 7fe23d47 Iustin Pop
        only, without actually increasing the size
480 cad0723b Iustin Pop
    @param backingstore: whether to execute the operation on backing storage
481 cad0723b Iustin Pop
        only, or on "logical" storage only; e.g. DRBD is logical storage,
482 cad0723b Iustin Pop
        whereas LVM, file, RBD are backing storage
483 1005d816 Iustin Pop

484 1005d816 Iustin Pop
    """
485 1005d816 Iustin Pop
    raise NotImplementedError
486 a0c3fea1 Michael Hanselmann
487 fcff3897 Iustin Pop
  def GetActualSize(self):
488 fcff3897 Iustin Pop
    """Return the actual disk size.
489 fcff3897 Iustin Pop

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

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

513 a8083063 Iustin Pop
  """
514 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
515 b8028dcf Michael Hanselmann
  _INVALID_NAMES = compat.UniqueFrozenset([".", "..", "snapshot", "pvmove"])
516 b8028dcf Michael Hanselmann
  _INVALID_SUBSTRINGS = compat.UniqueFrozenset(["_mlog", "_mimage"])
517 6136f8f0 Iustin Pop
518 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
519 a8083063 Iustin Pop
    """Attaches to a LV device.
520 a8083063 Iustin Pop

521 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
522 a8083063 Iustin Pop

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

539 63c73073 Bernardo Dal Seno
    @param pvs_info: list of objects.LvmPvInfo, cannot be empty
540 63c73073 Bernardo Dal Seno
    @rtype: float
541 63c73073 Bernardo Dal Seno
    @return: size in MiB
542 63c73073 Bernardo Dal Seno

543 63c73073 Bernardo Dal Seno
    """
544 63c73073 Bernardo Dal Seno
    assert len(pvs_info) > 0
545 63c73073 Bernardo Dal Seno
    smallest = min([pv.size for pv in pvs_info])
546 63c73073 Bernardo Dal Seno
    return smallest / (1 + constants.PART_MARGIN + constants.PART_RESERVED)
547 63c73073 Bernardo Dal Seno
548 63c73073 Bernardo Dal Seno
  @staticmethod
549 63c73073 Bernardo Dal Seno
  def _ComputeNumPvs(size, pvs_info):
550 63c73073 Bernardo Dal Seno
    """Compute the number of PVs needed for an LV (with exclusive storage).
551 63c73073 Bernardo Dal Seno

552 63c73073 Bernardo Dal Seno
    @type size: float
553 23d95cff Bernardo Dal Seno
    @param size: LV size in MiB
554 63c73073 Bernardo Dal Seno
    @param pvs_info: list of objects.LvmPvInfo, cannot be empty
555 63c73073 Bernardo Dal Seno
    @rtype: integer
556 63c73073 Bernardo Dal Seno
    @return: number of PVs needed
557 63c73073 Bernardo Dal Seno
    """
558 63c73073 Bernardo Dal Seno
    assert len(pvs_info) > 0
559 63c73073 Bernardo Dal Seno
    pv_size = float(LogicalVolume._GetStdPvSize(pvs_info))
560 63c73073 Bernardo Dal Seno
    return int(math.ceil(float(size) / pv_size))
561 63c73073 Bernardo Dal Seno
562 63c73073 Bernardo Dal Seno
  @staticmethod
563 63c73073 Bernardo Dal Seno
  def _GetEmptyPvNames(pvs_info, max_pvs=None):
564 63c73073 Bernardo Dal Seno
    """Return a list of empty PVs, by name.
565 63c73073 Bernardo Dal Seno

566 63c73073 Bernardo Dal Seno
    """
567 63c73073 Bernardo Dal Seno
    empty_pvs = filter(objects.LvmPvInfo.IsEmpty, pvs_info)
568 63c73073 Bernardo Dal Seno
    if max_pvs is not None:
569 63c73073 Bernardo Dal Seno
      empty_pvs = empty_pvs[:max_pvs]
570 63c73073 Bernardo Dal Seno
    return map((lambda pv: pv.name), empty_pvs)
571 63c73073 Bernardo Dal Seno
572 a8083063 Iustin Pop
  @classmethod
573 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
574 a8083063 Iustin Pop
    """Create a new logical volume.
575 a8083063 Iustin Pop

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

647 197478f2 René Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
648 197478f2 René Nussbaumer
    @param fields: Fields to return
649 197478f2 René Nussbaumer
    @return: A list of dicts each with the parsed fields
650 197478f2 René Nussbaumer

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

680 2070598f Iustin Pop
    @param vg_names: list of volume group names, if empty all will be returned
681 2070598f Iustin Pop
    @param filter_allocatable: whether to skip over unallocatable PVs
682 b496abdb Bernardo Dal Seno
    @param include_lvs: whether to include a list of LVs hosted on each PV
683 a8083063 Iustin Pop

684 c41eea6e Iustin Pop
    @rtype: list
685 59726e15 Bernardo Dal Seno
    @return: list of objects.LvmPvInfo objects
686 098c0958 Michael Hanselmann

687 a8083063 Iustin Pop
    """
688 b496abdb Bernardo Dal Seno
    # We request "lv_name" field only if we care about LVs, so we don't get
689 b496abdb Bernardo Dal Seno
    # a long list of entries with many duplicates unless we really have to.
690 b496abdb Bernardo Dal Seno
    # The duplicate "pv_name" field will be ignored.
691 b496abdb Bernardo Dal Seno
    if include_lvs:
692 b496abdb Bernardo Dal Seno
      lvfield = "lv_name"
693 b496abdb Bernardo Dal Seno
    else:
694 b496abdb Bernardo Dal Seno
      lvfield = "pv_name"
695 197478f2 René Nussbaumer
    try:
696 197478f2 René Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
697 b496abdb Bernardo Dal Seno
                                        "pv_attr", "pv_size", lvfield])
698 197478f2 René Nussbaumer
    except errors.GenericError, err:
699 197478f2 René Nussbaumer
      logging.error("Can't get PV information: %s", err)
700 a8083063 Iustin Pop
      return None
701 197478f2 René Nussbaumer
702 b496abdb Bernardo Dal Seno
    # When asked for LVs, "pvs" may return multiple entries for the same PV-LV
703 b496abdb Bernardo Dal Seno
    # pair. We sort entries by PV name and then LV name, so it's easy to weed
704 b496abdb Bernardo Dal Seno
    # out duplicates.
705 b496abdb Bernardo Dal Seno
    if include_lvs:
706 b496abdb Bernardo Dal Seno
      info.sort(key=(lambda i: (i[0], i[5])))
707 a8083063 Iustin Pop
    data = []
708 b496abdb Bernardo Dal Seno
    lastpvi = None
709 b496abdb Bernardo Dal Seno
    for (pv_name, vg_name, pv_free, pv_attr, pv_size, lv_name) in info:
710 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
711 197478f2 René Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
712 a8083063 Iustin Pop
        continue
713 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
714 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
715 2070598f Iustin Pop
        continue
716 b496abdb Bernardo Dal Seno
      # Beware of duplicates (check before inserting)
717 b496abdb Bernardo Dal Seno
      if lastpvi and lastpvi.name == pv_name:
718 b496abdb Bernardo Dal Seno
        if include_lvs and lv_name:
719 b496abdb Bernardo Dal Seno
          if not lastpvi.lv_list or lastpvi.lv_list[-1] != lv_name:
720 b496abdb Bernardo Dal Seno
            lastpvi.lv_list.append(lv_name)
721 b496abdb Bernardo Dal Seno
      else:
722 b496abdb Bernardo Dal Seno
        if include_lvs and lv_name:
723 b496abdb Bernardo Dal Seno
          lvl = [lv_name]
724 b496abdb Bernardo Dal Seno
        else:
725 b496abdb Bernardo Dal Seno
          lvl = []
726 b496abdb Bernardo Dal Seno
        lastpvi = objects.LvmPvInfo(name=pv_name, vg_name=vg_name,
727 b496abdb Bernardo Dal Seno
                                    size=float(pv_size), free=float(pv_free),
728 b496abdb Bernardo Dal Seno
                                    attributes=pv_attr, lv_list=lvl)
729 b496abdb Bernardo Dal Seno
        data.append(lastpvi)
730 197478f2 René Nussbaumer
731 197478f2 René Nussbaumer
    return data
732 197478f2 René Nussbaumer
733 197478f2 René Nussbaumer
  @classmethod
734 61481c52 Bernardo Dal Seno
  def _GetExclusiveStorageVgFree(cls, vg_name):
735 61481c52 Bernardo Dal Seno
    """Return the free disk space in the given VG, in exclusive storage mode.
736 61481c52 Bernardo Dal Seno

737 61481c52 Bernardo Dal Seno
    @type vg_name: string
738 61481c52 Bernardo Dal Seno
    @param vg_name: VG name
739 61481c52 Bernardo Dal Seno
    @rtype: float
740 61481c52 Bernardo Dal Seno
    @return: free space in MiB
741 61481c52 Bernardo Dal Seno
    """
742 61481c52 Bernardo Dal Seno
    pvs_info = cls.GetPVInfo([vg_name])
743 61481c52 Bernardo Dal Seno
    if not pvs_info:
744 61481c52 Bernardo Dal Seno
      return 0.0
745 61481c52 Bernardo Dal Seno
    pv_size = cls._GetStdPvSize(pvs_info)
746 61481c52 Bernardo Dal Seno
    num_pvs = len(cls._GetEmptyPvNames(pvs_info))
747 61481c52 Bernardo Dal Seno
    return pv_size * num_pvs
748 61481c52 Bernardo Dal Seno
749 61481c52 Bernardo Dal Seno
  @classmethod
750 1a3c5d4e Bernardo Dal Seno
  def GetVGInfo(cls, vg_names, excl_stor, filter_readonly=True):
751 197478f2 René Nussbaumer
    """Get the free space info for specific VGs.
752 197478f2 René Nussbaumer

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

757 197478f2 René Nussbaumer
    @rtype: list
758 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
759 673cd9c4 René Nussbaumer
             MiB
760 197478f2 René Nussbaumer

761 197478f2 René Nussbaumer
    """
762 197478f2 René Nussbaumer
    try:
763 673cd9c4 René Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
764 673cd9c4 René Nussbaumer
                                        "vg_size"])
765 197478f2 René Nussbaumer
    except errors.GenericError, err:
766 197478f2 René Nussbaumer
      logging.error("Can't get VG information: %s", err)
767 197478f2 René Nussbaumer
      return None
768 197478f2 René Nussbaumer
769 197478f2 René Nussbaumer
    data = []
770 673cd9c4 René Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
771 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not writable
772 197478f2 René Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
773 197478f2 René Nussbaumer
        continue
774 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
775 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
776 197478f2 René Nussbaumer
        continue
777 61481c52 Bernardo Dal Seno
      # Exclusive storage needs a different concept of free space
778 61481c52 Bernardo Dal Seno
      if excl_stor:
779 61481c52 Bernardo Dal Seno
        es_free = cls._GetExclusiveStorageVgFree(vg_name)
780 61481c52 Bernardo Dal Seno
        assert es_free <= vg_free
781 61481c52 Bernardo Dal Seno
        vg_free = es_free
782 673cd9c4 René Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
783 a8083063 Iustin Pop
784 a8083063 Iustin Pop
    return data
785 a8083063 Iustin Pop
786 6136f8f0 Iustin Pop
  @classmethod
787 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
788 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
789 6136f8f0 Iustin Pop

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

794 6136f8f0 Iustin Pop
    """
795 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
796 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
797 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
798 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
799 6136f8f0 Iustin Pop
800 a8083063 Iustin Pop
  def Remove(self):
801 a8083063 Iustin Pop
    """Remove this logical volume.
802 a8083063 Iustin Pop

803 a8083063 Iustin Pop
    """
804 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
805 a8083063 Iustin Pop
      # the LV does not exist
806 0c6c04ec Iustin Pop
      return
807 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
808 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
809 a8083063 Iustin Pop
    if result.failed:
810 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
811 a8083063 Iustin Pop
812 f3e513ad Iustin Pop
  def Rename(self, new_id):
813 f3e513ad Iustin Pop
    """Rename this logical volume.
814 f3e513ad Iustin Pop

815 f3e513ad Iustin Pop
    """
816 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
817 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
818 f3e513ad Iustin Pop
    new_vg, new_name = new_id
819 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
820 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
821 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
822 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
823 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
824 f3e513ad Iustin Pop
    if result.failed:
825 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
826 be345db0 Iustin Pop
    self._lv_name = new_name
827 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
828 be345db0 Iustin Pop
829 a8083063 Iustin Pop
  def Attach(self):
830 a8083063 Iustin Pop
    """Attach to an existing LV.
831 a8083063 Iustin Pop

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

836 a8083063 Iustin Pop
    """
837 cb999543 Iustin Pop
    self.attached = False
838 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
839 bc3427b7 Aaron Knister
                           "--units=k", "--nosuffix",
840 38256320 Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
841 38256320 Iustin Pop
                           "vg_extent_size,stripes", self.dev_path])
842 a8083063 Iustin Pop
    if result.failed:
843 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
844 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
845 a8083063 Iustin Pop
      return False
846 38256320 Iustin Pop
    # the output can (and will) have multiple lines for multi-segment
847 38256320 Iustin Pop
    # LVs, as the 'stripes' parameter is a segment one, so we take
848 38256320 Iustin Pop
    # only the last entry, which is the one we're interested in; note
849 38256320 Iustin Pop
    # that with LVM2 anyway the 'stripes' value must be constant
850 38256320 Iustin Pop
    # across segments, so this is a no-op actually
851 38256320 Iustin Pop
    out = result.stdout.splitlines()
852 38256320 Iustin Pop
    if not out: # totally empty result? splitlines() returns at least
853 38256320 Iustin Pop
                # one line for any non-empty string
854 38256320 Iustin Pop
      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
855 38256320 Iustin Pop
      return False
856 d0c8c01d Iustin Pop
    out = out[-1].strip().rstrip(",")
857 99e8295c Iustin Pop
    out = out.split(",")
858 38256320 Iustin Pop
    if len(out) != 5:
859 38256320 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
860 99e8295c Iustin Pop
      return False
861 99e8295c Iustin Pop
862 38256320 Iustin Pop
    status, major, minor, pe_size, stripes = out
863 0304f0ec Iustin Pop
    if len(status) < 6:
864 0304f0ec Iustin Pop
      logging.error("lvs lv_attr is not at least 6 characters (%s)", status)
865 99e8295c Iustin Pop
      return False
866 99e8295c Iustin Pop
867 99e8295c Iustin Pop
    try:
868 99e8295c Iustin Pop
      major = int(major)
869 99e8295c Iustin Pop
      minor = int(minor)
870 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
871 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
872 99e8295c Iustin Pop
873 38256320 Iustin Pop
    try:
874 38256320 Iustin Pop
      pe_size = int(float(pe_size))
875 38256320 Iustin Pop
    except (TypeError, ValueError), err:
876 38256320 Iustin Pop
      logging.error("Can't parse vg extent size: %s", err)
877 38256320 Iustin Pop
      return False
878 38256320 Iustin Pop
879 38256320 Iustin Pop
    try:
880 38256320 Iustin Pop
      stripes = int(stripes)
881 38256320 Iustin Pop
    except (TypeError, ValueError), err:
882 38256320 Iustin Pop
      logging.error("Can't parse the number of stripes: %s", err)
883 38256320 Iustin Pop
      return False
884 38256320 Iustin Pop
885 99e8295c Iustin Pop
    self.major = major
886 99e8295c Iustin Pop
    self.minor = minor
887 38256320 Iustin Pop
    self.pe_size = pe_size
888 38256320 Iustin Pop
    self.stripe_count = stripes
889 d0c8c01d Iustin Pop
    self._degraded = status[0] == "v" # virtual volume, i.e. doesn't backing
890 99e8295c Iustin Pop
                                      # storage
891 cb999543 Iustin Pop
    self.attached = True
892 99e8295c Iustin Pop
    return True
893 a8083063 Iustin Pop
894 a8083063 Iustin Pop
  def Assemble(self):
895 a8083063 Iustin Pop
    """Assemble the device.
896 a8083063 Iustin Pop

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

901 a8083063 Iustin Pop
    """
902 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
903 5574047a Iustin Pop
    if result.failed:
904 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
905 a8083063 Iustin Pop
906 a8083063 Iustin Pop
  def Shutdown(self):
907 a8083063 Iustin Pop
    """Shutdown the device.
908 a8083063 Iustin Pop

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

912 a8083063 Iustin Pop
    """
913 746f7476 Iustin Pop
    pass
914 a8083063 Iustin Pop
915 9db6dbce Iustin Pop
  def GetSyncStatus(self):
916 9db6dbce Iustin Pop
    """Returns the sync status of the device.
917 9db6dbce Iustin Pop

918 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
919 9db6dbce Iustin Pop
    status of the mirror.
920 9db6dbce Iustin Pop

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

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

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

934 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
935 c41eea6e Iustin Pop

936 9db6dbce Iustin Pop
    """
937 f208978a Michael Hanselmann
    if self._degraded:
938 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
939 f208978a Michael Hanselmann
    else:
940 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
941 f208978a Michael Hanselmann
942 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
943 96acbc09 Michael Hanselmann
                                  major=self.major,
944 96acbc09 Michael Hanselmann
                                  minor=self.minor,
945 96acbc09 Michael Hanselmann
                                  sync_percent=None,
946 96acbc09 Michael Hanselmann
                                  estimated_time=None,
947 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
948 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
949 9db6dbce Iustin Pop
950 a8083063 Iustin Pop
  def Open(self, force=False):
951 a8083063 Iustin Pop
    """Make the device ready for I/O.
952 a8083063 Iustin Pop

953 a8083063 Iustin Pop
    This is a no-op for the LV device type.
954 a8083063 Iustin Pop

955 a8083063 Iustin Pop
    """
956 fdbd668d Iustin Pop
    pass
957 a8083063 Iustin Pop
958 a8083063 Iustin Pop
  def Close(self):
959 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
960 a8083063 Iustin Pop

961 a8083063 Iustin Pop
    This is a no-op for the LV device type.
962 a8083063 Iustin Pop

963 a8083063 Iustin Pop
    """
964 fdbd668d Iustin Pop
    pass
965 a8083063 Iustin Pop
966 a8083063 Iustin Pop
  def Snapshot(self, size):
967 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
968 a8083063 Iustin Pop

969 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
970 800ac399 Iustin Pop

971 a8083063 Iustin Pop
    """
972 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
973 a8083063 Iustin Pop
974 a8083063 Iustin Pop
    # remove existing snapshot if found
975 94dcbdb0 Andrea Spadaccini
    snap = LogicalVolume((self._vg_name, snap_name), None, size, self.params)
976 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
977 a8083063 Iustin Pop
978 1a3c5d4e Bernardo Dal Seno
    vg_info = self.GetVGInfo([self._vg_name], False)
979 197478f2 René Nussbaumer
    if not vg_info:
980 197478f2 René Nussbaumer
      _ThrowError("Can't compute VG info for vg %s", self._vg_name)
981 673cd9c4 René Nussbaumer
    free_size, _, _ = vg_info[0]
982 a8083063 Iustin Pop
    if free_size < size:
983 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
984 82463074 Iustin Pop
                  " available %s", size, free_size)
985 a8083063 Iustin Pop
986 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
987 e398546b Iustin Pop
                               "-n%s" % snap_name, self.dev_path]))
988 a8083063 Iustin Pop
989 800ac399 Iustin Pop
    return (self._vg_name, snap_name)
990 a8083063 Iustin Pop
991 a1556cfa Iustin Pop
  def _RemoveOldInfo(self):
992 a1556cfa Iustin Pop
    """Try to remove old tags from the lv.
993 a1556cfa Iustin Pop

994 a1556cfa Iustin Pop
    """
995 a1556cfa Iustin Pop
    result = utils.RunCmd(["lvs", "-o", "tags", "--noheadings", "--nosuffix",
996 a1556cfa Iustin Pop
                           self.dev_path])
997 a1556cfa Iustin Pop
    _CheckResult(result)
998 a1556cfa Iustin Pop
999 a1556cfa Iustin Pop
    raw_tags = result.stdout.strip()
1000 a1556cfa Iustin Pop
    if raw_tags:
1001 a1556cfa Iustin Pop
      for tag in raw_tags.split(","):
1002 a1556cfa Iustin Pop
        _CheckResult(utils.RunCmd(["lvchange", "--deltag",
1003 a1556cfa Iustin Pop
                                   tag.strip(), self.dev_path]))
1004 a1556cfa Iustin Pop
1005 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
1006 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
1007 a0c3fea1 Michael Hanselmann

1008 a0c3fea1 Michael Hanselmann
    """
1009 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
1010 a0c3fea1 Michael Hanselmann
1011 a1556cfa Iustin Pop
    self._RemoveOldInfo()
1012 a1556cfa Iustin Pop
1013 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
1014 d0c8c01d Iustin Pop
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
1015 d0c8c01d Iustin Pop
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
1016 a0c3fea1 Michael Hanselmann
1017 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
1018 a0c3fea1 Michael Hanselmann
    text = text[:128]
1019 a0c3fea1 Michael Hanselmann
1020 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvchange", "--addtag", text, self.dev_path]))
1021 82463074 Iustin Pop
1022 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
1023 1005d816 Iustin Pop
    """Grow the logical volume.
1024 1005d816 Iustin Pop

1025 1005d816 Iustin Pop
    """
1026 cad0723b Iustin Pop
    if not backingstore:
1027 cad0723b Iustin Pop
      return
1028 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
1029 38256320 Iustin Pop
      if not self.Attach():
1030 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
1031 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
1032 bc3427b7 Aaron Knister
    # pe_size is in KB
1033 bc3427b7 Aaron Knister
    amount *= 1024
1034 38256320 Iustin Pop
    rest = amount % full_stripe_size
1035 38256320 Iustin Pop
    if rest != 0:
1036 38256320 Iustin Pop
      amount += full_stripe_size - rest
1037 bc3427b7 Aaron Knister
    cmd = ["lvextend", "-L", "+%dk" % amount]
1038 7fe23d47 Iustin Pop
    if dryrun:
1039 7fe23d47 Iustin Pop
      cmd.append("--test")
1040 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
1041 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
1042 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
1043 1005d816 Iustin Pop
    # supports 'cling'
1044 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
1045 7fe23d47 Iustin Pop
      result = utils.RunCmd(cmd + ["--alloc", alloc_policy, self.dev_path])
1046 1005d816 Iustin Pop
      if not result.failed:
1047 1005d816 Iustin Pop
        return
1048 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
1049 a0c3fea1 Michael Hanselmann
1050 a0c3fea1 Michael Hanselmann
1051 6b90c22e Iustin Pop
class DRBD8Status(object):
1052 6b90c22e Iustin Pop
  """A DRBD status representation class.
1053 6b90c22e Iustin Pop

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

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

1162 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
1163 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
1164 0f7f32d9 Iustin Pop

1165 abdf0113 Iustin Pop
  """
1166 fcee765d Manuel Franceschini
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)(?:\.\d+)?"
1167 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
1168 9122e60a Iustin Pop
  _VALID_LINE_RE = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
1169 9122e60a Iustin Pop
  _UNUSED_LINE_RE = re.compile("^ *([0-9]+): cs:Unconfigured$")
1170 a8083063 Iustin Pop
1171 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
1172 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
1173 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
1174 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
1175 a8083063 Iustin Pop
1176 61e062dd Michele Tartara
  _STATUS_FILE = constants.DRBD_STATUS_FILE
1177 549071a0 Luca Bigliardi
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
1178 6b90c22e Iustin Pop
1179 abdf0113 Iustin Pop
  @staticmethod
1180 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
1181 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
1182 a8083063 Iustin Pop

1183 a8083063 Iustin Pop
    """
1184 abdf0113 Iustin Pop
    try:
1185 13998ef2 Michael Hanselmann
      data = utils.ReadFile(filename).splitlines()
1186 f6eaed12 Iustin Pop
    except EnvironmentError, err:
1187 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
1188 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
1189 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
1190 f6eaed12 Iustin Pop
      else:
1191 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
1192 abdf0113 Iustin Pop
    if not data:
1193 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
1194 abdf0113 Iustin Pop
    return data
1195 a8083063 Iustin Pop
1196 9122e60a Iustin Pop
  @classmethod
1197 9122e60a Iustin Pop
  def _MassageProcData(cls, data):
1198 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
1199 a8083063 Iustin Pop

1200 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
1201 c41eea6e Iustin Pop
        for that minor
1202 a8083063 Iustin Pop

1203 a8083063 Iustin Pop
    """
1204 abdf0113 Iustin Pop
    results = {}
1205 abdf0113 Iustin Pop
    old_minor = old_line = None
1206 abdf0113 Iustin Pop
    for line in data:
1207 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
1208 67d101d4 Iustin Pop
        continue
1209 9122e60a Iustin Pop
      lresult = cls._VALID_LINE_RE.match(line)
1210 abdf0113 Iustin Pop
      if lresult is not None:
1211 abdf0113 Iustin Pop
        if old_minor is not None:
1212 abdf0113 Iustin Pop
          results[old_minor] = old_line
1213 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
1214 abdf0113 Iustin Pop
        old_line = line
1215 abdf0113 Iustin Pop
      else:
1216 abdf0113 Iustin Pop
        if old_minor is not None:
1217 abdf0113 Iustin Pop
          old_line += " " + line.strip()
1218 abdf0113 Iustin Pop
    # add last line
1219 abdf0113 Iustin Pop
    if old_minor is not None:
1220 abdf0113 Iustin Pop
      results[old_minor] = old_line
1221 abdf0113 Iustin Pop
    return results
1222 a8083063 Iustin Pop
1223 abdf0113 Iustin Pop
  @classmethod
1224 fcee765d Manuel Franceschini
  def _GetVersion(cls, proc_data):
1225 abdf0113 Iustin Pop
    """Return the DRBD version.
1226 a8083063 Iustin Pop

1227 abdf0113 Iustin Pop
    This will return a dict with keys:
1228 c41eea6e Iustin Pop
      - k_major
1229 c41eea6e Iustin Pop
      - k_minor
1230 c41eea6e Iustin Pop
      - k_point
1231 c41eea6e Iustin Pop
      - api
1232 c41eea6e Iustin Pop
      - proto
1233 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
1234 a8083063 Iustin Pop

1235 a8083063 Iustin Pop
    """
1236 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
1237 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
1238 abdf0113 Iustin Pop
    if not version:
1239 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
1240 abdf0113 Iustin Pop
                                    first_line)
1241 a8083063 Iustin Pop
1242 abdf0113 Iustin Pop
    values = version.groups()
1243 5ae4945a Iustin Pop
    retval = {
1244 5ae4945a Iustin Pop
      "k_major": int(values[0]),
1245 5ae4945a Iustin Pop
      "k_minor": int(values[1]),
1246 5ae4945a Iustin Pop
      "k_point": int(values[2]),
1247 5ae4945a Iustin Pop
      "api": int(values[3]),
1248 5ae4945a Iustin Pop
      "proto": int(values[4]),
1249 5ae4945a Iustin Pop
      }
1250 abdf0113 Iustin Pop
    if values[5] is not None:
1251 d0c8c01d Iustin Pop
      retval["proto2"] = values[5]
1252 a8083063 Iustin Pop
1253 abdf0113 Iustin Pop
    return retval
1254 abdf0113 Iustin Pop
1255 abdf0113 Iustin Pop
  @staticmethod
1256 549071a0 Luca Bigliardi
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
1257 549071a0 Luca Bigliardi
    """Returns DRBD usermode_helper currently set.
1258 549071a0 Luca Bigliardi

1259 549071a0 Luca Bigliardi
    """
1260 549071a0 Luca Bigliardi
    try:
1261 549071a0 Luca Bigliardi
      helper = utils.ReadFile(filename).splitlines()[0]
1262 549071a0 Luca Bigliardi
    except EnvironmentError, err:
1263 549071a0 Luca Bigliardi
      if err.errno == errno.ENOENT:
1264 549071a0 Luca Bigliardi
        _ThrowError("The file %s cannot be opened, check if the module"
1265 549071a0 Luca Bigliardi
                    " is loaded (%s)", filename, str(err))
1266 549071a0 Luca Bigliardi
      else:
1267 549071a0 Luca Bigliardi
        _ThrowError("Can't read DRBD helper file %s: %s", filename, str(err))
1268 549071a0 Luca Bigliardi
    if not helper:
1269 549071a0 Luca Bigliardi
      _ThrowError("Can't read any data from %s", filename)
1270 549071a0 Luca Bigliardi
    return helper
1271 549071a0 Luca Bigliardi
1272 549071a0 Luca Bigliardi
  @staticmethod
1273 abdf0113 Iustin Pop
  def _DevPath(minor):
1274 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
1275 a8083063 Iustin Pop

1276 a8083063 Iustin Pop
    """
1277 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
1278 a8083063 Iustin Pop
1279 abdf0113 Iustin Pop
  @classmethod
1280 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
1281 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
1282 a8083063 Iustin Pop

1283 a8083063 Iustin Pop
    """
1284 abdf0113 Iustin Pop
    data = cls._GetProcData()
1285 a8083063 Iustin Pop
1286 abdf0113 Iustin Pop
    used_devs = {}
1287 abdf0113 Iustin Pop
    for line in data:
1288 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1289 abdf0113 Iustin Pop
      if not match:
1290 abdf0113 Iustin Pop
        continue
1291 abdf0113 Iustin Pop
      minor = int(match.group(1))
1292 abdf0113 Iustin Pop
      state = match.group(2)
1293 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1294 abdf0113 Iustin Pop
        continue
1295 abdf0113 Iustin Pop
      used_devs[minor] = state, line
1296 a8083063 Iustin Pop
1297 abdf0113 Iustin Pop
    return used_devs
1298 a8083063 Iustin Pop
1299 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
1300 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
1301 0834c866 Iustin Pop

1302 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1303 a8083063 Iustin Pop

1304 a8083063 Iustin Pop
    """
1305 abdf0113 Iustin Pop
    if minor is None:
1306 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
1307 cb999543 Iustin Pop
      self.attached = False
1308 a8083063 Iustin Pop
    else:
1309 abdf0113 Iustin Pop
      self.minor = minor
1310 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
1311 cb999543 Iustin Pop
      self.attached = True
1312 a8083063 Iustin Pop
1313 a8083063 Iustin Pop
  @staticmethod
1314 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
1315 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
1316 a8083063 Iustin Pop

1317 3bc145d8 Bernardo Dal Seno
    This currently only checks the size, which must be around
1318 abdf0113 Iustin Pop
    128MiB.
1319 a8083063 Iustin Pop

1320 a8083063 Iustin Pop
    """
1321 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1322 abdf0113 Iustin Pop
    if result.failed:
1323 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
1324 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
1325 a8083063 Iustin Pop
    try:
1326 abdf0113 Iustin Pop
      sectors = int(result.stdout)
1327 691744c4 Iustin Pop
    except (TypeError, ValueError):
1328 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
1329 c04bc777 Iustin Pop
    num_bytes = sectors * 512
1330 c04bc777 Iustin Pop
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
1331 c04bc777 Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (num_bytes / 1024 / 1024))
1332 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
1333 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
1334 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
1335 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
1336 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
1337 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
1338 c04bc777 Iustin Pop
    if num_bytes > 1024 * 1024 * 1024:
1339 c04bc777 Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (num_bytes / 1024 / 1024))
1340 a8083063 Iustin Pop
1341 abdf0113 Iustin Pop
  def Rename(self, new_id):
1342 abdf0113 Iustin Pop
    """Rename a device.
1343 a8083063 Iustin Pop

1344 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1345 a8083063 Iustin Pop

1346 a8083063 Iustin Pop
    """
1347 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1348 a8083063 Iustin Pop
1349 f3e513ad Iustin Pop
1350 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1351 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1352 a2cfdea2 Iustin Pop

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

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

1362 a2cfdea2 Iustin Pop
  """
1363 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1364 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1365 a2cfdea2 Iustin Pop
1366 cf8df3f3 Iustin Pop
  # timeout constants
1367 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
1368 cf8df3f3 Iustin Pop
1369 8a69b3a8 Andrea Spadaccini
  # command line options for barriers
1370 8a69b3a8 Andrea Spadaccini
  _DISABLE_DISK_OPTION = "--no-disk-barrier"  # -a
1371 8a69b3a8 Andrea Spadaccini
  _DISABLE_DRAIN_OPTION = "--no-disk-drain"   # -D
1372 8a69b3a8 Andrea Spadaccini
  _DISABLE_FLUSH_OPTION = "--no-disk-flushes" # -i
1373 8a69b3a8 Andrea Spadaccini
  _DISABLE_META_FLUSH_OPTION = "--no-md-flushes"  # -m
1374 8a69b3a8 Andrea Spadaccini
1375 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
1376 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1377 fc1dc9d7 Iustin Pop
      children = []
1378 310fbb64 Iustin Pop
    if len(children) not in (0, 2):
1379 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1380 310fbb64 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
1381 310fbb64 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1382 310fbb64 Iustin Pop
    (self._lhost, self._lport,
1383 310fbb64 Iustin Pop
     self._rhost, self._rport,
1384 310fbb64 Iustin Pop
     self._aminor, self._secret) = unique_id
1385 310fbb64 Iustin Pop
    if children:
1386 310fbb64 Iustin Pop
      if not _CanReadDevice(children[1].dev_path):
1387 310fbb64 Iustin Pop
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
1388 310fbb64 Iustin Pop
        children = []
1389 94dcbdb0 Andrea Spadaccini
    super(DRBD8, self).__init__(unique_id, children, size, params)
1390 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1391 fcee765d Manuel Franceschini
    version = self._GetVersion(self._GetProcData())
1392 e687ec01 Michael Hanselmann
    if version["k_major"] != 8:
1393 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
1394 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
1395 d0c8c01d Iustin Pop
                  version["k_major"], version["k_minor"])
1396 a2cfdea2 Iustin Pop
1397 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
1398 ffa1c0dc Iustin Pop
        self._lport == self._rport):
1399 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
1400 ffa1c0dc Iustin Pop
                       (unique_id,))
1401 a2cfdea2 Iustin Pop
    self.Attach()
1402 a2cfdea2 Iustin Pop
1403 a2cfdea2 Iustin Pop
  @classmethod
1404 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1405 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1406 a2cfdea2 Iustin Pop

1407 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1408 a2cfdea2 Iustin Pop

1409 a2cfdea2 Iustin Pop
    """
1410 18e4dee6 Iustin Pop
    # Zero the metadata first, in order to make sure drbdmeta doesn't
1411 18e4dee6 Iustin Pop
    # try to auto-detect existing filesystems or similar (see
1412 18e4dee6 Iustin Pop
    # http://code.google.com/p/ganeti/issues/detail?id=182); we only
1413 18e4dee6 Iustin Pop
    # care about the first 128MB of data in the device, even though it
1414 18e4dee6 Iustin Pop
    # can be bigger
1415 18e4dee6 Iustin Pop
    result = utils.RunCmd([constants.DD_CMD,
1416 18e4dee6 Iustin Pop
                           "if=/dev/zero", "of=%s" % dev_path,
1417 18e4dee6 Iustin Pop
                           "bs=1048576", "count=128", "oflag=direct"])
1418 18e4dee6 Iustin Pop
    if result.failed:
1419 18e4dee6 Iustin Pop
      _ThrowError("Can't wipe the meta device: %s", result.output)
1420 18e4dee6 Iustin Pop
1421 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1422 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1423 a2cfdea2 Iustin Pop
    if result.failed:
1424 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
1425 a2cfdea2 Iustin Pop
1426 a2cfdea2 Iustin Pop
  @classmethod
1427 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1428 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1429 a2cfdea2 Iustin Pop

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

1433 a2cfdea2 Iustin Pop
    """
1434 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1435 a2cfdea2 Iustin Pop
1436 a2cfdea2 Iustin Pop
    highest = None
1437 a2cfdea2 Iustin Pop
    for line in data:
1438 9122e60a Iustin Pop
      match = cls._UNUSED_LINE_RE.match(line)
1439 a2cfdea2 Iustin Pop
      if match:
1440 a2cfdea2 Iustin Pop
        return int(match.group(1))
1441 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1442 a2cfdea2 Iustin Pop
      if match:
1443 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1444 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1445 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1446 a2cfdea2 Iustin Pop
      return 0
1447 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1448 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
1449 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1450 a2cfdea2 Iustin Pop
    return highest + 1
1451 a2cfdea2 Iustin Pop
1452 a2cfdea2 Iustin Pop
  @classmethod
1453 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1454 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1455 a2cfdea2 Iustin Pop

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

1459 a2cfdea2 Iustin Pop
    """
1460 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1461 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1462 a2cfdea2 Iustin Pop
1463 a2cfdea2 Iustin Pop
    # pyparsing setup
1464 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1465 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1466 5a672c30 Manuel Franceschini
    lbracket = pyp.Literal("[").suppress()
1467 5a672c30 Manuel Franceschini
    rbracket = pyp.Literal("]").suppress()
1468 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1469 5a672c30 Manuel Franceschini
    colon = pyp.Literal(":").suppress()
1470 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1471 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1472 a2cfdea2 Iustin Pop
1473 e687ec01 Michael Hanselmann
    comment = pyp.Literal("#") + pyp.Optional(pyp.restOfLine)
1474 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1475 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1476 a2cfdea2 Iustin Pop
1477 3ccb3a64 Michael Hanselmann
    keyword = pyp.Word(pyp.alphanums + "-")
1478 a2cfdea2 Iustin Pop
1479 a2cfdea2 Iustin Pop
    # value types
1480 3ccb3a64 Michael Hanselmann
    value = pyp.Word(pyp.alphanums + "_-/.:")
1481 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1482 5a672c30 Manuel Franceschini
    ipv4_addr = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1483 5a672c30 Manuel Franceschini
                 pyp.Word(pyp.nums + ".") + colon + number)
1484 5a672c30 Manuel Franceschini
    ipv6_addr = (pyp.Optional(pyp.Literal("ipv6")).suppress() +
1485 5a672c30 Manuel Franceschini
                 pyp.Optional(lbracket) + pyp.Word(pyp.hexnums + ":") +
1486 5a672c30 Manuel Franceschini
                 pyp.Optional(rbracket) + colon + number)
1487 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1488 5a672c30 Manuel Franceschini
    meta_value = ((value ^ quoted) + lbracket + number + rbracket)
1489 01e2ce3a Iustin Pop
    # device name, extended syntax
1490 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1491 a2cfdea2 Iustin Pop
1492 a2cfdea2 Iustin Pop
    # a statement
1493 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1494 5a672c30 Manuel Franceschini
            pyp.Optional(ipv4_addr ^ ipv6_addr ^ value ^ quoted ^ meta_value ^
1495 01e2ce3a Iustin Pop
                         device_value) +
1496 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1497 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1498 a2cfdea2 Iustin Pop
1499 a2cfdea2 Iustin Pop
    # an entire section
1500 d0c8c01d Iustin Pop
    section_name = pyp.Word(pyp.alphas + "_")
1501 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1502 a2cfdea2 Iustin Pop
1503 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1504 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1505 a2cfdea2 Iustin Pop
1506 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1507 a2cfdea2 Iustin Pop
1508 a2cfdea2 Iustin Pop
    return bnf
1509 a2cfdea2 Iustin Pop
1510 a2cfdea2 Iustin Pop
  @classmethod
1511 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1512 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1513 a2cfdea2 Iustin Pop

1514 a2cfdea2 Iustin Pop
    """
1515 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1516 a2cfdea2 Iustin Pop
    if result.failed:
1517 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1518 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1519 3840729d Iustin Pop
      return None
1520 3840729d Iustin Pop
    return result.stdout
1521 3840729d Iustin Pop
1522 3840729d Iustin Pop
  @classmethod
1523 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1524 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1525 3840729d Iustin Pop

1526 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1527 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1528 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1529 3840729d Iustin Pop
    by _GetShowData.
1530 3840729d Iustin Pop

1531 3840729d Iustin Pop
    """
1532 3840729d Iustin Pop
    data = {}
1533 a2cfdea2 Iustin Pop
    if not out:
1534 a2cfdea2 Iustin Pop
      return data
1535 a2cfdea2 Iustin Pop
1536 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1537 a2cfdea2 Iustin Pop
    # run pyparse
1538 a2cfdea2 Iustin Pop
1539 a2cfdea2 Iustin Pop
    try:
1540 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1541 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1542 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1543 a2cfdea2 Iustin Pop
1544 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1545 a2cfdea2 Iustin Pop
    for section in results:
1546 a2cfdea2 Iustin Pop
      sname = section[0]
1547 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1548 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1549 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1550 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1551 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1552 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1553 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1554 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1555 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1556 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1557 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1558 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1559 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1560 a2cfdea2 Iustin Pop
    return data
1561 a2cfdea2 Iustin Pop
1562 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1563 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1564 a2cfdea2 Iustin Pop

1565 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1566 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1567 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1568 a2cfdea2 Iustin Pop
    device.
1569 a2cfdea2 Iustin Pop

1570 a2cfdea2 Iustin Pop
    """
1571 b00b95dd Iustin Pop
    if self._children:
1572 b00b95dd Iustin Pop
      backend, meta = self._children
1573 b00b95dd Iustin Pop
    else:
1574 b00b95dd Iustin Pop
      backend = meta = None
1575 b00b95dd Iustin Pop
1576 a2cfdea2 Iustin Pop
    if backend is not None:
1577 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1578 a2cfdea2 Iustin Pop
    else:
1579 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1580 b00b95dd Iustin Pop
1581 a2cfdea2 Iustin Pop
    if meta is not None:
1582 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1583 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1584 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1585 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1586 a2cfdea2 Iustin Pop
    else:
1587 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1588 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1589 a2cfdea2 Iustin Pop
    return retval
1590 a2cfdea2 Iustin Pop
1591 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1592 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1593 a2cfdea2 Iustin Pop

1594 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1595 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1596 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1597 a2cfdea2 Iustin Pop
    device.
1598 a2cfdea2 Iustin Pop

1599 a2cfdea2 Iustin Pop
    """
1600 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1601 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1602 a2cfdea2 Iustin Pop
      return True
1603 a2cfdea2 Iustin Pop
1604 a2cfdea2 Iustin Pop
    if self._lhost is None:
1605 a2cfdea2 Iustin Pop
      return False
1606 a2cfdea2 Iustin Pop
1607 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1608 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1609 a2cfdea2 Iustin Pop
      return False
1610 a2cfdea2 Iustin Pop
1611 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1612 a2cfdea2 Iustin Pop
    retval = (retval and
1613 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1614 a2cfdea2 Iustin Pop
    return retval
1615 a2cfdea2 Iustin Pop
1616 8a69b3a8 Andrea Spadaccini
  def _AssembleLocal(self, minor, backend, meta, size):
1617 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1618 a2cfdea2 Iustin Pop

1619 a2cfdea2 Iustin Pop
    """
1620 8a69b3a8 Andrea Spadaccini
    args = ["drbdsetup", self._DevPath(minor), "disk",
1621 f069addf Iustin Pop
            backend, meta, "0",
1622 f069addf Iustin Pop
            "-e", "detach",
1623 f069addf Iustin Pop
            "--create-device"]
1624 60bca04a Iustin Pop
    if size:
1625 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1626 8a69b3a8 Andrea Spadaccini
1627 8a69b3a8 Andrea Spadaccini
    version = self._GetVersion(self._GetProcData())
1628 8a69b3a8 Andrea Spadaccini
    vmaj = version["k_major"]
1629 8a69b3a8 Andrea Spadaccini
    vmin = version["k_minor"]
1630 8a69b3a8 Andrea Spadaccini
    vrel = version["k_point"]
1631 8a69b3a8 Andrea Spadaccini
1632 8a69b3a8 Andrea Spadaccini
    barrier_args = \
1633 8a69b3a8 Andrea Spadaccini
      self._ComputeDiskBarrierArgs(vmaj, vmin, vrel,
1634 ac00bf1b Andrea Spadaccini
                                   self.params[constants.LDP_BARRIERS],
1635 ac00bf1b Andrea Spadaccini
                                   self.params[constants.LDP_NO_META_FLUSH])
1636 8a69b3a8 Andrea Spadaccini
    args.extend(barrier_args)
1637 8a69b3a8 Andrea Spadaccini
1638 ad1dd4c7 Andrea Spadaccini
    if self.params[constants.LDP_DISK_CUSTOM]:
1639 ad1dd4c7 Andrea Spadaccini
      args.extend(shlex.split(self.params[constants.LDP_DISK_CUSTOM]))
1640 ad1dd4c7 Andrea Spadaccini
1641 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1642 a2cfdea2 Iustin Pop
    if result.failed:
1643 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1644 a2cfdea2 Iustin Pop
1645 8a69b3a8 Andrea Spadaccini
  @classmethod
1646 8a69b3a8 Andrea Spadaccini
  def _ComputeDiskBarrierArgs(cls, vmaj, vmin, vrel, disabled_barriers,
1647 5ae4945a Iustin Pop
                              disable_meta_flush):
1648 8a69b3a8 Andrea Spadaccini
    """Compute the DRBD command line parameters for disk barriers
1649 8a69b3a8 Andrea Spadaccini

1650 8a69b3a8 Andrea Spadaccini
    Returns a list of the disk barrier parameters as requested via the
1651 8a69b3a8 Andrea Spadaccini
    disabled_barriers and disable_meta_flush arguments, and according to the
1652 8a69b3a8 Andrea Spadaccini
    supported ones in the DRBD version vmaj.vmin.vrel
1653 8a69b3a8 Andrea Spadaccini

1654 8a69b3a8 Andrea Spadaccini
    If the desired option is unsupported, raises errors.BlockDeviceError.
1655 8a69b3a8 Andrea Spadaccini

1656 8a69b3a8 Andrea Spadaccini
    """
1657 8a69b3a8 Andrea Spadaccini
    disabled_barriers_set = frozenset(disabled_barriers)
1658 8a69b3a8 Andrea Spadaccini
    if not disabled_barriers_set in constants.DRBD_VALID_BARRIER_OPT:
1659 8a69b3a8 Andrea Spadaccini
      raise errors.BlockDeviceError("%s is not a valid option set for DRBD"
1660 8a69b3a8 Andrea Spadaccini
                                    " barriers" % disabled_barriers)
1661 8a69b3a8 Andrea Spadaccini
1662 8a69b3a8 Andrea Spadaccini
    args = []
1663 8a69b3a8 Andrea Spadaccini
1664 8a69b3a8 Andrea Spadaccini
    # The following code assumes DRBD 8.x, with x < 4 and x != 1 (DRBD 8.1.x
1665 8a69b3a8 Andrea Spadaccini
    # does not exist)
1666 8a69b3a8 Andrea Spadaccini
    if not vmaj == 8 and vmin in (0, 2, 3):
1667 8a69b3a8 Andrea Spadaccini
      raise errors.BlockDeviceError("Unsupported DRBD version: %d.%d.%d" %
1668 8a69b3a8 Andrea Spadaccini
                                    (vmaj, vmin, vrel))
1669 8a69b3a8 Andrea Spadaccini
1670 8a69b3a8 Andrea Spadaccini
    def _AppendOrRaise(option, min_version):
1671 8a69b3a8 Andrea Spadaccini
      """Helper for DRBD options"""
1672 8a69b3a8 Andrea Spadaccini
      if min_version is not None and vrel >= min_version:
1673 8a69b3a8 Andrea Spadaccini
        args.append(option)
1674 8a69b3a8 Andrea Spadaccini
      else:
1675 8a69b3a8 Andrea Spadaccini
        raise errors.BlockDeviceError("Could not use the option %s as the"
1676 8a69b3a8 Andrea Spadaccini
                                      " DRBD version %d.%d.%d does not support"
1677 8a69b3a8 Andrea Spadaccini
                                      " it." % (option, vmaj, vmin, vrel))
1678 8a69b3a8 Andrea Spadaccini
1679 8a69b3a8 Andrea Spadaccini
    # the minimum version for each feature is encoded via pairs of (minor
1680 8a69b3a8 Andrea Spadaccini
    # version -> x) where x is version in which support for the option was
1681 8a69b3a8 Andrea Spadaccini
    # introduced.
1682 8a69b3a8 Andrea Spadaccini
    meta_flush_supported = disk_flush_supported = {
1683 8a69b3a8 Andrea Spadaccini
      0: 12,
1684 8a69b3a8 Andrea Spadaccini
      2: 7,
1685 8a69b3a8 Andrea Spadaccini
      3: 0,
1686 8a69b3a8 Andrea Spadaccini
      }
1687 8a69b3a8 Andrea Spadaccini
1688 8a69b3a8 Andrea Spadaccini
    disk_drain_supported = {
1689 8a69b3a8 Andrea Spadaccini
      2: 7,
1690 8a69b3a8 Andrea Spadaccini
      3: 0,
1691 8a69b3a8 Andrea Spadaccini
      }
1692 8a69b3a8 Andrea Spadaccini
1693 8a69b3a8 Andrea Spadaccini
    disk_barriers_supported = {
1694 8a69b3a8 Andrea Spadaccini
      3: 0,
1695 8a69b3a8 Andrea Spadaccini
      }
1696 8a69b3a8 Andrea Spadaccini
1697 8a69b3a8 Andrea Spadaccini
    # meta flushes
1698 8a69b3a8 Andrea Spadaccini
    if disable_meta_flush:
1699 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_META_FLUSH_OPTION,
1700 8a69b3a8 Andrea Spadaccini
                     meta_flush_supported.get(vmin, None))
1701 8a69b3a8 Andrea Spadaccini
1702 8a69b3a8 Andrea Spadaccini
    # disk flushes
1703 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_FLUSH in disabled_barriers_set:
1704 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_FLUSH_OPTION,
1705 8a69b3a8 Andrea Spadaccini
                     disk_flush_supported.get(vmin, None))
1706 8a69b3a8 Andrea Spadaccini
1707 8a69b3a8 Andrea Spadaccini
    # disk drain
1708 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_DRAIN in disabled_barriers_set:
1709 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_DRAIN_OPTION,
1710 8a69b3a8 Andrea Spadaccini
                     disk_drain_supported.get(vmin, None))
1711 8a69b3a8 Andrea Spadaccini
1712 8a69b3a8 Andrea Spadaccini
    # disk barriers
1713 8a69b3a8 Andrea Spadaccini
    if constants.DRBD_B_DISK_BARRIERS in disabled_barriers_set:
1714 8a69b3a8 Andrea Spadaccini
      _AppendOrRaise(cls._DISABLE_DISK_OPTION,
1715 8a69b3a8 Andrea Spadaccini
                     disk_barriers_supported.get(vmin, None))
1716 8a69b3a8 Andrea Spadaccini
1717 8a69b3a8 Andrea Spadaccini
    return args
1718 8a69b3a8 Andrea Spadaccini
1719 6e9814a1 Andrea Spadaccini
  def _AssembleNet(self, minor, net_info, protocol,
1720 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1721 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1722 a2cfdea2 Iustin Pop

1723 a2cfdea2 Iustin Pop
    """
1724 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1725 52857176 Iustin Pop
    if None in net_info:
1726 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1727 52857176 Iustin Pop
      # sure its shutdown
1728 6e9814a1 Andrea Spadaccini
      self._ShutdownNet(minor)
1729 1063abd1 Iustin Pop
      return
1730 52857176 Iustin Pop
1731 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1732 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1733 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1734 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1735 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1736 7d585316 Iustin Pop
    # about its peer.
1737 8584e922 Andrea Spadaccini
    sync_errors = self._SetMinorSyncParams(minor, self.params)
1738 8584e922 Andrea Spadaccini
    if sync_errors:
1739 8584e922 Andrea Spadaccini
      _ThrowError("drbd%d: can't set the synchronization parameters: %s" %
1740 8584e922 Andrea Spadaccini
                  (minor, utils.CommaJoin(sync_errors)))
1741 7d585316 Iustin Pop
1742 8b312c1d Manuel Franceschini
    if netutils.IP6Address.IsValid(lhost):
1743 8b312c1d Manuel Franceschini
      if not netutils.IP6Address.IsValid(rhost):
1744 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1745 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1746 5a672c30 Manuel Franceschini
      family = "ipv6"
1747 8b312c1d Manuel Franceschini
    elif netutils.IP4Address.IsValid(lhost):
1748 8b312c1d Manuel Franceschini
      if not netutils.IP4Address.IsValid(rhost):
1749 5a672c30 Manuel Franceschini
        _ThrowError("drbd%d: can't connect ip %s to ip %s" %
1750 5a672c30 Manuel Franceschini
                    (minor, lhost, rhost))
1751 5a672c30 Manuel Franceschini
      family = "ipv4"
1752 5a672c30 Manuel Franceschini
    else:
1753 5a672c30 Manuel Franceschini
      _ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
1754 5a672c30 Manuel Franceschini
1755 6e9814a1 Andrea Spadaccini
    args = ["drbdsetup", self._DevPath(minor), "net",
1756 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, lhost, lport),
1757 5a672c30 Manuel Franceschini
            "%s:%s:%s" % (family, rhost, rport), protocol,
1758 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1759 f38478b2 Iustin Pop
            "-B", "consensus",
1760 ab6cc81c Iustin Pop
            "--create-device",
1761 f38478b2 Iustin Pop
            ]
1762 a2cfdea2 Iustin Pop
    if dual_pri:
1763 a2cfdea2 Iustin Pop
      args.append("-m")
1764 a2cfdea2 Iustin Pop
    if hmac and secret:
1765 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1766 ad1dd4c7 Andrea Spadaccini
1767 ad1dd4c7 Andrea Spadaccini
    if self.params[constants.LDP_NET_CUSTOM]:
1768 ad1dd4c7 Andrea Spadaccini
      args.extend(shlex.split(self.params[constants.LDP_NET_CUSTOM]))
1769 ad1dd4c7 Andrea Spadaccini
1770 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1771 a2cfdea2 Iustin Pop
    if result.failed:
1772 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1773 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1774 a2cfdea2 Iustin Pop
1775 def8e2f6 Michael Hanselmann
    def _CheckNetworkConfig():
1776 6e9814a1 Andrea Spadaccini
      info = self._GetDevInfo(self._GetShowData(minor))
1777 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1778 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1779 def8e2f6 Michael Hanselmann
1780 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1781 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1782 def8e2f6 Michael Hanselmann
        raise utils.RetryAgain()
1783 def8e2f6 Michael Hanselmann
1784 def8e2f6 Michael Hanselmann
    try:
1785 def8e2f6 Michael Hanselmann
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
1786 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
1787 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1788 a2cfdea2 Iustin Pop
1789 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1790 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1791 b00b95dd Iustin Pop

1792 b00b95dd Iustin Pop
    """
1793 b00b95dd Iustin Pop
    if self.minor is None:
1794 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1795 82463074 Iustin Pop
                  self._aminor)
1796 b00b95dd Iustin Pop
    if len(devices) != 2:
1797 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1798 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1799 03ece5f3 Iustin Pop
    if "local_dev" in info:
1800 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1801 b00b95dd Iustin Pop
    backend, meta = devices
1802 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1803 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1804 b00b95dd Iustin Pop
    backend.Open()
1805 b00b95dd Iustin Pop
    meta.Open()
1806 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1807 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1808 b00b95dd Iustin Pop
1809 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1810 b00b95dd Iustin Pop
    self._children = devices
1811 b00b95dd Iustin Pop
1812 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1813 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1814 b00b95dd Iustin Pop

1815 b00b95dd Iustin Pop
    """
1816 b00b95dd Iustin Pop
    if self.minor is None:
1817 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1818 82463074 Iustin Pop
                  self._aminor)
1819 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1820 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1821 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1822 03ece5f3 Iustin Pop
      return
1823 b00b95dd Iustin Pop
    if len(self._children) != 2:
1824 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1825 82463074 Iustin Pop
                  self._children)
1826 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1827 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1828 e739bd57 Iustin Pop
      return
1829 b00b95dd Iustin Pop
    if len(devices) != 2:
1830 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1831 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1832 e739bd57 Iustin Pop
      if dev != child.dev_path:
1833 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1834 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1835 b00b95dd Iustin Pop
1836 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1837 b00b95dd Iustin Pop
    self._children = []
1838 b00b95dd Iustin Pop
1839 7d585316 Iustin Pop
  @classmethod
1840 f2f57b6e Andrea Spadaccini
  def _SetMinorSyncParams(cls, minor, params):
1841 f2f57b6e Andrea Spadaccini
    """Set the parameters of the DRBD syncer.
1842 a2cfdea2 Iustin Pop

1843 7d585316 Iustin Pop
    This is the low-level implementation.
1844 7d585316 Iustin Pop

1845 7d585316 Iustin Pop
    @type minor: int
1846 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1847 f2f57b6e Andrea Spadaccini
    @type params: dict
1848 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1849 8584e922 Andrea Spadaccini
    @rtype: list
1850 8584e922 Andrea Spadaccini
    @return: a list of error messages
1851 7d585316 Iustin Pop

1852 a2cfdea2 Iustin Pop
    """
1853 f2f57b6e Andrea Spadaccini
1854 f2f57b6e Andrea Spadaccini
    args = ["drbdsetup", cls._DevPath(minor), "syncer"]
1855 f2f57b6e Andrea Spadaccini
    if params[constants.LDP_DYNAMIC_RESYNC]:
1856 f2f57b6e Andrea Spadaccini
      version = cls._GetVersion(cls._GetProcData())
1857 f2f57b6e Andrea Spadaccini
      vmin = version["k_minor"]
1858 f2f57b6e Andrea Spadaccini
      vrel = version["k_point"]
1859 f2f57b6e Andrea Spadaccini
1860 f2f57b6e Andrea Spadaccini
      # By definition we are using 8.x, so just check the rest of the version
1861 f2f57b6e Andrea Spadaccini
      # number
1862 f2f57b6e Andrea Spadaccini
      if vmin != 3 or vrel < 9:
1863 8584e922 Andrea Spadaccini
        msg = ("The current DRBD version (8.%d.%d) does not support the "
1864 8584e922 Andrea Spadaccini
               "dynamic resync speed controller" % (vmin, vrel))
1865 8584e922 Andrea Spadaccini
        logging.error(msg)
1866 8584e922 Andrea Spadaccini
        return [msg]
1867 8584e922 Andrea Spadaccini
1868 8584e922 Andrea Spadaccini
      if params[constants.LDP_PLAN_AHEAD] == 0:
1869 8584e922 Andrea Spadaccini
        msg = ("A value of 0 for c-plan-ahead disables the dynamic sync speed"
1870 8584e922 Andrea Spadaccini
               " controller at DRBD level. If you want to disable it, please"
1871 8584e922 Andrea Spadaccini
               " set the dynamic-resync disk parameter to False.")
1872 8584e922 Andrea Spadaccini
        logging.error(msg)
1873 8584e922 Andrea Spadaccini
        return [msg]
1874 f2f57b6e Andrea Spadaccini
1875 f2f57b6e Andrea Spadaccini
      # add the c-* parameters to args
1876 8584e922 Andrea Spadaccini
      args.extend(["--c-plan-ahead", params[constants.LDP_PLAN_AHEAD],
1877 8584e922 Andrea Spadaccini
                   "--c-fill-target", params[constants.LDP_FILL_TARGET],
1878 8584e922 Andrea Spadaccini
                   "--c-delay-target", params[constants.LDP_DELAY_TARGET],
1879 8584e922 Andrea Spadaccini
                   "--c-max-rate", params[constants.LDP_MAX_RATE],
1880 8584e922 Andrea Spadaccini
                   "--c-min-rate", params[constants.LDP_MIN_RATE],
1881 5ae4945a Iustin Pop
                   ])
1882 f2f57b6e Andrea Spadaccini
1883 f2f57b6e Andrea Spadaccini
    else:
1884 f2f57b6e Andrea Spadaccini
      args.extend(["-r", "%d" % params[constants.LDP_RESYNC_RATE]])
1885 f2f57b6e Andrea Spadaccini
1886 f2f57b6e Andrea Spadaccini
    args.append("--create-device")
1887 f2f57b6e Andrea Spadaccini
    result = utils.RunCmd(args)
1888 a2cfdea2 Iustin Pop
    if result.failed:
1889 8584e922 Andrea Spadaccini
      msg = ("Can't change syncer rate: %s - %s" %
1890 8584e922 Andrea Spadaccini
             (result.fail_reason, result.output))
1891 8584e922 Andrea Spadaccini
      logging.error(msg)
1892 e0b57a5c Michael Hanselmann
      return [msg]
1893 8584e922 Andrea Spadaccini
1894 8584e922 Andrea Spadaccini
    return []
1895 7d585316 Iustin Pop
1896 f2f57b6e Andrea Spadaccini
  def SetSyncParams(self, params):
1897 f2f57b6e Andrea Spadaccini
    """Set the synchronization parameters of the DRBD syncer.
1898 7d585316 Iustin Pop

1899 f2f57b6e Andrea Spadaccini
    @type params: dict
1900 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1901 8584e922 Andrea Spadaccini
    @rtype: list
1902 8584e922 Andrea Spadaccini
    @return: a list of error messages, emitted both by the current node and by
1903 8584e922 Andrea Spadaccini
    children. An empty list means no errors
1904 7d585316 Iustin Pop

1905 7d585316 Iustin Pop
    """
1906 7d585316 Iustin Pop
    if self.minor is None:
1907 8584e922 Andrea Spadaccini
      err = "Not attached during SetSyncParams"
1908 8584e922 Andrea Spadaccini
      logging.info(err)
1909 8584e922 Andrea Spadaccini
      return [err]
1910 8584e922 Andrea Spadaccini
1911 f2f57b6e Andrea Spadaccini
    children_result = super(DRBD8, self).SetSyncParams(params)
1912 8584e922 Andrea Spadaccini
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
1913 8584e922 Andrea Spadaccini
    return children_result
1914 a2cfdea2 Iustin Pop
1915 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
1916 a3fffcc6 René Nussbaumer
    """Pauses or resumes the sync of a DRBD device.
1917 a3fffcc6 René Nussbaumer

1918 a3fffcc6 René Nussbaumer
    @param pause: Wether to pause or resume
1919 a3fffcc6 René Nussbaumer
    @return: the success of the operation
1920 a3fffcc6 René Nussbaumer

1921 a3fffcc6 René Nussbaumer
    """
1922 a3fffcc6 René Nussbaumer
    if self.minor is None:
1923 a3fffcc6 René Nussbaumer
      logging.info("Not attached during PauseSync")
1924 a3fffcc6 René Nussbaumer
      return False
1925 a3fffcc6 René Nussbaumer
1926 a3fffcc6 René Nussbaumer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
1927 a3fffcc6 René Nussbaumer
1928 a3fffcc6 René Nussbaumer
    if pause:
1929 a3fffcc6 René Nussbaumer
      cmd = "pause-sync"
1930 a3fffcc6 René Nussbaumer
    else:
1931 a3fffcc6 René Nussbaumer
      cmd = "resume-sync"
1932 a3fffcc6 René Nussbaumer
1933 a3fffcc6 René Nussbaumer
    result = utils.RunCmd(["drbdsetup", self.dev_path, cmd])
1934 a3fffcc6 René Nussbaumer
    if result.failed:
1935 a3fffcc6 René Nussbaumer
      logging.error("Can't %s: %s - %s", cmd,
1936 a3fffcc6 René Nussbaumer
                    result.fail_reason, result.output)
1937 a3fffcc6 René Nussbaumer
    return not result.failed and children_result
1938 a3fffcc6 René Nussbaumer
1939 6b90c22e Iustin Pop
  def GetProcStatus(self):
1940 6b90c22e Iustin Pop
    """Return device data from /proc.
1941 6b90c22e Iustin Pop

1942 6b90c22e Iustin Pop
    """
1943 6b90c22e Iustin Pop
    if self.minor is None:
1944 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1945 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1946 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1947 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1948 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1949 6b90c22e Iustin Pop
1950 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1951 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1952 a2cfdea2 Iustin Pop

1953 a2cfdea2 Iustin Pop

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

1958 0834c866 Iustin Pop

1959 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1960 0834c866 Iustin Pop
    network not connected or local disk missing.
1961 0834c866 Iustin Pop

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

1965 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1966 c41eea6e Iustin Pop

1967 a2cfdea2 Iustin Pop
    """
1968 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1969 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1970 96acbc09 Michael Hanselmann
1971 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1972 f208978a Michael Hanselmann
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
1973 f208978a Michael Hanselmann
1974 f208978a Michael Hanselmann
    if stats.is_disk_uptodate:
1975 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
1976 f208978a Michael Hanselmann
    elif stats.is_diskless:
1977 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
1978 f208978a Michael Hanselmann
    else:
1979 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_UNKNOWN
1980 96acbc09 Michael Hanselmann
1981 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
1982 96acbc09 Michael Hanselmann
                                  major=self.major,
1983 96acbc09 Michael Hanselmann
                                  minor=self.minor,
1984 96acbc09 Michael Hanselmann
                                  sync_percent=stats.sync_percent,
1985 96acbc09 Michael Hanselmann
                                  estimated_time=stats.est_time,
1986 f208978a Michael Hanselmann
                                  is_degraded=is_degraded,
1987 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
1988 a2cfdea2 Iustin Pop
1989 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1990 a2cfdea2 Iustin Pop
    """Make the local state primary.
1991 a2cfdea2 Iustin Pop

1992 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1993 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1994 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1995 f860ff4e Guido Trotter
    is mandatory.
1996 a2cfdea2 Iustin Pop

1997 a2cfdea2 Iustin Pop
    """
1998 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1999 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
2000 a2cfdea2 Iustin Pop
      return False
2001 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
2002 a2cfdea2 Iustin Pop
    if force:
2003 a2cfdea2 Iustin Pop
      cmd.append("-o")
2004 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
2005 a2cfdea2 Iustin Pop
    if result.failed:
2006 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
2007 82463074 Iustin Pop
                  result.output)
2008 a2cfdea2 Iustin Pop
2009 a2cfdea2 Iustin Pop
  def Close(self):
2010 a2cfdea2 Iustin Pop
    """Make the local state secondary.
2011 a2cfdea2 Iustin Pop

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

2014 a2cfdea2 Iustin Pop
    """
2015 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2016 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
2017 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
2018 a2cfdea2 Iustin Pop
    if result.failed:
2019 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
2020 82463074 Iustin Pop
                  self.minor, result.output)
2021 a2cfdea2 Iustin Pop
2022 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
2023 cf8df3f3 Iustin Pop
    """Removes network configuration.
2024 cf8df3f3 Iustin Pop

2025 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
2026 cf8df3f3 Iustin Pop

2027 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
2028 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
2029 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
2030 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
2031 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
2032 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
2033 cf8df3f3 Iustin Pop
    exception.
2034 cf8df3f3 Iustin Pop

2035 cf8df3f3 Iustin Pop
    """
2036 cf8df3f3 Iustin Pop
    if self.minor is None:
2037 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
2038 cf8df3f3 Iustin Pop
2039 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
2040 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
2041 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
2042 cf8df3f3 Iustin Pop
2043 def8e2f6 Michael Hanselmann
    class _DisconnectStatus:
2044 def8e2f6 Michael Hanselmann
      def __init__(self, ever_disconnected):
2045 def8e2f6 Michael Hanselmann
        self.ever_disconnected = ever_disconnected
2046 cf8df3f3 Iustin Pop
2047 def8e2f6 Michael Hanselmann
    dstatus = _DisconnectStatus(_IgnoreError(self._ShutdownNet, self.minor))
2048 def8e2f6 Michael Hanselmann
2049 def8e2f6 Michael Hanselmann
    def _WaitForDisconnect():
2050 def8e2f6 Michael Hanselmann
      if self.GetProcStatus().is_standalone:
2051 def8e2f6 Michael Hanselmann
        return
2052 def8e2f6 Michael Hanselmann
2053 def8e2f6 Michael Hanselmann
      # retry the disconnect, it seems possible that due to a well-time
2054 def8e2f6 Michael Hanselmann
      # disconnect on the peer, my disconnect command might be ignored and
2055 def8e2f6 Michael Hanselmann
      # forgotten
2056 def8e2f6 Michael Hanselmann
      dstatus.ever_disconnected = \
2057 def8e2f6 Michael Hanselmann
        _IgnoreError(self._ShutdownNet, self.minor) or dstatus.ever_disconnected
2058 def8e2f6 Michael Hanselmann
2059 def8e2f6 Michael Hanselmann
      raise utils.RetryAgain()
2060 def8e2f6 Michael Hanselmann
2061 def8e2f6 Michael Hanselmann
    # Keep start time
2062 def8e2f6 Michael Hanselmann
    start_time = time.time()
2063 def8e2f6 Michael Hanselmann
2064 def8e2f6 Michael Hanselmann
    try:
2065 def8e2f6 Michael Hanselmann
      # Start delay at 100 milliseconds and grow up to 2 seconds
2066 def8e2f6 Michael Hanselmann
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
2067 def8e2f6 Michael Hanselmann
                  self._NET_RECONFIG_TIMEOUT)
2068 def8e2f6 Michael Hanselmann
    except utils.RetryTimeout:
2069 def8e2f6 Michael Hanselmann
      if dstatus.ever_disconnected:
2070 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
2071 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
2072 cf8df3f3 Iustin Pop
      else:
2073 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
2074 def8e2f6 Michael Hanselmann
2075 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
2076 cf8df3f3 Iustin Pop
2077 def8e2f6 Michael Hanselmann
    reconfig_time = time.time() - start_time
2078 def8e2f6 Michael Hanselmann
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
2079 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
2080 82463074 Iustin Pop
                   self.minor, reconfig_time)
2081 cf8df3f3 Iustin Pop
2082 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
2083 cf8df3f3 Iustin Pop
    """Reconnects the network.
2084 cf8df3f3 Iustin Pop

2085 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
2086 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
2087 cf8df3f3 Iustin Pop
    but have valid network configuration data.
2088 cf8df3f3 Iustin Pop

2089 cf8df3f3 Iustin Pop
    Args:
2090 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
2091 cf8df3f3 Iustin Pop

2092 cf8df3f3 Iustin Pop
    """
2093 cf8df3f3 Iustin Pop
    if self.minor is None:
2094 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
2095 cf8df3f3 Iustin Pop
2096 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
2097 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
2098 cf8df3f3 Iustin Pop
2099 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
2100 cf8df3f3 Iustin Pop
2101 cf8df3f3 Iustin Pop
    if not status.is_standalone:
2102 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
2103 cf8df3f3 Iustin Pop
2104 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
2105 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
2106 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
2107 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2108 cf8df3f3 Iustin Pop
2109 a2cfdea2 Iustin Pop
  def Attach(self):
2110 2d0c8319 Iustin Pop
    """Check if our minor is configured.
2111 2d0c8319 Iustin Pop

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

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

2119 2d0c8319 Iustin Pop
    """
2120 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
2121 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
2122 2d0c8319 Iustin Pop
      minor = self._aminor
2123 2d0c8319 Iustin Pop
    else:
2124 2d0c8319 Iustin Pop
      minor = None
2125 2d0c8319 Iustin Pop
2126 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
2127 2d0c8319 Iustin Pop
    return minor is not None
2128 2d0c8319 Iustin Pop
2129 2d0c8319 Iustin Pop
  def Assemble(self):
2130 2d0c8319 Iustin Pop
    """Assemble the drbd.
2131 2d0c8319 Iustin Pop

2132 2d0c8319 Iustin Pop
    Method:
2133 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
2134 2d0c8319 Iustin Pop
        our config
2135 2d0c8319 Iustin Pop
      - if not, we create it from zero
2136 d529599f Andrea Spadaccini
      - anyway, set the device parameters
2137 2d0c8319 Iustin Pop

2138 2d0c8319 Iustin Pop
    """
2139 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
2140 2d0c8319 Iustin Pop
2141 2d0c8319 Iustin Pop
    self.Attach()
2142 2d0c8319 Iustin Pop
    if self.minor is None:
2143 2d0c8319 Iustin Pop
      # local device completely unconfigured
2144 1063abd1 Iustin Pop
      self._FastAssemble()
2145 2d0c8319 Iustin Pop
    else:
2146 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
2147 2d0c8319 Iustin Pop
      # the device
2148 1063abd1 Iustin Pop
      self._SlowAssemble()
2149 2d0c8319 Iustin Pop
2150 8584e922 Andrea Spadaccini
    sync_errors = self.SetSyncParams(self.params)
2151 8584e922 Andrea Spadaccini
    if sync_errors:
2152 8584e922 Andrea Spadaccini
      _ThrowError("drbd%d: can't set the synchronization parameters: %s" %
2153 8584e922 Andrea Spadaccini
                  (self.minor, utils.CommaJoin(sync_errors)))
2154 d529599f Andrea Spadaccini
2155 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
2156 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
2157 a2cfdea2 Iustin Pop

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

2162 a2cfdea2 Iustin Pop
    """
2163 527a15ac Iustin Pop
    # TODO: Rewrite to not use a for loop just because there is 'break'
2164 b459a848 Andrea Spadaccini
    # pylint: disable=W0631
2165 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
2166 a1578d63 Iustin Pop
    for minor in (self._aminor,):
2167 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
2168 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
2169 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
2170 1063abd1 Iustin Pop
2171 a2cfdea2 Iustin Pop
      if match_l and match_r:
2172 1063abd1 Iustin Pop
        # everything matches
2173 a2cfdea2 Iustin Pop
        break
2174 1063abd1 Iustin Pop
2175 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
2176 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
2177 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
2178 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2179 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2180 1063abd1 Iustin Pop
          break
2181 1063abd1 Iustin Pop
        else:
2182 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
2183 1063abd1 Iustin Pop
                      " show' disagrees", minor)
2184 1063abd1 Iustin Pop
2185 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
2186 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
2187 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
2188 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
2189 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2190 1063abd1 Iustin Pop
          break
2191 1063abd1 Iustin Pop
        else:
2192 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
2193 1063abd1 Iustin Pop
                      " show' disagrees", minor)
2194 bf25af3b Iustin Pop
2195 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
2196 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
2197 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
2198 bf25af3b Iustin Pop
      # configuration
2199 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
2200 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
2201 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
2202 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
2203 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
2204 9cdbe77f Iustin Pop
        # reconnect to our correct one
2205 1063abd1 Iustin Pop
        try:
2206 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
2207 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
2208 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
2209 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
2210 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
2211 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
2212 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
2213 9cdbe77f Iustin Pop
        # None)
2214 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
2215 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2216 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2217 9cdbe77f Iustin Pop
          break
2218 1063abd1 Iustin Pop
        else:
2219 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
2220 1063abd1 Iustin Pop
                      " show' disagrees", minor)
2221 9cdbe77f Iustin Pop
2222 a2cfdea2 Iustin Pop
    else:
2223 a2cfdea2 Iustin Pop
      minor = None
2224 a2cfdea2 Iustin Pop
2225 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2226 1063abd1 Iustin Pop
    if minor is None:
2227 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
2228 1063abd1 Iustin Pop
                  self._aminor)
2229 a2cfdea2 Iustin Pop
2230 2d0c8319 Iustin Pop
  def _FastAssemble(self):
2231 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
2232 a2cfdea2 Iustin Pop

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

2235 a2cfdea2 Iustin Pop
    """
2236 a1578d63 Iustin Pop
    minor = self._aminor
2237 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
2238 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
2239 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
2240 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
2241 1063abd1 Iustin Pop
      self._AssembleNet(minor,
2242 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
2243 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
2244 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2245 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2246 a2cfdea2 Iustin Pop
2247 a2cfdea2 Iustin Pop
  @classmethod
2248 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
2249 b00b95dd Iustin Pop
    """Detach from the local device.
2250 b00b95dd Iustin Pop

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

2254 b00b95dd Iustin Pop
    """
2255 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2256 b00b95dd Iustin Pop
    if result.failed:
2257 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
2258 b00b95dd Iustin Pop
2259 b00b95dd Iustin Pop
  @classmethod
2260 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2261 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2262 f3e513ad Iustin Pop

2263 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2264 f3e513ad Iustin Pop

2265 f3e513ad Iustin Pop
    """
2266 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2267 a8459f1c Iustin Pop
    if result.failed:
2268 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
2269 f3e513ad Iustin Pop
2270 f3e513ad Iustin Pop
  @classmethod
2271 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2272 a2cfdea2 Iustin Pop
    """Deactivate the device.
2273 a2cfdea2 Iustin Pop

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

2276 a2cfdea2 Iustin Pop
    """
2277 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2278 a2cfdea2 Iustin Pop
    if result.failed:
2279 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
2280 33bc6f01 Iustin Pop
                  minor, result.output)
2281 a2cfdea2 Iustin Pop
2282 a2cfdea2 Iustin Pop
  def Shutdown(self):
2283 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2284 a2cfdea2 Iustin Pop

2285 a2cfdea2 Iustin Pop
    """
2286 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2287 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
2288 746f7476 Iustin Pop
      return
2289 746f7476 Iustin Pop
    minor = self.minor
2290 a2cfdea2 Iustin Pop
    self.minor = None
2291 a2cfdea2 Iustin Pop
    self.dev_path = None
2292 746f7476 Iustin Pop
    self._ShutdownAll(minor)
2293 a2cfdea2 Iustin Pop
2294 a2cfdea2 Iustin Pop
  def Remove(self):
2295 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2296 a2cfdea2 Iustin Pop

2297 a2cfdea2 Iustin Pop
    """
2298 0c6c04ec Iustin Pop
    self.Shutdown()
2299 a2cfdea2 Iustin Pop
2300 a2cfdea2 Iustin Pop
  @classmethod
2301 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2302 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2303 a2cfdea2 Iustin Pop

2304 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2305 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2306 a2cfdea2 Iustin Pop

2307 a2cfdea2 Iustin Pop
    """
2308 a2cfdea2 Iustin Pop
    if len(children) != 2:
2309 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2310 ee1478e5 Bernardo Dal Seno
    if excl_stor:
2311 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("DRBD device requested with"
2312 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
2313 767d52d3 Iustin Pop
    # check that the minor is unused
2314 767d52d3 Iustin Pop
    aminor = unique_id[4]
2315 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
2316 767d52d3 Iustin Pop
    if aminor in proc_info:
2317 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
2318 767d52d3 Iustin Pop
      in_use = status.is_in_use
2319 767d52d3 Iustin Pop
    else:
2320 767d52d3 Iustin Pop
      in_use = False
2321 767d52d3 Iustin Pop
    if in_use:
2322 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
2323 a2cfdea2 Iustin Pop
    meta = children[1]
2324 a2cfdea2 Iustin Pop
    meta.Assemble()
2325 a2cfdea2 Iustin Pop
    if not meta.Attach():
2326 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
2327 33bc6f01 Iustin Pop
                  aminor, meta)
2328 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
2329 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
2330 94dcbdb0 Andrea Spadaccini
    return cls(unique_id, children, size, params)
2331 a2cfdea2 Iustin Pop
2332 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2333 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
2334 1005d816 Iustin Pop

2335 1005d816 Iustin Pop
    """
2336 1005d816 Iustin Pop
    if self.minor is None:
2337 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
2338 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
2339 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
2340 cad0723b Iustin Pop
    self._children[0].Grow(amount, dryrun, backingstore)
2341 cad0723b Iustin Pop
    if dryrun or backingstore:
2342 cad0723b Iustin Pop
      # DRBD does not support dry-run mode and is not backing storage,
2343 cad0723b Iustin Pop
      # so we'll return here
2344 7fe23d47 Iustin Pop
      return
2345 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
2346 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
2347 1005d816 Iustin Pop
    if result.failed:
2348 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
2349 1005d816 Iustin Pop
2350 a8083063 Iustin Pop
2351 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
2352 6f695a2e Manuel Franceschini
  """File device.
2353 abdf0113 Iustin Pop

2354 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
2355 6f695a2e Manuel Franceschini

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

2358 6f695a2e Manuel Franceschini
  """
2359 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2360 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
2361 6f695a2e Manuel Franceschini

2362 6f695a2e Manuel Franceschini
    """
2363 6f695a2e Manuel Franceschini
    if children:
2364 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
2365 94dcbdb0 Andrea Spadaccini
    super(FileStorage, self).__init__(unique_id, children, size, params)
2366 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2367 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2368 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
2369 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
2370 5e09a309 Michael Hanselmann
2371 5e09a309 Michael Hanselmann
    CheckFileStoragePath(self.dev_path)
2372 5e09a309 Michael Hanselmann
2373 ecb091e3 Iustin Pop
    self.Attach()
2374 6f695a2e Manuel Franceschini
2375 6f695a2e Manuel Franceschini
  def Assemble(self):
2376 6f695a2e Manuel Franceschini
    """Assemble the device.
2377 6f695a2e Manuel Franceschini

2378 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
2379 6f695a2e Manuel Franceschini

2380 6f695a2e Manuel Franceschini
    """
2381 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
2382 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
2383 6f695a2e Manuel Franceschini
2384 6f695a2e Manuel Franceschini
  def Shutdown(self):
2385 6f695a2e Manuel Franceschini
    """Shutdown the device.
2386 6f695a2e Manuel Franceschini

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

2390 6f695a2e Manuel Franceschini
    """
2391 746f7476 Iustin Pop
    pass
2392 6f695a2e Manuel Franceschini
2393 6f695a2e Manuel Franceschini
  def Open(self, force=False):
2394 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
2395 6f695a2e Manuel Franceschini

2396 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2397 6f695a2e Manuel Franceschini

2398 6f695a2e Manuel Franceschini
    """
2399 6f695a2e Manuel Franceschini
    pass
2400 6f695a2e Manuel Franceschini
2401 6f695a2e Manuel Franceschini
  def Close(self):
2402 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
2403 6f695a2e Manuel Franceschini

2404 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2405 6f695a2e Manuel Franceschini

2406 6f695a2e Manuel Franceschini
    """
2407 6f695a2e Manuel Franceschini
    pass
2408 6f695a2e Manuel Franceschini
2409 6f695a2e Manuel Franceschini
  def Remove(self):
2410 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
2411 6f695a2e Manuel Franceschini

2412 c41eea6e Iustin Pop
    @rtype: boolean
2413 c41eea6e Iustin Pop
    @return: True if the removal was successful
2414 6f695a2e Manuel Franceschini

2415 6f695a2e Manuel Franceschini
    """
2416 6f695a2e Manuel Franceschini
    try:
2417 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
2418 6f695a2e Manuel Franceschini
    except OSError, err:
2419 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
2420 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
2421 6f695a2e Manuel Franceschini
2422 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
2423 bbe4cc16 Iustin Pop
    """Renames the file.
2424 bbe4cc16 Iustin Pop

2425 bbe4cc16 Iustin Pop
    """
2426 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
2427 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
2428 bbe4cc16 Iustin Pop
2429 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2430 bbe4cc16 Iustin Pop
    """Grow the file
2431 bbe4cc16 Iustin Pop

2432 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2433 bbe4cc16 Iustin Pop

2434 bbe4cc16 Iustin Pop
    """
2435 cad0723b Iustin Pop
    if not backingstore:
2436 cad0723b Iustin Pop
      return
2437 91e2d9ec Guido Trotter
    # Check that the file exists
2438 91e2d9ec Guido Trotter
    self.Assemble()
2439 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
2440 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
2441 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
2442 7fe23d47 Iustin Pop
    # We can't really simulate the growth
2443 7fe23d47 Iustin Pop
    if dryrun:
2444 7fe23d47 Iustin Pop
      return
2445 91e2d9ec Guido Trotter
    try:
2446 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
2447 91e2d9ec Guido Trotter
      f.truncate(new_size)
2448 91e2d9ec Guido Trotter
      f.close()
2449 91e2d9ec Guido Trotter
    except EnvironmentError, err:
2450 91e2d9ec Guido Trotter
      _ThrowError("Error in file growth: %", str(err))
2451 bbe4cc16 Iustin Pop
2452 6f695a2e Manuel Franceschini
  def Attach(self):
2453 6f695a2e Manuel Franceschini
    """Attach to an existing file.
2454 6f695a2e Manuel Franceschini

2455 6f695a2e Manuel Franceschini
    Check if this file already exists.
2456 6f695a2e Manuel Franceschini

2457 c41eea6e Iustin Pop
    @rtype: boolean
2458 c41eea6e Iustin Pop
    @return: True if file exists
2459 6f695a2e Manuel Franceschini

2460 6f695a2e Manuel Franceschini
    """
2461 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
2462 ecb091e3 Iustin Pop
    return self.attached
2463 6f695a2e Manuel Franceschini
2464 fcff3897 Iustin Pop
  def GetActualSize(self):
2465 fcff3897 Iustin Pop
    """Return the actual disk size.
2466 fcff3897 Iustin Pop

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

2469 fcff3897 Iustin Pop
    """
2470 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2471 fcff3897 Iustin Pop
    try:
2472 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2473 fcff3897 Iustin Pop
      return st.st_size
2474 fcff3897 Iustin Pop
    except OSError, err:
2475 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2476 fcff3897 Iustin Pop
2477 6f695a2e Manuel Franceschini
  @classmethod
2478 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2479 6f695a2e Manuel Franceschini
    """Create a new file.
2480 6f695a2e Manuel Franceschini

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

2483 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2484 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2485 6f695a2e Manuel Franceschini

2486 6f695a2e Manuel Franceschini
    """
2487 ee1478e5 Bernardo Dal Seno
    if excl_stor:
2488 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("FileStorage device requested with"
2489 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
2490 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2491 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2492 5e09a309 Michael Hanselmann
2493 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
2494 5e09a309 Michael Hanselmann
2495 5e09a309 Michael Hanselmann
    CheckFileStoragePath(dev_path)
2496 5e09a309 Michael Hanselmann
2497 6f695a2e Manuel Franceschini
    try:
2498 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
2499 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
2500 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
2501 6f695a2e Manuel Franceschini
      f.close()
2502 cdeefd9b Guido Trotter
    except EnvironmentError, err:
2503 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
2504 cdeefd9b Guido Trotter
        _ThrowError("File already existing: %s", dev_path)
2505 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
2506 6f695a2e Manuel Franceschini
2507 94dcbdb0 Andrea Spadaccini
    return FileStorage(unique_id, children, size, params)
2508 6f695a2e Manuel Franceschini
2509 6f695a2e Manuel Franceschini
2510 b6135bbc Apollon Oikonomopoulos
class PersistentBlockDevice(BlockDev):
2511 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
2512 b6135bbc Apollon Oikonomopoulos

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

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

2519 b6135bbc Apollon Oikonomopoulos
  """
2520 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2521 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
2522 b6135bbc Apollon Oikonomopoulos

2523 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2524 b6135bbc Apollon Oikonomopoulos

2525 b6135bbc Apollon Oikonomopoulos
    """
2526 94dcbdb0 Andrea Spadaccini
    super(PersistentBlockDevice, self).__init__(unique_id, children, size,
2527 94dcbdb0 Andrea Spadaccini
                                                params)
2528 b6135bbc Apollon Oikonomopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2529 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2530 b6135bbc Apollon Oikonomopoulos
    self.dev_path = unique_id[1]
2531 d0c8c01d Iustin Pop
    if not os.path.realpath(self.dev_path).startswith("/dev/"):
2532 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Full path '%s' lies outside /dev" %
2533 b6135bbc Apollon Oikonomopoulos
                              os.path.realpath(self.dev_path))
2534 b6135bbc Apollon Oikonomopoulos
    # TODO: this is just a safety guard checking that we only deal with devices
2535 b6135bbc Apollon Oikonomopoulos
    # we know how to handle. In the future this will be integrated with
2536 b6135bbc Apollon Oikonomopoulos
    # external storage backends and possible values will probably be collected
2537 b6135bbc Apollon Oikonomopoulos
    # from the cluster configuration.
2538 b6135bbc Apollon Oikonomopoulos
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
2539 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Got persistent block device of invalid type: %s" %
2540 b6135bbc Apollon Oikonomopoulos
                       unique_id[0])
2541 b6135bbc Apollon Oikonomopoulos
2542 b6135bbc Apollon Oikonomopoulos
    self.major = self.minor = None
2543 b6135bbc Apollon Oikonomopoulos
    self.Attach()
2544 b6135bbc Apollon Oikonomopoulos
2545 b6135bbc Apollon Oikonomopoulos
  @classmethod
2546 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2547 b6135bbc Apollon Oikonomopoulos
    """Create a new device
2548 b6135bbc Apollon Oikonomopoulos

2549 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2550 b6135bbc Apollon Oikonomopoulos

2551 b6135bbc Apollon Oikonomopoulos
    """
2552 ee1478e5 Bernardo Dal Seno
    if excl_stor:
2553 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("Persistent block device requested with"
2554 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
2555 94dcbdb0 Andrea Spadaccini
    return PersistentBlockDevice(unique_id, children, 0, params)
2556 b6135bbc Apollon Oikonomopoulos
2557 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
2558 b6135bbc Apollon Oikonomopoulos
    """Remove a device
2559 b6135bbc Apollon Oikonomopoulos

2560 b6135bbc Apollon Oikonomopoulos
    This is a noop
2561 b6135bbc Apollon Oikonomopoulos

2562 b6135bbc Apollon Oikonomopoulos
    """
2563 b6135bbc Apollon Oikonomopoulos
    pass
2564 b6135bbc Apollon Oikonomopoulos
2565 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
2566 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
2567 b6135bbc Apollon Oikonomopoulos

2568 b6135bbc Apollon Oikonomopoulos
    """
2569 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Rename is not supported for PersistentBlockDev storage")
2570 b6135bbc Apollon Oikonomopoulos
2571 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
2572 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
2573 b6135bbc Apollon Oikonomopoulos

2574 b6135bbc Apollon Oikonomopoulos

2575 b6135bbc Apollon Oikonomopoulos
    """
2576 b6135bbc Apollon Oikonomopoulos
    self.attached = False
2577 b6135bbc Apollon Oikonomopoulos
    try:
2578 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
2579 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
2580 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2581 b6135bbc Apollon Oikonomopoulos
      return False
2582 b6135bbc Apollon Oikonomopoulos
2583 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
2584 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
2585 b6135bbc Apollon Oikonomopoulos
      return False
2586 b6135bbc Apollon Oikonomopoulos
2587 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
2588 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
2589 b6135bbc Apollon Oikonomopoulos
    self.attached = True
2590 b6135bbc Apollon Oikonomopoulos
2591 b6135bbc Apollon Oikonomopoulos
    return True
2592 b6135bbc Apollon Oikonomopoulos
2593 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
2594 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
2595 b6135bbc Apollon Oikonomopoulos

2596 b6135bbc Apollon Oikonomopoulos
    """
2597 b6135bbc Apollon Oikonomopoulos
    pass
2598 b6135bbc Apollon Oikonomopoulos
2599 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
2600 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
2601 b6135bbc Apollon Oikonomopoulos

2602 b6135bbc Apollon Oikonomopoulos
    """
2603 b6135bbc Apollon Oikonomopoulos
    pass
2604 b6135bbc Apollon Oikonomopoulos
2605 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
2606 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
2607 b6135bbc Apollon Oikonomopoulos

2608 b6135bbc Apollon Oikonomopoulos
    """
2609 b6135bbc Apollon Oikonomopoulos
    pass
2610 b6135bbc Apollon Oikonomopoulos
2611 b6135bbc Apollon Oikonomopoulos
  def Close(self):
2612 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
2613 b6135bbc Apollon Oikonomopoulos

2614 b6135bbc Apollon Oikonomopoulos
    """
2615 b6135bbc Apollon Oikonomopoulos
    pass
2616 b6135bbc Apollon Oikonomopoulos
2617 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2618 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
2619 b6135bbc Apollon Oikonomopoulos

2620 b6135bbc Apollon Oikonomopoulos
    """
2621 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2622 b6135bbc Apollon Oikonomopoulos
2623 b6135bbc Apollon Oikonomopoulos
2624 7181fba0 Constantinos Venetsanopoulos
class RADOSBlockDevice(BlockDev):
2625 7181fba0 Constantinos Venetsanopoulos
  """A RADOS Block Device (rbd).
2626 7181fba0 Constantinos Venetsanopoulos

2627 7181fba0 Constantinos Venetsanopoulos
  This class implements the RADOS Block Device for the backend. You need
2628 7181fba0 Constantinos Venetsanopoulos
  the rbd kernel driver, the RADOS Tools and a working RADOS cluster for
2629 7181fba0 Constantinos Venetsanopoulos
  this to be functional.
2630 7181fba0 Constantinos Venetsanopoulos

2631 7181fba0 Constantinos Venetsanopoulos
  """
2632 7181fba0 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
2633 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
2634 7181fba0 Constantinos Venetsanopoulos

2635 7181fba0 Constantinos Venetsanopoulos
    """
2636 7181fba0 Constantinos Venetsanopoulos
    super(RADOSBlockDevice, self).__init__(unique_id, children, size, params)
2637 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2638 7181fba0 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2639 7181fba0 Constantinos Venetsanopoulos
2640 7181fba0 Constantinos Venetsanopoulos
    self.driver, self.rbd_name = unique_id
2641 7181fba0 Constantinos Venetsanopoulos
2642 7181fba0 Constantinos Venetsanopoulos
    self.major = self.minor = None
2643 7181fba0 Constantinos Venetsanopoulos
    self.Attach()
2644 7181fba0 Constantinos Venetsanopoulos
2645 7181fba0 Constantinos Venetsanopoulos
  @classmethod
2646 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2647 7181fba0 Constantinos Venetsanopoulos
    """Create a new rbd device.
2648 7181fba0 Constantinos Venetsanopoulos

2649 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
2650 7181fba0 Constantinos Venetsanopoulos

2651 7181fba0 Constantinos Venetsanopoulos
    """
2652 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2653 7181fba0 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
2654 7181fba0 Constantinos Venetsanopoulos
                                   str(unique_id))
2655 ee1478e5 Bernardo Dal Seno
    if excl_stor:
2656 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("RBD device requested with"
2657 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
2658 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = params[constants.LDP_POOL]
2659 7181fba0 Constantinos Venetsanopoulos
    rbd_name = unique_id[1]
2660 7181fba0 Constantinos Venetsanopoulos
2661 7181fba0 Constantinos Venetsanopoulos
    # Provision a new rbd volume (Image) inside the RADOS cluster.
2662 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "create", "-p", rbd_pool,
2663 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % size]
2664 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2665 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2666 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd creation failed (%s): %s",
2667 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2668 7181fba0 Constantinos Venetsanopoulos
2669 7181fba0 Constantinos Venetsanopoulos
    return RADOSBlockDevice(unique_id, children, size, params)
2670 7181fba0 Constantinos Venetsanopoulos
2671 7181fba0 Constantinos Venetsanopoulos
  def Remove(self):
2672 7181fba0 Constantinos Venetsanopoulos
    """Remove the rbd device.
2673 7181fba0 Constantinos Venetsanopoulos

2674 7181fba0 Constantinos Venetsanopoulos
    """
2675 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
2676 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
2677 7181fba0 Constantinos Venetsanopoulos
2678 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2679 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
2680 7181fba0 Constantinos Venetsanopoulos
      return
2681 7181fba0 Constantinos Venetsanopoulos
2682 7181fba0 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
2683 7181fba0 Constantinos Venetsanopoulos
    self.Shutdown()
2684 7181fba0 Constantinos Venetsanopoulos
2685 7181fba0 Constantinos Venetsanopoulos
    # Remove the actual Volume (Image) from the RADOS cluster.
2686 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "rm", "-p", rbd_pool, rbd_name]
2687 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2688 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2689 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Can't remove Volume from cluster with rbd rm: %s - %s",
2690 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2691 7181fba0 Constantinos Venetsanopoulos
2692 7181fba0 Constantinos Venetsanopoulos
  def Rename(self, new_id):
2693 7181fba0 Constantinos Venetsanopoulos
    """Rename this device.
2694 7181fba0 Constantinos Venetsanopoulos

2695 7181fba0 Constantinos Venetsanopoulos
    """
2696 7181fba0 Constantinos Venetsanopoulos
    pass
2697 7181fba0 Constantinos Venetsanopoulos
2698 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
2699 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
2700 7181fba0 Constantinos Venetsanopoulos

2701 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
2702 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
2703 7181fba0 Constantinos Venetsanopoulos

2704 7181fba0 Constantinos Venetsanopoulos
    """
2705 7181fba0 Constantinos Venetsanopoulos
    self.attached = False
2706 7181fba0 Constantinos Venetsanopoulos
2707 7181fba0 Constantinos Venetsanopoulos
    # Map the rbd volume to a block device under /dev
2708 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = self._MapVolumeToBlockdev(self.unique_id)
2709 7181fba0 Constantinos Venetsanopoulos
2710 7181fba0 Constantinos Venetsanopoulos
    try:
2711 7181fba0 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
2712 7181fba0 Constantinos Venetsanopoulos
    except OSError, err:
2713 7181fba0 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2714 7181fba0 Constantinos Venetsanopoulos
      return False
2715 7181fba0 Constantinos Venetsanopoulos
2716 7181fba0 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
2717 7181fba0 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
2718 7181fba0 Constantinos Venetsanopoulos
      return False
2719 7181fba0 Constantinos Venetsanopoulos
2720 7181fba0 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
2721 7181fba0 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
2722 7181fba0 Constantinos Venetsanopoulos
    self.attached = True
2723 7181fba0 Constantinos Venetsanopoulos
2724 7181fba0 Constantinos Venetsanopoulos
    return True
2725 7181fba0 Constantinos Venetsanopoulos
2726 7181fba0 Constantinos Venetsanopoulos
  def _MapVolumeToBlockdev(self, unique_id):
2727 7181fba0 Constantinos Venetsanopoulos
    """Maps existing rbd volumes to block devices.
2728 7181fba0 Constantinos Venetsanopoulos

2729 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
2730 7181fba0 Constantinos Venetsanopoulos

2731 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
2732 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
2733 7181fba0 Constantinos Venetsanopoulos

2734 7181fba0 Constantinos Venetsanopoulos
    """
2735 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2736 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2737 7181fba0 Constantinos Venetsanopoulos
2738 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2739 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
2740 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2741 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Return it.
2742 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2743 7181fba0 Constantinos Venetsanopoulos
2744 7181fba0 Constantinos Venetsanopoulos
    # The mapping doesn't exist. Create it.
2745 7181fba0 Constantinos Venetsanopoulos
    map_cmd = [constants.RBD_CMD, "map", "-p", pool, name]
2746 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(map_cmd)
2747 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2748 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map failed (%s): %s",
2749 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2750 7181fba0 Constantinos Venetsanopoulos
2751 7181fba0 Constantinos Venetsanopoulos
    # Find the corresponding rbd device.
2752 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
2753 7181fba0 Constantinos Venetsanopoulos
    if not rbd_dev:
2754 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map succeeded, but could not find the rbd block"
2755 7181fba0 Constantinos Venetsanopoulos
                  " device in output of showmapped, for volume: %s", name)
2756 7181fba0 Constantinos Venetsanopoulos
2757 7181fba0 Constantinos Venetsanopoulos
    # The device was successfully mapped. Return it.
2758 7181fba0 Constantinos Venetsanopoulos
    return rbd_dev
2759 7181fba0 Constantinos Venetsanopoulos
2760 bdecfea2 Stratos Psomadakis
  @classmethod
2761 bdecfea2 Stratos Psomadakis
  def _VolumeToBlockdev(cls, pool, volume_name):
2762 bdecfea2 Stratos Psomadakis
    """Do the 'volume name'-to-'rbd block device' resolving.
2763 bdecfea2 Stratos Psomadakis

2764 bdecfea2 Stratos Psomadakis
    @type pool: string
2765 bdecfea2 Stratos Psomadakis
    @param pool: RADOS pool to use
2766 bdecfea2 Stratos Psomadakis
    @type volume_name: string
2767 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
2768 bdecfea2 Stratos Psomadakis
    @rtype: string or None
2769 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
2770 bdecfea2 Stratos Psomadakis

2771 bdecfea2 Stratos Psomadakis
    """
2772 bdecfea2 Stratos Psomadakis
    try:
2773 bdecfea2 Stratos Psomadakis
      # Newer versions of the rbd tool support json output formatting. Use it
2774 bdecfea2 Stratos Psomadakis
      # if available.
2775 bdecfea2 Stratos Psomadakis
      showmap_cmd = [
2776 bdecfea2 Stratos Psomadakis
        constants.RBD_CMD,
2777 bdecfea2 Stratos Psomadakis
        "showmapped",
2778 bdecfea2 Stratos Psomadakis
        "-p",
2779 bdecfea2 Stratos Psomadakis
        pool,
2780 bdecfea2 Stratos Psomadakis
        "--format",
2781 bdecfea2 Stratos Psomadakis
        "json"
2782 bdecfea2 Stratos Psomadakis
        ]
2783 bdecfea2 Stratos Psomadakis
      result = utils.RunCmd(showmap_cmd)
2784 bdecfea2 Stratos Psomadakis
      if result.failed:
2785 bdecfea2 Stratos Psomadakis
        logging.error("rbd JSON output formatting returned error (%s): %s,"
2786 bdecfea2 Stratos Psomadakis
                      "falling back to plain output parsing",
2787 bdecfea2 Stratos Psomadakis
                      result.fail_reason, result.output)
2788 bdecfea2 Stratos Psomadakis
        raise RbdShowmappedJsonError
2789 bdecfea2 Stratos Psomadakis
2790 bdecfea2 Stratos Psomadakis
      return cls._ParseRbdShowmappedJson(result.output, volume_name)
2791 bdecfea2 Stratos Psomadakis
    except RbdShowmappedJsonError:
2792 bdecfea2 Stratos Psomadakis
      # For older versions of rbd, we have to parse the plain / text output
2793 bdecfea2 Stratos Psomadakis
      # manually.
2794 bdecfea2 Stratos Psomadakis
      showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2795 bdecfea2 Stratos Psomadakis
      result = utils.RunCmd(showmap_cmd)
2796 bdecfea2 Stratos Psomadakis
      if result.failed:
2797 bdecfea2 Stratos Psomadakis
        _ThrowError("rbd showmapped failed (%s): %s",
2798 bdecfea2 Stratos Psomadakis
                    result.fail_reason, result.output)
2799 bdecfea2 Stratos Psomadakis
2800 bdecfea2 Stratos Psomadakis
      return cls._ParseRbdShowmappedPlain(result.output, volume_name)
2801 bdecfea2 Stratos Psomadakis
2802 bdecfea2 Stratos Psomadakis
  @staticmethod
2803 bdecfea2 Stratos Psomadakis
  def _ParseRbdShowmappedJson(output, volume_name):
2804 bdecfea2 Stratos Psomadakis
    """Parse the json output of `rbd showmapped'.
2805 bdecfea2 Stratos Psomadakis

2806 bdecfea2 Stratos Psomadakis
    This method parses the json output of `rbd showmapped' and returns the rbd
2807 bdecfea2 Stratos Psomadakis
    block device path (e.g. /dev/rbd0) that matches the given rbd volume.
2808 bdecfea2 Stratos Psomadakis

2809 bdecfea2 Stratos Psomadakis
    @type output: string
2810 bdecfea2 Stratos Psomadakis
    @param output: the json output of `rbd showmapped'
2811 bdecfea2 Stratos Psomadakis
    @type volume_name: string
2812 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
2813 bdecfea2 Stratos Psomadakis
    @rtype: string or None
2814 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
2815 bdecfea2 Stratos Psomadakis

2816 bdecfea2 Stratos Psomadakis
    """
2817 bdecfea2 Stratos Psomadakis
    try:
2818 bdecfea2 Stratos Psomadakis
      devices = serializer.LoadJson(output)
2819 bdecfea2 Stratos Psomadakis
    except ValueError, err:
2820 bdecfea2 Stratos Psomadakis
      _ThrowError("Unable to parse JSON data: %s" % err)
2821 bdecfea2 Stratos Psomadakis
2822 bdecfea2 Stratos Psomadakis
    rbd_dev = None
2823 bdecfea2 Stratos Psomadakis
    for d in devices.values(): # pylint: disable=E1103
2824 bdecfea2 Stratos Psomadakis
      try:
2825 bdecfea2 Stratos Psomadakis
        name = d["name"]
2826 bdecfea2 Stratos Psomadakis
      except KeyError:
2827 bdecfea2 Stratos Psomadakis
        _ThrowError("'name' key missing from json object %s", devices)
2828 bdecfea2 Stratos Psomadakis
2829 bdecfea2 Stratos Psomadakis
      if name == volume_name:
2830 bdecfea2 Stratos Psomadakis
        if rbd_dev is not None:
2831 bdecfea2 Stratos Psomadakis
          _ThrowError("rbd volume %s is mapped more than once", volume_name)
2832 bdecfea2 Stratos Psomadakis
2833 bdecfea2 Stratos Psomadakis
        rbd_dev = d["device"]
2834 bdecfea2 Stratos Psomadakis
2835 bdecfea2 Stratos Psomadakis
    return rbd_dev
2836 bdecfea2 Stratos Psomadakis
2837 7181fba0 Constantinos Venetsanopoulos
  @staticmethod
2838 bdecfea2 Stratos Psomadakis
  def _ParseRbdShowmappedPlain(output, volume_name):
2839 bdecfea2 Stratos Psomadakis
    """Parse the (plain / text) output of `rbd showmapped'.
2840 7181fba0 Constantinos Venetsanopoulos

2841 7181fba0 Constantinos Venetsanopoulos
    This method parses the output of `rbd showmapped' and returns
2842 7181fba0 Constantinos Venetsanopoulos
    the rbd block device path (e.g. /dev/rbd0) that matches the
2843 7181fba0 Constantinos Venetsanopoulos
    given rbd volume.
2844 7181fba0 Constantinos Venetsanopoulos

2845 7181fba0 Constantinos Venetsanopoulos
    @type output: string
2846 bdecfea2 Stratos Psomadakis
    @param output: the plain text output of `rbd showmapped'
2847 7181fba0 Constantinos Venetsanopoulos
    @type volume_name: string
2848 7181fba0 Constantinos Venetsanopoulos
    @param volume_name: the name of the volume whose device we search for
2849 7181fba0 Constantinos Venetsanopoulos
    @rtype: string or None
2850 7181fba0 Constantinos Venetsanopoulos
    @return: block device path if the volume is mapped, else None
2851 7181fba0 Constantinos Venetsanopoulos

2852 7181fba0 Constantinos Venetsanopoulos
    """
2853 7181fba0 Constantinos Venetsanopoulos
    allfields = 5
2854 7181fba0 Constantinos Venetsanopoulos
    volumefield = 2
2855 7181fba0 Constantinos Venetsanopoulos
    devicefield = 4
2856 7181fba0 Constantinos Venetsanopoulos
2857 7181fba0 Constantinos Venetsanopoulos
    lines = output.splitlines()
2858 7181fba0 Constantinos Venetsanopoulos
2859 bdecfea2 Stratos Psomadakis
    # Try parsing the new output format (ceph >= 0.55).
2860 bdecfea2 Stratos Psomadakis
    splitted_lines = map(lambda l: l.split(), lines)
2861 bdecfea2 Stratos Psomadakis
2862 bdecfea2 Stratos Psomadakis
    # Check for empty output.
2863 7181fba0 Constantinos Venetsanopoulos
    if not splitted_lines:
2864 bdecfea2 Stratos Psomadakis
      return None
2865 7181fba0 Constantinos Venetsanopoulos
2866 bdecfea2 Stratos Psomadakis
    # Check showmapped output, to determine number of fields.
2867 7181fba0 Constantinos Venetsanopoulos
    field_cnt = len(splitted_lines[0])
2868 7181fba0 Constantinos Venetsanopoulos
    if field_cnt != allfields:
2869 bdecfea2 Stratos Psomadakis
      # Parsing the new format failed. Fallback to parsing the old output
2870 bdecfea2 Stratos Psomadakis
      # format (< 0.55).
2871 bdecfea2 Stratos Psomadakis
      splitted_lines = map(lambda l: l.split("\t"), lines)
2872 bdecfea2 Stratos Psomadakis
      if field_cnt != allfields:
2873 bdecfea2 Stratos Psomadakis
        _ThrowError("Cannot parse rbd showmapped output expected %s fields,"
2874 bdecfea2 Stratos Psomadakis
                    " found %s", allfields, field_cnt)
2875 7181fba0 Constantinos Venetsanopoulos
2876 7181fba0 Constantinos Venetsanopoulos
    matched_lines = \
2877 7181fba0 Constantinos Venetsanopoulos
      filter(lambda l: len(l) == allfields and l[volumefield] == volume_name,
2878 7181fba0 Constantinos Venetsanopoulos
             splitted_lines)
2879 7181fba0 Constantinos Venetsanopoulos
2880 7181fba0 Constantinos Venetsanopoulos
    if len(matched_lines) > 1:
2881 bdecfea2 Stratos Psomadakis
      _ThrowError("rbd volume %s mapped more than once", volume_name)
2882 7181fba0 Constantinos Venetsanopoulos
2883 7181fba0 Constantinos Venetsanopoulos
    if matched_lines:
2884 7181fba0 Constantinos Venetsanopoulos
      # rbd block device found. Return it.
2885 7181fba0 Constantinos Venetsanopoulos
      rbd_dev = matched_lines[0][devicefield]
2886 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2887 7181fba0 Constantinos Venetsanopoulos
2888 7181fba0 Constantinos Venetsanopoulos
    # The given volume is not mapped.
2889 7181fba0 Constantinos Venetsanopoulos
    return None
2890 7181fba0 Constantinos Venetsanopoulos
2891 7181fba0 Constantinos Venetsanopoulos
  def Assemble(self):
2892 7181fba0 Constantinos Venetsanopoulos
    """Assemble the device.
2893 7181fba0 Constantinos Venetsanopoulos

2894 7181fba0 Constantinos Venetsanopoulos
    """
2895 7181fba0 Constantinos Venetsanopoulos
    pass
2896 7181fba0 Constantinos Venetsanopoulos
2897 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
2898 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
2899 7181fba0 Constantinos Venetsanopoulos

2900 7181fba0 Constantinos Venetsanopoulos
    """
2901 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2902 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
2903 7181fba0 Constantinos Venetsanopoulos
      return
2904 7181fba0 Constantinos Venetsanopoulos
2905 7181fba0 Constantinos Venetsanopoulos
    # Unmap the block device from the Volume.
2906 7181fba0 Constantinos Venetsanopoulos
    self._UnmapVolumeFromBlockdev(self.unique_id)
2907 7181fba0 Constantinos Venetsanopoulos
2908 7181fba0 Constantinos Venetsanopoulos
    self.minor = None
2909 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = None
2910 7181fba0 Constantinos Venetsanopoulos
2911 7181fba0 Constantinos Venetsanopoulos
  def _UnmapVolumeFromBlockdev(self, unique_id):
2912 7181fba0 Constantinos Venetsanopoulos
    """Unmaps the rbd device from the Volume it is mapped.
2913 7181fba0 Constantinos Venetsanopoulos

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

2917 7181fba0 Constantinos Venetsanopoulos
    """
2918 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2919 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2920 7181fba0 Constantinos Venetsanopoulos
2921 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2922 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
2923 7181fba0 Constantinos Venetsanopoulos
2924 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2925 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Unmap the rbd device.
2926 7181fba0 Constantinos Venetsanopoulos
      unmap_cmd = [constants.RBD_CMD, "unmap", "%s" % rbd_dev]
2927 7181fba0 Constantinos Venetsanopoulos
      result = utils.RunCmd(unmap_cmd)
2928 7181fba0 Constantinos Venetsanopoulos
      if result.failed:
2929 7181fba0 Constantinos Venetsanopoulos
        _ThrowError("rbd unmap failed (%s): %s",
2930 7181fba0 Constantinos Venetsanopoulos
                    result.fail_reason, result.output)
2931 7181fba0 Constantinos Venetsanopoulos
2932 7181fba0 Constantinos Venetsanopoulos
  def Open(self, force=False):
2933 7181fba0 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
2934 7181fba0 Constantinos Venetsanopoulos

2935 7181fba0 Constantinos Venetsanopoulos
    """
2936 7181fba0 Constantinos Venetsanopoulos
    pass
2937 7181fba0 Constantinos Venetsanopoulos
2938 7181fba0 Constantinos Venetsanopoulos
  def Close(self):
2939 7181fba0 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
2940 7181fba0 Constantinos Venetsanopoulos

2941 7181fba0 Constantinos Venetsanopoulos
    """
2942 7181fba0 Constantinos Venetsanopoulos
    pass
2943 7181fba0 Constantinos Venetsanopoulos
2944 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2945 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
2946 7181fba0 Constantinos Venetsanopoulos

2947 7181fba0 Constantinos Venetsanopoulos
    @type amount: integer
2948 7181fba0 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
2949 7181fba0 Constantinos Venetsanopoulos
    @type dryrun: boolean
2950 7181fba0 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
2951 7181fba0 Constantinos Venetsanopoulos
        only, without actually increasing the size
2952 7181fba0 Constantinos Venetsanopoulos

2953 7181fba0 Constantinos Venetsanopoulos
    """
2954 cad0723b Iustin Pop
    if not backingstore:
2955 cad0723b Iustin Pop
      return
2956 7181fba0 Constantinos Venetsanopoulos
    if not self.Attach():
2957 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Can't attach to rbd device during Grow()")
2958 7181fba0 Constantinos Venetsanopoulos
2959 7181fba0 Constantinos Venetsanopoulos
    if dryrun:
2960 7181fba0 Constantinos Venetsanopoulos
      # the rbd tool does not support dry runs of resize operations.
2961 7181fba0 Constantinos Venetsanopoulos
      # Since rbd volumes are thinly provisioned, we assume
2962 7181fba0 Constantinos Venetsanopoulos
      # there is always enough free space for the operation.
2963 7181fba0 Constantinos Venetsanopoulos
      return
2964 7181fba0 Constantinos Venetsanopoulos
2965 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
2966 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
2967 7181fba0 Constantinos Venetsanopoulos
    new_size = self.size + amount
2968 7181fba0 Constantinos Venetsanopoulos
2969 7181fba0 Constantinos Venetsanopoulos
    # Resize the rbd volume (Image) inside the RADOS cluster.
2970 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "resize", "-p", rbd_pool,
2971 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % new_size]
2972 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2973 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2974 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd resize failed (%s): %s",
2975 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2976 7181fba0 Constantinos Venetsanopoulos
2977 7181fba0 Constantinos Venetsanopoulos
2978 376631d1 Constantinos Venetsanopoulos
class ExtStorageDevice(BlockDev):
2979 376631d1 Constantinos Venetsanopoulos
  """A block device provided by an ExtStorage Provider.
2980 376631d1 Constantinos Venetsanopoulos

2981 376631d1 Constantinos Venetsanopoulos
  This class implements the External Storage Interface, which means
2982 376631d1 Constantinos Venetsanopoulos
  handling of the externally provided block devices.
2983 376631d1 Constantinos Venetsanopoulos

2984 376631d1 Constantinos Venetsanopoulos
  """
2985 376631d1 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
2986 376631d1 Constantinos Venetsanopoulos
    """Attaches to an extstorage block device.
2987 376631d1 Constantinos Venetsanopoulos

2988 376631d1 Constantinos Venetsanopoulos
    """
2989 376631d1 Constantinos Venetsanopoulos
    super(ExtStorageDevice, self).__init__(unique_id, children, size, params)
2990 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2991 376631d1 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2992 376631d1 Constantinos Venetsanopoulos
2993 376631d1 Constantinos Venetsanopoulos
    self.driver, self.vol_name = unique_id
2994 938adc87 Constantinos Venetsanopoulos
    self.ext_params = params
2995 376631d1 Constantinos Venetsanopoulos
2996 376631d1 Constantinos Venetsanopoulos
    self.major = self.minor = None
2997 376631d1 Constantinos Venetsanopoulos
    self.Attach()
2998 376631d1 Constantinos Venetsanopoulos
2999 376631d1 Constantinos Venetsanopoulos
  @classmethod
3000 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
3001 376631d1 Constantinos Venetsanopoulos
    """Create a new extstorage device.
3002 376631d1 Constantinos Venetsanopoulos

3003 376631d1 Constantinos Venetsanopoulos
    Provision a new volume using an extstorage provider, which will
3004 376631d1 Constantinos Venetsanopoulos
    then be mapped to a block device.
3005 376631d1 Constantinos Venetsanopoulos

3006 376631d1 Constantinos Venetsanopoulos
    """
3007 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
3008 376631d1 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
3009 376631d1 Constantinos Venetsanopoulos
                                   str(unique_id))
3010 ee1478e5 Bernardo Dal Seno
    if excl_stor:
3011 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("extstorage device requested with"
3012 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
3013 376631d1 Constantinos Venetsanopoulos
3014 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's create script,
3015 376631d1 Constantinos Venetsanopoulos
    # to provision a new Volume inside the External Storage
3016 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id,
3017 938adc87 Constantinos Venetsanopoulos
                      params, str(size))
3018 376631d1 Constantinos Venetsanopoulos
3019 376631d1 Constantinos Venetsanopoulos
    return ExtStorageDevice(unique_id, children, size, params)
3020 376631d1 Constantinos Venetsanopoulos
3021 376631d1 Constantinos Venetsanopoulos
  def Remove(self):
3022 376631d1 Constantinos Venetsanopoulos
    """Remove the extstorage device.
3023 376631d1 Constantinos Venetsanopoulos

3024 376631d1 Constantinos Venetsanopoulos
    """
3025 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
3026 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
3027 376631d1 Constantinos Venetsanopoulos
      return
3028 376631d1 Constantinos Venetsanopoulos
3029 376631d1 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
3030 376631d1 Constantinos Venetsanopoulos
    self.Shutdown()
3031 376631d1 Constantinos Venetsanopoulos
3032 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's remove script,
3033 376631d1 Constantinos Venetsanopoulos
    # to remove the Volume from the External Storage
3034 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id,
3035 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
3036 376631d1 Constantinos Venetsanopoulos
3037 376631d1 Constantinos Venetsanopoulos
  def Rename(self, new_id):
3038 376631d1 Constantinos Venetsanopoulos
    """Rename this device.
3039 376631d1 Constantinos Venetsanopoulos

3040 376631d1 Constantinos Venetsanopoulos
    """
3041 376631d1 Constantinos Venetsanopoulos
    pass
3042 376631d1 Constantinos Venetsanopoulos
3043 376631d1 Constantinos Venetsanopoulos
  def Attach(self):
3044 376631d1 Constantinos Venetsanopoulos
    """Attach to an existing extstorage device.
3045 376631d1 Constantinos Venetsanopoulos

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

3049 376631d1 Constantinos Venetsanopoulos
    """
3050 376631d1 Constantinos Venetsanopoulos
    self.attached = False
3051 376631d1 Constantinos Venetsanopoulos
3052 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's attach script,
3053 376631d1 Constantinos Venetsanopoulos
    # to attach an existing Volume to a block device under /dev
3054 376631d1 Constantinos Venetsanopoulos
    self.dev_path = _ExtStorageAction(constants.ES_ACTION_ATTACH,
3055 938adc87 Constantinos Venetsanopoulos
                                      self.unique_id, self.ext_params)
3056 376631d1 Constantinos Venetsanopoulos
3057 376631d1 Constantinos Venetsanopoulos
    try:
3058 376631d1 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
3059 376631d1 Constantinos Venetsanopoulos
    except OSError, err:
3060 376631d1 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
3061 376631d1 Constantinos Venetsanopoulos
      return False
3062 376631d1 Constantinos Venetsanopoulos
3063 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
3064 376631d1 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
3065 376631d1 Constantinos Venetsanopoulos
      return False
3066 376631d1 Constantinos Venetsanopoulos
3067 376631d1 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
3068 376631d1 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
3069 376631d1 Constantinos Venetsanopoulos
    self.attached = True
3070 376631d1 Constantinos Venetsanopoulos
3071 376631d1 Constantinos Venetsanopoulos
    return True
3072 376631d1 Constantinos Venetsanopoulos
3073 376631d1 Constantinos Venetsanopoulos
  def Assemble(self):
3074 376631d1 Constantinos Venetsanopoulos
    """Assemble the device.
3075 376631d1 Constantinos Venetsanopoulos

3076 376631d1 Constantinos Venetsanopoulos
    """
3077 376631d1 Constantinos Venetsanopoulos
    pass
3078 376631d1 Constantinos Venetsanopoulos
3079 376631d1 Constantinos Venetsanopoulos
  def Shutdown(self):
3080 376631d1 Constantinos Venetsanopoulos
    """Shutdown the device.
3081 376631d1 Constantinos Venetsanopoulos

3082 376631d1 Constantinos Venetsanopoulos
    """
3083 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
3084 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
3085 376631d1 Constantinos Venetsanopoulos
      return
3086 376631d1 Constantinos Venetsanopoulos
3087 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's detach script,
3088 376631d1 Constantinos Venetsanopoulos
    # to detach an existing Volume from it's block device under /dev
3089 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id,
3090 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
3091 376631d1 Constantinos Venetsanopoulos
3092 376631d1 Constantinos Venetsanopoulos
    self.minor = None
3093 376631d1 Constantinos Venetsanopoulos
    self.dev_path = None
3094 376631d1 Constantinos Venetsanopoulos
3095 376631d1 Constantinos Venetsanopoulos
  def Open(self, force=False):
3096 376631d1 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
3097 376631d1 Constantinos Venetsanopoulos

3098 376631d1 Constantinos Venetsanopoulos
    """
3099 376631d1 Constantinos Venetsanopoulos
    pass
3100 376631d1 Constantinos Venetsanopoulos
3101 376631d1 Constantinos Venetsanopoulos
  def Close(self):
3102 376631d1 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
3103 376631d1 Constantinos Venetsanopoulos

3104 376631d1 Constantinos Venetsanopoulos
    """
3105 376631d1 Constantinos Venetsanopoulos
    pass
3106 376631d1 Constantinos Venetsanopoulos
3107 376631d1 Constantinos Venetsanopoulos
  def Grow(self, amount, dryrun, backingstore):
3108 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
3109 376631d1 Constantinos Venetsanopoulos

3110 376631d1 Constantinos Venetsanopoulos
    @type amount: integer
3111 376631d1 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
3112 376631d1 Constantinos Venetsanopoulos
    @type dryrun: boolean
3113 376631d1 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
3114 376631d1 Constantinos Venetsanopoulos
        only, without actually increasing the size
3115 376631d1 Constantinos Venetsanopoulos

3116 376631d1 Constantinos Venetsanopoulos
    """
3117 376631d1 Constantinos Venetsanopoulos
    if not backingstore:
3118 376631d1 Constantinos Venetsanopoulos
      return
3119 376631d1 Constantinos Venetsanopoulos
    if not self.Attach():
3120 376631d1 Constantinos Venetsanopoulos
      _ThrowError("Can't attach to extstorage device during Grow()")
3121 376631d1 Constantinos Venetsanopoulos
3122 376631d1 Constantinos Venetsanopoulos
    if dryrun:
3123 376631d1 Constantinos Venetsanopoulos
      # we do not support dry runs of resize operations for now.
3124 376631d1 Constantinos Venetsanopoulos
      return
3125 376631d1 Constantinos Venetsanopoulos
3126 376631d1 Constantinos Venetsanopoulos
    new_size = self.size + amount
3127 376631d1 Constantinos Venetsanopoulos
3128 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's grow script,
3129 376631d1 Constantinos Venetsanopoulos
    # to grow an existing Volume inside the External Storage
3130 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_GROW, self.unique_id,
3131 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, str(self.size), grow=str(new_size))
3132 376631d1 Constantinos Venetsanopoulos
3133 376631d1 Constantinos Venetsanopoulos
  def SetInfo(self, text):
3134 376631d1 Constantinos Venetsanopoulos
    """Update metadata with info text.
3135 376631d1 Constantinos Venetsanopoulos

3136 376631d1 Constantinos Venetsanopoulos
    """
3137 376631d1 Constantinos Venetsanopoulos
    # Replace invalid characters
3138 376631d1 Constantinos Venetsanopoulos
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
3139 376631d1 Constantinos Venetsanopoulos
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
3140 376631d1 Constantinos Venetsanopoulos
3141 376631d1 Constantinos Venetsanopoulos
    # Only up to 128 characters are allowed
3142 376631d1 Constantinos Venetsanopoulos
    text = text[:128]
3143 376631d1 Constantinos Venetsanopoulos
3144 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's setinfo script,
3145 376631d1 Constantinos Venetsanopoulos
    # to set metadata for an existing Volume inside the External Storage
3146 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
3147 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, metadata=text)
3148 376631d1 Constantinos Venetsanopoulos
3149 376631d1 Constantinos Venetsanopoulos
3150 938adc87 Constantinos Venetsanopoulos
def _ExtStorageAction(action, unique_id, ext_params,
3151 938adc87 Constantinos Venetsanopoulos
                      size=None, grow=None, metadata=None):
3152 376631d1 Constantinos Venetsanopoulos
  """Take an External Storage action.
3153 376631d1 Constantinos Venetsanopoulos

3154 376631d1 Constantinos Venetsanopoulos
  Take an External Storage action concerning or affecting
3155 376631d1 Constantinos Venetsanopoulos
  a specific Volume inside the External Storage.
3156 376631d1 Constantinos Venetsanopoulos

3157 376631d1 Constantinos Venetsanopoulos
  @type action: string
3158 376631d1 Constantinos Venetsanopoulos
  @param action: which action to perform. One of:
3159 376631d1 Constantinos Venetsanopoulos
                 create / remove / grow / attach / detach
3160 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
3161 376631d1 Constantinos Venetsanopoulos
  @param unique_id: a tuple containing the type of ExtStorage (driver)
3162 376631d1 Constantinos Venetsanopoulos
                    and the Volume name
3163 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
3164 938adc87 Constantinos Venetsanopoulos
  @param ext_params: ExtStorage parameters
3165 376631d1 Constantinos Venetsanopoulos
  @type size: integer
3166 376631d1 Constantinos Venetsanopoulos
  @param size: the size of the Volume in mebibytes
3167 376631d1 Constantinos Venetsanopoulos
  @type grow: integer
3168 376631d1 Constantinos Venetsanopoulos
  @param grow: the new size in mebibytes (after grow)
3169 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
3170 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume, for use by the provider
3171 376631d1 Constantinos Venetsanopoulos
  @rtype: None or a block device path (during attach)
3172 376631d1 Constantinos Venetsanopoulos

3173 376631d1 Constantinos Venetsanopoulos
  """
3174 376631d1 Constantinos Venetsanopoulos
  driver, vol_name = unique_id
3175 376631d1 Constantinos Venetsanopoulos
3176 376631d1 Constantinos Venetsanopoulos
  # Create an External Storage instance of type `driver'
3177 376631d1 Constantinos Venetsanopoulos
  status, inst_es = ExtStorageFromDisk(driver)
3178 376631d1 Constantinos Venetsanopoulos
  if not status:
3179 376631d1 Constantinos Venetsanopoulos
    _ThrowError("%s" % inst_es)
3180 376631d1 Constantinos Venetsanopoulos
3181 376631d1 Constantinos Venetsanopoulos
  # Create the basic environment for the driver's scripts
3182 938adc87 Constantinos Venetsanopoulos
  create_env = _ExtStorageEnvironment(unique_id, ext_params, size,
3183 938adc87 Constantinos Venetsanopoulos
                                      grow, metadata)
3184 376631d1 Constantinos Venetsanopoulos
3185 376631d1 Constantinos Venetsanopoulos
  # Do not use log file for action `attach' as we need
3186 376631d1 Constantinos Venetsanopoulos
  # to get the output from RunResult
3187 376631d1 Constantinos Venetsanopoulos
  # TODO: find a way to have a log file for attach too
3188 376631d1 Constantinos Venetsanopoulos
  logfile = None
3189 376631d1 Constantinos Venetsanopoulos
  if action is not constants.ES_ACTION_ATTACH:
3190 376631d1 Constantinos Venetsanopoulos
    logfile = _VolumeLogName(action, driver, vol_name)
3191 376631d1 Constantinos Venetsanopoulos
3192 376631d1 Constantinos Venetsanopoulos
  # Make sure the given action results in a valid script
3193 376631d1 Constantinos Venetsanopoulos
  if action not in constants.ES_SCRIPTS:
3194 376631d1 Constantinos Venetsanopoulos
    _ThrowError("Action '%s' doesn't result in a valid ExtStorage script" %
3195 376631d1 Constantinos Venetsanopoulos
                action)
3196 376631d1 Constantinos Venetsanopoulos
3197 376631d1 Constantinos Venetsanopoulos
  # Find out which external script to run according the given action
3198 376631d1 Constantinos Venetsanopoulos
  script_name = action + "_script"
3199 376631d1 Constantinos Venetsanopoulos
  script = getattr(inst_es, script_name)
3200 376631d1 Constantinos Venetsanopoulos
3201 376631d1 Constantinos Venetsanopoulos
  # Run the external script
3202 376631d1 Constantinos Venetsanopoulos
  result = utils.RunCmd([script], env=create_env,
3203 376631d1 Constantinos Venetsanopoulos
                        cwd=inst_es.path, output=logfile,)
3204 376631d1 Constantinos Venetsanopoulos
  if result.failed:
3205 376631d1 Constantinos Venetsanopoulos
    logging.error("External storage's %s command '%s' returned"
3206 376631d1 Constantinos Venetsanopoulos
                  " error: %s, logfile: %s, output: %s",
3207 376631d1 Constantinos Venetsanopoulos
                  action, result.cmd, result.fail_reason,
3208 376631d1 Constantinos Venetsanopoulos
                  logfile, result.output)
3209 376631d1 Constantinos Venetsanopoulos
3210 376631d1 Constantinos Venetsanopoulos
    # If logfile is 'None' (during attach), it breaks TailFile
3211 376631d1 Constantinos Venetsanopoulos
    # TODO: have a log file for attach too
3212 376631d1 Constantinos Venetsanopoulos
    if action is not constants.ES_ACTION_ATTACH:
3213 376631d1 Constantinos Venetsanopoulos
      lines = [utils.SafeEncode(val)
3214 376631d1 Constantinos Venetsanopoulos
               for val in utils.TailFile(logfile, lines=20)]
3215 376631d1 Constantinos Venetsanopoulos
    else:
3216 376631d1 Constantinos Venetsanopoulos
      lines = result.output[-20:]
3217 376631d1 Constantinos Venetsanopoulos
3218 376631d1 Constantinos Venetsanopoulos
    _ThrowError("External storage's %s script failed (%s), last"
3219 376631d1 Constantinos Venetsanopoulos
                " lines of output:\n%s",
3220 376631d1 Constantinos Venetsanopoulos
                action, result.fail_reason, "\n".join(lines))
3221 376631d1 Constantinos Venetsanopoulos
3222 376631d1 Constantinos Venetsanopoulos
  if action == constants.ES_ACTION_ATTACH:
3223 376631d1 Constantinos Venetsanopoulos
    return result.stdout
3224 376631d1 Constantinos Venetsanopoulos
3225 376631d1 Constantinos Venetsanopoulos
3226 376631d1 Constantinos Venetsanopoulos
def ExtStorageFromDisk(name, base_dir=None):
3227 376631d1 Constantinos Venetsanopoulos
  """Create an ExtStorage instance from disk.
3228 376631d1 Constantinos Venetsanopoulos

3229 376631d1 Constantinos Venetsanopoulos
  This function will return an ExtStorage instance
3230 376631d1 Constantinos Venetsanopoulos
  if the given name is a valid ExtStorage name.
3231 376631d1 Constantinos Venetsanopoulos

3232 376631d1 Constantinos Venetsanopoulos
  @type base_dir: string
3233 376631d1 Constantinos Venetsanopoulos
  @keyword base_dir: Base directory containing ExtStorage installations.
3234 376631d1 Constantinos Venetsanopoulos
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
3235 376631d1 Constantinos Venetsanopoulos
  @rtype: tuple
3236 376631d1 Constantinos Venetsanopoulos
  @return: True and the ExtStorage instance if we find a valid one, or
3237 376631d1 Constantinos Venetsanopoulos
      False and the diagnose message on error
3238 376631d1 Constantinos Venetsanopoulos

3239 376631d1 Constantinos Venetsanopoulos
  """
3240 376631d1 Constantinos Venetsanopoulos
  if base_dir is None:
3241 376631d1 Constantinos Venetsanopoulos
    es_base_dir = pathutils.ES_SEARCH_PATH
3242 376631d1 Constantinos Venetsanopoulos
  else:
3243 376631d1 Constantinos Venetsanopoulos
    es_base_dir = [base_dir]
3244 376631d1 Constantinos Venetsanopoulos
3245 376631d1 Constantinos Venetsanopoulos
  es_dir = utils.FindFile(name, es_base_dir, os.path.isdir)
3246 376631d1 Constantinos Venetsanopoulos
3247 376631d1 Constantinos Venetsanopoulos
  if es_dir is None:
3248 376631d1 Constantinos Venetsanopoulos
    return False, ("Directory for External Storage Provider %s not"
3249 376631d1 Constantinos Venetsanopoulos
                   " found in search path" % name)
3250 376631d1 Constantinos Venetsanopoulos
3251 376631d1 Constantinos Venetsanopoulos
  # ES Files dictionary, we will populate it with the absolute path
3252 376631d1 Constantinos Venetsanopoulos
  # names; if the value is True, then it is a required file, otherwise
3253 376631d1 Constantinos Venetsanopoulos
  # an optional one
3254 376631d1 Constantinos Venetsanopoulos
  es_files = dict.fromkeys(constants.ES_SCRIPTS, True)
3255 376631d1 Constantinos Venetsanopoulos
3256 938adc87 Constantinos Venetsanopoulos
  es_files[constants.ES_PARAMETERS_FILE] = True
3257 938adc87 Constantinos Venetsanopoulos
3258 938adc87 Constantinos Venetsanopoulos
  for (filename, _) in es_files.items():
3259 376631d1 Constantinos Venetsanopoulos
    es_files[filename] = utils.PathJoin(es_dir, filename)
3260 376631d1 Constantinos Venetsanopoulos
3261 376631d1 Constantinos Venetsanopoulos
    try:
3262 376631d1 Constantinos Venetsanopoulos
      st = os.stat(es_files[filename])
3263 376631d1 Constantinos Venetsanopoulos
    except EnvironmentError, err:
3264 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is missing (%s)" %
3265 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir, utils.ErrnoOrStr(err)))
3266 376631d1 Constantinos Venetsanopoulos
3267 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
3268 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is not a regular file" %
3269 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir))
3270 376631d1 Constantinos Venetsanopoulos
3271 376631d1 Constantinos Venetsanopoulos
    if filename in constants.ES_SCRIPTS:
3272 376631d1 Constantinos Venetsanopoulos
      if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
3273 376631d1 Constantinos Venetsanopoulos
        return False, ("File '%s' under path '%s' is not executable" %
3274 376631d1 Constantinos Venetsanopoulos
                       (filename, es_dir))
3275 376631d1 Constantinos Venetsanopoulos
3276 938adc87 Constantinos Venetsanopoulos
  parameters = []
3277 938adc87 Constantinos Venetsanopoulos
  if constants.ES_PARAMETERS_FILE in es_files:
3278 938adc87 Constantinos Venetsanopoulos
    parameters_file = es_files[constants.ES_PARAMETERS_FILE]
3279 938adc87 Constantinos Venetsanopoulos
    try:
3280 938adc87 Constantinos Venetsanopoulos
      parameters = utils.ReadFile(parameters_file).splitlines()
3281 938adc87 Constantinos Venetsanopoulos
    except EnvironmentError, err:
3282 938adc87 Constantinos Venetsanopoulos
      return False, ("Error while reading the EXT parameters file at %s: %s" %
3283 938adc87 Constantinos Venetsanopoulos
                     (parameters_file, utils.ErrnoOrStr(err)))
3284 938adc87 Constantinos Venetsanopoulos
    parameters = [v.split(None, 1) for v in parameters]
3285 938adc87 Constantinos Venetsanopoulos
3286 376631d1 Constantinos Venetsanopoulos
  es_obj = \
3287 376631d1 Constantinos Venetsanopoulos
    objects.ExtStorage(name=name, path=es_dir,
3288 376631d1 Constantinos Venetsanopoulos
                       create_script=es_files[constants.ES_SCRIPT_CREATE],
3289 376631d1 Constantinos Venetsanopoulos
                       remove_script=es_files[constants.ES_SCRIPT_REMOVE],
3290 376631d1 Constantinos Venetsanopoulos
                       grow_script=es_files[constants.ES_SCRIPT_GROW],
3291 376631d1 Constantinos Venetsanopoulos
                       attach_script=es_files[constants.ES_SCRIPT_ATTACH],
3292 376631d1 Constantinos Venetsanopoulos
                       detach_script=es_files[constants.ES_SCRIPT_DETACH],
3293 938adc87 Constantinos Venetsanopoulos
                       setinfo_script=es_files[constants.ES_SCRIPT_SETINFO],
3294 938adc87 Constantinos Venetsanopoulos
                       verify_script=es_files[constants.ES_SCRIPT_VERIFY],
3295 938adc87 Constantinos Venetsanopoulos
                       supported_parameters=parameters)
3296 376631d1 Constantinos Venetsanopoulos
  return True, es_obj
3297 376631d1 Constantinos Venetsanopoulos
3298 376631d1 Constantinos Venetsanopoulos
3299 938adc87 Constantinos Venetsanopoulos
def _ExtStorageEnvironment(unique_id, ext_params,
3300 938adc87 Constantinos Venetsanopoulos
                           size=None, grow=None, metadata=None):
3301 376631d1 Constantinos Venetsanopoulos
  """Calculate the environment for an External Storage script.
3302 376631d1 Constantinos Venetsanopoulos

3303 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
3304 376631d1 Constantinos Venetsanopoulos
  @param unique_id: ExtStorage pool and name of the Volume
3305 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
3306 938adc87 Constantinos Venetsanopoulos
  @param ext_params: the EXT parameters
3307 376631d1 Constantinos Venetsanopoulos
  @type size: string
3308 376631d1 Constantinos Venetsanopoulos
  @param size: size of the Volume (in mebibytes)
3309 376631d1 Constantinos Venetsanopoulos
  @type grow: string
3310 376631d1 Constantinos Venetsanopoulos
  @param grow: new size of Volume after grow (in mebibytes)
3311 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
3312 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume
3313 376631d1 Constantinos Venetsanopoulos
  @rtype: dict
3314 376631d1 Constantinos Venetsanopoulos
  @return: dict of environment variables
3315 376631d1 Constantinos Venetsanopoulos

3316 376631d1 Constantinos Venetsanopoulos
  """
3317 376631d1 Constantinos Venetsanopoulos
  vol_name = unique_id[1]
3318 376631d1 Constantinos Venetsanopoulos
3319 376631d1 Constantinos Venetsanopoulos
  result = {}
3320 376631d1 Constantinos Venetsanopoulos
  result["VOL_NAME"] = vol_name
3321 376631d1 Constantinos Venetsanopoulos
3322 938adc87 Constantinos Venetsanopoulos
  # EXT params
3323 938adc87 Constantinos Venetsanopoulos
  for pname, pvalue in ext_params.items():
3324 938adc87 Constantinos Venetsanopoulos
    result["EXTP_%s" % pname.upper()] = str(pvalue)
3325 938adc87 Constantinos Venetsanopoulos
3326 376631d1 Constantinos Venetsanopoulos
  if size is not None:
3327 376631d1 Constantinos Venetsanopoulos
    result["VOL_SIZE"] = size
3328 376631d1 Constantinos Venetsanopoulos
3329 376631d1 Constantinos Venetsanopoulos
  if grow is not None:
3330 376631d1 Constantinos Venetsanopoulos
    result["VOL_NEW_SIZE"] = grow
3331 376631d1 Constantinos Venetsanopoulos
3332 376631d1 Constantinos Venetsanopoulos
  if metadata is not None:
3333 376631d1 Constantinos Venetsanopoulos
    result["VOL_METADATA"] = metadata
3334 376631d1 Constantinos Venetsanopoulos
3335 376631d1 Constantinos Venetsanopoulos
  return result
3336 376631d1 Constantinos Venetsanopoulos
3337 376631d1 Constantinos Venetsanopoulos
3338 376631d1 Constantinos Venetsanopoulos
def _VolumeLogName(kind, es_name, volume):
3339 376631d1 Constantinos Venetsanopoulos
  """Compute the ExtStorage log filename for a given Volume and operation.
3340 376631d1 Constantinos Venetsanopoulos

3341 376631d1 Constantinos Venetsanopoulos
  @type kind: string
3342 376631d1 Constantinos Venetsanopoulos
  @param kind: the operation type (e.g. create, remove etc.)
3343 376631d1 Constantinos Venetsanopoulos
  @type es_name: string
3344 376631d1 Constantinos Venetsanopoulos
  @param es_name: the ExtStorage name
3345 376631d1 Constantinos Venetsanopoulos
  @type volume: string
3346 376631d1 Constantinos Venetsanopoulos
  @param volume: the name of the Volume inside the External Storage
3347 376631d1 Constantinos Venetsanopoulos

3348 376631d1 Constantinos Venetsanopoulos
  """
3349 376631d1 Constantinos Venetsanopoulos
  # Check if the extstorage log dir is a valid dir
3350 376631d1 Constantinos Venetsanopoulos
  if not os.path.isdir(pathutils.LOG_ES_DIR):
3351 376631d1 Constantinos Venetsanopoulos
    _ThrowError("Cannot find log directory: %s", pathutils.LOG_ES_DIR)
3352 376631d1 Constantinos Venetsanopoulos
3353 376631d1 Constantinos Venetsanopoulos
  # TODO: Use tempfile.mkstemp to create unique filename
3354 376631d1 Constantinos Venetsanopoulos
  base = ("%s-%s-%s-%s.log" %
3355 376631d1 Constantinos Venetsanopoulos
          (kind, es_name, volume, utils.TimestampForFilename()))
3356 376631d1 Constantinos Venetsanopoulos
  return utils.PathJoin(pathutils.LOG_ES_DIR, base)
3357 376631d1 Constantinos Venetsanopoulos
3358 376631d1 Constantinos Venetsanopoulos
3359 a8083063 Iustin Pop
DEV_MAP = {
3360 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
3361 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
3362 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
3363 7181fba0 Constantinos Venetsanopoulos
  constants.LD_RBD: RADOSBlockDevice,
3364 376631d1 Constantinos Venetsanopoulos
  constants.LD_EXT: ExtStorageDevice,
3365 a8083063 Iustin Pop
  }
3366 a8083063 Iustin Pop
3367 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
3368 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
3369 cb7c0198 Iustin Pop
3370 a8083063 Iustin Pop
3371 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
3372 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
3373 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
3374 94dcbdb0 Andrea Spadaccini
3375 94dcbdb0 Andrea Spadaccini
3376 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
3377 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
3378 5ff82cc9 René Nussbaumer

3379 5ff82cc9 René Nussbaumer
  """
3380 5ff82cc9 René Nussbaumer
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
3381 5ff82cc9 René Nussbaumer
  if missing:
3382 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
3383 5ff82cc9 René Nussbaumer
                                 missing)
3384 5ff82cc9 René Nussbaumer
3385 5ff82cc9 René Nussbaumer
3386 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
3387 a8083063 Iustin Pop
  """Search for an existing, assembled device.
3388 a8083063 Iustin Pop

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

3392 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
3393 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
3394 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
3395 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
3396 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
3397 94dcbdb0 Andrea Spadaccini

3398 a8083063 Iustin Pop
  """
3399 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3400 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
3401 c7c6606d René Nussbaumer
                                  disk.params)
3402 cb999543 Iustin Pop
  if not device.attached:
3403 a8083063 Iustin Pop
    return None
3404 ecb091e3 Iustin Pop
  return device
3405 a8083063 Iustin Pop
3406 a8083063 Iustin Pop
3407 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
3408 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
3409 a8083063 Iustin Pop

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

3413 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
3414 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
3415 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
3416 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
3417 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
3418 94dcbdb0 Andrea Spadaccini

3419 a8083063 Iustin Pop
  """
3420 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3421 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
3422 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
3423 c7c6606d René Nussbaumer
                                  disk.params)
3424 1063abd1 Iustin Pop
  device.Assemble()
3425 a8083063 Iustin Pop
  return device
3426 a8083063 Iustin Pop
3427 a8083063 Iustin Pop
3428 ee1478e5 Bernardo Dal Seno
def Create(disk, children, excl_stor):
3429 a8083063 Iustin Pop
  """Create a device.
3430 a8083063 Iustin Pop

3431 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
3432 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
3433 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
3434 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
3435 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
3436 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
3437 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
3438 94dcbdb0 Andrea Spadaccini

3439 a8083063 Iustin Pop
  """
3440 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3441 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
3442 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
3443 ee1478e5 Bernardo Dal Seno
                                         disk.params, excl_stor)
3444 a8083063 Iustin Pop
  return device