Statistics
| Branch: | Tag: | Revision:

root / lib / storage / bdev.py @ 544e5b6c

History | View | Annotate | Download (58.5 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 5454737c Iustin Pop
# Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Block device abstraction"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import re
25 a8083063 Iustin Pop
import errno
26 b6135bbc Apollon Oikonomopoulos
import stat
27 6f695a2e Manuel Franceschini
import os
28 468c5f77 Iustin Pop
import logging
29 63c73073 Bernardo Dal Seno
import math
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
from ganeti import utils
32 a8083063 Iustin Pop
from ganeti import errors
33 fe96220b Iustin Pop
from ganeti import constants
34 96acbc09 Michael Hanselmann
from ganeti import objects
35 cea881e5 Michael Hanselmann
from ganeti import compat
36 fbdac0d9 Michael Hanselmann
from ganeti import pathutils
37 bdecfea2 Stratos Psomadakis
from ganeti import serializer
38 cde49218 Helga Velroyen
from ganeti.storage import 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 544e5b6c Klaus Aehlig
  def __init__(self, unique_id, children, size, params, dyn_params, *args):
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 0c3d9c7c Thomas Thrainer
    super(LogicalVolume, self).__init__(unique_id, children, size, params,
77 544e5b6c Klaus Aehlig
                                        dyn_params, *args)
78 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
79 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
80 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
81 6136f8f0 Iustin Pop
    self._ValidateName(self._vg_name)
82 6136f8f0 Iustin Pop
    self._ValidateName(self._lv_name)
83 6136f8f0 Iustin Pop
    self.dev_path = utils.PathJoin("/dev", self._vg_name, self._lv_name)
84 99e8295c Iustin Pop
    self._degraded = True
85 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
86 b5d48e87 Bernardo Dal Seno
    self.pv_names = None
87 a8083063 Iustin Pop
    self.Attach()
88 a8083063 Iustin Pop
89 63c73073 Bernardo Dal Seno
  @staticmethod
90 63c73073 Bernardo Dal Seno
  def _GetStdPvSize(pvs_info):
91 63c73073 Bernardo Dal Seno
    """Return the the standard PV size (used with exclusive storage).
92 63c73073 Bernardo Dal Seno

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

561 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
562 c41eea6e Iustin Pop

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

580 a8083063 Iustin Pop
    This is a no-op for the LV device type.
581 a8083063 Iustin Pop

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

588 a8083063 Iustin Pop
    This is a no-op for the LV device type.
589 a8083063 Iustin Pop

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

596 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
597 800ac399 Iustin Pop

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

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

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

653 c31ec06b Bernardo Dal Seno
    @rtype: float
654 c31ec06b Bernardo Dal Seno
    @return: available space in Mib
655 c31ec06b Bernardo Dal Seno

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

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

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

718 eac094c8 Klaus Aehlig
  This class represents a file storage backend device.
719 6f695a2e Manuel Franceschini

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

722 6f695a2e Manuel Franceschini
  """
723 544e5b6c Klaus Aehlig
  def __init__(self, unique_id, children, size, params, dyn_params, *args):
724 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
725 6f695a2e Manuel Franceschini

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

743 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
744 6f695a2e Manuel Franceschini

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

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

755 6f695a2e Manuel Franceschini
    """
756 746f7476 Iustin Pop
    pass
757 6f695a2e Manuel Franceschini
758 6f695a2e Manuel Franceschini
  def Open(self, force=False):
759 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
760 6f695a2e Manuel Franceschini

761 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
762 6f695a2e Manuel Franceschini

763 6f695a2e Manuel Franceschini
    """
764 6f695a2e Manuel Franceschini
    pass
765 6f695a2e Manuel Franceschini
766 6f695a2e Manuel Franceschini
  def Close(self):
767 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
768 6f695a2e Manuel Franceschini

769 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
770 6f695a2e Manuel Franceschini

771 6f695a2e Manuel Franceschini
    """
772 6f695a2e Manuel Franceschini
    pass
773 6f695a2e Manuel Franceschini
774 6f695a2e Manuel Franceschini
  def Remove(self):
775 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
776 6f695a2e Manuel Franceschini

777 c41eea6e Iustin Pop
    @rtype: boolean
778 c41eea6e Iustin Pop
    @return: True if the removal was successful
779 6f695a2e Manuel Franceschini

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

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

797 bbe4cc16 Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
798 bbe4cc16 Iustin Pop

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

820 6f695a2e Manuel Franceschini
    Check if this file already exists.
821 6f695a2e Manuel Franceschini

822 c41eea6e Iustin Pop
    @rtype: boolean
823 c41eea6e Iustin Pop
    @return: True if file exists
824 6f695a2e Manuel Franceschini

825 6f695a2e Manuel Franceschini
    """
826 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
827 ecb091e3 Iustin Pop
    return self.attached
828 6f695a2e Manuel Franceschini
829 fcff3897 Iustin Pop
  def GetActualSize(self):
830 fcff3897 Iustin Pop
    """Return the actual disk size.
831 fcff3897 Iustin Pop

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

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

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

849 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
850 c41eea6e Iustin Pop
    @return: an instance of FileStorage
851 6f695a2e Manuel Franceschini

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

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

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

885 b6135bbc Apollon Oikonomopoulos
  """
886 544e5b6c Klaus Aehlig
  def __init__(self, unique_id, children, size, params, dyn_params, *args):
887 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
888 b6135bbc Apollon Oikonomopoulos

889 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
890 b6135bbc Apollon Oikonomopoulos

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

916 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
917 b6135bbc Apollon Oikonomopoulos

918 b6135bbc Apollon Oikonomopoulos
    """
919 ee1478e5 Bernardo Dal Seno
    if excl_stor:
920 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("Persistent block device requested with"
921 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
922 0c3d9c7c Thomas Thrainer
    return PersistentBlockDevice(unique_id, children, 0, params, dyn_params)
923 b6135bbc Apollon Oikonomopoulos
924 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
925 b6135bbc Apollon Oikonomopoulos
    """Remove a device
926 b6135bbc Apollon Oikonomopoulos

927 b6135bbc Apollon Oikonomopoulos
    This is a noop
928 b6135bbc Apollon Oikonomopoulos

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

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

941 b6135bbc Apollon Oikonomopoulos

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

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

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

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

981 b6135bbc Apollon Oikonomopoulos
    """
982 b6135bbc Apollon Oikonomopoulos
    pass
983 b6135bbc Apollon Oikonomopoulos
984 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
985 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
986 b6135bbc Apollon Oikonomopoulos

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

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

998 7181fba0 Constantinos Venetsanopoulos
  """
999 544e5b6c Klaus Aehlig
  def __init__(self, unique_id, children, size, params, dyn_params, *args):
1000 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
1001 7181fba0 Constantinos Venetsanopoulos

1002 7181fba0 Constantinos Venetsanopoulos
    """
1003 0c3d9c7c Thomas Thrainer
    super(RADOSBlockDevice, self).__init__(unique_id, children, size, params,
1004 544e5b6c Klaus Aehlig
                                           dyn_params, *args)
1005 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1006 7181fba0 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1007 7181fba0 Constantinos Venetsanopoulos
1008 7181fba0 Constantinos Venetsanopoulos
    self.driver, self.rbd_name = unique_id
1009 f83057ad Raffa Santi
    self.rbd_pool = params[constants.LDP_POOL]
1010 7181fba0 Constantinos Venetsanopoulos
1011 7181fba0 Constantinos Venetsanopoulos
    self.major = self.minor = None
1012 7181fba0 Constantinos Venetsanopoulos
    self.Attach()
1013 7181fba0 Constantinos Venetsanopoulos
1014 7181fba0 Constantinos Venetsanopoulos
  @classmethod
1015 0c3d9c7c Thomas Thrainer
  def Create(cls, unique_id, children, size, spindles, params, excl_stor,
1016 0c3d9c7c Thomas Thrainer
             dyn_params):
1017 7181fba0 Constantinos Venetsanopoulos
    """Create a new rbd device.
1018 7181fba0 Constantinos Venetsanopoulos

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1311 7181fba0 Constantinos Venetsanopoulos
    """
1312 7181fba0 Constantinos Venetsanopoulos
    pass
1313 7181fba0 Constantinos Venetsanopoulos
1314 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
1315 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
1316 7181fba0 Constantinos Venetsanopoulos

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

1323 7181fba0 Constantinos Venetsanopoulos
    """
1324 cad0723b Iustin Pop
    if not backingstore:
1325 cad0723b Iustin Pop
      return
1326 7181fba0 Constantinos Venetsanopoulos
    if not self.Attach():
1327 89ff748d Thomas Thrainer
      base.ThrowError("Can't attach to rbd device during Grow()")
1328 7181fba0 Constantinos Venetsanopoulos
1329 7181fba0 Constantinos Venetsanopoulos
    if dryrun:
1330 7181fba0 Constantinos Venetsanopoulos
      # the rbd tool does not support dry runs of resize operations.
1331 7181fba0 Constantinos Venetsanopoulos
      # Since rbd volumes are thinly provisioned, we assume
1332 7181fba0 Constantinos Venetsanopoulos
      # there is always enough free space for the operation.
1333 7181fba0 Constantinos Venetsanopoulos
      return
1334 7181fba0 Constantinos Venetsanopoulos
1335 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
1336 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
1337 7181fba0 Constantinos Venetsanopoulos
    new_size = self.size + amount
1338 7181fba0 Constantinos Venetsanopoulos
1339 7181fba0 Constantinos Venetsanopoulos
    # Resize the rbd volume (Image) inside the RADOS cluster.
1340 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "resize", "-p", rbd_pool,
1341 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % new_size]
1342 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
1343 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
1344 89ff748d Thomas Thrainer
      base.ThrowError("rbd resize failed (%s): %s",
1345 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
1346 7181fba0 Constantinos Venetsanopoulos
1347 f83057ad Raffa Santi
  def GetUserspaceAccessUri(self, hypervisor):
1348 f83057ad Raffa Santi
    """Generate KVM userspace URIs to be used as `-drive file` settings.
1349 f83057ad Raffa Santi

1350 f83057ad Raffa Santi
    @see: L{BlockDev.GetUserspaceAccessUri}
1351 f83057ad Raffa Santi

1352 f83057ad Raffa Santi
    """
1353 f83057ad Raffa Santi
    if hypervisor == constants.HT_KVM:
1354 f83057ad Raffa Santi
      return "rbd:" + self.rbd_pool + "/" + self.rbd_name
1355 f83057ad Raffa Santi
    else:
1356 f83057ad Raffa Santi
      base.ThrowError("Hypervisor %s doesn't support RBD userspace access" %
1357 f83057ad Raffa Santi
                      hypervisor)
1358 f83057ad Raffa Santi
1359 7181fba0 Constantinos Venetsanopoulos
1360 89ff748d Thomas Thrainer
class ExtStorageDevice(base.BlockDev):
1361 376631d1 Constantinos Venetsanopoulos
  """A block device provided by an ExtStorage Provider.
1362 376631d1 Constantinos Venetsanopoulos

1363 376631d1 Constantinos Venetsanopoulos
  This class implements the External Storage Interface, which means
1364 376631d1 Constantinos Venetsanopoulos
  handling of the externally provided block devices.
1365 376631d1 Constantinos Venetsanopoulos

1366 376631d1 Constantinos Venetsanopoulos
  """
1367 2239acfb Dimitris Aragiorgis
  def __init__(self, unique_id, children, size, params, dyn_params, *args):
1368 376631d1 Constantinos Venetsanopoulos
    """Attaches to an extstorage block device.
1369 376631d1 Constantinos Venetsanopoulos

1370 376631d1 Constantinos Venetsanopoulos
    """
1371 0c3d9c7c Thomas Thrainer
    super(ExtStorageDevice, self).__init__(unique_id, children, size, params,
1372 0c3d9c7c Thomas Thrainer
                                           dyn_params)
1373 2239acfb Dimitris Aragiorgis
    (self.name, self.uuid) = args
1374 2239acfb Dimitris Aragiorgis
1375 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1376 376631d1 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1377 376631d1 Constantinos Venetsanopoulos
1378 376631d1 Constantinos Venetsanopoulos
    self.driver, self.vol_name = unique_id
1379 938adc87 Constantinos Venetsanopoulos
    self.ext_params = params
1380 376631d1 Constantinos Venetsanopoulos
1381 376631d1 Constantinos Venetsanopoulos
    self.major = self.minor = None
1382 376631d1 Constantinos Venetsanopoulos
    self.Attach()
1383 376631d1 Constantinos Venetsanopoulos
1384 376631d1 Constantinos Venetsanopoulos
  @classmethod
1385 0c3d9c7c Thomas Thrainer
  def Create(cls, unique_id, children, size, spindles, params, excl_stor,
1386 2239acfb Dimitris Aragiorgis
             dyn_params, *args):
1387 376631d1 Constantinos Venetsanopoulos
    """Create a new extstorage device.
1388 376631d1 Constantinos Venetsanopoulos

1389 376631d1 Constantinos Venetsanopoulos
    Provision a new volume using an extstorage provider, which will
1390 376631d1 Constantinos Venetsanopoulos
    then be mapped to a block device.
1391 376631d1 Constantinos Venetsanopoulos

1392 376631d1 Constantinos Venetsanopoulos
    """
1393 2239acfb Dimitris Aragiorgis
    (name, uuid) = args
1394 2239acfb Dimitris Aragiorgis
1395 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1396 376631d1 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
1397 376631d1 Constantinos Venetsanopoulos
                                   str(unique_id))
