Merge branch 'stable-2.9' into stable-2.10
[ganeti-local] / lib / objects.py
index 6105595..be427aa 100644 (file)
@@ -266,6 +266,10 @@ class ConfigObject(outils.ValidatedSlots):
     """Implement __repr__ for ConfigObjects."""
     return repr(self.ToDict())
 
+  def __eq__(self, other):
+    """Implement __eq__ for ConfigObjects."""
+    return isinstance(other, self.__class__) and self.ToDict() == other.ToDict()
+
   def UpgradeConfig(self):
     """Fill defaults for missing configuration values.
 
@@ -281,7 +285,7 @@ class TaggableObject(ConfigObject):
 
   """
   __slots__ = ["tags"]
-  VALID_TAG_RE = re.compile("^[\w.+*/:@-]+$")
+  VALID_TAG_RE = re.compile(r"^[\w.+*/:@-]+$")
 
   @classmethod
   def ValidateTag(cls, tag):
@@ -418,7 +422,7 @@ class ConfigData(ConfigObject):
   def HasAnyDiskOfType(self, dev_type):
     """Check if in there is at disk of the given type in the configuration.
 
-    @type dev_type: L{constants.LDS_BLOCK}
+    @type dev_type: L{constants.DTS_BLOCK}
     @param dev_type: the type to look for
     @rtype: boolean
     @return: boolean indicating if a disk of the given type was found or not
@@ -439,29 +443,26 @@ class ConfigData(ConfigObject):
       node.UpgradeConfig()
     for instance in self.instances.values():
       instance.UpgradeConfig()
+    self._UpgradeEnabledDiskTemplates()
     if self.nodegroups is None:
       self.nodegroups = {}
     for nodegroup in self.nodegroups.values():
       nodegroup.UpgradeConfig()
+      InstancePolicy.UpgradeDiskTemplates(
+        nodegroup.ipolicy, self.cluster.enabled_disk_templates)
     if self.cluster.drbd_usermode_helper is None:
-      # To decide if we set an helper let's check if at least one instance has
-      # a DRBD disk. This does not cover all the possible scenarios but it
-      # gives a good approximation.
-      if self.HasAnyDiskOfType(constants.LD_DRBD8):
+      if self.cluster.IsDiskTemplateEnabled(constants.DT_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()
-    self._UpgradeEnabledDiskTemplates()
 
   def _UpgradeEnabledDiskTemplates(self):
     """Upgrade the cluster's enabled disk templates by inspecting the currently
        enabled and/or used disk templates.
 
     """
-    # enabled_disk_templates in the cluster config were introduced in 2.8.
-    # Remove this code once upgrading from earlier versions is deprecated.
     if not self.cluster.enabled_disk_templates:
       template_set = \
         set([inst.disk_template for inst in self.instances.values()])
@@ -469,11 +470,6 @@ class ConfigData(ConfigObject):
       if self.cluster.volume_group_name:
         template_set.add(constants.DT_DRBD8)
         template_set.add(constants.DT_PLAIN)
-      # FIXME: Adapt this when dis/enabling at configure time is removed.
-      # Enable 'sharedfile', if they are enabled, even though they might
-      # currently not be used.
-      if constants.ENABLE_SHARED_FILE_STORAGE:
-        template_set.add(constants.DT_SHARED_FILE)
       # Set enabled_disk_templates to the inferred disk templates. Order them
       # according to a preference list that is based on Ganeti's history of
       # supported disk templates.
@@ -483,11 +479,14 @@ class ConfigData(ConfigObject):
           self.cluster.enabled_disk_templates.append(preferred_template)
           template_set.remove(preferred_template)
       self.cluster.enabled_disk_templates.extend(list(template_set))
+    InstancePolicy.UpgradeDiskTemplates(
+      self.cluster.ipolicy, self.cluster.enabled_disk_templates)
 
 
 class NIC(ConfigObject):
   """Config object representing a network card."""
-  __slots__ = ["name", "mac", "ip", "network", "nicparams", "netinfo"] + _UUID
+  __slots__ = ["name", "mac", "ip", "network",
+               "nicparams", "netinfo", "pci"] + _UUID
 
   @classmethod
   def CheckParameterSyntax(cls, nicparams):
@@ -510,21 +509,23 @@ class NIC(ConfigObject):
 
 class Disk(ConfigObject):
   """Config object representing a block device."""
-  __slots__ = (["name", "dev_type", "logical_id", "physical_id",
-                "children", "iv_name", "size", "mode", "params", "spindles"] +
-               _UUID)
+  __slots__ = (["name", "dev_type", "logical_id", "children", "iv_name",
+                "size", "mode", "params", "spindles", "pci"] + _UUID +
+               # dynamic_params is special. It depends on the node this instance
+               # is sent to, and should not be persisted.
+               ["dynamic_params"])
 
   def CreateOnSecondary(self):
     """Test if this device needs to be created on a secondary node."""
-    return self.dev_type in (constants.LD_DRBD8, constants.LD_LV)
+    return self.dev_type in (constants.DT_DRBD8, constants.DT_PLAIN)
 
   def AssembleOnSecondary(self):
     """Test if this device needs to be assembled on a secondary node."""
-    return self.dev_type in (constants.LD_DRBD8, constants.LD_LV)
+    return self.dev_type in (constants.DT_DRBD8, constants.DT_PLAIN)
 
   def OpenOnSecondary(self):
     """Test if this device needs to be opened on a secondary node."""
-    return self.dev_type in (constants.LD_LV,)
+    return self.dev_type in (constants.DT_PLAIN,)
 
   def StaticDevPath(self):
     """Return the device path if this device type has a static one.
