Statistics
| Branch: | Tag: | Revision:

root / lib / storage / bdev.py @ 653bc0f1

History | View | Annotate | Download (54.7 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 5454737c Iustin Pop
# Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 e7d34815 Santi Raffa
"""Block device abstraction.
23 e7d34815 Santi Raffa

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

726 b6135bbc Apollon Oikonomopoulos
  """
727 c032b2ce Klaus Aehlig
  def __init__(self, unique_id, children, size, params, dyn_params, *args):
728 b6135bbc Apollon Oikonomopoulos
    """Attaches to a static block device.
729 b6135bbc Apollon Oikonomopoulos

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

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

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

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

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

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

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

783 b6135bbc Apollon Oikonomopoulos

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

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

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

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

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

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

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

840 7181fba0 Constantinos Venetsanopoulos
  """
841 c032b2ce Klaus Aehlig
  def __init__(self, unique_id, children, size, params, dyn_params, *args):
842 7181fba0 Constantinos Venetsanopoulos
    """Attaches to an rbd device.
843 7181fba0 Constantinos Venetsanopoulos

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

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

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

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

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

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

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

942 7181fba0 Constantinos Venetsanopoulos
    This method should be idempotent if the mapping already exists.
943 7181fba0 Constantinos Venetsanopoulos

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1193 f83057ad Raffa Santi
    @see: L{BlockDev.GetUserspaceAccessUri}
1194 f83057ad Raffa Santi

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

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

1209 376631d1 Constantinos Venetsanopoulos
  """
1210 702c3270 Dimitris Aragiorgis
  def __init__(self, unique_id, children, size, params, dyn_params, *args):
1211 376631d1 Constantinos Venetsanopoulos
    """Attaches to an extstorage block device.
1212 376631d1 Constantinos Venetsanopoulos

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

1232 376631d1 Constantinos Venetsanopoulos
    Provision a new volume using an extstorage provider, which will
1233 376631d1 Constantinos Venetsanopoulos
    then be mapped to a block device.
1234 376631d1 Constantinos Venetsanopoulos

1235 376631d1 Constantinos Venetsanopoulos
    """
1236 702c3270 Dimitris Aragiorgis
    (name, uuid) = args
1237 702c3270 Dimitris Aragiorgis
1238 376631d1 Constantinos Venetsanopoulos
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1239 376631d1 Constantinos Venetsanopoulos
      raise errors.ProgrammerError("Invalid configuration data %s" %
1240 376631d1 Constantinos Venetsanopoulos
                                   str(unique_id))
1241 ee1478e5 Bernardo Dal Seno
    if excl_stor:
1242 ee1478e5 Bernardo Dal Seno
      raise errors.ProgrammerError("extstorage device requested with"
1243 ee1478e5 Bernardo Dal Seno
                                   " exclusive_storage")
1244 376631d1 Constantinos Venetsanopoulos
1245 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's create script,
1246 376631d1 Constantinos Venetsanopoulos
    # to provision a new Volume inside the External Storage
1247 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id,
1248 702c3270 Dimitris Aragiorgis
                      params, size=str(size), name=name, uuid=uuid)
1249 376631d1 Constantinos Venetsanopoulos
1250 bddc92ee Klaus Aehlig
    return ExtStorageDevice(unique_id, children, size, params, dyn_params,
1251 bddc92ee Klaus Aehlig
                            *args)
1252 376631d1 Constantinos Venetsanopoulos
1253 376631d1 Constantinos Venetsanopoulos
  def Remove(self):
1254 376631d1 Constantinos Venetsanopoulos
    """Remove the extstorage device.
1255 376631d1 Constantinos Venetsanopoulos

1256 376631d1 Constantinos Venetsanopoulos
    """
1257 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1258 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1259 376631d1 Constantinos Venetsanopoulos
      return
1260 376631d1 Constantinos Venetsanopoulos
1261 376631d1 Constantinos Venetsanopoulos
    # First shutdown the device (remove mappings).