1398 ee1478e5 Bernardo Dal Seno
    if excl_stor:
1399 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("extstorage device requested with"
1400 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
1401 376631d1 Constantinos Venetsanopoulos
1402 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's create script,
1403 376631d1 Constantinos Venetsanopoulos
    # to provision a new Volume inside the External Storage
1404 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id,
1405 2239acfb Dimitris Aragiorgis
                      params, size=str(size), name=name, uuid=uuid)
1406 376631d1 Constantinos Venetsanopoulos
1407 0c3d9c7c Thomas Thrainer
    return ExtStorageDevice(unique_id, children, size, params, dyn_params)
1408 376631d1 Constantinos Venetsanopoulos
1409 376631d1 Constantinos Venetsanopoulos
  def Remove(self):
1410 376631d1 Constantinos Venetsanopoulos
    """Remove the extstorage device.
1411 376631d1 Constantinos Venetsanopoulos

1412 376631d1 Constantinos Venetsanopoulos
    """
1413 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1414 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1415 376631d1 Constantinos Venetsanopoulos
      return
1416 376631d1 Constantinos Venetsanopoulos
1417 376631d1 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
1418 376631d1 Constantinos Venetsanopoulos
    self.Shutdown()
1419 376631d1 Constantinos Venetsanopoulos
1420 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's remove script,
1421 376631d1 Constantinos Venetsanopoulos
    # to remove the Volume from the External Storage
