Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 595149d5

History | View | Annotate | Download (107.2 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

749 197478f2 René Nussbaumer
    @rtype: list
750 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
751 673cd9c4 René Nussbaumer
             MiB
752 197478f2 René Nussbaumer

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

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

786 6136f8f0 Iustin Pop
    """
787 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
788 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
789 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
790 6136f8f0 Iustin Pop
      _ThrowError("Invalid LVM name '%s'", name)
791 6136f8f0 Iustin Pop
792 a8083063 Iustin Pop
  def Remove(self):
793 a8083063 Iustin Pop
    """Remove this logical volume.
794 a8083063 Iustin Pop

795 a8083063 Iustin Pop
    """
796 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
797 a8083063 Iustin Pop
      # the LV does not exist
798 0c6c04ec Iustin Pop
      return
799 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
800 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
801 a8083063 Iustin Pop
    if result.failed:
802 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
803 a8083063 Iustin Pop
804 f3e513ad Iustin Pop
  def Rename(self, new_id):
805 f3e513ad Iustin Pop
    """Rename this logical volume.
806 f3e513ad Iustin Pop

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

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

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

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

893 a8083063 Iustin Pop
    """
894 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
895 5574047a Iustin Pop
    if result.failed:
896 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
897 a8083063 Iustin Pop
898 a8083063 Iustin Pop
  def Shutdown(self):
899 a8083063 Iustin Pop
    """Shutdown the device.
900 a8083063 Iustin Pop

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

904 a8083063 Iustin Pop
    """
905 746f7476 Iustin Pop
    pass
906 a8083063 Iustin Pop
907 9db6dbce Iustin Pop
  def GetSyncStatus(self):
908 9db6dbce Iustin Pop
    """Returns the sync status of the device.
909 9db6dbce Iustin Pop

910 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
911 9db6dbce Iustin Pop
    status of the mirror.
912 9db6dbce Iustin Pop

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

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

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

926 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
927 c41eea6e Iustin Pop

928 9db6dbce Iustin Pop
    """
929 f208978a Michael Hanselmann
    if self._degraded:
930 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
931 f208978a Michael Hanselmann
    else:
932 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
933 f208978a Michael Hanselmann
934 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
935 96acbc09 Michael Hanselmann
                                  major=self.major,
936 96acbc09 Michael Hanselmann
                                  minor=self.minor,
937 96acbc09 Michael Hanselmann
                                  sync_percent=None,
938 96acbc09 Michael Hanselmann
                                  estimated_time=None,
939 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
940 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
941 9db6dbce Iustin Pop
942 a8083063 Iustin Pop
  def Open(self, force=False):
943 a8083063 Iustin Pop
    """Make the device ready for I/O.
944 a8083063 Iustin Pop

945 a8083063 Iustin Pop
    This is a no-op for the LV device type.
946 a8083063 Iustin Pop

947 a8083063 Iustin Pop
    """
948 fdbd668d Iustin Pop
    pass
949 a8083063 Iustin Pop
950 a8083063 Iustin Pop
  def Close(self):
951 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
952 a8083063 Iustin Pop

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

955 a8083063 Iustin Pop
    """
956 fdbd668d Iustin Pop
    pass
957 a8083063 Iustin Pop
958 a8083063 Iustin Pop
  def Snapshot(self, size):
959 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
960 a8083063 Iustin Pop

961 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
962 800ac399 Iustin Pop

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

986 a1556cfa Iustin Pop
    """
987 a1556cfa Iustin Pop
    result = utils.RunCmd(["lvs", "-o", "tags", "--noheadings", "--nosuffix",
988 a1556cfa Iustin Pop
                           self.dev_path])
989 a1556cfa Iustin Pop
    _CheckResult(result)
990 a1556cfa Iustin Pop
991 a1556cfa Iustin Pop
    raw_tags = result.stdout.strip()
992 a1556cfa Iustin Pop
    if raw_tags:
993 a1556cfa Iustin Pop
      for tag in raw_tags.split(","):
994 a1556cfa Iustin Pop
        _CheckResult(utils.RunCmd(["lvchange", "--deltag",
995 a1556cfa Iustin Pop
                                   tag.strip(), self.dev_path]))
996 a1556cfa Iustin Pop
997 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
998 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
999 a0c3fea1 Michael Hanselmann

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

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

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

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

1152 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
1153 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
1154 0f7f32d9 Iustin Pop

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

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

1190 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
1191 c41eea6e Iustin Pop
        for that minor
1192 a8083063 Iustin Pop

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

1217 abdf0113 Iustin Pop
    This will return a dict with keys:
1218 c41eea6e Iustin Pop
      - k_major
1219 c41eea6e Iustin Pop
      - k_minor
1220 c41eea6e Iustin Pop
      - k_point
1221 c41eea6e Iustin Pop
      - api
1222 c41eea6e Iustin Pop
      - proto
1223 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
1224 a8083063 Iustin Pop

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

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

1266 a8083063 Iustin Pop
    """
1267 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
1268 a8083063 Iustin Pop
1269 abdf0113 Iustin Pop
  @classmethod
1270 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
1271 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
1272 a8083063 Iustin Pop

1273 a8083063 Iustin Pop
    """
1274 abdf0113 Iustin Pop
    data = cls._GetProcData()
1275 a8083063 Iustin Pop
1276 abdf0113 Iustin Pop
    used_devs = {}
1277 abdf0113 Iustin Pop
    for line in data:
1278 9122e60a Iustin Pop
      match = cls._VALID_LINE_RE.match(line)
1279 abdf0113 Iustin Pop
      if not match:
1280 abdf0113 Iustin Pop
        continue
1281 abdf0113 Iustin Pop
      minor = int(match.group(1))
1282 abdf0113 Iustin Pop
      state = match.group(2)
1283 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1284 abdf0113 Iustin Pop
        continue
1285 abdf0113 Iustin Pop
      used_devs[minor] = state, line
1286 a8083063 Iustin Pop
1287 abdf0113 Iustin Pop
    return used_devs
1288 a8083063 Iustin Pop
1289 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
1290 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
1291 0834c866 Iustin Pop

1292 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
1293 a8083063 Iustin Pop

1294 a8083063 Iustin Pop
    """
1295 abdf0113 Iustin Pop
    if minor is None:
1296 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
1297 cb999543 Iustin Pop
      self.attached = False
1298 a8083063 Iustin Pop
    else:
1299 abdf0113 Iustin Pop
      self.minor = minor
1300 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
1301 cb999543 Iustin Pop
      self.attached = True
1302 a8083063 Iustin Pop
1303 a8083063 Iustin Pop
  @staticmethod
1304 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
1305 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
1306 a8083063 Iustin Pop

1307 3bc145d8 Bernardo Dal Seno
    This currently only checks the size, which must be around
1308 abdf0113 Iustin Pop
    128MiB.
1309 a8083063 Iustin Pop

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

1334 abdf0113 Iustin Pop
    This is not supported for drbd devices.
1335 a8083063 Iustin Pop

1336 a8083063 Iustin Pop
    """
1337 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1338 a8083063 Iustin Pop
1339 f3e513ad Iustin Pop
1340 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1341 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1342 a2cfdea2 Iustin Pop

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

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

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

1397 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1398 a2cfdea2 Iustin Pop

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

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

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

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

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

1504 a2cfdea2 Iustin Pop
    """
1505 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1506 a2cfdea2 Iustin Pop
    if result.failed:
1507 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1508 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1509 3840729d Iustin Pop
      return None
1510 3840729d Iustin Pop
    return result.stdout
1511 3840729d Iustin Pop
1512 3840729d Iustin Pop
  @classmethod
1513 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1514 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1515 3840729d Iustin Pop

1516 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1517 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1518 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1519 3840729d Iustin Pop
    by _GetShowData.
1520 3840729d Iustin Pop

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

1555 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1556 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1557 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1558 a2cfdea2 Iustin Pop
    device.
1559 a2cfdea2 Iustin Pop

1560 a2cfdea2 Iustin Pop
    """
1561 b00b95dd Iustin Pop
    if self._children:
1562 b00b95dd Iustin Pop
      backend, meta = self._children
1563 b00b95dd Iustin Pop
    else:
1564 b00b95dd Iustin Pop
      backend = meta = None
1565 b00b95dd Iustin Pop
1566 a2cfdea2 Iustin Pop
    if backend is not None:
1567 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1568 a2cfdea2 Iustin Pop
    else:
1569 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1570 b00b95dd Iustin Pop
1571 a2cfdea2 Iustin Pop
    if meta is not None:
1572 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1573 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1574 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1575 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1576 a2cfdea2 Iustin Pop
    else:
1577 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1578 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1579 a2cfdea2 Iustin Pop
    return retval
1580 a2cfdea2 Iustin Pop
1581 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1582 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1583 a2cfdea2 Iustin Pop

1584 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1585 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1586 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1587 a2cfdea2 Iustin Pop
    device.
1588 a2cfdea2 Iustin Pop

1589 a2cfdea2 Iustin Pop
    """
1590 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1591 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1592 a2cfdea2 Iustin Pop
      return True
1593 a2cfdea2 Iustin Pop
1594 a2cfdea2 Iustin Pop
    if self._lhost is None:
1595 a2cfdea2 Iustin Pop
      return False
1596 a2cfdea2 Iustin Pop
1597 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1598 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1599 a2cfdea2 Iustin Pop
      return False
1600 a2cfdea2 Iustin Pop
1601 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1602 a2cfdea2 Iustin Pop
    retval = (retval and
1603 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1604 a2cfdea2 Iustin Pop
    return retval
1605 a2cfdea2 Iustin Pop
1606 8a69b3a8 Andrea Spadaccini
  def _AssembleLocal(self, minor, backend, meta, size):
1607 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1608 a2cfdea2 Iustin Pop

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

1640 8a69b3a8 Andrea Spadaccini
    Returns a list of the disk barrier parameters as requested via the
1641 8a69b3a8 Andrea Spadaccini
    disabled_barriers and disable_meta_flush arguments, and according to the
1642 8a69b3a8 Andrea Spadaccini
    supported ones in the DRBD version vmaj.vmin.vrel
1643 8a69b3a8 Andrea Spadaccini

1644 8a69b3a8 Andrea Spadaccini
    If the desired option is unsupported, raises errors.BlockDeviceError.
1645 8a69b3a8 Andrea Spadaccini

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

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

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

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

1833 7d585316 Iustin Pop
    This is the low-level implementation.
1834 7d585316 Iustin Pop

1835 7d585316 Iustin Pop
    @type minor: int
1836 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1837 f2f57b6e Andrea Spadaccini
    @type params: dict
1838 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1839 8584e922 Andrea Spadaccini
    @rtype: list
1840 8584e922 Andrea Spadaccini
    @return: a list of error messages
1841 7d585316 Iustin Pop

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

1889 f2f57b6e Andrea Spadaccini
    @type params: dict
1890 f2f57b6e Andrea Spadaccini
    @param params: LD level disk parameters related to the synchronization
1891 8584e922 Andrea Spadaccini
    @rtype: list
1892 8584e922 Andrea Spadaccini
    @return: a list of error messages, emitted both by the current node and by
1893 8584e922 Andrea Spadaccini
    children. An empty list means no errors
1894 7d585316 Iustin Pop

1895 7d585316 Iustin Pop
    """
1896 7d585316 Iustin Pop
    if self.minor is None:
1897 8584e922 Andrea Spadaccini
      err = "Not attached during SetSyncParams"
1898 8584e922 Andrea Spadaccini
      logging.info(err)
1899 8584e922 Andrea Spadaccini
      return [err]
1900 8584e922 Andrea Spadaccini
1901 f2f57b6e Andrea Spadaccini
    children_result = super(DRBD8, self).SetSyncParams(params)
1902 8584e922 Andrea Spadaccini
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
1903 8584e922 Andrea Spadaccini
    return children_result
1904 a2cfdea2 Iustin Pop
1905 a3fffcc6 René Nussbaumer
  def PauseResumeSync(self, pause):
1906 a3fffcc6 René Nussbaumer
    """Pauses or resumes the sync of a DRBD device.
1907 a3fffcc6 René Nussbaumer

1908 a3fffcc6 René Nussbaumer
    @param pause: Wether to pause or resume
1909 a3fffcc6 René Nussbaumer
    @return: the success of the operation
1910 a3fffcc6 René Nussbaumer

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

1932 6b90c22e Iustin Pop
    """
1933 6b90c22e Iustin Pop
    if self.minor is None:
1934 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1935 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1936 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1937 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1938 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1939 6b90c22e Iustin Pop
1940 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1941 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1942 a2cfdea2 Iustin Pop

1943 a2cfdea2 Iustin Pop

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

1948 0834c866 Iustin Pop

1949 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1950 0834c866 Iustin Pop
    network not connected or local disk missing.
1951 0834c866 Iustin Pop

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

1955 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
1956 c41eea6e Iustin Pop

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

1982 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1983 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1984 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1985 f860ff4e Guido Trotter
    is mandatory.
1986 a2cfdea2 Iustin Pop

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

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

2004 a2cfdea2 Iustin Pop
    """
2005 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2006 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
2007 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
2008 a2cfdea2 Iustin Pop
    if result.failed:
2009 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
2010 82463074 Iustin Pop
                  self.minor, result.output)
2011 a2cfdea2 Iustin Pop
2012 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
2013 cf8df3f3 Iustin Pop
    """Removes network configuration.
2014 cf8df3f3 Iustin Pop

2015 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
2016 cf8df3f3 Iustin Pop

2017 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
2018 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
2019 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
2020 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
2021 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
2022 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
2023 cf8df3f3 Iustin Pop
    exception.
2024 cf8df3f3 Iustin Pop

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

2075 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
2076 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
2077 cf8df3f3 Iustin Pop
    but have valid network configuration data.
2078 cf8df3f3 Iustin Pop

2079 cf8df3f3 Iustin Pop
    Args:
2080 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
2081 cf8df3f3 Iustin Pop

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

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

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

2109 2d0c8319 Iustin Pop
    """
2110 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
2111 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
2112 2d0c8319 Iustin Pop
      minor = self._aminor
2113 2d0c8319 Iustin Pop
    else:
2114 2d0c8319 Iustin Pop
      minor = None
2115 2d0c8319 Iustin Pop
2116 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
2117 2d0c8319 Iustin Pop
    return minor is not None
2118 2d0c8319 Iustin Pop
2119 2d0c8319 Iustin Pop
  def Assemble(self):
2120 2d0c8319 Iustin Pop
    """Assemble the drbd.
2121 2d0c8319 Iustin Pop

2122 2d0c8319 Iustin Pop
    Method:
2123 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
2124 2d0c8319 Iustin Pop
        our config
2125 2d0c8319 Iustin Pop
      - if not, we create it from zero
2126 d529599f Andrea Spadaccini
      - anyway, set the device parameters
2127 2d0c8319 Iustin Pop

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

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

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

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

2225 a2cfdea2 Iustin Pop
    """
2226 a1578d63 Iustin Pop
    minor = self._aminor
2227 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
2228 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
2229 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
2230 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
2231 1063abd1 Iustin Pop
      self._AssembleNet(minor,
2232 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
2233 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
2234 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
2235 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2236 a2cfdea2 Iustin Pop
2237 a2cfdea2 Iustin Pop
  @classmethod
2238 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
2239 b00b95dd Iustin Pop
    """Detach from the local device.
2240 b00b95dd Iustin Pop

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

2244 b00b95dd Iustin Pop
    """
2245 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2246 b00b95dd Iustin Pop
    if result.failed:
2247 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
2248 b00b95dd Iustin Pop
2249 b00b95dd Iustin Pop
  @classmethod
2250 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2251 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2252 f3e513ad Iustin Pop

2253 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2254 f3e513ad Iustin Pop

2255 f3e513ad Iustin Pop
    """
2256 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2257 a8459f1c Iustin Pop
    if result.failed:
2258 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
2259 f3e513ad Iustin Pop
2260 f3e513ad Iustin Pop
  @classmethod
2261 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2262 a2cfdea2 Iustin Pop
    """Deactivate the device.
2263 a2cfdea2 Iustin Pop

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

2266 a2cfdea2 Iustin Pop
    """
2267 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2268 a2cfdea2 Iustin Pop
    if result.failed:
2269 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
2270 33bc6f01 Iustin Pop
                  minor, result.output)
2271 a2cfdea2 Iustin Pop
2272 a2cfdea2 Iustin Pop
  def Shutdown(self):
2273 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2274 a2cfdea2 Iustin Pop

2275 a2cfdea2 Iustin Pop
    """
2276 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2277 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
2278 746f7476 Iustin Pop
      return
2279 746f7476 Iustin Pop
    minor = self.minor
2280 a2cfdea2 Iustin Pop
    self.minor = None
2281 a2cfdea2 Iustin Pop
    self.dev_path = None
2282 746f7476 Iustin Pop
    self._ShutdownAll(minor)
2283 a2cfdea2 Iustin Pop
2284 a2cfdea2 Iustin Pop
  def Remove(self):
2285 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2286 a2cfdea2 Iustin Pop

2287 a2cfdea2 Iustin Pop
    """
2288 0c6c04ec Iustin Pop
    self.Shutdown()
2289 a2cfdea2 Iustin Pop
2290 a2cfdea2 Iustin Pop
  @classmethod
2291 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2292 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2293 a2cfdea2 Iustin Pop

2294 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2295 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2296 a2cfdea2 Iustin Pop

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

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

2344 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
2345 6f695a2e Manuel Franceschini

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

2348 6f695a2e Manuel Franceschini
  """
2349 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2350 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
2351 6f695a2e Manuel Franceschini

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

2368 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
2369 6f695a2e Manuel Franceschini

2370 6f695a2e Manuel Franceschini
    """
2371 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
2372 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
2373 6f695a2e Manuel Franceschini
2374 6f695a2e Manuel Franceschini
  def Shutdown(self):
2375 6f695a2e Manuel Franceschini
    """Shutdown the device.
2376 6f695a2e Manuel Franceschini

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

2380 6f695a2e Manuel Franceschini
    """
2381 746f7476 Iustin Pop
    pass
2382 6f695a2e Manuel Franceschini
2383 6f695a2e Manuel Franceschini
  def Open(self, force=False):
2384 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
2385 6f695a2e Manuel Franceschini

2386 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
2387 6f695a2e Manuel Franceschini

2388 6f695a2e Manuel Franceschini
    """
2389 6f695a2e Manuel Franceschini
    pass
2390 6f695a2e Manuel Franceschini
2391 6f695a2e Manuel Franceschini
  def Close(self):
2392 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
2393 6f695a2e Manuel Franceschini

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

2396 6f695a2e Manuel Franceschini
    """
2397 6f695a2e Manuel Franceschini
    pass
2398 6f695a2e Manuel Franceschini
2399 6f695a2e Manuel Franceschini
  def Remove(self):
2400 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
2401 6f695a2e Manuel Franceschini

2402 c41eea6e Iustin Pop
    @rtype: boolean
2403 c41eea6e Iustin Pop
    @return: True if the removal was successful
2404 6f695a2e Manuel Franceschini

2405 6f695a2e Manuel Franceschini
    """
2406 6f695a2e Manuel Franceschini
    try:
2407 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
2408 6f695a2e Manuel Franceschini
    except OSError, err:
2409 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
2410 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
2411 6f695a2e Manuel Franceschini
2412 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
2413 bbe4cc16 Iustin Pop
    """Renames the file.
2414 bbe4cc16 Iustin Pop

2415 bbe4cc16 Iustin Pop
    """
2416 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
2417 bbe4cc16 Iustin Pop
    _ThrowError("Rename is not supported for file-based storage")
2418 bbe4cc16 Iustin Pop
2419 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2420 bbe4cc16 Iustin Pop
    """Grow the file
2421 bbe4cc16 Iustin Pop

2422 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
2423 bbe4cc16 Iustin Pop

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

2445 6f695a2e Manuel Franceschini
    Check if this file already exists.
2446 6f695a2e Manuel Franceschini

2447 c41eea6e Iustin Pop
    @rtype: boolean
2448 c41eea6e Iustin Pop
    @return: True if file exists
2449 6f695a2e Manuel Franceschini

2450 6f695a2e Manuel Franceschini
    """
2451 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
2452 ecb091e3 Iustin Pop
    return self.attached
2453 6f695a2e Manuel Franceschini
2454 fcff3897 Iustin Pop
  def GetActualSize(self):
2455 fcff3897 Iustin Pop
    """Return the actual disk size.
2456 fcff3897 Iustin Pop

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

2459 fcff3897 Iustin Pop
    """
2460 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
2461 fcff3897 Iustin Pop
    try:
2462 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
2463 fcff3897 Iustin Pop
      return st.st_size
2464 fcff3897 Iustin Pop
    except OSError, err:
2465 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
2466 fcff3897 Iustin Pop
2467 6f695a2e Manuel Franceschini
  @classmethod
2468 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2469 6f695a2e Manuel Franceschini
    """Create a new file.
2470 6f695a2e Manuel Franceschini

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

2473 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
2474 c41eea6e Iustin Pop
    @return: an instance of FileStorage
2475 6f695a2e Manuel Franceschini

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

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

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

2509 b6135bbc Apollon Oikonomopoulos
  """
2510 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
2511 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
2512 b6135bbc Apollon Oikonomopoulos

2513 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
2514 b6135bbc Apollon Oikonomopoulos

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

2539 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
2540 b6135bbc Apollon Oikonomopoulos

2541 b6135bbc Apollon Oikonomopoulos
    """
2542 ee1478e5 Bernardo Dal Seno
    if excl_stor:
2543 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("Persistent block device requested with"
2544 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
2545 94dcbdb0 Andrea Spadaccini
    return PersistentBlockDevice(unique_id, children, 0, params)
2546 b6135bbc Apollon Oikonomopoulos
2547 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
2548 b6135bbc Apollon Oikonomopoulos
    """Remove a device
2549 b6135bbc Apollon Oikonomopoulos

2550 b6135bbc Apollon Oikonomopoulos
    This is a noop
2551 b6135bbc Apollon Oikonomopoulos

2552 b6135bbc Apollon Oikonomopoulos
    """
2553 b6135bbc Apollon Oikonomopoulos
    pass
2554 b6135bbc Apollon Oikonomopoulos
2555 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
2556 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
2557 b6135bbc Apollon Oikonomopoulos

2558 b6135bbc Apollon Oikonomopoulos
    """
2559 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Rename is not supported for PersistentBlockDev storage")
2560 b6135bbc Apollon Oikonomopoulos
2561 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
2562 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
2563 b6135bbc Apollon Oikonomopoulos

2564 b6135bbc Apollon Oikonomopoulos

2565 b6135bbc Apollon Oikonomopoulos
    """
2566 b6135bbc Apollon Oikonomopoulos
    self.attached = False
2567 b6135bbc Apollon Oikonomopoulos
    try:
2568 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
2569 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
2570 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2571 b6135bbc Apollon Oikonomopoulos
      return False
2572 b6135bbc Apollon Oikonomopoulos
2573 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
2574 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
2575 b6135bbc Apollon Oikonomopoulos
      return False
2576 b6135bbc Apollon Oikonomopoulos
2577 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
2578 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
2579 b6135bbc Apollon Oikonomopoulos
    self.attached = True
2580 b6135bbc Apollon Oikonomopoulos
2581 b6135bbc Apollon Oikonomopoulos
    return True
2582 b6135bbc Apollon Oikonomopoulos
2583 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
2584 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
2585 b6135bbc Apollon Oikonomopoulos

2586 b6135bbc Apollon Oikonomopoulos
    """
2587 b6135bbc Apollon Oikonomopoulos
    pass
2588 b6135bbc Apollon Oikonomopoulos
2589 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
2590 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
2591 b6135bbc Apollon Oikonomopoulos

2592 b6135bbc Apollon Oikonomopoulos
    """
2593 b6135bbc Apollon Oikonomopoulos
    pass
2594 b6135bbc Apollon Oikonomopoulos
2595 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
2596 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
2597 b6135bbc Apollon Oikonomopoulos

2598 b6135bbc Apollon Oikonomopoulos
    """
2599 b6135bbc Apollon Oikonomopoulos
    pass
2600 b6135bbc Apollon Oikonomopoulos
2601 b6135bbc Apollon Oikonomopoulos
  def Close(self):
2602 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
2603 b6135bbc Apollon Oikonomopoulos

2604 b6135bbc Apollon Oikonomopoulos
    """
2605 b6135bbc Apollon Oikonomopoulos
    pass
2606 b6135bbc Apollon Oikonomopoulos
2607 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2608 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
2609 b6135bbc Apollon Oikonomopoulos

2610 b6135bbc Apollon Oikonomopoulos
    """
2611 b6135bbc Apollon Oikonomopoulos
    _ThrowError("Grow is not supported for PersistentBlockDev storage")
2612 b6135bbc Apollon Oikonomopoulos
2613 b6135bbc Apollon Oikonomopoulos
2614 7181fba0 Constantinos Venetsanopoulos
class RADOSBlockDevice(BlockDev):
2615 7181fba0 Constantinos Venetsanopoulos
  """A RADOS Block Device (rbd).
2616 7181fba0 Constantinos Venetsanopoulos

2617 7181fba0 Constantinos Venetsanopoulos
  This class implements the RADOS Block Device for the backend. You need
2618 7181fba0 Constantinos Venetsanopoulos
  the rbd kernel driver, the RADOS Tools and a working RADOS cluster for
2619 7181fba0 Constantinos Venetsanopoulos
  this to be functional.
2620 7181fba0 Constantinos Venetsanopoulos

2621 7181fba0 Constantinos Venetsanopoulos
  """
2622 7181fba0 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
2623 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
2624 7181fba0 Constantinos Venetsanopoulos

2625 7181fba0 Constantinos Venetsanopoulos
    """
2626 7181fba0 Constantinos Venetsanopoulos
    super(RADOSBlockDevice, self).__init__(unique_id, children, size, params)
2627 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2628 7181fba0 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2629 7181fba0 Constantinos Venetsanopoulos
2630 7181fba0 Constantinos Venetsanopoulos
    self.driver, self.rbd_name = unique_id
2631 7181fba0 Constantinos Venetsanopoulos
2632 7181fba0 Constantinos Venetsanopoulos
    self.major = self.minor = None
2633 7181fba0 Constantinos Venetsanopoulos
    self.Attach()
2634 7181fba0 Constantinos Venetsanopoulos
2635 7181fba0 Constantinos Venetsanopoulos
  @classmethod
2636 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2637 7181fba0 Constantinos Venetsanopoulos
    """Create a new rbd device.
2638 7181fba0 Constantinos Venetsanopoulos

2639 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
2640 7181fba0 Constantinos Venetsanopoulos

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

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

2685 7181fba0 Constantinos Venetsanopoulos
    """
2686 7181fba0 Constantinos Venetsanopoulos
    pass
2687 7181fba0 Constantinos Venetsanopoulos
2688 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
2689 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
2690 7181fba0 Constantinos Venetsanopoulos

2691 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
2692 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
2693 7181fba0 Constantinos Venetsanopoulos

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

2719 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
2720 7181fba0 Constantinos Venetsanopoulos

2721 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
2722 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
2723 7181fba0 Constantinos Venetsanopoulos

2724 7181fba0 Constantinos Venetsanopoulos
    """
2725 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2726 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2727 7181fba0 Constantinos Venetsanopoulos
2728 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2729 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2730 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2731 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2732 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped failed (%s): %s",
2733 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2734 7181fba0 Constantinos Venetsanopoulos
2735 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2736 7181fba0 Constantinos Venetsanopoulos
2737 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2738 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Return it.
2739 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2740 7181fba0 Constantinos Venetsanopoulos
2741 7181fba0 Constantinos Venetsanopoulos
    # The mapping doesn't exist. Create it.
2742 7181fba0 Constantinos Venetsanopoulos
    map_cmd = [constants.RBD_CMD, "map", "-p", pool, name]
2743 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(map_cmd)
2744 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2745 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map failed (%s): %s",
2746 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2747 7181fba0 Constantinos Venetsanopoulos
2748 7181fba0 Constantinos Venetsanopoulos
    # Find the corresponding rbd device.
2749 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2750 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2751 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2752 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map succeeded, but showmapped failed (%s): %s",
2753 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2754 7181fba0 Constantinos Venetsanopoulos
2755 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2756 7181fba0 Constantinos Venetsanopoulos
2757 7181fba0 Constantinos Venetsanopoulos
    if not rbd_dev:
2758 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd map succeeded, but could not find the rbd block"
2759 7181fba0 Constantinos Venetsanopoulos
                  " device in output of showmapped, for volume: %s", name)
2760 7181fba0 Constantinos Venetsanopoulos
2761 7181fba0 Constantinos Venetsanopoulos
    # The device was successfully mapped. Return it.
2762 7181fba0 Constantinos Venetsanopoulos
    return rbd_dev
2763 7181fba0 Constantinos Venetsanopoulos
2764 7181fba0 Constantinos Venetsanopoulos
  @staticmethod
2765 7181fba0 Constantinos Venetsanopoulos
  def _ParseRbdShowmappedOutput(output, volume_name):
2766 7181fba0 Constantinos Venetsanopoulos
    """Parse the output of `rbd showmapped'.
2767 7181fba0 Constantinos Venetsanopoulos

2768 7181fba0 Constantinos Venetsanopoulos
    This method parses the output of `rbd showmapped' and returns
2769 7181fba0 Constantinos Venetsanopoulos
    the rbd block device path (e.g. /dev/rbd0) that matches the
2770 7181fba0 Constantinos Venetsanopoulos
    given rbd volume.
2771 7181fba0 Constantinos Venetsanopoulos

2772 7181fba0 Constantinos Venetsanopoulos
    @type output: string
2773 7181fba0 Constantinos Venetsanopoulos
    @param output: the whole output of `rbd showmapped'
2774 7181fba0 Constantinos Venetsanopoulos
    @type volume_name: string
2775 7181fba0 Constantinos Venetsanopoulos
    @param volume_name: the name of the volume whose device we search for
2776 7181fba0 Constantinos Venetsanopoulos
    @rtype: string or None
2777 7181fba0 Constantinos Venetsanopoulos
    @return: block device path if the volume is mapped, else None
2778 7181fba0 Constantinos Venetsanopoulos

2779 7181fba0 Constantinos Venetsanopoulos
    """
2780 7181fba0 Constantinos Venetsanopoulos
    allfields = 5
2781 7181fba0 Constantinos Venetsanopoulos
    volumefield = 2
2782 7181fba0 Constantinos Venetsanopoulos
    devicefield = 4
2783 7181fba0 Constantinos Venetsanopoulos
2784 7181fba0 Constantinos Venetsanopoulos
    field_sep = "\t"
2785 7181fba0 Constantinos Venetsanopoulos
2786 7181fba0 Constantinos Venetsanopoulos
    lines = output.splitlines()
2787 7181fba0 Constantinos Venetsanopoulos
    splitted_lines = map(lambda l: l.split(field_sep), lines)
2788 7181fba0 Constantinos Venetsanopoulos
2789 7181fba0 Constantinos Venetsanopoulos
    # Check empty output.
2790 7181fba0 Constantinos Venetsanopoulos
    if not splitted_lines:
2791 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped returned empty output")
2792 7181fba0 Constantinos Venetsanopoulos
2793 7181fba0 Constantinos Venetsanopoulos
    # Check showmapped header line, to determine number of fields.
2794 7181fba0 Constantinos Venetsanopoulos
    field_cnt = len(splitted_lines[0])
2795 7181fba0 Constantinos Venetsanopoulos
    if field_cnt != allfields:
2796 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Cannot parse rbd showmapped output because its format"
2797 7181fba0 Constantinos Venetsanopoulos
                  " seems to have changed; expected %s fields, found %s",
2798 7181fba0 Constantinos Venetsanopoulos
                  allfields, field_cnt)
