#
#
-# Copyright (C) 2007, 2010, 2011, 2012 Google Inc.
+# Copyright (C) 2007, 2010, 2011, 2012, 2013 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
"""
+import re
import tempfile
import os.path
from ganeti import constants
from ganeti import compat
from ganeti import utils
+from ganeti import pathutils
import qa_config
import qa_utils
from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
+# Prefix for LVM volumes created by QA code during tests
+_QA_LV_PREFIX = "qa-"
+
#: cluster verify command
_CLUSTER_VERIFY = ["gnt-cluster", "verify"]
"""
cmd = utils.ShellQuoteArgs(["cat", filename])
for node in qa_config.get("nodes"):
- AssertEqual(qa_utils.GetCommandOutput(node["primary"], cmd), content)
+ AssertEqual(qa_utils.GetCommandOutput(node.primary, cmd), content)
+
+
+# "gnt-cluster info" fields
+_CIFIELD_RE = re.compile(r"^[-\s]*(?P<field>[^\s:]+):\s*(?P<value>\S.*)$")
+
+
+def _GetBoolClusterField(field):
+ """Get the Boolean value of a cluster field.
+
+ This function currently assumes that the field name is unique in the cluster
+ configuration. An assertion checks this assumption.
+
+ @type field: string
+ @param field: Name of the field
+ @rtype: bool
+ @return: The effective value of the field
+
+ """
+ master = qa_config.GetMasterNode()
+ infocmd = "gnt-cluster info"
+ info_out = qa_utils.GetCommandOutput(master.primary, infocmd)
+ ret = None
+ for l in info_out.splitlines():
+ m = _CIFIELD_RE.match(l)
+ # FIXME: There should be a way to specify a field through a hierarchy
+ if m and m.group("field") == field:
+ # Make sure that ignoring the hierarchy doesn't cause a double match
+ assert ret is None
+ ret = (m.group("value").lower() == "true")
+ if ret is not None:
+ return ret
+ raise qa_error.Error("Field not found in cluster configuration: %s" % field)
+
+
+# Cluster-verify errors (date, "ERROR", then error code)
+_CVERROR_RE = re.compile(r"^[\w\s:]+\s+- ERROR:([A-Z0-9_-]+):")
+
+
+def _GetCVErrorCodes(cvout):
+ ret = set()
+ for l in cvout.splitlines():
+ m = _CVERROR_RE.match(l)
+ if m:
+ ecode = m.group(1)
+ ret.add(ecode)
+ return ret
+
+
+def AssertClusterVerify(fail=False, errors=None):
+ """Run cluster-verify and check the result
+
+ @type fail: bool
+ @param fail: if cluster-verify is expected to fail instead of succeeding
+ @type errors: list of tuples
+ @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}.
+
+ """
+ cvcmd = "gnt-cluster verify"
+ mnode = qa_config.GetMasterNode()
+ if errors:
+ 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))
+ else:
+ AssertCommand(cvcmd, fail=fail, node=mnode)
# data for testing failures due to bad keys/values for disk parameters
"""gnt-cluster init"""
master = qa_config.GetMasterNode()
- rapi_dir = os.path.dirname(constants.RAPI_USERS_FILE)
+ rapi_dir = os.path.dirname(pathutils.RAPI_USERS_FILE)
# First create the RAPI credentials
fh = tempfile.NamedTemporaryFile()
fh.write("%s %s write\n" % (rapi_user, rapi_secret))
fh.flush()
- tmpru = qa_utils.UploadFile(master["primary"], fh.name)
+ tmpru = qa_utils.UploadFile(master.primary, fh.name)
try:
AssertCommand(["mkdir", "-p", rapi_dir])
- AssertCommand(["mv", tmpru, constants.RAPI_USERS_FILE])
+ AssertCommand(["mv", tmpru, pathutils.RAPI_USERS_FILE])
finally:
AssertCommand(["rm", "-f", tmpru])
finally:
"nic-count"):
for spec_val in ("min", "max", "std"):
spec = qa_config.get("ispec_%s_%s" %
- (spec_type.replace('-', '_'), spec_val), None)
+ (spec_type.replace("-", "_"), spec_val), None)
if spec:
cmd.append("--specs-%s=%s=%d" % (spec_type, spec_val, spec))
- if master.get("secondary", None):
- cmd.append("--secondary-ip=%s" % master["secondary"])
+ if master.secondary:
+ cmd.append("--secondary-ip=%s" % master.secondary)
+
+ vgname = qa_config.get("vg-name", None)
+ if vgname:
+ cmd.append("--vg-name=%s" % vgname)
+
+ master_netdev = qa_config.get("master-netdev", None)
+ if master_netdev:
+ cmd.append("--master-netdev=%s" % master_netdev)
- bridge = qa_config.get("bridge", None)
- if bridge:
- cmd.append("--bridge=%s" % bridge)
- cmd.append("--master-netdev=%s" % bridge)
+ nicparams = qa_config.get("default-nicparams", None)
+ if nicparams:
+ cmd.append("--nic-parameters=%s" %
+ ",".join(utils.FormatKeyValue(nicparams)))
+
+ # Cluster value of the exclusive-storage node parameter
+ e_s = qa_config.get("exclusive-storage")
+ if e_s is not None:
+ cmd.extend(["--node-parameters", "exclusive_storage=%s" % e_s])
+ else:
+ e_s = False
+ qa_config.SetExclusiveStorage(e_s)
+
+ extra_args = qa_config.get("cluster-init-args")
+ if extra_args:
+ cmd.extend(extra_args)
cmd.append(qa_config.get("name"))
+
AssertCommand(cmd)
cmd = ["gnt-cluster", "modify"]
master = qa_config.GetMasterNode()
# Assert that OOB is unavailable for all nodes
- result_output = GetCommandOutput(master["primary"],
+ result_output = GetCommandOutput(master.primary,
"gnt-node list --verbose --no-headers -o"
" powered")
AssertEqual(compat.all(powered == "(unavail)"
AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
# Unless --all is given master is not allowed to be in the list
- AssertCommand(["gnt-cluster", "epo", "-f", master["primary"]], fail=True)
+ AssertCommand(["gnt-cluster", "epo", "-f", master.primary], fail=True)
# This shouldn't fail
AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
# All instances should have been stopped now
- result_output = GetCommandOutput(master["primary"],
+ result_output = GetCommandOutput(master.primary,
"gnt-instance list --no-headers -o status")
# ERROR_down because the instance is stopped but not recorded as such
AssertEqual(compat.all(status == "ERROR_down"
AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
# All instances should have been started now
- result_output = GetCommandOutput(master["primary"],
+ result_output = GetCommandOutput(master.primary,
"gnt-instance list --no-headers -o status")
AssertEqual(compat.all(status == "running"
for status in result_output.splitlines()), True)
AssertCommand(["gnt-debug", "delay", "1"])
AssertCommand(["gnt-debug", "delay", "--no-master", "1"])
AssertCommand(["gnt-debug", "delay", "--no-master",
- "-n", node["primary"], "1"])
+ "-n", node.primary, "1"])
def TestClusterReservedLvs():
"""gnt-cluster reserved lvs"""
+ vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
+ lvname = _QA_LV_PREFIX + "test"
+ lvfullname = "/".join([vgname, lvname])
for fail, cmd in [
(False, _CLUSTER_VERIFY),
(False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
- (False, ["lvcreate", "-L1G", "-nqa-test", "xenvg"]),
+ (False, ["lvcreate", "-L1G", "-n", lvname, vgname]),
(True, _CLUSTER_VERIFY),
(False, ["gnt-cluster", "modify", "--reserved-lvs",
- "xenvg/qa-test,.*/other-test"]),
+ "%s,.*/other-test" % lvfullname]),
(False, _CLUSTER_VERIFY),
- (False, ["gnt-cluster", "modify", "--reserved-lvs", ".*/qa-.*"]),
+ (False, ["gnt-cluster", "modify", "--reserved-lvs",
+ ".*/%s.*" % _QA_LV_PREFIX]),
(False, _CLUSTER_VERIFY),
(False, ["gnt-cluster", "modify", "--reserved-lvs", ""]),
(True, _CLUSTER_VERIFY),
- (False, ["lvremove", "-f", "xenvg/qa-test"]),
+ (False, ["lvremove", "-f", lvfullname]),
(False, _CLUSTER_VERIFY),
]:
AssertCommand(cmd, fail=fail)
"--rapi-certificate=/dev/null"]
AssertCommand(cmd, fail=True)
- rapi_cert_backup = qa_utils.BackupFile(master["primary"],
- constants.RAPI_CERT_FILE)
+ rapi_cert_backup = qa_utils.BackupFile(master.primary,
+ pathutils.RAPI_CERT_FILE)
try:
# Custom RAPI certificate
fh = tempfile.NamedTemporaryFile()
utils.GenerateSelfSignedSslCert(fh.name, validity=validity)
- tmpcert = qa_utils.UploadFile(master["primary"], fh.name)
+ tmpcert = qa_utils.UploadFile(master.primary, fh.name)
try:
AssertCommand(["gnt-cluster", "renew-crypto", "--force",
"--rapi-certificate=%s" % tmpcert])
cds_fh.write("\n")
cds_fh.flush()
- tmpcds = qa_utils.UploadFile(master["primary"], cds_fh.name)
+ tmpcds = qa_utils.UploadFile(master.primary, cds_fh.name)
try:
AssertCommand(["gnt-cluster", "renew-crypto", "--force",
"--cluster-domain-secret=%s" % tmpcds])
master = qa_config.GetMasterNode()
options = qa_config.get("options", {})
- disk_template = options.get("burnin-disk-template", "drbd")
+ disk_template = options.get("burnin-disk-template", constants.DT_DRBD8)
parallel = options.get("burnin-in-parallel", False)
check_inst = options.get("burnin-check-instances", False)
do_rename = options.get("burnin-rename", "")
if len(instances) < 1:
raise qa_error.Error("Burnin needs at least one instance")
- script = qa_utils.UploadFile(master["primary"], "../tools/burnin")
+ script = qa_utils.UploadFile(master.primary, "../tools/burnin")
try:
# Run burnin
cmd = [script,
cmd.append("--no-reboot")
else:
cmd.append("--reboot-types=%s" % ",".join(reboot_types))
- cmd += [inst["name"] for inst in instances]
+ cmd += [inst.name for inst in instances]
AssertCommand(cmd)
finally:
AssertCommand(["rm", "-f", script])
finally:
for inst in instances:
- qa_config.ReleaseInstance(inst)
+ inst.Release()
def TestClusterMasterFailover():
# Back to original master node
AssertCommand(cmd, node=master)
finally:
- qa_config.ReleaseNode(failovermaster)
+ failovermaster.Release()
def TestClusterMasterFailoverWithDrainedQueue():
"""gnt-cluster master-failover with drained queue"""
- drain_check = ["test", "-f", constants.JOB_QUEUE_DRAIN_FILE]
+ drain_check = ["test", "-f", pathutils.JOB_QUEUE_DRAIN_FILE]
master = qa_config.GetMasterNode()
failovermaster = qa_config.AcquireNode(exclude=master)
AssertCommand(drain_check, node=node, fail=True)
# Drain queue on failover master
- AssertCommand(["touch", constants.JOB_QUEUE_DRAIN_FILE], node=failovermaster)
+ AssertCommand(["touch", pathutils.JOB_QUEUE_DRAIN_FILE], node=failovermaster)
cmd = ["gnt-cluster", "master-failover"]
try:
# Back to original master node
AssertCommand(cmd, node=master)
finally:
- qa_config.ReleaseNode(failovermaster)
+ failovermaster.Release()
AssertCommand(drain_check, fail=True)
AssertCommand(drain_check, node=failovermaster, fail=True)
f.seek(0)
# Upload file to master node
- testname = qa_utils.UploadFile(master["primary"], f.name)
+ testname = qa_utils.UploadFile(master.primary, f.name)
try:
# Copy file to all nodes
AssertCommand(["gnt-cluster", "copyfile", testname])
def TestClusterRepairDiskSizes():
"""gnt-cluster repair-disk-sizes"""
AssertCommand(["gnt-cluster", "repair-disk-sizes"])
+
+
+def TestSetExclStorCluster(newvalue):
+ """Set the exclusive_storage node parameter at the cluster level.
+
+ @type newvalue: bool
+ @param newvalue: New value of exclusive_storage
+ @rtype: bool
+ @return: The old value of exclusive_storage
+
+ """
+ oldvalue = _GetBoolClusterField("exclusive_storage")
+ AssertCommand(["gnt-cluster", "modify", "--node-parameters",
+ "exclusive_storage=%s" % newvalue])
+ effvalue = _GetBoolClusterField("exclusive_storage")
+ if effvalue != newvalue:
+ raise qa_error.Error("exclusive_storage has the wrong value: %s instead"
+ " of %s" % (effvalue, newvalue))
+ qa_config.SetExclusiveStorage(newvalue)
+ return oldvalue
+
+
+def TestExclStorSharedPv(node):
+ """cluster-verify reports LVs that share the same PV with exclusive_storage.
+
+ """
+ vgname = qa_config.get("vg-name", constants.DEFAULT_VG)
+ lvname1 = _QA_LV_PREFIX + "vol1"
+ lvname2 = _QA_LV_PREFIX + "vol2"
+ node_name = node.primary
+ AssertCommand(["lvcreate", "-L1G", "-n", lvname1, vgname], node=node_name)
+ AssertClusterVerify(fail=True, errors=[constants.CV_ENODEORPHANLV])
+ AssertCommand(["lvcreate", "-L1G", "-n", lvname2, vgname], node=node_name)
+ AssertClusterVerify(fail=True, errors=[constants.CV_ENODELVM,
+ constants.CV_ENODEORPHANLV])
+ AssertCommand(["lvremove", "-f", "/".join([vgname, lvname1])], node=node_name)
+ AssertCommand(["lvremove", "-f", "/".join([vgname, lvname2])], node=node_name)
+ AssertClusterVerify()