Statistics
| Branch: | Tag: | Revision:

root / lib / storage / bdev.py @ 78f99abb

History | View | Annotate | Download (56.4 kB)

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

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

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

55 e398546b Iustin Pop
  """
56 e398546b Iustin Pop
  if result.failed:
57 89ff748d Thomas Thrainer
    base.ThrowError("Command: %s error: %s - %s",
58 89ff748d Thomas Thrainer
                    result.cmd, result.fail_reason, result.output)
59 310fbb64 Iustin Pop
60 310fbb64 Iustin Pop
61 89ff748d Thomas Thrainer
class LogicalVolume(base.BlockDev):
62 a8083063 Iustin Pop
  """Logical Volume block device.
63 a8083063 Iustin Pop

64 a8083063 Iustin Pop
  """
65 6136f8f0 Iustin Pop
  _VALID_NAME_RE = re.compile("^[a-zA-Z0-9+_.-]*$")
66 78f99abb Michele Tartara
  _PARSE_PV_DEV_RE = re.compile(r"^([^ ()]+)\([0-9]+\)$")
67 b8028dcf Michael Hanselmann
  _INVALID_NAMES = compat.UniqueFrozenset([".", "..", "snapshot", "pvmove"])
68 b8028dcf Michael Hanselmann
  _INVALID_SUBSTRINGS = compat.UniqueFrozenset(["_mlog", "_mimage"])
69 6136f8f0 Iustin Pop
70 94dcbdb0 Andrea Spadaccini
  def __init__(self, unique_id, children, size, params):
71 a8083063 Iustin Pop
    """Attaches to a LV device.
72 a8083063 Iustin Pop

73 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
74 a8083063 Iustin Pop

75 a8083063 Iustin Pop
    """
76 94dcbdb0 Andrea Spadaccini
    super(LogicalVolume, self).__init__(unique_id, children, size, params)
77 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
78 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
79 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
80 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
81 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
82 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
83 99e8295c Iustin Pop
    self._degraded = True
84 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
85 b5d48e87 Bernardo Dal Seno
    self.pv_names = None
86 a8083063 Iustin Pop
    self.Attach()
87 a8083063 Iustin Pop
88 63c73073 Bernardo Dal Seno
  @staticmethod
89 63c73073 Bernardo Dal Seno
  def _GetStdPvSize(pvs_info):
90 63c73073 Bernardo Dal Seno
    """Return the the standard PV size (used with exclusive storage).
91 63c73073 Bernardo Dal Seno

92 63c73073 Bernardo Dal Seno
    @param pvs_info: list of objects.LvmPvInfo, cannot be empty
93 63c73073 Bernardo Dal Seno
    @rtype: float
94 63c73073 Bernardo Dal Seno
    @return: size in MiB
95 63c73073 Bernardo Dal Seno

96 63c73073 Bernardo Dal Seno
    """
97 63c73073 Bernardo Dal Seno
    assert len(pvs_info) > 0
98 63c73073 Bernardo Dal Seno
    smallest = min([pv.size for pv in pvs_info])
99 63c73073 Bernardo Dal Seno
    return smallest / (1 + constants.PART_MARGIN + constants.PART_RESERVED)
100 63c73073 Bernardo Dal Seno
101 63c73073 Bernardo Dal Seno
  @staticmethod
102 63c73073 Bernardo Dal Seno
  def _ComputeNumPvs(size, pvs_info):
103 63c73073 Bernardo Dal Seno
    """Compute the number of PVs needed for an LV (with exclusive storage).
104 63c73073 Bernardo Dal Seno

105 63c73073 Bernardo Dal Seno
    @type size: float
106 23d95cff Bernardo Dal Seno
    @param size: LV size in MiB
107 63c73073 Bernardo Dal Seno
    @param pvs_info: list of objects.LvmPvInfo, cannot be empty
108 63c73073 Bernardo Dal Seno
    @rtype: integer
109 63c73073 Bernardo Dal Seno
    @return: number of PVs needed
110 63c73073 Bernardo Dal Seno
    """
111 63c73073 Bernardo Dal Seno
    assert len(pvs_info) > 0
112 63c73073 Bernardo Dal Seno
    pv_size = float(LogicalVolume._GetStdPvSize(pvs_info))
113 63c73073 Bernardo Dal Seno
    return int(math.ceil(float(size) / pv_size))
114 63c73073 Bernardo Dal Seno
115 63c73073 Bernardo Dal Seno
  @staticmethod
116 63c73073 Bernardo Dal Seno
  def _GetEmptyPvNames(pvs_info, max_pvs=None):
117 63c73073 Bernardo Dal Seno
    """Return a list of empty PVs, by name.
118 63c73073 Bernardo Dal Seno

119 63c73073 Bernardo Dal Seno
    """
120 63c73073 Bernardo Dal Seno
    empty_pvs = filter(objects.LvmPvInfo.IsEmpty, pvs_info)
121 63c73073 Bernardo Dal Seno
    if max_pvs is not None:
122 63c73073 Bernardo Dal Seno
      empty_pvs = empty_pvs[:max_pvs]
123 63c73073 Bernardo Dal Seno
    return map((lambda pv: pv.name), empty_pvs)
124 63c73073 Bernardo Dal Seno
125 a8083063 Iustin Pop
  @classmethod
126 24c06acb Bernardo Dal Seno
  def Create(cls, unique_id, children, size, spindles, params, excl_stor):
127 a8083063 Iustin Pop
    """Create a new logical volume.
128 a8083063 Iustin Pop