2799 7181fba0 Constantinos Venetsanopoulos
2800 7181fba0 Constantinos Venetsanopoulos
    matched_lines = \
2801 7181fba0 Constantinos Venetsanopoulos
      filter(lambda l: len(l) == allfields and l[volumefield] == volume_name,
2802 7181fba0 Constantinos Venetsanopoulos
             splitted_lines)
2803 7181fba0 Constantinos Venetsanopoulos
2804 7181fba0 Constantinos Venetsanopoulos
    if len(matched_lines) > 1:
2805 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("The rbd volume %s is mapped more than once."
2806 7181fba0 Constantinos Venetsanopoulos
                  " This shouldn't happen, try to unmap the extra"
2807 7181fba0 Constantinos Venetsanopoulos
                  " devices manually.", volume_name)
2808 7181fba0 Constantinos Venetsanopoulos
2809 7181fba0 Constantinos Venetsanopoulos
    if matched_lines:
2810 7181fba0 Constantinos Venetsanopoulos
      # rbd block device found. Return it.
2811 7181fba0 Constantinos Venetsanopoulos
      rbd_dev = matched_lines[0][devicefield]
2812 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
2813 7181fba0 Constantinos Venetsanopoulos
2814 7181fba0 Constantinos Venetsanopoulos
    # The given volume is not mapped.
