Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 11e90588

History | View | Annotate | Download (110 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 f24d3d3b Helga Velroyen
                                      " storage. A possible fix might be to add"
188 f24d3d3b Helga Velroyen
                                      " it to /etc/ganeti/file-storage-paths"
189 f24d3d3b Helga Velroyen
                                      " on all nodes." % path)
190 fbdac0d9 Michael Hanselmann
191 fbdac0d9 Michael Hanselmann
192 23e3c9b7 Michael Hanselmann
def _LoadAllowedFileStoragePaths(filename):
193 fbdac0d9 Michael Hanselmann
  """Loads file containing allowed file storage paths.
194 fbdac0d9 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

330 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
331 f3e513ad Iustin Pop

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

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

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

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

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

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

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

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

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

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

380 f2f57b6e Andrea Spadaccini
    @param pause: Whether to pause or resume
381 a3fffcc6 René Nussbaumer

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

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

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

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

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

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

408 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
409 c41eea6e Iustin Pop

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

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

426 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
427 96acbc09 Michael Hanselmann

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

468 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
469 a0c3fea1 Michael Hanselmann

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

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

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

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

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

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

523 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
524 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

936 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
937 c41eea6e Iustin Pop

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

955 a8083063 Iustin Pop
    This is a no-op for the LV device type.
956 a8083063 Iustin Pop

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

963 a8083063 Iustin Pop
    This is a no-op for the LV device type.
964 a8083063 Iustin Pop

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

971 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
972 800ac399 Iustin Pop

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

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

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

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

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

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

1165 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
1166 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
1167 0f7f32d9 Iustin Pop

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

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

1203 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
1204 c41eea6e Iustin Pop
        for that minor
1205 a8083063 Iustin Pop

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

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

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

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

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

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

1305 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1306 a8083063 Iustin Pop

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

1320 3bc145d8 Bernardo Dal Seno
    This currently only checks the size, which must be around
1321 abdf0113 Iustin Pop
    128MiB.
1322 a8083063 Iustin Pop

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

1347 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1348 a8083063 Iustin Pop

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

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

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

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

1410 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1411 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1657 8a69b3a8 Andrea Spadaccini
    If the desired option is unsupported, raises errors.BlockDeviceError.
1658 8a69b3a8 Andrea Spadaccini

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

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

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

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

1846 7d585316 Iustin Pop
    This is the low-level implementation.
1847 7d585316 Iustin Pop

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

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

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

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

1921 a3fffcc6 René Nussbaumer
    @param pause: Wether to pause or resume
1922 a3fffcc6 René Nussbaumer
    @return: the success of the operation
1923 a3fffcc6 René Nussbaumer

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

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

1956 a2cfdea2 Iustin Pop

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

1961 0834c866 Iustin Pop

1962 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1963 0834c866 Iustin Pop
    network not connected or local disk missing.
1964 0834c866 Iustin Pop

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

1968 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1969 c41eea6e Iustin Pop

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

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

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

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

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

2028 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
2029 cf8df3f3 Iustin Pop

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

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

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

2092 cf8df3f3 Iustin Pop
    Args:
2093 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
2094 cf8df3f3 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

2266 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2267 f3e513ad Iustin Pop

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

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

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

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

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

2307 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2308 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2309 a2cfdea2 Iustin Pop

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

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

2357 b05b5ec8 Santi Raffa
  This class represents a file storage backend device.
2358 6f695a2e Manuel Franceschini

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

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

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

2381 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
2382 6f695a2e Manuel Franceschini

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

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

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

2399 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2400 6f695a2e Manuel Franceschini

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

2407 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2408 6f695a2e Manuel Franceschini

2409 6f695a2e Manuel Franceschini
    """
2410 6f695a2e Manuel Franceschini
    pass
2411 6f695a2e Manuel Franceschini
2412 6f695a2e Manuel Franceschini
  def Remove(self):
2413 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
2414 6f695a2e Manuel Franceschini

2415 c41eea6e Iustin Pop
    @rtype: boolean
2416 c41eea6e Iustin Pop
    @return: True if the removal was successful
2417 6f695a2e Manuel Franceschini

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

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