1262 376631d1 Constantinos Venetsanopoulos
    self.Shutdown()
1263 376631d1 Constantinos Venetsanopoulos
1264 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's remove script,
1265 376631d1 Constantinos Venetsanopoulos
    # to remove the Volume from the External Storage
1266 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id,
1267 702c3270 Dimitris Aragiorgis
                      self.ext_params, name=self.name, uuid=self.uuid)
1268 376631d1 Constantinos Venetsanopoulos
1269 376631d1 Constantinos Venetsanopoulos
  def Rename(self, new_id):
1270 376631d1 Constantinos Venetsanopoulos
    """Rename this device.
1271 376631d1 Constantinos Venetsanopoulos

1272 376631d1 Constantinos Venetsanopoulos
    """
1273 376631d1 Constantinos Venetsanopoulos
    pass
1274 376631d1 Constantinos Venetsanopoulos
1275 376631d1 Constantinos Venetsanopoulos
  def Attach(self):
1276 376631d1 Constantinos Venetsanopoulos
    """Attach to an existing extstorage device.
1277 376631d1 Constantinos Venetsanopoulos

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

1281 376631d1 Constantinos Venetsanopoulos
    """
1282 376631d1 Constantinos Venetsanopoulos
    self.attached = False
1283 376631d1 Constantinos Venetsanopoulos
1284 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's attach script,
1285 376631d1 Constantinos Venetsanopoulos
    # to attach an existing Volume to a block device under /dev
1286 376631d1 Constantinos Venetsanopoulos
    self.dev_path = _ExtStorageAction(constants.ES_ACTION_ATTACH,
1287 702c3270 Dimitris Aragiorgis
                                      self.unique_id, self.ext_params,
1288 702c3270 Dimitris Aragiorgis
                                      name=self.name, uuid=self.uuid)
1289 376631d1 Constantinos Venetsanopoulos
1290 376631d1 Constantinos Venetsanopoulos
    try:
1291 376631d1 Constantinos Venetsanopoulos
      st = os.stat(self.dev_path)
1292 376631d1 Constantinos Venetsanopoulos
    except OSError, err:
1293 376631d1 Constantinos Venetsanopoulos
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
1294 376631d1 Constantinos Venetsanopoulos
      return False
1295 376631d1 Constantinos Venetsanopoulos
1296 376631d1 Constantinos Venetsanopoulos
    if not stat.S_ISBLK(st.st_mode):
1297 376631d1 Constantinos Venetsanopoulos
      logging.error("%s is not a block device", self.dev_path)
1298 376631d1 Constantinos Venetsanopoulos
      return False
1299 376631d1 Constantinos Venetsanopoulos
1300 376631d1 Constantinos Venetsanopoulos
    self.major = os.major(st.st_rdev)
1301 376631d1 Constantinos Venetsanopoulos
    self.minor = os.minor(st.st_rdev)
1302 376631d1 Constantinos Venetsanopoulos
    self.attached = True
1303 376631d1 Constantinos Venetsanopoulos
1304 376631d1 Constantinos Venetsanopoulos
    return True
1305 376631d1 Constantinos Venetsanopoulos
1306 376631d1 Constantinos Venetsanopoulos
  def Assemble(self):
1307 376631d1 Constantinos Venetsanopoulos
    """Assemble the device.
1308 376631d1 Constantinos Venetsanopoulos

1309 376631d1 Constantinos Venetsanopoulos
    """
1310 376631d1 Constantinos Venetsanopoulos
    pass
1311 376631d1 Constantinos Venetsanopoulos
1312 376631d1 Constantinos Venetsanopoulos
  def Shutdown(self):
1313 376631d1 Constantinos Venetsanopoulos
    """Shutdown the device.
1314 376631d1 Constantinos Venetsanopoulos

1315 376631d1 Constantinos Venetsanopoulos
    """
1316 376631d1 Constantinos Venetsanopoulos
    if not self.minor and not self.Attach():
1317 376631d1 Constantinos Venetsanopoulos
      # The extstorage device doesn't exist.