129 a8083063 Iustin Pop
    """
130 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
131 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
132 6c626518 Iustin Pop
                                   str(unique_id))
133 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
134 6136f8f0 Iustin Pop
    cls._ValidateName(vg_name)
135 6136f8f0 Iustin Pop
    cls._ValidateName(lv_name)
136 2070598f Iustin Pop
    pvs_info = cls.GetPVInfo([vg_name])
137 a8083063 Iustin Pop
    if not pvs_info:
138 63c73073 Bernardo Dal Seno
      if excl_stor:
139 63c73073 Bernardo Dal Seno
        msg = "No (empty) PVs found"
140 63c73073 Bernardo Dal Seno
      else:
141 63c73073 Bernardo Dal Seno
        msg = "Can't compute PV info for vg %s" % vg_name
142 89ff748d Thomas Thrainer
      base.ThrowError(msg)
143 59726e15 Bernardo Dal Seno
    pvs_info.sort(key=(lambda pv: pv.free), reverse=True)
144 5b7b5d49 Guido Trotter
145 59726e15 Bernardo Dal Seno
    pvlist = [pv.name for pv in pvs_info]
146 403f5172 Guido Trotter
    if compat.any(":" in v for v in pvlist):
147 89ff748d Thomas Thrainer
      base.ThrowError("Some of your PVs have the invalid character ':' in their"
148 89ff748d Thomas Thrainer
                      " name, this is not supported - please filter them out"
149 89ff748d Thomas Thrainer
                      " in lvm.conf using either 'filter' or 'preferred_names'")
150 63c73073 Bernardo Dal Seno
151 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
152 ac00bf1b Andrea Spadaccini
    desired_stripes = params[constants.LDP_STRIPES]
153 43e11798 Andrea Spadaccini
    stripes = min(current_pvs, desired_stripes)
154 63c73073 Bernardo Dal Seno
155 63c73073 Bernardo Dal Seno
    if excl_stor:
156 7c848a6a Bernardo Dal Seno
      if spindles is None:
157 7c848a6a Bernardo Dal Seno
        base.ThrowError("Unspecified number of spindles: this is required"
158 7c848a6a Bernardo Dal Seno
                        "when exclusive storage is enabled, try running"
159 7c848a6a Bernardo Dal Seno
                        " gnt-cluster repair-disk-sizes")
160 11064155 Bernardo Dal Seno
      (err_msgs, _) = utils.LvmExclusiveCheckNodePvs(pvs_info)
161 63c73073 Bernardo Dal Seno
      if err_msgs:
162 63c73073 Bernardo Dal Seno
        for m in err_msgs:
163 63c73073 Bernardo Dal Seno
          logging.warning(m)
164 63c73073 Bernardo Dal Seno
      req_pvs = cls._ComputeNumPvs(size, pvs_info)
165 7c848a6a Bernardo Dal Seno
      if spindles < req_pvs:
166 7c848a6a Bernardo Dal Seno
        base.ThrowError("Requested number of spindles (%s) is not enough for"
167 7c848a6a Bernardo Dal Seno
                        " a disk of %d MB (at least %d spindles needed)",
168 7c848a6a Bernardo Dal Seno
                        spindles, size, req_pvs)
169 7c848a6a Bernardo Dal Seno
      else:
170 7c848a6a Bernardo Dal Seno
        req_pvs = spindles
171 63c73073 Bernardo Dal Seno
      pvlist = cls._GetEmptyPvNames(pvs_info, req_pvs)
172 63c73073 Bernardo Dal Seno
      current_pvs = len(pvlist)
173 63c73073 Bernardo Dal Seno
      if current_pvs < req_pvs:
174 24c06acb Bernardo Dal Seno
        base.ThrowError("Not enough empty PVs (spindles) to create a disk of %d"
175 24c06acb Bernardo Dal Seno
                        " MB: %d available, %d needed",
176 24c06acb Bernardo Dal Seno
                        size, current_pvs, req_pvs)
177 63c73073 Bernardo Dal Seno
      assert current_pvs == len(pvlist)
178 577edf04 Bernardo Dal Seno
      # We must update stripes to be sure to use all the desired spindles
179 577edf04 Bernardo Dal Seno
      stripes = current_pvs
180 577edf04 Bernardo Dal Seno
      if stripes > desired_stripes:
181 577edf04 Bernardo Dal Seno
        # Don't warn when lowering stripes, as it's no surprise
182 577edf04 Bernardo Dal Seno
        logging.warning("Using %s stripes instead of %s, to be able to use"
183 577edf04 Bernardo Dal Seno
                        " %s spindles", stripes, desired_stripes, current_pvs)
184 63c73073 Bernardo Dal Seno
185 63c73073 Bernardo Dal Seno
    else:
186 63c73073 Bernardo Dal Seno
      if stripes < desired_stripes:
187 63c73073 Bernardo Dal Seno
        logging.warning("Could not use %d stripes for VG %s, as only %d PVs are"
188 63c73073 Bernardo Dal Seno
                        " available.", desired_stripes, vg_name, current_pvs)
189 63c73073 Bernardo Dal Seno
      free_size = sum([pv.free for pv in pvs_info])
190 63c73073 Bernardo Dal Seno
      # The size constraint should have been checked from the master before
191 63c73073 Bernardo Dal Seno
      # calling the create function.
192 63c73073 Bernardo Dal Seno
      if free_size < size:
193 89ff748d Thomas Thrainer
        base.ThrowError("Not enough free space: required %s,"
194 89ff748d Thomas Thrainer
                        " available %s", size, free_size)
195 63c73073 Bernardo Dal Seno
196 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
197 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
198 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
199 fecbe9d5 Iustin Pop
    # stripes
200 63c73073 Bernardo Dal Seno
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
201 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
202 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
203 fecbe9d5 Iustin Pop
      if not result.failed:
204 fecbe9d5 Iustin Pop
        break
205 a8083063 Iustin Pop
    if result.failed:
206 89ff748d Thomas Thrainer
      base.ThrowError("LV create failed (%s): %s",
207 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
208 94dcbdb0 Andrea Spadaccini
    return LogicalVolume(unique_id, children, size, params)
209 a8083063 Iustin Pop
210 a8083063 Iustin Pop
  @staticmethod
211 197478f2 René Nussbaumer
  def _GetVolumeInfo(lvm_cmd, fields):
212 511e00b8 Helga Velroyen
    """Returns LVM Volume infos using lvm_cmd
