Statistics
| Branch: | Tag: | Revision:

root / lib / storage / bdev.py @ 178ad717

History | View | Annotate | Download (53.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 e7d34815 Santi Raffa
"""Block device abstraction.
23 e7d34815 Santi Raffa

24 e7d34815 Santi Raffa
"""
25 a8083063 Iustin Pop
26 a8083063 Iustin Pop
import re
27 b6135bbc Apollon Oikonomopoulos
import stat
28 6f695a2e Manuel Franceschini
import os
29 468c5f77 Iustin Pop
import logging
30 63c73073 Bernardo Dal Seno
import math
31 a8083063 Iustin Pop
32 a8083063 Iustin Pop
from ganeti import utils
33 a8083063 Iustin Pop
from ganeti import errors
34 fe96220b Iustin Pop
from ganeti import constants
35 96acbc09 Michael Hanselmann
from ganeti import objects
36 cea881e5 Michael Hanselmann
from ganeti import compat
37 fbdac0d9 Michael Hanselmann
from ganeti import pathutils
38 bdecfea2 Stratos Psomadakis
from ganeti import serializer
39 cde49218 Helga Velroyen
from ganeti.storage import base
40 13a6c760 Helga Velroyen
from ganeti.storage import drbd
41 2656b017 Santi Raffa
from ganeti.storage.filestorage import FileStorage
42 8106dd64 Santi Raffa
from ganeti.storage.gluster import GlusterStorage
43 310fbb64 Iustin Pop
44 310fbb64 Iustin Pop
45 bdecfea2 Stratos Psomadakis
class RbdShowmappedJsonError(Exception):
46 bdecfea2 Stratos Psomadakis
  """`rbd showmmapped' JSON formatting error Exception class.
47 bdecfea2 Stratos Psomadakis

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

55 e398546b Iustin Pop
  @param result: result from RunCmd
56 e398546b Iustin Pop

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

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

75 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
76 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

563 96acbc09 Michael Hanselmann
    @rtype: objects.BlockDevStatus
564 c41eea6e Iustin Pop

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

582 a8083063 Iustin Pop
    This is a no-op for the LV device type.
583 a8083063 Iustin Pop

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

590 a8083063 Iustin Pop
    This is a no-op for the LV device type.
591 a8083063 Iustin Pop

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

598 800ac399 Iustin Pop
    @returns: tuple (vg, lv)
599 800ac399 Iustin Pop

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

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

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

655 c31ec06b Bernardo Dal Seno
    @rtype: float
656 c31ec06b Bernardo Dal Seno
    @return: available space in Mib
657 c31ec06b Bernardo Dal Seno

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

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

712 baa7f1d6 Bernardo Dal Seno
    """
713 baa7f1d6 Bernardo Dal Seno
    assert self.attached, "BlockDevice not attached in GetActualSpindles()"
714 baa7f1d6 Bernardo Dal Seno
    return len(self.pv_names)
715 baa7f1d6 Bernardo Dal Seno
716 a2cfdea2 Iustin Pop
717 89ff748d Thomas Thrainer
class PersistentBlockDevice(base.BlockDev):
718 b6135bbc Apollon Oikonomopoulos
  """A block device with persistent node
719 b6135bbc Apollon Oikonomopoulos

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

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

726 b6135bbc Apollon Oikonomopoulos
  """
727 0c3d9c7c Thomas Thrainer
  def __init__(self, unique_id, children, size, params, dyn_params):
728 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
729 b6135bbc Apollon Oikonomopoulos

730 b6135bbc Apollon Oikonomopoulos
    The unique_id is a path under /dev.
731 b6135bbc Apollon Oikonomopoulos

732 b6135bbc Apollon Oikonomopoulos
    """
733 94dcbdb0 Andrea Spadaccini
    super(PersistentBlockDevice, self).__init__(unique_id, children, size,
734 0c3d9c7c Thomas Thrainer
                                                params, dyn_params)
735 b6135bbc Apollon Oikonomopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
736 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
737 b6135bbc Apollon Oikonomopoulos
    self.dev_path = unique_id[1]
738 d0c8c01d Iustin Pop
    if not os.path.realpath(self.dev_path).startswith("/dev/"):
739 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Full path '%s' lies outside /dev" %
740 b6135bbc Apollon Oikonomopoulos
                              os.path.realpath(self.dev_path))
741 b6135bbc Apollon Oikonomopoulos
    # TODO: this is just a safety guard checking that we only deal with devices
742 b6135bbc Apollon Oikonomopoulos
    # we know how to handle. In the future this will be integrated with
743 b6135bbc Apollon Oikonomopoulos
    # external storage backends and possible values will probably be collected
744 b6135bbc Apollon Oikonomopoulos
    # from the cluster configuration.
745 b6135bbc Apollon Oikonomopoulos
    if unique_id[0] != constants.BLOCKDEV_DRIVER_MANUAL:
746 b6135bbc Apollon Oikonomopoulos
      raise ValueError("Got persistent block device of invalid type: %s" %
747 b6135bbc Apollon Oikonomopoulos
                       unique_id[0])
748 b6135bbc Apollon Oikonomopoulos
749 b6135bbc Apollon Oikonomopoulos
    self.major = self.minor = None
750 b6135bbc Apollon Oikonomopoulos
    self.Attach()
751 b6135bbc Apollon Oikonomopoulos
752 b6135bbc Apollon Oikonomopoulos
  @classmethod
753 0c3d9c7c Thomas Thrainer
  def Create(cls, unique_id, children, size, spindles, params, excl_stor,
754 0c3d9c7c Thomas Thrainer
             dyn_params):
755 b6135bbc Apollon Oikonomopoulos
    """Create a new device
756 b6135bbc Apollon Oikonomopoulos

757 b6135bbc Apollon Oikonomopoulos
    This is a noop, we only return a PersistentBlockDevice instance
758 b6135bbc Apollon Oikonomopoulos

759 b6135bbc Apollon Oikonomopoulos
    """
760 ee1478e5 Bernardo Dal Seno
    if excl_stor:
761 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("Persistent block device requested with"
762 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
763 0c3d9c7c Thomas Thrainer
    return PersistentBlockDevice(unique_id, children, 0, params, dyn_params)
764 b6135bbc Apollon Oikonomopoulos
765 b6135bbc Apollon Oikonomopoulos
  def Remove(self):
766 b6135bbc Apollon Oikonomopoulos
    """Remove a device
767 b6135bbc Apollon Oikonomopoulos

768 b6135bbc Apollon Oikonomopoulos
    This is a noop
769 b6135bbc Apollon Oikonomopoulos

770 b6135bbc Apollon Oikonomopoulos
    """
771 b6135bbc Apollon Oikonomopoulos
    pass
772 b6135bbc Apollon Oikonomopoulos
773 b6135bbc Apollon Oikonomopoulos
  def Rename(self, new_id):
774 b6135bbc Apollon Oikonomopoulos
    """Rename this device.
775 b6135bbc Apollon Oikonomopoulos

776 b6135bbc Apollon Oikonomopoulos
    """
777 89ff748d Thomas Thrainer
    base.ThrowError("Rename is not supported for PersistentBlockDev storage")
778 b6135bbc Apollon Oikonomopoulos
779 b6135bbc Apollon Oikonomopoulos
  def Attach(self):
780 b6135bbc Apollon Oikonomopoulos
    """Attach to an existing block device.
781 b6135bbc Apollon Oikonomopoulos

782 b6135bbc Apollon Oikonomopoulos

783 b6135bbc Apollon Oikonomopoulos
    """
784 b6135bbc Apollon Oikonomopoulos
    self.attached = False
785 b6135bbc Apollon Oikonomopoulos
    try:
786 b6135bbc Apollon Oikonomopoulos
      st = os.stat(self.dev_path)
787 b6135bbc Apollon Oikonomopoulos
    except OSError, err:
788 b6135bbc Apollon Oikonomopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
789 b6135bbc Apollon Oikonomopoulos
      return False
790 b6135bbc Apollon Oikonomopoulos
791 b6135bbc Apollon Oikonomopoulos
    if not stat.S_ISBLK(st.st_mode):
792 b6135bbc Apollon Oikonomopoulos
      logging.error("%s is not a block device", self.dev_path)
793 b6135bbc Apollon Oikonomopoulos
      return False
794 b6135bbc Apollon Oikonomopoulos
795 b6135bbc Apollon Oikonomopoulos
    self.major = os.major(st.st_rdev)
796 b6135bbc Apollon Oikonomopoulos
    self.minor = os.minor(st.st_rdev)
797 b6135bbc Apollon Oikonomopoulos
    self.attached = True
798 b6135bbc Apollon Oikonomopoulos
799 b6135bbc Apollon Oikonomopoulos
    return True
800 b6135bbc Apollon Oikonomopoulos
801 b6135bbc Apollon Oikonomopoulos
  def Assemble(self):
802 b6135bbc Apollon Oikonomopoulos
    """Assemble the device.
803 b6135bbc Apollon Oikonomopoulos

804 b6135bbc Apollon Oikonomopoulos
    """
805 b6135bbc Apollon Oikonomopoulos
    pass
806 b6135bbc Apollon Oikonomopoulos
807 b6135bbc Apollon Oikonomopoulos
  def Shutdown(self):
808 b6135bbc Apollon Oikonomopoulos
    """Shutdown the device.
809 b6135bbc Apollon Oikonomopoulos

810 b6135bbc Apollon Oikonomopoulos
    """
811 b6135bbc Apollon Oikonomopoulos
    pass
812 b6135bbc Apollon Oikonomopoulos
813 b6135bbc Apollon Oikonomopoulos
  def Open(self, force=False):
814 b6135bbc Apollon Oikonomopoulos
    """Make the device ready for I/O.
815 b6135bbc Apollon Oikonomopoulos

816 b6135bbc Apollon Oikonomopoulos
    """
817 b6135bbc Apollon Oikonomopoulos
    pass
818 b6135bbc Apollon Oikonomopoulos
819 b6135bbc Apollon Oikonomopoulos
  def Close(self):
820 b6135bbc Apollon Oikonomopoulos
    """Notifies that the device will no longer be used for I/O.
821 b6135bbc Apollon Oikonomopoulos

822 b6135bbc Apollon Oikonomopoulos
    """
823 b6135bbc Apollon Oikonomopoulos
    pass
824 b6135bbc Apollon Oikonomopoulos
825 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
826 b6135bbc Apollon Oikonomopoulos
    """Grow the logical volume.
827 b6135bbc Apollon Oikonomopoulos

828 b6135bbc Apollon Oikonomopoulos
    """
829 89ff748d Thomas Thrainer
    base.ThrowError("Grow is not supported for PersistentBlockDev storage")
830 b6135bbc Apollon Oikonomopoulos
831 b6135bbc Apollon Oikonomopoulos
832 89ff748d Thomas Thrainer
class RADOSBlockDevice(base.BlockDev):
833 7181fba0 Constantinos Venetsanopoulos
  """A RADOS Block Device (rbd).
834 7181fba0 Constantinos Venetsanopoulos

835 7181fba0 Constantinos Venetsanopoulos
  This class implements the RADOS Block Device for the backend. You need
836 7181fba0 Constantinos Venetsanopoulos
  the rbd kernel driver, the RADOS Tools and a working RADOS cluster for
837 7181fba0 Constantinos Venetsanopoulos
  this to be functional.
838 7181fba0 Constantinos Venetsanopoulos

839 7181fba0 Constantinos Venetsanopoulos
  """
840 0c3d9c7c Thomas Thrainer
  def __init__(self, unique_id, children, size, params, dyn_params):
841 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
842 7181fba0 Constantinos Venetsanopoulos

843 7181fba0 Constantinos Venetsanopoulos
    """
844 0c3d9c7c Thomas Thrainer
    super(RADOSBlockDevice, self).__init__(unique_id, children, size, params,
845 0c3d9c7c Thomas Thrainer
                                           dyn_params)
846 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
847 7181fba0 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
848 7181fba0 Constantinos Venetsanopoulos
849 7181fba0 Constantinos Venetsanopoulos
    self.driver, self.rbd_name = unique_id
850 f83057ad Raffa Santi
    self.rbd_pool = params[constants.LDP_POOL]
851 7181fba0 Constantinos Venetsanopoulos
852 7181fba0 Constantinos Venetsanopoulos
    self.major = self.minor = None
853 7181fba0 Constantinos Venetsanopoulos
    self.Attach()
854 7181fba0 Constantinos Venetsanopoulos
855 7181fba0 Constantinos Venetsanopoulos
  @classmethod
856 0c3d9c7c Thomas Thrainer
  def Create(cls, unique_id, children, size, spindles, params, excl_stor,
857 0c3d9c7c Thomas Thrainer
             dyn_params):
858 7181fba0 Constantinos Venetsanopoulos
    """Create a new rbd device.
859 7181fba0 Constantinos Venetsanopoulos

860 7181fba0 Constantinos Venetsanopoulos
    Provision a new rbd volume inside a RADOS pool.
861 7181fba0 Constantinos Venetsanopoulos

862 7181fba0 Constantinos Venetsanopoulos
    """
863 7181fba0 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
864 7181fba0 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
865 7181fba0 Constantinos Venetsanopoulos
                                   str(unique_id))
