Statistics
| Branch: | Tag: | Revision:

root / lib / storage / bdev.py @ be9150ea

History | View | Annotate | Download (58.5 kB)

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

45 bdecfea2 Stratos Psomadakis
  """
46 bdecfea2 Stratos Psomadakis
  pass
47 bdecfea2 Stratos Psomadakis
48 bdecfea2 Stratos Psomadakis
49 e398546b Iustin Pop
def _CheckResult(result):
50 e398546b Iustin Pop
  """Throws an error if the given result is a failed one.
51 e398546b Iustin Pop

52 e398546b Iustin Pop
  @param result: result from RunCmd
53 e398546b Iustin Pop

54 e398546b Iustin Pop
  """
55 e398546b Iustin Pop
  if result.failed:
56 89ff748d Thomas Thrainer
    base.ThrowError("Command: %s error: %s - %s",
57 89ff748d Thomas Thrainer
                    result.cmd, result.fail_reason, result.output)
58 310fbb64 Iustin Pop
59 310fbb64 Iustin Pop
60 23e3c9b7 Michael Hanselmann
def _GetForbiddenFileStoragePaths():
61 23e3c9b7 Michael Hanselmann
  """Builds a list of path prefixes which shouldn't be used for file storage.
62 23e3c9b7 Michael Hanselmann

63 23e3c9b7 Michael Hanselmann
  @rtype: frozenset
64 23e3c9b7 Michael Hanselmann

65 23e3c9b7 Michael Hanselmann
  """
66 23e3c9b7 Michael Hanselmann
  paths = set([
67 23e3c9b7 Michael Hanselmann
    "/boot",
68 23e3c9b7 Michael Hanselmann
    "/dev",
69 23e3c9b7 Michael Hanselmann
    "/etc",
70 23e3c9b7 Michael Hanselmann
    "/home",
71 23e3c9b7 Michael Hanselmann
    "/proc",
72 23e3c9b7 Michael Hanselmann
    "/root",
73 23e3c9b7 Michael Hanselmann
    "/sys",
74 23e3c9b7 Michael Hanselmann
    ])
75 23e3c9b7 Michael Hanselmann
76 23e3c9b7 Michael Hanselmann
  for prefix in ["", "/usr", "/usr/local"]:
77 23e3c9b7 Michael Hanselmann
    paths.update(map(lambda s: "%s/%s" % (prefix, s),
78 23e3c9b7 Michael Hanselmann
                     ["bin", "lib", "lib32", "lib64", "sbin"]))
79 23e3c9b7 Michael Hanselmann
80 b8028dcf Michael Hanselmann
  return compat.UniqueFrozenset(map(os.path.normpath, paths))
81 23e3c9b7 Michael Hanselmann
82 23e3c9b7 Michael Hanselmann
83 23e3c9b7 Michael Hanselmann
def _ComputeWrongFileStoragePaths(paths,
84 23e3c9b7 Michael Hanselmann
                                  _forbidden=_GetForbiddenFileStoragePaths()):
85 23e3c9b7 Michael Hanselmann
  """Cross-checks a list of paths for prefixes considered bad.
86 23e3c9b7 Michael Hanselmann

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

89 23e3c9b7 Michael Hanselmann
  @type paths: list
90 23e3c9b7 Michael Hanselmann
  @param paths: List of paths to be checked
91 23e3c9b7 Michael Hanselmann
  @rtype: list
92 23e3c9b7 Michael Hanselmann
  @return: Sorted list of paths for which the user should be warned
93 23e3c9b7 Michael Hanselmann

94 23e3c9b7 Michael Hanselmann
  """
95 23e3c9b7 Michael Hanselmann
  def _Check(path):
96 23e3c9b7 Michael Hanselmann
    return (not os.path.isabs(path) or
97 23e3c9b7 Michael Hanselmann
            path in _forbidden or
98 23e3c9b7 Michael Hanselmann
            filter(lambda p: utils.IsBelowDir(p, path), _forbidden))
99 23e3c9b7 Michael Hanselmann
100 23e3c9b7 Michael Hanselmann
  return utils.NiceSort(filter(_Check, map(os.path.normpath, paths)))
101 23e3c9b7 Michael Hanselmann
102 23e3c9b7 Michael Hanselmann
103 23e3c9b7 Michael Hanselmann
def ComputeWrongFileStoragePaths(_filename=pathutils.FILE_STORAGE_PATHS_FILE):
104 23e3c9b7 Michael Hanselmann
  """Returns a list of file storage paths whose prefix is considered bad.
105 23e3c9b7 Michael Hanselmann

106 23e3c9b7 Michael Hanselmann
  See L{_ComputeWrongFileStoragePaths}.
107 23e3c9b7 Michael Hanselmann

108 23e3c9b7 Michael Hanselmann
  """
109 23e3c9b7 Michael Hanselmann
  return _ComputeWrongFileStoragePaths(_LoadAllowedFileStoragePaths(_filename))
110 23e3c9b7 Michael Hanselmann
111 23e3c9b7 Michael Hanselmann
112 fbdac0d9 Michael Hanselmann
def _CheckFileStoragePath(path, allowed):
113 fbdac0d9 Michael Hanselmann
  """Checks if a path is in a list of allowed paths for file storage.
114 fbdac0d9 Michael Hanselmann

115 fbdac0d9 Michael Hanselmann
  @type path: string
116 fbdac0d9 Michael Hanselmann
  @param path: Path to check
117 fbdac0d9 Michael Hanselmann
  @type allowed: list
118 fbdac0d9 Michael Hanselmann
  @param allowed: List of allowed paths
119 fbdac0d9 Michael Hanselmann
  @raise errors.FileStoragePathError: If the path is not allowed
120 fbdac0d9 Michael Hanselmann

121 fbdac0d9 Michael Hanselmann
  """
122 fbdac0d9 Michael Hanselmann
  if not os.path.isabs(path):
123 fbdac0d9 Michael Hanselmann
    raise errors.FileStoragePathError("File storage path must be absolute,"
124 fbdac0d9 Michael Hanselmann
                                      " got '%s'" % path)
125 fbdac0d9 Michael Hanselmann
126 fbdac0d9 Michael Hanselmann
  for i in allowed:
127 fbdac0d9 Michael Hanselmann
    if not os.path.isabs(i):
128 fbdac0d9 Michael Hanselmann
      logging.info("Ignoring relative path '%s' for file storage", i)
129 fbdac0d9 Michael Hanselmann
      continue
130 fbdac0d9 Michael Hanselmann
131 fbdac0d9 Michael Hanselmann
    if utils.IsBelowDir(i, path):
132 fbdac0d9 Michael Hanselmann
      break
133 fbdac0d9 Michael Hanselmann
  else:
134 fbdac0d9 Michael Hanselmann
    raise errors.FileStoragePathError("Path '%s' is not acceptable for file"
135 fbdac0d9 Michael Hanselmann
                                      " storage" % path)
136 fbdac0d9 Michael Hanselmann
137 fbdac0d9 Michael Hanselmann
138 23e3c9b7 Michael Hanselmann
def _LoadAllowedFileStoragePaths(filename):
139 fbdac0d9 Michael Hanselmann
  """Loads file containing allowed file storage paths.
140 fbdac0d9 Michael Hanselmann

141 fbdac0d9 Michael Hanselmann
  @rtype: list
142 fbdac0d9 Michael Hanselmann
  @return: List of allowed paths (can be an empty list)
143 fbdac0d9 Michael Hanselmann

144 fbdac0d9 Michael Hanselmann
  """
145 fbdac0d9 Michael Hanselmann
  try:
146 fbdac0d9 Michael Hanselmann
    contents = utils.ReadFile(filename)
147 fbdac0d9 Michael Hanselmann
  except EnvironmentError:
148 fbdac0d9 Michael Hanselmann
    return []
149 fbdac0d9 Michael Hanselmann
  else:
150 fbdac0d9 Michael Hanselmann
    return utils.FilterEmptyLinesAndComments(contents)
151 fbdac0d9 Michael Hanselmann
152 fbdac0d9 Michael Hanselmann
153 fbdac0d9 Michael Hanselmann
def CheckFileStoragePath(path, _filename=pathutils.FILE_STORAGE_PATHS_FILE):
154 fbdac0d9 Michael Hanselmann
  """Checks if a path is allowed for file storage.
155 fbdac0d9 Michael Hanselmann

156 fbdac0d9 Michael Hanselmann
  @type path: string
157 fbdac0d9 Michael Hanselmann
  @param path: Path to check
158 fbdac0d9 Michael Hanselmann
  @raise errors.FileStoragePathError: If the path is not allowed
159 fbdac0d9 Michael Hanselmann

160 fbdac0d9 Michael Hanselmann
  """
161 23e3c9b7 Michael Hanselmann
  allowed = _LoadAllowedFileStoragePaths(_filename)
162 23e3c9b7 Michael Hanselmann
163 23e3c9b7 Michael Hanselmann
  if _ComputeWrongFileStoragePaths([path]):
164 23e3c9b7 Michael Hanselmann
    raise errors.FileStoragePathError("Path '%s' uses a forbidden prefix" %
165 23e3c9b7 Michael Hanselmann
                                      path)
166 23e3c9b7 Michael Hanselmann
167 23e3c9b7 Michael Hanselmann
  _CheckFileStoragePath(path, allowed)
168 fbdac0d9 Michael Hanselmann
169 fbdac0d9 Michael Hanselmann
170 89ff748d Thomas Thrainer
class LogicalVolume(base.BlockDev):
171 a8083063 Iustin Pop
  """Logical Volume block device.
172 a8083063 Iustin Pop

173 a8083063 Iustin Pop
  """
174 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
175 b5d48e87 Bernardo Dal Seno
  _PARSE_PV_DEV_RE = re.compile("^([^ ()]+)\([0-9]+\)$")
176 b8028dcf Michael Hanselmann
  _INVALID_NAMES = compat.UniqueFrozenset([".", "..", "snapshot", "pvmove"])
177 b8028dcf Michael Hanselmann
  _INVALID_SUBSTRINGS = compat.UniqueFrozenset(["_mlog", "_mimage"])
178 6136f8f0 Iustin Pop
179 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
180 a8083063 Iustin Pop
    """Attaches to a LV device.
181 a8083063 Iustin Pop

182 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
183 a8083063 Iustin Pop

184 a8083063 Iustin Pop
    """
185 94dcbdb0 Andrea Spadaccini
    super(LogicalVolume, self).__init__(unique_id, children, size, params)
186 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
187 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
188 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
189 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
190 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
191 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
192 99e8295c Iustin Pop
    self._degraded = True
193 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
194 b5d48e87 Bernardo Dal Seno
    self.pv_names = None
195 a8083063 Iustin Pop
    self.Attach()
196 a8083063 Iustin Pop
197 63c73073 Bernardo Dal Seno
  @staticmethod
198 63c73073 Bernardo Dal Seno
  def _GetStdPvSize(pvs_info):
199 63c73073 Bernardo Dal Seno
    """Return the the standard PV size (used with exclusive storage).
200 63c73073 Bernardo Dal Seno

201 63c73073 Bernardo Dal Seno
    @param pvs_info: list of objects.LvmPvInfo, cannot be empty
202 63c73073 Bernardo Dal Seno
    @rtype: float
203 63c73073 Bernardo Dal Seno
    @return: size in MiB
204 63c73073 Bernardo Dal Seno

205 63c73073 Bernardo Dal Seno
    """
206 63c73073 Bernardo Dal Seno
    assert len(pvs_info) > 0
207 63c73073 Bernardo Dal Seno
    smallest = min([pv.size for pv in pvs_info])
208 63c73073 Bernardo Dal Seno
    return smallest / (1 + constants.PART_MARGIN + constants.PART_RESERVED)
209 63c73073 Bernardo Dal Seno
210 63c73073 Bernardo Dal Seno
  @staticmethod
211 63c73073 Bernardo Dal Seno
  def _ComputeNumPvs(size, pvs_info):
212 63c73073 Bernardo Dal Seno
    """Compute the number of PVs needed for an LV (with exclusive storage).
213 63c73073 Bernardo Dal Seno

214 63c73073 Bernardo Dal Seno
    @type size: float
215 23d95cff Bernardo Dal Seno
    @param size: LV size in MiB
216 63c73073 Bernardo Dal Seno
    @param pvs_info: list of objects.LvmPvInfo, cannot be empty
217 63c73073 Bernardo Dal Seno
    @rtype: integer
218 63c73073 Bernardo Dal Seno
    @return: number of PVs needed
219 63c73073 Bernardo Dal Seno
    """
220 63c73073 Bernardo Dal Seno
    assert len(pvs_info) > 0
221 63c73073 Bernardo Dal Seno
    pv_size = float(LogicalVolume._GetStdPvSize(pvs_info))
222 63c73073 Bernardo Dal Seno
    return int(math.ceil(float(size) / pv_size))
223 63c73073 Bernardo Dal Seno
224 63c73073 Bernardo Dal Seno
  @staticmethod
225 63c73073 Bernardo Dal Seno
  def _GetEmptyPvNames(pvs_info, max_pvs=None):
226 63c73073 Bernardo Dal Seno
    """Return a list of empty PVs, by name.
227 63c73073 Bernardo Dal Seno

228 63c73073 Bernardo Dal Seno
    """
229 63c73073 Bernardo Dal Seno
    empty_pvs = filter(objects.LvmPvInfo.IsEmpty, pvs_info)
230 63c73073 Bernardo Dal Seno
    if max_pvs is not None:
231 63c73073 Bernardo Dal Seno
      empty_pvs = empty_pvs[:max_pvs]
232 63c73073 Bernardo Dal Seno
    return map((lambda pv: pv.name), empty_pvs)
233 63c73073 Bernardo Dal Seno
234 a8083063 Iustin Pop
  @classmethod
235 24c06acb Bernardo Dal Seno
  def Create(cls, unique_id, children, size, spindles, params, excl_stor):
236 a8083063 Iustin Pop
    """Create a new logical volume.
237 a8083063 Iustin Pop