1422 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id,
1423 2239acfb Dimitris Aragiorgis
                      self.ext_params, name=self.name, uuid=self.uuid)
1424 376631d1 Constantinos Venetsanopoulos
1425 376631d1 Constantinos Venetsanopoulos
  def Rename(self, new_id):
1426 376631d1 Constantinos Venetsanopoulos
    """Rename this device.
1427 376631d1 Constantinos Venetsanopoulos

1428 376631d1 Constantinos Venetsanopoulos
    """
1429 376631d1 Constantinos Venetsanopoulos
    pass
1430 376631d1 Constantinos Venetsanopoulos
1431 376631d1 Constantinos Venetsanopoulos
  def Attach(self):
1432 376631d1 Constantinos Venetsanopoulos
    """Attach to an existing extstorage device.
1433 376631d1 Constantinos Venetsanopoulos

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

1437 376631d1 Constantinos Venetsanopoulos
    """
1438 376631d1 Constantinos Venetsanopoulos
    self.attached = False
1439 376631d1 Constantinos Venetsanopoulos
1440 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's attach script,
1441 376631d1 Constantinos Venetsanopoulos
    # to attach an existing Volume to a block device under /dev
1442 376631d1 Constantinos Venetsanopoulos
    self.dev_path = _ExtStorageAction(constants.ES_ACTION_ATTACH,
1443 2239acfb Dimitris Aragiorgis
                                      self.unique_id, self.ext_params,
1444 2239acfb Dimitris Aragiorgis
                                      name=self.name, uuid=self.uuid)
1445 376631d1 Constantinos Venetsanopoulos
1446 376631d1 Constantinos Venetsanopoulos
    try:
1447 376631d1 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
1448 376631d1 Constantinos Venetsanopoulos
    except OSError, err:
1449 376631d1 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
1450 376631d1 Constantinos Venetsanopoulos
      return False
1451 376631d1 Constantinos Venetsanopoulos
1452 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
1453 376631d1 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
1454 376631d1 Constantinos Venetsanopoulos
      return False
1455 376631d1 Constantinos Venetsanopoulos
1456 376631d1 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
1457 376631d1 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
1458 376631d1 Constantinos Venetsanopoulos
    self.attached = True
