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
38 COL_ALLOCATABLE = "allocatable"
41 def _ParseSize(value):
42 return int(round(float(value), 0))
46 """Base class for storage abstraction.
49 def List(self, name, fields):
50 """Returns a list of all entities within the storage unit.
52 @type name: string or None
53 @param name: Entity name or None for all
55 @param fields: List with all requested result fields (order is preserved)
58 raise NotImplementedError()
61 class FileStorage(_Base):
65 def __init__(self, paths):
66 """Initializes this class.
69 @param paths: List of file storage paths
74 def List(self, name, fields):
75 """Returns a list of all entities within the storage unit.
88 rows.append(self._ListInner(path, fields))
93 def _ListInner(path, fields):
94 """Gathers requested information from directory.
97 @param path: Path to directory
99 @param fields: Requested fields
104 # Pre-calculate information in case it's requested more than once
105 if COL_SIZE in fields:
106 dirsize = utils.CalculateDirectorySize(path)
110 if COL_FREE in fields:
111 fsfree = utils.GetFreeFilesystemSpace(path)
115 for field_name in fields:
116 if field_name == COL_NAME:
119 elif field_name == COL_SIZE:
120 values.append(dirsize)
122 elif field_name == COL_FREE:
123 values.append(fsfree)
126 raise errors.StorageError("Unknown field: %r" % field_name)
131 class _LvmBase(_Base):
132 """Base class for LVM storage containers.
139 def List(self, name, wanted_field_names):
140 """Returns a list of all entities within the storage unit.
145 # Get needed LVM fields
146 lvm_fields = self._GetLvmFields(self.LIST_FIELDS, wanted_field_names)
149 cmd_args = self._BuildListCommand(self.LIST_COMMAND, self.LIST_SEP,
153 cmd_result = self._RunListCommand(cmd_args)
155 # Split and rearrange LVM command output
156 return self._BuildList(self._SplitList(cmd_result, self.LIST_SEP,
163 def _GetLvmFields(fields_def, wanted_field_names):
164 """Returns unique list of fields wanted from LVM command.
166 @type fields_def: list
167 @param fields_def: Field definitions
168 @type wanted_field_names: list
169 @param wanted_field_names: List of requested fields
172 field_to_idx = dict([(field_name, idx)
173 for (idx, (field_name, _, _)) in enumerate(fields_def)])
177 for field_name in wanted_field_names:
179 idx = field_to_idx[field_name]
181 raise errors.StorageError("Unknown field: %r" % field_name)
183 (_, lvm_name, _) = fields_def[idx]
185 lvm_fields.append(lvm_name)
187 return utils.UniqueSequence(lvm_fields)
190 def _BuildList(cls, cmd_result, fields_def, wanted_field_names, lvm_fields):
191 """Builds the final result list.
193 @type cmd_result: iterable
194 @param cmd_result: Iterable of LVM command output (iterable of lists)
195 @type fields_def: list
196 @param fields_def: Field definitions
197 @type wanted_field_names: list
198 @param wanted_field_names: List of requested fields
199 @type lvm_fields: list
200 @param lvm_fields: LVM fields
203 lvm_name_to_idx = dict([(lvm_name, idx)
204 for (idx, lvm_name) in enumerate(lvm_fields)])
205 field_to_idx = dict([(field_name, idx)
206 for (idx, (field_name, _, _)) in enumerate(fields_def)])
209 for raw_data in cmd_result:
212 for field_name in wanted_field_names:
213 (_, lvm_name, convert_fn) = fields_def[field_to_idx[field_name]]
215 value = raw_data[lvm_name_to_idx[lvm_name]]
218 value = convert_fn(value)
227 def _BuildListCommand(cmd, sep, options, name):
228 """Builds LVM command line.
231 @param cmd: Command name
233 @param sep: Field separator character
234 @type options: list of strings
235 @param options: Wanted LVM fields
236 @type name: name or None
237 @param name: Name of requested entity
241 "--noheadings", "--units=m", "--nosuffix",
243 "--options", ",".join(options)]
251 def _RunListCommand(args):
255 result = utils.RunCmd(args)
258 raise errors.StorageError("Failed to run %r, command output: %s" %
259 (args[0], result.output))
264 def _SplitList(data, sep, fieldcount):
265 """Splits LVM command output into rows and fields.
268 @param data: LVM command output
270 @param sep: Field separator character
271 @type fieldcount: int
272 @param fieldcount: Expected number of fields
275 for line in data.splitlines():
276 fields = line.strip().split(sep)
278 if len(fields) != fieldcount:
284 class LvmPvStorage(_LvmBase):
285 """LVM Physical Volume storage unit.
288 def _GetAllocatable(attr):
290 return (attr[0] == "a")
292 logging.warning("Invalid PV attribute: %r", attr)
297 (COL_NAME, "pv_name", None),
298 (COL_SIZE, "pv_size", _ParseSize),
299 (COL_USED, "pv_used", _ParseSize),
300 (COL_FREE, "pv_free", _ParseSize),
301 (COL_ALLOCATABLE, "pv_attr", _GetAllocatable),
305 class LvmVgStorage(_LvmBase):
306 """LVM Volume Group storage unit.
311 (COL_NAME, "vg_name", None),
312 (COL_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)