238 a8083063 Iustin Pop
    """
239 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
240 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
241 6c626518 Iustin Pop
                                   str(unique_id))
242 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
243 6136f8f0 Iustin Pop
    cls._ValidateName(vg_name)
244 6136f8f0 Iustin Pop
    cls._ValidateName(lv_name)
245 2070598f Iustin Pop
    pvs_info = cls.GetPVInfo([vg_name])
246 a8083063 Iustin Pop
    if not pvs_info:
247 63c73073 Bernardo Dal Seno
      if excl_stor:
248 63c73073 Bernardo Dal Seno
        msg = "No (empty) PVs found"
249 63c73073 Bernardo Dal Seno
      else:
250 63c73073 Bernardo Dal Seno
        msg = "Can't compute PV info for vg %s" % vg_name
251 89ff748d Thomas Thrainer
      base.ThrowError(msg)
252 59726e15 Bernardo Dal Seno
    pvs_info.sort(key=(lambda pv: pv.free), reverse=True)
253 5b7b5d49 Guido Trotter
254 59726e15 Bernardo Dal Seno
    pvlist = [pv.name for pv in pvs_info]
255 403f5172 Guido Trotter
    if compat.any(":" in v for v in pvlist):
256 89ff748d Thomas Thrainer
      base.ThrowError("Some of your PVs have the invalid character ':' in their"
257 89ff748d Thomas Thrainer
                      " name, this is not supported - please filter them out"
258 89ff748d Thomas Thrainer
                      " in lvm.conf using either 'filter' or 'preferred_names'")
259 63c73073 Bernardo Dal Seno
260 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
261 ac00bf1b Andrea Spadaccini
    desired_stripes = params[constants.LDP_STRIPES]
262 43e11798 Andrea Spadaccini
    stripes = min(current_pvs, desired_stripes)
263 63c73073 Bernardo Dal Seno
264 63c73073 Bernardo Dal Seno
    if excl_stor:
265 7c848a6a Bernardo Dal Seno
      if spindles is None:
266 7c848a6a Bernardo Dal Seno
        base.ThrowError("Unspecified number of spindles: this is required"
267 7c848a6a Bernardo Dal Seno
                        "when exclusive storage is enabled, try running"
268 7c848a6a Bernardo Dal Seno
                        " gnt-cluster repair-disk-sizes")
269 11064155 Bernardo Dal Seno
      (err_msgs, _) = utils.LvmExclusiveCheckNodePvs(pvs_info)
270 63c73073 Bernardo Dal Seno
      if err_msgs:
271 63c73073 Bernardo Dal Seno
        for m in err_msgs:
272 63c73073 Bernardo Dal Seno
          logging.warning(m)
273 63c73073 Bernardo Dal Seno
      req_pvs = cls._ComputeNumPvs(size, pvs_info)
274 7c848a6a Bernardo Dal Seno
      if spindles < req_pvs:
275 7c848a6a Bernardo Dal Seno
        base.ThrowError("Requested number of spindles (%s) is not enough for"
276 7c848a6a Bernardo Dal Seno
                        " a disk of %d MB (at least %d spindles needed)",
277 7c848a6a Bernardo Dal Seno
                        spindles, size, req_pvs)
278 7c848a6a Bernardo Dal Seno
      else:
279 7c848a6a Bernardo Dal Seno
        req_pvs = spindles
280 63c73073 Bernardo Dal Seno
      pvlist = cls._GetEmptyPvNames(pvs_info, req_pvs)
281 63c73073 Bernardo Dal Seno
      current_pvs = len(pvlist)
282 63c73073 Bernardo Dal Seno
      if current_pvs < req_pvs:
283 24c06acb Bernardo Dal Seno
        base.ThrowError("Not enough empty PVs (spindles) to create a disk of %d"
284 24c06acb Bernardo Dal Seno
                        " MB: %d available, %d needed",
285 24c06acb Bernardo Dal Seno
                        size, current_pvs, req_pvs)
286 63c73073 Bernardo Dal Seno
      assert current_pvs == len(pvlist)
287 577edf04 Bernardo Dal Seno
      # We must update stripes to be sure to use all the desired spindles
288 577edf04 Bernardo Dal Seno
      stripes = current_pvs
289 577edf04 Bernardo Dal Seno
      if stripes > desired_stripes:
290 577edf04 Bernardo Dal Seno
        # Don't warn when lowering stripes, as it's no surprise
291 577edf04 Bernardo Dal Seno
        logging.warning("Using %s stripes instead of %s, to be able to use"
292 577edf04 Bernardo Dal Seno
                        " %s spindles", stripes, desired_stripes, current_pvs)
293 63c73073 Bernardo Dal Seno
294 63c73073 Bernardo Dal Seno
    else:
295 63c73073 Bernardo Dal Seno
      if stripes < desired_stripes:
296 63c73073 Bernardo Dal Seno
        logging.warning("Could not use %d stripes for VG %s, as only %d PVs are"
297 63c73073 Bernardo Dal Seno
                        " available.", desired_stripes, vg_name, current_pvs)
298 63c73073 Bernardo Dal Seno
      free_size = sum([pv.free for pv in pvs_info])
299 63c73073 Bernardo Dal Seno
      # The size constraint should have been checked from the master before
300 63c73073 Bernardo Dal Seno
      # calling the create function.
301 63c73073 Bernardo Dal Seno
      if free_size < size:
302 89ff748d Thomas Thrainer
        base.ThrowError("Not enough free space: required %s,"
303 89ff748d Thomas Thrainer
                        " available %s", size, free_size)
304 63c73073 Bernardo Dal Seno
305 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
306 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
307 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
308 fecbe9d5 Iustin Pop
    # stripes
309 63c73073 Bernardo Dal Seno
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
310 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
311 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
312 fecbe9d5 Iustin Pop
      if not result.failed:
313 fecbe9d5 Iustin Pop
        break
314 a8083063 Iustin Pop
    if result.failed:
315 89ff748d Thomas Thrainer
      base.ThrowError("LV create failed (%s): %s",
316 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
317 94dcbdb0 Andrea Spadaccini
    return LogicalVolume(unique_id, children, size, params)
318 a8083063 Iustin Pop
319 a8083063 Iustin Pop
  @staticmethod
320 197478f2 René Nussbaumer
  def _GetVolumeInfo(lvm_cmd, fields):
321 511e00b8 Helga Velroyen
    """Returns LVM Volume infos using lvm_cmd
322 197478f2 René Nussbaumer

323 197478f2 René Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
324 197478f2 René Nussbaumer
    @param fields: Fields to return
325 197478f2 René Nussbaumer
    @return: A list of dicts each with the parsed fields
326 197478f2 René Nussbaumer

327 197478f2 René Nussbaumer
    """
328 197478f2 René Nussbaumer
    if not fields:
329 197478f2 René Nussbaumer
      raise errors.ProgrammerError("No fields specified")
330 197478f2 René Nussbaumer
331 197478f2 René Nussbaumer
    sep = "|"
332 197478f2 René Nussbaumer
    cmd = [lvm_cmd, "--noheadings", "--nosuffix", "--units=m", "--unbuffered",
333 197478f2 René Nussbaumer
           "--separator=%s" % sep, "-o%s" % ",".join(fields)]
334 197478f2 René Nussbaumer
335 197478f2 René Nussbaumer
    result = utils.RunCmd(cmd)
336 197478f2 René Nussbaumer
    if result.failed:
337 197478f2 René Nussbaumer
      raise errors.CommandError("Can't get the volume information: %s - %s" %
338 197478f2 René Nussbaumer
                                (result.fail_reason, result.output))
339 197478f2 René Nussbaumer
340 197478f2 René Nussbaumer
    data = []
341 197478f2 René Nussbaumer
    for line in result.stdout.splitlines():
342 197478f2 René Nussbaumer
      splitted_fields = line.strip().split(sep)
343 197478f2 René Nussbaumer
344 197478f2 René Nussbaumer
      if len(fields) != len(splitted_fields):
345 197478f2 René Nussbaumer
        raise errors.CommandError("Can't parse %s output: line '%s'" %
346 197478f2 René Nussbaumer
                                  (lvm_cmd, line))
347 197478f2 René Nussbaumer
348 197478f2 René Nussbaumer
      data.append(splitted_fields)
349 197478f2 René Nussbaumer
350 197478f2 René Nussbaumer
    return data
351 197478f2 René Nussbaumer
352 197478f2 René Nussbaumer
  @classmethod
353 b496abdb Bernardo Dal Seno
  def GetPVInfo(cls, vg_names, filter_allocatable=True, include_lvs=False):
354 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
355 a8083063 Iustin Pop

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

360 c41eea6e Iustin Pop
    @rtype: list
361 59726e15 Bernardo Dal Seno
    @return: list of objects.LvmPvInfo objects
362 098c0958 Michael Hanselmann

363 a8083063 Iustin Pop
    """
364 b496abdb Bernardo Dal Seno
    # We request "lv_name" field only if we care about LVs, so we don't get
365 b496abdb Bernardo Dal Seno
    # a long list of entries with many duplicates unless we really have to.
366 b496abdb Bernardo Dal Seno
    # The duplicate "pv_name" field will be ignored.
367 b496abdb Bernardo Dal Seno
    if include_lvs:
368 b496abdb Bernardo Dal Seno
      lvfield = "lv_name"
369 b496abdb Bernardo Dal Seno
    else:
370 b496abdb Bernardo Dal Seno
      lvfield = "pv_name"
371 197478f2 René Nussbaumer
    try:
372 197478f2 René Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
373 b496abdb Bernardo Dal Seno
                                        "pv_attr", "pv_size", lvfield])
374 197478f2 René Nussbaumer
    except errors.GenericError, err:
375 197478f2 René Nussbaumer
      logging.error("Can't get PV information: %s", err)
376 a8083063 Iustin Pop
      return None
377 197478f2 René Nussbaumer
378 b496abdb Bernardo Dal Seno
    # When asked for LVs, "pvs" may return multiple entries for the same PV-LV
379 b496abdb Bernardo Dal Seno
    # pair. We sort entries by PV name and then LV name, so it's easy to weed
380 b496abdb Bernardo Dal Seno
    # out duplicates.
381 b496abdb Bernardo Dal Seno
    if include_lvs:
382 b496abdb Bernardo Dal Seno
      info.sort(key=(lambda i: (i[0], i[5])))
383 a8083063 Iustin Pop
    data = []
384 b496abdb Bernardo Dal Seno
    lastpvi = None
385 b496abdb Bernardo Dal Seno
    for (pv_name, vg_name, pv_free, pv_attr, pv_size, lv_name) in info:
386 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
387 197478f2 René Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
388 a8083063 Iustin Pop
        continue
389 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
390 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
391 2070598f Iustin Pop
        continue
392 b496abdb Bernardo Dal Seno
      # Beware of duplicates (check before inserting)
393 b496abdb Bernardo Dal Seno
      if lastpvi and lastpvi.name == pv_name:
394 b496abdb Bernardo Dal Seno
        if include_lvs and lv_name:
395 b496abdb Bernardo Dal Seno
          if not lastpvi.lv_list or lastpvi.lv_list[-1] != lv_name:
396 b496abdb Bernardo Dal Seno
            lastpvi.lv_list.append(lv_name)
397 b496abdb Bernardo Dal Seno
      else:
398 b496abdb Bernardo Dal Seno
        if include_lvs and lv_name:
399 b496abdb Bernardo Dal Seno
          lvl = [lv_name]
400 b496abdb Bernardo Dal Seno
        else:
401 b496abdb Bernardo Dal Seno
          lvl = []
402 b496abdb Bernardo Dal Seno
        lastpvi = objects.LvmPvInfo(name=pv_name, vg_name=vg_name,
403 b496abdb Bernardo Dal Seno
                                    size=float(pv_size), free=float(pv_free),
404 b496abdb Bernardo Dal Seno
                                    attributes=pv_attr, lv_list=lvl)
405 b496abdb Bernardo Dal Seno
        data.append(lastpvi)
406 197478f2 René Nussbaumer
407 197478f2 René Nussbaumer
    return data
408 197478f2 René Nussbaumer
409 197478f2 René Nussbaumer
  @classmethod
410 a1860404 Bernardo Dal Seno
  def _GetRawFreePvInfo(cls, vg_name):
411 a1860404 Bernardo Dal Seno
    """Return info (size/free) about PVs.
412 a1860404 Bernardo Dal Seno

413 a1860404 Bernardo Dal Seno
    @type vg_name: string
414 a1860404 Bernardo Dal Seno
    @param vg_name: VG name
415 a1860404 Bernardo Dal Seno
    @rtype: tuple
416 a1860404 Bernardo Dal Seno
    @return: (standard_pv_size_in_MiB, number_of_free_pvs, total_number_of_pvs)
417 a1860404 Bernardo Dal Seno

418 a1860404 Bernardo Dal Seno
    """
419 a1860404 Bernardo Dal Seno
    pvs_info = cls.GetPVInfo([vg_name])
420 a1860404 Bernardo Dal Seno
    if not pvs_info:
421 a1860404 Bernardo Dal Seno
      pv_size = 0.0
422 a1860404 Bernardo Dal Seno
      free_pvs = 0
423 a1860404 Bernardo Dal Seno
      num_pvs = 0
424 a1860404 Bernardo Dal Seno
    else:
425 a1860404 Bernardo Dal Seno
      pv_size = cls._GetStdPvSize(pvs_info)
426 a1860404 Bernardo Dal Seno
      free_pvs = len(cls._GetEmptyPvNames(pvs_info))
427 a1860404 Bernardo Dal Seno
      num_pvs = len(pvs_info)
428 a1860404 Bernardo Dal Seno
    return (pv_size, free_pvs, num_pvs)
429 a1860404 Bernardo Dal Seno
430 a1860404 Bernardo Dal Seno
  @classmethod
431 61481c52 Bernardo Dal Seno
  def _GetExclusiveStorageVgFree(cls, vg_name):
432 61481c52 Bernardo Dal Seno
    """Return the free disk space in the given VG, in exclusive storage mode.
433 61481c52 Bernardo Dal Seno

434 61481c52 Bernardo Dal Seno
    @type vg_name: string
435 61481c52 Bernardo Dal Seno
    @param vg_name: VG name
436 61481c52 Bernardo Dal Seno
    @rtype: float
437 61481c52 Bernardo Dal Seno
    @return: free space in MiB
438 61481c52 Bernardo Dal Seno
    """
439 a1860404 Bernardo Dal Seno
    (pv_size, free_pvs, _) = cls._GetRawFreePvInfo(vg_name)
440 a1860404 Bernardo Dal Seno
    return pv_size * free_pvs
441 a1860404 Bernardo Dal Seno
442 a1860404 Bernardo Dal Seno
  @classmethod
443 a1860404 Bernardo Dal Seno
  def GetVgSpindlesInfo(cls, vg_name):
