Statistics
| Branch: | Tag: | Revision:

root / lib / block / bdev.py @ d01e51a5

History | View | Annotate | Download (55.4 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 89ff748d Thomas Thrainer
from ganeti.block import drbd
39 89ff748d Thomas Thrainer
from ganeti.block 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 b8028dcf Michael Hanselmann
  _INVALID_NAMES = compat.UniqueFrozenset([".", "..", "snapshot", "pvmove"])
176 b8028dcf Michael Hanselmann
  _INVALID_SUBSTRINGS = compat.UniqueFrozenset(["_mlog", "_mimage"])
177 6136f8f0 Iustin Pop
178 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
179 a8083063 Iustin Pop
    """Attaches to a LV device.
180 a8083063 Iustin Pop

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

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

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

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

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

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

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

307 197478f2 René Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
308 197478f2 René Nussbaumer
    @param fields: Fields to return
309 197478f2 René Nussbaumer
    @return: A list of dicts each with the parsed fields
310 197478f2 René Nussbaumer

311 197478f2 René Nussbaumer
    """
312 197478f2 René Nussbaumer
    if not fields:
313 197478f2 René Nussbaumer
      raise errors.ProgrammerError("No fields specified")
314 197478f2 René Nussbaumer
315 197478f2 René Nussbaumer
    sep = "|"
316 197478f2 René Nussbaumer
    cmd = [lvm_cmd, "--noheadings", "--nosuffix", "--units=m", "--unbuffered",
317 197478f2 René Nussbaumer
           "--separator=%s" % sep, "-o%s" % ",".join(fields)]
318 197478f2 René Nussbaumer
319 197478f2 René Nussbaumer
    result = utils.RunCmd(cmd)
320 197478f2 René Nussbaumer
    if result.failed:
321 197478f2 René Nussbaumer
      raise errors.CommandError("Can't get the volume information: %s - %s" %
322 197478f2 René Nussbaumer
                                (result.fail_reason, result.output))
323 197478f2 René Nussbaumer
324 197478f2 René Nussbaumer
    data = []
325 197478f2 René Nussbaumer
    for line in result.stdout.splitlines():
326 197478f2 René Nussbaumer
      splitted_fields = line.strip().split(sep)
327 197478f2 René Nussbaumer
328 197478f2 René Nussbaumer
      if len(fields) != len(splitted_fields):
329 197478f2 René Nussbaumer
        raise errors.CommandError("Can't parse %s output: line '%s'" %
330 197478f2 René Nussbaumer
                                  (lvm_cmd, line))
331 197478f2 René Nussbaumer
332 197478f2 René Nussbaumer
      data.append(splitted_fields)
333 197478f2 René Nussbaumer
334 197478f2 René Nussbaumer
    return data
335 197478f2 René Nussbaumer
336 197478f2 René Nussbaumer
  @classmethod
337 b496abdb Bernardo Dal Seno
  def GetPVInfo(cls, vg_names, filter_allocatable=True, include_lvs=False):
338 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
339 a8083063 Iustin Pop

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

344 c41eea6e Iustin Pop
    @rtype: list
345 59726e15 Bernardo Dal Seno
    @return: list of objects.LvmPvInfo objects
346 098c0958 Michael Hanselmann

347 a8083063 Iustin Pop
    """
348 b496abdb Bernardo Dal Seno
    # We request "lv_name" field only if we care about LVs, so we don't get
349 b496abdb Bernardo Dal Seno
    # a long list of entries with many duplicates unless we really have to.
350 b496abdb Bernardo Dal Seno
    # The duplicate "pv_name" field will be ignored.
351 b496abdb Bernardo Dal Seno
    if include_lvs:
352 b496abdb Bernardo Dal Seno
      lvfield = "lv_name"
353 b496abdb Bernardo Dal Seno
    else:
354 b496abdb Bernardo Dal Seno
      lvfield = "pv_name"
355 197478f2 René Nussbaumer
    try:
356 197478f2 René Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
357 b496abdb Bernardo Dal Seno
                                        "pv_attr", "pv_size", lvfield])
358 197478f2 René Nussbaumer
    except errors.GenericError, err:
359 197478f2 René Nussbaumer
      logging.error("Can't get PV information: %s", err)
360 a8083063 Iustin Pop
      return None
361 197478f2 René Nussbaumer
362 b496abdb Bernardo Dal Seno
    # When asked for LVs, "pvs" may return multiple entries for the same PV-LV
363 b496abdb Bernardo Dal Seno
    # pair. We sort entries by PV name and then LV name, so it's easy to weed
364 b496abdb Bernardo Dal Seno
    # out duplicates.
365 b496abdb Bernardo Dal Seno
    if include_lvs:
366 b496abdb Bernardo Dal Seno
      info.sort(key=(lambda i: (i[0], i[5])))
367 a8083063 Iustin Pop
    data = []
368 b496abdb Bernardo Dal Seno
    lastpvi = None
369 b496abdb Bernardo Dal Seno
    for (pv_name, vg_name, pv_free, pv_attr, pv_size, lv_name) in info:
370 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
371 197478f2 René Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
372 a8083063 Iustin Pop
        continue
373 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
374 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
375 2070598f Iustin Pop
        continue
376 b496abdb Bernardo Dal Seno
      # Beware of duplicates (check before inserting)
377 b496abdb Bernardo Dal Seno
      if lastpvi and lastpvi.name == pv_name:
378 b496abdb Bernardo Dal Seno
        if include_lvs and lv_name:
379 b496abdb Bernardo Dal Seno
          if not lastpvi.lv_list or lastpvi.lv_list[-1] != lv_name:
380 b496abdb Bernardo Dal Seno
            lastpvi.lv_list.append(lv_name)
381 b496abdb Bernardo Dal Seno
      else:
382 b496abdb Bernardo Dal Seno
        if include_lvs and lv_name:
383 b496abdb Bernardo Dal Seno
          lvl = [lv_name]
384 b496abdb Bernardo Dal Seno
        else:
385 b496abdb Bernardo Dal Seno
          lvl = []
386 b496abdb Bernardo Dal Seno
        lastpvi = objects.LvmPvInfo(name=pv_name, vg_name=vg_name,
387 b496abdb Bernardo Dal Seno
                                    size=float(pv_size), free=float(pv_free),
388 b496abdb Bernardo Dal Seno
                                    attributes=pv_attr, lv_list=lvl)
389 b496abdb Bernardo Dal Seno
        data.append(lastpvi)
390 197478f2 René Nussbaumer
391 197478f2 René Nussbaumer
    return data
392 197478f2 René Nussbaumer
393 197478f2 René Nussbaumer
  @classmethod
394 61481c52 Bernardo Dal Seno
  def _GetExclusiveStorageVgFree(cls, vg_name):
395 61481c52 Bernardo Dal Seno
    """Return the free disk space in the given VG, in exclusive storage mode.
396 61481c52 Bernardo Dal Seno

397 61481c52 Bernardo Dal Seno
    @type vg_name: string
398 61481c52 Bernardo Dal Seno
    @param vg_name: VG name
399 61481c52 Bernardo Dal Seno
    @rtype: float
400 61481c52 Bernardo Dal Seno
    @return: free space in MiB
401 61481c52 Bernardo Dal Seno
    """
402 61481c52 Bernardo Dal Seno
    pvs_info = cls.GetPVInfo([vg_name])
403 61481c52 Bernardo Dal Seno
    if not pvs_info:
404 61481c52 Bernardo Dal Seno
      return 0.0
405 61481c52 Bernardo Dal Seno
    pv_size = cls._GetStdPvSize(pvs_info)
406 61481c52 Bernardo Dal Seno
    num_pvs = len(cls._GetEmptyPvNames(pvs_info))
407 61481c52 Bernardo Dal Seno
    return pv_size * num_pvs
408 61481c52 Bernardo Dal Seno
409 61481c52 Bernardo Dal Seno
  @classmethod
410 1a3c5d4e Bernardo Dal Seno
  def GetVGInfo(cls, vg_names, excl_stor, filter_readonly=True):
411 197478f2 René Nussbaumer
    """Get the free space info for specific VGs.
412 197478f2 René Nussbaumer

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

417 197478f2 René Nussbaumer
    @rtype: list
418 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
419 673cd9c4 René Nussbaumer
             MiB
420 197478f2 René Nussbaumer

421 197478f2 René Nussbaumer
    """
422 197478f2 René Nussbaumer
    try:
423 673cd9c4 René Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
424 673cd9c4 René Nussbaumer
                                        "vg_size"])
425 197478f2 René Nussbaumer
    except errors.GenericError, err:
426 197478f2 René Nussbaumer
      logging.error("Can't get VG information: %s", err)
427 197478f2 René Nussbaumer
      return None
428 197478f2 René Nussbaumer
429 197478f2 René Nussbaumer
    data = []
430 673cd9c4 René Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
431 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not writable
432 197478f2 René Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
433 197478f2 René Nussbaumer
        continue
434 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
435 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
436 197478f2 René Nussbaumer
        continue
437 61481c52 Bernardo Dal Seno
      # Exclusive storage needs a different concept of free space
438 61481c52 Bernardo Dal Seno
      if excl_stor:
439 61481c52 Bernardo Dal Seno
        es_free = cls._GetExclusiveStorageVgFree(vg_name)
440 61481c52 Bernardo Dal Seno
        assert es_free <= vg_free
441 61481c52 Bernardo Dal Seno
        vg_free = es_free
442 673cd9c4 René Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
443 a8083063 Iustin Pop
444 a8083063 Iustin Pop
    return data
445 a8083063 Iustin Pop
446 6136f8f0 Iustin Pop
  @classmethod
447 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
448 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
449 6136f8f0 Iustin Pop

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

454 6136f8f0 Iustin Pop
    """
455 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
456 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
457 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
458 89ff748d Thomas Thrainer
      base.ThrowError("Invalid LVM name '%s'", name)
459 6136f8f0 Iustin Pop
460 a8083063 Iustin Pop
  def Remove(self):
461 a8083063 Iustin Pop
    """Remove this logical volume.
462 a8083063 Iustin Pop

463 a8083063 Iustin Pop
    """
464 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
465 a8083063 Iustin Pop
      # the LV does not exist
466 0c6c04ec Iustin Pop
      return
467 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
468 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
469 a8083063 Iustin Pop
    if result.failed:
470 89ff748d Thomas Thrainer
      base.ThrowError("Can't lvremove: %s - %s",
471 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
472 a8083063 Iustin Pop
473 f3e513ad Iustin Pop
  def Rename(self, new_id):
474 f3e513ad Iustin Pop
    """Rename this logical volume.
475 f3e513ad Iustin Pop

476 f3e513ad Iustin Pop
    """
477 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
478 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
479 f3e513ad Iustin Pop
    new_vg, new_name = new_id
480 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
481 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
482 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
483 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
484 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
485 f3e513ad Iustin Pop
    if result.failed:
486 89ff748d Thomas Thrainer
      base.ThrowError("Failed to rename the logical volume: %s", result.output)
487 be345db0 Iustin Pop
    self._lv_name = new_name
488 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
489 be345db0 Iustin Pop
490 a8083063 Iustin Pop
  def Attach(self):
491 a8083063 Iustin Pop
    """Attach to an existing LV.
492 a8083063 Iustin Pop

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

497 a8083063 Iustin Pop
    """
498 cb999543 Iustin Pop
    self.attached = False
499 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
500 38256320 Iustin Pop
                           "--units=m", "--nosuffix",
501 38256320 Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
502 38256320 Iustin Pop
                           "vg_extent_size,stripes", self.dev_path])
