QA: Handle multiple instance specs
authorBernardo Dal Seno <bdalseno@google.com>
Mon, 22 Apr 2013 23:24:25 +0000 (01:24 +0200)
committerBernardo Dal Seno <bdalseno@google.com>
Mon, 29 Apr 2013 15:54:34 +0000 (17:54 +0200)
Existing tests are updated to cope with the new instance specs format.

Signed-off-by: Bernardo Dal Seno <bdalseno@google.com>
Reviewed-by: Helga Velroyen <helgav@google.com>

qa/ganeti-qa.py
qa/qa_cluster.py
qa/qa_group.py
qa/qa_utils.py

index b4d8d2a..ca698d0 100755 (executable)
@@ -524,9 +524,11 @@ def RunExclusiveStorageTests():
 
 def _BuildSpecDict(par, mn, st, mx):
   return {
-    "min": {par: mn},
-    "max": {par: mx},
-    "std": {par: st},
+    constants.ISPECS_MINMAX: [{
+      constants.ISPECS_MIN: {par: mn},
+      constants.ISPECS_MAX: {par: mx},
+      }],
+    constants.ISPECS_STD: {par: st},
     }
 
 
@@ -539,6 +541,8 @@ def TestIPolicyPlainInstance():
 
   # This test assumes that the group policy is empty
   (_, old_specs) = qa_cluster.TestClusterSetISpecs()
+  # We also assume to have only one min/max bound
+  assert len(old_specs[constants.ISPECS_MINMAX]) == 1
   node = qa_config.AcquireNode()
   try:
     # Log of policy changes, list of tuples:
index a139e5f..e281178 100644 (file)
@@ -532,8 +532,8 @@ def _GetClusterIPolicy():
   @rtype: tuple
   @return: (policy, specs), where:
       - policy is a dictionary of the policy values, instance specs excluded
-      - specs is dict of dict, specs[key][par] is a spec value, where key is
-        "min", "max", or "std"
+      - specs is a dictionary containing only the specs, using the internal
+        format (see L{constants.IPOLICY_DEFAULTS} for an example)
 
   """
   info = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
@@ -541,7 +541,8 @@ def _GetClusterIPolicy():
   (ret_policy, ret_specs) = qa_utils.ParseIPolicy(policy)
 
   # Sanity checks
-  assert "min" in ret_specs and "std" in ret_specs and "max" in ret_specs
+  assert "minmax" in ret_specs and "std" in ret_specs
+  assert len(ret_specs["minmax"]) > 0
   assert len(ret_policy) > 0
   return (ret_policy, ret_specs)
 
@@ -613,8 +614,9 @@ def TestClusterSetISpecs(new_specs=None, diff_specs=None, fail=False,
   @param new_specs: new complete specs, in the same format returned by
       L{_GetClusterIPolicy}
   @type diff_specs: dict
-  @param diff_specs: diff_specs[key][par], where key is "min", "max", "std". It
-      can be an incomplete specifications or an empty dictionary.
+  @param diff_specs: partial specs, it can be an incomplete specifications, but
+      if min/max specs are specified, their number must match the number of the
+      existing specs
   @type fail: bool
   @param fail: if the change is expected to fail
   @type old_values: tuple
@@ -634,6 +636,8 @@ def TestClusterModifyISpecs():
   """gnt-cluster modify --specs-*"""
   params = ["memory-size", "disk-size", "disk-count", "cpu-count", "nic-count"]
   (cur_policy, cur_specs) = _GetClusterIPolicy()
+  # This test assumes that there is only one min/max bound
+  assert len(cur_specs[constants.ISPECS_MINMAX]) == 1
   for par in params:
     test_values = [
       (True, 0, 4, 12),
@@ -650,13 +654,17 @@ def TestClusterModifyISpecs():
       (False, 0, 4, "a"),
       # This is to restore the old values
       (True,
-       cur_specs["min"][par], cur_specs["std"][par], cur_specs["max"][par])
+       cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MIN][par],
+       cur_specs[constants.ISPECS_STD][par],
+       cur_specs[constants.ISPECS_MINMAX][0][constants.ISPECS_MAX][par])
       ]
     for (good, mn, st, mx) in test_values:
       new_vals = {
-        "min": {par: mn},
-        "std": {par: st},
-        "max": {par: mx}
+        constants.ISPECS_MINMAX: [{
+          constants.ISPECS_MIN: {par: mn},
+          constants.ISPECS_MAX: {par: mx}
+          }],
+        constants.ISPECS_STD: {par: st}
         }
       cur_state = (cur_policy, cur_specs)
       # We update cur_specs, as we've copied the values to restore already
index 202bde8..e71451a 100644 (file)
@@ -87,8 +87,9 @@ def _GetGroupIPolicy(groupname):
   @rtype: tuple
   @return: (policy, specs), where:
       - policy is a dictionary of the policy values, instance specs excluded
-      - specs is dict of dict, specs[key][par] is a spec value, where key is
-        "min" or "max"
+      - specs is a dictionary containing only the specs, using the internal
+        format (see L{constants.IPOLICY_DEFAULTS} for an example), but without
+        the standard values
 
   """
   info = qa_utils.GetObjectInfo(["gnt-group", "info", groupname])
