QA: remove the --default-hypervisor option
[ganeti-local] / lib / storage.py
index 64d5f9f..e589dd8 100644 (file)
@@ -31,13 +31,6 @@ from ganeti import constants
 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))
 
@@ -57,6 +50,31 @@ class _Base:
     """
     raise NotImplementedError()
 
+  def Modify(self, name, changes):
+    """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):
   """File storage unit.
@@ -102,24 +120,25 @@ class FileStorage(_Base):
     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:
+    if constants.SF_FREE in fields:
       fsfree = utils.GetFreeFilesystemSpace(path)
     else:
       fsfree = 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:
+      elif field_name == constants.SF_FREE:
         values.append(fsfree)
 
       else:
@@ -293,25 +312,101 @@ class LvmPvStorage(_LvmBase):
       return False
 
   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", _GetAllocatable),
     ]
 
+  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),
     ]
 
+  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 = {