1459 376631d1 Constantinos Venetsanopoulos
1460 376631d1 Constantinos Venetsanopoulos
    return True
1461 376631d1 Constantinos Venetsanopoulos
1462 376631d1 Constantinos Venetsanopoulos
  def Assemble(self):
1463 376631d1 Constantinos Venetsanopoulos
    """Assemble the device.
1464 376631d1 Constantinos Venetsanopoulos

1465 376631d1 Constantinos Venetsanopoulos
    """
1466 376631d1 Constantinos Venetsanopoulos
    pass
1467 376631d1 Constantinos Venetsanopoulos
1468 376631d1 Constantinos Venetsanopoulos
  def Shutdown(self):
1469 376631d1 Constantinos Venetsanopoulos
    """Shutdown the device.
1470 376631d1 Constantinos Venetsanopoulos

1471 376631d1 Constantinos Venetsanopoulos
    """
1472 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1473 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1474 376631d1 Constantinos Venetsanopoulos
      return
1475 376631d1 Constantinos Venetsanopoulos
1476 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's detach script,
1477 376631d1 Constantinos Venetsanopoulos
    # to detach an existing Volume from it's block device under /dev
1478 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id,
1479 2239acfb Dimitris Aragiorgis
                      self.ext_params, name=self.name, uuid=self.uuid)
1480 376631d1 Constantinos Venetsanopoulos
1481 376631d1 Constantinos Venetsanopoulos
    self.minor = None
1482 376631d1 Constantinos Venetsanopoulos
    self.dev_path = None
1483 376631d1 Constantinos Venetsanopoulos
1484 376631d1 Constantinos Venetsanopoulos
  def Open(self, force=False):
1485 376631d1 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
1486 376631d1 Constantinos Venetsanopoulos

1487 376631d1 Constantinos Venetsanopoulos
    """
1488 376631d1 Constantinos Venetsanopoulos
    pass
1489 376631d1 Constantinos Venetsanopoulos
1490 376631d1 Constantinos Venetsanopoulos
  def Close(self):
1491 376631d1 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1492 376631d1 Constantinos Venetsanopoulos

1493 376631d1 Constantinos Venetsanopoulos
    """
1494 376631d1 Constantinos Venetsanopoulos
    pass
1495 376631d1 Constantinos Venetsanopoulos
1496 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
1497 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
1498 376631d1 Constantinos Venetsanopoulos

1499 376631d1 Constantinos Venetsanopoulos
    @type amount: integer
1500 376631d1 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1501 376631d1 Constantinos Venetsanopoulos
    @type dryrun: boolean
1502 376631d1 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1503 376631d1 Constantinos Venetsanopoulos
        only, without actually increasing the size
1504 376631d1 Constantinos Venetsanopoulos

1505 376631d1 Constantinos Venetsanopoulos
    """
1506 376631d1 Constantinos Venetsanopoulos
    if not backingstore:
1507 376631d1 Constantinos Venetsanopoulos
      return
1508 376631d1 Constantinos Venetsanopoulos
    if not self.Attach():
1509 89ff748d Thomas Thrainer
      base.ThrowError("Can't attach to extstorage device during Grow()")
1510 376631d1 Constantinos Venetsanopoulos
1511 376631d1 Constantinos Venetsanopoulos
    if dryrun:
1512 376631d1 Constantinos Venetsanopoulos
      # we do not support dry runs of resize operations for now.
1513 376631d1 Constantinos Venetsanopoulos
      return
1514 376631d1 Constantinos Venetsanopoulos
1515 376631d1 Constantinos Venetsanopoulos
    new_size = self.size + amount
1516 376631d1 Constantinos Venetsanopoulos
1517 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's grow script,
1518 376631d1 Constantinos Venetsanopoulos
    # to grow an existing Volume inside the External Storage
1519 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_GROW, self.unique_id,
1520 2239acfb Dimitris Aragiorgis
                      self.ext_params, size=str(self.size), grow=str(new_size),
1521 2239acfb Dimitris Aragiorgis
                      name=self.name, uuid=self.uuid)
1522 376631d1 Constantinos Venetsanopoulos
1523 376631d1 Constantinos Venetsanopoulos
  def SetInfo(self, text):
1524 376631d1 Constantinos Venetsanopoulos
    """Update metadata with info text.
1525 376631d1 Constantinos Venetsanopoulos

1526 376631d1 Constantinos Venetsanopoulos
    """
1527 376631d1 Constantinos Venetsanopoulos
    # Replace invalid characters
1528 376631d1 Constantinos Venetsanopoulos
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
1529 376631d1 Constantinos Venetsanopoulos
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
1530 376631d1 Constantinos Venetsanopoulos
1531 376631d1 Constantinos Venetsanopoulos
    # Only up to 128 characters are allowed
1532 376631d1 Constantinos Venetsanopoulos
    text = text[:128]
1533 376631d1 Constantinos Venetsanopoulos
1534 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's setinfo script,
1535 376631d1 Constantinos Venetsanopoulos
    # to set metadata for an existing Volume inside the External Storage
1536 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
1537 2239acfb Dimitris Aragiorgis
                      self.ext_params, metadata=text,
1538 2239acfb Dimitris Aragiorgis
                      name=self.name, uuid=self.uuid)
1539 376631d1 Constantinos Venetsanopoulos
1540 376631d1 Constantinos Venetsanopoulos
1541 938adc87 Constantinos Venetsanopoulos
def _ExtStorageAction(action, unique_id, ext_params,
1542 2239acfb Dimitris Aragiorgis
                      size=None, grow=None, metadata=None,
1543 2239acfb Dimitris Aragiorgis
                      name=None, uuid=None):
1544 376631d1 Constantinos Venetsanopoulos
  """Take an External Storage action.
1545 376631d1 Constantinos Venetsanopoulos