213 197478f2 René Nussbaumer

214 197478f2 René Nussbaumer
    @param lvm_cmd: Should be one of "pvs", "vgs" or "lvs"
215 197478f2 René Nussbaumer
    @param fields: Fields to return
216 197478f2 René Nussbaumer
    @return: A list of dicts each with the parsed fields
217 197478f2 René Nussbaumer

218 197478f2 René Nussbaumer
    """
219 197478f2 René Nussbaumer
    if not fields:
220 197478f2 René Nussbaumer
      raise errors.ProgrammerError("No fields specified")
221 197478f2 René Nussbaumer
222 197478f2 René Nussbaumer
    sep = "|"
223 197478f2 René Nussbaumer
    cmd = [lvm_cmd, "--noheadings", "--nosuffix", "--units=m", "--unbuffered",
224 197478f2 René Nussbaumer
           "--separator=%s" % sep, "-o%s" % ",".join(fields)]
225 197478f2 René Nussbaumer
226 197478f2 René Nussbaumer
    result = utils.RunCmd(cmd)
227 197478f2 René Nussbaumer
    if result.failed:
228 197478f2 René Nussbaumer
      raise errors.CommandError("Can't get the volume information: %s - %s" %
229 197478f2 René Nussbaumer
                                (result.fail_reason, result.output))
230 197478f2 René Nussbaumer
231 197478f2 René Nussbaumer
    data = []
232 197478f2 René Nussbaumer
    for line in result.stdout.splitlines():
233 197478f2 René Nussbaumer
      splitted_fields = line.strip().split(sep)
234 197478f2 René Nussbaumer
235 197478f2 René Nussbaumer
      if len(fields) != len(splitted_fields):
236 197478f2 René Nussbaumer
        raise errors.CommandError("Can't parse %s output: line '%s'" %
237 197478f2 René Nussbaumer
                                  (lvm_cmd, line))
238 197478f2 René Nussbaumer
239 197478f2 René Nussbaumer
      data.append(splitted_fields)
240 197478f2 René Nussbaumer
241 197478f2 René Nussbaumer
    return data
242 197478f2 René Nussbaumer
243 197478f2 René Nussbaumer
  @classmethod
244 b496abdb Bernardo Dal Seno
  def GetPVInfo(cls, vg_names, filter_allocatable=True, include_lvs=False):
245 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
246 a8083063 Iustin Pop

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

251 c41eea6e Iustin Pop
    @rtype: list
252 59726e15 Bernardo Dal Seno
    @return: list of objects.LvmPvInfo objects
253 098c0958 Michael Hanselmann

254 a8083063 Iustin Pop
    """
255 b496abdb Bernardo Dal Seno
    # We request "lv_name" field only if we care about LVs, so we don't get
256 b496abdb Bernardo Dal Seno
    # a long list of entries with many duplicates unless we really have to.
257 b496abdb Bernardo Dal Seno
    # The duplicate "pv_name" field will be ignored.
258 b496abdb Bernardo Dal Seno
    if include_lvs:
259 b496abdb Bernardo Dal Seno
      lvfield = "lv_name"
260 b496abdb Bernardo Dal Seno
    else:
261 b496abdb Bernardo Dal Seno
      lvfield = "pv_name"
262 197478f2 René Nussbaumer
    try:
263 197478f2 René Nussbaumer
      info = cls._GetVolumeInfo("pvs", ["pv_name", "vg_name", "pv_free",
264 b496abdb Bernardo Dal Seno
                                        "pv_attr", "pv_size", lvfield])
265 197478f2 René Nussbaumer
    except errors.GenericError, err:
266 197478f2 René Nussbaumer
      logging.error("Can't get PV information: %s", err)
267 a8083063 Iustin Pop
      return None
268 197478f2 René Nussbaumer
269 b496abdb Bernardo Dal Seno
    # When asked for LVs, "pvs" may return multiple entries for the same PV-LV
270 b496abdb Bernardo Dal Seno
    # pair. We sort entries by PV name and then LV name, so it's easy to weed
271 b496abdb Bernardo Dal Seno
    # out duplicates.
272 b496abdb Bernardo Dal Seno
    if include_lvs:
273 b496abdb Bernardo Dal Seno
      info.sort(key=(lambda i: (i[0], i[5])))
274 a8083063 Iustin Pop
    data = []
275 b496abdb Bernardo Dal Seno
    lastpvi = None
276 b496abdb Bernardo Dal Seno
    for (pv_name, vg_name, pv_free, pv_attr, pv_size, lv_name) in info:
277 2070598f Iustin Pop
      # (possibly) skip over pvs which are not allocatable
278 197478f2 René Nussbaumer
      if filter_allocatable and pv_attr[0] != "a":
279 a8083063 Iustin Pop
        continue
280 2070598f Iustin Pop
      # (possibly) skip over pvs which are not in the right volume group(s)
281 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
282 2070598f Iustin Pop
        continue
283 b496abdb Bernardo Dal Seno
      # Beware of duplicates (check before inserting)
284 b496abdb Bernardo Dal Seno
      if lastpvi and lastpvi.name == pv_name:
285 b496abdb Bernardo Dal Seno
        if include_lvs and lv_name:
286 b496abdb Bernardo Dal Seno
          if not lastpvi.lv_list or lastpvi.lv_list[-1] != lv_name:
287 b496abdb Bernardo Dal Seno
            lastpvi.lv_list.append(lv_name)
288 b496abdb Bernardo Dal Seno
      else:
289 b496abdb Bernardo Dal Seno
        if include_lvs and lv_name:
290 b496abdb Bernardo Dal Seno
          lvl = [lv_name]
291 b496abdb Bernardo Dal Seno
        else:
292 b496abdb Bernardo Dal Seno
          lvl = []
293 b496abdb Bernardo Dal Seno
        lastpvi = objects.LvmPvInfo(name=pv_name, vg_name=vg_name,
294 b496abdb Bernardo Dal Seno
                                    size=float(pv_size), free=float(pv_free),
295 b496abdb Bernardo Dal Seno
                                    attributes=pv_attr, lv_list=lvl)
296 b496abdb Bernardo Dal Seno
        data.append(lastpvi)
297 197478f2 René Nussbaumer
298 197478f2 René Nussbaumer
    return data
299 197478f2 René Nussbaumer
300 197478f2 René Nussbaumer
  @classmethod
301 a1860404 Bernardo Dal Seno
  def _GetRawFreePvInfo(cls, vg_name):
302 a1860404 Bernardo Dal Seno
    """Return info (size/free) about PVs.
303 a1860404 Bernardo Dal Seno

304 a1860404 Bernardo Dal Seno
    @type vg_name: string
305 a1860404 Bernardo Dal Seno
    @param vg_name: VG name
306 a1860404 Bernardo Dal Seno
    @rtype: tuple
307 a1860404 Bernardo Dal Seno
    @return: (standard_pv_size_in_MiB, number_of_free_pvs, total_number_of_pvs)
308 a1860404 Bernardo Dal Seno

309 a1860404 Bernardo Dal Seno
    """
310 a1860404 Bernardo Dal Seno
    pvs_info = cls.GetPVInfo([vg_name])
311 a1860404 Bernardo Dal Seno
    if not pvs_info:
312 a1860404 Bernardo Dal Seno
      pv_size = 0.0
313 a1860404 Bernardo Dal Seno
      free_pvs = 0
314 a1860404 Bernardo Dal Seno
      num_pvs = 0
315 a1860404 Bernardo Dal Seno
    else:
316 a1860404 Bernardo Dal Seno
      pv_size = cls._GetStdPvSize(pvs_info)
317 a1860404 Bernardo Dal Seno
      free_pvs = len(cls._GetEmptyPvNames(pvs_info))
318 a1860404 Bernardo Dal Seno
      num_pvs = len(pvs_info)
319 a1860404 Bernardo Dal Seno
    return (pv_size, free_pvs, num_pvs)
320 a1860404 Bernardo Dal Seno
321 a1860404 Bernardo Dal Seno
  @classmethod
322 61481c52 Bernardo Dal Seno
  def _GetExclusiveStorageVgFree(cls, vg_name):
323 61481c52 Bernardo Dal Seno
    """Return the free disk space in the given VG, in exclusive storage mode.
324 61481c52 Bernardo Dal Seno

325 61481c52 Bernardo Dal Seno
    @type vg_name: string
326 61481c52 Bernardo Dal Seno
    @param vg_name: VG name
327 61481c52 Bernardo Dal Seno
    @rtype: float
328 61481c52 Bernardo Dal Seno
    @return: free space in MiB
329 61481c52 Bernardo Dal Seno
    """
330 a1860404 Bernardo Dal Seno
    (pv_size, free_pvs, _) = cls._GetRawFreePvInfo(vg_name)
331 a1860404 Bernardo Dal Seno
    return pv_size * free_pvs
332 a1860404 Bernardo Dal Seno
333 a1860404 Bernardo Dal Seno
  @classmethod
334 a1860404 Bernardo Dal Seno
  def GetVgSpindlesInfo(cls, vg_name):
335 a1860404 Bernardo Dal Seno
    """Get the free space info for specific VGs.
336 a1860404 Bernardo Dal Seno

337 a1860404 Bernardo Dal Seno
    @param vg_name: volume group name
338 a1860404 Bernardo Dal Seno
    @rtype: tuple
339 a1860404 Bernardo Dal Seno
    @return: (free_spindles, total_spindles)
340 a1860404 Bernardo Dal Seno

341 a1860404 Bernardo Dal Seno
    """
342 a1860404 Bernardo Dal Seno
    (_, free_pvs, num_pvs) = cls._GetRawFreePvInfo(vg_name)
343 a1860404 Bernardo Dal Seno
    return (free_pvs, num_pvs)
344 61481c52 Bernardo Dal Seno
345 61481c52 Bernardo Dal Seno
  @classmethod
346 1a3c5d4e Bernardo Dal Seno
  def GetVGInfo(cls, vg_names, excl_stor, filter_readonly=True):
347 197478f2 René Nussbaumer
    """Get the free space info for specific VGs.
348 197478f2 René Nussbaumer

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

353 197478f2 René Nussbaumer
    @rtype: list
354 673cd9c4 René Nussbaumer
    @return: list of tuples (free_space, total_size, name) with free_space in
355 673cd9c4 René Nussbaumer
             MiB
356 197478f2 René Nussbaumer

357 197478f2 René Nussbaumer
    """
358 197478f2 René Nussbaumer
    try:
359 673cd9c4 René Nussbaumer
      info = cls._GetVolumeInfo("vgs", ["vg_name", "vg_free", "vg_attr",
360 673cd9c4 René Nussbaumer
                                        "vg_size"])
361 197478f2 René Nussbaumer
    except errors.GenericError, err:
362 197478f2 René Nussbaumer
      logging.error("Can't get VG information: %s", err)
363 197478f2 René Nussbaumer
      return None
364 197478f2 René Nussbaumer
365 197478f2 René Nussbaumer
    data = []
366 673cd9c4 René Nussbaumer
    for vg_name, vg_free, vg_attr, vg_size in info:
367 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not writable
368 197478f2 René Nussbaumer
      if filter_readonly and vg_attr[0] == "r":