2815 7181fba0 Constantinos Venetsanopoulos
    return None
2816 7181fba0 Constantinos Venetsanopoulos
2817 7181fba0 Constantinos Venetsanopoulos
  def Assemble(self):
2818 7181fba0 Constantinos Venetsanopoulos
    """Assemble the device.
2819 7181fba0 Constantinos Venetsanopoulos

2820 7181fba0 Constantinos Venetsanopoulos
    """
2821 7181fba0 Constantinos Venetsanopoulos
    pass
2822 7181fba0 Constantinos Venetsanopoulos
2823 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
2824 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
2825 7181fba0 Constantinos Venetsanopoulos

2826 7181fba0 Constantinos Venetsanopoulos
    """
2827 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2828 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
2829 7181fba0 Constantinos Venetsanopoulos
      return
2830 7181fba0 Constantinos Venetsanopoulos
2831 7181fba0 Constantinos Venetsanopoulos
    # Unmap the block device from the Volume.
2832 7181fba0 Constantinos Venetsanopoulos
    self._UnmapVolumeFromBlockdev(self.unique_id)
2833 7181fba0 Constantinos Venetsanopoulos
2834 7181fba0 Constantinos Venetsanopoulos
    self.minor = None
2835 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = None
2836 7181fba0 Constantinos Venetsanopoulos
2837 7181fba0 Constantinos Venetsanopoulos
  def _UnmapVolumeFromBlockdev(self, unique_id):
2838 7181fba0 Constantinos Venetsanopoulos
    """Unmaps the rbd device from the Volume it is mapped.