503 a8083063 Iustin Pop
    if result.failed:
504 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
505 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
506 a8083063 Iustin Pop
      return False
507 38256320 Iustin Pop
    # the output can (and will) have multiple lines for multi-segment
508 38256320 Iustin Pop
    # LVs, as the 'stripes' parameter is a segment one, so we take
509 38256320 Iustin Pop
    # only the last entry, which is the one we're interested in; note
510 38256320 Iustin Pop
    # that with LVM2 anyway the 'stripes' value must be constant
511 38256320 Iustin Pop
    # across segments, so this is a no-op actually
512 38256320 Iustin Pop
    out = result.stdout.splitlines()
513 38256320 Iustin Pop
    if not out: # totally empty result? splitlines() returns at least
514 38256320 Iustin Pop
                # one line for any non-empty string
515 38256320 Iustin Pop
      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
516 38256320 Iustin Pop
      return False
517 d0c8c01d Iustin Pop
    out = out[-1].strip().rstrip(",")
518 99e8295c Iustin Pop
    out = out.split(",")
519 38256320 Iustin Pop
    if len(out) != 5:
520 38256320 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
521 99e8295c Iustin Pop
      return False
522 99e8295c Iustin Pop
523 38256320 Iustin Pop
    status, major, minor, pe_size, stripes = out
524 0304f0ec Iustin Pop
    if len(status) < 6:
525 0304f0ec Iustin Pop
      logging.error("lvs lv_attr is not at least 6 characters (%s)", status)
526 99e8295c Iustin Pop
      return False
527 99e8295c Iustin Pop
528 99e8295c Iustin Pop
    try:
529 99e8295c Iustin Pop
      major = int(major)
530 99e8295c Iustin Pop
      minor = int(minor)
531 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
532 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
533 99e8295c Iustin Pop
534 38256320 Iustin Pop
    try:
535 38256320 Iustin Pop
      pe_size = int(float(pe_size))
536 38256320 Iustin Pop
    except (TypeError, ValueError), err:
537 38256320 Iustin Pop
      logging.error("Can't parse vg extent size: %s", err)
538 38256320 Iustin Pop
      return False
539 38256320 Iustin Pop
540 38256320 Iustin Pop
    try:
541 38256320 Iustin Pop
      stripes = int(stripes)
542 38256320 Iustin Pop
    except (TypeError, ValueError), err:
543 38256320 Iustin Pop
      logging.error("Can't parse the number of stripes: %s", err)
544 38256320 Iustin Pop
      return False
545 38256320 Iustin Pop
546 99e8295c Iustin Pop
    self.major = major
547 99e8295c Iustin Pop
    self.minor = minor
548 38256320 Iustin Pop
    self.pe_size = pe_size
549 38256320 Iustin Pop
    self.stripe_count = stripes
550 d0c8c01d Iustin Pop
    self._degraded = status[0] == "v" # virtual volume, i.e. doesn't backing
551 99e8295c Iustin Pop
                                      # storage
552 cb999543 Iustin Pop
    self.attached = True
553 99e8295c Iustin Pop
    return True
554 a8083063 Iustin Pop
555 a8083063 Iustin Pop
  def Assemble(self):
556 a8083063 Iustin Pop
    """Assemble the device.
557 a8083063 Iustin Pop

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

562 a8083063 Iustin Pop
    """
563 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
564 5574047a Iustin Pop
    if result.failed:
565 89ff748d Thomas Thrainer
      base.ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
566 a8083063 Iustin Pop
567 a8083063 Iustin Pop
  def Shutdown(self):
568 a8083063 Iustin Pop
    """Shutdown the device.
569 a8083063 Iustin Pop

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

573 a8083063 Iustin Pop
    """
574 746f7476 Iustin Pop
    pass
575 a8083063 Iustin Pop
576 9db6dbce Iustin Pop
  def GetSyncStatus(self):
577 9db6dbce Iustin Pop
    """Returns the sync status of the device.
578 9db6dbce Iustin Pop

579 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
580 9db6dbce Iustin Pop
    status of the mirror.
581 9db6dbce Iustin Pop

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

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

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

595 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
596 c41eea6e Iustin Pop

597 9db6dbce Iustin Pop
    """
598 f208978a Michael Hanselmann
    if self._degraded:
599 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
600 f208978a Michael Hanselmann
    else:
601 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
602 f208978a Michael Hanselmann
603 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
604 96acbc09 Michael Hanselmann
                                  major=self.major,
605 96acbc09 Michael Hanselmann
                                  minor=self.minor,
606 96acbc09 Michael Hanselmann
                                  sync_percent=None,
607 96acbc09 Michael Hanselmann
                                  estimated_time=None,
608 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
609 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
610 9db6dbce Iustin Pop
611 a8083063 Iustin Pop
  def Open(self, force=False):
612 a8083063 Iustin Pop
    """Make the device ready for I/O.
613 a8083063 Iustin Pop

614 a8083063 Iustin Pop
    This is a no-op for the LV device type.
615 a8083063 Iustin Pop

616 a8083063 Iustin Pop
    """
617 fdbd668d Iustin Pop
    pass
618 a8083063 Iustin Pop
619 a8083063 Iustin Pop
  def Close(self):
620 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
621 a8083063 Iustin Pop

622 a8083063 Iustin Pop
    This is a no-op for the LV device type.
623 a8083063 Iustin Pop

624 a8083063 Iustin Pop
    """
625 fdbd668d Iustin Pop
    pass
626 a8083063 Iustin Pop
627 a8083063 Iustin Pop
  def Snapshot(self, size):
628 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
629 a8083063 Iustin Pop

630 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
631 800ac399 Iustin Pop

632 a8083063 Iustin Pop
    """
633 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
634 a8083063 Iustin Pop
635 a8083063 Iustin Pop
    # remove existing snapshot if found
636 94dcbdb0 Andrea Spadaccini
    snap = LogicalVolume((self._vg_name, snap_name), None, size, self.params)
637 89ff748d Thomas Thrainer
    base.IgnoreError(snap.Remove)
638 a8083063 Iustin Pop
639 1a3c5d4e Bernardo Dal Seno
    vg_info = self.GetVGInfo([self._vg_name], False)
640 197478f2 René Nussbaumer
    if not vg_info:
641 89ff748d Thomas Thrainer
      base.ThrowError("Can't compute VG info for vg %s", self._vg_name)
642 673cd9c4 René Nussbaumer
    free_size, _, _ = vg_info[0]
643 a8083063 Iustin Pop
    if free_size < size:
644 89ff748d Thomas Thrainer
      base.ThrowError("Not enough free space: required %s,"
645 89ff748d Thomas Thrainer
                      " available %s", size, free_size)
646 a8083063 Iustin Pop
647 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
648 e398546b Iustin Pop
                               "-n%s" % snap_name, self.dev_path]))
649 a8083063 Iustin Pop
650 800ac399 Iustin Pop
    return (self._vg_name, snap_name)
651 a8083063 Iustin Pop
652 a1556cfa Iustin Pop
  def _RemoveOldInfo(self):
653 a1556cfa Iustin Pop
    """Try to remove old tags from the lv.
654 a1556cfa Iustin Pop

655 a1556cfa Iustin Pop
    """
656 a1556cfa Iustin Pop
    result = utils.RunCmd(["lvs", "-o", "tags", "--noheadings", "--nosuffix",
657 a1556cfa Iustin Pop
                           self.dev_path])
658 a1556cfa Iustin Pop
    _CheckResult(result)
659 a1556cfa Iustin Pop
660 a1556cfa Iustin Pop
    raw_tags = result.stdout.strip()
661 a1556cfa Iustin Pop
    if raw_tags:
662 a1556cfa Iustin Pop
      for tag in raw_tags.split(","):
663 a1556cfa Iustin Pop
        _CheckResult(utils.RunCmd(["lvchange", "--deltag",
664 a1556cfa Iustin Pop
                                   tag.strip(), self.dev_path]))
665 a1556cfa Iustin Pop
666 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
667 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
668 a0c3fea1 Michael Hanselmann

669 a0c3fea1 Michael Hanselmann
    """
670 89ff748d Thomas Thrainer
    base.BlockDev.SetInfo(self, text)
671 a0c3fea1 Michael Hanselmann
672 a1556cfa Iustin Pop
    self._RemoveOldInfo()
673 a1556cfa Iustin Pop
674 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
675 d0c8c01d Iustin Pop
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
676 d0c8c01d Iustin Pop
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
677 a0c3fea1 Michael Hanselmann
678 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
679 a0c3fea1 Michael Hanselmann
    text = text[:128]
680 a0c3fea1 Michael Hanselmann
681 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvchange", "--addtag", text, self.dev_path]))
682 82463074 Iustin Pop
683 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
684 1005d816 Iustin Pop
    """Grow the logical volume.
685 1005d816 Iustin Pop

686 1005d816 Iustin Pop
    """
687 cad0723b Iustin Pop
    if not backingstore:
688 cad0723b Iustin Pop
      return
689 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
690 38256320 Iustin Pop
      if not self.Attach():
691 89ff748d Thomas Thrainer
        base.ThrowError("Can't attach to LV during Grow()")
692 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
693 38256320 Iustin Pop
    rest = amount % full_stripe_size
694 38256320 Iustin Pop
    if rest != 0:
695 38256320 Iustin Pop
      amount += full_stripe_size - rest
696 7fe23d47 Iustin Pop
    cmd = ["lvextend", "-L", "+%dm" % amount]
697 7fe23d47 Iustin Pop
    if dryrun:
698 7fe23d47 Iustin Pop
      cmd.append("--test")
699 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
700 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
701 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
702 1005d816 Iustin Pop
    # supports 'cling'
703 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
704 7fe23d47 Iustin Pop
      result = utils.RunCmd(cmd + ["--alloc", alloc_policy, self.dev_path])
705 1005d816 Iustin Pop
      if not result.failed:
706 1005d816 Iustin Pop
        return
707 89ff748d Thomas Thrainer
    base.ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
708 a8083063 Iustin Pop
709 a2cfdea2 Iustin Pop
710 89ff748d Thomas Thrainer
class FileStorage(base.BlockDev):
711 6f695a2e Manuel Franceschini
  """File device.
712 abdf0113 Iustin Pop

713 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
714 6f695a2e Manuel Franceschini

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

717 6f695a2e Manuel Franceschini
  """
718 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
719 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
720 6f695a2e Manuel Franceschini

721 6f695a2e Manuel Franceschini
    """
722 6f695a2e Manuel Franceschini
    if children:
723 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
724 94dcbdb0 Andrea Spadaccini
    super(FileStorage, self).__init__(unique_id, children, size, params)
725 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
726 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
727 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
728 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
729 5e09a309 Michael Hanselmann
730 5e09a309 Michael Hanselmann
    CheckFileStoragePath(self.dev_path)
