Merge branch 'devel-2.7'
[ganeti-local] / lib / objects.py
index a852593..fa811a6 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
 #
 #
 
-# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Google Inc.
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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
 #
 # 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
@@ -29,7 +29,7 @@ pass to and from external parties.
 # pylint: disable=E0203,W0201,R0902
 
 # E0203: Access to member %r before its definition, since we use
 # pylint: disable=E0203,W0201,R0902
 
 # E0203: Access to member %r before its definition, since we use
-# objects.py which doesn't explicitely initialise its members
+# objects.py which doesn't explicitly initialise its members
 
 # W0201: Attribute '%s' defined outside __init__
 
 
 # W0201: Attribute '%s' defined outside __init__
 
@@ -38,19 +38,21 @@ pass to and from external parties.
 import ConfigParser
 import re
 import copy
 import ConfigParser
 import re
 import copy
+import logging
 import time
 from cStringIO import StringIO
 
 from ganeti import errors
 from ganeti import constants
 from ganeti import netutils
 import time
 from cStringIO import StringIO
 
 from ganeti import errors
 from ganeti import constants
 from ganeti import netutils
+from ganeti import outils
 from ganeti import utils
 
 from socket import AF_INET
 
 
 __all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
 from ganeti import utils
 
 from socket import AF_INET
 
 
 __all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
-           "OS", "Node", "NodeGroup", "Cluster", "FillDict"]
+           "OS", "Node", "NodeGroup", "Cluster", "FillDict", "Network"]
 
 _TIMESTAMPS = ["ctime", "mtime"]
 _UUID = ["uuid"]
 
 _TIMESTAMPS = ["ctime", "mtime"]
 _UUID = ["uuid"]
@@ -191,7 +193,7 @@ def MakeEmptyIPolicy():
     ])
 
 
     ])
 
 
-class ConfigObject(object):
+class ConfigObject(outils.ValidatedSlots):
   """A generic config object.
 
   It has the following properties:
   """A generic config object.
 
   It has the following properties:
@@ -206,34 +208,22 @@ class ConfigObject(object):
   """
   __slots__ = []
 
   """
   __slots__ = []
 
-  def __init__(self, **kwargs):
-    for k, v in kwargs.iteritems():
-      setattr(self, k, v)
-
   def __getattr__(self, name):
   def __getattr__(self, name):
-    if name not in self._all_slots():
+    if name not in self.GetAllSlots():
       raise AttributeError("Invalid object attribute %s.%s" %
                            (type(self).__name__, name))
     return None
 
   def __setstate__(self, state):
       raise AttributeError("Invalid object attribute %s.%s" %
                            (type(self).__name__, name))
     return None
 
   def __setstate__(self, state):
-    slots = self._all_slots()
+    slots = self.GetAllSlots()
     for name in state:
       if name in slots:
         setattr(self, name, state[name])
 
     for name in state:
       if name in slots:
         setattr(self, name, state[name])
 
-  @classmethod
-  def _all_slots(cls):
-    """Compute the list of all declared slots for a class.
+  def Validate(self):
+    """Validates the slots.
 
     """
 
     """
-    slots = []
-    for parent in cls.__mro__:
-      slots.extend(getattr(parent, "__slots__", []))
-    return slots
-
-  #: Public getter for the defined slots
-  GetAllSlots = _all_slots
 
   def ToDict(self):
     """Convert to a dict holding only standard python types.
 
   def ToDict(self):
     """Convert to a dict holding only standard python types.
@@ -246,7 +236,7 @@ class ConfigObject(object):
 
     """
     result = {}
 
     """
     result = {}
-    for name in self._all_slots():
+    for name in self.GetAllSlots():
       value = getattr(self, name, None)
       if value is not None:
         result[name] = value
       value = getattr(self, name, None)
       if value is not None:
         result[name] = value
@@ -274,47 +264,6 @@ class ConfigObject(object):
     obj = cls(**val_str) # pylint: disable=W0142
     return obj
 
     obj = cls(**val_str) # pylint: disable=W0142
     return obj
 