866 ee1478e5 Bernardo Dal Seno
    if excl_stor:
867 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("RBD device requested with"
868 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
869 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = params[constants.LDP_POOL]
870 7181fba0 Constantinos Venetsanopoulos
    rbd_name = unique_id[1]
871 7181fba0 Constantinos Venetsanopoulos
872 7181fba0 Constantinos Venetsanopoulos
    # Provision a new rbd volume (Image) inside the RADOS cluster.
873 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "create", "-p", rbd_pool,
874 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % size]
875 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
876 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
877 89ff748d Thomas Thrainer
      base.ThrowError("rbd creation failed (%s): %s",
878 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
879 7181fba0 Constantinos Venetsanopoulos
880 0c3d9c7c Thomas Thrainer
    return RADOSBlockDevice(unique_id, children, size, params, dyn_params)
881 7181fba0 Constantinos Venetsanopoulos
882 7181fba0 Constantinos Venetsanopoulos
  def Remove(self):
883 7181fba0 Constantinos Venetsanopoulos
    """Remove the rbd device.
884 7181fba0 Constantinos Venetsanopoulos

885 7181fba0 Constantinos Venetsanopoulos
    """
886 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
887 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
888 7181fba0 Constantinos Venetsanopoulos
889 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
890 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
891 7181fba0 Constantinos Venetsanopoulos
      return
892 7181fba0 Constantinos Venetsanopoulos
893 7181fba0 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
894 7181fba0 Constantinos Venetsanopoulos
    self.Shutdown()
895 7181fba0 Constantinos Venetsanopoulos
896 7181fba0 Constantinos Venetsanopoulos
    # Remove the actual Volume (Image) from the RADOS cluster.
897 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "rm", "-p", rbd_pool, rbd_name]
898 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
899 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
900 89ff748d Thomas Thrainer
      base.ThrowError("Can't remove Volume from cluster with rbd rm: %s - %s",
901 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
902 7181fba0 Constantinos Venetsanopoulos
903 7181fba0 Constantinos Venetsanopoulos
  def Rename(self, new_id):
904 7181fba0 Constantinos Venetsanopoulos
    """Rename this device.
905 7181fba0 Constantinos Venetsanopoulos

906 7181fba0 Constantinos Venetsanopoulos
    """
907 7181fba0 Constantinos Venetsanopoulos
    pass
908 7181fba0 Constantinos Venetsanopoulos
909 7181fba0 Constantinos Venetsanopoulos
  def Attach(self):
910 7181fba0 Constantinos Venetsanopoulos
    """Attach to an existing rbd device.
911 7181fba0 Constantinos Venetsanopoulos

912 7181fba0 Constantinos Venetsanopoulos
    This method maps the rbd volume that matches our name with
913 7181fba0 Constantinos Venetsanopoulos
    an rbd device and then attaches to this device.
914 7181fba0 Constantinos Venetsanopoulos

915 7181fba0 Constantinos Venetsanopoulos
    """
916 7181fba0 Constantinos Venetsanopoulos
    self.attached = False
917 7181fba0 Constantinos Venetsanopoulos
918 7181fba0 Constantinos Venetsanopoulos
    # Map the rbd volume to a block device under /dev
919 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = self._MapVolumeToBlockdev(self.unique_id)
920 7181fba0 Constantinos Venetsanopoulos
921 7181fba0 Constantinos Venetsanopoulos
    try:
922 7181fba0 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
923 7181fba0 Constantinos Venetsanopoulos
    except OSError, err:
924 7181fba0 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
925 7181fba0 Constantinos Venetsanopoulos
      return False
926 7181fba0 Constantinos Venetsanopoulos
927 7181fba0 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
928 7181fba0 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
929 7181fba0 Constantinos Venetsanopoulos
      return False
930 7181fba0 Constantinos Venetsanopoulos
931 7181fba0 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
932 7181fba0 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
933 7181fba0 Constantinos Venetsanopoulos
    self.attached = True
934 7181fba0 Constantinos Venetsanopoulos
935 7181fba0 Constantinos Venetsanopoulos
    return True
936 7181fba0 Constantinos Venetsanopoulos
937 7181fba0 Constantinos Venetsanopoulos
  def _MapVolumeToBlockdev(self, unique_id):
938 7181fba0 Constantinos Venetsanopoulos
    """Maps existing rbd volumes to block devices.
939 7181fba0 Constantinos Venetsanopoulos

940 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
941 7181fba0 Constantinos Venetsanopoulos

942 7181fba0 Constantinos Venetsanopoulos
    @rtype: string
943 7181fba0 Constantinos Venetsanopoulos
    @return: the block device path that corresponds to the volume
944 7181fba0 Constantinos Venetsanopoulos

945 7181fba0 Constantinos Venetsanopoulos
    """
946 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
947 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
948 7181fba0 Constantinos Venetsanopoulos
949 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
950 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
951 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
952 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Return it.
953 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
954 7181fba0 Constantinos Venetsanopoulos
955 7181fba0 Constantinos Venetsanopoulos
    # The mapping doesn't exist. Create it.
956 7181fba0 Constantinos Venetsanopoulos
    map_cmd = [constants.RBD_CMD, "map", "-p", pool, name]
957 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(map_cmd)
958 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
959 89ff748d Thomas Thrainer
      base.ThrowError("rbd map failed (%s): %s",
960 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
961 7181fba0 Constantinos Venetsanopoulos
962 7181fba0 Constantinos Venetsanopoulos
    # Find the corresponding rbd device.
963 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
964 7181fba0 Constantinos Venetsanopoulos
    if not rbd_dev:
965 89ff748d Thomas Thrainer
      base.ThrowError("rbd map succeeded, but could not find the rbd block"
966 89ff748d Thomas Thrainer
                      " device in output of showmapped, for volume: %s", name)
967 7181fba0 Constantinos Venetsanopoulos
968 7181fba0 Constantinos Venetsanopoulos
    # The device was successfully mapped. Return it.
969 7181fba0 Constantinos Venetsanopoulos
    return rbd_dev
970 7181fba0 Constantinos Venetsanopoulos
971 bdecfea2 Stratos Psomadakis
  @classmethod
972 bdecfea2 Stratos Psomadakis
  def _VolumeToBlockdev(cls, pool, volume_name):
973 bdecfea2 Stratos Psomadakis
    """Do the 'volume name'-to-'rbd block device' resolving.
974 bdecfea2 Stratos Psomadakis

975 bdecfea2 Stratos Psomadakis
    @type pool: string
976 bdecfea2 Stratos Psomadakis
    @param pool: RADOS pool to use
977 bdecfea2 Stratos Psomadakis
    @type volume_name: string
978 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
979 bdecfea2 Stratos Psomadakis
    @rtype: string or None
980 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
981 bdecfea2 Stratos Psomadakis

982 bdecfea2 Stratos Psomadakis
    """
983 bdecfea2 Stratos Psomadakis
    try:
984 bdecfea2 Stratos Psomadakis
      # Newer versions of the rbd tool support json output formatting. Use it
985 bdecfea2 Stratos Psomadakis
      # if available.
986 bdecfea2 Stratos Psomadakis
      showmap_cmd = [
987 bdecfea2 Stratos Psomadakis
        constants.RBD_CMD,
988 bdecfea2 Stratos Psomadakis
        "showmapped",
989 bdecfea2 Stratos Psomadakis
        "-p",
990 bdecfea2 Stratos Psomadakis
        pool,
991 bdecfea2 Stratos Psomadakis
        "--format",
992 bdecfea2 Stratos Psomadakis
        "json"
993 bdecfea2 Stratos Psomadakis
        ]
994 bdecfea2 Stratos Psomadakis
      result = utils.RunCmd(showmap_cmd)
995 bdecfea2 Stratos Psomadakis
      if result.failed:
996 bdecfea2 Stratos Psomadakis
        logging.error("rbd JSON output formatting returned error (%s): %s,"
997 bdecfea2 Stratos Psomadakis
                      "falling back to plain output parsing",
998 bdecfea2 Stratos Psomadakis
                      result.fail_reason, result.output)
999 bdecfea2 Stratos Psomadakis
        raise RbdShowmappedJsonError
1000 bdecfea2 Stratos Psomadakis
1001 bdecfea2 Stratos Psomadakis
      return cls._ParseRbdShowmappedJson(result.output, volume_name)
1002 bdecfea2 Stratos Psomadakis
    except RbdShowmappedJsonError:
1003 bdecfea2 Stratos Psomadakis
      # For older versions of rbd, we have to parse the plain / text output
1004 bdecfea2 Stratos Psomadakis
      # manually.
1005 bdecfea2 Stratos Psomadakis
      showmap_cmd = [constants.RBD_CMD, "showmapped", "-p", pool]
1006 bdecfea2 Stratos Psomadakis
      result = utils.RunCmd(showmap_cmd)
1007 bdecfea2 Stratos Psomadakis
      if result.failed:
1008 89ff748d Thomas Thrainer
        base.ThrowError("rbd showmapped failed (%s): %s",
1009 89ff748d Thomas Thrainer
                        result.fail_reason, result.output)
1010 bdecfea2 Stratos Psomadakis
1011 bdecfea2 Stratos Psomadakis
      return cls._ParseRbdShowmappedPlain(result.output, volume_name)
1012 bdecfea2 Stratos Psomadakis
1013 bdecfea2 Stratos Psomadakis
  @staticmethod
1014 bdecfea2 Stratos Psomadakis
  def _ParseRbdShowmappedJson(output, volume_name):
1015 bdecfea2 Stratos Psomadakis
    """Parse the json output of `rbd showmapped'.
1016 bdecfea2 Stratos Psomadakis

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

1020 bdecfea2 Stratos Psomadakis
    @type output: string
1021 bdecfea2 Stratos Psomadakis
    @param output: the json output of `rbd showmapped'
1022 bdecfea2 Stratos Psomadakis
    @type volume_name: string
1023 bdecfea2 Stratos Psomadakis
    @param volume_name: the name of the volume whose device we search for
1024 bdecfea2 Stratos Psomadakis
    @rtype: string or None
1025 bdecfea2 Stratos Psomadakis
    @return: block device path if the volume is mapped, else None
1026 bdecfea2 Stratos Psomadakis

1027 bdecfea2 Stratos Psomadakis
    """
1028 bdecfea2 Stratos Psomadakis
    try:
1029 bdecfea2 Stratos Psomadakis
      devices = serializer.LoadJson(output)
1030 bdecfea2 Stratos Psomadakis
    except ValueError, err:
1031 89ff748d Thomas Thrainer
      base.ThrowError("Unable to parse JSON data: %s" % err)
1032 bdecfea2 Stratos Psomadakis
1033 bdecfea2 Stratos Psomadakis
    rbd_dev = None
1034 bdecfea2 Stratos Psomadakis
    for d in devices.values(): # pylint: disable=E1103
1035 bdecfea2 Stratos Psomadakis
      try:
1036 bdecfea2 Stratos Psomadakis
        name = d["name"]
1037 bdecfea2 Stratos Psomadakis
      except KeyError:
1038 89ff748d Thomas Thrainer
        base.ThrowError("'name' key missing from json object %s", devices)
1039 bdecfea2 Stratos Psomadakis
1040 bdecfea2 Stratos Psomadakis
      if name == volume_name:
1041 bdecfea2 Stratos Psomadakis
        if rbd_dev is not None:
1042 89ff748d Thomas Thrainer
          base.ThrowError("rbd volume %s is mapped more than once", volume_name)
1043 bdecfea2 Stratos Psomadakis
1044 bdecfea2 Stratos Psomadakis
        rbd_dev = d["device"]
1045 bdecfea2 Stratos Psomadakis
1046 bdecfea2 Stratos Psomadakis
    return rbd_dev
1047 bdecfea2 Stratos Psomadakis
1048 7181fba0 Constantinos Venetsanopoulos
  @staticmethod
1049 bdecfea2 Stratos Psomadakis
  def _ParseRbdShowmappedPlain(output, volume_name):
1050 bdecfea2 Stratos Psomadakis
    """Parse the (plain / text) output of `rbd showmapped'.
1051 7181fba0 Constantinos Venetsanopoulos

1052 7181fba0 Constantinos Venetsanopoulos
    This method parses the output of `rbd showmapped' and returns
1053 7181fba0 Constantinos Venetsanopoulos
    the rbd block device path (e.g. /dev/rbd0) that matches the
1054 7181fba0 Constantinos Venetsanopoulos
    given rbd volume.
1055 7181fba0 Constantinos Venetsanopoulos

1056 7181fba0 Constantinos Venetsanopoulos
    @type output: string
1057 bdecfea2 Stratos Psomadakis
    @param output: the plain text output of `rbd showmapped'
1058 7181fba0 Constantinos Venetsanopoulos
    @type volume_name: string
1059 7181fba0 Constantinos Venetsanopoulos
    @param volume_name: the name of the volume whose device we search for
1060 7181fba0 Constantinos Venetsanopoulos
    @rtype: string or None
1061 7181fba0 Constantinos Venetsanopoulos
    @return: block device path if the volume is mapped, else None
1062 7181fba0 Constantinos Venetsanopoulos

1063 7181fba0 Constantinos Venetsanopoulos
    """
1064 7181fba0 Constantinos Venetsanopoulos
    allfields = 5
1065 7181fba0 Constantinos Venetsanopoulos
    volumefield = 2
1066 7181fba0 Constantinos Venetsanopoulos
    devicefield = 4
1067 7181fba0 Constantinos Venetsanopoulos
1068 7181fba0 Constantinos Venetsanopoulos
    lines = output.splitlines()
1069 7181fba0 Constantinos Venetsanopoulos
1070 bdecfea2 Stratos Psomadakis
    # Try parsing the new output format (ceph >= 0.55).
1071 bdecfea2 Stratos Psomadakis
    splitted_lines = map(lambda l: l.split(), lines)
1072 bdecfea2 Stratos Psomadakis
1073 bdecfea2 Stratos Psomadakis
    # Check for empty output.
1074 7181fba0 Constantinos Venetsanopoulos
    if not splitted_lines:
1075 bdecfea2 Stratos Psomadakis
      return None
1076 7181fba0 Constantinos Venetsanopoulos
1077 bdecfea2 Stratos Psomadakis
    # Check showmapped output, to determine number of fields.
1078 7181fba0 Constantinos Venetsanopoulos
    field_cnt = len(splitted_lines[0])
1079 7181fba0 Constantinos Venetsanopoulos
    if field_cnt != allfields:
1080 bdecfea2 Stratos Psomadakis
      # Parsing the new format failed. Fallback to parsing the old output
1081 bdecfea2 Stratos Psomadakis
      # format (< 0.55).
1082 bdecfea2 Stratos Psomadakis
      splitted_lines = map(lambda l: l.split("\t"), lines)
1083 bdecfea2 Stratos Psomadakis
      if field_cnt != allfields:
1084 89ff748d Thomas Thrainer
        base.ThrowError("Cannot parse rbd showmapped output expected %s fields,"
1085 89ff748d Thomas Thrainer
                        " found %s", allfields, field_cnt)
1086 7181fba0 Constantinos Venetsanopoulos
1087 7181fba0 Constantinos Venetsanopoulos
    matched_lines = \
1088 7181fba0 Constantinos Venetsanopoulos
      filter(lambda l: len(l) == allfields and l[volumefield] == volume_name,
1089 7181fba0 Constantinos Venetsanopoulos
             splitted_lines)
1090 7181fba0 Constantinos Venetsanopoulos
1091 7181fba0 Constantinos Venetsanopoulos
    if len(matched_lines) > 1:
1092 89ff748d Thomas Thrainer
      base.ThrowError("rbd volume %s mapped more than once", volume_name)
1093 7181fba0 Constantinos Venetsanopoulos
1094 7181fba0 Constantinos Venetsanopoulos
    if matched_lines:
1095 7181fba0 Constantinos Venetsanopoulos
      # rbd block device found. Return it.
1096 7181fba0 Constantinos Venetsanopoulos
      rbd_dev = matched_lines[0][devicefield]
1097 7181fba0 Constantinos Venetsanopoulos
      return rbd_dev
1098 7181fba0 Constantinos Venetsanopoulos
1099 7181fba0 Constantinos Venetsanopoulos
    # The given volume is not mapped.
1100 7181fba0 Constantinos Venetsanopoulos
    return None
1101 7181fba0 Constantinos Venetsanopoulos
1102 7181fba0 Constantinos Venetsanopoulos
  def Assemble(self):
1103 7181fba0 Constantinos Venetsanopoulos
    """Assemble the device.
1104 7181fba0 Constantinos Venetsanopoulos

1105 7181fba0 Constantinos Venetsanopoulos
    """
1106 7181fba0 Constantinos Venetsanopoulos
    pass
1107 7181fba0 Constantinos Venetsanopoulos
1108 7181fba0 Constantinos Venetsanopoulos
  def Shutdown(self):
1109 7181fba0 Constantinos Venetsanopoulos
    """Shutdown the device.
1110 7181fba0 Constantinos Venetsanopoulos

1111 7181fba0 Constantinos Venetsanopoulos
    """
1112 7181fba0 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1113 7181fba0 Constantinos Venetsanopoulos
      # The rbd device doesn't exist.
1114 7181fba0 Constantinos Venetsanopoulos
      return
1115 7181fba0 Constantinos Venetsanopoulos
1116 7181fba0 Constantinos Venetsanopoulos
    # Unmap the block device from the Volume.
1117 7181fba0 Constantinos Venetsanopoulos
    self._UnmapVolumeFromBlockdev(self.unique_id)
1118 7181fba0 Constantinos Venetsanopoulos
1119 7181fba0 Constantinos Venetsanopoulos
    self.minor = None
1120 7181fba0 Constantinos Venetsanopoulos
    self.dev_path = None
1121 7181fba0 Constantinos Venetsanopoulos
1122 7181fba0 Constantinos Venetsanopoulos
  def _UnmapVolumeFromBlockdev(self, unique_id):
1123 7181fba0 Constantinos Venetsanopoulos
    """Unmaps the rbd device from the Volume it is mapped.
1124 7181fba0 Constantinos Venetsanopoulos

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

1128 7181fba0 Constantinos Venetsanopoulos
    """
1129 7181fba0 Constantinos Venetsanopoulos
    pool = self.params[constants.LDP_POOL]
1130 7181fba0 Constantinos Venetsanopoulos
    name = unique_id[1]
1131 7181fba0 Constantinos Venetsanopoulos
1132 7181fba0 Constantinos Venetsanopoulos
    # Check if the mapping already exists.
1133 bdecfea2 Stratos Psomadakis
    rbd_dev = self._VolumeToBlockdev(pool, name)
1134 7181fba0 Constantinos Venetsanopoulos
1135 7181fba0 Constantinos Venetsanopoulos
    if rbd_dev:
1136 7181fba0 Constantinos Venetsanopoulos
      # The mapping exists. Unmap the rbd device.
1137 7181fba0 Constantinos Venetsanopoulos
      unmap_cmd = [constants.RBD_CMD, "unmap", "%s" % rbd_dev]
1138 7181fba0 Constantinos Venetsanopoulos
      result = utils.RunCmd(unmap_cmd)
1139 7181fba0 Constantinos Venetsanopoulos
      if result.failed:
1140 89ff748d Thomas Thrainer
        base.ThrowError("rbd unmap failed (%s): %s",
1141 89ff748d Thomas Thrainer
                        result.fail_reason, result.output)
1142 7181fba0 Constantinos Venetsanopoulos
1143 7181fba0 Constantinos Venetsanopoulos
  def Open(self, force=False):
1144 7181fba0 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
1145 7181fba0 Constantinos Venetsanopoulos

1146 7181fba0 Constantinos Venetsanopoulos
    """
1147 7181fba0 Constantinos Venetsanopoulos
    pass
1148 7181fba0 Constantinos Venetsanopoulos
1149 7181fba0 Constantinos Venetsanopoulos
  def Close(self):
1150 7181fba0 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1151 7181fba0 Constantinos Venetsanopoulos

1152 7181fba0 Constantinos Venetsanopoulos
    """
1153 7181fba0 Constantinos Venetsanopoulos
    pass
1154 7181fba0 Constantinos Venetsanopoulos
1155 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
1156 7181fba0 Constantinos Venetsanopoulos
    """Grow the Volume.
1157 7181fba0 Constantinos Venetsanopoulos

1158 7181fba0 Constantinos Venetsanopoulos
    @type amount: integer
1159 7181fba0 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1160 7181fba0 Constantinos Venetsanopoulos
    @type dryrun: boolean
1161 7181fba0 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1162 7181fba0 Constantinos Venetsanopoulos
        only, without actually increasing the size
1163 7181fba0 Constantinos Venetsanopoulos

1164 7181fba0 Constantinos Venetsanopoulos
    """
1165 cad0723b Iustin Pop
    if not backingstore:
1166 cad0723b Iustin Pop
      return
1167 7181fba0 Constantinos Venetsanopoulos
    if not self.Attach():
1168 89ff748d Thomas Thrainer
      base.ThrowError("Can't attach to rbd device during Grow()")
1169 7181fba0 Constantinos Venetsanopoulos
1170 7181fba0 Constantinos Venetsanopoulos
    if dryrun:
1171 7181fba0 Constantinos Venetsanopoulos
      # the rbd tool does not support dry runs of resize operations.
1172 7181fba0 Constantinos Venetsanopoulos
      # Since rbd volumes are thinly provisioned, we assume
1173 7181fba0 Constantinos Venetsanopoulos
      # there is always enough free space for the operation.
1174 7181fba0 Constantinos Venetsanopoulos
      return
1175 7181fba0 Constantinos Venetsanopoulos
1176 7181fba0 Constantinos Venetsanopoulos
    rbd_pool = self.params[constants.LDP_POOL]
1177 7181fba0 Constantinos Venetsanopoulos
    rbd_name = self.unique_id[1]
1178 7181fba0 Constantinos Venetsanopoulos
    new_size = self.size + amount
1179 7181fba0 Constantinos Venetsanopoulos
1180 7181fba0 Constantinos Venetsanopoulos
    # Resize the rbd volume (Image) inside the RADOS cluster.
1181 7181fba0 Constantinos Venetsanopoulos
    cmd = [constants.RBD_CMD, "resize", "-p", rbd_pool,
1182 7181fba0 Constantinos Venetsanopoulos
           rbd_name, "--size", "%s" % new_size]
1183 7181fba0 Constantinos Venetsanopoulos
    result = utils.RunCmd(cmd)
1184 7181fba0 Constantinos Venetsanopoulos
    if result.failed:
1185 89ff748d Thomas Thrainer
      base.ThrowError("rbd resize failed (%s): %s",
1186 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
1187 7181fba0 Constantinos Venetsanopoulos
1188 f83057ad Raffa Santi
  def GetUserspaceAccessUri(self, hypervisor):
1189 f83057ad Raffa Santi
    """Generate KVM userspace URIs to be used as `-drive file` settings.
1190 f83057ad Raffa Santi

1191 f83057ad Raffa Santi
    @see: L{BlockDev.GetUserspaceAccessUri}
1192 f83057ad Raffa Santi

1193 f83057ad Raffa Santi
    """
1194 f83057ad Raffa Santi
    if hypervisor == constants.HT_KVM:
1195 f83057ad Raffa Santi
      return "rbd:" + self.rbd_pool + "/" + self.rbd_name
1196 f83057ad Raffa Santi
    else:
1197 f83057ad Raffa Santi
      base.ThrowError("Hypervisor %s doesn't support RBD userspace access" %
1198 f83057ad Raffa Santi
                      hypervisor)
1199 f83057ad Raffa Santi
1200 7181fba0 Constantinos Venetsanopoulos
1201 89ff748d Thomas Thrainer
class ExtStorageDevice(base.BlockDev):
1202 376631d1 Constantinos Venetsanopoulos
  """A block device provided by an ExtStorage Provider.
1203 376631d1 Constantinos Venetsanopoulos

1204 376631d1 Constantinos Venetsanopoulos
  This class implements the External Storage Interface, which means
1205 376631d1 Constantinos Venetsanopoulos
  handling of the externally provided block devices.
1206 376631d1 Constantinos Venetsanopoulos

1207 376631d1 Constantinos Venetsanopoulos
  """
1208 0c3d9c7c Thomas Thrainer
  def __init__(self, unique_id, children, size, params, dyn_params):
1209 376631d1 Constantinos Venetsanopoulos
    """Attaches to an extstorage block device.
1210 376631d1 Constantinos Venetsanopoulos

1211 376631d1 Constantinos Venetsanopoulos
    """
1212 0c3d9c7c Thomas Thrainer
    super(ExtStorageDevice, self).__init__(unique_id, children, size, params,
1213 0c3d9c7c Thomas Thrainer
                                           dyn_params)
1214 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1215 376631d1 Constantinos Venetsanopoulos
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1216 376631d1 Constantinos Venetsanopoulos
1217 376631d1 Constantinos Venetsanopoulos
    self.driver, self.vol_name = unique_id
1218 938adc87 Constantinos Venetsanopoulos
    self.ext_params = params
1219 376631d1 Constantinos Venetsanopoulos
1220 376631d1 Constantinos Venetsanopoulos
    self.major = self.minor = None
1221 376631d1 Constantinos Venetsanopoulos
    self.Attach()
1222 376631d1 Constantinos Venetsanopoulos
1223 376631d1 Constantinos Venetsanopoulos
  @classmethod
1224 0c3d9c7c Thomas Thrainer
  def Create(cls, unique_id, children, size, spindles, params, excl_stor,
1225 0c3d9c7c Thomas Thrainer
             dyn_params):
1226 376631d1 Constantinos Venetsanopoulos
    """Create a new extstorage device.
1227 376631d1 Constantinos Venetsanopoulos

1228 376631d1 Constantinos Venetsanopoulos
    Provision a new volume using an extstorage provider, which will
1229 376631d1 Constantinos Venetsanopoulos
    then be mapped to a block device.
1230 376631d1 Constantinos Venetsanopoulos

1231 376631d1 Constantinos Venetsanopoulos
    """
1232 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1233 376631d1 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
1234 376631d1 Constantinos Venetsanopoulos
                                   str(unique_id))
1235 ee1478e5 Bernardo Dal Seno
    if excl_stor:
1236 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("extstorage device requested with"
1237 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
1238 376631d1 Constantinos Venetsanopoulos
1239 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's create script,
1240 376631d1 Constantinos Venetsanopoulos
    # to provision a new Volume inside the External Storage
1241 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id,
1242 938adc87 Constantinos Venetsanopoulos
                      params, str(size))
1243 376631d1 Constantinos Venetsanopoulos
1244 0c3d9c7c Thomas Thrainer
    return ExtStorageDevice(unique_id, children, size, params, dyn_params)
1245 376631d1 Constantinos Venetsanopoulos
1246 376631d1 Constantinos Venetsanopoulos
  def Remove(self):
1247 376631d1 Constantinos Venetsanopoulos
    """Remove the extstorage device.
1248 376631d1 Constantinos Venetsanopoulos

1249 376631d1 Constantinos Venetsanopoulos
    """
1250 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1251 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1252 376631d1 Constantinos Venetsanopoulos
      return
1253 376631d1 Constantinos Venetsanopoulos
1254 376631d1 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
1255 376631d1 Constantinos Venetsanopoulos
    self.Shutdown()
1256 376631d1 Constantinos Venetsanopoulos
1257 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's remove script,
1258 376631d1 Constantinos Venetsanopoulos
    # to remove the Volume from the External Storage
1259 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id,
1260 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
1261 376631d1 Constantinos Venetsanopoulos
1262 376631d1 Constantinos Venetsanopoulos
  def Rename(self, new_id):
1263 376631d1 Constantinos Venetsanopoulos
    """Rename this device.
1264 376631d1 Constantinos Venetsanopoulos

1265 376631d1 Constantinos Venetsanopoulos
    """
1266 376631d1 Constantinos Venetsanopoulos
    pass
1267 376631d1 Constantinos Venetsanopoulos
1268 376631d1 Constantinos Venetsanopoulos
  def Attach(self):
1269 376631d1 Constantinos Venetsanopoulos
    """Attach to an existing extstorage device.
1270 376631d1 Constantinos Venetsanopoulos

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

1274 376631d1 Constantinos Venetsanopoulos
    """
1275 376631d1 Constantinos Venetsanopoulos
    self.attached = False
1276 376631d1 Constantinos Venetsanopoulos
1277 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's attach script,
1278 376631d1 Constantinos Venetsanopoulos
    # to attach an existing Volume to a block device under /dev
1279 376631d1 Constantinos Venetsanopoulos
    self.dev_path = _ExtStorageAction(constants.ES_ACTION_ATTACH,
1280 938adc87 Constantinos Venetsanopoulos
                                      self.unique_id, self.ext_params)
1281 376631d1 Constantinos Venetsanopoulos
1282 376631d1 Constantinos Venetsanopoulos
    try:
1283 376631d1 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
1284 376631d1 Constantinos Venetsanopoulos
    except OSError, err:
1285 376631d1 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
1286 376631d1 Constantinos Venetsanopoulos
      return False
1287 376631d1 Constantinos Venetsanopoulos
1288 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
1289 376631d1 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
1290 376631d1 Constantinos Venetsanopoulos
      return False
1291 376631d1 Constantinos Venetsanopoulos
1292 376631d1 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
1293 376631d1 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
1294 376631d1 Constantinos Venetsanopoulos
    self.attached = True
1295 376631d1 Constantinos Venetsanopoulos
1296 376631d1 Constantinos Venetsanopoulos
    return True
1297 376631d1 Constantinos Venetsanopoulos
1298 376631d1 Constantinos Venetsanopoulos
  def Assemble(self):
1299 376631d1 Constantinos Venetsanopoulos
    """Assemble the device.
1300 376631d1 Constantinos Venetsanopoulos

1301 376631d1 Constantinos Venetsanopoulos
    """
1302 376631d1 Constantinos Venetsanopoulos
    pass
1303 376631d1 Constantinos Venetsanopoulos
1304 376631d1 Constantinos Venetsanopoulos
  def Shutdown(self):
1305 376631d1 Constantinos Venetsanopoulos
    """Shutdown the device.
1306 376631d1 Constantinos Venetsanopoulos

1307 376631d1 Constantinos Venetsanopoulos
    """
1308 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1309 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1310 376631d1 Constantinos Venetsanopoulos
      return
1311 376631d1 Constantinos Venetsanopoulos
1312 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's detach script,
1313 376631d1 Constantinos Venetsanopoulos
    # to detach an existing Volume from it's block device under /dev
1314 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id,
1315 938adc87 Constantinos Venetsanopoulos
                      self.ext_params)
1316 376631d1 Constantinos Venetsanopoulos
1317 376631d1 Constantinos Venetsanopoulos
    self.minor = None
1318 376631d1 Constantinos Venetsanopoulos
    self.dev_path = None
1319 376631d1 Constantinos Venetsanopoulos
1320 376631d1 Constantinos Venetsanopoulos
  def Open(self, force=False):
1321 376631d1 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
1322 376631d1 Constantinos Venetsanopoulos

1323 376631d1 Constantinos Venetsanopoulos
    """
1324 376631d1 Constantinos Venetsanopoulos
    pass
1325 376631d1 Constantinos Venetsanopoulos
1326 376631d1 Constantinos Venetsanopoulos
  def Close(self):
1327 376631d1 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1328 376631d1 Constantinos Venetsanopoulos

1329 376631d1 Constantinos Venetsanopoulos
    """
1330 376631d1 Constantinos Venetsanopoulos
    pass
1331 376631d1 Constantinos Venetsanopoulos
1332 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
1333 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
1334 376631d1 Constantinos Venetsanopoulos

1335 376631d1 Constantinos Venetsanopoulos
    @type amount: integer
1336 376631d1 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1337 376631d1 Constantinos Venetsanopoulos
    @type dryrun: boolean
1338 376631d1 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1339 376631d1 Constantinos Venetsanopoulos
        only, without actually increasing the size
1340 376631d1 Constantinos Venetsanopoulos

1341 376631d1 Constantinos Venetsanopoulos
    """
1342 376631d1 Constantinos Venetsanopoulos
    if not backingstore:
1343 376631d1 Constantinos Venetsanopoulos
      return
1344 376631d1 Constantinos Venetsanopoulos
    if not self.Attach():
1345 89ff748d Thomas Thrainer
      base.ThrowError("Can't attach to extstorage device during Grow()")
1346 376631d1 Constantinos Venetsanopoulos
1347 376631d1 Constantinos Venetsanopoulos
    if dryrun:
1348 376631d1 Constantinos Venetsanopoulos
      # we do not support dry runs of resize operations for now.
1349 376631d1 Constantinos Venetsanopoulos
      return
1350 376631d1 Constantinos Venetsanopoulos
1351 376631d1 Constantinos Venetsanopoulos
    new_size = self.size + amount
1352 376631d1 Constantinos Venetsanopoulos
1353 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's grow script,
1354 376631d1 Constantinos Venetsanopoulos
    # to grow an existing Volume inside the External Storage
1355 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_GROW, self.unique_id,
1356 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, str(self.size), grow=str(new_size))
1357 376631d1 Constantinos Venetsanopoulos
1358 376631d1 Constantinos Venetsanopoulos
  def SetInfo(self, text):
1359 376631d1 Constantinos Venetsanopoulos
    """Update metadata with info text.
1360 376631d1 Constantinos Venetsanopoulos

1361 376631d1 Constantinos Venetsanopoulos
    """
1362 376631d1 Constantinos Venetsanopoulos
    # Replace invalid characters
1363 376631d1 Constantinos Venetsanopoulos
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
1364 376631d1 Constantinos Venetsanopoulos
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
1365 376631d1 Constantinos Venetsanopoulos
1366 376631d1 Constantinos Venetsanopoulos
    # Only up to 128 characters are allowed
1367 376631d1 Constantinos Venetsanopoulos
    text = text[:128]
1368 376631d1 Constantinos Venetsanopoulos
1369 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's setinfo script,
1370 376631d1 Constantinos Venetsanopoulos
    # to set metadata for an existing Volume inside the External Storage
1371 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
1372 938adc87 Constantinos Venetsanopoulos
                      self.ext_params, metadata=text)
1373 376631d1 Constantinos Venetsanopoulos
1374 376631d1 Constantinos Venetsanopoulos
1375 938adc87 Constantinos Venetsanopoulos
def _ExtStorageAction(action, unique_id, ext_params,
1376 938adc87 Constantinos Venetsanopoulos
                      size=None, grow=None, metadata=None):
1377 376631d1 Constantinos Venetsanopoulos
  """Take an External Storage action.
1378 376631d1 Constantinos Venetsanopoulos

1379 376631d1 Constantinos Venetsanopoulos
  Take an External Storage action concerning or affecting
1380 376631d1 Constantinos Venetsanopoulos
  a specific Volume inside the External Storage.
1381 376631d1 Constantinos Venetsanopoulos

1382 376631d1 Constantinos Venetsanopoulos
  @type action: string
1383 376631d1 Constantinos Venetsanopoulos
  @param action: which action to perform. One of:
1384 376631d1 Constantinos Venetsanopoulos
                 create / remove / grow / attach / detach
1385 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1386 376631d1 Constantinos Venetsanopoulos
  @param unique_id: a tuple containing the type of ExtStorage (driver)
1387 376631d1 Constantinos Venetsanopoulos
                    and the Volume name
1388 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1389 938adc87 Constantinos Venetsanopoulos
  @param ext_params: ExtStorage parameters
1390 376631d1 Constantinos Venetsanopoulos
  @type size: integer
1391 376631d1 Constantinos Venetsanopoulos
  @param size: the size of the Volume in mebibytes
1392 376631d1 Constantinos Venetsanopoulos
  @type grow: integer
1393 376631d1 Constantinos Venetsanopoulos
  @param grow: the new size in mebibytes (after grow)
1394 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1395 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume, for use by the provider
1396 376631d1 Constantinos Venetsanopoulos
  @rtype: None or a block device path (during attach)
1397 376631d1 Constantinos Venetsanopoulos

1398 376631d1 Constantinos Venetsanopoulos
  """
1399 376631d1 Constantinos Venetsanopoulos
  driver, vol_name = unique_id
1400 376631d1 Constantinos Venetsanopoulos
1401 376631d1 Constantinos Venetsanopoulos
  # Create an External Storage instance of type `driver'
1402 376631d1 Constantinos Venetsanopoulos
  status, inst_es = ExtStorageFromDisk(driver)
1403 376631d1 Constantinos Venetsanopoulos
  if not status:
1404 89ff748d Thomas Thrainer
    base.ThrowError("%s" % inst_es)
1405 376631d1 Constantinos Venetsanopoulos
1406 376631d1 Constantinos Venetsanopoulos
  # Create the basic environment for the driver's scripts
1407 938adc87 Constantinos Venetsanopoulos
  create_env = _ExtStorageEnvironment(unique_id, ext_params, size,
1408 938adc87 Constantinos Venetsanopoulos
                                      grow, metadata)
1409 376631d1 Constantinos Venetsanopoulos
1410 376631d1 Constantinos Venetsanopoulos
  # Do not use log file for action `attach' as we need
1411 376631d1 Constantinos Venetsanopoulos
  # to get the output from RunResult
1412 376631d1 Constantinos Venetsanopoulos
  # TODO: find a way to have a log file for attach too
1413 376631d1 Constantinos Venetsanopoulos
  logfile = None
1414 376631d1 Constantinos Venetsanopoulos
  if action is not constants.ES_ACTION_ATTACH:
1415 376631d1 Constantinos Venetsanopoulos
    logfile = _VolumeLogName(action, driver, vol_name)
1416 376631d1 Constantinos Venetsanopoulos
1417 376631d1 Constantinos Venetsanopoulos
  # Make sure the given action results in a valid script
1418 376631d1 Constantinos Venetsanopoulos
  if action not in constants.ES_SCRIPTS:
1419 89ff748d Thomas Thrainer
    base.ThrowError("Action '%s' doesn't result in a valid ExtStorage script" %
1420 89ff748d Thomas Thrainer
                    action)
1421 376631d1 Constantinos Venetsanopoulos
1422 376631d1 Constantinos Venetsanopoulos
  # Find out which external script to run according the given action
1423 376631d1 Constantinos Venetsanopoulos
  script_name = action + "_script"
1424 376631d1 Constantinos Venetsanopoulos
  script = getattr(inst_es, script_name)
1425 376631d1 Constantinos Venetsanopoulos
1426 376631d1 Constantinos Venetsanopoulos
  # Run the external script
1427 376631d1 Constantinos Venetsanopoulos
  result = utils.RunCmd([script], env=create_env,
1428 376631d1 Constantinos Venetsanopoulos
                        cwd=inst_es.path, output=logfile,)
1429 376631d1 Constantinos Venetsanopoulos
  if result.failed:
1430 376631d1 Constantinos Venetsanopoulos
    logging.error("External storage's %s command '%s' returned"
1431 376631d1 Constantinos Venetsanopoulos
                  " error: %s, logfile: %s, output: %s",
1432 376631d1 Constantinos Venetsanopoulos
                  action, result.cmd, result.fail_reason,
1433 376631d1 Constantinos Venetsanopoulos
                  logfile, result.output)
1434 376631d1 Constantinos Venetsanopoulos
1435 376631d1 Constantinos Venetsanopoulos
    # If logfile is 'None' (during attach), it breaks TailFile
1436 376631d1 Constantinos Venetsanopoulos
    # TODO: have a log file for attach too
1437 376631d1 Constantinos Venetsanopoulos
    if action is not constants.ES_ACTION_ATTACH:
1438 376631d1 Constantinos Venetsanopoulos
      lines = [utils.SafeEncode(val)
1439 376631d1 Constantinos Venetsanopoulos
               for val in utils.TailFile(logfile, lines=20)]
1440 376631d1 Constantinos Venetsanopoulos
    else:
1441 376631d1 Constantinos Venetsanopoulos
      lines = result.output[-20:]
1442 376631d1 Constantinos Venetsanopoulos
1443 89ff748d Thomas Thrainer
    base.ThrowError("External storage's %s script failed (%s), last"
1444 89ff748d Thomas Thrainer
                    " lines of output:\n%s",
1445 89ff748d Thomas Thrainer
                    action, result.fail_reason, "\n".join(lines))
1446 376631d1 Constantinos Venetsanopoulos
1447 376631d1 Constantinos Venetsanopoulos
  if action == constants.ES_ACTION_ATTACH:
1448 376631d1 Constantinos Venetsanopoulos
    return result.stdout
1449 376631d1 Constantinos Venetsanopoulos
1450 376631d1 Constantinos Venetsanopoulos
1451 376631d1 Constantinos Venetsanopoulos
def ExtStorageFromDisk(name, base_dir=None):
1452 376631d1 Constantinos Venetsanopoulos
  """Create an ExtStorage instance from disk.
1453 376631d1 Constantinos Venetsanopoulos

1454 376631d1 Constantinos Venetsanopoulos
  This function will return an ExtStorage instance
1455 376631d1 Constantinos Venetsanopoulos
  if the given name is a valid ExtStorage name.
1456 376631d1 Constantinos Venetsanopoulos

1457 376631d1 Constantinos Venetsanopoulos
  @type base_dir: string
1458 376631d1 Constantinos Venetsanopoulos
  @keyword base_dir: Base directory containing ExtStorage installations.
1459 376631d1 Constantinos Venetsanopoulos
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
1460 376631d1 Constantinos Venetsanopoulos
  @rtype: tuple
1461 376631d1 Constantinos Venetsanopoulos
  @return: True and the ExtStorage instance if we find a valid one, or
1462 376631d1 Constantinos Venetsanopoulos
      False and the diagnose message on error
1463 376631d1 Constantinos Venetsanopoulos

1464 376631d1 Constantinos Venetsanopoulos
  """
1465 376631d1 Constantinos Venetsanopoulos
  if base_dir is None:
1466 376631d1 Constantinos Venetsanopoulos
    es_base_dir = pathutils.ES_SEARCH_PATH
1467 376631d1 Constantinos Venetsanopoulos
  else:
1468 376631d1 Constantinos Venetsanopoulos
    es_base_dir = [base_dir]
1469 376631d1 Constantinos Venetsanopoulos
1470 376631d1 Constantinos Venetsanopoulos
  es_dir = utils.FindFile(name, es_base_dir, os.path.isdir)
1471 376631d1 Constantinos Venetsanopoulos
1472 376631d1 Constantinos Venetsanopoulos
  if es_dir is None:
1473 376631d1 Constantinos Venetsanopoulos
    return False, ("Directory for External Storage Provider %s not"
1474 376631d1 Constantinos Venetsanopoulos
                   " found in search path" % name)
1475 376631d1 Constantinos Venetsanopoulos
1476 376631d1 Constantinos Venetsanopoulos
  # ES Files dictionary, we will populate it with the absolute path
1477 376631d1 Constantinos Venetsanopoulos
  # names; if the value is True, then it is a required file, otherwise
1478 376631d1 Constantinos Venetsanopoulos
  # an optional one
1479 376631d1 Constantinos Venetsanopoulos
  es_files = dict.fromkeys(constants.ES_SCRIPTS, True)
1480 376631d1 Constantinos Venetsanopoulos
1481 938adc87 Constantinos Venetsanopoulos
  es_files[constants.ES_PARAMETERS_FILE] = True
1482 938adc87 Constantinos Venetsanopoulos
1483 938adc87 Constantinos Venetsanopoulos
  for (filename, _) in es_files.items():
1484 376631d1 Constantinos Venetsanopoulos
    es_files[filename] = utils.PathJoin(es_dir, filename)
1485 376631d1 Constantinos Venetsanopoulos
1486 376631d1 Constantinos Venetsanopoulos
    try:
1487 376631d1 Constantinos Venetsanopoulos
      st = os.stat(es_files[filename])
1488 376631d1 Constantinos Venetsanopoulos
    except EnvironmentError, err:
1489 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is missing (%s)" %
1490 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir, utils.ErrnoOrStr(err)))
1491 376631d1 Constantinos Venetsanopoulos
1492 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
1493 376631d1 Constantinos Venetsanopoulos
      return False, ("File '%s' under path '%s' is not a regular file" %
1494 376631d1 Constantinos Venetsanopoulos
                     (filename, es_dir))
1495 376631d1 Constantinos Venetsanopoulos
1496 376631d1 Constantinos Venetsanopoulos
    if filename in constants.ES_SCRIPTS:
1497 376631d1 Constantinos Venetsanopoulos
      if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
1498 376631d1 Constantinos Venetsanopoulos
        return False, ("File '%s' under path '%s' is not executable" %
1499 376631d1 Constantinos Venetsanopoulos
                       (filename, es_dir))
1500 376631d1 Constantinos Venetsanopoulos
1501 938adc87 Constantinos Venetsanopoulos
  parameters = []
1502 938adc87 Constantinos Venetsanopoulos
  if constants.ES_PARAMETERS_FILE in es_files:
1503 938adc87 Constantinos Venetsanopoulos
    parameters_file = es_files[constants.ES_PARAMETERS_FILE]
1504 938adc87 Constantinos Venetsanopoulos
    try:
1505 938adc87 Constantinos Venetsanopoulos
      parameters = utils.ReadFile(parameters_file).splitlines()
1506 938adc87 Constantinos Venetsanopoulos
    except EnvironmentError, err:
1507 938adc87 Constantinos Venetsanopoulos
      return False, ("Error while reading the EXT parameters file at %s: %s" %
1508 938adc87 Constantinos Venetsanopoulos
                     (parameters_file, utils.ErrnoOrStr(err)))
1509 938adc87 Constantinos Venetsanopoulos
    parameters = [v.split(None, 1) for v in parameters]
1510 938adc87 Constantinos Venetsanopoulos
1511 376631d1 Constantinos Venetsanopoulos
  es_obj = \
1512 376631d1 Constantinos Venetsanopoulos
    objects.ExtStorage(name=name, path=es_dir,
1513 376631d1 Constantinos Venetsanopoulos
                       create_script=es_files[constants.ES_SCRIPT_CREATE],
1514 376631d1 Constantinos Venetsanopoulos
                       remove_script=es_files[constants.ES_SCRIPT_REMOVE],
1515 376631d1 Constantinos Venetsanopoulos
                       grow_script=es_files[constants.ES_SCRIPT_GROW],
1516 376631d1 Constantinos Venetsanopoulos
                       attach_script=es_files[constants.ES_SCRIPT_ATTACH],
1517 376631d1 Constantinos Venetsanopoulos
                       detach_script=es_files[constants.ES_SCRIPT_DETACH],
1518 938adc87 Constantinos Venetsanopoulos
                       setinfo_script=es_files[constants.ES_SCRIPT_SETINFO],
1519 938adc87 Constantinos Venetsanopoulos
                       verify_script=es_files[constants.ES_SCRIPT_VERIFY],
1520 938adc87 Constantinos Venetsanopoulos
                       supported_parameters=parameters)
1521 376631d1 Constantinos Venetsanopoulos
  return True, es_obj
1522 376631d1 Constantinos Venetsanopoulos
1523 376631d1 Constantinos Venetsanopoulos
1524 938adc87 Constantinos Venetsanopoulos
def _ExtStorageEnvironment(unique_id, ext_params,
1525 938adc87 Constantinos Venetsanopoulos
                           size=None, grow=None, metadata=None):
1526 376631d1 Constantinos Venetsanopoulos
  """Calculate the environment for an External Storage script.
1527 376631d1 Constantinos Venetsanopoulos

1528 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1529 376631d1 Constantinos Venetsanopoulos
  @param unique_id: ExtStorage pool and name of the Volume
1530 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1531 938adc87 Constantinos Venetsanopoulos
  @param ext_params: the EXT parameters
1532 376631d1 Constantinos Venetsanopoulos
  @type size: string
1533 376631d1 Constantinos Venetsanopoulos
  @param size: size of the Volume (in mebibytes)
1534 376631d1 Constantinos Venetsanopoulos
  @type grow: string
1535 376631d1 Constantinos Venetsanopoulos
  @param grow: new size of Volume after grow (in mebibytes)
1536 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1537 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume
1538 376631d1 Constantinos Venetsanopoulos
  @rtype: dict
1539 376631d1 Constantinos Venetsanopoulos
  @return: dict of environment variables
1540 376631d1 Constantinos Venetsanopoulos

1541 376631d1 Constantinos Venetsanopoulos
  """
1542 376631d1 Constantinos Venetsanopoulos
  vol_name = unique_id[1]
1543 376631d1 Constantinos Venetsanopoulos
1544 376631d1 Constantinos Venetsanopoulos
  result = {}
1545 376631d1 Constantinos Venetsanopoulos
  result["VOL_NAME"] = vol_name
1546 376631d1 Constantinos Venetsanopoulos
1547 938adc87 Constantinos Venetsanopoulos
  # EXT params
1548 938adc87 Constantinos Venetsanopoulos
  for pname, pvalue in ext_params.items():
1549 938adc87 Constantinos Venetsanopoulos
    result["EXTP_%s" % pname.upper()] = str(pvalue)
1550 938adc87 Constantinos Venetsanopoulos
1551 376631d1 Constantinos Venetsanopoulos
  if size is not None:
1552 376631d1 Constantinos Venetsanopoulos
    result["VOL_SIZE"] = size
1553 376631d1 Constantinos Venetsanopoulos
1554 376631d1 Constantinos Venetsanopoulos
  if grow is not None:
1555 376631d1 Constantinos Venetsanopoulos
    result["VOL_NEW_SIZE"] = grow
1556 376631d1 Constantinos Venetsanopoulos
1557 376631d1 Constantinos Venetsanopoulos
  if metadata is not None:
1558 376631d1 Constantinos Venetsanopoulos
    result["VOL_METADATA"] = metadata
1559 376631d1 Constantinos Venetsanopoulos
1560 376631d1 Constantinos Venetsanopoulos
  return result
1561 376631d1 Constantinos Venetsanopoulos
1562 376631d1 Constantinos Venetsanopoulos
1563 376631d1 Constantinos Venetsanopoulos
def _VolumeLogName(kind, es_name, volume):
1564 376631d1 Constantinos Venetsanopoulos
  """Compute the ExtStorage log filename for a given Volume and operation.
1565 376631d1 Constantinos Venetsanopoulos

1566 376631d1 Constantinos Venetsanopoulos
  @type kind: string
1567 376631d1 Constantinos Venetsanopoulos
  @param kind: the operation type (e.g. create, remove etc.)
1568 376631d1 Constantinos Venetsanopoulos
  @type es_name: string
1569 376631d1 Constantinos Venetsanopoulos
  @param es_name: the ExtStorage name
1570 376631d1 Constantinos Venetsanopoulos
  @type volume: string
1571 376631d1 Constantinos Venetsanopoulos
  @param volume: the name of the Volume inside the External Storage
1572 376631d1 Constantinos Venetsanopoulos

1573 376631d1 Constantinos Venetsanopoulos
  """
1574 376631d1 Constantinos Venetsanopoulos
  # Check if the extstorage log dir is a valid dir
1575 376631d1 Constantinos Venetsanopoulos
  if not os.path.isdir(pathutils.LOG_ES_DIR):
1576 89ff748d Thomas Thrainer
    base.ThrowError("Cannot find log directory: %s", pathutils.LOG_ES_DIR)
1577 376631d1 Constantinos Venetsanopoulos
1578 376631d1 Constantinos Venetsanopoulos
  # TODO: Use tempfile.mkstemp to create unique filename
1579 89ff748d Thomas Thrainer
  basename = ("%s-%s-%s-%s.log" %
1580 89ff748d Thomas Thrainer
              (kind, es_name, volume, utils.TimestampForFilename()))
1581 89ff748d Thomas Thrainer
  return utils.PathJoin(pathutils.LOG_ES_DIR, basename)
1582 376631d1 Constantinos Venetsanopoulos
1583 376631d1 Constantinos Venetsanopoulos
1584 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
1585 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
1586 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1587 94dcbdb0 Andrea Spadaccini
1588 94dcbdb0 Andrea Spadaccini
1589 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
1590 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
1591 5ff82cc9 René Nussbaumer

1592 5ff82cc9 René Nussbaumer
  """
1593 6da90c0a Helga Velroyen
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
1594 5ff82cc9 René Nussbaumer
  if missing:
1595 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
1596 5ff82cc9 René Nussbaumer
                                 missing)
