ConfigObject docstring, close an open parenthesis
[ganeti-local] / lib / objects.py
index f1bc5c9..1fc2db3 100644 (file)
@@ -29,6 +29,7 @@ pass to and from external parties.
 
 import ConfigParser
 import re
 
 import ConfigParser
 import re
+import copy
 from cStringIO import StringIO
 
 from ganeti import errors
 from cStringIO import StringIO
 
 from ganeti import errors
@@ -49,7 +50,7 @@ class ConfigObject(object):
       as None instead of raising an error
 
   Classes derived from this must always declare __slots__ (we use many
       as None instead of raising an error
 
   Classes derived from this must always declare __slots__ (we use many
-  config objects and the memory reduction is useful.
+  config objects and the memory reduction is useful)
 
   """
   __slots__ = []
 
   """
   __slots__ = []
@@ -273,7 +274,7 @@ class NIC(ConfigObject):
 class Disk(ConfigObject):
   """Config object representing a block device."""
   __slots__ = ["dev_type", "logical_id", "physical_id",
 class Disk(ConfigObject):
   """Config object representing a block device."""
   __slots__ = ["dev_type", "logical_id", "physical_id",
-               "children", "iv_name", "size"]
+               "children", "iv_name", "size", "mode"]
 
   def CreateOnSecondary(self):
     """Test if this device needs to be created on a secondary node."""
 
   def CreateOnSecondary(self):
     """Test if this device needs to be created on a secondary node."""
@@ -503,68 +504,69 @@ class Instance(TaggableObject):
     "name",
     "primary_node",
     "os",
     "name",
     "primary_node",
     "os",
+    "hypervisor",
+    "hvparams",
+    "beparams",
     "status",
     "status",
-    "memory",
-    "vcpus",
     "nics",
     "disks",
     "disk_template",
     "network_port",
     "nics",
     "disks",
     "disk_template",
     "network_port",
-    "kernel_path",
-    "initrd_path",
-    "hvm_boot_order",
-    "hvm_acpi",
-    "hvm_pae",
-    "hvm_cdrom_image_path",
-    "hvm_nic_type",
-    "hvm_disk_type",
-    "vnc_bind_address",
     "serial_no",
     ]
 
   def _ComputeSecondaryNodes(self):
     """Compute the list of secondary nodes.
 
     "serial_no",
     ]
 
   def _ComputeSecondaryNodes(self):
     """Compute the list of secondary nodes.
 
+    This is a simple wrapper over _ComputeAllNodes.
+
+    """
+    all_nodes = set(self._ComputeAllNodes())
+    all_nodes.discard(self.primary_node)
+    return tuple(all_nodes)
+
+  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
+                             "List of secondary nodes")
+
+  def _ComputeAllNodes(self):
+    """Compute the list of all nodes.
+
     Since the data is already there (in the drbd disks), keeping it as
     a separate normal attribute is redundant and if not properly
     synchronised can cause problems. Thus it's better to compute it
     dynamically.
 
     """
     Since the data is already there (in the drbd disks), keeping it as
     a separate normal attribute is redundant and if not properly
     synchronised can cause problems. Thus it's better to compute it
     dynamically.
 
     """
-    def _Helper(primary, sec_nodes, device):
-      """Recursively computes secondary nodes given a top device."""
+    def _Helper(nodes, device):
+      """Recursively computes nodes given a top device."""
       if device.dev_type in constants.LDS_DRBD:
       if device.dev_type in constants.LDS_DRBD:
-        nodea, nodeb, dummy = device.logical_id[:3]
-        if nodea == primary:
-          candidate = nodeb
-        else:
-          candidate = nodea
-        if candidate not in sec_nodes:
-          sec_nodes.append(candidate)
+        nodea, nodeb = device.logical_id[:2]
+        nodes.add(nodea)
+        nodes.add(nodeb)
       if device.children:
         for child in device.children:
       if device.children:
         for child in device.children:
-          _Helper(primary, sec_nodes, child)
+          _Helper(nodes, child)
 
 
-    secondary_nodes = []
+    all_nodes = set()
+    all_nodes.add(self.primary_node)
     for device in self.disks:
     for device in self.disks:
-      _Helper(self.primary_node, secondary_nodes, device)
-    return tuple(secondary_nodes)
+      _Helper(all_nodes, device)
+    return tuple(all_nodes)
 
 
-  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
-                             "List of secondary nodes")
+  all_nodes = property(_ComputeAllNodes, None, None,
+                       "List of all nodes of the instance")
 
   def MapLVsByNode(self, lvmap=None, devs=None, node=None):
     """Provide a mapping of nodes to LVs this instance owns.
 
 
   def MapLVsByNode(self, lvmap=None, devs=None, node=None):
     """Provide a mapping of nodes to LVs this instance owns.
 