731 5e09a309 Michael Hanselmann
732 ecb091e3 Iustin Pop
    self.Attach()
733 6f695a2e Manuel Franceschini
734 6f695a2e Manuel Franceschini
  def Assemble(self):
735 6f695a2e Manuel Franceschini
    """Assemble the device.
736 6f695a2e Manuel Franceschini

737 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
738 6f695a2e Manuel Franceschini

739 6f695a2e Manuel Franceschini
    """
740 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
741 89ff748d Thomas Thrainer
      base.ThrowError("File device '%s' does not exist" % self.dev_path)
742 6f695a2e Manuel Franceschini
743 6f695a2e Manuel Franceschini
  def Shutdown(self):
744 6f695a2e Manuel Franceschini
    """Shutdown the device.
745 6f695a2e Manuel Franceschini

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

749 6f695a2e Manuel Franceschini
    """
750 746f7476 Iustin Pop
    pass
751 6f695a2e Manuel Franceschini
752 6f695a2e Manuel Franceschini
  def Open(self, force=False):
753 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
754 6f695a2e Manuel Franceschini

755 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
756 6f695a2e Manuel Franceschini

757 6f695a2e Manuel Franceschini
    """
758 6f695a2e Manuel Franceschini
    pass
759 6f695a2e Manuel Franceschini
760 6f695a2e Manuel Franceschini
  def Close(self):
761 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
762 6f695a2e Manuel Franceschini

763 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
764 6f695a2e Manuel Franceschini

765 6f695a2e Manuel Franceschini
    """
766 6f695a2e Manuel Franceschini
    pass
767 6f695a2e Manuel Franceschini
768 6f695a2e Manuel Franceschini
  def Remove(self):
769 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
770 6f695a2e Manuel Franceschini

771 c41eea6e Iustin Pop
    @rtype: boolean
772 c41eea6e Iustin Pop
    @return: True if the removal was successful
773 6f695a2e Manuel Franceschini

774 6f695a2e Manuel Franceschini
    """
775 6f695a2e Manuel Franceschini
    try:
776 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
777 6f695a2e Manuel Franceschini
    except OSError, err:
778 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
779 89ff748d Thomas Thrainer
        base.ThrowError("Can't remove file '%s': %s", self.dev_path, err)
780 6f695a2e Manuel Franceschini
781 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
782 bbe4cc16 Iustin Pop
    """Renames the file.
783 bbe4cc16 Iustin Pop

784 bbe4cc16 Iustin Pop
    """
785 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
786 89ff748d Thomas Thrainer
    base.ThrowError("Rename is not supported for file-based storage")
787 bbe4cc16 Iustin Pop
788 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
789 bbe4cc16 Iustin Pop
    """Grow the file
790 bbe4cc16 Iustin Pop

791 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
792 bbe4cc16 Iustin Pop

793 bbe4cc16 Iustin Pop
    """
794 cad0723b Iustin Pop
    if not backingstore:
795 cad0723b Iustin Pop
      return
796 91e2d9ec Guido Trotter
    # Check that the file exists
797 91e2d9ec Guido Trotter
    self.Assemble()
798 91e2d9ec Guido Trotter
    current_size = self.GetActualSize()
799 91e2d9ec Guido Trotter
    new_size = current_size + amount * 1024 * 1024
800 91e2d9ec Guido Trotter
    assert new_size > current_size, "Cannot Grow with a negative amount"
801 7fe23d47 Iustin Pop
    # We can't really simulate the growth
802 7fe23d47 Iustin Pop
    if dryrun:
803 7fe23d47 Iustin Pop
      return
804 91e2d9ec Guido Trotter
    try:
805 91e2d9ec Guido Trotter
      f = open(self.dev_path, "a+")
806 91e2d9ec Guido Trotter
      f.truncate(new_size)
807 91e2d9ec Guido Trotter
      f.close()
808 91e2d9ec Guido Trotter
    except EnvironmentError, err:
809 89ff748d Thomas Thrainer
      base.ThrowError("Error in file growth: %", str(err))
810 bbe4cc16 Iustin Pop
811 6f695a2e Manuel Franceschini
  def Attach(self):
812 6f695a2e Manuel Franceschini
    """Attach to an existing file.
813 6f695a2e Manuel Franceschini

814 6f695a2e Manuel Franceschini
    Check if this file already exists.
815 6f695a2e Manuel Franceschini

816 c41eea6e Iustin Pop
    @rtype: boolean
817 c41eea6e Iustin Pop
    @return: True if file exists
818 6f695a2e Manuel Franceschini

819 6f695a2e Manuel Franceschini
    """
820 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
821 ecb091e3 Iustin Pop
    return self.attached
822 6f695a2e Manuel Franceschini
823 fcff3897 Iustin Pop
  def GetActualSize(self):
824 fcff3897 Iustin Pop
    """Return the actual disk size.
825 fcff3897 Iustin Pop

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

828 fcff3897 Iustin Pop
    """
829 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
830 fcff3897 Iustin Pop
    try:
831 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
832 fcff3897 Iustin Pop
      return st.st_size
833 fcff3897 Iustin Pop
    except OSError, err:
834 89ff748d Thomas Thrainer
      base.ThrowError("Can't stat %s: %s", self.dev_path, err)
835 fcff3897 Iustin Pop
836 6f695a2e Manuel Franceschini
  @classmethod
837 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
838 6f695a2e Manuel Franceschini
    """Create a new file.
839 6f695a2e Manuel Franceschini

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

842 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
843 c41eea6e Iustin Pop
    @return: an instance of FileStorage
844 6f695a2e Manuel Franceschini

845 6f695a2e Manuel Franceschini
    """
846 ee1478e5 Bernardo Dal Seno
    if excl_stor:
847 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("FileStorage device requested with"
848 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
849 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
850 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
851 5e09a309 Michael Hanselmann
852 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
853 5e09a309 Michael Hanselmann
854 5e09a309 Michael Hanselmann
    CheckFileStoragePath(dev_path)
855 5e09a309 Michael Hanselmann
856 6f695a2e Manuel Franceschini
    try:
857 cdeefd9b Guido Trotter
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
858 cdeefd9b Guido Trotter
      f = os.fdopen(fd, "w")
859 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
860 6f695a2e Manuel Franceschini
      f.close()
861 cdeefd9b Guido Trotter
    except EnvironmentError, err:
862 cdeefd9b Guido Trotter
      if err.errno == errno.EEXIST:
863 89ff748d Thomas Thrainer
        base.ThrowError("File already existing: %s", dev_path)
864 89ff748d Thomas Thrainer
      base.ThrowError("Error in file creation: %", str(err))
865 6f695a2e Manuel Franceschini
866 94dcbdb0 Andrea Spadaccini
    return FileStorage(unique_id, children, size, params)
867 6f695a2e Manuel Franceschini
868 6f695a2e Manuel Franceschini
869 89ff748d Thomas Thrainer
class PersistentBlockDevice(base.BlockDev):
870 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
871 b6135bbc Apollon Oikonomopoulos

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

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

878 b6135bbc Apollon Oikonomopoulos
  """
879 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
880 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
881 b6135bbc Apollon Oikonomopoulos

882 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
883 b6135bbc Apollon Oikonomopoulos

884 b6135bbc Apollon Oikonomopoulos
    """
885 94dcbdb0 Andrea Spadaccini
    super(PersistentBlockDevice, self).__init__(unique_id, children, size,
886 94dcbdb0 Andrea Spadaccini
                                                params)
887 b6135bbc Apollon Oikonomopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
888 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
889 b6135bbc Apollon Oikonomopoulos
    self.dev_path = unique_id[1]
890 d0c8c01d Iustin Pop
    if not os.path.realpath(self.dev_path).startswith("/dev/"):
891 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Full path '%s' lies outside /dev" %
892 b6135bbc Apollon Oikonomopoulos
                              os.path.realpath(self.dev_path))
893 b6135bbc Apollon Oikonomopoulos
    # TODO: this is just a safety guard checking that we only deal with devices
894 b6135bbc Apollon Oikonomopoulos
    # we know how to handle. In the future this will be integrated with
895 b6135bbc Apollon Oikonomopoulos
    # external storage backends and possible values will probably be collected
896 b6135bbc Apollon Oikonomopoulos
    # from the cluster configuration.
897 b6135bbc Apollon Oikonomopoulos
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
898 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Got persistent block device of invalid type: %s" %
899 b6135bbc Apollon Oikonomopoulos
                       unique_id[0])
900 b6135bbc Apollon Oikonomopoulos
901 b6135bbc Apollon Oikonomopoulos
    self.major = self.minor = None
902 b6135bbc Apollon Oikonomopoulos
    self.Attach()
903 b6135bbc Apollon Oikonomopoulos
904 b6135bbc Apollon Oikonomopoulos
  @classmethod
905 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
906 b6135bbc Apollon Oikonomopoulos
    """Create a new device
907 b6135bbc Apollon Oikonomopoulos

908 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
909 b6135bbc Apollon Oikonomopoulos

910 b6135bbc Apollon Oikonomopoulos
    """
911 ee1478e5 Bernardo Dal Seno
    if excl_stor:
912 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("Persistent block device requested with"
913 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
914 94dcbdb0 Andrea Spadaccini
    return PersistentBlockDevice(unique_id, children, 0, params)
915 b6135bbc Apollon Oikonomopoulos
916 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
917 b6135bbc Apollon Oikonomopoulos
    """Remove a device
918 b6135bbc Apollon Oikonomopoulos

919 b6135bbc Apollon Oikonomopoulos
    This is a noop
920 b6135bbc Apollon Oikonomopoulos

921 b6135bbc Apollon Oikonomopoulos
    """
922 b6135bbc Apollon Oikonomopoulos
    pass
923 b6135bbc Apollon Oikonomopoulos
924 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
925 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
926 b6135bbc Apollon Oikonomopoulos

927 b6135bbc Apollon Oikonomopoulos
    """
928 89ff748d Thomas Thrainer
    base.ThrowError("Rename is not supported for PersistentBlockDev storage")
929 b6135bbc Apollon Oikonomopoulos
930 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
931 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
932 b6135bbc Apollon Oikonomopoulos

933 b6135bbc Apollon Oikonomopoulos

934 b6135bbc Apollon Oikonomopoulos
    """
935 b6135bbc Apollon Oikonomopoulos
    self.attached = False
936 b6135bbc Apollon Oikonomopoulos
    try:
937 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
938 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
939 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
940 b6135bbc Apollon Oikonomopoulos
      return False
941 b6135bbc Apollon Oikonomopoulos
942 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
943 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
944 b6135bbc Apollon Oikonomopoulos
      return False
945 b6135bbc Apollon Oikonomopoulos
946 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
947 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
948 b6135bbc Apollon Oikonomopoulos
    self.attached = True
949 b6135bbc Apollon Oikonomopoulos
950 b6135bbc Apollon Oikonomopoulos
    return True
951 b6135bbc Apollon Oikonomopoulos
952 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
953 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
954 b6135bbc Apollon Oikonomopoulos

955 b6135bbc Apollon Oikonomopoulos
    """