-  @staticmethod
-  def _ContainerToDicts(container):
-    """Convert the elements of a container to standard python types.
-
-    This method converts a container with elements derived from
-    ConfigData to standard python types. If the container is a dict,
-    we don't touch the keys, only the values.
-
-    """
-    if isinstance(container, dict):
-      ret = dict([(k, v.ToDict()) for k, v in container.iteritems()])
-    elif isinstance(container, (list, tuple, set, frozenset)):
-      ret = [elem.ToDict() for elem in container]
-    else:
-      raise TypeError("Invalid type %s passed to _ContainerToDicts" %
-                      type(container))
-    return ret
-
-  @staticmethod
-  def _ContainerFromDicts(source, c_type, e_type):
-    """Convert a container from standard python types.
-
-    This method converts a container with standard python types to
-    ConfigData objects. If the container is a dict, we don't touch the
-    keys, only the values.
-
-    """
-    if not isinstance(c_type, type):
-      raise TypeError("Container type %s passed to _ContainerFromDicts is"
-                      " not a type" % type(c_type))
-    if source is None:
-      source = c_type()
-    if c_type is dict:
-      ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
-    elif c_type in (list, tuple, set, frozenset):
-      ret = c_type([e_type.FromDict(elem) for elem in source])
-    else:
-      raise TypeError("Invalid container type %s passed to"
-                      " _ContainerFromDicts" % c_type)
-    return ret
-
   def Copy(self):
     """Makes a deep copy of the current object and its children.
 
   def Copy(self):
     """Makes a deep copy of the current object and its children.
 
@@ -431,7 +380,7 @@ class MasterNetworkParameters(ConfigObject):
     "ip",
     "netmask",
     "netdev",
     "ip",
     "netmask",
     "netdev",
-    "ip_family"
+    "ip_family",
     ]
 
 
     ]
 
 
@@ -443,6 +392,7 @@ class ConfigData(ConfigObject):
     "nodes",
     "nodegroups",
     "instances",
     "nodes",
     "nodegroups",
     "instances",
+    "networks",
     "serial_no",
     ] + _TIMESTAMPS
 
     "serial_no",
     ] + _TIMESTAMPS
 
@@ -455,8 +405,8 @@ class ConfigData(ConfigObject):
     """
     mydict = super(ConfigData, self).ToDict()
     mydict["cluster"] = mydict["cluster"].ToDict()
     """
     mydict = super(ConfigData, self).ToDict()
     mydict["cluster"] = mydict["cluster"].ToDict()
-    for key in "nodes", "instances", "nodegroups":
-      mydict[key] = self._ContainerToDicts(mydict[key])
+    for key in "nodes", "instances", "nodegroups", "networks":
+      mydict[key] = outils.ContainerToDicts(mydict[key])
 
     return mydict
 
 
     return mydict
 
@@ -467,9 +417,12 @@ class ConfigData(ConfigObject):
     """
     obj = super(ConfigData, cls).FromDict(val)
     obj.cluster = Cluster.FromDict(obj.cluster)
     """
     obj = super(ConfigData, cls).FromDict(val)
     obj.cluster = Cluster.FromDict(obj.cluster)
-    obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
-    obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
-    obj.nodegroups = cls._ContainerFromDicts(obj.nodegroups, dict, NodeGroup)
+    obj.nodes = outils.ContainerFromDicts(obj.nodes, dict, Node)
+    obj.instances = \
+      outils.ContainerFromDicts(obj.instances, dict, Instance)
+    obj.nodegroups = \
+      outils.ContainerFromDicts(obj.nodegroups, dict, NodeGroup)
+    obj.networks = outils.ContainerFromDicts(obj.networks, dict, Network)
     return obj
 
   def HasAnyDiskOfType(self, dev_type):
     return obj
 
   def HasAnyDiskOfType(self, dev_type):
@@ -506,11 +459,15 @@ class ConfigData(ConfigObject):
       # gives a good approximation.
       if self.HasAnyDiskOfType(constants.LD_DRBD8):
         self.cluster.drbd_usermode_helper = constants.DEFAULT_DRBD_HELPER
       # gives a good approximation.
       if self.HasAnyDiskOfType(constants.LD_DRBD8):
         self.cluster.drbd_usermode_helper = constants.DEFAULT_DRBD_HELPER
+    if self.networks is None:
+      self.networks = {}
+    for network in self.networks.values():
+      network.UpgradeConfig()
 
 
 class NIC(ConfigObject):
   """Config object representing a network card."""
 
 
 class NIC(ConfigObject):
   """Config object representing a network card."""