1318 376631d1 Constantinos Venetsanopoulos
      return
1319 376631d1 Constantinos Venetsanopoulos
1320 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's detach script,
1321 376631d1 Constantinos Venetsanopoulos
    # to detach an existing Volume from it's block device under /dev
1322 938adc87 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id,
1323 702c3270 Dimitris Aragiorgis
                      self.ext_params, name=self.name, uuid=self.uuid)
1324 376631d1 Constantinos Venetsanopoulos
1325 376631d1 Constantinos Venetsanopoulos
    self.minor = None
1326 376631d1 Constantinos Venetsanopoulos
    self.dev_path = None
1327 376631d1 Constantinos Venetsanopoulos
1328 376631d1 Constantinos Venetsanopoulos
  def Open(self, force=False):
1329 376631d1 Constantinos Venetsanopoulos
    """Make the device ready for I/O.
1330 376631d1 Constantinos Venetsanopoulos

1331 376631d1 Constantinos Venetsanopoulos
    """
1332 376631d1 Constantinos Venetsanopoulos
    pass
1333 376631d1 Constantinos Venetsanopoulos
1334 376631d1 Constantinos Venetsanopoulos
  def Close(self):
1335 376631d1 Constantinos Venetsanopoulos
    """Notifies that the device will no longer be used for I/O.
1336 376631d1 Constantinos Venetsanopoulos

1337 376631d1 Constantinos Venetsanopoulos
    """
1338 376631d1 Constantinos Venetsanopoulos
    pass
1339 376631d1 Constantinos Venetsanopoulos
1340 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
1341 376631d1 Constantinos Venetsanopoulos
    """Grow the Volume.
1342 376631d1 Constantinos Venetsanopoulos

1343 376631d1 Constantinos Venetsanopoulos
    @type amount: integer
1344 376631d1 Constantinos Venetsanopoulos
    @param amount: the amount (in mebibytes) to grow with
1345 376631d1 Constantinos Venetsanopoulos
    @type dryrun: boolean
1346 376631d1 Constantinos Venetsanopoulos
    @param dryrun: whether to execute the operation in simulation mode
1347 376631d1 Constantinos Venetsanopoulos
        only, without actually increasing the size
1348 376631d1 Constantinos Venetsanopoulos

1349 376631d1 Constantinos Venetsanopoulos
    """
1350 376631d1 Constantinos Venetsanopoulos
    if not backingstore:
1351 376631d1 Constantinos Venetsanopoulos
      return
1352 376631d1 Constantinos Venetsanopoulos
    if not self.Attach():
1353 89ff748d Thomas Thrainer
      base.ThrowError("Can't attach to extstorage device during Grow()")
1354 376631d1 Constantinos Venetsanopoulos
1355 376631d1 Constantinos Venetsanopoulos
    if dryrun:
1356 376631d1 Constantinos Venetsanopoulos
      # we do not support dry runs of resize operations for now.
1357 376631d1 Constantinos Venetsanopoulos
      return
1358 376631d1 Constantinos Venetsanopoulos
1359 376631d1 Constantinos Venetsanopoulos
    new_size = self.size + amount
1360 376631d1 Constantinos Venetsanopoulos
1361 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's grow script,
1362 376631d1 Constantinos Venetsanopoulos
    # to grow an existing Volume inside the External Storage
1363 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_GROW, self.unique_id,
1364 702c3270 Dimitris Aragiorgis
                      self.ext_params, size=str(self.size), grow=str(new_size),
1365 702c3270 Dimitris Aragiorgis
                      name=self.name, uuid=self.uuid)
1366 376631d1 Constantinos Venetsanopoulos
1367 376631d1 Constantinos Venetsanopoulos
  def SetInfo(self, text):
1368 376631d1 Constantinos Venetsanopoulos
    """Update metadata with info text.
1369 376631d1 Constantinos Venetsanopoulos

1370 376631d1 Constantinos Venetsanopoulos
    """