956 b6135bbc Apollon Oikonomopoulos
    pass
957 b6135bbc Apollon Oikonomopoulos
958 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
959 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
960 b6135bbc Apollon Oikonomopoulos

961 b6135bbc Apollon Oikonomopoulos
    """
962 b6135bbc Apollon Oikonomopoulos
    pass
963 b6135bbc Apollon Oikonomopoulos
964 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
965 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
966 b6135bbc Apollon Oikonomopoulos

967 b6135bbc Apollon Oikonomopoulos
    """
968 b6135bbc Apollon Oikonomopoulos
    pass
969 b6135bbc Apollon Oikonomopoulos
970 b6135bbc Apollon Oikonomopoulos
  def Close(self):
971 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
972 b6135bbc Apollon Oikonomopoulos

973 b6135bbc Apollon Oikonomopoulos
    """
974 b6135bbc Apollon Oikonomopoulos
    pass
975 b6135bbc Apollon Oikonomopoulos
976 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
977 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
978 b6135bbc Apollon Oikonomopoulos

979 b6135bbc Apollon Oikonomopoulos
    """
980 89ff748d Thomas Thrainer
    base.ThrowError("Grow is not supported for PersistentBlockDev storage")
981 b6135bbc Apollon Oikonomopoulos
982 b6135bbc Apollon Oikonomopoulos
983 89ff748d Thomas Thrainer
class RADOSBlockDevice(base.BlockDev):
984 7181fba0 Constantinos Venetsanopoulos
  """A RADOS Block Device (rbd).
985 7181fba0 Constantinos Venetsanopoulos

986 7181fba0 Constantinos Venetsanopoulos
  This class implements the RADOS Block Device for the backend. You need
987 7181fba0 Constantinos Venetsanopoulos
  the rbd kernel driver, the RADOS Tools and a working RADOS cluster for
988 7181fba0 Constantinos Venetsanopoulos
  this to be functional.
989 7181fba0 Constantinos Venetsanopoulos

990 7181fba0 Constantinos Venetsanopoulos
  """
991 7181fba0 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
992 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
993 7181fba0 Constantinos Venetsanopoulos

994 7181fba0 Constantinos Venetsanopoulos
    """
995 7181fba0 Constantinos Venetsanopoulos
    super(RADOSBlockDevice, self).__init__(unique_id, children, size, params)
996 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
997 7181fba0 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
998 7181fba0 Constantinos Venetsanopoulos
999 7181fba0 Constantinos Venetsanopoulos
    self.driver, self.rbd_name = unique_id
1000 7181fba0 Constantinos Venetsanopoulos
1001 7181fba0 Constantinos Venetsanopoulos
    self.major = self.minor = None
1002 7181fba0 Constantinos Venetsanopoulos
    self.Attach()
1003 7181fba0 Constantinos Venetsanopoulos
1004 7181fba0 Constantinos Venetsanopoulos
  @classmethod
1005 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
1006 7181fba0 Constantinos Venetsanopoulos
    """Create a new rbd device.
1007 7181fba0 Constantinos Venetsanopoulos

1008 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
1009 7181fba0 Constantinos Venetsanopoulos

1010 7181fba0 Constantinos Venetsanopoulos
    """
1011 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1012 7181fba0 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
1013 7181fba0 Constantinos Venetsanopoulos
                                   str(unique_id))
1014 ee1478e5 Bernardo Dal Seno
    if excl_stor:
1015 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("RBD device requested with"
1016 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
1017 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = params[constants.LDP_POOL]
1018 7181fba0 Constantinos Venetsanopoulos
    rbd_name = unique_id[1]
1019 7181fba0 Constantinos Venetsanopoulos
1020 7181fba0 Constantinos Venetsanopoulos
    # Provision a new rbd volume (Image) inside the RADOS cluster.
1021 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "create", "-p", rbd_pool,
1022 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % size]
1023 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
1024 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
1025 89ff748d Thomas Thrainer
      base.ThrowError("rbd creation failed (%s): %s",
1026 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
1027 7181fba0 Constantinos Venetsanopoulos
1028 7181fba0 Constantinos Venetsanopoulos
    return RADOSBlockDevice(unique_id, children, size, params)
1029 7181fba0 Constantinos Venetsanopoulos
1030 7181fba0 Constantinos Venetsanopoulos
  def Remove(self):
1031 7181fba0 Constantinos Venetsanopoulos
    """Remove the rbd device.
1032 7181fba0 Constantinos Venetsanopoulos

1033 7181fba0 Constantinos Venetsanopoulos
    """
1034 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
1035 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
1036 7181fba0 Constantinos Venetsanopoulos
1037 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1038 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
1039 7181fba0 Constantinos Venetsanopoulos
      return
1040 7181fba0 Constantinos Venetsanopoulos
1041 7181fba0 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
1042 7181fba0 Constantinos Venetsanopoulos
    self.Shutdown()
1043 7181fba0 Constantinos Venetsanopoulos
1044 7181fba0 Constantinos Venetsanopoulos
    # Remove the actual Volume (Image) from the RADOS cluster.
1045 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "rm", "-p", rbd_pool, rbd_name]
1046 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
1047 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
1048 89ff748d Thomas Thrainer
      base.ThrowError("Can't remove Volume from cluster with rbd rm: %s - %s",
1049 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
1050 7181fba0 Constantinos Venetsanopoulos
1051 7181fba0 Constantinos Venetsanopoulos
  def Rename(self, new_id):
1052 7181fba0 Constantinos Venetsanopoulos
    """Rename this device.
1053 7181fba0 Constantinos Venetsanopoulos

1054 7181fba0 Constantinos Venetsanopoulos
    """
1055 7181fba0 Constantinos Venetsanopoulos
    pass
1056 7181fba0 Constantinos Venetsanopoulos
1057 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
1058 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
1059 7181fba0 Constantinos Venetsanopoulos

1060 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
1061 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
1062 7181fba0 Constantinos Venetsanopoulos

1063 7181fba0 Constantinos Venetsanopoulos
    """
1064 7181fba0 Constantinos Venetsanopoulos
    self.attached = False
1065 7181fba0 Constantinos Venetsanopoulos
1066 7181fba0 Constantinos Venetsanopoulos
    # Map the rbd volume to a block device under /dev
1067 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = self._MapVolumeToBlockdev(self.unique_id)
1068 7181fba0 Constantinos Venetsanopoulos
1069 7181fba0 Constantinos Venetsanopoulos
    try:
1070 7181fba0 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
1071 7181fba0 Constantinos Venetsanopoulos
    except OSError, err:
1072 7181fba0 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
1073 7181fba0 Constantinos Venetsanopoulos
      return False
1074 7181fba0 Constantinos Venetsanopoulos
1075 7181fba0 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
1076 7181fba0 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
1077 7181fba0 Constantinos Venetsanopoulos
      return False
1078 7181fba0 Constantinos Venetsanopoulos
1079 7181fba0 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
1080 7181fba0 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
1081 7181fba0 Constantinos Venetsanopoulos
    self.attached = True
1082 7181fba0 Constantinos Venetsanopoulos
1083 7181fba0 Constantinos Venetsanopoulos
    return True
1084 7181fba0 Constantinos Venetsanopoulos
1085 7181fba0 Constantinos Venetsanopoulos
  def _MapVolumeToBlockdev(self, unique_id):
1086 7181fba0 Constantinos Venetsanopoulos
    """Maps existing rbd volumes to block devices.
1087 7181fba0 Constantinos Venetsanopoulos

1088 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
1089 7181fba0 Constantinos Venetsanopoulos

1090 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
1091 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
1092 7181fba0 Constantinos Venetsanopoulos

1093 7181fba0 Constantinos Venetsanopoulos
    """
1094 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
1095 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
1096 7181fba0 Constantinos Venetsanopoulos
1097 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
1098 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
1099 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
1100 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Return it.
1101 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
1102 7181fba0 Constantinos Venetsanopoulos
1103 7181fba0 Constantinos Venetsanopoulos
    # The mapping doesn't exist. Create it.
1104 7181fba0 Constantinos Venetsanopoulos
    map_cmd = [constants.RBD_CMD, "map", "-p", pool, name]
