From 7736a5f2ae02b10e92f8bb4cf55f0f0c60740c83 Mon Sep 17 00:00:00 2001
From: Iustin Pop <iustin@google.com>
Date: Wed, 4 Nov 2009 17:41:04 +0100
Subject: [PATCH] Introduce 'global hypervisor parameters' support

This patch adds support for global hypervisor parameters in instance
creation, instance modification, instance query and at instance load
time.

We basically prevent any query on these parameters, discard them at load
time, and do not allow their modification. Together, this should make
any such parameters go away if existing and not allowed to be added.

Signed-off-by: Iustin Pop <iustin@google.com>
Reviewed-by: Guido Trotter <ultrotter@google.com>
---
 lib/cmdlib.py    | 30 ++++++++++++++++++++++++++----
 lib/constants.py |  4 ++++
 lib/objects.py   | 28 ++++++++++++++++++++++++----
 3 files changed, 54 insertions(+), 8 deletions(-)

diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 7f046ec1e..c1a52ffeb 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -500,6 +500,21 @@ def _CheckBooleanOpField(op, name):
   setattr(op, name, val)
 
 
+def _CheckGlobalHvParams(params):
+  """Validates that given hypervisor params are not global ones.
+
+  This will ensure that instances don't get customised versions of
+  global params.
+
+  """
+  used_globals = constants.HVC_GLOBALS.intersection(params)
+  if used_globals:
+    msg = ("The following hypervisor parameters are global and cannot"
+           " be customized at instance level, please modify them at"
+           " cluster level: %s" % ", ".join(used_globals))
+    raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
+
+
 def _CheckNodeOnline(lu, node):
   """Ensure that a given node is online.
 
@@ -4200,7 +4215,8 @@ class LUQueryInstances(NoHooksLU):
                                     "hvparams",
                                     ] + _SIMPLE_FIELDS +
                                   ["hv/%s" % name
-                                   for name in constants.HVS_PARAMETERS] +
+                                   for name in constants.HVS_PARAMETERS
+                                   if name not in constants.HVC_GLOBALS] +
                                   ["be/%s" % name
                                    for name in constants.BES_PARAMETERS])
   _FIELDS_DYNAMIC = utils.FieldSet("oper_state", "oper_ram", "status")
@@ -4295,7 +4311,7 @@ class LUQueryInstances(NoHooksLU):
     cluster = self.cfg.GetClusterInfo()
     for instance in instance_list:
       iout = []
-      i_hv = cluster.FillHV(instance)
+      i_hv = cluster.FillHV(instance, skip_globals=True)
       i_be = cluster.FillBE(instance)
       i_nicp = [objects.FillDict(cluster.nicparams[constants.PP_DEFAULT],
                                  nic.nicparams) for nic in instance.nics]
@@ -4382,7 +4398,8 @@ class LUQueryInstances(NoHooksLU):
         elif field == "hvparams":
           val = i_hv
         elif (field.startswith(HVPREFIX) and
-              field[len(HVPREFIX):] in constants.HVS_PARAMETERS):
+              field[len(HVPREFIX):] in constants.HVS_PARAMETERS and
+              field[len(HVPREFIX):] not in constants.HVC_GLOBALS):
           val = i_hv.get(field[len(HVPREFIX):], None)
         elif field == "beparams":
           val = i_be
@@ -5599,6 +5616,8 @@ class LUCreateInstance(LogicalUnit):
     hv_type = hypervisor.GetHypervisor(self.op.hypervisor)
     hv_type.CheckParameterSyntax(filled_hvp)
     self.hv_full = filled_hvp
+    # check that we don't specify global parameters on an instance
+    _CheckGlobalHvParams(self.op.hvparams)
 
     # fill and remember the beparams dict
     utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
@@ -7292,7 +7311,7 @@ class LUQueryInstanceData(NoHooksLU):
         "hypervisor": instance.hypervisor,
         "network_port": instance.network_port,
         "hv_instance": instance.hvparams,
-        "hv_actual": cluster.FillHV(instance),
+        "hv_actual": cluster.FillHV(instance, skip_globals=True),
         "be_instance": instance.beparams,
         "be_actual": cluster.FillBE(instance),
         "serial_no": instance.serial_no,
@@ -7329,6 +7348,9 @@ class LUSetInstanceParams(LogicalUnit):
             self.op.hvparams or self.op.beparams):
       raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL)
 
+    if self.op.hvparams:
+      _CheckGlobalHvParams(self.op.hvparams)
+
     # Disk validation
     disk_addremove = 0
     for disk_op, disk_dict in self.op.disks:
diff --git a/lib/constants.py b/lib/constants.py
index bab6d8d11..8a47a9dda 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -650,6 +650,10 @@ HVC_DEFAULTS = {
     },
   }
 
+HVC_GLOBALS = frozenset([
+  HV_MIGRATION_PORT,
+  ])
+
 BEC_DEFAULTS = {
   BE_MEMORY: 128,
   BE_VCPUS: 1,
diff --git a/lib/objects.py b/lib/objects.py
index e87faddaf..06bc7e029 100644
--- a/lib/objects.py
+++ b/lib/objects.py
@@ -42,19 +42,26 @@ __all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
 _TIMESTAMPS = ["ctime", "mtime"]
 _UUID = ["uuid"]
 
-def FillDict(defaults_dict, custom_dict):
+def FillDict(defaults_dict, custom_dict, skip_keys=[]):
   """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
+  @type skip_keys: list
+  @param skip_keys: which keys not to fill
   @rtype: dict
   @return: dict with the 'full' values
 
   """
   ret_dict = copy.deepcopy(defaults_dict)
   ret_dict.update(custom_dict)
+  for k in skip_keys:
+    try:
+      del ret_dict[k]
+    except KeyError:
+      pass
   return ret_dict
 
 
@@ -777,6 +784,12 @@ class Instance(TaggableObject):
       nic.UpgradeConfig()
     for disk in self.disks:
       disk.UpgradeConfig()
+    if self.hvparams:
+      for key in constants.HVC_GLOBALS:
+        try:
+          del self.hvparams[key]
+        except KeyError:
+          pass
 
 
 class OS(ConfigObject):
@@ -887,18 +900,25 @@ class Cluster(TaggableObject):
       obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
     return obj
 
-  def FillHV(self, instance):
+  def FillHV(self, instance, skip_globals=False):
     """Fill an instance's hvparams dict.
 
     @type instance: L{objects.Instance}
     @param instance: the instance parameter to fill
+    @type skip_globals: boolean
+    @param skip_globals: if True, the global hypervisor parameters will
+        not be filled
     @rtype: dict
     @return: a copy of the instance's hvparams with missing keys filled from
         the cluster defaults
 
     """
+    if skip_globals:
+      skip_keys = constants.HVC_GLOBALS
+    else:
+      skip_keys = []
     return FillDict(self.hvparams.get(instance.hypervisor, {}),
-                         instance.hvparams)
+                    instance.hvparams, skip_keys=skip_keys)
 
   def FillBE(self, instance):
     """Fill an instance's beparams dict.
@@ -911,7 +931,7 @@ class Cluster(TaggableObject):
 
     """
     return FillDict(self.beparams.get(constants.PP_DEFAULT, {}),
-                          instance.beparams)
+                    instance.beparams)
 
 
 class BlockDevStatus(ConfigObject):
-- 
GitLab