@@ -537,11 +538,11 @@ class Disk(ConfigObject):
         should check that it is a valid path.
 
     """
-    if self.dev_type == constants.LD_LV:
+    if self.dev_type == constants.DT_PLAIN:
       return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
-    elif self.dev_type == constants.LD_BLOCKDEV:
+    elif self.dev_type == constants.DT_BLOCK:
       return self.logical_id[1]
-    elif self.dev_type == constants.LD_RBD:
+    elif self.dev_type == constants.DT_RBD:
       return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
     return None
 
@@ -557,14 +558,14 @@ class Disk(ConfigObject):
     -1.
 
     """
-    if self.dev_type == constants.LD_DRBD8:
+    if self.dev_type == constants.DT_DRBD8:
       return 0
     return -1
 
   def IsBasedOnDiskType(self, dev_type):
     """Check if the disk or its children are based on the given type.
 
-    @type dev_type: L{constants.LDS_BLOCK}
+    @type dev_type: L{constants.DTS_BLOCK}
     @param dev_type: the type to look for
     @rtype: boolean
     @return: boolean indicating if a device of the given type was found or not
@@ -585,11 +586,11 @@ class Disk(ConfigObject):
     devices needs to (or can) be assembled.
 
     """
-    if self.dev_type in [constants.LD_LV, constants.LD_FILE,
-                         constants.LD_BLOCKDEV, constants.LD_RBD,
-                         constants.LD_EXT]:
+    if self.dev_type in [constants.DT_PLAIN, constants.DT_FILE,
+                         constants.DT_BLOCK, constants.DT_RBD,
+                         constants.DT_EXT, constants.DT_SHARED_FILE]:
       result = [node_uuid]
-    elif self.dev_type in constants.LDS_DRBD:
+    elif self.dev_type in constants.DTS_DRBD:
       result = [self.logical_id[0], self.logical_id[1]]
       if node_uuid not in result:
         raise errors.ConfigurationError("DRBD device passed unknown node")