1105 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(map_cmd)
1106 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
1107 89ff748d Thomas Thrainer
      base.ThrowError("rbd map failed (%s): %s",
1108 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
1109 7181fba0 Constantinos Venetsanopoulos
1110 7181fba0 Constantinos Venetsanopoulos
    # Find the corresponding rbd device.
1111 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
1112 7181fba0 Constantinos Venetsanopoulos
    if not rbd_dev:
1113 89ff748d Thomas Thrainer
      base.ThrowError("rbd map succeeded, but could not find the rbd block"
1114 89ff748d Thomas Thrainer
                      " device in output of showmapped, for volume: %s", name)
1115 7181fba0 Constantinos Venetsanopoulos
1116 7181fba0 Constantinos Venetsanopoulos
    # The device was successfully mapped. Return it.
1117 7181fba0 Constantinos Venetsanopoulos
    return rbd_dev
1118 7181fba0 Constantinos Venetsanopoulos
1119 bdecfea2 Stratos Psomadakis
  @classmethod
1120 bdecfea2 Stratos Psomadakis
  def _VolumeToBlockdev(cls, pool, volume_name):
1121 bdecfea2 Stratos Psomadakis
    """Do the 'volume name'-to-'rbd block device' resolving.
1122 bdecfea2 Stratos Psomadakis

1123 bdecfea2 Stratos Psomadakis
    @type pool: string
1124 bdecfea2 Stratos Psomadakis
    @param pool: RADOS pool to use
1125 bdecfea2 Stratos Psomadakis
    @type volume_name: string
1126 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
1127 bdecfea2 Stratos Psomadakis
    @rtype: string or None
1128 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
1129 bdecfea2 Stratos Psomadakis

1130 bdecfea2 Stratos Psomadakis
    """
1131 bdecfea2 Stratos Psomadakis
    try:
1132 bdecfea2 Stratos Psomadakis
      # Newer versions of the rbd tool support json output formatting. Use it
1133 bdecfea2 Stratos Psomadakis
      # if available.
1134 bdecfea2 Stratos Psomadakis
      showmap_cmd = [
1135 bdecfea2 Stratos Psomadakis
        constants.RBD_CMD,
1136 bdecfea2 Stratos Psomadakis
        "showmapped",
1137 bdecfea2 Stratos Psomadakis
        "-p",
1138 bdecfea2 Stratos Psomadakis
        pool,
1139 bdecfea2 Stratos Psomadakis
        "--format",
1140 bdecfea2 Stratos Psomadakis
        "json"
1141 bdecfea2 Stratos Psomadakis
        ]
1142 bdecfea2 Stratos Psomadakis
      result = utils.RunCmd(showmap_cmd)
1143 bdecfea2 Stratos Psomadakis
      if result.failed:
1144 bdecfea2 Stratos Psomadakis
        logging.error("rbd JSON output formatting returned error (%s): %s,"
1145 bdecfea2 Stratos Psomadakis
                      "falling back to plain output parsing",
1146 bdecfea2 Stratos Psomadakis
                      result.fail_reason, result.output)
1147 bdecfea2 Stratos Psomadakis
        raise RbdShowmappedJsonError
1148 bdecfea2 Stratos Psomadakis
1149 bdecfea2 Stratos Psomadakis
      return cls._ParseRbdShowmappedJson(result.output, volume_name)
1150 bdecfea2 Stratos Psomadakis
    except RbdShowmappedJsonError:
1151 bdecfea2 Stratos Psomadakis
      # For older versions of rbd, we have to parse the plain / text output
1152 bdecfea2 Stratos Psomadakis
      # manually.
1153 bdecfea2 Stratos Psomadakis
      showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
1154 bdecfea2 Stratos Psomadakis
      result = utils.RunCmd(showmap_cmd)
1155 bdecfea2 Stratos Psomadakis
      if result.failed:
1156 89ff748d Thomas Thrainer
        base.ThrowError("rbd showmapped failed (%s): %s",
1157 89ff748d Thomas Thrainer
                        result.fail_reason, result.output)
1158 bdecfea2 Stratos Psomadakis
1159 bdecfea2 Stratos Psomadakis
      return cls._ParseRbdShowmappedPlain(result.output, volume_name)
1160 bdecfea2 Stratos Psomadakis
1161 bdecfea2 Stratos Psomadakis
  @staticmethod
1162 bdecfea2 Stratos Psomadakis
  def _ParseRbdShowmappedJson(output, volume_name):
1163 bdecfea2 Stratos Psomadakis
    """Parse the json output of `rbd showmapped'.
1164 bdecfea2 Stratos Psomadakis

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

1168 bdecfea2 Stratos Psomadakis
    @type output: string
1169 bdecfea2 Stratos Psomadakis
    @param output: the json output of `rbd showmapped'
1170 bdecfea2 Stratos Psomadakis
    @type volume_name: string
1171 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
1172 bdecfea2 Stratos Psomadakis
    @rtype: string or None
1173 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
1174 bdecfea2 Stratos Psomadakis

1175 bdecfea2 Stratos Psomadakis
    """
1176 bdecfea2 Stratos Psomadakis
    try:
1177 bdecfea2 Stratos Psomadakis
      devices = serializer.LoadJson(output)
1178 bdecfea2 Stratos Psomadakis
    except ValueError, err:
1179 89ff748d Thomas Thrainer
      base.ThrowError("Unable to parse JSON data: %s" % err)
1180 bdecfea2 Stratos Psomadakis
1181 bdecfea2 Stratos Psomadakis
    rbd_dev = None
1182 bdecfea2 Stratos Psomadakis
    for d in devices.values(): # pylint: disable=E1103
1183 bdecfea2 Stratos Psomadakis
      try:
1184 bdecfea2 Stratos Psomadakis
        name = d["name"]
1185 bdecfea2 Stratos Psomadakis
      except KeyError:
1186 89ff748d Thomas Thrainer
        base.ThrowError("'name' key missing from json object %s", devices)
1187 bdecfea2 Stratos Psomadakis
1188 bdecfea2 Stratos Psomadakis
      if name == volume_name:
1189 bdecfea2 Stratos Psomadakis
        if rbd_dev is not None:
1190 89ff748d Thomas Thrainer
          base.ThrowError("rbd volume %s is mapped more than once", volume_name)
1191 bdecfea2 Stratos Psomadakis
1192 bdecfea2 Stratos Psomadakis
        rbd_dev = d["device"]
1193 bdecfea2 Stratos Psomadakis
1194 bdecfea2 Stratos Psomadakis
    return rbd_dev
1195 bdecfea2 Stratos Psomadakis
1196 7181fba0 Constantinos Venetsanopoulos
  @staticmethod
1197 bdecfea2 Stratos Psomadakis
  def _ParseRbdShowmappedPlain(output, volume_name):
1198 bdecfea2 Stratos Psomadakis
    """Parse the (plain / text) output of `rbd showmapped'.
1199 7181fba0 Constantinos Venetsanopoulos

1200 7181fba0 Constantinos Venetsanopoulos
    This method parses the output of `rbd showmapped' and returns
1201 7181fba0 Constantinos Venetsanopoulos
    the rbd block device path (e.g. /dev/rbd0) that matches the
1202 7181fba0 Constantinos Venetsanopoulos
    given rbd volume.
1203 7181fba0 Constantinos Venetsanopoulos

1204 7181fba0 Constantinos Venetsanopoulos
    @type output: string
1205 bdecfea2 Stratos Psomadakis
    @param output: the plain text output of `rbd showmapped'
1206 7181fba0 Constantinos Venetsanopoulos
    @type volume_name: string
1207 7181fba0 Constantinos Venetsanopoulos
    @param volume_name: the name of the volume whose device we search for
1208 7181fba0 Constantinos Venetsanopoulos
    @rtype: string or None
1209 7181fba0 Constantinos Venetsanopoulos
    @return: block device path if the volume is mapped, else None
1210 7181fba0 Constantinos Venetsanopoulos

1211 7181fba0 Constantinos Venetsanopoulos
    """
1212 7181fba0 Constantinos Venetsanopoulos
    allfields = 5
1213 7181fba0 Constantinos Venetsanopoulos
    volumefield = 2
1214 7181fba0 Constantinos Venetsanopoulos
    devicefield = 4
1215 7181fba0 Constantinos Venetsanopoulos
1216 7181fba0 Constantinos Venetsanopoulos
    lines = output.splitlines()
1217 7181fba0 Constantinos Venetsanopoulos
1218 bdecfea2 Stratos Psomadakis
    # Try parsing the new output format (ceph >= 0.55).
1219 bdecfea2 Stratos Psomadakis
    splitted_lines = map(lambda l: l.split(), lines)
1220 bdecfea2 Stratos Psomadakis
1221 bdecfea2 Stratos Psomadakis
    # Check for empty output.
1222 7181fba0 Constantinos Venetsanopoulos
    if not splitted_lines:
1223 bdecfea2 Stratos Psomadakis
      return None
1224 7181fba0 Constantinos Venetsanopoulos
1225 bdecfea2 Stratos Psomadakis
    # Check showmapped output, to determine number of fields.
1226 7181fba0 Constantinos Venetsanopoulos
    field_cnt = len(splitted_lines[0])
1227 7181fba0 Constantinos Venetsanopoulos
    if field_cnt != allfields:
1228 bdecfea2 Stratos Psomadakis
      # Parsing the new format failed. Fallback to parsing the old output
1229 bdecfea2 Stratos Psomadakis
      # format (< 0.55).
1230 bdecfea2 Stratos Psomadakis
      splitted_lines = map(lambda l: l.split("\t"), lines)
1231 bdecfea2 Stratos Psomadakis
      if field_cnt != allfields:
1232 89ff748d Thomas Thrainer
        base.ThrowError("Cannot parse rbd showmapped output expected %s fields,"
1233 89ff748d Thomas Thrainer
                        " found %s", allfields, field_cnt)
1234 7181fba0 Constantinos Venetsanopoulos
1235 7181fba0 Constantinos Venetsanopoulos
    matched_lines = \
1236 7181fba0 Constantinos Venetsanopoulos
      filter(lambda l: len(l) == allfields and l[volumefield] == volume_name,
1237 7181fba0 Constantinos Venetsanopoulos
             splitted_lines)
1238 7181fba0 Constantinos Venetsanopoulos
1239 7181fba0 Constantinos Venetsanopoulos
    if len(matched_lines) > 1:
1240 89ff748d Thomas Thrainer
      base.ThrowError("rbd volume %s mapped more than once", volume_name)
1241 7181fba0 Constantinos Venetsanopoulos
1242 7181fba0 Constantinos Venetsanopoulos
    if matched_lines:
1243 7181fba0 Constantinos Venetsanopoulos
      # rbd block device found. Return it.
1244 7181fba0 Constantinos Venetsanopoulos
      rbd_dev = matched_lines[0][devicefield]
1245 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
1246 7181fba0 Constantinos Venetsanopoulos
1247 7181fba0 Constantinos Venetsanopoulos
    # The given volume is not mapped.
1248 7181fba0 Constantinos Venetsanopoulos
    return None
1249 7181fba0 Constantinos Venetsanopoulos
1250 7181fba0 Constantinos Venetsanopoulos
  def Assemble(self):
1251 7181fba0 Constantinos Venetsanopoulos
    """Assemble the device.
1252 7181fba0 Constantinos Venetsanopoulos

1253 7181fba0 Constantinos Venetsanopoulos
    """
1254 7181fba0 Constantinos Venetsanopoulos
    pass
1255 7181fba0 Constantinos Venetsanopoulos
1256 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
1257 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
1258 7181fba0 Constantinos Venetsanopoulos

1259 7181fba0 Constantinos Venetsanopoulos
    """
1260 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1261 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
1262 7181fba0 Constantinos Venetsanopoulos
      return
1263 7181fba0 Constantinos Venetsanopoulos
1264 7181fba0 Constantinos Venetsanopoulos
    # Unmap the block device from the Volume.
1265 7181fba0 Constantinos Venetsanopoulos
    self._UnmapVolumeFromBlockdev(self.unique_id)
1266 7181fba0 Constantinos Venetsanopoulos
1267 7181fba0 Constantinos Venetsanopoulos
    self.minor = None
1268 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = None
1269 7181fba0 Constantinos Venetsanopoulos
1270 7181fba0 Constantinos Venetsanopoulos
  def _UnmapVolumeFromBlockdev(self, unique_id):
1271 7181fba0 Constantinos Venetsanopoulos
    """Unmaps the rbd device from the Volume it is mapped.
1272 7181fba0 Constantinos Venetsanopoulos

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

1276 7181fba0 Constantinos Venetsanopoulos
    """
1277 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
1278 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
1279 7181fba0 Constantinos Venetsanopoulos
1280 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
1281 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
1282 7181fba0 Constantinos Venetsanopoulos
1283 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
1284 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Unmap the rbd device.
1285 7181fba0 Constantinos Venetsanopoulos
      unmap_cmd = [constants.RBD_CMD, "unmap", "%s" % rbd_dev]
1286 7181fba0 Constantinos Venetsanopoulos
      result = utils.RunCmd(unmap_cmd)
1287 7181fba0 Constantinos Venetsanopoulos
      if result.failed:
1288 89ff748d Thomas Thrainer
        base.ThrowError("rbd unmap failed (%s): %s",
1289 89ff748d Thomas Thrainer
                        result.fail_reason, result.output)
1290 7181fba0 Constantinos Venetsanopoulos
1291 7181fba0 Constantinos Venetsanopoulos
  def Open(self, force=False):
1292 7181fba0 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
1293 7181fba0 Constantinos Venetsanopoulos

1294 7181fba0 Constantinos Venetsanopoulos
    """
1295 7181fba0 Constantinos Venetsanopoulos
    pass
1296 7181fba0 Constantinos Venetsanopoulos
1297 7181fba0 Constantinos Venetsanopoulos
  def Close(self):
1298 7181fba0 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1299 7181fba0 Constantinos Venetsanopoulos

1300 7181fba0 Constantinos Venetsanopoulos
    """
1301 7181fba0 Constantinos Venetsanopoulos
    pass
1302 7181fba0 Constantinos Venetsanopoulos
1303 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
1304 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
1305 7181fba0 Constantinos Venetsanopoulos

1306 7181fba0 Constantinos Venetsanopoulos
    @type amount: integer
1307 7181fba0 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1308 7181fba0 Constantinos Venetsanopoulos
    @type dryrun: boolean
1309 7181fba0 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1310 7181fba0 Constantinos Venetsanopoulos
        only, without actually increasing the size
1311 7181fba0 Constantinos Venetsanopoulos

1312 7181fba0 Constantinos Venetsanopoulos
    """
1313 cad0723b Iustin Pop
    if not backingstore:
1314 cad0723b Iustin Pop
      return
1315 7181fba0 Constantinos Venetsanopoulos
    if not self.Attach():
1316 89ff748d Thomas Thrainer
      base.ThrowError("Can't attach to rbd device during Grow()")
1317 7181fba0 Constantinos Venetsanopoulos
1318 7181fba0 Constantinos Venetsanopoulos
    if dryrun:
1319 7181fba0 Constantinos Venetsanopoulos
      # the rbd tool does not support dry runs of resize operations.
1320 7181fba0 Constantinos Venetsanopoulos
      # Since rbd volumes are thinly provisioned, we assume
1321 7181fba0 Constantinos Venetsanopoulos
      # there is always enough free space for the operation.
1322 7181fba0 Constantinos Venetsanopoulos
      return
1323 7181fba0 Constantinos Venetsanopoulos
1324 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
1325 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
1326 7181fba0 Constantinos Venetsanopoulos
    new_size = self.size + amount
1327 7181fba0 Constantinos Venetsanopoulos
1328 7181fba0 Constantinos Venetsanopoulos
    # Resize the rbd volume (Image) inside the RADOS cluster.
1329 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "resize", "-p", rbd_pool,
1330 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % new_size]
1331 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
1332 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
1333 89ff748d Thomas Thrainer
      base.ThrowError("rbd resize failed (%s): %s",
1334 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
1335 7181fba0 Constantinos Venetsanopoulos
1336 7181fba0 Constantinos Venetsanopoulos
1337 89ff748d Thomas Thrainer
class ExtStorageDevice(base.BlockDev):
1338 376631d1 Constantinos Venetsanopoulos
  """A block device provided by an ExtStorage Provider.
1339 376631d1 Constantinos Venetsanopoulos

1340 376631d1 Constantinos Venetsanopoulos
  This class implements the External Storage Interface, which means
1341 376631d1 Constantinos Venetsanopoulos
  handling of the externally provided block devices.
1342 376631d1 Constantinos Venetsanopoulos

1343 376631d1 Constantinos Venetsanopoulos
  """
1344 376631d1 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
1345 376631d1 Constantinos Venetsanopoulos
    """Attaches to an extstorage block device.
1346 376631d1 Constantinos Venetsanopoulos

1347 376631d1 Constantinos Venetsanopoulos
    """
1348 376631d1 Constantinos Venetsanopoulos
    super(ExtStorageDevice, self).__init__(unique_id, children, size, params)
1349 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1350 376631d1 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1351 376631d1 Constantinos Venetsanopoulos
1352 376631d1 Constantinos Venetsanopoulos
    self.driver, self.vol_name = unique_id
1353 938adc87 Constantinos Venetsanopoulos
    self.ext_params = params
1354 376631d1 Constantinos Venetsanopoulos
1355 376631d1 Constantinos Venetsanopoulos
    self.major = self.minor = None
1356 376631d1 Constantinos Venetsanopoulos
    self.Attach()
1357 376631d1 Constantinos Venetsanopoulos
1358 376631d1 Constantinos Venetsanopoulos
  @classmethod
1359 ee1478e5 Bernardo Dal Seno
  def Create(cls, unique_id, children, size, params, excl_stor):
1360 376631d1 Constantinos Venetsanopoulos
    """Create a new extstorage device.
1361 376631d1 Constantinos Venetsanopoulos

1362 376631d1 Constantinos Venetsanopoulos
    Provision a new volume using an extstorage provider, which will
1363 376631d1 Constantinos Venetsanopoulos
    then be mapped to a block device.
1364 376631d1 Constantinos Venetsanopoulos

1365 376631d1 Constantinos Venetsanopoulos
    """
1366 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1367 376631d1 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
1368 376631d1 Constantinos Venetsanopoulos
                                   str(unique_id))