444 a1860404 Bernardo Dal Seno
    """Get the free space info for specific VGs.
445 a1860404 Bernardo Dal Seno

446 a1860404 Bernardo Dal Seno
    @param vg_name: volume group name
447 a1860404 Bernardo Dal Seno
    @rtype: tuple
448 a1860404 Bernardo Dal Seno
    @return: (free_spindles, total_spindles)
449 a1860404 Bernardo Dal Seno

450 a1860404 Bernardo Dal Seno
    """
451 a1860404 Bernardo Dal Seno
    (_, free_pvs, num_pvs) = cls._GetRawFreePvInfo(vg_name)
452 a1860404 Bernardo Dal Seno
    return (free_pvs, num_pvs)
453 61481c52 Bernardo Dal Seno
454 61481c52 Bernardo Dal Seno
  @classmethod
455 1a3c5d4e Bernardo Dal Seno
  def GetVGInfo(cls, vg_names, excl_stor, filter_readonly=True):
456 197478f2 René Nussbaumer
    """Get the free space info for specific VGs.
457 197478f2 René Nussbaumer

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

462 197478f2 René Nussbaumer
    @rtype: list
463 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
464 673cd9c4 René Nussbaumer
             MiB
465 197478f2 René Nussbaumer

466 197478f2 René Nussbaumer
    """
467 197478f2 René Nussbaumer
    try:
468 673cd9c4 René Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
469 673cd9c4 René Nussbaumer
                                        "vg_size"])
470 197478f2 René Nussbaumer
    except errors.GenericError, err:
471 197478f2 René Nussbaumer
      logging.error("Can't get VG information: %s", err)
472 197478f2 René Nussbaumer
      return None
473 197478f2 René Nussbaumer
474 197478f2 René Nussbaumer
    data = []
475 673cd9c4 René Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
476 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not writable
477 197478f2 René Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
478 197478f2 René Nussbaumer
        continue
479 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
480 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
481 197478f2 René Nussbaumer
        continue
482 61481c52 Bernardo Dal Seno
      # Exclusive storage needs a different concept of free space
483 61481c52 Bernardo Dal Seno
      if excl_stor:
484 61481c52 Bernardo Dal Seno
        es_free = cls._GetExclusiveStorageVgFree(vg_name)
485 61481c52 Bernardo Dal Seno
        assert es_free <= vg_free
486 61481c52 Bernardo Dal Seno
        vg_free = es_free
487 673cd9c4 René Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
488 a8083063 Iustin Pop
489 a8083063 Iustin Pop
    return data
490 a8083063 Iustin Pop
491 6136f8f0 Iustin Pop
  @classmethod
492 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
493 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
494 6136f8f0 Iustin Pop

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

499 6136f8f0 Iustin Pop
    """
500 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
501 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
502 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
503 89ff748d Thomas Thrainer
      base.ThrowError("Invalid LVM name '%s'", name)
504 6136f8f0 Iustin Pop
505 a8083063 Iustin Pop
  def Remove(self):
506 a8083063 Iustin Pop
    """Remove this logical volume.
507 a8083063 Iustin Pop

508 a8083063 Iustin Pop
    """
509 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
510 a8083063 Iustin Pop
      # the LV does not exist
511 0c6c04ec Iustin Pop
      return
512 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
513 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
514 a8083063 Iustin Pop
    if result.failed:
515 89ff748d Thomas Thrainer
      base.ThrowError("Can't lvremove: %s - %s",
516 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
517 a8083063 Iustin Pop
518 f3e513ad Iustin Pop
  def Rename(self, new_id):
519 f3e513ad Iustin Pop
    """Rename this logical volume.
520 f3e513ad Iustin Pop

521 f3e513ad Iustin Pop
    """
522 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
523 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
524 f3e513ad Iustin Pop
    new_vg, new_name = new_id
525 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
526 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
527 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
528 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
529 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
530 f3e513ad Iustin Pop
    if result.failed:
531 89ff748d Thomas Thrainer
      base.ThrowError("Failed to rename the logical volume: %s", result.output)
532 be345db0 Iustin Pop
    self._lv_name = new_name
533 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
534 be345db0 Iustin Pop
535 688b5752 Bernardo Dal Seno
  @classmethod
536 688b5752 Bernardo Dal Seno
  def _ParseLvInfoLine(cls, line, sep):
537 688b5752 Bernardo Dal Seno
    """Parse one line of the lvs output used in L{_GetLvInfo}.
538 a8083063 Iustin Pop

539 a8083063 Iustin Pop
    """
540 688b5752 Bernardo Dal Seno
    elems = line.strip().rstrip(sep).split(sep)
541 b5d48e87 Bernardo Dal Seno
    if len(elems) != 6:
542 b5d48e87 Bernardo Dal Seno
      base.ThrowError("Can't parse LVS output, len(%s) != 6", str(elems))
543 99e8295c Iustin Pop
544 b5d48e87 Bernardo Dal Seno
    (status, major, minor, pe_size, stripes, pvs) = elems
545 0304f0ec Iustin Pop
    if len(status) < 6:
546 688b5752 Bernardo Dal Seno
      base.ThrowError("lvs lv_attr is not at least 6 characters (%s)", status)
547 99e8295c Iustin Pop
548 99e8295c Iustin Pop
    try:
549 99e8295c Iustin Pop
      major = int(major)
550 99e8295c Iustin Pop
      minor = int(minor)
551 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
552 688b5752 Bernardo Dal Seno
      base.ThrowError("lvs major/minor cannot be parsed: %s", str(err))
553 99e8295c Iustin Pop
554 38256320 Iustin Pop
    try:
555 38256320 Iustin Pop
      pe_size = int(float(pe_size))
556 38256320 Iustin Pop
    except (TypeError, ValueError), err:
557 688b5752 Bernardo Dal Seno
      base.ThrowError("Can't parse vg extent size: %s", err)
558 38256320 Iustin Pop
559 38256320 Iustin Pop
    try:
560 38256320 Iustin Pop
      stripes = int(stripes)
561 38256320 Iustin Pop
    except (TypeError, ValueError), err:
562 688b5752 Bernardo Dal Seno
      base.ThrowError("Can't parse the number of stripes: %s", err)
563 688b5752 Bernardo Dal Seno
564 b5d48e87 Bernardo Dal Seno
    pv_names = []
565 b5d48e87 Bernardo Dal Seno
    for pv in pvs.split(","):
566 b5d48e87 Bernardo Dal Seno
      m = re.match(cls._PARSE_PV_DEV_RE, pv)
567 b5d48e87 Bernardo Dal Seno
      if not m:
568 b5d48e87 Bernardo Dal Seno
        base.ThrowError("Can't parse this device list: %s", pvs)
569 b5d48e87 Bernardo Dal Seno
      pv_names.append(m.group(1))
570 b5d48e87 Bernardo Dal Seno
    assert len(pv_names) > 0
571 b5d48e87 Bernardo Dal Seno
572 b5d48e87 Bernardo Dal Seno
    return (status, major, minor, pe_size, stripes, pv_names)
573 688b5752 Bernardo Dal Seno
574 688b5752 Bernardo Dal Seno
  @classmethod
575 688b5752 Bernardo Dal Seno
  def _GetLvInfo(cls, dev_path, _run_cmd=utils.RunCmd):
576 688b5752 Bernardo Dal Seno
    """Get info about the given existing LV to be used.
577 688b5752 Bernardo Dal Seno

578 688b5752 Bernardo Dal Seno
    """
579 b5d48e87 Bernardo Dal Seno
    sep = "|"
580 b5d48e87 Bernardo Dal Seno
    result = _run_cmd(["lvs", "--noheadings", "--separator=%s" % sep,
581 688b5752 Bernardo Dal Seno
                       "--units=k", "--nosuffix",
582 688b5752 Bernardo Dal Seno
                       "-olv_attr,lv_kernel_major,lv_kernel_minor,"
583 b5d48e87 Bernardo Dal Seno
                       "vg_extent_size,stripes,devices", dev_path])
584 688b5752 Bernardo Dal Seno
    if result.failed:
585 688b5752 Bernardo Dal Seno
      base.ThrowError("Can't find LV %s: %s, %s",
586 688b5752 Bernardo Dal Seno
                      dev_path, result.fail_reason, result.output)
587 688b5752 Bernardo Dal Seno
    # the output can (and will) have multiple lines for multi-segment
588 688b5752 Bernardo Dal Seno
    # LVs, as the 'stripes' parameter is a segment one, so we take
589 688b5752 Bernardo Dal Seno
    # only the last entry, which is the one we're interested in; note
590 688b5752 Bernardo Dal Seno
    # that with LVM2 anyway the 'stripes' value must be constant
591 688b5752 Bernardo Dal Seno
    # across segments, so this is a no-op actually
592 688b5752 Bernardo Dal Seno
    out = result.stdout.splitlines()
593 688b5752 Bernardo Dal Seno
    if not out: # totally empty result? splitlines() returns at least
594 688b5752 Bernardo Dal Seno
                # one line for any non-empty string
595 688b5752 Bernardo Dal Seno
      base.ThrowError("Can't parse LVS output, no lines? Got '%s'", str(out))
596 b5d48e87 Bernardo Dal Seno
    pv_names = set()
597 b5d48e87 Bernardo Dal Seno
    for line in out:
598 b5d48e87 Bernardo Dal Seno
      (status, major, minor, pe_size, stripes, more_pvs) = \
599 b5d48e87 Bernardo Dal Seno
        cls._ParseLvInfoLine(line, sep)
600 b5d48e87 Bernardo Dal Seno
      pv_names.update(more_pvs)
601 b5d48e87 Bernardo Dal Seno
    return (status, major, minor, pe_size, stripes, pv_names)
602 688b5752 Bernardo Dal Seno
603 688b5752 Bernardo Dal Seno
  def Attach(self):
604 688b5752 Bernardo Dal Seno
    """Attach to an existing LV.
605 688b5752 Bernardo Dal Seno

606 688b5752 Bernardo Dal Seno
    This method will try to see if an existing and active LV exists
607 688b5752 Bernardo Dal Seno
    which matches our name. If so, its major/minor will be
608 688b5752 Bernardo Dal Seno
    recorded.
609 688b5752 Bernardo Dal Seno

610 688b5752 Bernardo Dal Seno
    """
611 688b5752 Bernardo Dal Seno
    self.attached = False
612 688b5752 Bernardo Dal Seno
    try:
613 b5d48e87 Bernardo Dal Seno
      (status, major, minor, pe_size, stripes, pv_names) = \
614 688b5752 Bernardo Dal Seno
        self._GetLvInfo(self.dev_path)
615 688b5752 Bernardo Dal Seno
    except errors.BlockDeviceError:
616 38256320 Iustin Pop
      return False
617 38256320 Iustin Pop
618 99e8295c Iustin Pop
    self.major = major
619 99e8295c Iustin Pop
    self.minor = minor
620 38256320 Iustin Pop
    self.pe_size = pe_size
621 38256320 Iustin Pop
    self.stripe_count = stripes
622 d0c8c01d Iustin Pop
    self._degraded = status[0] == "v" # virtual volume, i.e. doesn't backing
623 99e8295c Iustin Pop
                                      # storage
624 b5d48e87 Bernardo Dal Seno
    self.pv_names = pv_names
625 cb999543 Iustin Pop
    self.attached = True
626 99e8295c Iustin Pop
    return True
627 a8083063 Iustin Pop
628 a8083063 Iustin Pop
  def Assemble(self):
629 a8083063 Iustin Pop
    """Assemble the device.
630 a8083063 Iustin Pop

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

635 a8083063 Iustin Pop
    """
636 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
637 5574047a Iustin Pop
    if result.failed:
638 89ff748d Thomas Thrainer
      base.ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
639 a8083063 Iustin Pop
640 a8083063 Iustin Pop
  def Shutdown(self):
641 a8083063 Iustin Pop
    """Shutdown the device.
642 a8083063 Iustin Pop

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

646 a8083063 Iustin Pop
    """
647 746f7476 Iustin Pop
    pass
648 a8083063 Iustin Pop
649 9db6dbce Iustin Pop
  def GetSyncStatus(self):
650 9db6dbce Iustin Pop
    """Returns the sync status of the device.
651 9db6dbce Iustin Pop

652 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
653 9db6dbce Iustin Pop
    status of the mirror.
654 9db6dbce Iustin Pop

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

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

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

668 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
669 c41eea6e Iustin Pop

670 9db6dbce Iustin Pop
    """
671 f208978a Michael Hanselmann
    if self._degraded:
672 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
673 f208978a Michael Hanselmann
    else:
674 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
675 f208978a Michael Hanselmann
676 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
677 96acbc09 Michael Hanselmann
                                  major=self.major,
678 96acbc09 Michael Hanselmann
                                  minor=self.minor,
679 96acbc09 Michael Hanselmann
                                  sync_percent=None,
680 96acbc09 Michael Hanselmann
                                  estimated_time=None,
681 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
682 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
683 9db6dbce Iustin Pop
684 a8083063 Iustin Pop
  def Open(self, force=False):
685 a8083063 Iustin Pop
    """Make the device ready for I/O.
686 a8083063 Iustin Pop

687 a8083063 Iustin Pop
    This is a no-op for the LV device type.
688 a8083063 Iustin Pop

689 a8083063 Iustin Pop
    """
690 fdbd668d Iustin Pop
    pass
691 a8083063 Iustin Pop
692 a8083063 Iustin Pop
  def Close(self):
693 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
694 a8083063 Iustin Pop

695 a8083063 Iustin Pop
    This is a no-op for the LV device type.
696 a8083063 Iustin Pop

697 a8083063 Iustin Pop
    """
698 fdbd668d Iustin Pop
    pass
699 a8083063 Iustin Pop
700 a8083063 Iustin Pop
  def Snapshot(self, size):
701 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
702 a8083063 Iustin Pop

703 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
704 800ac399 Iustin Pop