@@ -643,9 +644,9 @@ class Disk(ConfigObject):
     @return: a dictionary of volume-groups and the required size
 
     """
-    if self.dev_type == constants.LD_LV:
+    if self.dev_type == constants.DT_PLAIN:
       return {self.logical_id[0]: amount}
-    elif self.dev_type == constants.LD_DRBD8:
+    elif self.dev_type == constants.DT_DRBD8:
       if self.children:
         return self.children[0].ComputeGrowth(amount)
       else:
@@ -662,10 +663,11 @@ class Disk(ConfigObject):
     actual algorithms from bdev.
 
     """
-    if self.dev_type in (constants.LD_LV, constants.LD_FILE,
-                         constants.LD_RBD, constants.LD_EXT):
+    if self.dev_type in (constants.DT_PLAIN, constants.DT_FILE,
+                         constants.DT_RBD, constants.DT_EXT,
+                         constants.DT_SHARED_FILE):
       self.size += amount
-    elif self.dev_type == constants.LD_DRBD8:
+    elif self.dev_type == constants.DT_DRBD8:
       if self.children:
         self.children[0].RecordGrow(amount)
       self.size += amount
@@ -677,7 +679,7 @@ class Disk(ConfigObject):
     """Apply changes to size, spindles and mode.
 
     """
-    if self.dev_type == constants.LD_DRBD8:
+    if self.dev_type == constants.DT_DRBD8:
       if self.children:
         self.children[0].Update(size=size, mode=mode)
     else:
@@ -699,51 +701,53 @@ class Disk(ConfigObject):
         child.UnsetSize()
     self.size = 0
 
-  def SetPhysicalID(self, target_node_uuid, nodes_ip):
-    """Convert the logical ID to the physical ID.
-
-    This is used only for drbd, which needs ip/port configuration.
+  def UpdateDynamicDiskParams(self, target_node_uuid, nodes_ip):
+    """Updates the dynamic disk params for the given node.
 
-    The routine descends down and updates its children also, because
-    this helps when the only the top device is passed to the remote
-    node.
+    This is mainly used for drbd, which needs ip/port configuration.
 
     Arguments:
       - target_node_uuid: the node UUID we wish to configure for
       - nodes_ip: a mapping of node name to ip
 
-    The target_node must exist in in nodes_ip, and must be one of the
-    nodes in the logical ID for each of the DRBD devices encountered
-    in the disk tree.
+    The target_node must exist in nodes_ip, and should be one of the
+    nodes in the logical ID if this device is a DRBD device.
 
     """
     if self.children:
       for child in self.children:
-        child.SetPhysicalID(target_node_uuid, nodes_ip)
+        child.UpdateDynamicDiskParams(target_node_uuid, nodes_ip)
 
-    if self.logical_id is None and self.physical_id is not None:
-      return
-    if self.dev_type in constants.LDS_DRBD:
-      pnode_uuid, snode_uuid, port, pminor, sminor, secret = self.logical_id
+    dyn_disk_params = {}
+    if self.logical_id is not None and self.dev_type in constants.DTS_DRBD:
+      pnode_uuid, snode_uuid, _, pminor, sminor, _ = self.logical_id
       if target_node_uuid not in (pnode_uuid, snode_uuid):
-        raise errors.ConfigurationError("DRBD device not knowing node %s" %
-                                        target_node_uuid)
+        # disk object is being sent to neither the primary nor the secondary
+        # node. reset the dynamic parameters, the target node is not
+        # supposed to use them.
+        self.dynamic_params = dyn_disk_params
+        return
+
       pnode_ip = nodes_ip.get(pnode_uuid, None)
       snode_ip = nodes_ip.get(snode_uuid, None)
       if pnode_ip is None or snode_ip is None:
         raise errors.ConfigurationError("Can't find primary or secondary node"
                                         " for %s" % str(self))
-      p_data = (pnode_ip, port)
-      s_data = (snode_ip, port)
       if pnode_uuid == target_node_uuid:
-        self.physical_id = p_data + s_data + (pminor, secret)
+        dyn_disk_params[constants.DDP_LOCAL_IP] = pnode_ip
+        dyn_disk_params[constants.DDP_REMOTE_IP] = snode_ip
+        dyn_disk_params[constants.DDP_LOCAL_MINOR] = pminor
+        dyn_disk_params[constants.DDP_REMOTE_MINOR] = sminor
       else: # it must be secondary, we tested above
-        self.physical_id = s_data + p_data + (sminor, secret)
-    else:
-      self.physical_id = self.logical_id
-    return
+        dyn_disk_params[constants.DDP_LOCAL_IP] = snode_ip
+        dyn_disk_params[constants.DDP_REMOTE_IP] = pnode_ip
+        dyn_disk_params[constants.DDP_LOCAL_MINOR] = sminor
+        dyn_disk_params[constants.DDP_REMOTE_MINOR] = pminor
 
-  def ToDict(self):
+    self.dynamic_params = dyn_disk_params
+
+  # pylint: disable=W0221
+  def ToDict(self, include_dynamic_params=False):
     """Disk-specific conversion to standard python types.
 
     This replaces the children lists of objects with lists of