369 197478f2 René Nussbaumer
        continue
370 197478f2 René Nussbaumer
      # (possibly) skip over vgs which are not in the right volume group(s)
371 197478f2 René Nussbaumer
      if vg_names and vg_name not in vg_names:
372 197478f2 René Nussbaumer
        continue
373 61481c52 Bernardo Dal Seno
      # Exclusive storage needs a different concept of free space
374 61481c52 Bernardo Dal Seno
      if excl_stor:
375 61481c52 Bernardo Dal Seno
        es_free = cls._GetExclusiveStorageVgFree(vg_name)
376 61481c52 Bernardo Dal Seno
        assert es_free <= vg_free
377 61481c52 Bernardo Dal Seno
        vg_free = es_free
378 673cd9c4 René Nussbaumer
      data.append((float(vg_free), float(vg_size), vg_name))
379 a8083063 Iustin Pop
380 a8083063 Iustin Pop
    return data
381 a8083063 Iustin Pop
382 6136f8f0 Iustin Pop
  @classmethod
383 6136f8f0 Iustin Pop
  def _ValidateName(cls, name):
384 6136f8f0 Iustin Pop
    """Validates that a given name is valid as VG or LV name.
385 6136f8f0 Iustin Pop

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

390 6136f8f0 Iustin Pop
    """
391 6136f8f0 Iustin Pop
    if (not cls._VALID_NAME_RE.match(name) or
392 6136f8f0 Iustin Pop
        name in cls._INVALID_NAMES or
393 403f5172 Guido Trotter
        compat.any(substring in name for substring in cls._INVALID_SUBSTRINGS)):
394 89ff748d Thomas Thrainer
      base.ThrowError("Invalid LVM name '%s'", name)
395 6136f8f0 Iustin Pop
396 a8083063 Iustin Pop
  def Remove(self):
397 a8083063 Iustin Pop
    """Remove this logical volume.
398 a8083063 Iustin Pop

399 a8083063 Iustin Pop
    """
400 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
401 a8083063 Iustin Pop
      # the LV does not exist
402 0c6c04ec Iustin Pop
      return
403 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
404 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
405 a8083063 Iustin Pop
    if result.failed:
406 89ff748d Thomas Thrainer
      base.ThrowError("Can't lvremove: %s - %s",
407 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
408 a8083063 Iustin Pop
409 f3e513ad Iustin Pop
  def Rename(self, new_id):
410 f3e513ad Iustin Pop
    """Rename this logical volume.
411 f3e513ad Iustin Pop

412 f3e513ad Iustin Pop
    """
413 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
414 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
415 f3e513ad Iustin Pop
    new_vg, new_name = new_id
416 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
417 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
418 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
419 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
420 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
421 f3e513ad Iustin Pop
    if result.failed:
422 89ff748d Thomas Thrainer
      base.ThrowError("Failed to rename the logical volume: %s", result.output)
423 be345db0 Iustin Pop
    self._lv_name = new_name
424 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
425 be345db0 Iustin Pop
426 688b5752 Bernardo Dal Seno
  @classmethod
427 688b5752 Bernardo Dal Seno
  def _ParseLvInfoLine(cls, line, sep):
428 688b5752 Bernardo Dal Seno
    """Parse one line of the lvs output used in L{_GetLvInfo}.
429 a8083063 Iustin Pop

430 a8083063 Iustin Pop
    """
431 688b5752 Bernardo Dal Seno
    elems = line.strip().rstrip(sep).split(sep)
432 b5d48e87 Bernardo Dal Seno
    if len(elems) != 6:
433 b5d48e87 Bernardo Dal Seno
      base.ThrowError("Can't parse LVS output, len(%s) != 6", str(elems))
434 99e8295c Iustin Pop
435 b5d48e87 Bernardo Dal Seno
    (status, major, minor, pe_size, stripes, pvs) = elems
436 0304f0ec Iustin Pop
    if len(status) < 6:
437 688b5752 Bernardo Dal Seno
      base.ThrowError("lvs lv_attr is not at least 6 characters (%s)", status)
438 99e8295c Iustin Pop
439 99e8295c Iustin Pop
    try:
440 99e8295c Iustin Pop
      major = int(major)
441 99e8295c Iustin Pop
      minor = int(minor)
442 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
443 688b5752 Bernardo Dal Seno
      base.ThrowError("lvs major/minor cannot be parsed: %s", str(err))
444 99e8295c Iustin Pop
445 38256320 Iustin Pop
    try:
446 38256320 Iustin Pop
      pe_size = int(float(pe_size))
447 38256320 Iustin Pop
    except (TypeError, ValueError), err:
448 688b5752 Bernardo Dal Seno
      base.ThrowError("Can't parse vg extent size: %s", err)
449 38256320 Iustin Pop
450 38256320 Iustin Pop
    try:
451 38256320 Iustin Pop
      stripes = int(stripes)
452 38256320 Iustin Pop
    except (TypeError, ValueError), err:
453 688b5752 Bernardo Dal Seno
      base.ThrowError("Can't parse the number of stripes: %s", err)
454 688b5752 Bernardo Dal Seno
455 b5d48e87 Bernardo Dal Seno
    pv_names = []
456 b5d48e87 Bernardo Dal Seno
    for pv in pvs.split(","):
457 b5d48e87 Bernardo Dal Seno
      m = re.match(cls._PARSE_PV_DEV_RE, pv)
458 b5d48e87 Bernardo Dal Seno
      if not m:
459 b5d48e87 Bernardo Dal Seno
        base.ThrowError("Can't parse this device list: %s", pvs)
460 b5d48e87 Bernardo Dal Seno
      pv_names.append(m.group(1))
461 b5d48e87 Bernardo Dal Seno
    assert len(pv_names) > 0
462 b5d48e87 Bernardo Dal Seno
463 b5d48e87 Bernardo Dal Seno
    return (status, major, minor, pe_size, stripes, pv_names)
464 688b5752 Bernardo Dal Seno
465 688b5752 Bernardo Dal Seno
  @classmethod
466 688b5752 Bernardo Dal Seno
  def _GetLvInfo(cls, dev_path, _run_cmd=utils.RunCmd):
467 688b5752 Bernardo Dal Seno
    """Get info about the given existing LV to be used.
468 688b5752 Bernardo Dal Seno

469 688b5752 Bernardo Dal Seno
    """
470 b5d48e87 Bernardo Dal Seno
    sep = "|"
471 b5d48e87 Bernardo Dal Seno
    result = _run_cmd(["lvs", "--noheadings", "--separator=%s" % sep,
472 688b5752 Bernardo Dal Seno
                       "--units=k", "--nosuffix",
473 688b5752 Bernardo Dal Seno
                       "-olv_attr,lv_kernel_major,lv_kernel_minor,"
474 b5d48e87 Bernardo Dal Seno
                       "vg_extent_size,stripes,devices", dev_path])
475 688b5752 Bernardo Dal Seno
    if result.failed:
476 688b5752 Bernardo Dal Seno
      base.ThrowError("Can't find LV %s: %s, %s",
477 688b5752 Bernardo Dal Seno
                      dev_path, result.fail_reason, result.output)
478 688b5752 Bernardo Dal Seno
    # the output can (and will) have multiple lines for multi-segment
479 688b5752 Bernardo Dal Seno
    # LVs, as the 'stripes' parameter is a segment one, so we take
480 688b5752 Bernardo Dal Seno
    # only the last entry, which is the one we're interested in; note
481 688b5752 Bernardo Dal Seno
    # that with LVM2 anyway the 'stripes' value must be constant
482 688b5752 Bernardo Dal Seno
    # across segments, so this is a no-op actually
483 688b5752 Bernardo Dal Seno
    out = result.stdout.splitlines()
484 688b5752 Bernardo Dal Seno
    if not out: # totally empty result? splitlines() returns at least
485 688b5752 Bernardo Dal Seno
                # one line for any non-empty string
486 688b5752 Bernardo Dal Seno
      base.ThrowError("Can't parse LVS output, no lines? Got '%s'", str(out))
487 b5d48e87 Bernardo Dal Seno
    pv_names = set()
488 b5d48e87 Bernardo Dal Seno
    for line in out:
489 b5d48e87 Bernardo Dal Seno
      (status, major, minor, pe_size, stripes, more_pvs) = \
490 b5d48e87 Bernardo Dal Seno
        cls._ParseLvInfoLine(line, sep)
491 b5d48e87 Bernardo Dal Seno
      pv_names.update(more_pvs)
492 b5d48e87 Bernardo Dal Seno
    return (status, major, minor, pe_size, stripes, pv_names)
493 688b5752 Bernardo Dal Seno
494 688b5752 Bernardo Dal Seno
  def Attach(self):
495 688b5752 Bernardo Dal Seno
    """Attach to an existing LV.
496 688b5752 Bernardo Dal Seno

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

501 688b5752 Bernardo Dal Seno
    """
502 688b5752 Bernardo Dal Seno
    self.attached = False
503 688b5752 Bernardo Dal Seno
    try:
504 b5d48e87 Bernardo Dal Seno
      (status, major, minor, pe_size, stripes, pv_names) = \
505 688b5752 Bernardo Dal Seno
        self._GetLvInfo(self.dev_path)
506 688b5752 Bernardo Dal Seno
    except errors.BlockDeviceError:
507 38256320 Iustin Pop
      return False
508 38256320 Iustin Pop
509 99e8295c Iustin Pop
    self.major = major
510 99e8295c Iustin Pop
    self.minor = minor
511 38256320 Iustin Pop
    self.pe_size = pe_size
512 38256320 Iustin Pop
    self.stripe_count = stripes
513 d0c8c01d Iustin Pop
    self._degraded = status[0] == "v" # virtual volume, i.e. doesn't backing
514 99e8295c Iustin Pop
                                      # storage
515 b5d48e87 Bernardo Dal Seno
    self.pv_names = pv_names
516 cb999543 Iustin Pop
    self.attached = True
517 99e8295c Iustin Pop
    return True
518 a8083063 Iustin Pop
519 a8083063 Iustin Pop
  def Assemble(self):
520 a8083063 Iustin Pop
    """Assemble the device.
521 a8083063 Iustin Pop

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

526 a8083063 Iustin Pop
    """
527 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
528 5574047a Iustin Pop
    if result.failed:
529 89ff748d Thomas Thrainer
      base.ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
530 a8083063 Iustin Pop
531 a8083063 Iustin Pop
  def Shutdown(self):
532 a8083063 Iustin Pop
    """Shutdown the device.
533 a8083063 Iustin Pop

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

537 a8083063 Iustin Pop
    """
538 746f7476 Iustin Pop
    pass
539 a8083063 Iustin Pop
540 9db6dbce Iustin Pop
  def GetSyncStatus(self):
541 9db6dbce Iustin Pop
    """Returns the sync status of the device.
542 9db6dbce Iustin Pop

543 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
544 9db6dbce Iustin Pop
    status of the mirror.
545 9db6dbce Iustin Pop

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

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

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

559 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
560 c41eea6e Iustin Pop

561 9db6dbce Iustin Pop
    """
562 f208978a Michael Hanselmann
    if self._degraded:
563 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_FAULTY
564 f208978a Michael Hanselmann
    else:
565 f208978a Michael Hanselmann
      ldisk_status = constants.LDS_OKAY
566 f208978a Michael Hanselmann
567 96acbc09 Michael Hanselmann
    return objects.BlockDevStatus(dev_path=self.dev_path,
568 96acbc09 Michael Hanselmann
                                  major=self.major,
569 96acbc09 Michael Hanselmann
                                  minor=self.minor,
570 96acbc09 Michael Hanselmann
                                  sync_percent=None,
571 96acbc09 Michael Hanselmann
                                  estimated_time=None,
572 96acbc09 Michael Hanselmann
                                  is_degraded=self._degraded,
573 f208978a Michael Hanselmann
                                  ldisk_status=ldisk_status)
574 9db6dbce Iustin Pop
575 a8083063 Iustin Pop
  def Open(self, force=False):
576 a8083063 Iustin Pop
    """Make the device ready for I/O.
577 a8083063 Iustin Pop

578 a8083063 Iustin Pop
    This is a no-op for the LV device type.
579 a8083063 Iustin Pop

580 a8083063 Iustin Pop
    """
581 fdbd668d Iustin Pop
    pass
582 a8083063 Iustin Pop
583 a8083063 Iustin Pop
  def Close(self):
584 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
585 a8083063 Iustin Pop

586 a8083063 Iustin Pop
    This is a no-op for the LV device type.
587 a8083063 Iustin Pop

588 a8083063 Iustin Pop
    """
589 fdbd668d Iustin Pop
    pass
590 a8083063 Iustin Pop
591 a8083063 Iustin Pop
  def Snapshot(self, size):
592 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
593 a8083063 Iustin Pop

594 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
595 800ac399 Iustin Pop

596 a8083063 Iustin Pop
    """
597 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
598 a8083063 Iustin Pop
599 a8083063 Iustin Pop
    # remove existing snapshot if found
600 94dcbdb0 Andrea Spadaccini
    snap = LogicalVolume((self._vg_name, snap_name), None, size, self.params)
601 89ff748d Thomas Thrainer
    base.IgnoreError(snap.Remove)
602 a8083063 Iustin Pop
603 1a3c5d4e Bernardo Dal Seno
    vg_info = self.GetVGInfo([self._vg_name], False)
604 197478f2 René Nussbaumer
    if not vg_info:
605 89ff748d Thomas Thrainer
      base.ThrowError("Can't compute VG info for vg %s", self._vg_name)
606 673cd9c4 René Nussbaumer
    free_size, _, _ = vg_info[0]
607 a8083063 Iustin Pop
    if free_size < size:
608 89ff748d Thomas Thrainer
      base.ThrowError("Not enough free space: required %s,"
609 89ff748d Thomas Thrainer
                      " available %s", size, free_size)
610 a8083063 Iustin Pop
611 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
612 e398546b Iustin Pop
                               "-n%s" % snap_name, self.dev_path]))