1546 376631d1 Constantinos Venetsanopoulos
  Take an External Storage action concerning or affecting
1547 376631d1 Constantinos Venetsanopoulos
  a specific Volume inside the External Storage.
1548 376631d1 Constantinos Venetsanopoulos

1549 376631d1 Constantinos Venetsanopoulos
  @type action: string
1550 376631d1 Constantinos Venetsanopoulos
  @param action: which action to perform. One of:
1551 376631d1 Constantinos Venetsanopoulos
                 create / remove / grow / attach / detach
1552 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1553 376631d1 Constantinos Venetsanopoulos
  @param unique_id: a tuple containing the type of ExtStorage (driver)
1554 376631d1 Constantinos Venetsanopoulos
                    and the Volume name
1555 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1556 938adc87 Constantinos Venetsanopoulos
  @param ext_params: ExtStorage parameters
1557 376631d1 Constantinos Venetsanopoulos
  @type size: integer
1558 376631d1 Constantinos Venetsanopoulos
  @param size: the size of the Volume in mebibytes
1559 376631d1 Constantinos Venetsanopoulos
  @type grow: integer
1560 376631d1 Constantinos Venetsanopoulos
  @param grow: the new size in mebibytes (after grow)
1561 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1562 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume, for use by the provider
1563 2239acfb Dimitris Aragiorgis
  @type name: string
1564 2239acfb Dimitris Aragiorgis
  @param name: name of the Volume (objects.Disk.name)
1565 2239acfb Dimitris Aragiorgis
  @type uuid: string
1566 2239acfb Dimitris Aragiorgis
  @param uuid: uuid of the Volume (objects.Disk.uuid)
1567 376631d1 Constantinos Venetsanopoulos
  @rtype: None or a block device path (during attach)
1568 376631d1 Constantinos Venetsanopoulos

1569 376631d1 Constantinos Venetsanopoulos
  """
1570 376631d1 Constantinos Venetsanopoulos
  driver, vol_name = unique_id
1571 376631d1 Constantinos Venetsanopoulos
1572 376631d1 Constantinos Venetsanopoulos
  # Create an External Storage instance of type `driver'
1573 376631d1 Constantinos Venetsanopoulos
  status, inst_es = ExtStorageFromDisk(driver)
1574 376631d1 Constantinos Venetsanopoulos
  if not status:
1575 89ff748d Thomas Thrainer
    base.ThrowError("%s" % inst_es)
1576 376631d1 Constantinos Venetsanopoulos
1577 376631d1 Constantinos Venetsanopoulos
  # Create the basic environment for the driver's scripts
1578 938adc87 Constantinos Venetsanopoulos
  create_env = _ExtStorageEnvironment(unique_id, ext_params, size,
1579 2239acfb Dimitris Aragiorgis
                                      grow, metadata, name, uuid)
1580 376631d1 Constantinos Venetsanopoulos
1581 376631d1 Constantinos Venetsanopoulos
  # Do not use log file for action `attach' as we need
1582 376631d1 Constantinos Venetsanopoulos
  # to get the output from RunResult
1583 376631d1 Constantinos Venetsanopoulos
  # TODO: find a way to have a log file for attach too
1584 376631d1 Constantinos Venetsanopoulos
  logfile = None
1585 376631d1 Constantinos Venetsanopoulos
  if action is not constants.ES_ACTION_ATTACH:
1586 376631d1 Constantinos Venetsanopoulos
    logfile = _VolumeLogName(action, driver, vol_name)
1587 376631d1 Constantinos Venetsanopoulos
1588 376631d1 Constantinos Venetsanopoulos
  # Make sure the given action results in a valid script
1589 376631d1 Constantinos Venetsanopoulos
  if action not in constants.ES_SCRIPTS:
1590 89ff748d Thomas Thrainer
    base.ThrowError("Action '%s' doesn't result in a valid ExtStorage script" %
1591 89ff748d Thomas Thrainer
                    action)
1592 376631d1 Constantinos Venetsanopoulos
1593 376631d1 Constantinos Venetsanopoulos
  # Find out which external script to run according the given action
1594 376631d1 Constantinos Venetsanopoulos
  script_name = action + "_script"
1595 376631d1 Constantinos Venetsanopoulos
  script = getattr(inst_es, script_name)
1596 376631d1 Constantinos Venetsanopoulos
1597 376631d1 Constantinos Venetsanopoulos
  # Run the external script
1598 376631d1 Constantinos Venetsanopoulos
  result = utils.RunCmd([script], env=create_env,
1599 376631d1 Constantinos Venetsanopoulos
                        cwd=inst_es.path, output=logfile,)
1600 376631d1 Constantinos Venetsanopoulos
  if result.failed:
1601 376631d1 Constantinos Venetsanopoulos
    logging.error("External storage's %s command '%s' returned"
1602 376631d1 Constantinos Venetsanopoulos
                  " error: %s, logfile: %s, output: %s",
1603 376631d1 Constantinos Venetsanopoulos
                  action, result.cmd, result.fail_reason,
1604 376631d1 Constantinos Venetsanopoulos
                  logfile, result.output)
1605 376631d1 Constantinos Venetsanopoulos
1606 376631d1 Constantinos Venetsanopoulos
    # If logfile is 'None' (during attach), it breaks TailFile
1607 376631d1 Constantinos Venetsanopoulos
    # TODO: have a log file for attach too
1608 376631d1 Constantinos Venetsanopoulos
    if action is not constants.ES_ACTION_ATTACH:
1609 376631d1 Constantinos Venetsanopoulos
      lines = [utils.SafeEncode(val)
1610 376631d1 Constantinos Venetsanopoulos
               for val in utils.TailFile(logfile, lines=20)]
1611 376631d1 Constantinos Venetsanopoulos
    else:
1612 376631d1 Constantinos Venetsanopoulos
      lines = result.output[-20:]
1613 376631d1 Constantinos Venetsanopoulos
1614 89ff748d Thomas Thrainer
    base.ThrowError("External storage's %s script failed (%s), last"
1615 89ff748d Thomas Thrainer
                    " lines of output:\n%s",
1616 89ff748d Thomas Thrainer
                    action, result.fail_reason, "\n".join(lines))
1617 376631d1 Constantinos Venetsanopoulos
1618 376631d1 Constantinos Venetsanopoulos
  if action == constants.ES_ACTION_ATTACH:
1619 376631d1 Constantinos Venetsanopoulos
    return result.stdout
1620 376631d1 Constantinos Venetsanopoulos
1621 376631d1 Constantinos Venetsanopoulos
1622 376631d1 Constantinos Venetsanopoulos
def ExtStorageFromDisk(name, base_dir=None):
1623 376631d1 Constantinos Venetsanopoulos
  """Create an ExtStorage instance from disk.