2839 7181fba0 Constantinos Venetsanopoulos

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

2843 7181fba0 Constantinos Venetsanopoulos
    """
2844 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
2845 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
2846 7181fba0 Constantinos Venetsanopoulos
2847 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
2848 7181fba0 Constantinos Venetsanopoulos
    showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
2849 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(showmap_cmd)
2850 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2851 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd showmapped failed [during unmap](%s): %s",
2852 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2853 7181fba0 Constantinos Venetsanopoulos
2854 7181fba0 Constantinos Venetsanopoulos
    rbd_dev = self._ParseRbdShowmappedOutput(result.output, name)
2855 7181fba0 Constantinos Venetsanopoulos
2856 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
2857 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Unmap the rbd device.
2858 7181fba0 Constantinos Venetsanopoulos
      unmap_cmd = [constants.RBD_CMD, "unmap", "%s" % rbd_dev]
2859 7181fba0 Constantinos Venetsanopoulos
      result = utils.RunCmd(unmap_cmd)
2860 7181fba0 Constantinos Venetsanopoulos
      if result.failed:
2861 7181fba0 Constantinos Venetsanopoulos
        _ThrowError("rbd unmap failed (%s): %s",
2862 7181fba0 Constantinos Venetsanopoulos
                    result.fail_reason, result.output)
2863 7181fba0 Constantinos Venetsanopoulos
2864 7181fba0 Constantinos Venetsanopoulos
  def Open(self, force=False):
2865 7181fba0 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
2866 7181fba0 Constantinos Venetsanopoulos

2867 7181fba0 Constantinos Venetsanopoulos
    """