1369 ee1478e5 Bernardo Dal Seno
    if excl_stor:
1370 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("extstorage device requested with"
1371 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
1372 376631d1 Constantinos Venetsanopoulos
1373 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's create script,
1374 376631d1 Constantinos Venetsanopoulos
    # to provision a new Volume inside the External Storage
1375 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id,
1376 938adc87 Constantinos Venetsanopoulos
                      params, str(size))
1377 376631d1 Constantinos Venetsanopoulos
1378 376631d1 Constantinos Venetsanopoulos
    return ExtStorageDevice(unique_id, children, size, params)
1379 376631d1 Constantinos Venetsanopoulos
1380 376631d1 Constantinos Venetsanopoulos
  def Remove(self):
1381 376631d1 Constantinos Venetsanopoulos
    """Remove the extstorage device.
1382 376631d1 Constantinos Venetsanopoulos

1383 376631d1 Constantinos Venetsanopoulos
    """
1384 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1385 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1386 376631d1 Constantinos Venetsanopoulos
      return
1387 376631d1 Constantinos Venetsanopoulos
1388 376631d1 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
1389 376631d1 Constantinos Venetsanopoulos
    self.Shutdown()
1390 376631d1 Constantinos Venetsanopoulos
1391 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's remove script,
1392 376631d1 Constantinos Venetsanopoulos
    # to remove the Volume from the External Storage
1393 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id,
1394 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
1395 376631d1 Constantinos Venetsanopoulos
1396 376631d1 Constantinos Venetsanopoulos
  def Rename(self, new_id):
1397 376631d1 Constantinos Venetsanopoulos
    """Rename this device.
1398 376631d1 Constantinos Venetsanopoulos

1399 376631d1 Constantinos Venetsanopoulos
    """
1400 376631d1 Constantinos Venetsanopoulos
    pass
1401 376631d1 Constantinos Venetsanopoulos
1402 376631d1 Constantinos Venetsanopoulos
  def Attach(self):
1403 376631d1 Constantinos Venetsanopoulos
    """Attach to an existing extstorage device.
1404 376631d1 Constantinos Venetsanopoulos

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

1408 376631d1 Constantinos Venetsanopoulos
    """
1409 376631d1 Constantinos Venetsanopoulos
    self.attached = False
1410 376631d1 Constantinos Venetsanopoulos
1411 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's attach script,
1412 376631d1 Constantinos Venetsanopoulos
    # to attach an existing Volume to a block device under /dev
1413 376631d1 Constantinos Venetsanopoulos
    self.dev_path = _ExtStorageAction(constants.ES_ACTION_ATTACH,
1414 938adc87 Constantinos Venetsanopoulos
                                      self.unique_id, self.ext_params)
1415 376631d1 Constantinos Venetsanopoulos
1416 376631d1 Constantinos Venetsanopoulos
    try:
1417 376631d1 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
1418 376631d1 Constantinos Venetsanopoulos
    except OSError, err:
1419 376631d1 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
1420 376631d1 Constantinos Venetsanopoulos
      return False
1421 376631d1 Constantinos Venetsanopoulos
1422 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
1423 376631d1 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
1424 376631d1 Constantinos Venetsanopoulos
      return False
1425 376631d1 Constantinos Venetsanopoulos
1426 376631d1 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
1427 376631d1 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
1428 376631d1 Constantinos Venetsanopoulos
    self.attached = True
1429 376631d1 Constantinos Venetsanopoulos
1430 376631d1 Constantinos Venetsanopoulos
    return True
1431 376631d1 Constantinos Venetsanopoulos
1432 376631d1 Constantinos Venetsanopoulos
  def Assemble(self):
1433 376631d1 Constantinos Venetsanopoulos
    """Assemble the device.
1434 376631d1 Constantinos Venetsanopoulos

1435 376631d1 Constantinos Venetsanopoulos
    """
1436 376631d1 Constantinos Venetsanopoulos
    pass
1437 376631d1 Constantinos Venetsanopoulos
1438 376631d1 Constantinos Venetsanopoulos
  def Shutdown(self):
1439 376631d1 Constantinos Venetsanopoulos
    """Shutdown the device.
1440 376631d1 Constantinos Venetsanopoulos

1441 376631d1 Constantinos Venetsanopoulos
    """
1442 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1443 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1444 376631d1 Constantinos Venetsanopoulos
      return
1445 376631d1 Constantinos Venetsanopoulos
1446 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's detach script,
1447 376631d1 Constantinos Venetsanopoulos
    # to detach an existing Volume from it's block device under /dev
1448 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id,
1449 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
1450 376631d1 Constantinos Venetsanopoulos
1451 376631d1 Constantinos Venetsanopoulos
    self.minor = None
1452 376631d1 Constantinos Venetsanopoulos
    self.dev_path = None
1453 376631d1 Constantinos Venetsanopoulos
1454 376631d1 Constantinos Venetsanopoulos
  def Open(self, force=False):
1455 376631d1 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
1456 376631d1 Constantinos Venetsanopoulos

1457 376631d1 Constantinos Venetsanopoulos
    """
1458 376631d1 Constantinos Venetsanopoulos
    pass
1459 376631d1 Constantinos Venetsanopoulos
1460 376631d1 Constantinos Venetsanopoulos
  def Close(self):
1461 376631d1 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1462 376631d1 Constantinos Venetsanopoulos

1463 376631d1 Constantinos Venetsanopoulos
    """
1464 376631d1 Constantinos Venetsanopoulos
    pass
1465 376631d1 Constantinos Venetsanopoulos
1466 376631d1 Constantinos Venetsanopoulos
  def Grow(self, amount, dryrun, backingstore):
1467 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
1468 376631d1 Constantinos Venetsanopoulos

1469 376631d1 Constantinos Venetsanopoulos
    @type amount: integer
1470 376631d1 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1471 376631d1 Constantinos Venetsanopoulos
    @type dryrun: boolean
1472 376631d1 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1473 376631d1 Constantinos Venetsanopoulos
        only, without actually increasing the size
1474 376631d1 Constantinos Venetsanopoulos

1475 376631d1 Constantinos Venetsanopoulos
    """
1476 376631d1 Constantinos Venetsanopoulos
    if not backingstore:
1477 376631d1 Constantinos Venetsanopoulos
      return
1478 376631d1 Constantinos Venetsanopoulos
    if not self.Attach():
1479 89ff748d Thomas Thrainer
      base.ThrowError("Can't attach to extstorage device during Grow()")
1480 376631d1 Constantinos Venetsanopoulos
1481 376631d1 Constantinos Venetsanopoulos
    if dryrun:
1482 376631d1 Constantinos Venetsanopoulos
      # we do not support dry runs of resize operations for now.
1483 376631d1 Constantinos Venetsanopoulos
      return
1484 376631d1 Constantinos Venetsanopoulos
1485 376631d1 Constantinos Venetsanopoulos
    new_size = self.size + amount
1486 376631d1 Constantinos Venetsanopoulos
1487 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's grow script,
1488 376631d1 Constantinos Venetsanopoulos
    # to grow an existing Volume inside the External Storage
1489 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_GROW, self.unique_id,
1490 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, str(self.size), grow=str(new_size))
1491 376631d1 Constantinos Venetsanopoulos
1492 376631d1 Constantinos Venetsanopoulos
  def SetInfo(self, text):
1493 376631d1 Constantinos Venetsanopoulos
    """Update metadata with info text.