1624 376631d1 Constantinos Venetsanopoulos

1625 376631d1 Constantinos Venetsanopoulos
  This function will return an ExtStorage instance
1626 376631d1 Constantinos Venetsanopoulos
  if the given name is a valid ExtStorage name.
1627 376631d1 Constantinos Venetsanopoulos

1628 376631d1 Constantinos Venetsanopoulos
  @type base_dir: string
1629 376631d1 Constantinos Venetsanopoulos
  @keyword base_dir: Base directory containing ExtStorage installations.
1630 376631d1 Constantinos Venetsanopoulos
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
1631 376631d1 Constantinos Venetsanopoulos
  @rtype: tuple
1632 376631d1 Constantinos Venetsanopoulos
  @return: True and the ExtStorage instance if we find a valid one, or
1633 376631d1 Constantinos Venetsanopoulos
      False and the diagnose message on error
1634 376631d1 Constantinos Venetsanopoulos

1635 376631d1 Constantinos Venetsanopoulos
  """
1636 376631d1 Constantinos Venetsanopoulos
  if base_dir is None:
1637 376631d1 Constantinos Venetsanopoulos
    es_base_dir = pathutils.ES_SEARCH_PATH
1638 376631d1 Constantinos Venetsanopoulos
  else:
1639 376631d1 Constantinos Venetsanopoulos
    es_base_dir = [base_dir]
1640 376631d1 Constantinos Venetsanopoulos
1641 376631d1 Constantinos Venetsanopoulos
  es_dir = utils.FindFile(name, es_base_dir, os.path.isdir)
1642 376631d1 Constantinos Venetsanopoulos
1643 376631d1 Constantinos Venetsanopoulos
  if es_dir is None:
1644 376631d1 Constantinos Venetsanopoulos
    return False, ("Directory for External Storage Provider %s not"
1645 376631d1 Constantinos Venetsanopoulos
                   " found in search path" % name)
1646 376631d1 Constantinos Venetsanopoulos
1647 376631d1 Constantinos Venetsanopoulos
  # ES Files dictionary, we will populate it with the absolute path
1648 376631d1 Constantinos Venetsanopoulos
  # names; if the value is True, then it is a required file, otherwise
1649 376631d1 Constantinos Venetsanopoulos
  # an optional one
1650 376631d1 Constantinos Venetsanopoulos
  es_files = dict.fromkeys(constants.ES_SCRIPTS, True)
1651 376631d1 Constantinos Venetsanopoulos
1652 938adc87 Constantinos Venetsanopoulos
  es_files[constants.ES_PARAMETERS_FILE] = True
1653 938adc87 Constantinos Venetsanopoulos
1654 938adc87 Constantinos Venetsanopoulos
  for (filename, _) in es_files.items():
1655 376631d1 Constantinos Venetsanopoulos
    es_files[filename] = utils.PathJoin(es_dir, filename)
1656 376631d1 Constantinos Venetsanopoulos
1657 376631d1 Constantinos Venetsanopoulos
    try:
1658 376631d1 Constantinos Venetsanopoulos
      st = os.stat(es_files[filename])
1659 376631d1 Constantinos Venetsanopoulos
    except EnvironmentError, err:
1660 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is missing (%s)" %
1661 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir, utils.ErrnoOrStr(err)))
1662 376631d1 Constantinos Venetsanopoulos
1663 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1664 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is not a regular file" %
1665 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir))
1666 376631d1 Constantinos Venetsanopoulos
1667 376631d1 Constantinos Venetsanopoulos
    if filename in constants.ES_SCRIPTS:
1668 376631d1 Constantinos Venetsanopoulos
      if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
1669 376631d1 Constantinos Venetsanopoulos
        return False, ("File '%s' under path '%s' is not executable" %
1670 376631d1 Constantinos Venetsanopoulos
                       (filename, es_dir))
1671 376631d1 Constantinos Venetsanopoulos
1672 938adc87 Constantinos Venetsanopoulos
  parameters = []
1673 938adc87 Constantinos Venetsanopoulos
  if constants.ES_PARAMETERS_FILE in es_files:
1674 938adc87 Constantinos Venetsanopoulos
    parameters_file = es_files[constants.ES_PARAMETERS_FILE]
1675 938adc87 Constantinos Venetsanopoulos
    try:
1676 938adc87 Constantinos Venetsanopoulos
      parameters = utils.ReadFile(parameters_file).splitlines()
1677 938adc87 Constantinos Venetsanopoulos
    except EnvironmentError, err:
1678 938adc87 Constantinos Venetsanopoulos
      return False, ("Error while reading the EXT parameters file at %s: %s" %
1679 938adc87 Constantinos Venetsanopoulos
                     (parameters_file, utils.ErrnoOrStr(err)))
1680 938adc87 Constantinos Venetsanopoulos
    parameters = [v.split(None, 1) for v in parameters]
1681 938adc87 Constantinos Venetsanopoulos
1682 376631d1 Constantinos Venetsanopoulos
  es_obj = \
1683 376631d1 Constantinos Venetsanopoulos
    objects.ExtStorage(name=name, path=es_dir,
1684 376631d1 Constantinos Venetsanopoulos
                       create_script=es_files[constants.ES_SCRIPT_CREATE],
1685 376631d1 Constantinos Venetsanopoulos
                       remove_script=es_files[constants.ES_SCRIPT_REMOVE],
1686 376631d1 Constantinos Venetsanopoulos
                       grow_script=es_files[constants.ES_SCRIPT_GROW],
1687 376631d1 Constantinos Venetsanopoulos
                       attach_script=es_files[constants.ES_SCRIPT_ATTACH],
1688 376631d1 Constantinos Venetsanopoulos
                       detach_script=es_files[constants.ES_SCRIPT_DETACH],
1689 938adc87 Constantinos Venetsanopoulos
                       setinfo_script=es_files[constants.ES_SCRIPT_SETINFO],
1690 938adc87 Constantinos Venetsanopoulos
                       verify_script=es_files[constants.ES_SCRIPT_VERIFY],
1691 938adc87 Constantinos Venetsanopoulos
                       supported_parameters=parameters)
1692 376631d1 Constantinos Venetsanopoulos
  return True, es_obj
1693 376631d1 Constantinos Venetsanopoulos
1694 376631d1 Constantinos Venetsanopoulos
1695 938adc87 Constantinos Venetsanopoulos
def _ExtStorageEnvironment(unique_id, ext_params,
1696 2239acfb Dimitris Aragiorgis
                           size=None, grow=None, metadata=None,
1697 2239acfb Dimitris Aragiorgis
                           name=None, uuid=None):
1698 376631d1 Constantinos Venetsanopoulos
  """Calculate the environment for an External Storage script.
