Violations on policy changes are checked.
Signed-off-by: Bernardo Dal Seno <bdalseno@google.com>
Reviewed-by: Guido Trotter <ultrotter@google.com>
qa_config.ReleaseNode(node)
+def _BuildSpecDict(par, mn, st, mx):
+ return {par: {"min": mn, "std": st, "max": mx}}
+
+
+def TestIPolicyPlainInstance():
+ """Test instance policy interaction with instances"""
+ params = ["mem-size", "cpu-count", "disk-count", "disk-size", "nic-count"]
+ if not qa_config.IsTemplateSupported(constants.DT_PLAIN):
+ print "Template %s not supported" % constants.DT_PLAIN
+ return
+
+ # This test assumes that the group policy is empty
+ (_, old_specs) = qa_cluster.TestClusterSetISpecs({})
+ node = qa_config.AcquireNode()
+ try:
+ instance = qa_instance.TestInstanceAddWithPlainDisk([node])
+ try:
+ policyerror = [constants.CV_EINSTANCEPOLICY]
+ for par in params:
+ qa_cluster.AssertClusterVerify()
+ (iminval, imaxval) = qa_instance.GetInstanceSpec(instance["name"], par)
+ # Some specs must be multiple of 4
+ new_spec = _BuildSpecDict(par, imaxval + 4, imaxval + 4, imaxval + 4)
+ qa_cluster.TestClusterSetISpecs(new_spec)
+ qa_cluster.AssertClusterVerify(warnings=policyerror)
+ if iminval > 0:
+ # Some specs must be multiple of 4
+ if iminval >= 4:
+ upper = iminval - 4
+ else:
+ upper = iminval - 1
+ new_spec = _BuildSpecDict(par, 0, upper, upper)
+ qa_cluster.TestClusterSetISpecs(new_spec)
+ qa_cluster.AssertClusterVerify(warnings=policyerror)
+ qa_cluster.TestClusterSetISpecs(old_specs)
+ qa_instance.TestInstanceRemove(instance)
+ finally:
+ qa_config.ReleaseInstance(instance)
+ finally:
+ qa_config.ReleaseNode(node)
+
+
def RunInstanceTests():
"""Create and exercise instances."""
instance_tests = [
qa_config.ReleaseNode(pnode)
RunExclusiveStorageTests()
+ RunTestIf(["cluster-instance-policy", "instance-add-plain-disk"],
+ TestIPolicyPlainInstance)
# Test removing instance with offline drbd secondary
if qa_config.TestEnabled("instance-remove-drbd-offline"):
"cluster-redist-conf": true,
"cluster-repair-disk-sizes": true,
"cluster-exclusive-storage": true,
+ "cluster-instance-policy": true,
"haskell-confd": true,
"htools": true,
# Cluster-verify errors (date, "ERROR", then error code)
-_CVERROR_RE = re.compile(r"^[\w\s:]+\s+- ERROR:([A-Z0-9_-]+):")
+_CVERROR_RE = re.compile(r"^[\w\s:]+\s+- (ERROR|WARNING):([A-Z0-9_-]+):")
def _GetCVErrorCodes(cvout):
- ret = set()
+ errs = set()
+ warns = set()
for l in cvout.splitlines():
m = _CVERROR_RE.match(l)
if m:
- ecode = m.group(1)
- ret.add(ecode)
- return ret
+ etype = m.group(1)
+ ecode = m.group(2)
+ if etype == "ERROR":
+ errs.add(ecode)
+ elif etype == "WARNING":
+ warns.add(ecode)
+ return (errs, warns)
-def AssertClusterVerify(fail=False, errors=None):
+def _CheckVerifyErrors(actual, expected, etype):
+ exp_codes = compat.UniqueFrozenset(e for (_, e, _) in expected)
+ if not actual.issuperset(exp_codes):
+ missing = exp_codes.difference(actual)
+ raise qa_error.Error("Cluster-verify didn't return these expected"
+ " %ss: %s" % (etype, utils.CommaJoin(missing)))
+
+
+def AssertClusterVerify(fail=False, errors=None, warnings=None):
"""Run cluster-verify and check the result
@type fail: bool
@param errors: List of CV_XXX errors that are expected; if specified, all the
errors listed must appear in cluster-verify output. A non-empty value
implies C{fail=True}.
+ @type warnings: list of tuples
+ @param warnings: Same as C{errors} but for warnings.
"""
cvcmd = "gnt-cluster verify"
mnode = qa_config.GetMasterNode()
- if errors:
+ if errors or warnings:
cvout = GetCommandOutput(mnode["primary"], cvcmd + " --error-codes",
- fail=True)
- actual = _GetCVErrorCodes(cvout)
- expected = compat.UniqueFrozenset(e for (_, e, _) in errors)
- if not actual.issuperset(expected):
- missing = expected.difference(actual)
- raise qa_error.Error("Cluster-verify didn't return these expected"
- " errors: %s" % utils.CommaJoin(missing))
+ fail=(fail or errors))
+ (act_errs, act_warns) = _GetCVErrorCodes(cvout)
+ if errors:
+ _CheckVerifyErrors(act_errs, errors, "error")
+ if warnings:
+ _CheckVerifyErrors(act_warns, warnings, "warning")
else:
AssertCommand(cvcmd, fail=fail, node=mnode)
AssertCommand(["lvremove", "-f"] + vols, node=node)
-def _GetBoolInstanceField(instance, field):
- """Get the Boolean value of a field of an instance.
+def _GetInstanceField(instance, field):
+ """Get the value of a field of an instance.
@type instance: string
@param instance: Instance name
@type field: string
@param field: Name of the field
+ @rtype: string
"""
master = qa_config.GetMasterNode()
infocmd = utils.ShellQuoteArgs(["gnt-instance", "list", "--no-headers",
- "-o", field, instance])
- info_out = qa_utils.GetCommandOutput(master["primary"], infocmd).strip()
+ "--units", "m", "-o", field, instance])
+ return qa_utils.GetCommandOutput(master["primary"], infocmd).strip()
+
+
+def _GetBoolInstanceField(instance, field):
+ """Get the Boolean value of a field of an instance.
+
+ @type instance: string
+ @param instance: Instance name
+ @type field: string
+ @param field: Name of the field
+ @rtype: bool
+
+ """
+ info_out = _GetInstanceField(instance, field)
if info_out == "Y":
return True
elif info_out == "N":
" %s" % (field, instance, info_out))
+def _GetNumInstanceField(instance, field):
+ """Get a numeric value of a field of an instance.
+
+ @type instance: string
+ @param instance: Instance name
+ @type field: string
+ @param field: Name of the field
+ @rtype: int or float
+
+ """
+ info_out = _GetInstanceField(instance, field)
+ try:
+ ret = int(info_out)
+ except ValueError:
+ try:
+ ret = float(info_out)
+ except ValueError:
+ raise qa_error.Error("Field %s of instance %s has a non-numeric value:"
+ " %s" % (field, instance, info_out))
+ return ret
+
+
+def GetInstanceSpec(instance, spec):
+ """Return the current spec for the given parameter.
+
+ @type instance: string
+ @param instance: Instance name
+ @type spec: string
+ @param spec: one of the supported parameters: "mem-size", "cpu-count",
+ "disk-count", "disk-size", "nic-count"
+ @rtype: tuple
+ @return: (minspec, maxspec); minspec and maxspec can be different only for
+ memory and disk size
+
+ """
+ specmap = {
+ "mem-size": ["be/minmem", "be/maxmem"],
+ "cpu-count": ["vcpus"],
+ "disk-count": ["disk.count"],
+ "disk-size": ["disk.size/ "],
+ "nic-count": ["nic.count"],
+ }
+ # For disks, first we need the number of disks
+ if spec == "disk-size":
+ (numdisk, _) = GetInstanceSpec(instance, "disk-count")
+ fields = ["disk.size/%s" % k for k in range(0, numdisk)]
+ else:
+ assert spec in specmap, "%s not in %s" % (spec, specmap)
+ fields = specmap[spec]
+ values = [_GetNumInstanceField(instance, f) for f in fields]
+ return (min(values), max(values))
+
+
def IsFailoverSupported(instance):
templ = qa_config.GetInstanceTemplate(instance)
return templ in constants.DTS_MIRRORED