Statistics
| Branch: | Tag: | Revision:

root / lib / block / bdev.py @ ce9283c1

History | View | Annotate | Download (109.1 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1300 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1301 a8083063 Iustin Pop

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

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

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

1342 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1343 a8083063 Iustin Pop

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

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

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

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

1405 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1406 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1652 8a69b3a8 Andrea Spadaccini
    If the desired option is unsupported, raises errors.BlockDeviceError.
1653 8a69b3a8 Andrea Spadaccini

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

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

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

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

1841 7d585316 Iustin Pop
    This is the low-level implementation.
1842 7d585316 Iustin Pop

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

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

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

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

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

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

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

1951 a2cfdea2 Iustin Pop

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

1956 0834c866 Iustin Pop

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

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

1963 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1964 c41eea6e Iustin Pop

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

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

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

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

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

2023 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
2024 cf8df3f3 Iustin Pop

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

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

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

2087 cf8df3f3 Iustin Pop
    Args:
2088 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
2089 cf8df3f3 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

2261 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2262 f3e513ad Iustin Pop

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

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

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

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

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

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

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

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

2352 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
2353 6f695a2e Manuel Franceschini

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

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

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

2376 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
2377 6f695a2e Manuel Franceschini

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

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

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

2394 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2395 6f695a2e Manuel Franceschini

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

2402 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2403 6f695a2e Manuel Franceschini

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

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

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

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

2430 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2431 bbe4cc16 Iustin Pop

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

2453 6f695a2e Manuel Franceschini
    Check if this file already exists.
2454 6f695a2e Manuel Franceschini

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

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

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

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

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

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

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

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

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

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

2521 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2522 b6135bbc Apollon Oikonomopoulos

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

2547 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2548 b6135bbc Apollon Oikonomopoulos

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

2558 b6135bbc Apollon Oikonomopoulos
    This is a noop
2559 b6135bbc Apollon Oikonomopoulos

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

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

2572 b6135bbc Apollon Oikonomopoulos

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

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

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

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

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

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

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

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

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

2647 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
2648 7181fba0 Constantinos Venetsanopoulos

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

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

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

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

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

2727 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
2728 7181fba0 Constantinos Venetsanopoulos

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3429 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
3430 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
3431 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
3432 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
3433 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
3434 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
3435 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
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].Create(disk.physical_id, children, disk.size,
3441 ee1478e5 Bernardo Dal Seno
                                         disk.params, excl_stor)
3442 a8083063 Iustin Pop
  return device