@@ -751,6 +755,8 @@ class Disk(ConfigObject):
 
     """
     bo = super(Disk, self).ToDict()
+    if not include_dynamic_params and "dynamic_params" in bo:
+      del bo["dynamic_params"]
 
     for attr in ("children",):
       alist = bo.get(attr, None)
@@ -768,9 +774,7 @@ class Disk(ConfigObject):
       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):
-      obj.physical_id = tuple(obj.physical_id)
-    if obj.dev_type in constants.LDS_DRBD:
+    if obj.dev_type in constants.DTS_DRBD:
       # we need a tuple of length six here
       if len(obj.logical_id) < 6:
         obj.logical_id += (None,) * (6 - len(obj.logical_id))
@@ -780,27 +784,21 @@ class Disk(ConfigObject):
     """Custom str() formatter for disks.
 
     """
-    if self.dev_type == constants.LD_LV:
+    if self.dev_type == constants.DT_PLAIN:
       val = "<LogicalVolume(/dev/%s/%s" % self.logical_id
-    elif self.dev_type in constants.LDS_DRBD:
+    elif self.dev_type in constants.DTS_DRBD:
       node_a, node_b, port, minor_a, minor_b = self.logical_id[:5]
       val = "<DRBD8("
-      if self.physical_id is None:
-        phy = "unconfigured"
-      else:
-        phy = ("configured as %s:%s %s:%s" %
-               (self.physical_id[0], self.physical_id[1],
-                self.physical_id[2], self.physical_id[3]))
 
-      val += ("hosts=%s/%d-%s/%d, port=%s, %s, " %
-              (node_a, minor_a, node_b, minor_b, port, phy))
+      val += ("hosts=%s/%d-%s/%d, port=%s, " %
+              (node_a, minor_a, node_b, minor_b, port))
       if self.children and self.children.count(None) == 0:
         val += "backend=%s, metadev=%s" % (self.children[0], self.children[1])
       else:
         val += "no local storage"
     else:
-      val = ("<Disk(type=%s, logical_id=%s, physical_id=%s, children=%s" %
-             (self.dev_type, self.logical_id, self.physical_id, self.children))
+      val = ("<Disk(type=%s, logical_id=%s, children=%s" %
+             (self.dev_type, self.logical_id, self.children))
     if self.iv_name is None:
       val += ", not visible"
     else:
@@ -834,6 +832,12 @@ class Disk(ConfigObject):
     self.params = {}
     # add here config upgrade for this disk
 
+    # map of legacy device types (mapping differing LD constants to new
+    # DT constants)
+    LEG_DEV_TYPE_MAP = {"lvm": constants.DT_PLAIN, "drbd8": constants.DT_DRBD8}
+    if self.dev_type in LEG_DEV_TYPE_MAP:
+      self.dev_type = LEG_DEV_TYPE_MAP[self.dev_type]
+
   @staticmethod
   def ComputeLDParams(disk_template, disk_params):
     """Computes Logical Disk parameters from Disk Template parameters.