1371 376631d1 Constantinos Venetsanopoulos
    # Replace invalid characters
1372 376631d1 Constantinos Venetsanopoulos
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
1373 376631d1 Constantinos Venetsanopoulos
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
1374 376631d1 Constantinos Venetsanopoulos
1375 376631d1 Constantinos Venetsanopoulos
    # Only up to 128 characters are allowed
1376 376631d1 Constantinos Venetsanopoulos
    text = text[:128]
1377 376631d1 Constantinos Venetsanopoulos
1378 376631d1 Constantinos Venetsanopoulos
    # Call the External Storage's setinfo script,
1379 376631d1 Constantinos Venetsanopoulos
    # to set metadata for an existing Volume inside the External Storage
1380 376631d1 Constantinos Venetsanopoulos
    _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
1381 702c3270 Dimitris Aragiorgis
                      self.ext_params, metadata=text,
1382 702c3270 Dimitris Aragiorgis
                      name=self.name, uuid=self.uuid)
1383 376631d1 Constantinos Venetsanopoulos
1384 376631d1 Constantinos Venetsanopoulos
1385 938adc87 Constantinos Venetsanopoulos
def _ExtStorageAction(action, unique_id, ext_params,
1386 702c3270 Dimitris Aragiorgis
                      size=None, grow=None, metadata=None,
1387 702c3270 Dimitris Aragiorgis
                      name=None, uuid=None):
1388 376631d1 Constantinos Venetsanopoulos
  """Take an External Storage action.
1389 376631d1 Constantinos Venetsanopoulos

1390 376631d1 Constantinos Venetsanopoulos
  Take an External Storage action concerning or affecting
1391 376631d1 Constantinos Venetsanopoulos
  a specific Volume inside the External Storage.
1392 376631d1 Constantinos Venetsanopoulos

1393 376631d1 Constantinos Venetsanopoulos
  @type action: string
1394 376631d1 Constantinos Venetsanopoulos
  @param action: which action to perform. One of:
1395 376631d1 Constantinos Venetsanopoulos
                 create / remove / grow / attach / detach
1396 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1397 376631d1 Constantinos Venetsanopoulos
  @param unique_id: a tuple containing the type of ExtStorage (driver)
1398 376631d1 Constantinos Venetsanopoulos
                    and the Volume name
1399 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1400 938adc87 Constantinos Venetsanopoulos
  @param ext_params: ExtStorage parameters
1401 376631d1 Constantinos Venetsanopoulos
  @type size: integer
1402 376631d1 Constantinos Venetsanopoulos
  @param size: the size of the Volume in mebibytes
1403 376631d1 Constantinos Venetsanopoulos
  @type grow: integer
1404 376631d1 Constantinos Venetsanopoulos
  @param grow: the new size in mebibytes (after grow)
1405 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1406 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume, for use by the provider
1407 702c3270 Dimitris Aragiorgis
  @type name: string
1408 702c3270 Dimitris Aragiorgis
  @param name: name of the Volume (objects.Disk.name)
1409 702c3270 Dimitris Aragiorgis
  @type uuid: string
1410 702c3270 Dimitris Aragiorgis
  @param uuid: uuid of the Volume (objects.Disk.uuid)
1411 376631d1 Constantinos Venetsanopoulos
  @rtype: None or a block device path (during attach)
1412 376631d1 Constantinos Venetsanopoulos

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

1469 376631d1 Constantinos Venetsanopoulos
  This function will return an ExtStorage instance
1470 376631d1 Constantinos Venetsanopoulos
  if the given name is a valid ExtStorage name.
1471 376631d1 Constantinos Venetsanopoulos

1472 376631d1 Constantinos Venetsanopoulos
  @type base_dir: string
1473 376631d1 Constantinos Venetsanopoulos
  @keyword base_dir: Base directory containing ExtStorage installations.
1474 376631d1 Constantinos Venetsanopoulos
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
1475 376631d1 Constantinos Venetsanopoulos
  @rtype: tuple
1476 376631d1 Constantinos Venetsanopoulos
  @return: True and the ExtStorage instance if we find a valid one, or
1477 376631d1 Constantinos Venetsanopoulos
      False and the diagnose message on error
1478 376631d1 Constantinos Venetsanopoulos

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

1544 376631d1 Constantinos Venetsanopoulos
  @type unique_id: tuple (driver, vol_name)
1545 376631d1 Constantinos Venetsanopoulos
  @param unique_id: ExtStorage pool and name of the Volume
1546 938adc87 Constantinos Venetsanopoulos
  @type ext_params: dict
1547 938adc87 Constantinos Venetsanopoulos
  @param ext_params: the EXT parameters
1548 376631d1 Constantinos Venetsanopoulos
  @type size: string
1549 376631d1 Constantinos Venetsanopoulos
  @param size: size of the Volume (in mebibytes)
1550 376631d1 Constantinos Venetsanopoulos
  @type grow: string
1551 376631d1 Constantinos Venetsanopoulos
  @param grow: new size of Volume after grow (in mebibytes)
1552 376631d1 Constantinos Venetsanopoulos
  @type metadata: string
1553 376631d1 Constantinos Venetsanopoulos
  @param metadata: metadata info of the Volume
1554 702c3270 Dimitris Aragiorgis
  @type name: string
1555 702c3270 Dimitris Aragiorgis
  @param name: name of the Volume (objects.Disk.name)
1556 702c3270 Dimitris Aragiorgis
  @type uuid: string
1557 702c3270 Dimitris Aragiorgis
  @param uuid: uuid of the Volume (objects.Disk.uuid)
1558 376631d1 Constantinos Venetsanopoulos
  @rtype: dict
1559 376631d1 Constantinos Venetsanopoulos
  @return: dict of environment variables
1560 376631d1 Constantinos Venetsanopoulos

1561 376631d1 Constantinos Venetsanopoulos
  """
