Statistics
| Branch: | Tag: | Revision:

root / lib / storage / bdev.py @ 24c06acb

History | View | Annotate | Download (55.9 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

173 a8083063 Iustin Pop
  """
174 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
175 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 24c06acb Bernardo Dal Seno
  def Create(cls, unique_id, children, size, spindles, 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 24c06acb Bernardo Dal Seno
      if spindles:
269 24c06acb Bernardo Dal Seno
        if spindles < req_pvs:
270 24c06acb Bernardo Dal Seno
          base.ThrowError("Requested number of spindles (%s) is not enough for"
271 24c06acb Bernardo Dal Seno
                          " a disk of %d MB (at least %d spindles needed)",
272 24c06acb Bernardo Dal Seno
                          spindles, size, req_pvs)
273 24c06acb Bernardo Dal Seno
        else:
274 24c06acb Bernardo Dal Seno
          req_pvs = spindles
275 63c73073 Bernardo Dal Seno
      pvlist = cls._GetEmptyPvNames(pvs_info, req_pvs)
276 63c73073 Bernardo Dal Seno
      current_pvs = len(pvlist)
277 63c73073 Bernardo Dal Seno
      if current_pvs < req_pvs:
278 24c06acb Bernardo Dal Seno
        base.ThrowError("Not enough empty PVs (spindles) to create a disk of %d"
279 24c06acb Bernardo Dal Seno
                        " MB: %d available, %d needed",
280 24c06acb Bernardo Dal Seno
                        size, current_pvs, req_pvs)
281 63c73073 Bernardo Dal Seno
      assert current_pvs == len(pvlist)
282 63c73073 Bernardo Dal Seno
      if stripes > current_pvs:
283 63c73073 Bernardo Dal Seno
        # No warning issued for this, as it's no surprise
284 63c73073 Bernardo Dal Seno
        stripes = current_pvs
285 63c73073 Bernardo Dal Seno
286 63c73073 Bernardo Dal Seno
    else:
287 63c73073 Bernardo Dal Seno
      if stripes < desired_stripes:
288 63c73073 Bernardo Dal Seno
        logging.warning("Could not use %d stripes for VG %s, as only %d PVs are"
289 63c73073 Bernardo Dal Seno
                        " available.", desired_stripes, vg_name, current_pvs)
290 63c73073 Bernardo Dal Seno
      free_size = sum([pv.free for pv in pvs_info])
291 63c73073 Bernardo Dal Seno
      # The size constraint should have been checked from the master before
292 63c73073 Bernardo Dal Seno
      # calling the create function.
293 63c73073 Bernardo Dal Seno
      if free_size < size:
294 89ff748d Thomas Thrainer
        base.ThrowError("Not enough free space: required %s,"
295 89ff748d Thomas Thrainer
                        " available %s", size, free_size)
296 63c73073 Bernardo Dal Seno
297 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
298 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
299 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
300 fecbe9d5 Iustin Pop
    # stripes
301 63c73073 Bernardo Dal Seno
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
302 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
303 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
304 fecbe9d5 Iustin Pop
      if not result.failed:
305 fecbe9d5 Iustin Pop
        break
306 a8083063 Iustin Pop
    if result.failed:
307 89ff748d Thomas Thrainer
      base.ThrowError("LV create failed (%s): %s",
308 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
309 94dcbdb0 Andrea Spadaccini
    return LogicalVolume(unique_id, children, size, params)
310 a8083063 Iustin Pop
311 a8083063 Iustin Pop
  @staticmethod
312 197478f2 René Nussbaumer
  def _GetVolumeInfo(lvm_cmd, fields):
313 511e00b8 Helga Velroyen
    """Returns LVM Volume infos using lvm_cmd
314 197478f2 René Nussbaumer

315 197478f2 René Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
316 197478f2 René Nussbaumer
    @param fields: Fields to return
317 197478f2 René Nussbaumer
    @return: A list of dicts each with the parsed fields
318 197478f2 René Nussbaumer

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

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

352 c41eea6e Iustin Pop
    @rtype: list
353 59726e15 Bernardo Dal Seno
    @return: list of objects.LvmPvInfo objects
354 098c0958 Michael Hanselmann

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

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

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

425 197478f2 René Nussbaumer
    @rtype: list
426 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
427 673cd9c4 René Nussbaumer
             MiB
428 197478f2 René Nussbaumer

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

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

462 6136f8f0 Iustin Pop
    """
463 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
464 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
465 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
466 89ff748d Thomas Thrainer
      base.ThrowError("Invalid LVM name '%s'", name)
467 6136f8f0 Iustin Pop
468 a8083063 Iustin Pop
  def Remove(self):
469 a8083063 Iustin Pop
    """Remove this logical volume.
470 a8083063 Iustin Pop

471 a8083063 Iustin Pop
    """
472 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
473 a8083063 Iustin Pop
      # the LV does not exist
474 0c6c04ec Iustin Pop
      return
475 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
476 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
477 a8083063 Iustin Pop
    if result.failed:
478 89ff748d Thomas Thrainer
      base.ThrowError("Can't lvremove: %s - %s",
479 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
480 a8083063 Iustin Pop
481 f3e513ad Iustin Pop
  def Rename(self, new_id):
482 f3e513ad Iustin Pop
    """Rename this logical volume.
483 f3e513ad Iustin Pop

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

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

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

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

570 a8083063 Iustin Pop
    """
571 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
572 5574047a Iustin Pop
    if result.failed:
573 89ff748d Thomas Thrainer
      base.ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
574 a8083063 Iustin Pop
575 a8083063 Iustin Pop
  def Shutdown(self):
576 a8083063 Iustin Pop
    """Shutdown the device.
577 a8083063 Iustin Pop

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

581 a8083063 Iustin Pop
    """
582 746f7476 Iustin Pop
    pass
583 a8083063 Iustin Pop
584 9db6dbce Iustin Pop
  def GetSyncStatus(self):
585 9db6dbce Iustin Pop
    """Returns the sync status of the device.
586 9db6dbce Iustin Pop

587 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
588 9db6dbce Iustin Pop
    status of the mirror.
589 9db6dbce Iustin Pop

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

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

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

603 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
604 c41eea6e Iustin Pop

605 9db6dbce Iustin Pop
    """
606 f208978a Michael Hanselmann
    if self._degraded:
607 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
608 f208978a Michael Hanselmann
    else:
609 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
610 f208978a Michael Hanselmann
611 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
612 96acbc09 Michael Hanselmann
                                  major=self.major,
613 96acbc09 Michael Hanselmann
                                  minor=self.minor,
614 96acbc09 Michael Hanselmann
                                  sync_percent=None,
615 96acbc09 Michael Hanselmann
                                  estimated_time=None,
616 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
617 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
618 9db6dbce Iustin Pop
619 a8083063 Iustin Pop
  def Open(self, force=False):
620 a8083063 Iustin Pop
    """Make the device ready 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 Close(self):
628 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
629 a8083063 Iustin Pop

630 a8083063 Iustin Pop
    This is a no-op for the LV device type.
631 a8083063 Iustin Pop

632 a8083063 Iustin Pop
    """
633 fdbd668d Iustin Pop
    pass
634 a8083063 Iustin Pop
635 a8083063 Iustin Pop
  def Snapshot(self, size):
636 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
637 a8083063 Iustin Pop

638 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
639 800ac399 Iustin Pop

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

663 a1556cfa Iustin Pop
    """
664 a1556cfa Iustin Pop
    result = utils.RunCmd(["lvs", "-o", "tags", "--noheadings", "--nosuffix",
665 a1556cfa Iustin Pop
                           self.dev_path])
666 a1556cfa Iustin Pop
    _CheckResult(result)
667 a1556cfa Iustin Pop
668 a1556cfa Iustin Pop
    raw_tags = result.stdout.strip()
669 a1556cfa Iustin Pop
    if raw_tags:
670 a1556cfa Iustin Pop
      for tag in raw_tags.split(","):
671 a1556cfa Iustin Pop
        _CheckResult(utils.RunCmd(["lvchange", "--deltag",
672 a1556cfa Iustin Pop
                                   tag.strip(), self.dev_path]))
673 a1556cfa Iustin Pop
674 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
675 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
676 a0c3fea1 Michael Hanselmann

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

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

723 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
724 6f695a2e Manuel Franceschini

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

727 6f695a2e Manuel Franceschini
  """
728 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
729 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
730 6f695a2e Manuel Franceschini

731 6f695a2e Manuel Franceschini
    """
732 6f695a2e Manuel Franceschini
    if children:
733 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
734 94dcbdb0 Andrea Spadaccini
    super(FileStorage, self).__init__(unique_id, children, size, params)
735 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
736 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
737 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
738 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
739 5e09a309 Michael Hanselmann
740 5e09a309 Michael Hanselmann
    CheckFileStoragePath(self.dev_path)
741 5e09a309 Michael Hanselmann
742 ecb091e3 Iustin Pop
    self.Attach()
743 6f695a2e Manuel Franceschini
744 6f695a2e Manuel Franceschini
  def Assemble(self):
745 6f695a2e Manuel Franceschini
    """Assemble the device.
746 6f695a2e Manuel Franceschini

747 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
748 6f695a2e Manuel Franceschini

749 6f695a2e Manuel Franceschini
    """
750 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
751 89ff748d Thomas Thrainer
      base.ThrowError("File device '%s' does not exist" % self.dev_path)
752 6f695a2e Manuel Franceschini
753 6f695a2e Manuel Franceschini
  def Shutdown(self):
754 6f695a2e Manuel Franceschini
    """Shutdown the device.
755 6f695a2e Manuel Franceschini

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

759 6f695a2e Manuel Franceschini
    """
760 746f7476 Iustin Pop
    pass
761 6f695a2e Manuel Franceschini
762 6f695a2e Manuel Franceschini
  def Open(self, force=False):
763 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
764 6f695a2e Manuel Franceschini

765 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
766 6f695a2e Manuel Franceschini

767 6f695a2e Manuel Franceschini
    """
768 6f695a2e Manuel Franceschini
    pass
769 6f695a2e Manuel Franceschini
770 6f695a2e Manuel Franceschini
  def Close(self):
771 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
772 6f695a2e Manuel Franceschini

773 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
774 6f695a2e Manuel Franceschini

775 6f695a2e Manuel Franceschini
    """
776 6f695a2e Manuel Franceschini
    pass
777 6f695a2e Manuel Franceschini
778 6f695a2e Manuel Franceschini
  def Remove(self):
779 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
780 6f695a2e Manuel Franceschini

781 c41eea6e Iustin Pop
    @rtype: boolean
782 c41eea6e Iustin Pop
    @return: True if the removal was successful
783 6f695a2e Manuel Franceschini

784 6f695a2e Manuel Franceschini
    """
785 6f695a2e Manuel Franceschini
    try:
786 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
787 6f695a2e Manuel Franceschini
    except OSError, err:
788 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
789 89ff748d Thomas Thrainer
        base.ThrowError("Can't remove file '%s': %s", self.dev_path, err)
790 6f695a2e Manuel Franceschini
791 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
792 bbe4cc16 Iustin Pop
    """Renames the file.
793 bbe4cc16 Iustin Pop

794 bbe4cc16 Iustin Pop
    """
795 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
796 89ff748d Thomas Thrainer
    base.ThrowError("Rename is not supported for file-based storage")
797 bbe4cc16 Iustin Pop
798 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
799 bbe4cc16 Iustin Pop
    """Grow the file
800 bbe4cc16 Iustin Pop

801 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
802 bbe4cc16 Iustin Pop

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

824 6f695a2e Manuel Franceschini
    Check if this file already exists.
825 6f695a2e Manuel Franceschini

826 c41eea6e Iustin Pop
    @rtype: boolean
827 c41eea6e Iustin Pop
    @return: True if file exists
828 6f695a2e Manuel Franceschini

829 6f695a2e Manuel Franceschini
    """
830 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
831 ecb091e3 Iustin Pop
    return self.attached
832 6f695a2e Manuel Franceschini
833 fcff3897 Iustin Pop
  def GetActualSize(self):
834 fcff3897 Iustin Pop
    """Return the actual disk size.
835 fcff3897 Iustin Pop

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

838 fcff3897 Iustin Pop
    """
839 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
840 fcff3897 Iustin Pop
    try:
841 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
842 fcff3897 Iustin Pop
      return st.st_size
843 fcff3897 Iustin Pop
    except OSError, err:
844 89ff748d Thomas Thrainer
      base.ThrowError("Can't stat %s: %s", self.dev_path, err)
845 fcff3897 Iustin Pop
846 6f695a2e Manuel Franceschini
  @classmethod
847 24c06acb Bernardo Dal Seno
  def Create(cls, unique_id, children, size, spindles, params, excl_stor):
848 6f695a2e Manuel Franceschini
    """Create a new file.
849 6f695a2e Manuel Franceschini

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

852 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
853 c41eea6e Iustin Pop
    @return: an instance of FileStorage
854 6f695a2e Manuel Franceschini

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

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

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

888 b6135bbc Apollon Oikonomopoulos
  """
889 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
890 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
891 b6135bbc Apollon Oikonomopoulos

892 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
893 b6135bbc Apollon Oikonomopoulos

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

918 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
919 b6135bbc Apollon Oikonomopoulos

920 b6135bbc Apollon Oikonomopoulos
    """
921 ee1478e5 Bernardo Dal Seno
    if excl_stor:
922 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("Persistent block device requested with"
923 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
924 94dcbdb0 Andrea Spadaccini
    return PersistentBlockDevice(unique_id, children, 0, params)
925 b6135bbc Apollon Oikonomopoulos
926 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
927 b6135bbc Apollon Oikonomopoulos
    """Remove a device
928 b6135bbc Apollon Oikonomopoulos

929 b6135bbc Apollon Oikonomopoulos
    This is a noop
930 b6135bbc Apollon Oikonomopoulos

931 b6135bbc Apollon Oikonomopoulos
    """
932 b6135bbc Apollon Oikonomopoulos
    pass
933 b6135bbc Apollon Oikonomopoulos
934 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
935 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
936 b6135bbc Apollon Oikonomopoulos

937 b6135bbc Apollon Oikonomopoulos
    """
938 89ff748d Thomas Thrainer
    base.ThrowError("Rename is not supported for PersistentBlockDev storage")
939 b6135bbc Apollon Oikonomopoulos
940 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
941 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
942 b6135bbc Apollon Oikonomopoulos

943 b6135bbc Apollon Oikonomopoulos

944 b6135bbc Apollon Oikonomopoulos
    """
945 b6135bbc Apollon Oikonomopoulos
    self.attached = False
946 b6135bbc Apollon Oikonomopoulos
    try:
947 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
948 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
949 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
950 b6135bbc Apollon Oikonomopoulos
      return False
951 b6135bbc Apollon Oikonomopoulos
952 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
953 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
954 b6135bbc Apollon Oikonomopoulos
      return False
955 b6135bbc Apollon Oikonomopoulos
956 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
957 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
958 b6135bbc Apollon Oikonomopoulos
    self.attached = True
959 b6135bbc Apollon Oikonomopoulos
960 b6135bbc Apollon Oikonomopoulos
    return True
961 b6135bbc Apollon Oikonomopoulos
962 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
963 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
964 b6135bbc Apollon Oikonomopoulos

965 b6135bbc Apollon Oikonomopoulos
    """
966 b6135bbc Apollon Oikonomopoulos
    pass
967 b6135bbc Apollon Oikonomopoulos
968 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
969 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
970 b6135bbc Apollon Oikonomopoulos

971 b6135bbc Apollon Oikonomopoulos
    """
972 b6135bbc Apollon Oikonomopoulos
    pass
973 b6135bbc Apollon Oikonomopoulos
974 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
975 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
976 b6135bbc Apollon Oikonomopoulos

977 b6135bbc Apollon Oikonomopoulos
    """
978 b6135bbc Apollon Oikonomopoulos
    pass
979 b6135bbc Apollon Oikonomopoulos
980 b6135bbc Apollon Oikonomopoulos
  def Close(self):
981 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
982 b6135bbc Apollon Oikonomopoulos

983 b6135bbc Apollon Oikonomopoulos
    """
984 b6135bbc Apollon Oikonomopoulos
    pass
985 b6135bbc Apollon Oikonomopoulos
986 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
987 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
988 b6135bbc Apollon Oikonomopoulos

989 b6135bbc Apollon Oikonomopoulos
    """
990 89ff748d Thomas Thrainer
    base.ThrowError("Grow is not supported for PersistentBlockDev storage")
991 b6135bbc Apollon Oikonomopoulos
992 b6135bbc Apollon Oikonomopoulos
993 89ff748d Thomas Thrainer
class RADOSBlockDevice(base.BlockDev):
994 7181fba0 Constantinos Venetsanopoulos
  """A RADOS Block Device (rbd).
995 7181fba0 Constantinos Venetsanopoulos

996 7181fba0 Constantinos Venetsanopoulos
  This class implements the RADOS Block Device for the backend. You need
997 7181fba0 Constantinos Venetsanopoulos
  the rbd kernel driver, the RADOS Tools and a working RADOS cluster for
998 7181fba0 Constantinos Venetsanopoulos
  this to be functional.
999 7181fba0 Constantinos Venetsanopoulos

1000 7181fba0 Constantinos Venetsanopoulos
  """
1001 7181fba0 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
1002 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
1003 7181fba0 Constantinos Venetsanopoulos

1004 7181fba0 Constantinos Venetsanopoulos
    """
1005 7181fba0 Constantinos Venetsanopoulos
    super(RADOSBlockDevice, self).__init__(unique_id, children, size, params)
1006 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1007 7181fba0 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1008 7181fba0 Constantinos Venetsanopoulos
1009 7181fba0 Constantinos Venetsanopoulos
    self.driver, self.rbd_name = unique_id
1010 7181fba0 Constantinos Venetsanopoulos
1011 7181fba0 Constantinos Venetsanopoulos
    self.major = self.minor = None
1012 7181fba0 Constantinos Venetsanopoulos
    self.Attach()
1013 7181fba0 Constantinos Venetsanopoulos
1014 7181fba0 Constantinos Venetsanopoulos
  @classmethod
1015 24c06acb Bernardo Dal Seno
  def Create(cls, unique_id, children, size, spindles, params, excl_stor):
1016 7181fba0 Constantinos Venetsanopoulos
    """Create a new rbd device.
1017 7181fba0 Constantinos Venetsanopoulos

1018 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
1019 7181fba0 Constantinos Venetsanopoulos

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

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

1064 7181fba0 Constantinos Venetsanopoulos
    """
1065 7181fba0 Constantinos Venetsanopoulos
    pass
1066 7181fba0 Constantinos Venetsanopoulos
1067 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
1068 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
1069 7181fba0 Constantinos Venetsanopoulos

1070 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
1071 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
1072 7181fba0 Constantinos Venetsanopoulos

1073 7181fba0 Constantinos Venetsanopoulos
    """
1074 7181fba0 Constantinos Venetsanopoulos
    self.attached = False
1075 7181fba0 Constantinos Venetsanopoulos
1076 7181fba0 Constantinos Venetsanopoulos
    # Map the rbd volume to a block device under /dev
1077 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = self._MapVolumeToBlockdev(self.unique_id)
1078 7181fba0 Constantinos Venetsanopoulos
1079 7181fba0 Constantinos Venetsanopoulos
    try:
1080 7181fba0 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
1081 7181fba0 Constantinos Venetsanopoulos
    except OSError, err:
1082 7181fba0 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
1083 7181fba0 Constantinos Venetsanopoulos
      return False
1084 7181fba0 Constantinos Venetsanopoulos
1085 7181fba0 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
1086 7181fba0 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
1087 7181fba0 Constantinos Venetsanopoulos
      return False
1088 7181fba0 Constantinos Venetsanopoulos
1089 7181fba0 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
1090 7181fba0 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
1091 7181fba0 Constantinos Venetsanopoulos
    self.attached = True
1092 7181fba0 Constantinos Venetsanopoulos
1093 7181fba0 Constantinos Venetsanopoulos
    return True
1094 7181fba0 Constantinos Venetsanopoulos
1095 7181fba0 Constantinos Venetsanopoulos
  def _MapVolumeToBlockdev(self, unique_id):
1096 7181fba0 Constantinos Venetsanopoulos
    """Maps existing rbd volumes to block devices.
1097 7181fba0 Constantinos Venetsanopoulos

1098 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
1099 7181fba0 Constantinos Venetsanopoulos

1100 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
1101 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
1102 7181fba0 Constantinos Venetsanopoulos

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

1133 bdecfea2 Stratos Psomadakis
    @type pool: string
1134 bdecfea2 Stratos Psomadakis
    @param pool: RADOS pool to use
1135 bdecfea2 Stratos Psomadakis
    @type volume_name: string
1136 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
1137 bdecfea2 Stratos Psomadakis
    @rtype: string or None
1138 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
1139 bdecfea2 Stratos Psomadakis

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

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

1178 bdecfea2 Stratos Psomadakis
    @type output: string
1179 bdecfea2 Stratos Psomadakis
    @param output: the json output of `rbd showmapped'
1180 bdecfea2 Stratos Psomadakis
    @type volume_name: string
1181 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
1182 bdecfea2 Stratos Psomadakis
    @rtype: string or None
1183 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
1184 bdecfea2 Stratos Psomadakis

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

1210 7181fba0 Constantinos Venetsanopoulos
    This method parses the output of `rbd showmapped' and returns
1211 7181fba0 Constantinos Venetsanopoulos
    the rbd block device path (e.g. /dev/rbd0) that matches the
1212 7181fba0 Constantinos Venetsanopoulos
    given rbd volume.
1213 7181fba0 Constantinos Venetsanopoulos

1214 7181fba0 Constantinos Venetsanopoulos
    @type output: string
1215 bdecfea2 Stratos Psomadakis
    @param output: the plain text output of `rbd showmapped'
1216 7181fba0 Constantinos Venetsanopoulos
    @type volume_name: string
1217 7181fba0 Constantinos Venetsanopoulos
    @param volume_name: the name of the volume whose device we search for
1218 7181fba0 Constantinos Venetsanopoulos
    @rtype: string or None
1219 7181fba0 Constantinos Venetsanopoulos
    @return: block device path if the volume is mapped, else None
1220 7181fba0 Constantinos Venetsanopoulos

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

1263 7181fba0 Constantinos Venetsanopoulos
    """
1264 7181fba0 Constantinos Venetsanopoulos
    pass
1265 7181fba0 Constantinos Venetsanopoulos
1266 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
1267 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
1268 7181fba0 Constantinos Venetsanopoulos

1269 7181fba0 Constantinos Venetsanopoulos
    """
1270 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1271 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
1272 7181fba0 Constantinos Venetsanopoulos
      return
1273 7181fba0 Constantinos Venetsanopoulos
1274 7181fba0 Constantinos Venetsanopoulos
    # Unmap the block device from the Volume.
1275 7181fba0 Constantinos Venetsanopoulos
    self._UnmapVolumeFromBlockdev(self.unique_id)
1276 7181fba0 Constantinos Venetsanopoulos
1277 7181fba0 Constantinos Venetsanopoulos
    self.minor = None
1278 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = None
1279 7181fba0 Constantinos Venetsanopoulos
1280 7181fba0 Constantinos Venetsanopoulos
  def _UnmapVolumeFromBlockdev(self, unique_id):
1281 7181fba0 Constantinos Venetsanopoulos
    """Unmaps the rbd device from the Volume it is mapped.
1282 7181fba0 Constantinos Venetsanopoulos

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

1286 7181fba0 Constantinos Venetsanopoulos
    """
1287 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
1288 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
1289 7181fba0 Constantinos Venetsanopoulos
1290 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
1291 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
1292 7181fba0 Constantinos Venetsanopoulos
1293 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
1294 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Unmap the rbd device.
1295 7181fba0 Constantinos Venetsanopoulos
      unmap_cmd = [constants.RBD_CMD, "unmap", "%s" % rbd_dev]
1296 7181fba0 Constantinos Venetsanopoulos
      result = utils.RunCmd(unmap_cmd)
1297 7181fba0 Constantinos Venetsanopoulos
      if result.failed:
1298 89ff748d Thomas Thrainer
        base.ThrowError("rbd unmap failed (%s): %s",
1299 89ff748d Thomas Thrainer
                        result.fail_reason, result.output)
1300 7181fba0 Constantinos Venetsanopoulos
1301 7181fba0 Constantinos Venetsanopoulos
  def Open(self, force=False):
1302 7181fba0 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
1303 7181fba0 Constantinos Venetsanopoulos

1304 7181fba0 Constantinos Venetsanopoulos
    """
1305 7181fba0 Constantinos Venetsanopoulos
    pass
1306 7181fba0 Constantinos Venetsanopoulos
1307 7181fba0 Constantinos Venetsanopoulos
  def Close(self):
1308 7181fba0 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1309 7181fba0 Constantinos Venetsanopoulos

1310 7181fba0 Constantinos Venetsanopoulos
    """
1311 7181fba0 Constantinos Venetsanopoulos
    pass
1312 7181fba0 Constantinos Venetsanopoulos
1313 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
1314 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
1315 7181fba0 Constantinos Venetsanopoulos

1316 7181fba0 Constantinos Venetsanopoulos
    @type amount: integer
1317 7181fba0 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1318 7181fba0 Constantinos Venetsanopoulos
    @type dryrun: boolean
1319 7181fba0 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1320 7181fba0 Constantinos Venetsanopoulos
        only, without actually increasing the size
1321 7181fba0 Constantinos Venetsanopoulos

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

1350 376631d1 Constantinos Venetsanopoulos
  This class implements the External Storage Interface, which means
1351 376631d1 Constantinos Venetsanopoulos
  handling of the externally provided block devices.
1352 376631d1 Constantinos Venetsanopoulos

1353 376631d1 Constantinos Venetsanopoulos
  """
1354 376631d1 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
1355 376631d1 Constantinos Venetsanopoulos
    """Attaches to an extstorage block device.
1356 376631d1 Constantinos Venetsanopoulos

1357 376631d1 Constantinos Venetsanopoulos
    """
1358 376631d1 Constantinos Venetsanopoulos
    super(ExtStorageDevice, self).__init__(unique_id, children, size, params)
1359 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1360 376631d1 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1361 376631d1 Constantinos Venetsanopoulos
1362 376631d1 Constantinos Venetsanopoulos
    self.driver, self.vol_name = unique_id
1363 938adc87 Constantinos Venetsanopoulos
    self.ext_params = params
1364 376631d1 Constantinos Venetsanopoulos
1365 376631d1 Constantinos Venetsanopoulos
    self.major = self.minor = None
1366 376631d1 Constantinos Venetsanopoulos
    self.Attach()
1367 376631d1 Constantinos Venetsanopoulos
1368 376631d1 Constantinos Venetsanopoulos
  @classmethod
1369 24c06acb Bernardo Dal Seno
  def Create(cls, unique_id, children, size, spindles, params, excl_stor):
1370 376631d1 Constantinos Venetsanopoulos
    """Create a new extstorage device.
1371 376631d1 Constantinos Venetsanopoulos

1372 376631d1 Constantinos Venetsanopoulos
    Provision a new volume using an extstorage provider, which will
1373 376631d1 Constantinos Venetsanopoulos
    then be mapped to a block device.
1374 376631d1 Constantinos Venetsanopoulos

1375 376631d1 Constantinos Venetsanopoulos
    """
1376 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1377 376631d1 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
1378 376631d1 Constantinos Venetsanopoulos
                                   str(unique_id))
1379 ee1478e5 Bernardo Dal Seno
    if excl_stor:
1380 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("extstorage device requested with"
1381 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
1382 376631d1 Constantinos Venetsanopoulos
1383 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's create script,
1384 376631d1 Constantinos Venetsanopoulos
    # to provision a new Volume inside the External Storage
1385 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id,
1386 938adc87 Constantinos Venetsanopoulos
                      params, str(size))
1387 376631d1 Constantinos Venetsanopoulos
1388 376631d1 Constantinos Venetsanopoulos
    return ExtStorageDevice(unique_id, children, size, params)
1389 376631d1 Constantinos Venetsanopoulos
1390 376631d1 Constantinos Venetsanopoulos
  def Remove(self):
1391 376631d1 Constantinos Venetsanopoulos
    """Remove the extstorage device.
1392 376631d1 Constantinos Venetsanopoulos

1393 376631d1 Constantinos Venetsanopoulos
    """
1394 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1395 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1396 376631d1 Constantinos Venetsanopoulos
      return
1397 376631d1 Constantinos Venetsanopoulos
1398 376631d1 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
1399 376631d1 Constantinos Venetsanopoulos
    self.Shutdown()
1400 376631d1 Constantinos Venetsanopoulos
1401 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's remove script,
1402 376631d1 Constantinos Venetsanopoulos
    # to remove the Volume from the External Storage
1403 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id,
1404 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
1405 376631d1 Constantinos Venetsanopoulos
1406 376631d1 Constantinos Venetsanopoulos
  def Rename(self, new_id):
1407 376631d1 Constantinos Venetsanopoulos
    """Rename this device.
1408 376631d1 Constantinos Venetsanopoulos

1409 376631d1 Constantinos Venetsanopoulos
    """
1410 376631d1 Constantinos Venetsanopoulos
    pass
1411 376631d1 Constantinos Venetsanopoulos
1412 376631d1 Constantinos Venetsanopoulos
  def Attach(self):
1413 376631d1 Constantinos Venetsanopoulos
    """Attach to an existing extstorage device.
1414 376631d1 Constantinos Venetsanopoulos

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

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

1445 376631d1 Constantinos Venetsanopoulos
    """
1446 376631d1 Constantinos Venetsanopoulos
    pass
1447 376631d1 Constantinos Venetsanopoulos
1448 376631d1 Constantinos Venetsanopoulos
  def Shutdown(self):
1449 376631d1 Constantinos Venetsanopoulos
    """Shutdown the device.
1450 376631d1 Constantinos Venetsanopoulos

1451 376631d1 Constantinos Venetsanopoulos
    """
1452 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1453 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1454 376631d1 Constantinos Venetsanopoulos
      return
1455 376631d1 Constantinos Venetsanopoulos
1456 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's detach script,
1457 376631d1 Constantinos Venetsanopoulos
    # to detach an existing Volume from it's block device under /dev
1458 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id,
1459 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
1460 376631d1 Constantinos Venetsanopoulos
1461 376631d1 Constantinos Venetsanopoulos
    self.minor = None
1462 376631d1 Constantinos Venetsanopoulos
    self.dev_path = None
1463 376631d1 Constantinos Venetsanopoulos
1464 376631d1 Constantinos Venetsanopoulos
  def Open(self, force=False):
1465 376631d1 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
1466 376631d1 Constantinos Venetsanopoulos

1467 376631d1 Constantinos Venetsanopoulos
    """
1468 376631d1 Constantinos Venetsanopoulos
    pass
1469 376631d1 Constantinos Venetsanopoulos
1470 376631d1 Constantinos Venetsanopoulos
  def Close(self):
1471 376631d1 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1472 376631d1 Constantinos Venetsanopoulos

1473 376631d1 Constantinos Venetsanopoulos
    """
1474 376631d1 Constantinos Venetsanopoulos
    pass
1475 376631d1 Constantinos Venetsanopoulos
1476 376631d1 Constantinos Venetsanopoulos
  def Grow(self, amount, dryrun, backingstore):
1477 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
1478 376631d1 Constantinos Venetsanopoulos

1479 376631d1 Constantinos Venetsanopoulos
    @type amount: integer
1480 376631d1 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1481 376631d1 Constantinos Venetsanopoulos
    @type dryrun: boolean
1482 376631d1 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1483 376631d1 Constantinos Venetsanopoulos
        only, without actually increasing the size
1484 376631d1 Constantinos Venetsanopoulos

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

1505 376631d1 Constantinos Venetsanopoulos
    """
1506 376631d1 Constantinos Venetsanopoulos
    # Replace invalid characters
1507 376631d1 Constantinos Venetsanopoulos
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
1508 376631d1 Constantinos Venetsanopoulos
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
1509 376631d1 Constantinos Venetsanopoulos
1510 376631d1 Constantinos Venetsanopoulos
    # Only up to 128 characters are allowed
1511 376631d1 Constantinos Venetsanopoulos
    text = text[:128]
1512 376631d1 Constantinos Venetsanopoulos
1513 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's setinfo script,
1514 376631d1 Constantinos Venetsanopoulos
    # to set metadata for an existing Volume inside the External Storage
1515 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
1516 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, metadata=text)
1517 376631d1 Constantinos Venetsanopoulos
1518 376631d1 Constantinos Venetsanopoulos
1519 938adc87 Constantinos Venetsanopoulos
def _ExtStorageAction(action, unique_id, ext_params,
1520 938adc87 Constantinos Venetsanopoulos
                      size=None, grow=None, metadata=None):
1521 376631d1 Constantinos Venetsanopoulos
  """Take an External Storage action.
1522 376631d1 Constantinos Venetsanopoulos

1523 376631d1 Constantinos Venetsanopoulos
  Take an External Storage action concerning or affecting
1524 376631d1 Constantinos Venetsanopoulos
  a specific Volume inside the External Storage.
1525 376631d1 Constantinos Venetsanopoulos

1526 376631d1 Constantinos Venetsanopoulos
  @type action: string
1527 376631d1 Constantinos Venetsanopoulos
  @param action: which action to perform. One of:
1528 376631d1 Constantinos Venetsanopoulos
                 create / remove / grow / attach / detach
1529 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1530 376631d1 Constantinos Venetsanopoulos
  @param unique_id: a tuple containing the type of ExtStorage (driver)
1531 376631d1 Constantinos Venetsanopoulos
                    and the Volume name
1532 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1533 938adc87 Constantinos Venetsanopoulos
  @param ext_params: ExtStorage parameters
1534 376631d1 Constantinos Venetsanopoulos
  @type size: integer
1535 376631d1 Constantinos Venetsanopoulos
  @param size: the size of the Volume in mebibytes
1536 376631d1 Constantinos Venetsanopoulos
  @type grow: integer
1537 376631d1 Constantinos Venetsanopoulos
  @param grow: the new size in mebibytes (after grow)
1538 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1539 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume, for use by the provider
1540 376631d1 Constantinos Venetsanopoulos
  @rtype: None or a block device path (during attach)
1541 376631d1 Constantinos Venetsanopoulos

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

1598 376631d1 Constantinos Venetsanopoulos
  This function will return an ExtStorage instance
1599 376631d1 Constantinos Venetsanopoulos
  if the given name is a valid ExtStorage name.
1600 376631d1 Constantinos Venetsanopoulos

1601 376631d1 Constantinos Venetsanopoulos
  @type base_dir: string
1602 376631d1 Constantinos Venetsanopoulos
  @keyword base_dir: Base directory containing ExtStorage installations.
1603 376631d1 Constantinos Venetsanopoulos
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
1604 376631d1 Constantinos Venetsanopoulos
  @rtype: tuple
1605 376631d1 Constantinos Venetsanopoulos
  @return: True and the ExtStorage instance if we find a valid one, or
1606 376631d1 Constantinos Venetsanopoulos
      False and the diagnose message on error
1607 376631d1 Constantinos Venetsanopoulos

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

1672 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1673 376631d1 Constantinos Venetsanopoulos
  @param unique_id: ExtStorage pool and name of the Volume
1674 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1675 938adc87 Constantinos Venetsanopoulos
  @param ext_params: the EXT parameters
1676 376631d1 Constantinos Venetsanopoulos
  @type size: string
1677 376631d1 Constantinos Venetsanopoulos
  @param size: size of the Volume (in mebibytes)
1678 376631d1 Constantinos Venetsanopoulos
  @type grow: string
1679 376631d1 Constantinos Venetsanopoulos
  @param grow: new size of Volume after grow (in mebibytes)
1680 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1681 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume
1682 376631d1 Constantinos Venetsanopoulos
  @rtype: dict
1683 376631d1 Constantinos Venetsanopoulos
  @return: dict of environment variables
1684 376631d1 Constantinos Venetsanopoulos

1685 376631d1 Constantinos Venetsanopoulos
  """
1686 376631d1 Constantinos Venetsanopoulos
  vol_name = unique_id[1]
1687 376631d1 Constantinos Venetsanopoulos
1688 376631d1 Constantinos Venetsanopoulos
  result = {}
1689 376631d1 Constantinos Venetsanopoulos
  result["VOL_NAME"] = vol_name
1690 376631d1 Constantinos Venetsanopoulos
1691 938adc87 Constantinos Venetsanopoulos
  # EXT params
1692 938adc87 Constantinos Venetsanopoulos
  for pname, pvalue in ext_params.items():
1693 938adc87 Constantinos Venetsanopoulos
    result["EXTP_%s" % pname.upper()] = str(pvalue)
1694 938adc87 Constantinos Venetsanopoulos
1695 376631d1 Constantinos Venetsanopoulos
  if size is not None:
1696 376631d1 Constantinos Venetsanopoulos
    result["VOL_SIZE"] = size
1697 376631d1 Constantinos Venetsanopoulos
1698 376631d1 Constantinos Venetsanopoulos
  if grow is not None:
1699 376631d1 Constantinos Venetsanopoulos
    result["VOL_NEW_SIZE"] = grow
1700 376631d1 Constantinos Venetsanopoulos
1701 376631d1 Constantinos Venetsanopoulos
  if metadata is not None:
1702 376631d1 Constantinos Venetsanopoulos
    result["VOL_METADATA"] = metadata
1703 376631d1 Constantinos Venetsanopoulos
1704 376631d1 Constantinos Venetsanopoulos
  return result
1705 376631d1 Constantinos Venetsanopoulos
1706 376631d1 Constantinos Venetsanopoulos
1707 376631d1 Constantinos Venetsanopoulos
def _VolumeLogName(kind, es_name, volume):
1708 376631d1 Constantinos Venetsanopoulos
  """Compute the ExtStorage log filename for a given Volume and operation.
1709 376631d1 Constantinos Venetsanopoulos

1710 376631d1 Constantinos Venetsanopoulos
  @type kind: string
1711 376631d1 Constantinos Venetsanopoulos
  @param kind: the operation type (e.g. create, remove etc.)
1712 376631d1 Constantinos Venetsanopoulos
  @type es_name: string
1713 376631d1 Constantinos Venetsanopoulos
  @param es_name: the ExtStorage name
1714 376631d1 Constantinos Venetsanopoulos
  @type volume: string
1715 376631d1 Constantinos Venetsanopoulos
  @param volume: the name of the Volume inside the External Storage
1716 376631d1 Constantinos Venetsanopoulos

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

1748 5ff82cc9 René Nussbaumer
  """
1749 5ff82cc9 René Nussbaumer
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
1750 5ff82cc9 René Nussbaumer
  if missing:
1751 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
1752 5ff82cc9 René Nussbaumer
                                 missing)
1753 5ff82cc9 René Nussbaumer
1754 5ff82cc9 René Nussbaumer
1755 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
1756 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1757 a8083063 Iustin Pop

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

1761 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1762 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
1763 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1764 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1765 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1766 94dcbdb0 Andrea Spadaccini

1767 a8083063 Iustin Pop
  """
1768 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1769 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
1770 c7c6606d René Nussbaumer
                                  disk.params)
1771 cb999543 Iustin Pop
  if not device.attached:
1772 a8083063 Iustin Pop
    return None
1773 ecb091e3 Iustin Pop
  return device
1774 a8083063 Iustin Pop
1775 a8083063 Iustin Pop
1776 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
1777 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1778 a8083063 Iustin Pop

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

1782 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1783 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
1784 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1785 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1786 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1787 94dcbdb0 Andrea Spadaccini

1788 a8083063 Iustin Pop
  """
1789 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1790 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1791 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
1792 c7c6606d René Nussbaumer
                                  disk.params)
1793 1063abd1 Iustin Pop
  device.Assemble()
1794 a8083063 Iustin Pop
  return device
1795 a8083063 Iustin Pop
1796 a8083063 Iustin Pop
1797 ee1478e5 Bernardo Dal Seno
def Create(disk, children, excl_stor):
1798 a8083063 Iustin Pop
  """Create a device.
1799 a8083063 Iustin Pop

1800 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1801 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
1802 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1803 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1804 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1805 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
1806 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
1807 5073fa0c Bernardo Dal Seno
  @rtype: L{bdev.BlockDev}
1808 5073fa0c Bernardo Dal Seno
  @return: the created device, or C{None} in case of an error
1809 94dcbdb0 Andrea Spadaccini

1810 a8083063 Iustin Pop
  """
1811 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1812 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1813 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
1814 24c06acb Bernardo Dal Seno
                                         disk.spindles, disk.params, excl_stor)
1815 a8083063 Iustin Pop
  return device