613 a8083063 Iustin Pop
614 800ac399 Iustin Pop
    return (self._vg_name, snap_name)
615 a8083063 Iustin Pop
616 a1556cfa Iustin Pop
  def _RemoveOldInfo(self):
617 a1556cfa Iustin Pop
    """Try to remove old tags from the lv.
618 a1556cfa Iustin Pop

619 a1556cfa Iustin Pop
    """
620 a1556cfa Iustin Pop
    result = utils.RunCmd(["lvs", "-o", "tags", "--noheadings", "--nosuffix",
621 a1556cfa Iustin Pop
                           self.dev_path])
622 a1556cfa Iustin Pop
    _CheckResult(result)
623 a1556cfa Iustin Pop
624 a1556cfa Iustin Pop
    raw_tags = result.stdout.strip()
625 a1556cfa Iustin Pop
    if raw_tags:
626 a1556cfa Iustin Pop
      for tag in raw_tags.split(","):
627 a1556cfa Iustin Pop
        _CheckResult(utils.RunCmd(["lvchange", "--deltag",
628 a1556cfa Iustin Pop
                                   tag.strip(), self.dev_path]))
629 a1556cfa Iustin Pop
630 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
631 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
632 a0c3fea1 Michael Hanselmann

633 a0c3fea1 Michael Hanselmann
    """
634 89ff748d Thomas Thrainer
    base.BlockDev.SetInfo(self, text)
635 a0c3fea1 Michael Hanselmann
636 a1556cfa Iustin Pop
    self._RemoveOldInfo()
637 a1556cfa Iustin Pop
638 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
639 d0c8c01d Iustin Pop
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
640 d0c8c01d Iustin Pop
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
641 a0c3fea1 Michael Hanselmann
642 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
643 a0c3fea1 Michael Hanselmann
    text = text[:128]
644 a0c3fea1 Michael Hanselmann
645 e398546b Iustin Pop
    _CheckResult(utils.RunCmd(["lvchange", "--addtag", text, self.dev_path]))
646 82463074 Iustin Pop
647 c31ec06b Bernardo Dal Seno
  def _GetGrowthAvaliabilityExclStor(self):
648 c31ec06b Bernardo Dal Seno
    """Return how much the disk can grow with exclusive storage.
649 c31ec06b Bernardo Dal Seno

650 c31ec06b Bernardo Dal Seno
    @rtype: float
651 c31ec06b Bernardo Dal Seno
    @return: available space in Mib
652 c31ec06b Bernardo Dal Seno

653 c31ec06b Bernardo Dal Seno
    """
654 c31ec06b Bernardo Dal Seno
    pvs_info = self.GetPVInfo([self._vg_name])
655 c31ec06b Bernardo Dal Seno
    if not pvs_info:
656 c31ec06b Bernardo Dal Seno
      base.ThrowError("Cannot get information about PVs for %s", self.dev_path)
657 c31ec06b Bernardo Dal Seno
    std_pv_size = self._GetStdPvSize(pvs_info)
658 c31ec06b Bernardo Dal Seno
    free_space = sum(pvi.free - (pvi.size - std_pv_size)
659 c31ec06b Bernardo Dal Seno
                        for pvi in pvs_info
660 c31ec06b Bernardo Dal Seno
                        if pvi.name in self.pv_names)
661 c31ec06b Bernardo Dal Seno
    return free_space
662 c31ec06b Bernardo Dal Seno
663 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
664 1005d816 Iustin Pop
    """Grow the logical volume.