705 a8083063 Iustin Pop
    """
706 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
707 a8083063 Iustin Pop
708 a8083063 Iustin Pop
    # remove existing snapshot if found
709 94dcbdb0 Andrea Spadaccini
    snap = LogicalVolume((self._vg_name, snap_name), None, size, self.params)
710 89ff748d Thomas Thrainer
    base.IgnoreError(snap.Remove)
711 a8083063 Iustin Pop
712 1a3c5d4e Bernardo Dal Seno
    vg_info = self.GetVGInfo([self._vg_name], False)
713 197478f2 René Nussbaumer
    if not vg_info:
714 89ff748d Thomas Thrainer
      base.ThrowError("Can't compute VG info for vg %s", self._vg_name)
715 673cd9c4 René Nussbaumer
    free_size, _, _ = vg_info[0]
716 a8083063 Iustin Pop
    if free_size < size:
717 89ff748d Thomas Thrainer
      base.ThrowError("Not enough free space: required %s,"
718 89ff748d Thomas Thrainer
                      " available %s", size, free_size)
719 a8083063 Iustin Pop
720 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
721 e398546b Iustin Pop
                               "-n%s" % snap_name, self.dev_path]))
722 a8083063 Iustin Pop
723 800ac399 Iustin Pop
    return (self._vg_name, snap_name)
724 a8083063 Iustin Pop
725 a1556cfa Iustin Pop
  def _RemoveOldInfo(self):
726 a1556cfa Iustin Pop
    """Try to remove old tags from the lv.
727 a1556cfa Iustin Pop

728 a1556cfa Iustin Pop
    """
729 a1556cfa Iustin Pop
    result = utils.RunCmd(["lvs", "-o", "tags", "--noheadings", "--nosuffix",
730 a1556cfa Iustin Pop
                           self.dev_path])
731 a1556cfa Iustin Pop
    _CheckResult(result)
732 a1556cfa Iustin Pop
733 a1556cfa Iustin Pop
    raw_tags = result.stdout.strip()
734 a1556cfa Iustin Pop
    if raw_tags:
735 a1556cfa Iustin Pop
      for tag in raw_tags.split(","):
736 a1556cfa Iustin Pop
        _CheckResult(utils.RunCmd(["lvchange", "--deltag",
737 a1556cfa Iustin Pop
                                   tag.strip(), self.dev_path]))
738 a1556cfa Iustin Pop
739 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
740 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
741 a0c3fea1 Michael Hanselmann

742 a0c3fea1 Michael Hanselmann
    """
743 89ff748d Thomas Thrainer
    base.BlockDev.SetInfo(self, text)
744 a0c3fea1 Michael Hanselmann
745 a1556cfa Iustin Pop
    self._RemoveOldInfo()
746 a1556cfa Iustin Pop
747 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
748 d0c8c01d Iustin Pop
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
749 d0c8c01d Iustin Pop
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
750 a0c3fea1 Michael Hanselmann
751 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
752 a0c3fea1 Michael Hanselmann
    text = text[:128]
753 a0c3fea1 Michael Hanselmann
754 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvchange", "--addtag", text, self.dev_path]))
755 82463074 Iustin Pop
756 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
757 1005d816 Iustin Pop
    """Grow the logical volume.
758 1005d816 Iustin Pop

759 1005d816 Iustin Pop
    """
760 cad0723b Iustin Pop
    if not backingstore:
761 cad0723b Iustin Pop
      return
762 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
763 38256320 Iustin Pop
      if not self.Attach():
764 89ff748d Thomas Thrainer
        base.ThrowError("Can't attach to LV during Grow()")
765 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
766 5519f036 Bernardo Dal Seno
    # pe_size is in KB
767 5519f036 Bernardo Dal Seno
    amount *= 1024
768 38256320 Iustin Pop
    rest = amount % full_stripe_size
769 38256320 Iustin Pop
    if rest != 0:
770 38256320 Iustin Pop
      amount += full_stripe_size - rest
771 5519f036 Bernardo Dal Seno
    cmd = ["lvextend", "-L", "+%dk" % amount]
772 7fe23d47 Iustin Pop
    if dryrun:
773 7fe23d47 Iustin Pop
      cmd.append("--test")
774 be9150ea Bernardo Dal Seno
    if excl_stor:
775 be9150ea Bernardo Dal Seno
      # Disk growth doesn't grow the number of spindles, so we must stay within
776 be9150ea Bernardo Dal Seno
      # our assigned volumes
777 be9150ea Bernardo Dal Seno
      pvlist = list(self.pv_names)
778 be9150ea Bernardo Dal Seno
    else:
779 be9150ea Bernardo Dal Seno
      pvlist = []
780 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
781 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
782 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
783 1005d816 Iustin Pop
    # supports 'cling'
784 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
785 be9150ea Bernardo Dal Seno
      result = utils.RunCmd(cmd + ["--alloc", alloc_policy, self.dev_path] +
786 be9150ea Bernardo Dal Seno
                            pvlist)
787 1005d816 Iustin Pop
      if not result.failed:
788 1005d816 Iustin Pop
        return
789 89ff748d Thomas Thrainer
    base.ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
790 a8083063 Iustin Pop
791 baa7f1d6 Bernardo Dal Seno
  def GetActualSpindles(self):
792 baa7f1d6 Bernardo Dal Seno
    """Return the number of spindles used.
793 baa7f1d6 Bernardo Dal Seno

794 baa7f1d6 Bernardo Dal Seno
    """
795 baa7f1d6 Bernardo Dal Seno
    assert self.attached, "BlockDevice not attached in GetActualSpindles()"
796 baa7f1d6 Bernardo Dal Seno
    return len(self.pv_names)
797 baa7f1d6 Bernardo Dal Seno
798 a2cfdea2 Iustin Pop
799 89ff748d Thomas Thrainer
class FileStorage(base.BlockDev):
800 6f695a2e Manuel Franceschini
  """File device.
801 abdf0113 Iustin Pop

802 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
803 6f695a2e Manuel Franceschini

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

806 6f695a2e Manuel Franceschini
  """
807 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
808 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
809 6f695a2e Manuel Franceschini

810 6f695a2e Manuel Franceschini
    """
811 6f695a2e Manuel Franceschini
    if children:
812 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
813 94dcbdb0 Andrea Spadaccini
    super(FileStorage, self).__init__(unique_id, children, size, params)
814 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
815 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
816 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
817 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
818 5e09a309 Michael Hanselmann
819 5e09a309 Michael Hanselmann
    CheckFileStoragePath(self.dev_path)
820 5e09a309 Michael Hanselmann
821 ecb091e3 Iustin Pop
    self.Attach()
822 6f695a2e Manuel Franceschini
823 6f695a2e Manuel Franceschini
  def Assemble(self):
824 6f695a2e Manuel Franceschini
    """Assemble the device.
825 6f695a2e Manuel Franceschini

826 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
827 6f695a2e Manuel Franceschini

828 6f695a2e Manuel Franceschini
    """
829 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
830 89ff748d Thomas Thrainer
      base.ThrowError("File device '%s' does not exist" % self.dev_path)
831 6f695a2e Manuel Franceschini
832 6f695a2e Manuel Franceschini
  def Shutdown(self):
833 6f695a2e Manuel Franceschini
    """Shutdown the device.
834 6f695a2e Manuel Franceschini

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

838 6f695a2e Manuel Franceschini
    """
839 746f7476 Iustin Pop
    pass
840 6f695a2e Manuel Franceschini
841 6f695a2e Manuel Franceschini
  def Open(self, force=False):
842 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
843 6f695a2e Manuel Franceschini

844 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
845 6f695a2e Manuel Franceschini

846 6f695a2e Manuel Franceschini
    """
847 6f695a2e Manuel Franceschini
    pass
848 6f695a2e Manuel Franceschini
849 6f695a2e Manuel Franceschini
  def Close(self):
850 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
851 6f695a2e Manuel Franceschini

852 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
853 6f695a2e Manuel Franceschini

854 6f695a2e Manuel Franceschini
    """
855 6f695a2e Manuel Franceschini
    pass
856 6f695a2e Manuel Franceschini
857 6f695a2e Manuel Franceschini
  def Remove(self):
858 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
859 6f695a2e Manuel Franceschini

860 c41eea6e Iustin Pop
    @rtype: boolean
861 c41eea6e Iustin Pop
    @return: True if the removal was successful
862 6f695a2e Manuel Franceschini

863 6f695a2e Manuel Franceschini
    """
864 6f695a2e Manuel Franceschini
    try:
865 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
866 6f695a2e Manuel Franceschini
    except OSError, err:
867 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
868 89ff748d Thomas Thrainer
        base.ThrowError("Can't remove file '%s': %s", self.dev_path, err)
869 6f695a2e Manuel Franceschini
870 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
871 bbe4cc16 Iustin Pop
    """Renames the file.
872 bbe4cc16 Iustin Pop

873 bbe4cc16 Iustin Pop
    """
874 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
875 89ff748d Thomas Thrainer
    base.ThrowError("Rename is not supported for file-based storage")
876 bbe4cc16 Iustin Pop
877 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
878 bbe4cc16 Iustin Pop
    """Grow the file
879 bbe4cc16 Iustin Pop

880 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
881 bbe4cc16 Iustin Pop

882 bbe4cc16 Iustin Pop
    """
883 cad0723b Iustin Pop
    if not backingstore:
884 cad0723b Iustin Pop
      return
885 91e2d9ec Guido Trotter
    # Check that the file exists
886 91e2d9ec Guido Trotter
    self.Assemble()
887 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
888 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
889 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
890 7fe23d47 Iustin Pop
    # We can't really simulate the growth
891 7fe23d47 Iustin Pop
    if dryrun:
892 7fe23d47 Iustin Pop
      return
893 91e2d9ec Guido Trotter
    try:
894 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
895 91e2d9ec Guido Trotter
      f.truncate(new_size)
896 91e2d9ec Guido Trotter
      f.close()
897 91e2d9ec Guido Trotter
    except EnvironmentError, err:
898 89ff748d Thomas Thrainer
      base.ThrowError("Error in file growth: %", str(err))
899 bbe4cc16 Iustin Pop
900 6f695a2e Manuel Franceschini
  def Attach(self):
901 6f695a2e Manuel Franceschini
    """Attach to an existing file.
902 6f695a2e Manuel Franceschini

903 6f695a2e Manuel Franceschini
    Check if this file already exists.
904 6f695a2e Manuel Franceschini

905 c41eea6e Iustin Pop
    @rtype: boolean
906 c41eea6e Iustin Pop
    @return: True if file exists
907 6f695a2e Manuel Franceschini

908 6f695a2e Manuel Franceschini
    """
909 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
910 ecb091e3 Iustin Pop
    return self.attached
911 6f695a2e Manuel Franceschini
912 fcff3897 Iustin Pop
  def GetActualSize(self):
913 fcff3897 Iustin Pop
    """Return the actual disk size.
914 fcff3897 Iustin Pop

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

917 fcff3897 Iustin Pop
    """
918 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
919 fcff3897 Iustin Pop
    try:
920 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
921 fcff3897 Iustin Pop
      return st.st_size
922 fcff3897 Iustin Pop
    except OSError, err:
923 89ff748d Thomas Thrainer
      base.ThrowError("Can't stat %s: %s", self.dev_path, err)
924 fcff3897 Iustin Pop
925 6f695a2e Manuel Franceschini
  @classmethod
926 24c06acb Bernardo Dal Seno
  def Create(cls, unique_id, children, size, spindles, params, excl_stor):
927 6f695a2e Manuel Franceschini
    """Create a new file.
928 6f695a2e Manuel Franceschini

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

931 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
932 c41eea6e Iustin Pop
    @return: an instance of FileStorage
933 6f695a2e Manuel Franceschini

934 6f695a2e Manuel Franceschini
    """
935 ee1478e5 Bernardo Dal Seno
    if excl_stor:
936 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("FileStorage device requested with"
937 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
938 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
939 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
940 5e09a309 Michael Hanselmann
941 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
942 5e09a309 Michael Hanselmann
943 5e09a309 Michael Hanselmann
    CheckFileStoragePath(dev_path)
944 5e09a309 Michael Hanselmann
945 6f695a2e Manuel Franceschini
    try:
946 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
947 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
948 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
949 6f695a2e Manuel Franceschini
      f.close()
950 cdeefd9b Guido Trotter
    except EnvironmentError, err:
951 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
952 89ff748d Thomas Thrainer
        base.ThrowError("File already existing: %s", dev_path)
953 89ff748d Thomas Thrainer
      base.ThrowError("Error in file creation: %", str(err))
954 6f695a2e Manuel Franceschini
955 94dcbdb0 Andrea Spadaccini
    return FileStorage(unique_id, children, size, params)
956 6f695a2e Manuel Franceschini
957 6f695a2e Manuel Franceschini
958 89ff748d Thomas Thrainer
class PersistentBlockDevice(base.BlockDev):
959 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
960 b6135bbc Apollon Oikonomopoulos

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

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

967 b6135bbc Apollon Oikonomopoulos
  """
968 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
969 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
970 b6135bbc Apollon Oikonomopoulos

971 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
972 b6135bbc Apollon Oikonomopoulos

973 b6135bbc Apollon Oikonomopoulos
    """
974 94dcbdb0 Andrea Spadaccini
    super(PersistentBlockDevice, self).__init__(unique_id, children, size,
975 94dcbdb0 Andrea Spadaccini
                                                params)
976 b6135bbc Apollon Oikonomopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
977 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
978 b6135bbc Apollon Oikonomopoulos
    self.dev_path = unique_id[1]
979 d0c8c01d Iustin Pop
    if not os.path.realpath(self.dev_path).startswith("/dev/"):
980 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Full path '%s' lies outside /dev" %
981 b6135bbc Apollon Oikonomopoulos
                              os.path.realpath(self.dev_path))
982 b6135bbc Apollon Oikonomopoulos
    # TODO: this is just a safety guard checking that we only deal with devices
983 b6135bbc Apollon Oikonomopoulos
    # we know how to handle. In the future this will be integrated with
984 b6135bbc Apollon Oikonomopoulos
    # external storage backends and possible values will probably be collected
985 b6135bbc Apollon Oikonomopoulos
    # from the cluster configuration.
986 b6135bbc Apollon Oikonomopoulos
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
987 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Got persistent block device of invalid type: %s" %
988 b6135bbc Apollon Oikonomopoulos
                       unique_id[0])
989 b6135bbc Apollon Oikonomopoulos
990 b6135bbc Apollon Oikonomopoulos
    self.major = self.minor = None
991 b6135bbc Apollon Oikonomopoulos
    self.Attach()
992 b6135bbc Apollon Oikonomopoulos
993 b6135bbc Apollon Oikonomopoulos
  @classmethod
994 24c06acb Bernardo Dal Seno
  def Create(cls, unique_id, children, size, spindles, params, excl_stor):
995 b6135bbc Apollon Oikonomopoulos
    """Create a new device
996 b6135bbc Apollon Oikonomopoulos

997 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
998 b6135bbc Apollon Oikonomopoulos

999 b6135bbc Apollon Oikonomopoulos
    """