2868 7181fba0 Constantinos Venetsanopoulos
    pass
2869 7181fba0 Constantinos Venetsanopoulos
2870 7181fba0 Constantinos Venetsanopoulos
  def Close(self):
2871 7181fba0 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
2872 7181fba0 Constantinos Venetsanopoulos

2873 7181fba0 Constantinos Venetsanopoulos
    """
2874 7181fba0 Constantinos Venetsanopoulos
    pass
2875 7181fba0 Constantinos Venetsanopoulos
2876 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
2877 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
2878 7181fba0 Constantinos Venetsanopoulos

2879 7181fba0 Constantinos Venetsanopoulos
    @type amount: integer
2880 7181fba0 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
2881 7181fba0 Constantinos Venetsanopoulos
    @type dryrun: boolean
2882 7181fba0 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
2883 7181fba0 Constantinos Venetsanopoulos
        only, without actually increasing the size
2884 7181fba0 Constantinos Venetsanopoulos

2885 7181fba0 Constantinos Venetsanopoulos
    """
2886 cad0723b Iustin Pop
    if not backingstore:
2887 cad0723b Iustin Pop
      return
2888 7181fba0 Constantinos Venetsanopoulos
    if not self.Attach():
2889 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("Can't attach to rbd device during Grow()")
2890 7181fba0 Constantinos Venetsanopoulos
2891 7181fba0 Constantinos Venetsanopoulos
    if dryrun:
2892 7181fba0 Constantinos Venetsanopoulos
      # the rbd tool does not support dry runs of resize operations.
2893 7181fba0 Constantinos Venetsanopoulos
      # Since rbd volumes are thinly provisioned, we assume
2894 7181fba0 Constantinos Venetsanopoulos
      # there is always enough free space for the operation.
2895 7181fba0 Constantinos Venetsanopoulos
      return
2896 7181fba0 Constantinos Venetsanopoulos
2897 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
2898 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
2899 7181fba0 Constantinos Venetsanopoulos
    new_size = self.size + amount
2900 7181fba0 Constantinos Venetsanopoulos
2901 7181fba0 Constantinos Venetsanopoulos
    # Resize the rbd volume (Image) inside the RADOS cluster.
2902 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "resize", "-p", rbd_pool,
2903 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % new_size]
2904 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
2905 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
2906 7181fba0 Constantinos Venetsanopoulos
      _ThrowError("rbd resize failed (%s): %s",
2907 7181fba0 Constantinos Venetsanopoulos
                  result.fail_reason, result.output)
2908 7181fba0 Constantinos Venetsanopoulos
2909 7181fba0 Constantinos Venetsanopoulos
2910 376631d1 Constantinos Venetsanopoulos
class ExtStorageDevice(BlockDev):
2911 376631d1 Constantinos Venetsanopoulos
  """A block device provided by an ExtStorage Provider.
2912 376631d1 Constantinos Venetsanopoulos

2913 376631d1 Constantinos Venetsanopoulos
  This class implements the External Storage Interface, which means
2914 376631d1 Constantinos Venetsanopoulos
  handling of the externally provided block devices.
2915 376631d1 Constantinos Venetsanopoulos

2916 376631d1 Constantinos Venetsanopoulos
  """
2917 376631d1 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
2918 376631d1 Constantinos Venetsanopoulos
    """Attaches to an extstorage block device.
2919 376631d1 Constantinos Venetsanopoulos

2920 376631d1 Constantinos Venetsanopoulos
    """
2921 376631d1 Constantinos Venetsanopoulos
    super(ExtStorageDevice, self).__init__(unique_id, children, size, params)
2922 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2923 376631d1 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2924 376631d1 Constantinos Venetsanopoulos
2925 376631d1 Constantinos Venetsanopoulos
    self.driver, self.vol_name = unique_id
2926 938adc87 Constantinos Venetsanopoulos
    self.ext_params = params
2927 376631d1 Constantinos Venetsanopoulos
2928 376631d1 Constantinos Venetsanopoulos
    self.major = self.minor = None
2929 376631d1 Constantinos Venetsanopoulos
    self.Attach()
2930 376631d1 Constantinos Venetsanopoulos
2931 376631d1 Constantinos Venetsanopoulos
  @classmethod
2932 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
2933 376631d1 Constantinos Venetsanopoulos
    """Create a new extstorage device.
2934 376631d1 Constantinos Venetsanopoulos

2935 376631d1 Constantinos Venetsanopoulos
    Provision a new volume using an extstorage provider, which will
2936 376631d1 Constantinos Venetsanopoulos
    then be mapped to a block device.
2937 376631d1 Constantinos Venetsanopoulos

2938 376631d1 Constantinos Venetsanopoulos
    """
2939 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2940 376631d1 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
2941 376631d1 Constantinos Venetsanopoulos
                                   str(unique_id))
2942 ee1478e5 Bernardo Dal Seno
    if excl_stor:
2943 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("extstorage device requested with"
2944 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
2945 376631d1 Constantinos Venetsanopoulos
2946 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's create script,
2947 376631d1 Constantinos Venetsanopoulos
    # to provision a new Volume inside the External Storage
2948 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id,
2949 938adc87 Constantinos Venetsanopoulos
                      params, str(size))
2950 376631d1 Constantinos Venetsanopoulos
2951 376631d1 Constantinos Venetsanopoulos
    return ExtStorageDevice(unique_id, children, size, params)
2952 376631d1 Constantinos Venetsanopoulos
2953 376631d1 Constantinos Venetsanopoulos
  def Remove(self):
2954 376631d1 Constantinos Venetsanopoulos
    """Remove the extstorage device.
2955 376631d1 Constantinos Venetsanopoulos

2956 376631d1 Constantinos Venetsanopoulos
    """
2957 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
2958 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
2959 376631d1 Constantinos Venetsanopoulos
      return
2960 376631d1 Constantinos Venetsanopoulos
2961 376631d1 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
2962 376631d1 Constantinos Venetsanopoulos
    self.Shutdown()
2963 376631d1 Constantinos Venetsanopoulos
2964 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's remove script,
2965 376631d1 Constantinos Venetsanopoulos
    # to remove the Volume from the External Storage
2966 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id,
2967 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
2968 376631d1 Constantinos Venetsanopoulos
2969 376631d1 Constantinos Venetsanopoulos
  def Rename(self, new_id):
2970 376631d1 Constantinos Venetsanopoulos
    """Rename this device.
2971 376631d1 Constantinos Venetsanopoulos

2972 376631d1 Constantinos Venetsanopoulos
    """
2973 376631d1 Constantinos Venetsanopoulos
    pass
2974 376631d1 Constantinos Venetsanopoulos
2975 376631d1 Constantinos Venetsanopoulos
  def Attach(self):
2976 376631d1 Constantinos Venetsanopoulos
    """Attach to an existing extstorage device.
2977 376631d1 Constantinos Venetsanopoulos

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

2981 376631d1 Constantinos Venetsanopoulos
    """
2982 376631d1 Constantinos Venetsanopoulos
    self.attached = False
2983 376631d1 Constantinos Venetsanopoulos
2984 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's attach script,
2985 376631d1 Constantinos Venetsanopoulos
    # to attach an existing Volume to a block device under /dev