-  __slots__ = ["mac", "ip", "nicparams"]
+  __slots__ = ["mac", "ip", "network", "nicparams", "netinfo"]
 
   @classmethod
   def CheckParameterSyntax(cls, nicparams):
 
   @classmethod
   def CheckParameterSyntax(cls, nicparams):
@@ -521,15 +478,14 @@ class NIC(ConfigObject):
     @raise errors.ConfigurationError: when a parameter is not valid
 
     """
     @raise errors.ConfigurationError: when a parameter is not valid
 
     """
-    if (nicparams[constants.NIC_MODE] not in constants.NIC_VALID_MODES and
-        nicparams[constants.NIC_MODE] != constants.VALUE_AUTO):
-      err = "Invalid nic mode: %s" % nicparams[constants.NIC_MODE]
-      raise errors.ConfigurationError(err)
+    mode = nicparams[constants.NIC_MODE]
+    if (mode not in constants.NIC_VALID_MODES and
+        mode != constants.VALUE_AUTO):
+      raise errors.ConfigurationError("Invalid NIC mode '%s'" % mode)
 
 
-    if (nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED and
+    if (mode == constants.NIC_MODE_BRIDGED and
         not nicparams[constants.NIC_LINK]):
         not nicparams[constants.NIC_LINK]):
-      err = "Missing bridged nic link"
-      raise errors.ConfigurationError(err)
+      raise errors.ConfigurationError("Missing bridged NIC link")
 
 
 class Disk(ConfigObject):
 
 
 class Disk(ConfigObject):
@@ -609,7 +565,8 @@ class Disk(ConfigObject):
 
     """
     if self.dev_type in [constants.LD_LV, constants.LD_FILE,
 
     """
     if self.dev_type in [constants.LD_LV, constants.LD_FILE,
-                         constants.LD_BLOCKDEV, constants.LD_RBD]:
+                         constants.LD_BLOCKDEV, constants.LD_RBD,
+                         constants.LD_EXT]:
       result = [node]
     elif self.dev_type in constants.LDS_DRBD:
       result = [self.logical_id[0], self.logical_id[1]]
       result = [node]
     elif self.dev_type in constants.LDS_DRBD:
       result = [self.logical_id[0], self.logical_id[1]]
@@ -685,7 +642,7 @@ class Disk(ConfigObject):
 
     """
     if self.dev_type in (constants.LD_LV, constants.LD_FILE,
 
     """
     if self.dev_type in (constants.LD_LV, constants.LD_FILE,
-                         constants.LD_RBD):
+                         constants.LD_RBD, constants.LD_EXT):
       self.size += amount
     elif self.dev_type == constants.LD_DRBD8:
       if self.children:
       self.size += amount
     elif self.dev_type == constants.LD_DRBD8:
       if self.children:
@@ -775,7 +732,7 @@ class Disk(ConfigObject):
     for attr in ("children",):
       alist = bo.get(attr, None)
       if alist:
     for attr in ("children",):
       alist = bo.get(attr, None)
       if alist:
-        bo[attr] = self._ContainerToDicts(alist)
+        bo[attr] = outils.ContainerToDicts(alist)
     return bo
 
   @classmethod
     return bo
 
   @classmethod
@@ -785,7 +742,7 @@ class Disk(ConfigObject):
     """
     obj = super(Disk, cls).FromDict(val)
     if obj.children:
     """
     obj = super(Disk, cls).FromDict(val)
     if obj.children:
-      obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
+      obj.children = outils.ContainerFromDicts(obj.children, list, Disk)
     if obj.logical_id and isinstance(obj.logical_id, list):
       obj.logical_id = tuple(obj.logical_id)
     if obj.physical_id and isinstance(obj.physical_id, list):
     if obj.logical_id and isinstance(obj.logical_id, list):
       obj.logical_id = tuple(obj.logical_id)
     if obj.physical_id and isinstance(obj.physical_id, list):
@@ -874,7 +831,7 @@ class Disk(ConfigObject):
     result = list()
     dt_params = disk_params[disk_template]
     if disk_template == constants.DT_DRBD8:
     result = list()
     dt_params = disk_params[disk_template]
     if disk_template == constants.DT_DRBD8:
-      drbd_params = {
+      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_DRBD8], {
         constants.LDP_RESYNC_RATE: dt_params[constants.DRBD_RESYNC_RATE],
         constants.LDP_BARRIERS: dt_params[constants.DRBD_DISK_BARRIERS],
         constants.LDP_NO_META_FLUSH: dt_params[constants.DRBD_META_BARRIERS],
         constants.LDP_RESYNC_RATE: dt_params[constants.DRBD_RESYNC_RATE],
         constants.LDP_BARRIERS: dt_params[constants.DRBD_DISK_BARRIERS],
         constants.LDP_NO_META_FLUSH: dt_params[constants.DRBD_META_BARRIERS],
@@ -887,56 +844,36 @@ class Disk(ConfigObject):
         constants.LDP_DELAY_TARGET: dt_params[constants.DRBD_DELAY_TARGET],
         constants.LDP_MAX_RATE: dt_params[constants.DRBD_MAX_RATE],
         constants.LDP_MIN_RATE: dt_params[constants.DRBD_MIN_RATE],
         constants.LDP_DELAY_TARGET: dt_params[constants.DRBD_DELAY_TARGET],
         constants.LDP_MAX_RATE: dt_params[constants.DRBD_MAX_RATE],
         constants.LDP_MIN_RATE: dt_params[constants.DRBD_MIN_RATE],
-        }
-
-      drbd_params = \
-        FillDict(constants.DISK_LD_DEFAULTS[constants.LD_DRBD8],
-                 drbd_params)
-
-      result.append(drbd_params)
+        }))
 
       # data LV
 
       # data LV
-      data_params = {
+      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
         constants.LDP_STRIPES: dt_params[constants.DRBD_DATA_STRIPES],
         constants.LDP_STRIPES: dt_params[constants.DRBD_DATA_STRIPES],
-        }
-      data_params = \
-        FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV],
-                 data_params)
-      result.append(data_params)
+        }))
 
       # metadata LV
 
       # metadata LV
-      meta_params = {
+      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
         constants.LDP_STRIPES: dt_params[constants.DRBD_META_STRIPES],
         constants.LDP_STRIPES: dt_params[constants.DRBD_META_STRIPES],
-        }
-      meta_params = \
-        FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV],
-                 meta_params)
-      result.append(meta_params)
+        }))
 
 
-    elif (disk_template == constants.DT_FILE or
-          disk_template == constants.DT_SHARED_FILE):
+    elif disk_template in (constants.DT_FILE, constants.DT_SHARED_FILE):
       result.append(constants.DISK_LD_DEFAULTS[constants.LD_FILE])
 
     elif disk_template == constants.DT_PLAIN:
       result.append(constants.DISK_LD_DEFAULTS[constants.LD_FILE])
 
     elif disk_template == constants.DT_PLAIN:
-      params = {
+      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
         constants.LDP_STRIPES: dt_params[constants.LV_STRIPES],
         constants.LDP_STRIPES: dt_params[constants.LV_STRIPES],
-        }
-      params = \
-        FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV],
-                 params)
-      result.append(params)
+        }))
 
     elif disk_template == constants.DT_BLOCK:
       result.append(constants.DISK_LD_DEFAULTS[constants.LD_BLOCKDEV])
 
     elif disk_template == constants.DT_RBD:
 
     elif disk_template == constants.DT_BLOCK:
       result.append(constants.DISK_LD_DEFAULTS[constants.LD_BLOCKDEV])
 
     elif disk_template == constants.DT_RBD:
-      params = {
-        constants.LDP_POOL: dt_params[constants.RBD_POOL]
-        }
-      params = \
-        FillDict(constants.DISK_LD_DEFAULTS[constants.LD_RBD],
-                 params)
-      result.append(params)
+      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_RBD], {
+        constants.LDP_POOL: dt_params[constants.RBD_POOL],
+        }))
+
+    elif disk_template == constants.DT_EXT:
+      result.append(constants.DISK_LD_DEFAULTS[constants.LD_EXT])
 
     return result
 
 
     return result
 