1597 5ff82cc9 René Nussbaumer
1598 5ff82cc9 René Nussbaumer
1599 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
1600 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1601 a8083063 Iustin Pop

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

1605 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1606 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
1607 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1608 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1609 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1610 94dcbdb0 Andrea Spadaccini

1611 a8083063 Iustin Pop
  """
1612 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1613 0c3d9c7c Thomas Thrainer
  device = DEV_MAP[disk.dev_type](disk.logical_id, children, disk.size,
1614 0c3d9c7c Thomas Thrainer
                                  disk.params, disk.dynamic_params)
1615 cb999543 Iustin Pop
  if not device.attached:
1616 a8083063 Iustin Pop
    return None
1617 ecb091e3 Iustin Pop
  return device
1618 a8083063 Iustin Pop
1619 a8083063 Iustin Pop
1620 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
1621 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1622 a8083063 Iustin Pop

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

1626 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1627 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
1628 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1629 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1630 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1631 94dcbdb0 Andrea Spadaccini

1632 a8083063 Iustin Pop
  """
1633 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1634 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1635 0c3d9c7c Thomas Thrainer
  device = DEV_MAP[disk.dev_type](disk.logical_id, children, disk.size,
1636 0c3d9c7c Thomas Thrainer
                                  disk.params, disk.dynamic_params)