2986 376631d1 Constantinos Venetsanopoulos
    self.dev_path = _ExtStorageAction(constants.ES_ACTION_ATTACH,
2987 938adc87 Constantinos Venetsanopoulos
                                      self.unique_id, self.ext_params)
2988 376631d1 Constantinos Venetsanopoulos
2989 376631d1 Constantinos Venetsanopoulos
    try:
2990 376631d1 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
2991 376631d1 Constantinos Venetsanopoulos
    except OSError, err:
2992 376631d1 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2993 376631d1 Constantinos Venetsanopoulos
      return False
2994 376631d1 Constantinos Venetsanopoulos
2995 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
2996 376631d1 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
2997 376631d1 Constantinos Venetsanopoulos
      return False
2998 376631d1 Constantinos Venetsanopoulos
2999 376631d1 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
3000 376631d1 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
3001 376631d1 Constantinos Venetsanopoulos
    self.attached = True
3002 376631d1 Constantinos Venetsanopoulos
3003 376631d1 Constantinos Venetsanopoulos
    return True
3004 376631d1 Constantinos Venetsanopoulos
3005 376631d1 Constantinos Venetsanopoulos
  def Assemble(self):
3006 376631d1 Constantinos Venetsanopoulos
    """Assemble the device.
3007 376631d1 Constantinos Venetsanopoulos

3008 376631d1 Constantinos Venetsanopoulos
    """
3009 376631d1 Constantinos Venetsanopoulos
    pass
3010 376631d1 Constantinos Venetsanopoulos
3011 376631d1 Constantinos Venetsanopoulos
  def Shutdown(self):
3012 376631d1 Constantinos Venetsanopoulos
    """Shutdown the device.
3013 376631d1 Constantinos Venetsanopoulos

3014 376631d1 Constantinos Venetsanopoulos
    """
3015 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
3016 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
3017 376631d1 Constantinos Venetsanopoulos
      return
3018 376631d1 Constantinos Venetsanopoulos
3019 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's detach script,
3020 376631d1 Constantinos Venetsanopoulos
    # to detach an existing Volume from it's block device under /dev
3021 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id,
3022 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
3023 376631d1 Constantinos Venetsanopoulos
3024 376631d1 Constantinos Venetsanopoulos
    self.minor = None
3025 376631d1 Constantinos Venetsanopoulos
    self.dev_path = None
3026 376631d1 Constantinos Venetsanopoulos
3027 376631d1 Constantinos Venetsanopoulos
  def Open(self, force=False):
3028 376631d1 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
3029 376631d1 Constantinos Venetsanopoulos

3030 376631d1 Constantinos Venetsanopoulos
    """
3031 376631d1 Constantinos Venetsanopoulos
    pass
3032 376631d1 Constantinos Venetsanopoulos
3033 376631d1 Constantinos Venetsanopoulos
  def Close(self):
3034 376631d1 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
3035 376631d1 Constantinos Venetsanopoulos

3036 376631d1 Constantinos Venetsanopoulos
    """
3037 376631d1 Constantinos Venetsanopoulos
    pass
3038 376631d1 Constantinos Venetsanopoulos
3039 376631d1 Constantinos Venetsanopoulos
  def Grow(self, amount, dryrun, backingstore):
3040 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
3041 376631d1 Constantinos Venetsanopoulos

3042 376631d1 Constantinos Venetsanopoulos
    @type amount: integer
3043 376631d1 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
3044 376631d1 Constantinos Venetsanopoulos
    @type dryrun: boolean
3045 376631d1 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
3046 376631d1 Constantinos Venetsanopoulos
        only, without actually increasing the size
3047 376631d1 Constantinos Venetsanopoulos

3048 376631d1 Constantinos Venetsanopoulos
    """
3049 376631d1 Constantinos Venetsanopoulos
    if not backingstore:
3050 376631d1 Constantinos Venetsanopoulos
      return
3051 376631d1 Constantinos Venetsanopoulos
    if not self.Attach():
3052 376631d1 Constantinos Venetsanopoulos
      _ThrowError("Can't attach to extstorage device during Grow()")
3053 376631d1 Constantinos Venetsanopoulos
3054 376631d1 Constantinos Venetsanopoulos
    if dryrun:
3055 376631d1 Constantinos Venetsanopoulos
      # we do not support dry runs of resize operations for now.
3056 376631d1 Constantinos Venetsanopoulos
      return
3057 376631d1 Constantinos Venetsanopoulos
3058 376631d1 Constantinos Venetsanopoulos
    new_size = self.size + amount
3059 376631d1 Constantinos Venetsanopoulos
3060 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's grow script,
3061 376631d1 Constantinos Venetsanopoulos
    # to grow an existing Volume inside the External Storage
3062 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_GROW, self.unique_id,
3063 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, str(self.size), grow=str(new_size))
3064 376631d1 Constantinos Venetsanopoulos
3065 376631d1 Constantinos Venetsanopoulos
  def SetInfo(self, text):
3066 376631d1 Constantinos Venetsanopoulos
    """Update metadata with info text.
3067 376631d1 Constantinos Venetsanopoulos

3068 376631d1 Constantinos Venetsanopoulos
    """
3069 376631d1 Constantinos Venetsanopoulos
    # Replace invalid characters
3070 376631d1 Constantinos Venetsanopoulos
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
3071 376631d1 Constantinos Venetsanopoulos
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
3072 376631d1 Constantinos Venetsanopoulos
3073 376631d1 Constantinos Venetsanopoulos
    # Only up to 128 characters are allowed
3074 376631d1 Constantinos Venetsanopoulos
    text = text[:128]
3075 376631d1 Constantinos Venetsanopoulos
3076 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's setinfo script,
3077 376631d1 Constantinos Venetsanopoulos
    # to set metadata for an existing Volume inside the External Storage
3078 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
3079 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, metadata=text)
3080 376631d1 Constantinos Venetsanopoulos
3081 376631d1 Constantinos Venetsanopoulos
3082 938adc87 Constantinos Venetsanopoulos
def _ExtStorageAction(action, unique_id, ext_params,
3083 938adc87 Constantinos Venetsanopoulos
                      size=None, grow=None, metadata=None):
3084 376631d1 Constantinos Venetsanopoulos
  """Take an External Storage action.
3085 376631d1 Constantinos Venetsanopoulos

3086 376631d1 Constantinos Venetsanopoulos
  Take an External Storage action concerning or affecting
3087 376631d1 Constantinos Venetsanopoulos
  a specific Volume inside the External Storage.
3088 376631d1 Constantinos Venetsanopoulos

3089 376631d1 Constantinos Venetsanopoulos
  @type action: string
3090 376631d1 Constantinos Venetsanopoulos
  @param action: which action to perform. One of:
3091 376631d1 Constantinos Venetsanopoulos
                 create / remove / grow / attach / detach
3092 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
3093 376631d1 Constantinos Venetsanopoulos
  @param unique_id: a tuple containing the type of ExtStorage (driver)
3094 376631d1 Constantinos Venetsanopoulos
                    and the Volume name
3095 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
3096 938adc87 Constantinos Venetsanopoulos
  @param ext_params: ExtStorage parameters
3097 376631d1 Constantinos Venetsanopoulos
  @type size: integer
3098 376631d1 Constantinos Venetsanopoulos
  @param size: the size of the Volume in mebibytes
3099 376631d1 Constantinos Venetsanopoulos
  @type grow: integer
3100 376631d1 Constantinos Venetsanopoulos
  @param grow: the new size in mebibytes (after grow)
3101 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
3102 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume, for use by the provider
3103 376631d1 Constantinos Venetsanopoulos
  @rtype: None or a block device path (during attach)
3104 376631d1 Constantinos Venetsanopoulos