1562 376631d1 Constantinos Venetsanopoulos
  vol_name = unique_id[1]
1563 376631d1 Constantinos Venetsanopoulos
1564 376631d1 Constantinos Venetsanopoulos
  result = {}
1565 376631d1 Constantinos Venetsanopoulos
  result["VOL_NAME"] = vol_name
1566 376631d1 Constantinos Venetsanopoulos
1567 938adc87 Constantinos Venetsanopoulos
  # EXT params
1568 938adc87 Constantinos Venetsanopoulos
  for pname, pvalue in ext_params.items():
1569 938adc87 Constantinos Venetsanopoulos
    result["EXTP_%s" % pname.upper()] = str(pvalue)
1570 938adc87 Constantinos Venetsanopoulos
1571 376631d1 Constantinos Venetsanopoulos
  if size is not None:
1572 376631d1 Constantinos Venetsanopoulos
    result["VOL_SIZE"] = size
1573 376631d1 Constantinos Venetsanopoulos
1574 376631d1 Constantinos Venetsanopoulos
  if grow is not None:
1575 376631d1 Constantinos Venetsanopoulos
    result["VOL_NEW_SIZE"] = grow
1576 376631d1 Constantinos Venetsanopoulos
1577 376631d1 Constantinos Venetsanopoulos
  if metadata is not None:
1578 376631d1 Constantinos Venetsanopoulos
    result["VOL_METADATA"] = metadata
1579 376631d1 Constantinos Venetsanopoulos
1580 702c3270 Dimitris Aragiorgis
  if name is not None:
1581 702c3270 Dimitris Aragiorgis
    result["VOL_CNAME"] = name
1582 702c3270 Dimitris Aragiorgis
1583 702c3270 Dimitris Aragiorgis
  if uuid is not None:
1584 702c3270 Dimitris Aragiorgis
    result["VOL_UUID"] = uuid
1585 702c3270 Dimitris Aragiorgis
1586 376631d1 Constantinos Venetsanopoulos
  return result
