4 # Copyright (C) 2009 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Storage container abstraction.
29 from ganeti import errors
30 from ganeti import constants
31 from ganeti import utils
34 def _ParseSize(value):
35 return int(round(float(value), 0))
39 """Base class for storage abstraction.
42 def List(self, name, fields):
43 """Returns a list of all entities within the storage unit.
45 @type name: string or None
46 @param name: Entity name or None for all
48 @param fields: List with all requested result fields (order is preserved)
51 raise NotImplementedError()
54 class FileStorage(_Base):
58 def __init__(self, paths):
59 """Initializes this class.
62 @param paths: List of file storage paths
67 def List(self, name, fields):
68 """Returns a list of all entities within the storage unit.
81 rows.append(self._ListInner(path, fields))
86 def _ListInner(path, fields):
87 """Gathers requested information from directory.
90 @param path: Path to directory
92 @param fields: Requested fields
97 # Pre-calculate information in case it's requested more than once
98 if constants.SF_USED in fields:
99 dirsize = utils.CalculateDirectorySize(path)
103 if constants.SF_FREE in fields:
104 fsfree = utils.GetFreeFilesystemSpace(path)
108 # Make sure to update constants.VALID_STORAGE_FIELDS when changing fields.
109 for field_name in fields:
110 if field_name == constants.SF_NAME:
113 elif field_name == constants.SF_USED:
114 values.append(dirsize)
116 elif field_name == constants.SF_FREE:
117 values.append(fsfree)
120 raise errors.StorageError("Unknown field: %r" % field_name)
125 class _LvmBase(_Base):
126 """Base class for LVM storage containers.
133 def List(self, name, wanted_field_names):
134 """Returns a list of all entities within the storage unit.
139 # Get needed LVM fields
140 lvm_fields = self._GetLvmFields(self.LIST_FIELDS, wanted_field_names)
143 cmd_args = self._BuildListCommand(self.LIST_COMMAND, self.LIST_SEP,
147 cmd_result = self._RunListCommand(cmd_args)
149 # Split and rearrange LVM command output
150 return self._BuildList(self._SplitList(cmd_result, self.LIST_SEP,
157 def _GetLvmFields(fields_def, wanted_field_names):
158 """Returns unique list of fields wanted from LVM command.
160 @type fields_def: list
161 @param fields_def: Field definitions
162 @type wanted_field_names: list
163 @param wanted_field_names: List of requested fields
166 field_to_idx = dict([(field_name, idx)
167 for (idx, (field_name, _, _)) in enumerate(fields_def)])
171 for field_name in wanted_field_names:
173 idx = field_to_idx[field_name]
175 raise errors.StorageError("Unknown field: %r" % field_name)
177 (_, lvm_name, _) = fields_def[idx]
179 lvm_fields.append(lvm_name)
181 return utils.UniqueSequence(lvm_fields)
184 def _BuildList(cls, cmd_result, fields_def, wanted_field_names, lvm_fields):
185 """Builds the final result list.
187 @type cmd_result: iterable
188 @param cmd_result: Iterable of LVM command output (iterable of lists)
189 @type fields_def: list
190 @param fields_def: Field definitions
191 @type wanted_field_names: list
192 @param wanted_field_names: List of requested fields
193 @type lvm_fields: list
194 @param lvm_fields: LVM fields
197 lvm_name_to_idx = dict([(lvm_name, idx)
198 for (idx, lvm_name) in enumerate(lvm_fields)])
199 field_to_idx = dict([(field_name, idx)
200 for (idx, (field_name, _, _)) in enumerate(fields_def)])
203 for raw_data in cmd_result:
206 for field_name in wanted_field_names:
207 (_, lvm_name, convert_fn) = fields_def[field_to_idx[field_name]]
209 value = raw_data[lvm_name_to_idx[lvm_name]]
212 value = convert_fn(value)
221 def _BuildListCommand(cmd, sep, options, name):
222 """Builds LVM command line.
225 @param cmd: Command name
227 @param sep: Field separator character
228 @type options: list of strings
229 @param options: Wanted LVM fields
230 @type name: name or None
231 @param name: Name of requested entity
235 "--noheadings", "--units=m", "--nosuffix",
237 "--options", ",".join(options)]
245 def _RunListCommand(args):
249 result = utils.RunCmd(args)
252 raise errors.StorageError("Failed to run %r, command output: %s" %
253 (args[0], result.output))
258 def _SplitList(data, sep, fieldcount):
259 """Splits LVM command output into rows and fields.
262 @param data: LVM command output
264 @param sep: Field separator character
265 @type fieldcount: int
266 @param fieldcount: Expected number of fields
269 for line in data.splitlines():
270 fields = line.strip().split(sep)
272 if len(fields) != fieldcount:
278 class LvmPvStorage(_LvmBase):
279 """LVM Physical Volume storage unit.
282 def _GetAllocatable(attr):
284 return (attr[0] == "a")
286 logging.warning("Invalid PV attribute: %r", attr)
291 # Make sure to update constants.VALID_STORAGE_FIELDS when changing field
294 (constants.SF_NAME, "pv_name", None),
295 (constants.SF_SIZE, "pv_size", _ParseSize),
296 (constants.SF_USED, "pv_used", _ParseSize),
297 (constants.SF_FREE, "pv_free", _ParseSize),
298 (constants.SF_ALLOCATABLE, "pv_attr", _GetAllocatable),
302 class LvmVgStorage(_LvmBase):
303 """LVM Volume Group storage unit.
308 # Make sure to update constants.VALID_STORAGE_FIELDS when changing field
311 (constants.SF_NAME, "vg_name", None),
312 (constants.SF_SIZE, "vg_size", _ParseSize),
316 # Lookup table for storage types
318 constants.ST_FILE: FileStorage,
319 constants.ST_LVM_PV: LvmPvStorage,
320 constants.ST_LVM_VG: LvmVgStorage,
324 def GetStorageClass(name):
325 """Returns the class for a storage type.
328 @param name: Storage type
332 return _STORAGE_TYPES[name]
334 raise errors.StorageError("Unknown storage type: %r" % name)
337 def GetStorage(name, *args):
338 """Factory function for storage methods.
341 @param name: Storage type
344 return GetStorageClass(name)(*args)