3105 376631d1 Constantinos Venetsanopoulos
  """
3106 376631d1 Constantinos Venetsanopoulos
  driver, vol_name = unique_id
3107 376631d1 Constantinos Venetsanopoulos
3108 376631d1 Constantinos Venetsanopoulos
  # Create an External Storage instance of type `driver'
3109 376631d1 Constantinos Venetsanopoulos
  status, inst_es = ExtStorageFromDisk(driver)
3110 376631d1 Constantinos Venetsanopoulos
  if not status:
3111 376631d1 Constantinos Venetsanopoulos
    _ThrowError("%s" % inst_es)
3112 376631d1 Constantinos Venetsanopoulos
3113 376631d1 Constantinos Venetsanopoulos
  # Create the basic environment for the driver's scripts
3114 938adc87 Constantinos Venetsanopoulos
  create_env = _ExtStorageEnvironment(unique_id, ext_params, size,
3115 938adc87 Constantinos Venetsanopoulos
                                      grow, metadata)
3116 376631d1 Constantinos Venetsanopoulos
3117 376631d1 Constantinos Venetsanopoulos
  # Do not use log file for action `attach' as we need
3118 376631d1 Constantinos Venetsanopoulos
  # to get the output from RunResult
3119 376631d1 Constantinos Venetsanopoulos
  # TODO: find a way to have a log file for attach too
3120 376631d1 Constantinos Venetsanopoulos
  logfile = None
3121 376631d1 Constantinos Venetsanopoulos
  if action is not constants.ES_ACTION_ATTACH:
3122 376631d1 Constantinos Venetsanopoulos
    logfile = _VolumeLogName(action, driver, vol_name)
3123 376631d1 Constantinos Venetsanopoulos
3124 376631d1 Constantinos Venetsanopoulos
  # Make sure the given action results in a valid script
3125 376631d1 Constantinos Venetsanopoulos
  if action not in constants.ES_SCRIPTS:
3126 376631d1 Constantinos Venetsanopoulos
    _ThrowError("Action '%s' doesn't result in a valid ExtStorage script" %
3127 376631d1 Constantinos Venetsanopoulos
                action)
3128 376631d1 Constantinos Venetsanopoulos
3129 376631d1 Constantinos Venetsanopoulos
  # Find out which external script to run according the given action
3130 376631d1 Constantinos Venetsanopoulos
  script_name = action + "_script"
3131 376631d1 Constantinos Venetsanopoulos
  script = getattr(inst_es, script_name)
3132 376631d1 Constantinos Venetsanopoulos
3133 376631d1 Constantinos Venetsanopoulos
  # Run the external script
3134 376631d1 Constantinos Venetsanopoulos
  result = utils.RunCmd([script], env=create_env,
3135 376631d1 Constantinos Venetsanopoulos
                        cwd=inst_es.path, output=logfile,)
3136 376631d1 Constantinos Venetsanopoulos
  if result.failed:
3137 376631d1 Constantinos Venetsanopoulos
    logging.error("External storage's %s command '%s' returned"
3138 376631d1 Constantinos Venetsanopoulos
                  " error: %s, logfile: %s, output: %s",
3139 376631d1 Constantinos Venetsanopoulos
                  action, result.cmd, result.fail_reason,
3140 376631d1 Constantinos Venetsanopoulos
                  logfile, result.output)
3141 376631d1 Constantinos Venetsanopoulos
3142 376631d1 Constantinos Venetsanopoulos
    # If logfile is 'None' (during attach), it breaks TailFile
3143 376631d1 Constantinos Venetsanopoulos
    # TODO: have a log file for attach too
3144 376631d1 Constantinos Venetsanopoulos
    if action is not constants.ES_ACTION_ATTACH:
3145 376631d1 Constantinos Venetsanopoulos
      lines = [utils.SafeEncode(val)
3146 376631d1 Constantinos Venetsanopoulos
               for val in utils.TailFile(logfile, lines=20)]
3147 376631d1 Constantinos Venetsanopoulos
    else:
3148 376631d1 Constantinos Venetsanopoulos
      lines = result.output[-20:]
3149 376631d1 Constantinos Venetsanopoulos
3150 376631d1 Constantinos Venetsanopoulos
    _ThrowError("External storage's %s script failed (%s), last"
3151 376631d1 Constantinos Venetsanopoulos
                " lines of output:\n%s",
3152 376631d1 Constantinos Venetsanopoulos
                action, result.fail_reason, "\n".join(lines))
3153 376631d1 Constantinos Venetsanopoulos
3154 376631d1 Constantinos Venetsanopoulos
  if action == constants.ES_ACTION_ATTACH:
3155 376631d1 Constantinos Venetsanopoulos
    return result.stdout
3156 376631d1 Constantinos Venetsanopoulos
3157 376631d1 Constantinos Venetsanopoulos
3158 376631d1 Constantinos Venetsanopoulos
def ExtStorageFromDisk(name, base_dir=None):
3159 376631d1 Constantinos Venetsanopoulos
  """Create an ExtStorage instance from disk.
3160 376631d1 Constantinos Venetsanopoulos

3161 376631d1 Constantinos Venetsanopoulos
  This function will return an ExtStorage instance
3162 376631d1 Constantinos Venetsanopoulos
  if the given name is a valid ExtStorage name.
3163 376631d1 Constantinos Venetsanopoulos

3164 376631d1 Constantinos Venetsanopoulos
  @type base_dir: string
3165 376631d1 Constantinos Venetsanopoulos
  @keyword base_dir: Base directory containing ExtStorage installations.
3166 376631d1 Constantinos Venetsanopoulos
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
3167 376631d1 Constantinos Venetsanopoulos
  @rtype: tuple
3168 376631d1 Constantinos Venetsanopoulos
  @return: True and the ExtStorage instance if we find a valid one, or
3169 376631d1 Constantinos Venetsanopoulos
      False and the diagnose message on error
3170 376631d1 Constantinos Venetsanopoulos

3171 376631d1 Constantinos Venetsanopoulos
  """
3172 376631d1 Constantinos Venetsanopoulos
  if base_dir is None:
3173 376631d1 Constantinos Venetsanopoulos
    es_base_dir = pathutils.ES_SEARCH_PATH
3174 376631d1 Constantinos Venetsanopoulos
  else:
3175 376631d1 Constantinos Venetsanopoulos
    es_base_dir = [base_dir]
3176 376631d1 Constantinos Venetsanopoulos
3177 376631d1 Constantinos Venetsanopoulos
  es_dir = utils.FindFile(name, es_base_dir, os.path.isdir)
3178 376631d1 Constantinos Venetsanopoulos
3179 376631d1 Constantinos Venetsanopoulos
  if es_dir is None:
3180 376631d1 Constantinos Venetsanopoulos
    return False, ("Directory for External Storage Provider %s not"
3181 376631d1 Constantinos Venetsanopoulos
                   " found in search path" % name)
3182 376631d1 Constantinos Venetsanopoulos
3183 376631d1 Constantinos Venetsanopoulos
  # ES Files dictionary, we will populate it with the absolute path
3184 376631d1 Constantinos Venetsanopoulos
  # names; if the value is True, then it is a required file, otherwise
3185 376631d1 Constantinos Venetsanopoulos
  # an optional one
3186 376631d1 Constantinos Venetsanopoulos
  es_files = dict.fromkeys(constants.ES_SCRIPTS, True)
3187 376631d1 Constantinos Venetsanopoulos
3188 938adc87 Constantinos Venetsanopoulos
  es_files[constants.ES_PARAMETERS_FILE] = True
3189 938adc87 Constantinos Venetsanopoulos
3190 938adc87 Constantinos Venetsanopoulos
  for (filename, _) in es_files.items():
3191 376631d1 Constantinos Venetsanopoulos
    es_files[filename] = utils.PathJoin(es_dir, filename)
3192 376631d1 Constantinos Venetsanopoulos
3193 376631d1 Constantinos Venetsanopoulos
    try:
3194 376631d1 Constantinos Venetsanopoulos
      st = os.stat(es_files[filename])
3195 376631d1 Constantinos Venetsanopoulos
    except EnvironmentError, err:
3196 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is missing (%s)" %
3197 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir, utils.ErrnoOrStr(err)))
3198 376631d1 Constantinos Venetsanopoulos
3199 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
3200 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is not a regular file" %
3201 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir))
3202 376631d1 Constantinos Venetsanopoulos
3203 376631d1 Constantinos Venetsanopoulos
    if filename in constants.ES_SCRIPTS:
3204 376631d1 Constantinos Venetsanopoulos
      if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
3205 376631d1 Constantinos Venetsanopoulos
        return False, ("File '%s' under path '%s' is not executable" %
3206 376631d1 Constantinos Venetsanopoulos
                       (filename, es_dir))
3207 376631d1 Constantinos Venetsanopoulos
3208 938adc87 Constantinos Venetsanopoulos
  parameters = []
3209 938adc87 Constantinos Venetsanopoulos
  if constants.ES_PARAMETERS_FILE in es_files:
3210 938adc87 Constantinos Venetsanopoulos
    parameters_file = es_files[constants.ES_PARAMETERS_FILE]
3211 938adc87 Constantinos Venetsanopoulos
    try:
3212 938adc87 Constantinos Venetsanopoulos
      parameters = utils.ReadFile(parameters_file).splitlines()
3213 938adc87 Constantinos Venetsanopoulos
    except EnvironmentError, err:
3214 938adc87 Constantinos Venetsanopoulos
      return False, ("Error while reading the EXT parameters file at %s: %s" %
3215 938adc87 Constantinos Venetsanopoulos
                     (parameters_file, utils.ErrnoOrStr(err)))
3216 938adc87 Constantinos Venetsanopoulos
    parameters = [v.split(None, 1) for v in parameters]
3217 938adc87 Constantinos Venetsanopoulos
3218 376631d1 Constantinos Venetsanopoulos
  es_obj = \
3219 376631d1 Constantinos Venetsanopoulos
    objects.ExtStorage(name=name, path=es_dir,
3220 376631d1 Constantinos Venetsanopoulos
                       create_script=es_files[constants.ES_SCRIPT_CREATE],
3221 376631d1 Constantinos Venetsanopoulos
                       remove_script=es_files[constants.ES_SCRIPT_REMOVE],
3222 376631d1 Constantinos Venetsanopoulos
                       grow_script=es_files[constants.ES_SCRIPT_GROW],
3223 376631d1 Constantinos Venetsanopoulos
                       attach_script=es_files[constants.ES_SCRIPT_ATTACH],
3224 376631d1 Constantinos Venetsanopoulos
                       detach_script=es_files[constants.ES_SCRIPT_DETACH],
3225 938adc87 Constantinos Venetsanopoulos
                       setinfo_script=es_files[constants.ES_SCRIPT_SETINFO],
3226 938adc87 Constantinos Venetsanopoulos
                       verify_script=es_files[constants.ES_SCRIPT_VERIFY],
3227 938adc87 Constantinos Venetsanopoulos
                       supported_parameters=parameters)
3228 376631d1 Constantinos Venetsanopoulos
  return True, es_obj
3229 376631d1 Constantinos Venetsanopoulos
3230 376631d1 Constantinos Venetsanopoulos
3231 938adc87 Constantinos Venetsanopoulos
def _ExtStorageEnvironment(unique_id, ext_params,
3232 938adc87 Constantinos Venetsanopoulos
                           size=None, grow=None, metadata=None):
3233 376631d1 Constantinos Venetsanopoulos
  """Calculate the environment for an External Storage script.