1587 376631d1 Constantinos Venetsanopoulos
1588 376631d1 Constantinos Venetsanopoulos
1589 376631d1 Constantinos Venetsanopoulos
def _VolumeLogName(kind, es_name, volume):
1590 376631d1 Constantinos Venetsanopoulos
  """Compute the ExtStorage log filename for a given Volume and operation.
1591 376631d1 Constantinos Venetsanopoulos

1592 376631d1 Constantinos Venetsanopoulos
  @type kind: string
1593 376631d1 Constantinos Venetsanopoulos
  @param kind: the operation type (e.g. create, remove etc.)
1594 376631d1 Constantinos Venetsanopoulos
  @type es_name: string
1595 376631d1 Constantinos Venetsanopoulos
  @param es_name: the ExtStorage name
1596 376631d1 Constantinos Venetsanopoulos
  @type volume: string
1597 376631d1 Constantinos Venetsanopoulos
  @param volume: the name of the Volume inside the External Storage
1598 376631d1 Constantinos Venetsanopoulos

1599 376631d1 Constantinos Venetsanopoulos
  """
1600 376631d1 Constantinos Venetsanopoulos
  # Check if the extstorage log dir is a valid dir
1601 376631d1 Constantinos Venetsanopoulos
  if not os.path.isdir(pathutils.LOG_ES_DIR):
1602 89ff748d Thomas Thrainer
    base.ThrowError("Cannot find log directory: %s", pathutils.LOG_ES_DIR)
1603 376631d1 Constantinos Venetsanopoulos
1604 376631d1 Constantinos Venetsanopoulos
  # TODO: Use tempfile.mkstemp to create unique filename
1605 89ff748d Thomas Thrainer
  basename = ("%s-%s-%s-%s.log" %
1606 89ff748d Thomas Thrainer
              (kind, es_name, volume, utils.TimestampForFilename()))
1607 89ff748d Thomas Thrainer
  return utils.PathJoin(pathutils.LOG_ES_DIR, basename)
1608 376631d1 Constantinos Venetsanopoulos
1609 376631d1 Constantinos Venetsanopoulos
1610 94dcbdb0 Andrea Spadaccini
def _VerifyDiskType(dev_type):
1611 94dcbdb0 Andrea Spadaccini
  if dev_type not in DEV_MAP:
1612 94dcbdb0 Andrea Spadaccini
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1613 94dcbdb0 Andrea Spadaccini
1614 94dcbdb0 Andrea Spadaccini
1615 5ff82cc9 René Nussbaumer
def _VerifyDiskParams(disk):
1616 5ff82cc9 René Nussbaumer
  """Verifies if all disk parameters are set.
1617 5ff82cc9 René Nussbaumer

1618 5ff82cc9 René Nussbaumer
  """
1619 6da90c0a Helga Velroyen
  missing = set(constants.DISK_LD_DEFAULTS[disk.dev_type]) - set(disk.params)
1620 5ff82cc9 René Nussbaumer
  if missing:
1621 5ff82cc9 René Nussbaumer
    raise errors.ProgrammerError("Block device is missing disk parameters: %s" %
1622 5ff82cc9 René Nussbaumer
                                 missing)
1623 5ff82cc9 René Nussbaumer
1624 5ff82cc9 René Nussbaumer
1625 94dcbdb0 Andrea Spadaccini
def FindDevice(disk, children):
1626 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1627 a8083063 Iustin Pop

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

1631 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1632 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to find
1633 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1634 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1635 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1636 94dcbdb0 Andrea Spadaccini

1637 a8083063 Iustin Pop
  """
1638 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1639 0c3d9c7c Thomas Thrainer
  device = DEV_MAP[disk.dev_type](disk.logical_id, children, disk.size,
1640 702c3270 Dimitris Aragiorgis
                                  disk.params, disk.dynamic_params,
1641 702c3270 Dimitris Aragiorgis
                                  disk.name, disk.uuid)
1642 cb999543 Iustin Pop
  if not device.attached:
1643 a8083063 Iustin Pop
    return None
1644 ecb091e3 Iustin Pop
  return device
1645 a8083063 Iustin Pop
1646 a8083063 Iustin Pop
1647 94dcbdb0 Andrea Spadaccini
def Assemble(disk, children):
1648 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1649 a8083063 Iustin Pop

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

1653 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1654 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to assemble
1655 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1656 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1657 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1658 94dcbdb0 Andrea Spadaccini

1659 a8083063 Iustin Pop
  """