2435 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2436 bbe4cc16 Iustin Pop

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

2458 6f695a2e Manuel Franceschini
    Check if this file already exists.
2459 6f695a2e Manuel Franceschini

2460 c41eea6e Iustin Pop
    @rtype: boolean
2461 c41eea6e Iustin Pop
    @return: True if file exists
2462 6f695a2e Manuel Franceschini

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

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

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

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

2486 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2487 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2488 6f695a2e Manuel Franceschini

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

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

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

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

2526 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2527 b6135bbc Apollon Oikonomopoulos

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

2552 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2553 b6135bbc Apollon Oikonomopoulos

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

2563 b6135bbc Apollon Oikonomopoulos
    This is a noop
2564 b6135bbc Apollon Oikonomopoulos

2565 b6135bbc Apollon Oikonomopoulos
    """
2566 b6135bbc Apollon Oikonomopoulos
    pass
2567 b6135bbc Apollon Oikonomopoulos
2568 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
2569 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
2570 b6135bbc Apollon Oikonomopoulos

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

2577 b6135bbc Apollon Oikonomopoulos

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

2599 b6135bbc Apollon Oikonomopoulos
    """
2600 b6135bbc Apollon Oikonomopoulos
    pass
2601 b6135bbc Apollon Oikonomopoulos
2602 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
2603 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
2604 b6135bbc Apollon Oikonomopoulos

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

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

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

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

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

2634 7181fba0 Constantinos Venetsanopoulos
  """
2635 7181fba0 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
2636 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
2637 7181fba0 Constantinos Venetsanopoulos

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

2652 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
2653 7181fba0 Constantinos Venetsanopoulos

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

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

2698 7181fba0 Constantinos Venetsanopoulos
    """
2699 7181fba0 Constantinos Venetsanopoulos
    pass
2700 7181fba0 Constantinos Venetsanopoulos
2701 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
2702 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
2703 7181fba0 Constantinos Venetsanopoulos

2704 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
2705 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
2706 7181fba0 Constantinos Venetsanopoulos

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

2732 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
2733 7181fba0 Constantinos Venetsanopoulos

2734 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
2735 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
2736 7181fba0 Constantinos Venetsanopoulos

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

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

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

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

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

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

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

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

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

2897 7181fba0 Constantinos Venetsanopoulos
    """
2898 7181fba0 Constantinos Venetsanopoulos
    pass
2899 7181fba0 Constantinos Venetsanopoulos
2900 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
2901 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
2902 7181fba0 Constantinos Venetsanopoulos

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

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

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

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

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

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

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

2984 376631d1 Constantinos Venetsanopoulos
  This class implements the External Storage Interface, which means
2985 376631d1 Constantinos Venetsanopoulos
  handling of the externally provided block devices.
2986 376631d1 Constantinos Venetsanopoulos

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

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

3006 376631d1 Constantinos Venetsanopoulos
    Provision a new volume using an extstorage provider, which will
3007 376631d1 Constantinos Venetsanopoulos
    then be mapped to a block device.
3008 376631d1 Constantinos Venetsanopoulos

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

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

3043 376631d1 Constantinos Venetsanopoulos
    """
3044 376631d1 Constantinos Venetsanopoulos
    pass
3045 376631d1 Constantinos Venetsanopoulos
3046 376631d1 Constantinos Venetsanopoulos
  def Attach(self):
3047 376631d1 Constantinos Venetsanopoulos
    """Attach to an existing extstorage device.
3048 376631d1 Constantinos Venetsanopoulos

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

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

3079 376631d1 Constantinos Venetsanopoulos
    """
3080 376631d1 Constantinos Venetsanopoulos
    pass
3081 376631d1 Constantinos Venetsanopoulos
3082 376631d1 Constantinos Venetsanopoulos
  def Shutdown(self):
3083 376631d1 Constantinos Venetsanopoulos
    """Shutdown the device.
3084 376631d1 Constantinos Venetsanopoulos

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

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

3107 376631d1 Constantinos Venetsanopoulos
    """
3108 376631d1 Constantinos Venetsanopoulos
    pass