3234 376631d1 Constantinos Venetsanopoulos

3235 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
3236 376631d1 Constantinos Venetsanopoulos
  @param unique_id: ExtStorage pool and name of the Volume
3237 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
3238 938adc87 Constantinos Venetsanopoulos
  @param ext_params: the EXT parameters
3239 376631d1 Constantinos Venetsanopoulos
  @type size: string
3240 376631d1 Constantinos Venetsanopoulos
  @param size: size of the Volume (in mebibytes)
3241 376631d1 Constantinos Venetsanopoulos
  @type grow: string
3242 376631d1 Constantinos Venetsanopoulos
  @param grow: new size of Volume after grow (in mebibytes)
3243 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
3244 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume
3245 376631d1 Constantinos Venetsanopoulos
  @rtype: dict
3246 376631d1 Constantinos Venetsanopoulos
  @return: dict of environment variables
3247 376631d1 Constantinos Venetsanopoulos

3248 376631d1 Constantinos Venetsanopoulos
  """
3249 376631d1 Constantinos Venetsanopoulos
  vol_name = unique_id[1]
3250 376631d1 Constantinos Venetsanopoulos
3251 376631d1 Constantinos Venetsanopoulos
  result = {}
3252 376631d1 Constantinos Venetsanopoulos
  result["VOL_NAME"] = vol_name
3253 376631d1 Constantinos Venetsanopoulos
3254 938adc87 Constantinos Venetsanopoulos
  # EXT params
3255 938adc87 Constantinos Venetsanopoulos
  for pname, pvalue in ext_params.items():
3256 938adc87 Constantinos Venetsanopoulos
    result["EXTP_%s" % pname.upper()] = str(pvalue)
3257 938adc87 Constantinos Venetsanopoulos
3258 376631d1 Constantinos Venetsanopoulos
  if size is not None:
3259 376631d1 Constantinos Venetsanopoulos
    result["VOL_SIZE"] = size
3260 376631d1 Constantinos Venetsanopoulos
3261 376631d1 Constantinos Venetsanopoulos
  if grow is not None:
3262 376631d1 Constantinos Venetsanopoulos
    result["VOL_NEW_SIZE"] = grow
3263 376631d1 Constantinos Venetsanopoulos
3264 376631d1 Constantinos Venetsanopoulos
  if metadata is not None:
3265 376631d1 Constantinos Venetsanopoulos
    result["VOL_METADATA"] = metadata
3266 376631d1 Constantinos Venetsanopoulos
3267 376631d1 Constantinos Venetsanopoulos
  return result
3268 376631d1 Constantinos Venetsanopoulos
3269 376631d1 Constantinos Venetsanopoulos
3270 376631d1 Constantinos Venetsanopoulos
def _VolumeLogName(kind, es_name, volume):
3271 376631d1 Constantinos Venetsanopoulos
  """Compute the ExtStorage log filename for a given Volume and operation.
3272 376631d1 Constantinos Venetsanopoulos

3273 376631d1 Constantinos Venetsanopoulos
  @type kind: string
3274 376631d1 Constantinos Venetsanopoulos
  @param kind: the operation type (e.g. create, remove etc.)
3275 376631d1 Constantinos Venetsanopoulos
  @type es_name: string
3276 376631d1 Constantinos Venetsanopoulos
  @param es_name: the ExtStorage name
3277 376631d1 Constantinos Venetsanopoulos
  @type volume: string
3278 376631d1 Constantinos Venetsanopoulos
  @param volume: the name of the Volume inside the External Storage
3279 376631d1 Constantinos Venetsanopoulos

3280 376631d1 Constantinos Venetsanopoulos
  """
3281 376631d1 Constantinos Venetsanopoulos
  # Check if the extstorage log dir is a valid dir
3282 376631d1 Constantinos Venetsanopoulos
  if not os.path.isdir(pathutils.LOG_ES_DIR):
3283 376631d1 Constantinos Venetsanopoulos
    _ThrowError("Cannot find log directory: %s", pathutils.LOG_ES_DIR)
3284 376631d1 Constantinos Venetsanopoulos
3285 376631d1 Constantinos Venetsanopoulos
  # TODO: Use tempfile.mkstemp to create unique filename
3286 376631d1 Constantinos Venetsanopoulos
  base = ("%s-%s-%s-%s.log" %
3287 376631d1 Constantinos Venetsanopoulos
          (kind, es_name, volume, utils.TimestampForFilename()))
3288 376631d1 Constantinos Venetsanopoulos
  return utils.PathJoin(pathutils.LOG_ES_DIR, base)
3289 376631d1 Constantinos Venetsanopoulos
3290 376631d1 Constantinos Venetsanopoulos
3291 a8083063 Iustin Pop
DEV_MAP = {
3292 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
3293 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
3294 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
3295 7181fba0 Constantinos Venetsanopoulos
  constants.LD_RBD: RADOSBlockDevice,
3296 376631d1 Constantinos Venetsanopoulos
  constants.LD_EXT: ExtStorageDevice,
3297 a8083063 Iustin Pop
  }
3298 a8083063 Iustin Pop
3299 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
3300 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
3301 cb7c0198 Iustin Pop
3302 a8083063 Iustin Pop
3303 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
3304 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
3305 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
3306 94dcbdb0 Andrea Spadaccini
3307 94dcbdb0 Andrea Spadaccini
3308 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
3309 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
3310 5ff82cc9 René Nussbaumer

3311 5ff82cc9 René Nussbaumer
  """
3312 5ff82cc9 René Nussbaumer
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
3313 5ff82cc9 René Nussbaumer
  if missing:
3314 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
3315 5ff82cc9 René Nussbaumer
                                 missing)
3316 5ff82cc9 René Nussbaumer
3317 5ff82cc9 René Nussbaumer
3318 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
3319 a8083063 Iustin Pop
  """Search for an existing, assembled device.
3320 a8083063 Iustin Pop

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

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

3330 a8083063 Iustin Pop
  """
3331 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3332 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
3333 c7c6606d René Nussbaumer
                                  disk.params)
3334 cb999543 Iustin Pop
  if not device.attached:
3335 a8083063 Iustin Pop
    return None
3336 ecb091e3 Iustin Pop
  return device
3337 a8083063 Iustin Pop
3338 a8083063 Iustin Pop
3339 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
3340 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
3341 a8083063 Iustin Pop

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

3345 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
3346 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
3347 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
3348 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
3349 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
3350 94dcbdb0 Andrea Spadaccini

3351 a8083063 Iustin Pop
  """
3352 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3353 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
3354 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
3355 c7c6606d René Nussbaumer
                                  disk.params)
3356 1063abd1 Iustin Pop
  device.Assemble()
3357 a8083063 Iustin Pop
  return device
3358 a8083063 Iustin Pop
3359 a8083063 Iustin Pop
3360 ee1478e5 Bernardo Dal Seno
def Create(disk, children, excl_stor):
3361 a8083063 Iustin Pop
  """Create a device.
3362 a8083063 Iustin Pop

3363 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
3364 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
3365 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
3366 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
3367 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
3368 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
3369 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
3370 94dcbdb0 Andrea Spadaccini

3371 a8083063 Iustin Pop
  """
3372 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
3373 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
3374 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
3375 ee1478e5 Bernardo Dal Seno
                                         disk.params, excl_stor)
3376 a8083063 Iustin Pop
  return device