1699 376631d1 Constantinos Venetsanopoulos

1700 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1701 376631d1 Constantinos Venetsanopoulos
  @param unique_id: ExtStorage pool and name of the Volume
1702 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1703 938adc87 Constantinos Venetsanopoulos
  @param ext_params: the EXT parameters
1704 376631d1 Constantinos Venetsanopoulos
  @type size: string
1705 376631d1 Constantinos Venetsanopoulos
  @param size: size of the Volume (in mebibytes)
1706 376631d1 Constantinos Venetsanopoulos
  @type grow: string
1707 376631d1 Constantinos Venetsanopoulos
  @param grow: new size of Volume after grow (in mebibytes)
1708 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1709 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume
1710 2239acfb Dimitris Aragiorgis
  @type name: string
1711 2239acfb Dimitris Aragiorgis
  @param name: name of the Volume (objects.Disk.name)
1712 2239acfb Dimitris Aragiorgis
  @type uuid: string
1713 2239acfb Dimitris Aragiorgis
  @param uuid: uuid of the Volume (objects.Disk.uuid)
1714 376631d1 Constantinos Venetsanopoulos
  @rtype: dict
1715 376631d1 Constantinos Venetsanopoulos
  @return: dict of environment variables
1716 376631d1 Constantinos Venetsanopoulos

1717 376631d1 Constantinos Venetsanopoulos
  """
1718 376631d1 Constantinos Venetsanopoulos
  vol_name = unique_id[1]
1719 376631d1 Constantinos Venetsanopoulos
1720 376631d1 Constantinos Venetsanopoulos
  result = {}
1721 376631d1 Constantinos Venetsanopoulos
  result["VOL_NAME"] = vol_name
1722 376631d1 Constantinos Venetsanopoulos
1723 938adc87 Constantinos Venetsanopoulos
  # EXT params
1724 938adc87 Constantinos Venetsanopoulos
  for pname, pvalue in ext_params.items():
1725 938adc87 Constantinos Venetsanopoulos
    result["EXTP_%s" % pname.upper()] = str(pvalue)
1726 938adc87 Constantinos Venetsanopoulos
1727 376631d1 Constantinos Venetsanopoulos
  if size is not None:
1728 376631d1 Constantinos Venetsanopoulos
    result["VOL_SIZE"] = size
1729 376631d1 Constantinos Venetsanopoulos
1730 376631d1 Constantinos Venetsanopoulos
  if grow is not None:
1731 376631d1 Constantinos Venetsanopoulos
    result["VOL_NEW_SIZE"] = grow
1732 376631d1 Constantinos Venetsanopoulos
1733 376631d1 Constantinos Venetsanopoulos
  if metadata is not None:
1734 376631d1 Constantinos Venetsanopoulos
    result["VOL_METADATA"] = metadata
1735 376631d1 Constantinos Venetsanopoulos
1736 2239acfb Dimitris Aragiorgis
  if name is not None:
1737 2239acfb Dimitris Aragiorgis
    result["VOL_CNAME"] = name
1738 2239acfb Dimitris Aragiorgis
1739 2239acfb Dimitris Aragiorgis
  if uuid is not None:
1740 2239acfb Dimitris Aragiorgis
    result["VOL_UUID"] = uuid
1741 2239acfb Dimitris Aragiorgis
1742 376631d1 Constantinos Venetsanopoulos
  return result
1743 376631d1 Constantinos Venetsanopoulos
1744 376631d1 Constantinos Venetsanopoulos
1745 376631d1 Constantinos Venetsanopoulos
def _VolumeLogName(kind, es_name, volume):
1746 376631d1 Constantinos Venetsanopoulos
  """Compute the ExtStorage log filename for a given Volume and operation.
1747 376631d1 Constantinos Venetsanopoulos

1748 376631d1 Constantinos Venetsanopoulos
  @type kind: string