@@ -1053,7 +990,7 @@ class Instance(TaggableObject):
     return tuple(all_nodes)
 
   secondary_nodes = property(_ComputeSecondaryNodes, None, None,
     return tuple(all_nodes)
 
   secondary_nodes = property(_ComputeSecondaryNodes, None, None,
-                             "List of secondary nodes")
+                             "List of names of secondary nodes")
 
   def _ComputeAllNodes(self):
     """Compute the list of all nodes.
 
   def _ComputeAllNodes(self):
     """Compute the list of all nodes.
@@ -1081,7 +1018,7 @@ class Instance(TaggableObject):
     return tuple(all_nodes)
 
   all_nodes = property(_ComputeAllNodes, None, None,
     return tuple(all_nodes)
 
   all_nodes = property(_ComputeAllNodes, None, None,
-                       "List of all nodes of the instance")
+                       "List of names of all the 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.
@@ -1098,7 +1035,7 @@ class Instance(TaggableObject):
         GetVolumeList()
 
     """
         GetVolumeList()
 
     """
-    if node == None:
+    if node is None:
       node = self.primary_node
 
     if lvmap is None:
       node = self.primary_node
 
     if lvmap is None:
@@ -1163,7 +1100,7 @@ class Instance(TaggableObject):
     for attr in "nics", "disks":
       alist = bo.get(attr, None)
       if alist:
     for attr in "nics", "disks":
       alist = bo.get(attr, None)
       if alist:
-        nlist = self._ContainerToDicts(alist)
+        nlist = outils.ContainerToDicts(alist)
       else:
         nlist = []
       bo[attr] = nlist
       else:
         nlist = []
       bo[attr] = nlist
@@ -1182,8 +1119,8 @@ class Instance(TaggableObject):
     if "admin_up" in val:
       del val["admin_up"]
     obj = super(Instance, cls).FromDict(val)
     if "admin_up" in val:
       del val["admin_up"]
     obj = super(Instance, cls).FromDict(val)
-    obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
-    obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
+    obj.nics = outils.ContainerFromDicts(obj.nics, list, NIC)
+    obj.disks = outils.ContainerFromDicts(obj.disks, list, Disk)
     return obj
 
   def UpgradeConfig(self):
     return obj
 
   def UpgradeConfig(self):
@@ -1265,6 +1202,24 @@ class OS(ConfigObject):
     return cls.SplitNameVariant(name)[1]
 
 
     return cls.SplitNameVariant(name)[1]
 
 
+class ExtStorage(ConfigObject):
+  """Config object representing an External Storage Provider.
+
+  """
+  __slots__ = [
+    "name",
+    "path",
+    "create_script",
+    "remove_script",
+    "grow_script",
+    "attach_script",
+    "detach_script",
+    "setinfo_script",
+    "verify_script",
+    "supported_parameters",
+    ]
+
+
 class NodeHvState(ConfigObject):
   """Hypvervisor state on a node.
 
 class NodeHvState(ConfigObject):
   """Hypvervisor state on a node.
 
@@ -1341,6 +1296,12 @@ class Node(TaggableObject):
 
     if self.ndparams is None:
       self.ndparams = {}
 
     if self.ndparams is None:
       self.ndparams = {}
+    # And remove any global parameter
+    for key in constants.NDC_GLOBALS:
+      if key in self.ndparams:
+        logging.warning("Ignoring %s node parameter for node %s",
+                        key, self.name)
+        del self.ndparams[key]
 
     if self.powered is None:
       self.powered = True
 
     if self.powered is None:
       self.powered = True
@@ -1353,12 +1314,12 @@ class Node(TaggableObject):
 
     hv_state = data.get("hv_state", None)
     if hv_state is not None:
 
     hv_state = data.get("hv_state", None)
     if hv_state is not None:
-      data["hv_state"] = self._ContainerToDicts(hv_state)
+      data["hv_state"] = outils.ContainerToDicts(hv_state)
 
     disk_state = data.get("disk_state", None)
     if disk_state is not None:
       data["disk_state"] = \
 
     disk_state = data.get("disk_state", None)
     if disk_state is not None:
       data["disk_state"] = \
-        dict((key, self._ContainerToDicts(value))
+        dict((key, outils.ContainerToDicts(value))
              for (key, value) in disk_state.items())
 
     return data
              for (key, value) in disk_state.items())
 
     return data
@@ -1371,11 +1332,12 @@ class Node(TaggableObject):
     obj = super(Node, cls).FromDict(val)
 
     if obj.hv_state is not None:
     obj = super(Node, cls).FromDict(val)
 
     if obj.hv_state is not None:
-      obj.hv_state = cls._ContainerFromDicts(obj.hv_state, dict, NodeHvState)
+      obj.hv_state = \
+        outils.ContainerFromDicts(obj.hv_state, dict, NodeHvState)
 
     if obj.disk_state is not None:
       obj.disk_state = \
 
     if obj.disk_state is not None:
       obj.disk_state = \
-        dict((key, cls._ContainerFromDicts(value, dict, NodeDiskState))
+        dict((key, outils.ContainerFromDicts(value, dict, NodeDiskState))
              for (key, value) in obj.disk_state.items())
 
     return obj
              for (key, value) in obj.disk_state.items())
 
     return obj
@@ -1393,6 +1355,7 @@ class NodeGroup(TaggableObject):
     "hv_state_static",
     "disk_state_static",
     "alloc_policy",
     "hv_state_static",
     "disk_state_static",
     "alloc_policy",
+    "networks",
     ] + _TIMESTAMPS + _UUID
 
   def ToDict(self):
     ] + _TIMESTAMPS + _UUID
 
   def ToDict(self):
