Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 7af7da68

History | View | Annotate | Download (109.3 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

173 fbdac0d9 Michael Hanselmann
  """
174 fbdac0d9 Michael Hanselmann
  if not os.path.isabs(path):
175 fbdac0d9 Michael Hanselmann
    raise errors.FileStoragePathError("File storage path must be absolute,"
176 fbdac0d9 Michael Hanselmann
                                      " got '%s'" % path)
177 fbdac0d9 Michael Hanselmann
178 fbdac0d9 Michael Hanselmann
  for i in allowed:
179 fbdac0d9 Michael Hanselmann
    if not os.path.isabs(i):
180 fbdac0d9 Michael Hanselmann
      logging.info("Ignoring relative path '%s' for file storage", i)
181 fbdac0d9 Michael Hanselmann
      continue
182 fbdac0d9 Michael Hanselmann
183 fbdac0d9 Michael Hanselmann
    if utils.IsBelowDir(i, path):
184 fbdac0d9 Michael Hanselmann
      break
185 fbdac0d9 Michael Hanselmann
  else:
186 fbdac0d9 Michael Hanselmann
    raise errors.FileStoragePathError("Path '%s' is not acceptable for file"
187 f24d3d3b Helga Velroyen
                                      " storage. A possible fix might be to add"
188 f24d3d3b Helga Velroyen
                                      " it to /etc/ganeti/file-storage-paths"
189 f24d3d3b Helga Velroyen
                                      " on all nodes." % path)
190 fbdac0d9 Michael Hanselmann
191 fbdac0d9 Michael Hanselmann
192 23e3c9b7 Michael Hanselmann
def _LoadAllowedFileStoragePaths(filename):
193 fbdac0d9 Michael Hanselmann
  """Loads file containing allowed file storage paths.
194 fbdac0d9 Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1955 a2cfdea2 Iustin Pop

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

1960 0834c866 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2356 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
2357 6f695a2e Manuel Franceschini

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2576 b6135bbc Apollon Oikonomopoulos

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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