Statistics
| Branch: | Tag: | Revision:

root / lib / storage / bdev.py @ 11aa3ca5

History | View | Annotate | Download (58.2 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

787 baa7f1d6 Bernardo Dal Seno
    """
788 baa7f1d6 Bernardo Dal Seno
    assert self.attached, "BlockDevice not attached in GetActualSpindles()"
789 baa7f1d6 Bernardo Dal Seno
    return len(self.pv_names)
790 baa7f1d6 Bernardo Dal Seno
791 a2cfdea2 Iustin Pop
792 89ff748d Thomas Thrainer
class FileStorage(base.BlockDev):
793 6f695a2e Manuel Franceschini
  """File device.
794 abdf0113 Iustin Pop

795 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
796 6f695a2e Manuel Franceschini

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

799 6f695a2e Manuel Franceschini
  """
800 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
801 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
802 6f695a2e Manuel Franceschini

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

819 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
820 6f695a2e Manuel Franceschini

821 6f695a2e Manuel Franceschini
    """
822 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
823 89ff748d Thomas Thrainer
      base.ThrowError("File device '%s' does not exist" % self.dev_path)
824 6f695a2e Manuel Franceschini
825 6f695a2e Manuel Franceschini
  def Shutdown(self):
826 6f695a2e Manuel Franceschini
    """Shutdown the device.
827 6f695a2e Manuel Franceschini

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

831 6f695a2e Manuel Franceschini
    """
832 746f7476 Iustin Pop
    pass
833 6f695a2e Manuel Franceschini
834 6f695a2e Manuel Franceschini
  def Open(self, force=False):
835 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
836 6f695a2e Manuel Franceschini

837 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
838 6f695a2e Manuel Franceschini

839 6f695a2e Manuel Franceschini
    """
840 6f695a2e Manuel Franceschini
    pass
841 6f695a2e Manuel Franceschini
842 6f695a2e Manuel Franceschini
  def Close(self):
843 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
844 6f695a2e Manuel Franceschini

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

847 6f695a2e Manuel Franceschini
    """
848 6f695a2e Manuel Franceschini
    pass
849 6f695a2e Manuel Franceschini
850 6f695a2e Manuel Franceschini
  def Remove(self):
851 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
852 6f695a2e Manuel Franceschini

853 c41eea6e Iustin Pop
    @rtype: boolean
854 c41eea6e Iustin Pop
    @return: True if the removal was successful
855 6f695a2e Manuel Franceschini

856 6f695a2e Manuel Franceschini
    """
857 6f695a2e Manuel Franceschini
    try:
858 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
859 6f695a2e Manuel Franceschini
    except OSError, err:
860 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
861 89ff748d Thomas Thrainer
        base.ThrowError("Can't remove file '%s': %s", self.dev_path, err)
862 6f695a2e Manuel Franceschini
863 bbe4cc16 Iustin Pop
  def Rename(self, new_id):
864 bbe4cc16 Iustin Pop
    """Renames the file.
865 bbe4cc16 Iustin Pop

866 bbe4cc16 Iustin Pop
    """
867 bbe4cc16 Iustin Pop
    # TODO: implement rename for file-based storage
868 89ff748d Thomas Thrainer
    base.ThrowError("Rename is not supported for file-based storage")
869 bbe4cc16 Iustin Pop
870 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
871 bbe4cc16 Iustin Pop
    """Grow the file
872 bbe4cc16 Iustin Pop

873 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
874 bbe4cc16 Iustin Pop

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

896 6f695a2e Manuel Franceschini
    Check if this file already exists.
897 6f695a2e Manuel Franceschini

898 c41eea6e Iustin Pop
    @rtype: boolean
899 c41eea6e Iustin Pop
    @return: True if file exists
900 6f695a2e Manuel Franceschini

901 6f695a2e Manuel Franceschini
    """
902 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
903 ecb091e3 Iustin Pop
    return self.attached
904 6f695a2e Manuel Franceschini
905 fcff3897 Iustin Pop
  def GetActualSize(self):
906 fcff3897 Iustin Pop
    """Return the actual disk size.
907 fcff3897 Iustin Pop

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

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

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

924 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
925 c41eea6e Iustin Pop
    @return: an instance of FileStorage
926 6f695a2e Manuel Franceschini

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

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

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

960 b6135bbc Apollon Oikonomopoulos
  """
961 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
962 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
963 b6135bbc Apollon Oikonomopoulos

964 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
965 b6135bbc Apollon Oikonomopoulos

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

990 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
991 b6135bbc Apollon Oikonomopoulos

992 b6135bbc Apollon Oikonomopoulos
    """
993 ee1478e5 Bernardo Dal Seno
    if excl_stor:
994 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("Persistent block device requested with"
995 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
996 94dcbdb0 Andrea Spadaccini
    return PersistentBlockDevice(unique_id, children, 0, params)
997 b6135bbc Apollon Oikonomopoulos
998 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
999 b6135bbc Apollon Oikonomopoulos
    """Remove a device
1000 b6135bbc Apollon Oikonomopoulos

1001 b6135bbc Apollon Oikonomopoulos
    This is a noop
1002 b6135bbc Apollon Oikonomopoulos

1003 b6135bbc Apollon Oikonomopoulos
    """
1004 b6135bbc Apollon Oikonomopoulos
    pass
1005 b6135bbc Apollon Oikonomopoulos
1006 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
1007 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
1008 b6135bbc Apollon Oikonomopoulos

1009 b6135bbc Apollon Oikonomopoulos
    """
1010 89ff748d Thomas Thrainer
    base.ThrowError("Rename is not supported for PersistentBlockDev storage")
1011 b6135bbc Apollon Oikonomopoulos
1012 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
1013 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
1014 b6135bbc Apollon Oikonomopoulos

1015 b6135bbc Apollon Oikonomopoulos

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

1037 b6135bbc Apollon Oikonomopoulos
    """
1038 b6135bbc Apollon Oikonomopoulos
    pass
1039 b6135bbc Apollon Oikonomopoulos
1040 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
1041 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
1042 b6135bbc Apollon Oikonomopoulos

1043 b6135bbc Apollon Oikonomopoulos
    """
1044 b6135bbc Apollon Oikonomopoulos
    pass
1045 b6135bbc Apollon Oikonomopoulos
1046 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
1047 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
1048 b6135bbc Apollon Oikonomopoulos

1049 b6135bbc Apollon Oikonomopoulos
    """
1050 b6135bbc Apollon Oikonomopoulos
    pass
1051 b6135bbc Apollon Oikonomopoulos
1052 b6135bbc Apollon Oikonomopoulos
  def Close(self):
1053 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
1054 b6135bbc Apollon Oikonomopoulos

1055 b6135bbc Apollon Oikonomopoulos
    """
1056 b6135bbc Apollon Oikonomopoulos
    pass
1057 b6135bbc Apollon Oikonomopoulos
1058 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
1059 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
1060 b6135bbc Apollon Oikonomopoulos

1061 b6135bbc Apollon Oikonomopoulos
    """
1062 89ff748d Thomas Thrainer
    base.ThrowError("Grow is not supported for PersistentBlockDev storage")
1063 b6135bbc Apollon Oikonomopoulos
1064 b6135bbc Apollon Oikonomopoulos
1065 89ff748d Thomas Thrainer
class RADOSBlockDevice(base.BlockDev):
1066 7181fba0 Constantinos Venetsanopoulos
  """A RADOS Block Device (rbd).
1067 7181fba0 Constantinos Venetsanopoulos

1068 7181fba0 Constantinos Venetsanopoulos
  This class implements the RADOS Block Device for the backend. You need
1069 7181fba0 Constantinos Venetsanopoulos
  the rbd kernel driver, the RADOS Tools and a working RADOS cluster for
1070 7181fba0 Constantinos Venetsanopoulos
  this to be functional.
1071 7181fba0 Constantinos Venetsanopoulos

1072 7181fba0 Constantinos Venetsanopoulos
  """
1073 7181fba0 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
1074 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
1075 7181fba0 Constantinos Venetsanopoulos

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

1090 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
1091 7181fba0 Constantinos Venetsanopoulos

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

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

1136 7181fba0 Constantinos Venetsanopoulos
    """
1137 7181fba0 Constantinos Venetsanopoulos
    pass
1138 7181fba0 Constantinos Venetsanopoulos
1139 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
1140 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
1141 7181fba0 Constantinos Venetsanopoulos

1142 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
1143 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
1144 7181fba0 Constantinos Venetsanopoulos

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

1170 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
1171 7181fba0 Constantinos Venetsanopoulos

1172 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
1173 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
1174 7181fba0 Constantinos Venetsanopoulos

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

1205 bdecfea2 Stratos Psomadakis
    @type pool: string
1206 bdecfea2 Stratos Psomadakis
    @param pool: RADOS pool to use
1207 bdecfea2 Stratos Psomadakis
    @type volume_name: string
1208 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
1209 bdecfea2 Stratos Psomadakis
    @rtype: string or None
1210 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
1211 bdecfea2 Stratos Psomadakis

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

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

1250 bdecfea2 Stratos Psomadakis
    @type output: string
1251 bdecfea2 Stratos Psomadakis
    @param output: the json output of `rbd showmapped'
1252 bdecfea2 Stratos Psomadakis
    @type volume_name: string
1253 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
1254 bdecfea2 Stratos Psomadakis
    @rtype: string or None
1255 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
1256 bdecfea2 Stratos Psomadakis

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

1282 7181fba0 Constantinos Venetsanopoulos
    This method parses the output of `rbd showmapped' and returns
1283 7181fba0 Constantinos Venetsanopoulos
    the rbd block device path (e.g. /dev/rbd0) that matches the
1284 7181fba0 Constantinos Venetsanopoulos
    given rbd volume.
1285 7181fba0 Constantinos Venetsanopoulos

1286 7181fba0 Constantinos Venetsanopoulos
    @type output: string
1287 bdecfea2 Stratos Psomadakis
    @param output: the plain text output of `rbd showmapped'
1288 7181fba0 Constantinos Venetsanopoulos
    @type volume_name: string
1289 7181fba0 Constantinos Venetsanopoulos
    @param volume_name: the name of the volume whose device we search for
1290 7181fba0 Constantinos Venetsanopoulos
    @rtype: string or None
1291 7181fba0 Constantinos Venetsanopoulos
    @return: block device path if the volume is mapped, else None
1292 7181fba0 Constantinos Venetsanopoulos

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

1335 7181fba0 Constantinos Venetsanopoulos
    """
1336 7181fba0 Constantinos Venetsanopoulos
    pass
1337 7181fba0 Constantinos Venetsanopoulos
1338 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
1339 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
1340 7181fba0 Constantinos Venetsanopoulos

1341 7181fba0 Constantinos Venetsanopoulos
    """
1342 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1343 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
1344 7181fba0 Constantinos Venetsanopoulos
      return
1345 7181fba0 Constantinos Venetsanopoulos
1346 7181fba0 Constantinos Venetsanopoulos
    # Unmap the block device from the Volume.
1347 7181fba0 Constantinos Venetsanopoulos
    self._UnmapVolumeFromBlockdev(self.unique_id)
1348 7181fba0 Constantinos Venetsanopoulos
1349 7181fba0 Constantinos Venetsanopoulos
    self.minor = None
1350 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = None
1351 7181fba0 Constantinos Venetsanopoulos
1352 7181fba0 Constantinos Venetsanopoulos
  def _UnmapVolumeFromBlockdev(self, unique_id):
1353 7181fba0 Constantinos Venetsanopoulos
    """Unmaps the rbd device from the Volume it is mapped.
1354 7181fba0 Constantinos Venetsanopoulos

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

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

1376 7181fba0 Constantinos Venetsanopoulos
    """
1377 7181fba0 Constantinos Venetsanopoulos
    pass
1378 7181fba0 Constantinos Venetsanopoulos
1379 7181fba0 Constantinos Venetsanopoulos
  def Close(self):
1380 7181fba0 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1381 7181fba0 Constantinos Venetsanopoulos

1382 7181fba0 Constantinos Venetsanopoulos
    """
1383 7181fba0 Constantinos Venetsanopoulos
    pass
1384 7181fba0 Constantinos Venetsanopoulos
1385 cad0723b Iustin Pop
  def Grow(self, amount, dryrun, backingstore):
1386 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
1387 7181fba0 Constantinos Venetsanopoulos

1388 7181fba0 Constantinos Venetsanopoulos
    @type amount: integer
1389 7181fba0 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1390 7181fba0 Constantinos Venetsanopoulos
    @type dryrun: boolean
1391 7181fba0 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1392 7181fba0 Constantinos Venetsanopoulos
        only, without actually increasing the size
1393 7181fba0 Constantinos Venetsanopoulos

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

1422 376631d1 Constantinos Venetsanopoulos
  This class implements the External Storage Interface, which means
1423 376631d1 Constantinos Venetsanopoulos
  handling of the externally provided block devices.
1424 376631d1 Constantinos Venetsanopoulos

1425 376631d1 Constantinos Venetsanopoulos
  """
1426 376631d1 Constantinos Venetsanopoulos
  def __init__(self, unique_id, children, size, params):
1427 376631d1 Constantinos Venetsanopoulos
    """Attaches to an extstorage block device.
1428 376631d1 Constantinos Venetsanopoulos

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

1444 376631d1 Constantinos Venetsanopoulos
    Provision a new volume using an extstorage provider, which will
1445 376631d1 Constantinos Venetsanopoulos
    then be mapped to a block device.
1446 376631d1 Constantinos Venetsanopoulos

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

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

1481 376631d1 Constantinos Venetsanopoulos
    """
1482 376631d1 Constantinos Venetsanopoulos
    pass
1483 376631d1 Constantinos Venetsanopoulos
1484 376631d1 Constantinos Venetsanopoulos
  def Attach(self):
1485 376631d1 Constantinos Venetsanopoulos
    """Attach to an existing extstorage device.
1486 376631d1 Constantinos Venetsanopoulos

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

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

1517 376631d1 Constantinos Venetsanopoulos
    """
1518 376631d1 Constantinos Venetsanopoulos
    pass
1519 376631d1 Constantinos Venetsanopoulos
1520 376631d1 Constantinos Venetsanopoulos
  def Shutdown(self):
1521 376631d1 Constantinos Venetsanopoulos
    """Shutdown the device.
1522 376631d1 Constantinos Venetsanopoulos

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

1539 376631d1 Constantinos Venetsanopoulos
    """
1540 376631d1 Constantinos Venetsanopoulos
    pass
1541 376631d1 Constantinos Venetsanopoulos
1542 376631d1 Constantinos Venetsanopoulos
  def Close(self):
1543 376631d1 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1544 376631d1 Constantinos Venetsanopoulos

1545 376631d1 Constantinos Venetsanopoulos
    """
1546 376631d1 Constantinos Venetsanopoulos
    pass
1547 376631d1 Constantinos Venetsanopoulos
1548 376631d1 Constantinos Venetsanopoulos
  def Grow(self, amount, dryrun, backingstore):
1549 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
1550 376631d1 Constantinos Venetsanopoulos

1551 376631d1 Constantinos Venetsanopoulos
    @type amount: integer
1552 376631d1 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1553 376631d1 Constantinos Venetsanopoulos
    @type dryrun: boolean
1554 376631d1 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1555 376631d1 Constantinos Venetsanopoulos
        only, without actually increasing the size
1556 376631d1 Constantinos Venetsanopoulos

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

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

1595 376631d1 Constantinos Venetsanopoulos
  Take an External Storage action concerning or affecting
1596 376631d1 Constantinos Venetsanopoulos
  a specific Volume inside the External Storage.
1597 376631d1 Constantinos Venetsanopoulos

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

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

1670 376631d1 Constantinos Venetsanopoulos
  This function will return an ExtStorage instance
1671 376631d1 Constantinos Venetsanopoulos
  if the given name is a valid ExtStorage name.
1672 376631d1 Constantinos Venetsanopoulos

1673 376631d1 Constantinos Venetsanopoulos
  @type base_dir: string
1674 376631d1 Constantinos Venetsanopoulos
  @keyword base_dir: Base directory containing ExtStorage installations.
1675 376631d1 Constantinos Venetsanopoulos
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
1676 376631d1 Constantinos Venetsanopoulos
  @rtype: tuple
1677 376631d1 Constantinos Venetsanopoulos
  @return: True and the ExtStorage instance if we find a valid one, or
1678 376631d1 Constantinos Venetsanopoulos
      False and the diagnose message on error
1679 376631d1 Constantinos Venetsanopoulos

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

1744 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1745 376631d1 Constantinos Venetsanopoulos
  @param unique_id: ExtStorage pool and name of the Volume
1746 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1747 938adc87 Constantinos Venetsanopoulos
  @param ext_params: the EXT parameters
1748 376631d1 Constantinos Venetsanopoulos
  @type size: string
1749 376631d1 Constantinos Venetsanopoulos
  @param size: size of the Volume (in mebibytes)
1750 376631d1 Constantinos Venetsanopoulos
  @type grow: string
1751 376631d1 Constantinos Venetsanopoulos
  @param grow: new size of Volume after grow (in mebibytes)
1752 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1753 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume
1754 376631d1 Constantinos Venetsanopoulos
  @rtype: dict
1755 376631d1 Constantinos Venetsanopoulos
  @return: dict of environment variables
1756 376631d1 Constantinos Venetsanopoulos

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

1782 376631d1 Constantinos Venetsanopoulos
  @type kind: string
1783 376631d1 Constantinos Venetsanopoulos
  @param kind: the operation type (e.g. create, remove etc.)
1784 376631d1 Constantinos Venetsanopoulos
  @type es_name: string
1785 376631d1 Constantinos Venetsanopoulos
  @param es_name: the ExtStorage name
1786 376631d1 Constantinos Venetsanopoulos
  @type volume: string
1787 376631d1 Constantinos Venetsanopoulos
  @param volume: the name of the Volume inside the External Storage
1788 376631d1 Constantinos Venetsanopoulos

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

1820 5ff82cc9 René Nussbaumer
  """
1821 5ff82cc9 René Nussbaumer
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
1822 5ff82cc9 René Nussbaumer
  if missing:
1823 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
1824 5ff82cc9 René Nussbaumer
                                 missing)
1825 5ff82cc9 René Nussbaumer
1826 5ff82cc9 René Nussbaumer
1827 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
1828 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1829 a8083063 Iustin Pop

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

1833 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1834 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
1835 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1836 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1837 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1838 94dcbdb0 Andrea Spadaccini

1839 a8083063 Iustin Pop
  """
1840 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1841 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
1842 c7c6606d René Nussbaumer
                                  disk.params)
1843 cb999543 Iustin Pop
  if not device.attached:
1844 a8083063 Iustin Pop
    return None
1845 ecb091e3 Iustin Pop
  return device
1846 a8083063 Iustin Pop
1847 a8083063 Iustin Pop
1848 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
1849 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1850 a8083063 Iustin Pop

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

1854 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1855 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
1856 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1857 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1858 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1859 94dcbdb0 Andrea Spadaccini

1860 a8083063 Iustin Pop
  """
1861 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1862 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1863 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type](disk.physical_id, children, disk.size,
1864 c7c6606d René Nussbaumer
                                  disk.params)
1865 1063abd1 Iustin Pop
  device.Assemble()
1866 a8083063 Iustin Pop
  return device
1867 a8083063 Iustin Pop
1868 a8083063 Iustin Pop
1869 ee1478e5 Bernardo Dal Seno
def Create(disk, children, excl_stor):
1870 a8083063 Iustin Pop
  """Create a device.
1871 a8083063 Iustin Pop

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

1882 a8083063 Iustin Pop
  """
1883 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1884 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1885 94dcbdb0 Andrea Spadaccini
  device = DEV_MAP[disk.dev_type].Create(disk.physical_id, children, disk.size,
1886 24c06acb Bernardo Dal Seno
                                         disk.spindles, disk.params, excl_stor)
1887 a8083063 Iustin Pop
  return device