3109 376631d1 Constantinos Venetsanopoulos
3110 376631d1 Constantinos Venetsanopoulos
  def Grow(self, amount, dryrun, backingstore):
3111 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
3112 376631d1 Constantinos Venetsanopoulos

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

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

3139 376631d1 Constantinos Venetsanopoulos
    """
3140 376631d1 Constantinos Venetsanopoulos
    # Replace invalid characters
3141 376631d1 Constantinos Venetsanopoulos
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
3142 376631d1 Constantinos Venetsanopoulos
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
3143 376631d1 Constantinos Venetsanopoulos
3144 376631d1 Constantinos Venetsanopoulos
    # Only up to 128 characters are allowed
3145 376631d1 Constantinos Venetsanopoulos
    text = text[:128]
3146 376631d1 Constantinos Venetsanopoulos
3147 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's setinfo script,
3148 376631d1 Constantinos Venetsanopoulos
    # to set metadata for an existing Volume inside the External Storage
3149 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
3150 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, metadata=text)
3151 376631d1 Constantinos Venetsanopoulos
3152 06c2fb4a Dimitris Aragiorgis
  def Snapshot(self, snapshot_name):
3153 06c2fb4a Dimitris Aragiorgis
    """Take a snapshot of the block device.
3154 06c2fb4a Dimitris Aragiorgis

3155 06c2fb4a Dimitris Aragiorgis
    """
3156 06c2fb4a Dimitris Aragiorgis
    # Call the External Storage's setinfo script,
3157 06c2fb4a Dimitris Aragiorgis
    # to set metadata for an existing Volume inside the External Storage
3158 06c2fb4a Dimitris Aragiorgis
    _ExtStorageAction(constants.ES_ACTION_SNAPSHOT, self.unique_id,
3159 06c2fb4a Dimitris Aragiorgis
                      self.ext_params, snapshot_name=snapshot_name)
3160 06c2fb4a Dimitris Aragiorgis
3161 376631d1 Constantinos Venetsanopoulos
3162 938adc87 Constantinos Venetsanopoulos
def _ExtStorageAction(action, unique_id, ext_params,
3163 06c2fb4a Dimitris Aragiorgis
                      size=None, grow=None, metadata=None,
3164 06c2fb4a Dimitris Aragiorgis
                      snapshot_name=None):
3165 376631d1 Constantinos Venetsanopoulos
  """Take an External Storage action.
3166 376631d1 Constantinos Venetsanopoulos

3167 376631d1 Constantinos Venetsanopoulos
  Take an External Storage action concerning or affecting
3168 376631d1 Constantinos Venetsanopoulos
  a specific Volume inside the External Storage.
3169 376631d1 Constantinos Venetsanopoulos

3170 376631d1 Constantinos Venetsanopoulos
  @type action: string
3171 376631d1 Constantinos Venetsanopoulos
  @param action: which action to perform. One of:
3172 376631d1 Constantinos Venetsanopoulos
                 create / remove / grow / attach / detach
3173 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
3174 376631d1 Constantinos Venetsanopoulos
  @param unique_id: a tuple containing the type of ExtStorage (driver)
3175 376631d1 Constantinos Venetsanopoulos
                    and the Volume name
3176 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
3177 938adc87 Constantinos Venetsanopoulos
  @param ext_params: ExtStorage parameters
3178 376631d1 Constantinos Venetsanopoulos
  @type size: integer
3179 376631d1 Constantinos Venetsanopoulos
  @param size: the size of the Volume in mebibytes
3180 376631d1 Constantinos Venetsanopoulos
  @type grow: integer
3181 376631d1 Constantinos Venetsanopoulos
  @param grow: the new size in mebibytes (after grow)
3182 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
3183 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume, for use by the provider
3184 376631d1 Constantinos Venetsanopoulos
  @rtype: None or a block device path (during attach)
3185 376631d1 Constantinos Venetsanopoulos

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

3242 376631d1 Constantinos Venetsanopoulos
  This function will return an ExtStorage instance
3243 376631d1 Constantinos Venetsanopoulos
  if the given name is a valid ExtStorage name.
3244 376631d1 Constantinos Venetsanopoulos