1000 ee1478e5 Bernardo Dal Seno
    if excl_stor:
1001 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("Persistent block device requested with"
1002 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
1003 94dcbdb0 Andrea Spadaccini
    return PersistentBlockDevice(unique_id, children, 0, params)
1004 b6135bbc Apollon Oikonomopoulos
1005 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
1006 b6135bbc Apollon Oikonomopoulos
    """Remove a device
1007 b6135bbc Apollon Oikonomopoulos

1008 b6135bbc Apollon Oikonomopoulos
    This is a noop
1009 b6135bbc Apollon Oikonomopoulos

1010 b6135bbc Apollon Oikonomopoulos
    """
1011 b6135bbc Apollon Oikonomopoulos
    pass
1012 b6135bbc Apollon Oikonomopoulos
1013 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
1014 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
1015 b6135bbc Apollon Oikonomopoulos

1016 b6135bbc Apollon Oikonomopoulos
    """
1017 89ff748d Thomas Thrainer
    base.ThrowError("Rename is not supported for PersistentBlockDev storage")
1018 b6135bbc Apollon Oikonomopoulos
1019 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
1020 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
1021 b6135bbc Apollon Oikonomopoulos

1022 b6135bbc Apollon Oikonomopoulos

1023 b6135bbc Apollon Oikonomopoulos
    """
1024 b6135bbc Apollon Oikonomopoulos
    self.attached = False
1025 b6135bbc Apollon Oikonomopoulos
    try:
1026 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
1027 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
1028 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
1029 b6135bbc Apollon Oikonomopoulos
      return False
1030 b6135bbc Apollon Oikonomopoulos
1031 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
1032 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
1033 b6135bbc Apollon Oikonomopoulos
      return False
1034 b6135bbc Apollon Oikonomopoulos
1035 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
1036 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
1037 b6135bbc Apollon Oikonomopoulos
    self.attached = True
1038 b6135bbc Apollon Oikonomopoulos
1039 b6135bbc Apollon Oikonomopoulos
    return True
1040 b6135bbc Apollon Oikonomopoulos
1041 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
1042 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
1043 b6135bbc Apollon Oikonomopoulos

1044 b6135bbc Apollon Oikonomopoulos
    """
1045 b6135bbc Apollon Oikonomopoulos
    pass
1046 b6135bbc Apollon Oikonomopoulos
1047 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
1048 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
1049 b6135bbc Apollon Oikonomopoulos

1050 b6135bbc Apollon Oikonomopoulos
    """
1051 b6135bbc Apollon Oikonomopoulos
    pass
1052 b6135bbc Apollon Oikonomopoulos
1053 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
1054 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
1055 b6135bbc Apollon Oikonomopoulos

1056 b6135bbc Apollon Oikonomopoulos
    """
1057 b6135bbc Apollon Oikonomopoulos
    pass
1058 b6135bbc Apollon Oikonomopoulos
1059 b6135bbc Apollon Oikonomopoulos
  def Close(self):
1060 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
1061 b6135bbc Apollon Oikonomopoulos

1062 b6135bbc Apollon Oikonomopoulos
    """
1063 b6135bbc Apollon Oikonomopoulos
    pass
1064 b6135bbc Apollon Oikonomopoulos
1065 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
1066 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
1067 b6135bbc Apollon Oikonomopoulos

1068 b6135bbc Apollon Oikonomopoulos
    """
1069 89ff748d Thomas Thrainer
    base.ThrowError("Grow is not supported for PersistentBlockDev storage")
1070 b6135bbc Apollon Oikonomopoulos
1071 b6135bbc Apollon Oikonomopoulos
1072 89ff748d Thomas Thrainer
class RADOSBlockDevice(base.BlockDev):
1073 7181fba0 Constantinos Venetsanopoulos
  """A RADOS Block Device (rbd).
1074 7181fba0 Constantinos Venetsanopoulos

1075 7181fba0 Constantinos Venetsanopoulos
  This class implements the RADOS Block Device for the backend. You need
1076 7181fba0 Constantinos Venetsanopoulos
  the rbd kernel driver, the RADOS Tools and a working RADOS cluster for
1077 7181fba0 Constantinos Venetsanopoulos
  this to be functional.
1078 7181fba0 Constantinos Venetsanopoulos

1079 7181fba0 Constantinos Venetsanopoulos
  """
1080 7181fba0 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
1081 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
1082 7181fba0 Constantinos Venetsanopoulos

1083 7181fba0 Constantinos Venetsanopoulos
    """
1084 7181fba0 Constantinos Venetsanopoulos
    super(RADOSBlockDevice, self).__init__(unique_id, children, size, params)
1085 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1086 7181fba0 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1087 7181fba0 Constantinos Venetsanopoulos
1088 7181fba0 Constantinos Venetsanopoulos
    self.driver, self.rbd_name = unique_id
1089 7181fba0 Constantinos Venetsanopoulos
1090 7181fba0 Constantinos Venetsanopoulos
    self.major = self.minor = None
1091 7181fba0 Constantinos Venetsanopoulos
    self.Attach()
1092 7181fba0 Constantinos Venetsanopoulos
1093 7181fba0 Constantinos Venetsanopoulos
  @classmethod
1094 24c06acb Bernardo Dal Seno
  def Create(cls, unique_id, children, size, spindles, params, excl_stor):
1095 7181fba0 Constantinos Venetsanopoulos
    """Create a new rbd device.
1096 7181fba0 Constantinos Venetsanopoulos

1097 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
1098 7181fba0 Constantinos Venetsanopoulos

1099 7181fba0 Constantinos Venetsanopoulos
    """
1100 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1101 7181fba0 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
1102 7181fba0 Constantinos Venetsanopoulos
                                   str(unique_id))
1103 ee1478e5 Bernardo Dal Seno
    if excl_stor:
1104 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("RBD device requested with"
1105 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
1106 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = params[constants.LDP_POOL]
1107 7181fba0 Constantinos Venetsanopoulos
    rbd_name = unique_id[1]
1108 7181fba0 Constantinos Venetsanopoulos
1109 7181fba0 Constantinos Venetsanopoulos
    # Provision a new rbd volume (Image) inside the RADOS cluster.
1110 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "create", "-p", rbd_pool,
1111 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % size]
1112 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
1113 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
1114 89ff748d Thomas Thrainer
      base.ThrowError("rbd creation failed (%s): %s",
1115 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
1116 7181fba0 Constantinos Venetsanopoulos
1117 7181fba0 Constantinos Venetsanopoulos
    return RADOSBlockDevice(unique_id, children, size, params)
1118 7181fba0 Constantinos Venetsanopoulos
1119 7181fba0 Constantinos Venetsanopoulos
  def Remove(self):
1120 7181fba0 Constantinos Venetsanopoulos
    """Remove the rbd device.
1121 7181fba0 Constantinos Venetsanopoulos

1122 7181fba0 Constantinos Venetsanopoulos
    """
1123 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
1124 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
1125 7181fba0 Constantinos Venetsanopoulos
1126 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1127 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
1128 7181fba0 Constantinos Venetsanopoulos
      return
1129 7181fba0 Constantinos Venetsanopoulos
1130 7181fba0 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
1131 7181fba0 Constantinos Venetsanopoulos
    self.Shutdown()
1132 7181fba0 Constantinos Venetsanopoulos
1133 7181fba0 Constantinos Venetsanopoulos
    # Remove the actual Volume (Image) from the RADOS cluster.
1134 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "rm", "-p", rbd_pool, rbd_name]
1135 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
1136 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
1137 89ff748d Thomas Thrainer
      base.ThrowError("Can't remove Volume from cluster with rbd rm: %s - %s",
1138 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
1139 7181fba0 Constantinos Venetsanopoulos
1140 7181fba0 Constantinos Venetsanopoulos
  def Rename(self, new_id):
1141 7181fba0 Constantinos Venetsanopoulos
    """Rename this device.
1142 7181fba0 Constantinos Venetsanopoulos

1143 7181fba0 Constantinos Venetsanopoulos
    """
1144 7181fba0 Constantinos Venetsanopoulos
    pass
1145 7181fba0 Constantinos Venetsanopoulos
1146 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
1147 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
1148 7181fba0 Constantinos Venetsanopoulos

1149 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
1150 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
1151 7181fba0 Constantinos Venetsanopoulos

1152 7181fba0 Constantinos Venetsanopoulos
    """
1153 7181fba0 Constantinos Venetsanopoulos
    self.attached = False
1154 7181fba0 Constantinos Venetsanopoulos
1155 7181fba0 Constantinos Venetsanopoulos
    # Map the rbd volume to a block device under /dev
1156 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = self._MapVolumeToBlockdev(self.unique_id)
1157 7181fba0 Constantinos Venetsanopoulos
1158 7181fba0 Constantinos Venetsanopoulos
    try:
1159 7181fba0 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
1160 7181fba0 Constantinos Venetsanopoulos
    except OSError, err:
1161 7181fba0 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
1162 7181fba0 Constantinos Venetsanopoulos
      return False
1163 7181fba0 Constantinos Venetsanopoulos
1164 7181fba0 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
1165 7181fba0 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
1166 7181fba0 Constantinos Venetsanopoulos
      return False
1167 7181fba0 Constantinos Venetsanopoulos
1168 7181fba0 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
1169 7181fba0 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
1170 7181fba0 Constantinos Venetsanopoulos
    self.attached = True
1171 7181fba0 Constantinos Venetsanopoulos
1172 7181fba0 Constantinos Venetsanopoulos
    return True
1173 7181fba0 Constantinos Venetsanopoulos
1174 7181fba0 Constantinos Venetsanopoulos
  def _MapVolumeToBlockdev(self, unique_id):
1175 7181fba0 Constantinos Venetsanopoulos
    """Maps existing rbd volumes to block devices.
1176 7181fba0 Constantinos Venetsanopoulos

1177 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
1178 7181fba0 Constantinos Venetsanopoulos

1179 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
1180 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
1181 7181fba0 Constantinos Venetsanopoulos

1182 7181fba0 Constantinos Venetsanopoulos
    """
1183 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
1184 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
1185 7181fba0 Constantinos Venetsanopoulos
1186 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
1187 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
1188 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
1189 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Return it.
1190 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
1191 7181fba0 Constantinos Venetsanopoulos
1192 7181fba0 Constantinos Venetsanopoulos
    # The mapping doesn't exist. Create it.
1193 7181fba0 Constantinos Venetsanopoulos
    map_cmd = [constants.RBD_CMD, "map", "-p", pool, name]
1194 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(map_cmd)
1195 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
1196 89ff748d Thomas Thrainer
      base.ThrowError("rbd map failed (%s): %s",
1197 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
1198 7181fba0 Constantinos Venetsanopoulos
1199 7181fba0 Constantinos Venetsanopoulos
    # Find the corresponding rbd device.
1200 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
1201 7181fba0 Constantinos Venetsanopoulos
    if not rbd_dev:
1202 89ff748d Thomas Thrainer
      base.ThrowError("rbd map succeeded, but could not find the rbd block"
1203 89ff748d Thomas Thrainer
                      " device in output of showmapped, for volume: %s", name)
1204 7181fba0 Constantinos Venetsanopoulos
1205 7181fba0 Constantinos Venetsanopoulos
    # The device was successfully mapped. Return it.
1206 7181fba0 Constantinos Venetsanopoulos
    return rbd_dev
1207 7181fba0 Constantinos Venetsanopoulos
1208 bdecfea2 Stratos Psomadakis
  @classmethod
1209 bdecfea2 Stratos Psomadakis
  def _VolumeToBlockdev(cls, pool, volume_name):
1210 bdecfea2 Stratos Psomadakis
    """Do the 'volume name'-to-'rbd block device' resolving.
1211 bdecfea2 Stratos Psomadakis

1212 bdecfea2 Stratos Psomadakis
    @type pool: string
1213 bdecfea2 Stratos Psomadakis
    @param pool: RADOS pool to use
1214 bdecfea2 Stratos Psomadakis
    @type volume_name: string
1215 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
1216 bdecfea2 Stratos Psomadakis
    @rtype: string or None
1217 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
1218 bdecfea2 Stratos Psomadakis

1219 bdecfea2 Stratos Psomadakis
    """
1220 bdecfea2 Stratos Psomadakis
    try:
1221 bdecfea2 Stratos Psomadakis
      # Newer versions of the rbd tool support json output formatting. Use it
1222 bdecfea2 Stratos Psomadakis
      # if available.
1223 bdecfea2 Stratos Psomadakis
      showmap_cmd = [
1224 bdecfea2 Stratos Psomadakis
        constants.RBD_CMD,
1225 bdecfea2 Stratos Psomadakis
        "showmapped",
1226 bdecfea2 Stratos Psomadakis
        "-p",
1227 bdecfea2 Stratos Psomadakis
        pool,
1228 bdecfea2 Stratos Psomadakis
        "--format",
1229 bdecfea2 Stratos Psomadakis
        "json"
1230 bdecfea2 Stratos Psomadakis
        ]
1231 bdecfea2 Stratos Psomadakis
      result = utils.RunCmd(showmap_cmd)
1232 bdecfea2 Stratos Psomadakis
      if result.failed:
1233 bdecfea2 Stratos Psomadakis
        logging.error("rbd JSON output formatting returned error (%s): %s,"
1234 bdecfea2 Stratos Psomadakis
                      "falling back to plain output parsing",
1235 bdecfea2 Stratos Psomadakis
                      result.fail_reason, result.output)
1236 bdecfea2 Stratos Psomadakis
        raise RbdShowmappedJsonError
1237 bdecfea2 Stratos Psomadakis
1238 bdecfea2 Stratos Psomadakis
      return cls._ParseRbdShowmappedJson(result.output, volume_name)
1239 bdecfea2 Stratos Psomadakis
    except RbdShowmappedJsonError:
1240 bdecfea2 Stratos Psomadakis
      # For older versions of rbd, we have to parse the plain / text output
1241 bdecfea2 Stratos Psomadakis
      # manually.
1242 bdecfea2 Stratos Psomadakis
      showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
1243 bdecfea2 Stratos Psomadakis
      result = utils.RunCmd(showmap_cmd)
1244 bdecfea2 Stratos Psomadakis
      if result.failed:
1245 89ff748d Thomas Thrainer
        base.ThrowError("rbd showmapped failed (%s): %s",
1246 89ff748d Thomas Thrainer
                        result.fail_reason, result.output)
1247 bdecfea2 Stratos Psomadakis
1248 bdecfea2 Stratos Psomadakis
      return cls._ParseRbdShowmappedPlain(result.output, volume_name)
1249 bdecfea2 Stratos Psomadakis
1250 bdecfea2 Stratos Psomadakis
  @staticmethod
1251 bdecfea2 Stratos Psomadakis
  def _ParseRbdShowmappedJson(output, volume_name):
1252 bdecfea2 Stratos Psomadakis
    """Parse the json output of `rbd showmapped'.
1253 bdecfea2 Stratos Psomadakis

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

1257 bdecfea2 Stratos Psomadakis
    @type output: string
1258 bdecfea2 Stratos Psomadakis
    @param output: the json output of `rbd showmapped'
1259 bdecfea2 Stratos Psomadakis
    @type volume_name: string
1260 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
1261 bdecfea2 Stratos Psomadakis
    @rtype: string or None
1262 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
1263 bdecfea2 Stratos Psomadakis

1264 bdecfea2 Stratos Psomadakis
    """
1265 bdecfea2 Stratos Psomadakis
    try:
1266 bdecfea2 Stratos Psomadakis
      devices = serializer.LoadJson(output)
1267 bdecfea2 Stratos Psomadakis
    except ValueError, err:
1268 89ff748d Thomas Thrainer
      base.ThrowError("Unable to parse JSON data: %s" % err)
1269 bdecfea2 Stratos Psomadakis
1270 bdecfea2 Stratos Psomadakis
    rbd_dev = None
1271 bdecfea2 Stratos Psomadakis
    for d in devices.values(): # pylint: disable=E1103
1272 bdecfea2 Stratos Psomadakis
      try:
1273 bdecfea2 Stratos Psomadakis
        name = d["name"]
1274 bdecfea2 Stratos Psomadakis
      except KeyError:
1275 89ff748d Thomas Thrainer
        base.ThrowError("'name' key missing from json object %s", devices)
1276 bdecfea2 Stratos Psomadakis
1277 bdecfea2 Stratos Psomadakis
      if name == volume_name:
1278 bdecfea2 Stratos Psomadakis
        if rbd_dev is not None:
1279 89ff748d Thomas Thrainer
          base.ThrowError("rbd volume %s is mapped more than once", volume_name)
1280 bdecfea2 Stratos Psomadakis
1281 bdecfea2 Stratos Psomadakis
        rbd_dev = d["device"]
1282 bdecfea2 Stratos Psomadakis
1283 bdecfea2 Stratos Psomadakis
    return rbd_dev
1284 bdecfea2 Stratos Psomadakis
1285 7181fba0 Constantinos Venetsanopoulos
  @staticmethod
1286 bdecfea2 Stratos Psomadakis
  def _ParseRbdShowmappedPlain(output, volume_name):
1287 bdecfea2 Stratos Psomadakis
    """Parse the (plain / text) output of `rbd showmapped'.
1288 7181fba0 Constantinos Venetsanopoulos

1289 7181fba0 Constantinos Venetsanopoulos
    This method parses the output of `rbd showmapped' and returns
1290 7181fba0 Constantinos Venetsanopoulos
    the rbd block device path (e.g. /dev/rbd0) that matches the
1291 7181fba0 Constantinos Venetsanopoulos
    given rbd volume.
1292 7181fba0 Constantinos Venetsanopoulos

1293 7181fba0 Constantinos Venetsanopoulos
    @type output: string
1294 bdecfea2 Stratos Psomadakis
    @param output: the plain text output of `rbd showmapped'
1295 7181fba0 Constantinos Venetsanopoulos
    @type volume_name: string
1296 7181fba0 Constantinos Venetsanopoulos
    @param volume_name: the name of the volume whose device we search for
1297 7181fba0 Constantinos Venetsanopoulos
    @rtype: string or None
1298 7181fba0 Constantinos Venetsanopoulos
    @return: block device path if the volume is mapped, else None
1299 7181fba0 Constantinos Venetsanopoulos

1300 7181fba0 Constantinos Venetsanopoulos
    """
1301 7181fba0 Constantinos Venetsanopoulos
    allfields = 5
1302 7181fba0 Constantinos Venetsanopoulos
    volumefield = 2
1303 7181fba0 Constantinos Venetsanopoulos
    devicefield = 4
1304 7181fba0 Constantinos Venetsanopoulos
1305 7181fba0 Constantinos Venetsanopoulos
    lines = output.splitlines()
1306 7181fba0 Constantinos Venetsanopoulos
1307 bdecfea2 Stratos Psomadakis
    # Try parsing the new output format (ceph >= 0.55).
1308 bdecfea2 Stratos Psomadakis
    splitted_lines = map(lambda l: l.split(), lines)
1309 bdecfea2 Stratos Psomadakis
1310 bdecfea2 Stratos Psomadakis
    # Check for empty output.
1311 7181fba0 Constantinos Venetsanopoulos
    if not splitted_lines:
1312 bdecfea2 Stratos Psomadakis
      return None
1313 7181fba0 Constantinos Venetsanopoulos
1314 bdecfea2 Stratos Psomadakis
    # Check showmapped output, to determine number of fields.
1315 7181fba0 Constantinos Venetsanopoulos
    field_cnt = len(splitted_lines[0])
1316 7181fba0 Constantinos Venetsanopoulos
    if field_cnt != allfields:
1317 bdecfea2 Stratos Psomadakis
      # Parsing the new format failed. Fallback to parsing the old output
1318 bdecfea2 Stratos Psomadakis
      # format (< 0.55).
1319 bdecfea2 Stratos Psomadakis
      splitted_lines = map(lambda l: l.split("\t"), lines)
1320 bdecfea2 Stratos Psomadakis
      if field_cnt != allfields:
1321 89ff748d Thomas Thrainer
        base.ThrowError("Cannot parse rbd showmapped output expected %s fields,"
1322 89ff748d Thomas Thrainer
                        " found %s", allfields, field_cnt)
1323 7181fba0 Constantinos Venetsanopoulos
1324 7181fba0 Constantinos Venetsanopoulos
    matched_lines = \
1325 7181fba0 Constantinos Venetsanopoulos
      filter(lambda l: len(l) == allfields and l[volumefield] == volume_name,
1326 7181fba0 Constantinos Venetsanopoulos
             splitted_lines)
1327 7181fba0 Constantinos Venetsanopoulos
1328 7181fba0 Constantinos Venetsanopoulos
    if len(matched_lines) > 1:
1329 89ff748d Thomas Thrainer
      base.ThrowError("rbd volume %s mapped more than once", volume_name)
1330 7181fba0 Constantinos Venetsanopoulos
1331 7181fba0 Constantinos Venetsanopoulos
    if matched_lines:
1332 7181fba0 Constantinos Venetsanopoulos
      # rbd block device found. Return it.
1333 7181fba0 Constantinos Venetsanopoulos
      rbd_dev = matched_lines[0][devicefield]
1334 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
1335 7181fba0 Constantinos Venetsanopoulos
1336 7181fba0 Constantinos Venetsanopoulos
    # The given volume is not mapped.
1337 7181fba0 Constantinos Venetsanopoulos
    return None
1338 7181fba0 Constantinos Venetsanopoulos
1339 7181fba0 Constantinos Venetsanopoulos
  def Assemble(self):
1340 7181fba0 Constantinos Venetsanopoulos
    """Assemble the device.
1341 7181fba0 Constantinos Venetsanopoulos

1342 7181fba0 Constantinos Venetsanopoulos
    """
1343 7181fba0 Constantinos Venetsanopoulos
    pass
1344 7181fba0 Constantinos Venetsanopoulos
1345 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
1346 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
1347 7181fba0 Constantinos Venetsanopoulos

1348 7181fba0 Constantinos Venetsanopoulos
    """
1349 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1350 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
1351 7181fba0 Constantinos Venetsanopoulos
      return
1352 7181fba0 Constantinos Venetsanopoulos
1353 7181fba0 Constantinos Venetsanopoulos
    # Unmap the block device from the Volume.
1354 7181fba0 Constantinos Venetsanopoulos
    self._UnmapVolumeFromBlockdev(self.unique_id)
1355 7181fba0 Constantinos Venetsanopoulos
1356 7181fba0 Constantinos Venetsanopoulos
    self.minor = None
1357 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = None
1358 7181fba0 Constantinos Venetsanopoulos
1359 7181fba0 Constantinos Venetsanopoulos
  def _UnmapVolumeFromBlockdev(self, unique_id):
1360 7181fba0 Constantinos Venetsanopoulos
    """Unmaps the rbd device from the Volume it is mapped.
1361 7181fba0 Constantinos Venetsanopoulos

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

1365 7181fba0 Constantinos Venetsanopoulos
    """
1366 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
1367 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
1368 7181fba0 Constantinos Venetsanopoulos
1369 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
1370 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
1371 7181fba0 Constantinos Venetsanopoulos
1372 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
1373 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Unmap the rbd device.
1374 7181fba0 Constantinos Venetsanopoulos
      unmap_cmd = [constants.RBD_CMD, "unmap", "%s" % rbd_dev]
1375 7181fba0 Constantinos Venetsanopoulos
      result = utils.RunCmd(unmap_cmd)
1376 7181fba0 Constantinos Venetsanopoulos
      if result.failed:
1377 89ff748d Thomas Thrainer
        base.ThrowError("rbd unmap failed (%s): %s",
1378 89ff748d Thomas Thrainer
                        result.fail_reason, result.output)
1379 7181fba0 Constantinos Venetsanopoulos
1380 7181fba0 Constantinos Venetsanopoulos
  def Open(self, force=False):
1381 7181fba0 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
1382 7181fba0 Constantinos Venetsanopoulos

1383 7181fba0 Constantinos Venetsanopoulos
    """
1384 7181fba0 Constantinos Venetsanopoulos
    pass
1385 7181fba0 Constantinos Venetsanopoulos
1386 7181fba0 Constantinos Venetsanopoulos
  def Close(self):
1387 7181fba0 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1388 7181fba0 Constantinos Venetsanopoulos

1389 7181fba0 Constantinos Venetsanopoulos
    """
1390 7181fba0 Constantinos Venetsanopoulos
    pass
1391 7181fba0 Constantinos Venetsanopoulos
1392 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
1393 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
1394 7181fba0 Constantinos Venetsanopoulos

1395 7181fba0 Constantinos Venetsanopoulos
    @type amount: integer
1396 7181fba0 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1397 7181fba0 Constantinos Venetsanopoulos
    @type dryrun: boolean
1398 7181fba0 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1399 7181fba0 Constantinos Venetsanopoulos
        only, without actually increasing the size
1400 7181fba0 Constantinos Venetsanopoulos

1401 7181fba0 Constantinos Venetsanopoulos
    """
1402 cad0723b Iustin Pop
    if not backingstore:
1403 cad0723b Iustin Pop
      return
1404 7181fba0 Constantinos Venetsanopoulos
    if not self.Attach():
1405 89ff748d Thomas Thrainer
      base.ThrowError("Can't attach to rbd device during Grow()")
1406 7181fba0 Constantinos Venetsanopoulos
1407 7181fba0 Constantinos Venetsanopoulos
    if dryrun:
1408 7181fba0 Constantinos Venetsanopoulos
      # the rbd tool does not support dry runs of resize operations.
1409 7181fba0 Constantinos Venetsanopoulos
      # Since rbd volumes are thinly provisioned, we assume
1410 7181fba0 Constantinos Venetsanopoulos
      # there is always enough free space for the operation.
1411 7181fba0 Constantinos Venetsanopoulos
      return
1412 7181fba0 Constantinos Venetsanopoulos
1413 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
1414 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
1415 7181fba0 Constantinos Venetsanopoulos
    new_size = self.size + amount
1416 7181fba0 Constantinos Venetsanopoulos
1417 7181fba0 Constantinos Venetsanopoulos
    # Resize the rbd volume (Image) inside the RADOS cluster.
1418 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "resize", "-p", rbd_pool,
1419 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % new_size]
1420 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
1421 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
1422 89ff748d Thomas Thrainer
      base.ThrowError("rbd resize failed (%s): %s",
1423 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
1424 7181fba0 Constantinos Venetsanopoulos
1425 7181fba0 Constantinos Venetsanopoulos
1426 89ff748d Thomas Thrainer
class ExtStorageDevice(base.BlockDev):
1427 376631d1 Constantinos Venetsanopoulos
  """A block device provided by an ExtStorage Provider.
1428 376631d1 Constantinos Venetsanopoulos

1429 376631d1 Constantinos Venetsanopoulos
  This class implements the External Storage Interface, which means
1430 376631d1 Constantinos Venetsanopoulos
  handling of the externally provided block devices.
1431 376631d1 Constantinos Venetsanopoulos

1432 376631d1 Constantinos Venetsanopoulos
  """
1433 376631d1 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
1434 376631d1 Constantinos Venetsanopoulos
    """Attaches to an extstorage block device.
1435 376631d1 Constantinos Venetsanopoulos

1436 376631d1 Constantinos Venetsanopoulos
    """
1437 376631d1 Constantinos Venetsanopoulos
    super(ExtStorageDevice, self).__init__(unique_id, children, size, params)
1438 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1439 376631d1 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1440 376631d1 Constantinos Venetsanopoulos
1441 376631d1 Constantinos Venetsanopoulos
    self.driver, self.vol_name = unique_id
1442 938adc87 Constantinos Venetsanopoulos
    self.ext_params = params
1443 376631d1 Constantinos Venetsanopoulos
1444 376631d1 Constantinos Venetsanopoulos
    self.major = self.minor = None
1445 376631d1 Constantinos Venetsanopoulos
    self.Attach()
1446 376631d1 Constantinos Venetsanopoulos
1447 376631d1 Constantinos Venetsanopoulos
  @classmethod
1448 24c06acb Bernardo Dal Seno
  def Create(cls, unique_id, children, size, spindles, params, excl_stor):
1449 376631d1 Constantinos Venetsanopoulos
    """Create a new extstorage device.
1450 376631d1 Constantinos Venetsanopoulos

1451 376631d1 Constantinos Venetsanopoulos
    Provision a new volume using an extstorage provider, which will
1452 376631d1 Constantinos Venetsanopoulos
    then be mapped to a block device.
1453 376631d1 Constantinos Venetsanopoulos

1454 376631d1 Constantinos Venetsanopoulos
    """
1455 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1456 376631d1 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
1457 376631d1 Constantinos Venetsanopoulos
                                   str(unique_id))
1458 ee1478e5 Bernardo Dal Seno
    if excl_stor:
1459 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("extstorage device requested with"
1460 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
1461 376631d1 Constantinos Venetsanopoulos
1462 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's create script,
1463 376631d1 Constantinos Venetsanopoulos
    # to provision a new Volume inside the External Storage
1464 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id,
1465 938adc87 Constantinos Venetsanopoulos
                      params, str(size))
1466 376631d1 Constantinos Venetsanopoulos
1467 376631d1 Constantinos Venetsanopoulos
    return ExtStorageDevice(unique_id, children, size, params)
1468 376631d1 Constantinos Venetsanopoulos
1469 376631d1 Constantinos Venetsanopoulos
  def Remove(self):
1470 376631d1 Constantinos Venetsanopoulos
    """Remove the extstorage device.
1471 376631d1 Constantinos Venetsanopoulos

1472 376631d1 Constantinos Venetsanopoulos
    """
1473 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1474 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1475 376631d1 Constantinos Venetsanopoulos
      return
1476 376631d1 Constantinos Venetsanopoulos
1477 376631d1 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
1478 376631d1 Constantinos Venetsanopoulos
    self.Shutdown()
1479 376631d1 Constantinos Venetsanopoulos
1480 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's remove script,
1481 376631d1 Constantinos Venetsanopoulos
    # to remove the Volume from the External Storage
1482 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id,
1483 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
1484 376631d1 Constantinos Venetsanopoulos
1485 376631d1 Constantinos Venetsanopoulos
  def Rename(self, new_id):
1486 376631d1 Constantinos Venetsanopoulos
    """Rename this device.
1487 376631d1 Constantinos Venetsanopoulos

1488 376631d1 Constantinos Venetsanopoulos
    """
1489 376631d1 Constantinos Venetsanopoulos
    pass
1490 376631d1 Constantinos Venetsanopoulos
1491 376631d1 Constantinos Venetsanopoulos
  def Attach(self):
1492 376631d1 Constantinos Venetsanopoulos
    """Attach to an existing extstorage device.
1493 376631d1 Constantinos Venetsanopoulos

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

1497 376631d1 Constantinos Venetsanopoulos
    """
1498 376631d1 Constantinos Venetsanopoulos
    self.attached = False
1499 376631d1 Constantinos Venetsanopoulos
1500 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's attach script,
1501 376631d1 Constantinos Venetsanopoulos
    # to attach an existing Volume to a block device under /dev
1502 376631d1 Constantinos Venetsanopoulos
    self.dev_path = _ExtStorageAction(constants.ES_ACTION_ATTACH,
1503 938adc87 Constantinos Venetsanopoulos
                                      self.unique_id, self.ext_params)
1504 376631d1 Constantinos Venetsanopoulos
1505 376631d1 Constantinos Venetsanopoulos
    try:
1506 376631d1 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
1507 376631d1 Constantinos Venetsanopoulos
    except OSError, err:
1508 376631d1 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
1509 376631d1 Constantinos Venetsanopoulos
      return False
1510 376631d1 Constantinos Venetsanopoulos
1511 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
1512 376631d1 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
1513 376631d1 Constantinos Venetsanopoulos
      return False
1514 376631d1 Constantinos Venetsanopoulos
1515 376631d1 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
1516 376631d1 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
1517 376631d1 Constantinos Venetsanopoulos
    self.attached = True
1518 376631d1 Constantinos Venetsanopoulos
1519 376631d1 Constantinos Venetsanopoulos
    return True
1520 376631d1 Constantinos Venetsanopoulos
1521 376631d1 Constantinos Venetsanopoulos
  def Assemble(self):
1522 376631d1 Constantinos Venetsanopoulos
    """Assemble the device.
1523 376631d1 Constantinos Venetsanopoulos

1524 376631d1 Constantinos Venetsanopoulos
    """
1525 376631d1 Constantinos Venetsanopoulos
    pass
1526 376631d1 Constantinos Venetsanopoulos
1527 376631d1 Constantinos Venetsanopoulos
  def Shutdown(self):
1528 376631d1 Constantinos Venetsanopoulos
    """Shutdown the device.
1529 376631d1 Constantinos Venetsanopoulos

1530 376631d1 Constantinos Venetsanopoulos
    """
1531 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1532 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1533 376631d1 Constantinos Venetsanopoulos
      return
1534 376631d1 Constantinos Venetsanopoulos
1535 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's detach script,
1536 376631d1 Constantinos Venetsanopoulos
    # to detach an existing Volume from it's block device under /dev
1537 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id,
1538 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
1539 376631d1 Constantinos Venetsanopoulos
1540 376631d1 Constantinos Venetsanopoulos
    self.minor = None
1541 376631d1 Constantinos Venetsanopoulos
    self.dev_path = None
1542 376631d1 Constantinos Venetsanopoulos
1543 376631d1 Constantinos Venetsanopoulos
  def Open(self, force=False):
1544 376631d1 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
1545 376631d1 Constantinos Venetsanopoulos

1546 376631d1 Constantinos Venetsanopoulos
    """
1547 376631d1 Constantinos Venetsanopoulos
    pass
1548 376631d1 Constantinos Venetsanopoulos
1549 376631d1 Constantinos Venetsanopoulos
  def Close(self):
1550 376631d1 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1551 376631d1 Constantinos Venetsanopoulos

1552 376631d1 Constantinos Venetsanopoulos
    """
1553 376631d1 Constantinos Venetsanopoulos
    pass
1554 376631d1 Constantinos Venetsanopoulos
1555 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
1556 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
1557 376631d1 Constantinos Venetsanopoulos

1558 376631d1 Constantinos Venetsanopoulos
    @type amount: integer
1559 376631d1 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1560 376631d1 Constantinos Venetsanopoulos
    @type dryrun: boolean
1561 376631d1 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1562 376631d1 Constantinos Venetsanopoulos
        only, without actually increasing the size
1563 376631d1 Constantinos Venetsanopoulos

1564 376631d1 Constantinos Venetsanopoulos
    """
1565 376631d1 Constantinos Venetsanopoulos
    if not backingstore:
1566 376631d1 Constantinos Venetsanopoulos
      return
1567 376631d1 Constantinos Venetsanopoulos
    if not self.Attach():
1568 89ff748d Thomas Thrainer
      base.ThrowError("Can't attach to extstorage device during Grow()")
1569 376631d1 Constantinos Venetsanopoulos
1570 376631d1 Constantinos Venetsanopoulos
    if dryrun:
1571 376631d1 Constantinos Venetsanopoulos
      # we do not support dry runs of resize operations for now.
1572 376631d1 Constantinos Venetsanopoulos
      return
1573 376631d1 Constantinos Venetsanopoulos
1574 376631d1 Constantinos Venetsanopoulos
    new_size = self.size + amount
1575 376631d1 Constantinos Venetsanopoulos
1576 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's grow script,
1577 376631d1 Constantinos Venetsanopoulos
    # to grow an existing Volume inside the External Storage
1578 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_GROW, self.unique_id,
1579 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, str(self.size), grow=str(new_size))
1580 376631d1 Constantinos Venetsanopoulos
1581 376631d1 Constantinos Venetsanopoulos
  def SetInfo(self, text):
1582 376631d1 Constantinos Venetsanopoulos
    """Update metadata with info text.
1583 376631d1 Constantinos Venetsanopoulos

1584 376631d1 Constantinos Venetsanopoulos
    """
1585 376631d1 Constantinos Venetsanopoulos
    # Replace invalid characters
1586 376631d1 Constantinos Venetsanopoulos
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
1587 376631d1 Constantinos Venetsanopoulos
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
1588 376631d1 Constantinos Venetsanopoulos
1589 376631d1 Constantinos Venetsanopoulos
    # Only up to 128 characters are allowed
1590 376631d1 Constantinos Venetsanopoulos
    text = text[:128]
1591 376631d1 Constantinos Venetsanopoulos
1592 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's setinfo script,
1593 376631d1 Constantinos Venetsanopoulos
    # to set metadata for an existing Volume inside the External Storage
1594 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
1595 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, metadata=text)
1596 376631d1 Constantinos Venetsanopoulos
1597 376631d1 Constantinos Venetsanopoulos
1598 938adc87 Constantinos Venetsanopoulos
def _ExtStorageAction(action, unique_id, ext_params,
1599 938adc87 Constantinos Venetsanopoulos
                      size=None, grow=None, metadata=None):
1600 376631d1 Constantinos Venetsanopoulos
  """Take an External Storage action.
1601 376631d1 Constantinos Venetsanopoulos

1602 376631d1 Constantinos Venetsanopoulos
  Take an External Storage action concerning or affecting
1603 376631d1 Constantinos Venetsanopoulos
  a specific Volume inside the External Storage.
1604 376631d1 Constantinos Venetsanopoulos

1605 376631d1 Constantinos Venetsanopoulos
  @type action: string
1606 376631d1 Constantinos Venetsanopoulos
  @param action: which action to perform. One of:
1607 376631d1 Constantinos Venetsanopoulos
                 create / remove / grow / attach / detach
1608 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1609 376631d1 Constantinos Venetsanopoulos
  @param unique_id: a tuple containing the type of ExtStorage (driver)
1610 376631d1 Constantinos Venetsanopoulos
                    and the Volume name
1611 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1612 938adc87 Constantinos Venetsanopoulos
  @param ext_params: ExtStorage parameters
1613 376631d1 Constantinos Venetsanopoulos
  @type size: integer
1614 376631d1 Constantinos Venetsanopoulos
  @param size: the size of the Volume in mebibytes
1615 376631d1 Constantinos Venetsanopoulos
  @type grow: integer
1616 376631d1 Constantinos Venetsanopoulos
  @param grow: the new size in mebibytes (after grow)
1617 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1618 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume, for use by the provider
1619 376631d1 Constantinos Venetsanopoulos
  @rtype: None or a block device path (during attach)
1620 376631d1 Constantinos Venetsanopoulos

1621 376631d1 Constantinos Venetsanopoulos
  """
1622 376631d1 Constantinos Venetsanopoulos
  driver, vol_name = unique_id
1623 376631d1 Constantinos Venetsanopoulos
1624 376631d1 Constantinos Venetsanopoulos
  # Create an External Storage instance of type `driver'
1625 376631d1 Constantinos Venetsanopoulos
  status, inst_es = ExtStorageFromDisk(driver)
1626 376631d1 Constantinos Venetsanopoulos
  if not status:
1627 89ff748d Thomas Thrainer
    base.ThrowError("%s" % inst_es)
1628 376631d1 Constantinos Venetsanopoulos
1629 376631d1 Constantinos Venetsanopoulos
  # Create the basic environment for the driver's scripts
1630 938adc87 Constantinos Venetsanopoulos
  create_env = _ExtStorageEnvironment(unique_id, ext_params, size,
1631 938adc87 Constantinos Venetsanopoulos
                                      grow, metadata)
1632 376631d1 Constantinos Venetsanopoulos
1633 376631d1 Constantinos Venetsanopoulos
  # Do not use log file for action `attach' as we need
1634 376631d1 Constantinos Venetsanopoulos
  # to get the output from RunResult
1635 376631d1 Constantinos Venetsanopoulos
  # TODO: find a way to have a log file for attach too
1636 376631d1 Constantinos Venetsanopoulos
  logfile = None
1637 376631d1 Constantinos Venetsanopoulos
  if action is not constants.ES_ACTION_ATTACH:
1638 376631d1 Constantinos Venetsanopoulos
    logfile = _VolumeLogName(action, driver, vol_name)
1639 376631d1 Constantinos Venetsanopoulos
1640 376631d1 Constantinos Venetsanopoulos
  # Make sure the given action results in a valid script
1641 376631d1 Constantinos Venetsanopoulos
  if action not in constants.ES_SCRIPTS:
1642 89ff748d Thomas Thrainer
    base.ThrowError("Action '%s' doesn't result in a valid ExtStorage script" %
1643 89ff748d Thomas Thrainer
                    action)
1644 376631d1 Constantinos Venetsanopoulos
1645 376631d1 Constantinos Venetsanopoulos
  # Find out which external script to run according the given action
1646 376631d1 Constantinos Venetsanopoulos
  script_name = action + "_script"
1647 376631d1 Constantinos Venetsanopoulos
  script = getattr(inst_es, script_name)
1648 376631d1 Constantinos Venetsanopoulos
1649 376631d1 Constantinos Venetsanopoulos
  # Run the external script
1650 376631d1 Constantinos Venetsanopoulos
  result = utils.RunCmd([script], env=create_env,
1651 376631d1 Constantinos Venetsanopoulos
                        cwd=inst_es.path, output=logfile,)
1652 376631d1 Constantinos Venetsanopoulos
  if result.failed:
1653 376631d1 Constantinos Venetsanopoulos
    logging.error("External storage's %s command '%s' returned"
1654 376631d1 Constantinos Venetsanopoulos
                  " error: %s, logfile: %s, output: %s",
1655 376631d1 Constantinos Venetsanopoulos
                  action, result.cmd, result.fail_reason,
1656 376631d1 Constantinos Venetsanopoulos
                  logfile, result.output)
1657 376631d1 Constantinos Venetsanopoulos
1658 376631d1 Constantinos Venetsanopoulos
    # If logfile is 'None' (during attach), it breaks TailFile
1659 376631d1 Constantinos Venetsanopoulos
    # TODO: have a log file for attach too
1660 376631d1 Constantinos Venetsanopoulos
    if action is not constants.ES_ACTION_ATTACH:
1661 376631d1 Constantinos Venetsanopoulos
      lines = [utils.SafeEncode(val)
1662 376631d1 Constantinos Venetsanopoulos
               for val in utils.TailFile(logfile, lines=20)]
1663 376631d1 Constantinos Venetsanopoulos
    else:
1664 376631d1 Constantinos Venetsanopoulos
      lines = result.output[-20:]
1665 376631d1 Constantinos Venetsanopoulos
1666 89ff748d Thomas Thrainer
    base.ThrowError("External storage's %s script failed (%s), last"
1667 89ff748d Thomas Thrainer
                    " lines of output:\n%s",
1668 89ff748d Thomas Thrainer
                    action, result.fail_reason, "\n".join(lines))
1669 376631d1 Constantinos Venetsanopoulos
1670 376631d1 Constantinos Venetsanopoulos
  if action == constants.ES_ACTION_ATTACH:
1671 376631d1 Constantinos Venetsanopoulos
    return result.stdout
1672 376631d1 Constantinos Venetsanopoulos
1673 376631d1 Constantinos Venetsanopoulos
1674 376631d1 Constantinos Venetsanopoulos
def ExtStorageFromDisk(name, base_dir=None):
1675 376631d1 Constantinos Venetsanopoulos
  """Create an ExtStorage instance from disk.
1676 376631d1 Constantinos Venetsanopoulos

1677 376631d1 Constantinos Venetsanopoulos
  This function will return an ExtStorage instance
1678 376631d1 Constantinos Venetsanopoulos
  if the given name is a valid ExtStorage name.
1679 376631d1 Constantinos Venetsanopoulos

1680 376631d1 Constantinos Venetsanopoulos
  @type base_dir: string
1681 376631d1 Constantinos Venetsanopoulos
  @keyword base_dir: Base directory containing ExtStorage installations.
1682 376631d1 Constantinos Venetsanopoulos
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
1683 376631d1 Constantinos Venetsanopoulos
  @rtype: tuple
1684 376631d1 Constantinos Venetsanopoulos
  @return: True and the ExtStorage instance if we find a valid one, or
1685 376631d1 Constantinos Venetsanopoulos
      False and the diagnose message on error
1686 376631d1 Constantinos Venetsanopoulos

1687 376631d1 Constantinos Venetsanopoulos
  """
1688 376631d1 Constantinos Venetsanopoulos
  if base_dir is None:
1689 376631d1 Constantinos Venetsanopoulos
    es_base_dir = pathutils.ES_SEARCH_PATH
1690 376631d1 Constantinos Venetsanopoulos
  else:
1691 376631d1 Constantinos Venetsanopoulos
    es_base_dir = [base_dir]
1692 376631d1 Constantinos Venetsanopoulos
1693 376631d1 Constantinos Venetsanopoulos
  es_dir = utils.FindFile(name, es_base_dir, os.path.isdir)
1694 376631d1 Constantinos Venetsanopoulos
1695 376631d1 Constantinos Venetsanopoulos
  if es_dir is None:
1696 376631d1 Constantinos Venetsanopoulos
    return False, ("Directory for External Storage Provider %s not"
1697 376631d1 Constantinos Venetsanopoulos
                   " found in search path" % name)
1698 376631d1 Constantinos Venetsanopoulos
1699 376631d1 Constantinos Venetsanopoulos
  # ES Files dictionary, we will populate it with the absolute path
1700 376631d1 Constantinos Venetsanopoulos
  # names; if the value is True, then it is a required file, otherwise
1701 376631d1 Constantinos Venetsanopoulos
  # an optional one
1702 376631d1 Constantinos Venetsanopoulos
  es_files = dict.fromkeys(constants.ES_SCRIPTS, True)
1703 376631d1 Constantinos Venetsanopoulos
1704 938adc87 Constantinos Venetsanopoulos
  es_files[constants.ES_PARAMETERS_FILE] = True
1705 938adc87 Constantinos Venetsanopoulos
1706 938adc87 Constantinos Venetsanopoulos
  for (filename, _) in es_files.items():
1707 376631d1 Constantinos Venetsanopoulos
    es_files[filename] = utils.PathJoin(es_dir, filename)
1708 376631d1 Constantinos Venetsanopoulos
1709 376631d1 Constantinos Venetsanopoulos
    try:
1710 376631d1 Constantinos Venetsanopoulos
      st = os.stat(es_files[filename])
1711 376631d1 Constantinos Venetsanopoulos
    except EnvironmentError, err:
1712 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is missing (%s)" %
1713 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir, utils.ErrnoOrStr(err)))
1714 376631d1 Constantinos Venetsanopoulos
1715 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1716 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is not a regular file" %
1717 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir))
1718 376631d1 Constantinos Venetsanopoulos
1719 376631d1 Constantinos Venetsanopoulos
    if filename in constants.ES_SCRIPTS:
1720 376631d1 Constantinos Venetsanopoulos
      if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
1721 376631d1 Constantinos Venetsanopoulos
        return False, ("File '%s' under path '%s' is not executable" %
1722 376631d1 Constantinos Venetsanopoulos
                       (filename, es_dir))
1723 376631d1 Constantinos Venetsanopoulos
1724 938adc87 Constantinos Venetsanopoulos
  parameters = []
1725 938adc87 Constantinos Venetsanopoulos
  if constants.ES_PARAMETERS_FILE in es_files:
1726 938adc87 Constantinos Venetsanopoulos
    parameters_file = es_files[constants.ES_PARAMETERS_FILE]
1727 938adc87 Constantinos Venetsanopoulos
    try:
1728 938adc87 Constantinos Venetsanopoulos
      parameters = utils.ReadFile(parameters_file).splitlines()
1729 938adc87 Constantinos Venetsanopoulos
    except EnvironmentError, err:
1730 938adc87 Constantinos Venetsanopoulos
      return False, ("Error while reading the EXT parameters file at %s: %s" %
1731 938adc87 Constantinos Venetsanopoulos
                     (parameters_file, utils.ErrnoOrStr(err)))
1732 938adc87 Constantinos Venetsanopoulos
    parameters = [v.split(None, 1) for v in parameters]