1494 376631d1 Constantinos Venetsanopoulos

1495 376631d1 Constantinos Venetsanopoulos
    """
1496 376631d1 Constantinos Venetsanopoulos
    # Replace invalid characters
1497 376631d1 Constantinos Venetsanopoulos
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
1498 376631d1 Constantinos Venetsanopoulos
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
1499 376631d1 Constantinos Venetsanopoulos
1500 376631d1 Constantinos Venetsanopoulos
    # Only up to 128 characters are allowed
1501 376631d1 Constantinos Venetsanopoulos
    text = text[:128]
1502 376631d1 Constantinos Venetsanopoulos
1503 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's setinfo script,
1504 376631d1 Constantinos Venetsanopoulos
    # to set metadata for an existing Volume inside the External Storage
1505 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
1506 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, metadata=text)
1507 376631d1 Constantinos Venetsanopoulos
1508 376631d1 Constantinos Venetsanopoulos
1509 938adc87 Constantinos Venetsanopoulos
def _ExtStorageAction(action, unique_id, ext_params,
1510 938adc87 Constantinos Venetsanopoulos
                      size=None, grow=None, metadata=None):
1511 376631d1 Constantinos Venetsanopoulos
  """Take an External Storage action.
1512 376631d1 Constantinos Venetsanopoulos

1513 376631d1 Constantinos Venetsanopoulos
  Take an External Storage action concerning or affecting
1514 376631d1 Constantinos Venetsanopoulos
  a specific Volume inside the External Storage.
1515 376631d1 Constantinos Venetsanopoulos

1516 376631d1 Constantinos Venetsanopoulos
  @type action: string
1517 376631d1 Constantinos Venetsanopoulos
  @param action: which action to perform. One of:
1518 376631d1 Constantinos Venetsanopoulos
                 create / remove / grow / attach / detach
1519 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1520 376631d1 Constantinos Venetsanopoulos
  @param unique_id: a tuple containing the type of ExtStorage (driver)
1521 376631d1 Constantinos Venetsanopoulos
                    and the Volume name
1522 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1523 938adc87 Constantinos Venetsanopoulos
  @param ext_params: ExtStorage parameters
1524 376631d1 Constantinos Venetsanopoulos
  @type size: integer
1525 376631d1 Constantinos Venetsanopoulos
  @param size: the size of the Volume in mebibytes
1526 376631d1 Constantinos Venetsanopoulos
  @type grow: integer
1527 376631d1 Constantinos Venetsanopoulos
  @param grow: the new size in mebibytes (after grow)
1528 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1529 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume, for use by the provider
1530 376631d1 Constantinos Venetsanopoulos
  @rtype: None or a block device path (during attach)
1531 376631d1 Constantinos Venetsanopoulos

1532 376631d1 Constantinos Venetsanopoulos
  """
1533 376631d1 Constantinos Venetsanopoulos
  driver, vol_name = unique_id
1534 376631d1 Constantinos Venetsanopoulos
1535 376631d1 Constantinos Venetsanopoulos
  # Create an External Storage instance of type `driver'
1536 376631d1 Constantinos Venetsanopoulos
  status, inst_es = ExtStorageFromDisk(driver)
1537 376631d1 Constantinos Venetsanopoulos
  if not status:
1538 89ff748d Thomas Thrainer
    base.ThrowError("%s" % inst_es)
1539 376631d1 Constantinos Venetsanopoulos
1540 376631d1 Constantinos Venetsanopoulos
  # Create the basic environment for the driver's scripts
1541 938adc87 Constantinos Venetsanopoulos
  create_env = _ExtStorageEnvironment(unique_id, ext_params, size,
1542 938adc87 Constantinos Venetsanopoulos
                                      grow, metadata)
1543 376631d1 Constantinos Venetsanopoulos
1544 376631d1 Constantinos Venetsanopoulos
  # Do not use log file for action `attach' as we need
1545 376631d1 Constantinos Venetsanopoulos
  # to get the output from RunResult
1546 376631d1 Constantinos Venetsanopoulos
  # TODO: find a way to have a log file for attach too
1547 376631d1 Constantinos Venetsanopoulos
  logfile = None
1548 376631d1 Constantinos Venetsanopoulos
  if action is not constants.ES_ACTION_ATTACH:
1549 376631d1 Constantinos Venetsanopoulos
    logfile = _VolumeLogName(action, driver, vol_name)
1550 376631d1 Constantinos Venetsanopoulos
1551 376631d1 Constantinos Venetsanopoulos
  # Make sure the given action results in a valid script
1552 376631d1 Constantinos Venetsanopoulos
  if action not in constants.ES_SCRIPTS:
1553 89ff748d Thomas Thrainer
    base.ThrowError("Action '%s' doesn't result in a valid ExtStorage script" %
1554 89ff748d Thomas Thrainer
                    action)
1555 376631d1 Constantinos Venetsanopoulos
1556 376631d1 Constantinos Venetsanopoulos
  # Find out which external script to run according the given action
1557 376631d1 Constantinos Venetsanopoulos
  script_name = action + "_script"
1558 376631d1 Constantinos Venetsanopoulos
  script = getattr(inst_es, script_name)
1559 376631d1 Constantinos Venetsanopoulos
1560 376631d1 Constantinos Venetsanopoulos
  # Run the external script
1561 376631d1 Constantinos Venetsanopoulos
  result = utils.RunCmd([script], env=create_env,
1562 376631d1 Constantinos Venetsanopoulos
                        cwd=inst_es.path, output=logfile,)
1563 376631d1 Constantinos Venetsanopoulos
  if result.failed:
1564 376631d1 Constantinos Venetsanopoulos
    logging.error("External storage's %s command '%s' returned"
1565 376631d1 Constantinos Venetsanopoulos
                  " error: %s, logfile: %s, output: %s",
1566 376631d1 Constantinos Venetsanopoulos
                  action, result.cmd, result.fail_reason,
1567 376631d1 Constantinos Venetsanopoulos
                  logfile, result.output)
1568 376631d1 Constantinos Venetsanopoulos
1569 376631d1 Constantinos Venetsanopoulos
    # If logfile is 'None' (during attach), it breaks TailFile
1570 376631d1 Constantinos Venetsanopoulos
    # TODO: have a log file for attach too
1571 376631d1 Constantinos Venetsanopoulos
    if action is not constants.ES_ACTION_ATTACH:
1572 376631d1 Constantinos Venetsanopoulos
      lines = [utils.SafeEncode(val)
1573 376631d1 Constantinos Venetsanopoulos
               for val in utils.TailFile(logfile, lines=20)]
1574 376631d1 Constantinos Venetsanopoulos
    else:
1575 376631d1 Constantinos Venetsanopoulos
      lines = result.output[-20:]
1576 376631d1 Constantinos Venetsanopoulos
1577 89ff748d Thomas Thrainer
    base.ThrowError("External storage's %s script failed (%s), last"
1578 89ff748d Thomas Thrainer
                    " lines of output:\n%s",
1579 89ff748d Thomas Thrainer
                    action, result.fail_reason, "\n".join(lines))
1580 376631d1 Constantinos Venetsanopoulos
1581 376631d1 Constantinos Venetsanopoulos
  if action == constants.ES_ACTION_ATTACH:
1582 376631d1 Constantinos Venetsanopoulos
    return result.stdout
1583 376631d1 Constantinos Venetsanopoulos
1584 376631d1 Constantinos Venetsanopoulos
1585 376631d1 Constantinos Venetsanopoulos
def ExtStorageFromDisk(name, base_dir=None):
1586 376631d1 Constantinos Venetsanopoulos
  """Create an ExtStorage instance from disk.
1587 376631d1 Constantinos Venetsanopoulos

1588 376631d1 Constantinos Venetsanopoulos
  This function will return an ExtStorage instance
1589 376631d1 Constantinos Venetsanopoulos
  if the given name is a valid ExtStorage name.
1590 376631d1 Constantinos Venetsanopoulos

1591 376631d1 Constantinos Venetsanopoulos
  @type base_dir: string
1592 376631d1 Constantinos Venetsanopoulos
  @keyword base_dir: Base directory containing ExtStorage installations.
1593 376631d1 Constantinos Venetsanopoulos
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
1594 376631d1 Constantinos Venetsanopoulos
  @rtype: tuple
1595 376631d1 Constantinos Venetsanopoulos
  @return: True and the ExtStorage instance if we find a valid one, or
1596 376631d1 Constantinos Venetsanopoulos
      False and the diagnose message on error
1597 376631d1 Constantinos Venetsanopoulos