3245 376631d1 Constantinos Venetsanopoulos
  @type base_dir: string
3246 376631d1 Constantinos Venetsanopoulos
  @keyword base_dir: Base directory containing ExtStorage installations.
3247 376631d1 Constantinos Venetsanopoulos
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
3248 376631d1 Constantinos Venetsanopoulos
  @rtype: tuple
3249 376631d1 Constantinos Venetsanopoulos
  @return: True and the ExtStorage instance if we find a valid one, or
3250 376631d1 Constantinos Venetsanopoulos
      False and the diagnose message on error
3251 376631d1 Constantinos Venetsanopoulos

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

3318 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
3319 376631d1 Constantinos Venetsanopoulos
  @param unique_id: ExtStorage pool and name of the Volume
3320 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
3321 938adc87 Constantinos Venetsanopoulos
  @param ext_params: the EXT parameters
3322 376631d1 Constantinos Venetsanopoulos
  @type size: string
3323 376631d1 Constantinos Venetsanopoulos
  @param size: size of the Volume (in mebibytes)
3324 376631d1 Constantinos Venetsanopoulos
  @type grow: string
3325 376631d1 Constantinos Venetsanopoulos
  @param grow: new size of Volume after grow (in mebibytes)
3326 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
3327 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume
3328 376631d1 Constantinos Venetsanopoulos
  @rtype: dict
3329 376631d1 Constantinos Venetsanopoulos
  @return: dict of environment variables
3330 376631d1 Constantinos Venetsanopoulos

3331 376631d1 Constantinos Venetsanopoulos
  """
3332 376631d1 Constantinos Venetsanopoulos
  vol_name = unique_id[1]
3333 376631d1 Constantinos Venetsanopoulos
3334 376631d1 Constantinos Venetsanopoulos
  result = {}
3335 376631d1 Constantinos Venetsanopoulos
  result["VOL_NAME"] = vol_name
3336 376631d1 Constantinos Venetsanopoulos
3337 938adc87 Constantinos Venetsanopoulos
  # EXT params
3338 938adc87 Constantinos Venetsanopoulos
  for pname, pvalue in ext_params.items():
3339 938adc87 Constantinos Venetsanopoulos
    result["EXTP_%s" % pname.upper()] = str(pvalue)
3340 938adc87 Constantinos Venetsanopoulos
3341 376631d1 Constantinos Venetsanopoulos
  if size is not None:
3342 376631d1 Constantinos Venetsanopoulos
    result["VOL_SIZE"] = size
3343 376631d1 Constantinos Venetsanopoulos
3344 376631d1 Constantinos Venetsanopoulos
  if grow is not None:
3345 376631d1 Constantinos Venetsanopoulos
    result["VOL_NEW_SIZE"] = grow
3346 376631d1 Constantinos Venetsanopoulos
3347 376631d1 Constantinos Venetsanopoulos
  if metadata is not None:
3348 376631d1 Constantinos Venetsanopoulos
    result["VOL_METADATA"] = metadata
3349 376631d1 Constantinos Venetsanopoulos
3350 06c2fb4a Dimitris Aragiorgis
  if snapshot_name is not None:
3351 06c2fb4a Dimitris Aragiorgis
    result["VOL_SNAPSHOT_NAME"] = snapshot_name
3352 06c2fb4a Dimitris Aragiorgis
3353 376631d1 Constantinos Venetsanopoulos
  return result
3354 376631d1 Constantinos Venetsanopoulos
3355 376631d1 Constantinos Venetsanopoulos
3356 376631d1 Constantinos Venetsanopoulos
def _VolumeLogName(kind, es_name, volume):
3357 376631d1 Constantinos Venetsanopoulos
  """Compute the ExtStorage log filename for a given Volume and operation.
3358 376631d1 Constantinos Venetsanopoulos

3359 376631d1 Constantinos Venetsanopoulos
  @type kind: string
3360 376631d1 Constantinos Venetsanopoulos
  @param kind: the operation type (e.g. create, remove etc.)
3361 376631d1 Constantinos Venetsanopoulos
  @type es_name: string
