Statistics
| Branch: | Tag: | Revision:

root / lib / storage / bdev.py @ 2656b017

History | View | Annotate | Download (53.4 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 5454737c Iustin Pop
# Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 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 310fbb64 Iustin Pop
43 310fbb64 Iustin Pop
44 bdecfea2 Stratos Psomadakis
class RbdShowmappedJsonError(Exception):
45 bdecfea2 Stratos Psomadakis
  """`rbd showmmapped' JSON formatting error Exception class.
46 bdecfea2 Stratos Psomadakis

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

781 b6135bbc Apollon Oikonomopoulos

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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