1733 938adc87 Constantinos Venetsanopoulos
1734 376631d1 Constantinos Venetsanopoulos
  es_obj = \
1735 376631d1 Constantinos Venetsanopoulos
    objects.ExtStorage(name=name, path=es_dir,
1736 376631d1 Constantinos Venetsanopoulos
                       create_script=es_files[constants.ES_SCRIPT_CREATE],
1737 376631d1 Constantinos Venetsanopoulos
                       remove_script=es_files[constants.ES_SCRIPT_REMOVE],
1738 376631d1 Constantinos Venetsanopoulos
                       grow_script=es_files[constants.ES_SCRIPT_GROW],
1739 376631d1 Constantinos Venetsanopoulos
                       attach_script=es_files[constants.ES_SCRIPT_ATTACH],
1740 376631d1 Constantinos Venetsanopoulos
                       detach_script=es_files[constants.ES_SCRIPT_DETACH],
1741 938adc87 Constantinos Venetsanopoulos
                       setinfo_script=es_files[constants.ES_SCRIPT_SETINFO],
1742 938adc87 Constantinos Venetsanopoulos
                       verify_script=es_files[constants.ES_SCRIPT_VERIFY],
1743 938adc87 Constantinos Venetsanopoulos
                       supported_parameters=parameters)
1744 376631d1 Constantinos Venetsanopoulos
  return True, es_obj
1745 376631d1 Constantinos Venetsanopoulos
1746 376631d1 Constantinos Venetsanopoulos
1747 938adc87 Constantinos Venetsanopoulos
def _ExtStorageEnvironment(unique_id, ext_params,
1748 938adc87 Constantinos Venetsanopoulos
                           size=None, grow=None, metadata=None):
1749 376631d1 Constantinos Venetsanopoulos
  """Calculate the environment for an External Storage script.
1750 376631d1 Constantinos Venetsanopoulos

1751 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1752 376631d1 Constantinos Venetsanopoulos
  @param unique_id: ExtStorage pool and name of the Volume
1753 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1754 938adc87 Constantinos Venetsanopoulos
  @param ext_params: the EXT parameters
1755 376631d1 Constantinos Venetsanopoulos
  @type size: string
1756 376631d1 Constantinos Venetsanopoulos
  @param size: size of the Volume (in mebibytes)
1757 376631d1 Constantinos Venetsanopoulos
  @type grow: string
1758 376631d1 Constantinos Venetsanopoulos
  @param grow: new size of Volume after grow (in mebibytes)
1759 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1760 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume
1761 376631d1 Constantinos Venetsanopoulos
  @rtype: dict
1762 376631d1 Constantinos Venetsanopoulos
  @return: dict of environment variables
1763 376631d1 Constantinos Venetsanopoulos

1764 376631d1 Constantinos Venetsanopoulos
  """
1765 376631d1 Constantinos Venetsanopoulos
  vol_name = unique_id[1]
1766 376631d1 Constantinos Venetsanopoulos
1767 376631d1 Constantinos Venetsanopoulos
  result = {}
1768 376631d1 Constantinos Venetsanopoulos
  result["VOL_NAME"] = vol_name
1769 376631d1 Constantinos Venetsanopoulos
1770 938adc87 Constantinos Venetsanopoulos
  # EXT params
1771 938adc87 Constantinos Venetsanopoulos
  for pname, pvalue in ext_params.items():
1772 938adc87 Constantinos Venetsanopoulos
    result["EXTP_%s" % pname.upper()] = str(pvalue)
1773 938adc87 Constantinos Venetsanopoulos
1774 376631d1 Constantinos Venetsanopoulos
  if size is not None:
1775 376631d1 Constantinos Venetsanopoulos
    result["VOL_SIZE"] = size
1776 376631d1 Constantinos Venetsanopoulos
1777 376631d1 Constantinos Venetsanopoulos
  if grow is not None:
1778 376631d1 Constantinos Venetsanopoulos
    result["VOL_NEW_SIZE"] = grow
1779 376631d1 Constantinos Venetsanopoulos
1780 376631d1 Constantinos Venetsanopoulos
  if metadata is not None:
1781 376631d1 Constantinos Venetsanopoulos
    result["VOL_METADATA"] = metadata
1782 376631d1 Constantinos Venetsanopoulos
1783 376631d1 Constantinos Venetsanopoulos
  return result
1784 376631d1 Constantinos Venetsanopoulos
1785 376631d1 Constantinos Venetsanopoulos
1786 376631d1 Constantinos Venetsanopoulos
def _VolumeLogName(kind, es_name, volume):
1787 376631d1 Constantinos Venetsanopoulos
  """Compute the ExtStorage log filename for a given Volume and operation.
1788 376631d1 Constantinos Venetsanopoulos

1789 376631d1 Constantinos Venetsanopoulos
  @type kind: string
1790 376631d1 Constantinos Venetsanopoulos
  @param kind: the operation type (e.g. create, remove etc.)
1791 376631d1 Constantinos Venetsanopoulos
  @type es_name: string
1792 376631d1 Constantinos Venetsanopoulos
  @param es_name: the ExtStorage name
1793 376631d1 Constantinos Venetsanopoulos
  @type volume: string
1794 376631d1 Constantinos Venetsanopoulos
  @param volume: the name of the Volume inside the External Storage
1795 376631d1 Constantinos Venetsanopoulos

1796 376631d1 Constantinos Venetsanopoulos
  """
1797 376631d1 Constantinos Venetsanopoulos
  # Check if the extstorage log dir is a valid dir
1798 376631d1 Constantinos Venetsanopoulos
  if not os.path.isdir(pathutils.LOG_ES_DIR):
1799 89ff748d Thomas Thrainer
    base.ThrowError("Cannot find log directory: %s", pathutils.LOG_ES_DIR)
1800 376631d1 Constantinos Venetsanopoulos
1801 376631d1 Constantinos Venetsanopoulos
  # TODO: Use tempfile.mkstemp to create unique filename
1802 89ff748d Thomas Thrainer
  basename = ("%s-%s-%s-%s.log" %
1803 89ff748d Thomas Thrainer
              (kind, es_name, volume, utils.TimestampForFilename()))
1804 89ff748d Thomas Thrainer
  return utils.PathJoin(pathutils.LOG_ES_DIR, basename)
1805 376631d1 Constantinos Venetsanopoulos
1806 376631d1 Constantinos Venetsanopoulos
1807 a8083063 Iustin Pop
DEV_MAP = {
1808 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1809 239364d0 Thomas Thrainer
  constants.LD_DRBD8: drbd.DRBD8Dev,
1810 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
1811 7181fba0 Constantinos Venetsanopoulos
  constants.LD_RBD: RADOSBlockDevice,
1812 376631d1 Constantinos Venetsanopoulos
  constants.LD_EXT: ExtStorageDevice,
1813 a8083063 Iustin Pop
  }
1814 a8083063 Iustin Pop
1815 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
1816 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
1817 cb7c0198 Iustin Pop
1818 a8083063 Iustin Pop
1819 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
1820 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
1821 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1822 94dcbdb0 Andrea Spadaccini
1823 94dcbdb0 Andrea Spadaccini
1824 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
1825 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
1826 5ff82cc9 René Nussbaumer

1827 5ff82cc9 René Nussbaumer
  """
1828 5ff82cc9 René Nussbaumer
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
1829 5ff82cc9 René Nussbaumer
  if missing:
1830 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
1831 5ff82cc9 René Nussbaumer
                                 missing)
1832 5ff82cc9 René Nussbaumer
1833 5ff82cc9 René Nussbaumer
1834 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
1835 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1836 a8083063 Iustin Pop

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

1840 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1841 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
1842 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1843 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1844 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1845 94dcbdb0 Andrea Spadaccini

1846 a8083063 Iustin Pop
  """
1847 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1848 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
1849 c7c6606d René Nussbaumer
                                  disk.params)
1850 cb999543 Iustin Pop
  if not device.attached:
1851 a8083063 Iustin Pop
    return None
1852 ecb091e3 Iustin Pop
  return device
1853 a8083063 Iustin Pop
1854 a8083063 Iustin Pop
1855 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
1856 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1857 a8083063 Iustin Pop

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

1861 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1862 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
1863 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1864 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1865 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1866 94dcbdb0 Andrea Spadaccini

1867 a8083063 Iustin Pop
  """
1868 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1869 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1870 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
1871 c7c6606d René Nussbaumer
                                  disk.params)
1872 1063abd1 Iustin Pop
  device.Assemble()
1873 a8083063 Iustin Pop
  return device
1874 a8083063 Iustin Pop
1875 a8083063 Iustin Pop
1876 ee1478e5 Bernardo Dal Seno
def Create(disk, children, excl_stor):
1877 a8083063 Iustin Pop
  """Create a device.
1878 a8083063 Iustin Pop

1879 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1880 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
1881 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1882 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1883 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1884 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
1885 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
1886 5073fa0c Bernardo Dal Seno
  @rtype: L{bdev.BlockDev}
1887 5073fa0c Bernardo Dal Seno
  @return: the created device, or C{None} in case of an error
1888 94dcbdb0 Andrea Spadaccini

1889 a8083063 Iustin Pop
  """
1890 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1891 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1892 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
1893 24c06acb Bernardo Dal Seno
                                         disk.spindles, disk.params, excl_stor)
1894 a8083063 Iustin Pop
  return device