3362 376631d1 Constantinos Venetsanopoulos
  @param es_name: the ExtStorage name
3363 376631d1 Constantinos Venetsanopoulos
  @type volume: string
3364 376631d1 Constantinos Venetsanopoulos
  @param volume: the name of the Volume inside the External Storage
3365 376631d1 Constantinos Venetsanopoulos

3366 376631d1 Constantinos Venetsanopoulos
  """
3367 376631d1 Constantinos Venetsanopoulos
  # Check if the extstorage log dir is a valid dir
3368 376631d1 Constantinos Venetsanopoulos
  if not os.path.isdir(pathutils.LOG_ES_DIR):
3369 376631d1 Constantinos Venetsanopoulos
    _ThrowError("Cannot find log directory: %s", pathutils.LOG_ES_DIR)
3370 376631d1 Constantinos Venetsanopoulos
3371 376631d1 Constantinos Venetsanopoulos
  # TODO: Use tempfile.mkstemp to create unique filename
3372 376631d1 Constantinos Venetsanopoulos
  base = ("%s-%s-%s-%s.log" %
3373 376631d1 Constantinos Venetsanopoulos
          (kind, es_name, volume, utils.TimestampForFilename()))
3374 376631d1 Constantinos Venetsanopoulos
  return utils.PathJoin(pathutils.LOG_ES_DIR, base)
3375 376631d1 Constantinos Venetsanopoulos
3376 376631d1 Constantinos Venetsanopoulos
3377 a8083063 Iustin Pop
DEV_MAP = {
3378 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
3379 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
3380 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
3381 7181fba0 Constantinos Venetsanopoulos
  constants.LD_RBD: RADOSBlockDevice,
3382 376631d1 Constantinos Venetsanopoulos
  constants.LD_EXT: ExtStorageDevice,
3383 a8083063 Iustin Pop
  }
3384 a8083063 Iustin Pop
3385 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
3386 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
3387 cb7c0198 Iustin Pop
3388 a8083063 Iustin Pop
3389 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
3390 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
3391 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
3392 94dcbdb0 Andrea Spadaccini
3393 94dcbdb0 Andrea Spadaccini
3394 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
3395 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
3396 5ff82cc9 René Nussbaumer

3397 5ff82cc9 René Nussbaumer
  """
3398 5ff82cc9 René Nussbaumer
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
3399 5ff82cc9 René Nussbaumer
  if missing:
3400 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
3401 5ff82cc9 René Nussbaumer
                                 missing)
3402 5ff82cc9 René Nussbaumer
3403 5ff82cc9 René Nussbaumer
3404 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
3405 a8083063 Iustin Pop
  """Search for an existing, assembled device.
3406 a8083063 Iustin Pop

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

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

3416 a8083063 Iustin Pop
  """
3417 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3418 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
3419 c7c6606d René Nussbaumer
                                  disk.params)
3420 cb999543 Iustin Pop
  if not device.attached:
3421 a8083063 Iustin Pop
    return None
3422 ecb091e3 Iustin Pop
  return device
3423 a8083063 Iustin Pop
3424 a8083063 Iustin Pop
3425 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
3426 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
3427 a8083063 Iustin Pop

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

3431 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
3432 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
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 94dcbdb0 Andrea Spadaccini

3437 a8083063 Iustin Pop
  """
3438 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3439 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
3440 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
3441 c7c6606d René Nussbaumer
                                  disk.params)
3442 1063abd1 Iustin Pop
  device.Assemble()
3443 a8083063 Iustin Pop
  return device
3444 a8083063 Iustin Pop
3445 a8083063 Iustin Pop
3446 ee1478e5 Bernardo Dal Seno
def Create(disk, children, excl_stor):
3447 a8083063 Iustin Pop
  """Create a device.
3448 a8083063 Iustin Pop

3449 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
3450 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
3451 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
3452 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
3453 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
3454 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
3455 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
3456 94dcbdb0 Andrea Spadaccini

3457 a8083063 Iustin Pop
  """
3458 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3459 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
3460 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
3461 ee1478e5 Bernardo Dal Seno
                                         disk.params, excl_stor)
3462 a8083063 Iustin Pop
  return device