#
#
-# Copyright (C) 2009 Google Inc.
+# Copyright (C) 2009, 2011 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
"""
+# pylint: disable-msg=W0232,R0201
+
+# W0232, since we use these as singletons rather than object holding
+# data
+
+# R0201, for the same reason
+
+# TODO: FileStorage initialised with paths whereas the others not
import logging
from ganeti import utils
-COL_NAME = "name"
-COL_SIZE = "size"
-COL_FREE = "free"
-COL_USED = "used"
-COL_ALLOCATABLE = "allocatable"
-
-
def _ParseSize(value):
return int(round(float(value), 0))
"""
raise NotImplementedError()
+ def Modify(self, name, changes): # pylint: disable-msg=W0613
+ """Modifies an entity within the storage unit.
+
+ @type name: string
+ @param name: Entity name
+ @type changes: dict
+ @param changes: New field values
+
+ """
+ # Don't raise an error if no changes are requested
+ if changes:
+ raise errors.ProgrammerError("Unable to modify the following"
+ "fields: %r" % (changes.keys(), ))
+
+ def Execute(self, name, op):
+ """Executes an operation on an entity within the storage unit.
+
+ @type name: string
+ @param name: Entity name
+ @type op: string
+ @param op: Operation name
+
+ """
+ raise NotImplementedError()
+
-class FileStorage(_Base):
+class FileStorage(_Base): # pylint: disable-msg=W0223
"""File storage unit.
"""
values = []
# Pre-calculate information in case it's requested more than once
- if COL_SIZE in fields:
+ if constants.SF_USED in fields:
dirsize = utils.CalculateDirectorySize(path)
else:
dirsize = None
- if COL_FREE in fields:
- fsfree = utils.GetFreeFilesystemSpace(path)
+ if constants.SF_FREE in fields or constants.SF_SIZE in fields:
+ fsstats = utils.GetFilesystemStats(path)
else:
- fsfree = None
+ fsstats = None
+ # Make sure to update constants.VALID_STORAGE_FIELDS when changing fields.
for field_name in fields:
- if field_name == COL_NAME:
+ if field_name == constants.SF_NAME:
values.append(path)
- elif field_name == COL_SIZE:
+ elif field_name == constants.SF_USED:
values.append(dirsize)
- elif field_name == COL_FREE:
- values.append(fsfree)
+ elif field_name == constants.SF_FREE:
+ values.append(fsstats[1])
+
+ elif field_name == constants.SF_SIZE:
+ values.append(fsstats[0])
+
+ elif field_name == constants.SF_ALLOCATABLE:
+ values.append(True)
else:
raise errors.StorageError("Unknown field: %r" % field_name)
return values
-class _LvmBase(_Base):
+class _LvmBase(_Base): # pylint: disable-msg=W0223
"""Base class for LVM storage containers.
+ @cvar LIST_FIELDS: list of tuples consisting of three elements: SF_*
+ constants, lvm command output fields (list), and conversion
+ function or static value (for static value, the lvm output field
+ can be an empty list)
+
"""
LIST_SEP = "|"
LIST_COMMAND = None
"""
field_to_idx = dict([(field_name, idx)
- for (idx, (field_name, _, _)) in enumerate(fields_def)])
+ for (idx, (field_name, _, _)) in
+ enumerate(fields_def)])
lvm_fields = []
except IndexError:
raise errors.StorageError("Unknown field: %r" % field_name)
- (_, lvm_name, _) = fields_def[idx]
+ (_, lvm_names, _) = fields_def[idx]
- lvm_fields.append(lvm_name)
+ lvm_fields.extend(lvm_names)
return utils.UniqueSequence(lvm_fields)
lvm_name_to_idx = dict([(lvm_name, idx)
for (idx, lvm_name) in enumerate(lvm_fields)])
field_to_idx = dict([(field_name, idx)
- for (idx, (field_name, _, _)) in enumerate(fields_def)])
+ for (idx, (field_name, _, _)) in
+ enumerate(fields_def)])
data = []
for raw_data in cmd_result:
row = []
for field_name in wanted_field_names:
- (_, lvm_name, convert_fn) = fields_def[field_to_idx[field_name]]
-
- value = raw_data[lvm_name_to_idx[lvm_name]]
-
- if convert_fn:
- value = convert_fn(value)
-
- row.append(value)
+ (_, lvm_names, mapper) = fields_def[field_to_idx[field_name]]
+
+ values = [raw_data[lvm_name_to_idx[i]] for i in lvm_names]
+
+ if callable(mapper):
+ # we got a function, call it with all the declared fields
+ val = mapper(*values) # pylint: disable-msg=W0142
+ elif len(values) == 1:
+ assert mapper is None, ("Invalid mapper value (neither callable"
+ " nor None) for one-element fields")
+ # we don't have a function, but we had a single field
+ # declared, pass it unchanged
+ val = values[0]
+ else:
+ # let's make sure there are no fields declared (cannot map >
+ # 1 field without a function)
+ assert not values, "LVM storage has multi-fields without a function"
+ val = mapper
+
+ row.append(val)
data.append(row)
fields = line.strip().split(sep)
if len(fields) != fieldcount:
+ logging.warning("Invalid line returned from lvm command: %s", line)
continue
yield fields
-class LvmPvStorage(_LvmBase):
- """LVM Physical Volume storage unit.
+def _LvmPvGetAllocatable(attr):
+ """Determines whether LVM PV is allocatable.
+
+ @rtype: bool
"""
- def _GetAllocatable(attr):
- if attr:
- return (attr[0] == "a")
- else:
- logging.warning("Invalid PV attribute: %r", attr)
- return False
+ if attr:
+ return (attr[0] == "a")
+ else:
+ logging.warning("Invalid PV attribute: %r", attr)
+ return False
+
+class LvmPvStorage(_LvmBase): # pylint: disable-msg=W0223
+ """LVM Physical Volume storage unit.
+
+ """
LIST_COMMAND = "pvs"
+
+ # Make sure to update constants.VALID_STORAGE_FIELDS when changing field
+ # definitions.
LIST_FIELDS = [
- (COL_NAME, "pv_name", None),
- (COL_SIZE, "pv_size", _ParseSize),
- (COL_USED, "pv_used", _ParseSize),
- (COL_FREE, "pv_free", _ParseSize),
- (COL_ALLOCATABLE, "pv_attr", _GetAllocatable),
+ (constants.SF_NAME, ["pv_name"], None),
+ (constants.SF_SIZE, ["pv_size"], _ParseSize),
+ (constants.SF_USED, ["pv_used"], _ParseSize),
+ (constants.SF_FREE, ["pv_free"], _ParseSize),
+ (constants.SF_ALLOCATABLE, ["pv_attr"], _LvmPvGetAllocatable),
]
+ def _SetAllocatable(self, name, allocatable):
+ """Sets the "allocatable" flag on a physical volume.
+
+ @type name: string
+ @param name: Physical volume name
+ @type allocatable: bool
+ @param allocatable: Whether to set the "allocatable" flag
+
+ """
+ args = ["pvchange", "--allocatable"]
+
+ if allocatable:
+ args.append("y")
+ else:
+ args.append("n")
+
+ args.append(name)
+
+ result = utils.RunCmd(args)
+ if result.failed:
+ raise errors.StorageError("Failed to modify physical volume,"
+ " pvchange output: %s" %
+ result.output)
+
+ def Modify(self, name, changes):
+ """Modifies flags on a physical volume.
+
+ See L{_Base.Modify}.
+
+ """
+ if constants.SF_ALLOCATABLE in changes:
+ self._SetAllocatable(name, changes[constants.SF_ALLOCATABLE])
+ del changes[constants.SF_ALLOCATABLE]
+
+ # Other changes will be handled (and maybe refused) by the base class.
+ return _LvmBase.Modify(self, name, changes)
+
class LvmVgStorage(_LvmBase):
"""LVM Volume Group storage unit.
"""
LIST_COMMAND = "vgs"
+
+ # Make sure to update constants.VALID_STORAGE_FIELDS when changing field
+ # definitions.
LIST_FIELDS = [
- (COL_NAME, "vg_name", None),
- (COL_SIZE, "vg_size", _ParseSize),
+ (constants.SF_NAME, ["vg_name"], None),
+ (constants.SF_SIZE, ["vg_size"], _ParseSize),
+ (constants.SF_FREE, ["vg_free"], _ParseSize),
+ (constants.SF_USED, ["vg_size", "vg_free"],
+ lambda x, y: _ParseSize(x) - _ParseSize(y)),
+ (constants.SF_ALLOCATABLE, [], True),
]
+ def _RemoveMissing(self, name):
+ """Runs "vgreduce --removemissing" on a volume group.
+
+ @type name: string
+ @param name: Volume group name
+
+ """
+ # Ignoring vgreduce exit code. Older versions exit with an error even tough
+ # the VG is already consistent. This was fixed in later versions, but we
+ # cannot depend on it.
+ result = utils.RunCmd(["vgreduce", "--removemissing", name])
+
+ # Keep output in case something went wrong
+ vgreduce_output = result.output
+
+ result = utils.RunCmd(["vgs", "--noheadings", "--nosuffix", name])
+ if result.failed:
+ raise errors.StorageError(("Volume group '%s' still not consistent,"
+ " 'vgreduce' output: %r,"
+ " 'vgs' output: %r") %
+ (name, vgreduce_output, result.output))
+
+ def Execute(self, name, op):
+ """Executes an operation on a virtual volume.
+
+ See L{_Base.Execute}.
+
+ """
+ if op == constants.SO_FIX_CONSISTENCY:
+ return self._RemoveMissing(name)
+
+ return _LvmBase.Execute(self, name, op)
+
# Lookup table for storage types
_STORAGE_TYPES = {