@@ -98,7 +99,8 @@ def _GetGroupIPolicy(groupname):
   (ret_policy, ret_specs) = qa_utils.ParseIPolicy(policy)
 
   # Sanity checks
-  assert "min" in ret_specs and "max" in ret_specs
+  assert "minmax" in ret_specs
+  assert len(ret_specs["minmax"]) > 0
   assert len(ret_policy) > 0
   return (ret_policy, ret_specs)
 
@@ -115,8 +117,9 @@ def _TestGroupSetISpecs(groupname, new_specs=None, diff_specs=None,
   @param new_specs: new complete specs, in the same format returned by
       L{_GetGroupIPolicy}
   @type diff_specs: dict
-  @param diff_specs: diff_specs[key][par], where key is "min", "max". It
-      can be an incomplete specifications or an empty dictionary.
+  @param diff_specs: partial specs, it can be an incomplete specifications, but
+      if min/max specs are specified, their number must match the number of the
+      existing specs
   @type fail: bool
   @param fail: if the change is expected to fail
   @type old_values: tuple
@@ -138,15 +141,30 @@ def _TestGroupModifyISpecs(groupname):
   # the node group under test
   old_values = _GetGroupIPolicy(groupname)
   samevals = dict((p, 4) for p in constants.ISPECS_PARAMETERS)
-  base_specs = {"min": samevals, "max": samevals}
+  base_specs = {
+    constants.ISPECS_MINMAX: [{
+      constants.ISPECS_MIN: samevals,
+      constants.ISPECS_MAX: samevals,
+      }],
+    }
   mod_values = _TestGroupSetISpecs(groupname, new_specs=base_specs,
                                    old_values=old_values)
   for par in constants.ISPECS_PARAMETERS:
     # First make sure that the test works with good values
-    good_specs = {"min": {par: 8}, "max": {par: 8}}
+    good_specs = {
+      constants.ISPECS_MINMAX: [{
+        constants.ISPECS_MIN: {par: 8},
+        constants.ISPECS_MAX: {par: 8},
+        }],
+      }
     mod_values = _TestGroupSetISpecs(groupname, diff_specs=good_specs,
                                      old_values=mod_values)
-    bad_specs = {"min": {par: 8}, "max": {par: 4}}
+    bad_specs = {
+      constants.ISPECS_MINMAX: [{
+        constants.ISPECS_MIN: {par: 8},
+        constants.ISPECS_MAX: {par: 4},
+        }],
+      }
     _TestGroupSetISpecs(groupname, diff_specs=bad_specs, fail=True,
                         old_values=mod_values)
   AssertCommand(["gnt-group", "modify", "--ipolicy-bounds-specs", "default",
index 412f913..7ec0d49 100644 (file)
@@ -797,8 +797,9 @@ def TestSetISpecs(new_specs=None, diff_specs=None, get_policy_fn=None,
   @param new_specs: new complete specs, in the same format returned by
       L{ParseIPolicy}.
   @type diff_specs: dict
-  @param diff_specs: diff_specs[key][par], where key is "min", "max", "std". It
-      can be an incomplete specifications or an empty dictionary.
+  @param diff_specs: partial specs, it can be an incomplete specifications, but
+      if min/max specs are specified, their number must match the number of the
+      existing specs
   @type get_policy_fn: function
   @param get_policy_fn: function that returns the current policy as in
       L{ParseIPolicy}
@@ -824,26 +825,35 @@ def TestSetISpecs(new_specs=None, diff_specs=None, get_policy_fn=None,
 
   if diff_specs:
     new_specs = copy.deepcopy(old_specs)
-    for (key, parvals) in diff_specs.items():
-      for (par, val) in parvals.items():
-        new_specs[key][par] = val
+    if constants.ISPECS_MINMAX in diff_specs:
+      AssertEqual(len(new_specs[constants.ISPECS_MINMAX]),
+                  len(diff_specs[constants.ISPECS_MINMAX]))
+      for (new_minmax, diff_minmax) in zip(new_specs[constants.ISPECS_MINMAX],
+                                           diff_specs[constants.ISPECS_MINMAX]):
+        for (key, parvals) in diff_minmax.items():
+          for (par, val) in parvals.items():
+            new_minmax[key][par] = val
+    for (par, val) in diff_specs.get(constants.ISPECS_STD, {}).items():
+      new_specs[constants.ISPECS_STD][par] = val
 
   if new_specs:
     cmd = []
-    if (diff_specs is None or
-        ("min" in diff_specs or "max" in diff_specs)):
+    if (diff_specs is None or constants.ISPECS_MINMAX in diff_specs):
       minmax_opt_items = []
-      for key in ["min", "max"]:
-        keyopt = _GetParameterOptions(new_specs[key])
-        minmax_opt_items.append("%s:%s" % (key, keyopt))
+      for minmax in new_specs[constants.ISPECS_MINMAX]:
+        minmax_opts = []
+        for key in ["min", "max"]:
+          keyopt = _GetParameterOptions(minmax[key])
+          minmax_opts.append("%s:%s" % (key, keyopt))
+        minmax_opt_items.append("/".join(minmax_opts))
       cmd.extend([
         "--ipolicy-bounds-specs",
-        "/".join(minmax_opt_items)
+        "//".join(minmax_opt_items)
         ])
-    if diff_specs:
-      std_source = diff_specs
-    else:
+    if diff_specs is None:
       std_source = new_specs
+    else:
+      std_source = diff_specs
     std_opt = _GetParameterOptions(std_source.get("std", {}))
     if std_opt:
       cmd.extend(["--ipolicy-std-specs", std_opt])
@@ -871,15 +881,23 @@ def ParseIPolicy(policy):
   @rtype: tuple
   @return: (policy, specs), where:
       - policy is a dictionary of the policy values, instance specs excluded
-      - specs is dict of dict, specs[key][par] is a spec value, where key is
-        "min", "max", or "std"
+      - specs is a dictionary containing only the specs, using the internal
+        format (see L{constants.IPOLICY_DEFAULTS} for an example)
 
   """
   ret_specs = {}
   ret_policy = {}
-  ispec_keys = constants.ISPECS_MINMAX_KEYS | frozenset([constants.ISPECS_STD])
   for (key, val) in policy.items():
-    if key in ispec_keys:
+    if key == "bounds specs":
+      ret_specs[constants.ISPECS_MINMAX] = []
+      for minmax in val:
+        ret_minmax = {}
+        for key in minmax:
+          keyparts = key.split("/", 1)
+          assert len(keyparts) > 1
+          ret_minmax[keyparts[0]] = minmax[key]
+        ret_specs[constants.ISPECS_MINMAX].append(ret_minmax)
+    elif key == constants.ISPECS_STD:
       ret_specs[key] = val
     else:
       ret_policy[key] = val