1598 376631d1 Constantinos Venetsanopoulos
  """
1599 376631d1 Constantinos Venetsanopoulos
  if base_dir is None:
1600 376631d1 Constantinos Venetsanopoulos
    es_base_dir = pathutils.ES_SEARCH_PATH
1601 376631d1 Constantinos Venetsanopoulos
  else:
1602 376631d1 Constantinos Venetsanopoulos
    es_base_dir = [base_dir]
1603 376631d1 Constantinos Venetsanopoulos
1604 376631d1 Constantinos Venetsanopoulos
  es_dir = utils.FindFile(name, es_base_dir, os.path.isdir)
1605 376631d1 Constantinos Venetsanopoulos
1606 376631d1 Constantinos Venetsanopoulos
  if es_dir is None:
1607 376631d1 Constantinos Venetsanopoulos
    return False, ("Directory for External Storage Provider %s not"
1608 376631d1 Constantinos Venetsanopoulos
                   " found in search path" % name)
1609 376631d1 Constantinos Venetsanopoulos
1610 376631d1 Constantinos Venetsanopoulos
  # ES Files dictionary, we will populate it with the absolute path
1611 376631d1 Constantinos Venetsanopoulos
  # names; if the value is True, then it is a required file, otherwise
1612 376631d1 Constantinos Venetsanopoulos
  # an optional one
1613 376631d1 Constantinos Venetsanopoulos
  es_files = dict.fromkeys(constants.ES_SCRIPTS, True)
1614 376631d1 Constantinos Venetsanopoulos
1615 938adc87 Constantinos Venetsanopoulos
  es_files[constants.ES_PARAMETERS_FILE] = True
1616 938adc87 Constantinos Venetsanopoulos
1617 938adc87 Constantinos Venetsanopoulos
  for (filename, _) in es_files.items():
1618 376631d1 Constantinos Venetsanopoulos
    es_files[filename] = utils.PathJoin(es_dir, filename)
1619 376631d1 Constantinos Venetsanopoulos
1620 376631d1 Constantinos Venetsanopoulos
    try:
1621 376631d1 Constantinos Venetsanopoulos
      st = os.stat(es_files[filename])
1622 376631d1 Constantinos Venetsanopoulos
    except EnvironmentError, err:
1623 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is missing (%s)" %
1624 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir, utils.ErrnoOrStr(err)))
1625 376631d1 Constantinos Venetsanopoulos
1626 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1627 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is not a regular file" %
1628 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir))
1629 376631d1 Constantinos Venetsanopoulos
1630 376631d1 Constantinos Venetsanopoulos
    if filename in constants.ES_SCRIPTS:
1631 376631d1 Constantinos Venetsanopoulos
      if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
1632 376631d1 Constantinos Venetsanopoulos
        return False, ("File '%s' under path '%s' is not executable" %
1633 376631d1 Constantinos Venetsanopoulos
                       (filename, es_dir))
1634 376631d1 Constantinos Venetsanopoulos
1635 938adc87 Constantinos Venetsanopoulos
  parameters = []
1636 938adc87 Constantinos Venetsanopoulos
  if constants.ES_PARAMETERS_FILE in es_files:
1637 938adc87 Constantinos Venetsanopoulos
    parameters_file = es_files[constants.ES_PARAMETERS_FILE]
1638 938adc87 Constantinos Venetsanopoulos
    try:
1639 938adc87 Constantinos Venetsanopoulos
      parameters = utils.ReadFile(parameters_file).splitlines()
1640 938adc87 Constantinos Venetsanopoulos
    except EnvironmentError, err:
1641 938adc87 Constantinos Venetsanopoulos
      return False, ("Error while reading the EXT parameters file at %s: %s" %
1642 938adc87 Constantinos Venetsanopoulos
                     (parameters_file, utils.ErrnoOrStr(err)))
1643 938adc87 Constantinos Venetsanopoulos
    parameters = [v.split(None, 1) for v in parameters]
1644 938adc87 Constantinos Venetsanopoulos
1645 376631d1 Constantinos Venetsanopoulos
  es_obj = \
1646 376631d1 Constantinos Venetsanopoulos
    objects.ExtStorage(name=name, path=es_dir,
1647 376631d1 Constantinos Venetsanopoulos
                       create_script=es_files[constants.ES_SCRIPT_CREATE],
1648 376631d1 Constantinos Venetsanopoulos
                       remove_script=es_files[constants.ES_SCRIPT_REMOVE],
1649 376631d1 Constantinos Venetsanopoulos
                       grow_script=es_files[constants.ES_SCRIPT_GROW],
1650 376631d1 Constantinos Venetsanopoulos
                       attach_script=es_files[constants.ES_SCRIPT_ATTACH],
1651 376631d1 Constantinos Venetsanopoulos
                       detach_script=es_files[constants.ES_SCRIPT_DETACH],
1652 938adc87 Constantinos Venetsanopoulos
                       setinfo_script=es_files[constants.ES_SCRIPT_SETINFO],
1653 938adc87 Constantinos Venetsanopoulos
                       verify_script=es_files[constants.ES_SCRIPT_VERIFY],
1654 938adc87 Constantinos Venetsanopoulos
                       supported_parameters=parameters)
1655 376631d1 Constantinos Venetsanopoulos
  return True, es_obj
1656 376631d1 Constantinos Venetsanopoulos
1657 376631d1 Constantinos Venetsanopoulos
1658 938adc87 Constantinos Venetsanopoulos
def _ExtStorageEnvironment(unique_id, ext_params,
1659 938adc87 Constantinos Venetsanopoulos
                           size=None, grow=None, metadata=None):
1660 376631d1 Constantinos Venetsanopoulos
  """Calculate the environment for an External Storage script.
1661 376631d1 Constantinos Venetsanopoulos

1662 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1663 376631d1 Constantinos Venetsanopoulos
  @param unique_id: ExtStorage pool and name of the Volume
1664 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1665 938adc87 Constantinos Venetsanopoulos
  @param ext_params: the EXT parameters
1666 376631d1 Constantinos Venetsanopoulos
  @type size: string
1667 376631d1 Constantinos Venetsanopoulos
  @param size: size of the Volume (in mebibytes)
1668 376631d1 Constantinos Venetsanopoulos
  @type grow: string
1669 376631d1 Constantinos Venetsanopoulos
  @param grow: new size of Volume after grow (in mebibytes)
1670 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1671 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume
1672 376631d1 Constantinos Venetsanopoulos
  @rtype: dict
1673 376631d1 Constantinos Venetsanopoulos
  @return: dict of environment variables
1674 376631d1 Constantinos Venetsanopoulos

1675 376631d1 Constantinos Venetsanopoulos
  """
1676 376631d1 Constantinos Venetsanopoulos
  vol_name = unique_id[1]
1677 376631d1 Constantinos Venetsanopoulos
1678 376631d1 Constantinos Venetsanopoulos
  result = {}
1679 376631d1 Constantinos Venetsanopoulos
  result["VOL_NAME"] = vol_name
1680 376631d1 Constantinos Venetsanopoulos
1681 938adc87 Constantinos Venetsanopoulos
  # EXT params
1682 938adc87 Constantinos Venetsanopoulos
  for pname, pvalue in ext_params.items():
1683 938adc87 Constantinos Venetsanopoulos
    result["EXTP_%s" % pname.upper()] = str(pvalue)
1684 938adc87 Constantinos Venetsanopoulos
1685 376631d1 Constantinos Venetsanopoulos
  if size is not None:
1686 376631d1 Constantinos Venetsanopoulos
    result["VOL_SIZE"] = size
1687 376631d1 Constantinos Venetsanopoulos
1688 376631d1 Constantinos Venetsanopoulos
  if grow is not None:
1689 376631d1 Constantinos Venetsanopoulos
    result["VOL_NEW_SIZE"] = grow
1690 376631d1 Constantinos Venetsanopoulos
1691 376631d1 Constantinos Venetsanopoulos
  if metadata is not None:
1692 376631d1 Constantinos Venetsanopoulos
    result["VOL_METADATA"] = metadata
1693 376631d1 Constantinos Venetsanopoulos
1694 376631d1 Constantinos Venetsanopoulos
  return result
1695 376631d1 Constantinos Venetsanopoulos
1696 376631d1 Constantinos Venetsanopoulos
1697 376631d1 Constantinos Venetsanopoulos
def _VolumeLogName(kind, es_name, volume):
1698 376631d1 Constantinos Venetsanopoulos
  """Compute the ExtStorage log filename for a given Volume and operation.
1699 376631d1 Constantinos Venetsanopoulos

1700 376631d1 Constantinos Venetsanopoulos
  @type kind: string
1701 376631d1 Constantinos Venetsanopoulos
  @param kind: the operation type (e.g. create, remove etc.)
1702 376631d1 Constantinos Venetsanopoulos
  @type es_name: string
1703 376631d1 Constantinos Venetsanopoulos
  @param es_name: the ExtStorage name
1704 376631d1 Constantinos Venetsanopoulos
  @type volume: string
1705 376631d1 Constantinos Venetsanopoulos
  @param volume: the name of the Volume inside the External Storage
1706 376631d1 Constantinos Venetsanopoulos

1707 376631d1 Constantinos Venetsanopoulos
  """
1708 376631d1 Constantinos Venetsanopoulos
  # Check if the extstorage log dir is a valid dir
1709 376631d1 Constantinos Venetsanopoulos
  if not os.path.isdir(pathutils.LOG_ES_DIR):
1710 89ff748d Thomas Thrainer
    base.ThrowError("Cannot find log directory: %s", pathutils.LOG_ES_DIR)
1711 376631d1 Constantinos Venetsanopoulos
1712 376631d1 Constantinos Venetsanopoulos
  # TODO: Use tempfile.mkstemp to create unique filename
1713 89ff748d Thomas Thrainer
  basename = ("%s-%s-%s-%s.log" %
1714 89ff748d Thomas Thrainer
              (kind, es_name, volume, utils.TimestampForFilename()))
1715 89ff748d Thomas Thrainer
  return utils.PathJoin(pathutils.LOG_ES_DIR, basename)
1716 376631d1 Constantinos Venetsanopoulos
1717 376631d1 Constantinos Venetsanopoulos
1718 a8083063 Iustin Pop
DEV_MAP = {
1719 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1720 89ff748d Thomas Thrainer
  constants.LD_DRBD8: drbd.DRBD8,
1721 b6135bbc Apollon Oikonomopoulos
  constants.LD_BLOCKDEV: PersistentBlockDevice,
1722 7181fba0 Constantinos Venetsanopoulos
  constants.LD_RBD: RADOSBlockDevice,
1723 376631d1 Constantinos Venetsanopoulos
  constants.LD_EXT: ExtStorageDevice,
1724 a8083063 Iustin Pop
  }
1725 a8083063 Iustin Pop
1726 4b97f902 Apollon Oikonomopoulos
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
1727 cb7c0198 Iustin Pop
  DEV_MAP[constants.LD_FILE] = FileStorage
1728 cb7c0198 Iustin Pop
1729 a8083063 Iustin Pop
1730 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
1731 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
1732 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1733 94dcbdb0 Andrea Spadaccini
1734 94dcbdb0 Andrea Spadaccini
1735 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
1736 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
1737 5ff82cc9 René Nussbaumer

1738 5ff82cc9 René Nussbaumer
  """
1739 5ff82cc9 René Nussbaumer
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
1740 5ff82cc9 René Nussbaumer
  if missing:
1741 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
1742 5ff82cc9 René Nussbaumer
                                 missing)
1743 5ff82cc9 René Nussbaumer
1744 5ff82cc9 René Nussbaumer
1745 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
1746 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1747 a8083063 Iustin Pop

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

1751 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1752 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
1753 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1754 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1755 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1756 94dcbdb0 Andrea Spadaccini

1757 a8083063 Iustin Pop
  """
1758 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1759 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
1760 c7c6606d René Nussbaumer
                                  disk.params)
1761 cb999543 Iustin Pop
  if not device.attached:
1762 a8083063 Iustin Pop
    return None
1763 ecb091e3 Iustin Pop
  return device
1764 a8083063 Iustin Pop
1765 a8083063 Iustin Pop
1766 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
1767 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1768 a8083063 Iustin Pop

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

1772 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1773 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
1774 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1775 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1776 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1777 94dcbdb0 Andrea Spadaccini

1778 a8083063 Iustin Pop
  """
1779 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1780 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1781 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
1782 c7c6606d René Nussbaumer
                                  disk.params)
1783 1063abd1 Iustin Pop
  device.Assemble()
1784 a8083063 Iustin Pop
  return device
1785 a8083063 Iustin Pop
1786 a8083063 Iustin Pop
1787 ee1478e5 Bernardo Dal Seno
def Create(disk, children, excl_stor):
1788 a8083063 Iustin Pop
  """Create a device.
1789 a8083063 Iustin Pop

1790 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1791 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
1792 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1793 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1794 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1795 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
1796 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
1797 94dcbdb0 Andrea Spadaccini

1798 a8083063 Iustin Pop
  """
1799 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1800 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1801 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
1802 ee1478e5 Bernardo Dal Seno
                                         disk.params, excl_stor)
1803 a8083063 Iustin Pop
  return device