665 1005d816 Iustin Pop

666 1005d816 Iustin Pop
    """
667 cad0723b Iustin Pop
    if not backingstore:
668 cad0723b Iustin Pop
      return
669 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
670 38256320 Iustin Pop
      if not self.Attach():
671 89ff748d Thomas Thrainer
        base.ThrowError("Can't attach to LV during Grow()")
672 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
673 5519f036 Bernardo Dal Seno
    # pe_size is in KB
674 5519f036 Bernardo Dal Seno
    amount *= 1024
675 38256320 Iustin Pop
    rest = amount % full_stripe_size
676 38256320 Iustin Pop
    if rest != 0:
677 38256320 Iustin Pop
      amount += full_stripe_size - rest
678 5519f036 Bernardo Dal Seno
    cmd = ["lvextend", "-L", "+%dk" % amount]
679 7fe23d47 Iustin Pop
    if dryrun:
680 7fe23d47 Iustin Pop
      cmd.append("--test")
681 be9150ea Bernardo Dal Seno
    if excl_stor:
682 c31ec06b Bernardo Dal Seno
      free_space = self._GetGrowthAvaliabilityExclStor()
683 c31ec06b Bernardo Dal Seno
      # amount is in KiB, free_space in MiB
684 c31ec06b Bernardo Dal Seno
      if amount > free_space * 1024:
685 c31ec06b Bernardo Dal Seno
        base.ThrowError("Not enough free space to grow %s: %d MiB required,"
686 c31ec06b Bernardo Dal Seno
                        " %d available", self.dev_path, amount / 1024,
687 c31ec06b Bernardo Dal Seno
                        free_space)
688 be9150ea Bernardo Dal Seno
      # Disk growth doesn't grow the number of spindles, so we must stay within
689 be9150ea Bernardo Dal Seno
      # our assigned volumes
690 be9150ea Bernardo Dal Seno
      pvlist = list(self.pv_names)
691 be9150ea Bernardo Dal Seno
    else:
692 be9150ea Bernardo Dal Seno
      pvlist = []
693 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
694 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
695 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
696 1005d816 Iustin Pop
    # supports 'cling'
697 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
698 be9150ea Bernardo Dal Seno
      result = utils.RunCmd(cmd + ["--alloc", alloc_policy, self.dev_path] +
699 be9150ea Bernardo Dal Seno
                            pvlist)
700 1005d816 Iustin Pop
      if not result.failed:
701 1005d816 Iustin Pop
        return
702 89ff748d Thomas Thrainer
    base.ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
703 a8083063 Iustin Pop
704 baa7f1d6 Bernardo Dal Seno
  def GetActualSpindles(self):
705 baa7f1d6 Bernardo Dal Seno
    """Return the number of spindles used.
706 baa7f1d6 Bernardo Dal Seno

707 baa7f1d6 Bernardo Dal Seno
    """
708 baa7f1d6 Bernardo Dal Seno
    assert self.attached, "BlockDevice not attached in GetActualSpindles()"
709 baa7f1d6 Bernardo Dal Seno
    return len(self.pv_names)
710 baa7f1d6 Bernardo Dal Seno
711 a2cfdea2 Iustin Pop
712 89ff748d Thomas Thrainer
class FileStorage(base.BlockDev):
713 6f695a2e Manuel Franceschini
  """File device.
714 abdf0113 Iustin Pop

715 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
716 6f695a2e Manuel Franceschini

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

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

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

739 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
740 6f695a2e Manuel Franceschini

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

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

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

757 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
758 6f695a2e Manuel Franceschini

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

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

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

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

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

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

793 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
794 bbe4cc16 Iustin Pop

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

816 6f695a2e Manuel Franceschini
    Check if this file already exists.
817 6f695a2e Manuel Franceschini

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

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

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

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

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

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

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

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

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

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

884 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
885 b6135bbc Apollon Oikonomopoulos

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

910 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
911 b6135bbc Apollon Oikonomopoulos

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

921 b6135bbc Apollon Oikonomopoulos
    This is a noop
922 b6135bbc Apollon Oikonomopoulos

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

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

935 b6135bbc Apollon Oikonomopoulos

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

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

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

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

975 b6135bbc Apollon Oikonomopoulos
    """
976 b6135bbc Apollon Oikonomopoulos
    pass
977 b6135bbc Apollon Oikonomopoulos
978 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
979 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
980 b6135bbc Apollon Oikonomopoulos

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

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

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

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

1010 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
1011 7181fba0 Constantinos Venetsanopoulos

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

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

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

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

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

1090 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
1091 7181fba0 Constantinos Venetsanopoulos

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1302 7181fba0 Constantinos Venetsanopoulos
    """
1303 7181fba0 Constantinos Venetsanopoulos
    pass
1304 7181fba0 Constantinos Venetsanopoulos
1305 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
1306 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
1307 7181fba0 Constantinos Venetsanopoulos

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1465 376631d1 Constantinos Venetsanopoulos
    """
1466 376631d1 Constantinos Venetsanopoulos
    pass
1467 376631d1 Constantinos Venetsanopoulos
1468 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
1469 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
1470 376631d1 Constantinos Venetsanopoulos

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1790 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1791 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
1792 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1793 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1794 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1795 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
1796 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
1797 5073fa0c Bernardo Dal Seno
  @rtype: L{bdev.BlockDev}
1798 5073fa0c Bernardo Dal Seno
  @return: the created device, or C{None} in case of an error
1799 94dcbdb0 Andrea Spadaccini

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