@@ -1440,6 +1403,9 @@ class NodeGroup(TaggableObject):
     if self.ipolicy is None:
       self.ipolicy = MakeEmptyIPolicy()
 
     if self.ipolicy is None:
       self.ipolicy = MakeEmptyIPolicy()
 
+    if self.networks is None:
+      self.networks = {}
+
   def FillND(self, node):
     """Return filled out ndparams for L{objects.Node}
 
   def FillND(self, node):
     """Return filled out ndparams for L{objects.Node}
 
@@ -1557,8 +1523,8 @@ class Cluster(TaggableObject):
     # code can be removed once upgrading straight from 2.0 is deprecated.
     if self.default_hypervisor is not None:
       self.enabled_hypervisors = ([self.default_hypervisor] +
     # code can be removed once upgrading straight from 2.0 is deprecated.
     if self.default_hypervisor is not None:
       self.enabled_hypervisors = ([self.default_hypervisor] +
-        [hvname for hvname in self.enabled_hypervisors
-         if hvname != self.default_hypervisor])
+                                  [hvname for hvname in self.enabled_hypervisors
+                                   if hvname != self.default_hypervisor])
       self.default_hypervisor = None
 
     # maintain_node_health added after 2.1.1
       self.default_hypervisor = None
 
     # maintain_node_health added after 2.1.1
@@ -1628,7 +1594,14 @@ class Cluster(TaggableObject):
 
     """
     mydict = super(Cluster, self).ToDict()
 
     """
     mydict = super(Cluster, self).ToDict()
-    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
+
+    if self.tcpudp_port_pool is None:
+      tcpudp_port_pool = []
+    else:
+      tcpudp_port_pool = list(self.tcpudp_port_pool)
+
+    mydict["tcpudp_port_pool"] = tcpudp_port_pool
+
     return mydict
 
   @classmethod
     return mydict
 
   @classmethod
@@ -1637,8 +1610,12 @@ class Cluster(TaggableObject):
 
     """
     obj = super(Cluster, cls).FromDict(val)
 
     """
     obj = super(Cluster, cls).FromDict(val)
-    if not isinstance(obj.tcpudp_port_pool, set):
+
+    if obj.tcpudp_port_pool is None:
+      obj.tcpudp_port_pool = set()
+    elif not isinstance(obj.tcpudp_port_pool, set):
       obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
       obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
+
     return obj
 
   def SimpleFillDP(self, diskparams):
     return obj
 
   def SimpleFillDP(self, diskparams):
@@ -1929,7 +1906,7 @@ class _QueryResponseBase(ConfigObject):
 
     """
     mydict = super(_QueryResponseBase, self).ToDict()
 
     """
     mydict = super(_QueryResponseBase, self).ToDict()
-    mydict["fields"] = self._ContainerToDicts(mydict["fields"])
+    mydict["fields"] = outils.ContainerToDicts(mydict["fields"])
     return mydict
 
   @classmethod
     return mydict
 
   @classmethod