@@ -856,7 +860,7 @@ class Disk(ConfigObject):
     result = list()
     dt_params = disk_params[disk_template]
     if disk_template == constants.DT_DRBD8:
-      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_DRBD8], {
+      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.DT_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],
@@ -873,33 +877,34 @@ class Disk(ConfigObject):
         }))
 
       # data LV
-      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
+      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.DT_PLAIN], {
         constants.LDP_STRIPES: dt_params[constants.DRBD_DATA_STRIPES],
         }))
 
       # metadata LV
-      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
+      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.DT_PLAIN], {
         constants.LDP_STRIPES: dt_params[constants.DRBD_META_STRIPES],
         }))
 
     elif disk_template in (constants.DT_FILE, constants.DT_SHARED_FILE):
-      result.append(constants.DISK_LD_DEFAULTS[constants.LD_FILE])
+      result.append(constants.DISK_LD_DEFAULTS[disk_template])
 
     elif disk_template == constants.DT_PLAIN:
-      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
+      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.DT_PLAIN], {
         constants.LDP_STRIPES: dt_params[constants.LV_STRIPES],
         }))
 
     elif disk_template == constants.DT_BLOCK:
-      result.append(constants.DISK_LD_DEFAULTS[constants.LD_BLOCKDEV])
+      result.append(constants.DISK_LD_DEFAULTS[constants.DT_BLOCK])
 
     elif disk_template == constants.DT_RBD:
-      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_RBD], {
+      result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.DT_RBD], {
         constants.LDP_POOL: dt_params[constants.RBD_POOL],
+        constants.LDP_ACCESS: dt_params[constants.RBD_ACCESS],
         }))
 
     elif disk_template == constants.DT_EXT:
-      result.append(constants.DISK_LD_DEFAULTS[constants.LD_EXT])
+      result.append(constants.DISK_LD_DEFAULTS[constants.DT_EXT])
 
     return result
 
@@ -912,6 +917,15 @@ class InstancePolicy(ConfigObject):
 
   """
   @classmethod
+  def UpgradeDiskTemplates(cls, ipolicy, enabled_disk_templates):
+    """Upgrades the ipolicy configuration."""
+    if constants.IPOLICY_DTS in ipolicy:
+      if not set(ipolicy[constants.IPOLICY_DTS]).issubset(
+        set(enabled_disk_templates)):
+        ipolicy[constants.IPOLICY_DTS] = list(
+          set(ipolicy[constants.IPOLICY_DTS]) & set(enabled_disk_templates))
+
+  @classmethod
   def CheckParameterSyntax(cls, ipolicy, check_std):
     """ Check the instance policy for validity.
 
@@ -1090,7 +1104,7 @@ class Instance(TaggableObject):
     """
     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.DTS_DRBD:
         nodea, nodeb = device.logical_id[:2]
         nodes.add(nodea)
         nodes.add(nodeb)
@@ -1145,10 +1159,10 @@ class Instance(TaggableObject):
       devs = self.disks
 
     for dev in devs:
-      if dev.dev_type == constants.LD_LV:
+      if dev.dev_type == constants.DT_PLAIN:
         lvmap[node_uuid].append(dev.logical_id[0] + "/" + dev.logical_id[1])
 
-      elif dev.dev_type in constants.LDS_DRBD:
+      elif dev.dev_type in constants.DTS_DRBD:
         if dev.children:
           self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
           self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
@@ -1529,6 +1543,7 @@ class Cluster(TaggableObject):
   __slots__ = [
     "serial_no",
     "rsahostkeypub",
+    "dsahostkeypub",
     "highest_used_port",
     "tcpudp_port_pool",
     "mac_prefix",