1637 1063abd1 Iustin Pop
  device.Assemble()
1638 a8083063 Iustin Pop
  return device
1639 a8083063 Iustin Pop
1640 a8083063 Iustin Pop
1641 ee1478e5 Bernardo Dal Seno
def Create(disk, children, excl_stor):
1642 a8083063 Iustin Pop
  """Create a device.
1643 a8083063 Iustin Pop

1644 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1645 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
1646 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1647 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1648 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1649 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
1650 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
1651 5073fa0c Bernardo Dal Seno
  @rtype: L{bdev.BlockDev}
1652 5073fa0c Bernardo Dal Seno
  @return: the created device, or C{None} in case of an error
1653 94dcbdb0 Andrea Spadaccini

1654 a8083063 Iustin Pop
  """
1655 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1656 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1657 0c3d9c7c Thomas Thrainer
  device = DEV_MAP[disk.dev_type].Create(disk.logical_id, children, disk.size,
1658 0c3d9c7c Thomas Thrainer
                                         disk.spindles, disk.params, excl_stor,
1659 0c3d9c7c Thomas Thrainer
                                         disk.dynamic_params)
1660 a8083063 Iustin Pop
  return device
1661 e7d34815 Santi Raffa
1662 e7d34815 Santi Raffa
# Please keep this at the bottom of the file for visibility.
1663 e7d34815 Santi Raffa
DEV_MAP = {
1664 e7d34815 Santi Raffa
  constants.DT_PLAIN: LogicalVolume,
1665 e7d34815 Santi Raffa
  constants.DT_DRBD8: drbd.DRBD8Dev,
1666 e7d34815 Santi Raffa
  constants.DT_BLOCK: PersistentBlockDevice,
1667 e7d34815 Santi Raffa
  constants.DT_RBD: RADOSBlockDevice,
1668 e7d34815 Santi Raffa
  constants.DT_EXT: ExtStorageDevice,
1669 e7d34815 Santi Raffa
  constants.DT_FILE: FileStorage,
1670 e7d34815 Santi Raffa
  constants.DT_SHARED_FILE: FileStorage,
1671 8106dd64 Santi Raffa
  constants.DT_GLUSTER: GlusterStorage,
1672 e7d34815 Santi Raffa
}
1673 e7d34815 Santi Raffa
"""Map disk types to disk type classes.
1674 e7d34815 Santi Raffa

1675 e7d34815 Santi Raffa
@see: L{Assemble}, L{FindDevice}, L{Create}.""" # pylint: disable=W0105