@@ -1938,7 +1915,8 @@ class _QueryResponseBase(ConfigObject):
 
     """
     obj = super(_QueryResponseBase, cls).FromDict(val)
 
     """
     obj = super(_QueryResponseBase, cls).FromDict(val)
-    obj.fields = cls._ContainerFromDicts(obj.fields, list, QueryFieldDefinition)
+    obj.fields = \
+      outils.ContainerFromDicts(obj.fields, list, QueryFieldDefinition)
     return obj
 
 
     return obj
 
 
@@ -1970,8 +1948,7 @@ class QueryFieldsResponse(_QueryResponseBase):
   @ivar fields: List of L{QueryFieldDefinition} objects
 
   """
   @ivar fields: List of L{QueryFieldDefinition} objects
 
   """
-  __slots__ = [
-    ]
+  __slots__ = []
 
 
 class MigrationStatus(ConfigObject):
 
 
 class MigrationStatus(ConfigObject):
@@ -2024,6 +2001,62 @@ class InstanceConsole(ConfigObject):
     return True
 
 
     return True
 
 
+class Network(TaggableObject):
+  """Object representing a network definition for ganeti.
+
+  """
+  __slots__ = [
+    "name",
+    "serial_no",
+    "mac_prefix",
+    "network",
+    "network6",
+    "gateway",
+    "gateway6",
+    "reservations",
+    "ext_reservations",
+    ] + _TIMESTAMPS + _UUID
+
+  def HooksDict(self, prefix=""):
+    """Export a dictionary used by hooks with a network's information.
+
+    @type prefix: String
+    @param prefix: Prefix to prepend to the dict entries
+
+    """
+    result = {
+      "%sNETWORK_NAME" % prefix: self.name,
+      "%sNETWORK_UUID" % prefix: self.uuid,
+      "%sNETWORK_TAGS" % prefix: " ".join(self.tags),
+    }
+    if self.network:
+      result["%sNETWORK_SUBNET" % prefix] = self.network
+    if self.gateway:
+      result["%sNETWORK_GATEWAY" % prefix] = self.gateway
+    if self.network6:
+      result["%sNETWORK_SUBNET6" % prefix] = self.network6
+    if self.gateway6:
+      result["%sNETWORK_GATEWAY6" % prefix] = self.gateway6
+    if self.mac_prefix:
+      result["%sNETWORK_MAC_PREFIX" % prefix] = self.mac_prefix
+
+    return result
+
+  @classmethod
+  def FromDict(cls, val):
+    """Custom function for networks.
+
+    Remove deprecated network_type and family.
+
+    """
+    if "network_type" in val:
+      del val["network_type"]
+    if "family" in val:
+      del val["family"]
+    obj = super(Network, cls).FromDict(val)
+    return obj
+
+
 class SerializableConfigParser(ConfigParser.SafeConfigParser):
   """Simple wrapper over ConfigParse that allows serialization.
 
 class SerializableConfigParser(ConfigParser.SafeConfigParser):
   """Simple wrapper over ConfigParse that allows serialization.
 
@@ -2045,3 +2078,41 @@ class SerializableConfigParser(ConfigParser.SafeConfigParser):
     cfp = cls()
     cfp.readfp(buf)
     return cfp
     cfp = cls()
     cfp.readfp(buf)
     return cfp
+
+
+class LvmPvInfo(ConfigObject):
+  """Information about an LVM physical volume (PV).
+
+  @type name: string
+  @ivar name: name of the PV
+  @type vg_name: string
+  @ivar vg_name: name of the volume group containing the PV
+  @type size: float
+  @ivar size: size of the PV in MiB
+  @type free: float
+  @ivar free: free space in the PV, in MiB
+  @type attributes: string
+  @ivar attributes: PV attributes
+  @type lv_list: list of strings
+  @ivar lv_list: names of the LVs hosted on the PV
+  """
+  __slots__ = [
+    "name",
+    "vg_name",
+    "size",
+    "free",
+    "attributes",
+    "lv_list"
+    ]
+
+  def IsEmpty(self):
+    """Is this PV empty?
+
+    """
+    return self.size <= (self.free + 1)
+
+  def IsAllocatable(self):
+    """Is this PV allocatable?
+
+    """
+    return ("a" in self.attributes)