1660 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1661 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1662 0c3d9c7c Thomas Thrainer
  device = DEV_MAP[disk.dev_type](disk.logical_id, children, disk.size,
1663 702c3270 Dimitris Aragiorgis
                                  disk.params, disk.dynamic_params,
1664 702c3270 Dimitris Aragiorgis
                                  disk.name, disk.uuid)
1665 1063abd1 Iustin Pop
  device.Assemble()
1666 a8083063 Iustin Pop
  return device
1667 a8083063 Iustin Pop
1668 a8083063 Iustin Pop
1669 ee1478e5 Bernardo Dal Seno
def Create(disk, children, excl_stor):
1670 a8083063 Iustin Pop
  """Create a device.
1671 a8083063 Iustin Pop

1672 94dcbdb0 Andrea Spadaccini
  @type disk: L{objects.Disk}
1673 94dcbdb0 Andrea Spadaccini
  @param disk: the disk object to create
1674 94dcbdb0 Andrea Spadaccini
  @type children: list of L{bdev.BlockDev}
1675 94dcbdb0 Andrea Spadaccini
  @param children: the list of block devices that are children of the device
1676 94dcbdb0 Andrea Spadaccini
                  represented by the disk parameter
1677 ee1478e5 Bernardo Dal Seno
  @type excl_stor: boolean
1678 ee1478e5 Bernardo Dal Seno
  @param excl_stor: Whether exclusive_storage is active
1679 5073fa0c Bernardo Dal Seno
  @rtype: L{bdev.BlockDev}
1680 5073fa0c Bernardo Dal Seno
  @return: the created device, or C{None} in case of an error
1681 94dcbdb0 Andrea Spadaccini

1682 a8083063 Iustin Pop
  """
1683 94dcbdb0 Andrea Spadaccini
  _VerifyDiskType(disk.dev_type)
1684 5ff82cc9 René Nussbaumer
  _VerifyDiskParams(disk)
1685 0c3d9c7c Thomas Thrainer
  device = DEV_MAP[disk.dev_type].Create(disk.logical_id, children, disk.size,
1686 0c3d9c7c Thomas Thrainer
                                         disk.spindles, disk.params, excl_stor,
1687 702c3270 Dimitris Aragiorgis
                                         disk.dynamic_params,
1688 702c3270 Dimitris Aragiorgis
                                         disk.name, disk.uuid)
1689 a8083063 Iustin Pop
  return device
1690 e7d34815 Santi Raffa
1691 e7d34815 Santi Raffa
# Please keep this at the bottom of the file for visibility.
1692 e7d34815 Santi Raffa
DEV_MAP = {
1693 e7d34815 Santi Raffa
  constants.DT_PLAIN: LogicalVolume,
1694 e7d34815 Santi Raffa
  constants.DT_DRBD8: drbd.DRBD8Dev,
1695 e7d34815 Santi Raffa
  constants.DT_BLOCK: PersistentBlockDevice,
1696 e7d34815 Santi Raffa
  constants.DT_RBD: RADOSBlockDevice,
1697 e7d34815 Santi Raffa
  constants.DT_EXT: ExtStorageDevice,
1698 e7d34815 Santi Raffa
  constants.DT_FILE: FileStorage,
1699 e7d34815 Santi Raffa
  constants.DT_SHARED_FILE: FileStorage,
1700 8106dd64 Santi Raffa
  constants.DT_GLUSTER: GlusterStorage,
1701 e7d34815 Santi Raffa
}
1702 e7d34815 Santi Raffa
"""Map disk types to disk type classes.
1703 e7d34815 Santi Raffa

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