+def _GetClusterIPolicy():
+ """Return the run-time values of the cluster-level instance policy.
+
+ @rtype: tuple
+ @return: (policy, specs), where:
+ - policy is a dictionary of the policy values, instance specs excluded
+ - specs is dict of dict, specs[par][key] is a spec value, where key is
+ "min", "max", or "std"
+
+ """
+ info = qa_utils.GetObjectInfo(["gnt-cluster", "info"])
+ policy = info["Instance policy - limits for instances"]
+ (ret_policy, ret_specs) = qa_utils.ParseIPolicy(policy)
+
+ # Sanity checks
+ assert len(ret_specs) > 0
+ good = all("min" in d and "std" in d and "max" in d
+ for d in ret_specs.values())
+ assert good, "Missing item in specs: %s" % ret_specs
+ assert len(ret_policy) > 0
+ return (ret_policy, ret_specs)
+
+
+def TestClusterModifyIPolicy():
+ """gnt-cluster modify --ipolicy-*"""
+ basecmd = ["gnt-cluster", "modify"]
+ (old_policy, old_specs) = _GetClusterIPolicy()
+ for par in ["vcpu-ratio", "spindle-ratio"]:
+ curr_val = float(old_policy[par])
+ test_values = [
+ (True, 1.0),
+ (True, 1.5),
+ (True, 2),
+ (False, "a"),
+ # Restore the old value
+ (True, curr_val),
+ ]
+ for (good, val) in test_values:
+ cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
+ AssertCommand(cmd, fail=not good)
+ if good:
+ curr_val = val
+ # Check the affected parameter
+ (eff_policy, eff_specs) = _GetClusterIPolicy()
+ AssertEqual(float(eff_policy[par]), curr_val)
+ # Check everything else
+ AssertEqual(eff_specs, old_specs)
+ for p in eff_policy.keys():
+ if p == par:
+ continue
+ AssertEqual(eff_policy[p], old_policy[p])
+
+ # Disk templates are treated slightly differently
+ par = "disk-templates"
+ disp_str = "enabled disk templates"
+ curr_val = old_policy[disp_str]
+ test_values = [
+ (True, constants.DT_PLAIN),
+ (True, "%s,%s" % (constants.DT_PLAIN, constants.DT_DRBD8)),
+ (False, "thisisnotadisktemplate"),
+ (False, ""),
+ # Restore the old value
+ (True, curr_val.replace(" ", "")),
+ ]
+ for (good, val) in test_values:
+ cmd = basecmd + ["--ipolicy-%s=%s" % (par, val)]
+ AssertCommand(cmd, fail=not good)
+ if good:
+ curr_val = val
+ # Check the affected parameter
+ (eff_policy, eff_specs) = _GetClusterIPolicy()
+ AssertEqual(eff_policy[disp_str].replace(" ", ""), curr_val)
+ # Check everything else
+ AssertEqual(eff_specs, old_specs)
+ for p in eff_policy.keys():
+ if p == disp_str:
+ continue
+ AssertEqual(eff_policy[p], old_policy[p])
+
+
+def TestClusterSetISpecs(new_specs, fail=False, old_values=None):
+ """Change instance specs.
+
+ @type new_specs: dict of dict
+ @param new_specs: new_specs[par][key], where key is "min", "max", "std". It
+ can be an empty dictionary.
+ @type fail: bool
+ @param fail: if the change is expected to fail
+ @type old_values: tuple
+ @param old_values: (old_policy, old_specs), as returned by
+ L{_GetClusterIPolicy}
+ @return: same as L{_GetClusterIPolicy}
+
+ """
+ build_cmd = lambda opts: ["gnt-cluster", "modify"] + opts
+ return qa_utils.TestSetISpecs(new_specs, get_policy_fn=_GetClusterIPolicy,
+ build_cmd_fn=build_cmd, fail=fail,
+ old_values=old_values)
+
+
+def TestClusterModifyISpecs():
+ """gnt-cluster modify --specs-*"""
+ params = ["memory-size", "disk-size", "disk-count", "cpu-count", "nic-count"]
+ (cur_policy, cur_specs) = _GetClusterIPolicy()
+ for par in params:
+ test_values = [
+ (True, 0, 4, 12),
+ (True, 4, 4, 12),
+ (True, 4, 12, 12),
+ (True, 4, 4, 4),
+ (False, 4, 0, 12),
+ (False, 4, 16, 12),
+ (False, 4, 4, 0),
+ (False, 12, 4, 4),
+ (False, 12, 4, 0),
+ (False, "a", 4, 12),
+ (False, 0, "a", 12),
+ (False, 0, 4, "a"),
+ # This is to restore the old values
+ (True,
+ cur_specs[par]["min"], cur_specs[par]["std"], cur_specs[par]["max"])
+ ]
+ for (good, mn, st, mx) in test_values:
+ new_vals = {par: {"min": str(mn), "std": str(st), "max": str(mx)}}
+ cur_state = (cur_policy, cur_specs)
+ # We update cur_specs, as we've copied the values to restore already
+ (cur_policy, cur_specs) = TestClusterSetISpecs(new_vals, fail=not good,
+ old_values=cur_state)
+
+ # Get the ipolicy command
+ mnode = qa_config.GetMasterNode()
+ initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
+ modcmd = ["gnt-cluster", "modify"]
+ opts = initcmd.split()
+ assert opts[0:2] == ["gnt-cluster", "init"]
+ for k in range(2, len(opts) - 1):
+ if opts[k].startswith("--ipolicy-"):
+ assert k + 2 <= len(opts)
+ modcmd.extend(opts[k:k + 2])
+ # Re-apply the ipolicy (this should be a no-op)
+ AssertCommand(modcmd)
+ new_initcmd = GetCommandOutput(mnode.primary, "gnt-cluster show-ispecs-cmd")
+ AssertEqual(initcmd, new_initcmd)
+
+