Introduce 'global hypervisor parameters' support
authorIustin Pop <iustin@google.com>
Wed, 4 Nov 2009 16:41:04 +0000 (17:41 +0100)
committerIustin Pop <iustin@google.com>
Fri, 6 Nov 2009 11:42:06 +0000 (12:42 +0100)
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
lib/constants.py
lib/objects.py

index 7f046ec..c1a52ff 100644 (file)
@@ -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:
index bab6d8d..8a47a9d 100644 (file)
@@ -650,6 +650,10 @@ HVC_DEFAULTS = {
     },
   }
 
+HVC_GLOBALS = frozenset([
+  HV_MIGRATION_PORT,
+  ])
+
 BEC_DEFAULTS = {
   BE_MEMORY: 128,
   BE_VCPUS: 1,
index e87fadd..06bc7e0 100644 (file)
@@ -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):