-    This function figures out what logical volumes should belong on which
-    nodes, recursing through a device tree.
+    This function figures out what logical volumes should belong on
+    which nodes, recursing through a device tree.
 
 
-    Args:
-      lvmap: (optional) a dictionary to receive the 'node' : ['lv', ...] data.
+    @param lvmap: optional dictionary to receive the
+        'node' : ['lv', ...] data.
 
 
-    Returns:
-      None if lvmap arg is given.
-      Otherwise, { 'nodename' : ['volume1', 'volume2', ...], ... }
+    @return: None if lvmap arg is given, otherwise, a dictionary
+        of the form { 'nodename' : ['volume1', 'volume2', ...], ... }
 
     """
     if node == None:
 
     """
     if node == None:
@@ -586,12 +588,6 @@ class Instance(TaggableObject):
         lvmap[node].append(dev.logical_id[1])
 
       elif dev.dev_type in constants.LDS_DRBD:
         lvmap[node].append(dev.logical_id[1])
 
       elif dev.dev_type in constants.LDS_DRBD:
-        if dev.logical_id[0] not in lvmap:
-          lvmap[dev.logical_id[0]] = []
-
-        if dev.logical_id[1] not in lvmap:
-          lvmap[dev.logical_id[1]] = []
-
         if dev.children:
           self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
           self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
         if dev.children:
           self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
           self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
@@ -601,17 +597,26 @@ class Instance(TaggableObject):
 
     return ret
 
 
     return ret
 
-  def FindDisk(self, name):
-    """Find a disk given having a specified name.
+  def FindDisk(self, idx):
+    """Find a disk given having a specified index.
 
 
-    This will return the disk which has the given iv_name.
+    This is just a wrapper that does validation of the index.
 
 
-    """
-    for disk in self.disks:
-      if disk.iv_name == name:
-        return disk
+    @type idx: int
+    @param idx: the disk index
+    @rtype: L{Disk}
+    @return: the corresponding disk
+    @raise errors.OpPrereqError: when the given index is not valid
 
 
-    return None
+    """
+    try:
+      idx = int(idx)
+      return self.disks[idx]
+    except ValueError, err:
+      raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err))
+    except IndexError:
+      raise errors.OpPrereqError("Invalid disk index: %d (instace has disks"
+                                 " 0 to %d" % (idx, len(self.disks)))
 
   def ToDict(self):
     """Instance-specific conversion to standard python types.
 
   def ToDict(self):
     """Instance-specific conversion to standard python types.
@@ -648,7 +653,7 @@ class OS(ConfigObject):
     "name",
     "path",
     "status",
     "name",
     "path",
     "status",
-    "api_version",
+    "api_versions",
     "create_script",
     "export_script",
     "import_script",
     "create_script",
     "export_script",
     "import_script",
@@ -682,6 +687,8 @@ class Node(TaggableObject):
     "primary_ip",
     "secondary_ip",
     "serial_no",
     "primary_ip",
     "secondary_ip",
     "serial_no",
+    "master_candidate",
+    "offline",
     ]
 
 
     ]
 
 
@@ -695,12 +702,16 @@ class Cluster(TaggableObject):
     "mac_prefix",
     "volume_group_name",
     "default_bridge",
     "mac_prefix",
     "volume_group_name",
     "default_bridge",
-    "hypervisor",
+    "default_hypervisor",
     "master_node",
     "master_ip",
     "master_netdev",
     "cluster_name",
     "file_storage_dir",
     "master_node",
     "master_ip",
     "master_netdev",
     "cluster_name",
     "file_storage_dir",
+    "enabled_hypervisors",
+    "hvparams",
+    "beparams",
+    "candidate_pool_size",
     ]
 
   def ToDict(self):
     ]
 
   def ToDict(self):
@@ -721,6 +732,48 @@ class Cluster(TaggableObject):
       obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
     return obj
 
       obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
     return obj
 
+  @staticmethod
+  def FillDict(defaults_dict, custom_dict):
+    """Basic function to apply settings on top a default dict.
+
+    @type defaults_dict: dict
+    @param defaults_dict: dictionary holding the default values
+    @type custom_dict: dict
+    @param custom_dict: dictionary holding customized value
+    @rtype: dict
+    @return: dict with the 'full' values
+
+    """
+    ret_dict = copy.deepcopy(defaults_dict)
+    ret_dict.update(custom_dict)
+    return ret_dict
+
+  def FillHV(self, instance):
+    """Fill an instance's hvparams dict.
+
+    @type instance: object
+    @param instance: the instance parameter to fill
+    @rtype: dict
+    @return: a copy of the instance's hvparams with missing keys filled from
+        the cluster defaults
+
+    """
+    return self.FillDict(self.hvparams.get(instance.hypervisor, {}),
+                         instance.hvparams)
+
+  def FillBE(self, instance):
+    """Fill an instance's beparams dict.
+
+    @type instance: object
+    @param instance: the instance parameter to fill
+    @rtype: dict
+    @return: a copy of the instance's beparams with missing keys filled from
+        the cluster defaults
+
+    """
+    return self.FillDict(self.beparams.get(constants.BEGR_DEFAULT, {}),
+                         instance.beparams)
+
 
 class SerializableConfigParser(ConfigParser.SafeConfigParser):
   """Simple wrapper over ConfigParse that allows serialization.
 
 class SerializableConfigParser(ConfigParser.SafeConfigParser):
   """Simple wrapper over ConfigParse that allows serialization.