1749 376631d1 Constantinos Venetsanopoulos
  @param kind: the operation type (e.g. create, remove etc.)
1750 376631d1 Constantinos Venetsanopoulos
  @type es_name: string
1751 376631d1 Constantinos Venetsanopoulos
  @param es_name: the ExtStorage name
1752 376631d1 Constantinos Venetsanopoulos
  @type volume: string
1753 376631d1 Constantinos Venetsanopoulos
  @param volume: the name of the Volume inside the External Storage
1754 376631d1 Constantinos Venetsanopoulos

1755 376631d1 Constantinos Venetsanopoulos
  """
1756 376631d1 Constantinos Venetsanopoulos
  # Check if the extstorage log dir is a valid dir
1757 376631d1 Constantinos Venetsanopoulos
  if not os.path.isdir(pathutils.LOG_ES_DIR):
1758 89ff748d Thomas Thrainer
    base.ThrowError("Cannot find log directory: %s", pathutils.LOG_ES_DIR)
1759 376631d1 Constantinos Venetsanopoulos
1760 376631d1 Constantinos Venetsanopoulos
  # TODO: Use tempfile.mkstemp to create unique filename
1761 89ff748d Thomas Thrainer
  basename = ("%s-%s-%s-%s.log" %
1762 89ff748d Thomas Thrainer
              (kind, es_name, volume, utils.TimestampForFilename()))
1763 89ff748d Thomas Thrainer
  return utils.PathJoin(pathutils.LOG_ES_DIR, basename)
1764 376631d1 Constantinos Venetsanopoulos
1765 376631d1 Constantinos Venetsanopoulos
1766 a8083063 Iustin Pop
DEV_MAP = {
1767 cd3b4ff4 Helga Velroyen
  constants.DT_PLAIN: LogicalVolume,
1768 cd3b4ff4 Helga Velroyen
  constants.DT_DRBD8: drbd.DRBD8Dev,
1769 cd3b4ff4 Helga Velroyen
  constants.DT_BLOCK: PersistentBlockDevice,
1770 cd3b4ff4 Helga Velroyen
  constants.DT_RBD: RADOSBlockDevice,
1771 cd3b4ff4 Helga Velroyen
  constants.DT_EXT: ExtStorageDevice,
1772 cd3b4ff4 Helga Velroyen
  constants.DT_FILE: FileStorage,
1773 cd3b4ff4 Helga Velroyen
  constants.DT_SHARED_FILE: FileStorage,
1774 a8083063 Iustin Pop
  }
1775 a8083063 Iustin Pop
1776 a8083063 Iustin Pop
1777 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
1778 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
1779 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1780 94dcbdb0 Andrea Spadaccini
1781 94dcbdb0 Andrea Spadaccini
1782 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
1783 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
1784 5ff82cc9 René Nussbaumer

1785 5ff82cc9 René Nussbaumer
  """
1786 6da90c0a Helga Velroyen
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
1787 5ff82cc9 René Nussbaumer
  if missing:
1788 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
1789 5ff82cc9 René Nussbaumer
                                 missing)
1790 5ff82cc9 René Nussbaumer
1791 5ff82cc9 René Nussbaumer
1792 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
1793 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1794 a8083063 Iustin Pop

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

1798 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1799 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
1800 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1801 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1802 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1803 94dcbdb0 Andrea Spadaccini

1804 a8083063 Iustin Pop
  """
1805 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1806 0c3d9c7c Thomas Thrainer
  device = DEV_MAP[disk.dev_type](disk.logical_id, children, disk.size,
1807 2239acfb Dimitris Aragiorgis
                                  disk.params, disk.dynamic_params,
1808 2239acfb Dimitris Aragiorgis
                                  disk.name, disk.uuid)
1809 cb999543 Iustin Pop
  if not device.attached:
1810 a8083063 Iustin Pop
    return None
1811 ecb091e3 Iustin Pop
  return device
1812 a8083063 Iustin Pop
1813 a8083063 Iustin Pop
1814 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
1815 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1816 a8083063 Iustin Pop

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

1820 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1821 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
1822 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1823 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1824 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1825 94dcbdb0 Andrea Spadaccini

1826 a8083063 Iustin Pop
  """
1827 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1828 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1829 0c3d9c7c Thomas Thrainer
  device = DEV_MAP[disk.dev_type](disk.logical_id, children, disk.size,
1830 2239acfb Dimitris Aragiorgis
                                  disk.params, disk.dynamic_params,
1831 2239acfb Dimitris Aragiorgis
                                  disk.name, disk.uuid)
1832 1063abd1 Iustin Pop
  device.Assemble()
1833 a8083063 Iustin Pop
  return device
1834 a8083063 Iustin Pop
1835 a8083063 Iustin Pop
1836 ee1478e5 Bernardo Dal Seno
def Create(disk, children, excl_stor):
1837 a8083063 Iustin Pop
  """Create a device.
1838 a8083063 Iustin Pop

1839 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1840 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
1841 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1842 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1843 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1844 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
1845 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
1846 5073fa0c Bernardo Dal Seno
  @rtype: L{bdev.BlockDev}
1847 5073fa0c Bernardo Dal Seno
  @return: the created device, or C{None} in case of an error
1848 94dcbdb0 Andrea Spadaccini

1849 a8083063 Iustin Pop
  """
1850 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1851 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1852 0c3d9c7c Thomas Thrainer
  device = DEV_MAP[disk.dev_type].Create(disk.logical_id, children, disk.size,
1853 0c3d9c7c Thomas Thrainer
                                         disk.spindles, disk.params, excl_stor,
1854 2239acfb Dimitris Aragiorgis
                                         disk.dynamic_params,
1855 2239acfb Dimitris Aragiorgis
                                         disk.name, disk.uuid)
1856 a8083063 Iustin Pop
  return device