Commit 2f4b4f78 authored by Iustin Pop's avatar Iustin Pop

Simplify QA commands

Currently, 95% of the QA commands are executed in the same way: on the
master, based on a command list and with expectancies for succes:

    AssertEqual(StartSSH(master['primary'],
                         utils.ShellQuoteArgs(cmd)).wait(), 0)

The rest 5% are variations on this theme (maybe the command needs to
fail, or the node is different, etc.). Based on this, we can simplify
the code significantly if we abstract the common theme into a new
AssertCommand() function. This saves ~250 lines of code in the QA suite,
around 8% of the entire QA code size.

Additionally, the output was very cryptic before (the famous "QA error:
1 != 0" messages), whereas now we show a clear error message (node,
command, exit code and failure mode).

The patch replaces single quotes with double quotes in all the parts of
the code that I touch; let me know if that's not OK…
Signed-off-by: default avatarIustin Pop <iustin@google.com>
Reviewed-by: default avatarMichael Hanselmann <hansmi@google.com>
parent f394c0de
This diff is collapsed.
......@@ -32,15 +32,15 @@ import qa_config
import qa_utils
import qa_error
from qa_utils import AssertEqual, AssertMatch, StartSSH, GetCommandOutput
from qa_utils import AssertMatch, AssertCommand, StartSSH, GetCommandOutput
def _InstanceRunning(node, name):
"""Checks whether an instance is running.
Args:
node: Node the instance runs on
name: Full name of Xen instance
@param node: node the instance runs on
@param name: full name of the Xen instance
"""
cmd = utils.ShellQuoteArgs(['xm', 'list', name]) + ' >/dev/null'
ret = StartSSH(node['primary'], cmd).wait()
......@@ -50,15 +50,11 @@ def _InstanceRunning(node, name):
def _XmShutdownInstance(node, name):
"""Shuts down instance using "xm" and waits for completion.
Args:
node: Node the instance runs on
name: Full name of Xen instance
"""
master = qa_config.GetMasterNode()
@param node: node the instance runs on
@param name: full name of Xen instance
cmd = ['xm', 'shutdown', name]
AssertEqual(StartSSH(node['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
"""
AssertCommand(["xm", "shutdown", name], node=node)
# Wait up to a minute
end = time.time() + 60
......@@ -73,25 +69,15 @@ def _XmShutdownInstance(node, name):
def _ResetWatcherDaemon():
"""Removes the watcher daemon's state file.
Args:
node: Node to be reset
"""
master = qa_config.GetMasterNode()
cmd = ['rm', '-f', constants.WATCHER_STATEFILE]
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["rm", "-f", constants.WATCHER_STATEFILE])
def _RunWatcherDaemon():
"""Runs the ganeti-watcher daemon on the master node.
"""
master = qa_config.GetMasterNode()
cmd = ["ganeti-watcher", "-d", "--ignore-pause"]
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["ganeti-watcher", "-d", "--ignore-pause"])
def TestPauseWatcher():
......@@ -100,9 +86,7 @@ def TestPauseWatcher():
"""
master = qa_config.GetMasterNode()
cmd = ["gnt-cluster", "watcher", "pause", "4h"]
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-cluster", "watcher", "pause", "4h"])
cmd = ["gnt-cluster", "watcher", "info"]
output = GetCommandOutput(master["primary"],
......@@ -116,9 +100,7 @@ def TestResumeWatcher():
"""
master = qa_config.GetMasterNode()
cmd = ["gnt-cluster", "watcher", "continue"]
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-cluster", "watcher", "continue"])
cmd = ["gnt-cluster", "watcher", "info"]
output = GetCommandOutput(master["primary"],
......@@ -130,7 +112,6 @@ def TestInstanceAutomaticRestart(node, instance):
"""Test automatic restart of instance by ganeti-watcher.
"""
master = qa_config.GetMasterNode()
inst_name = qa_utils.ResolveInstanceName(instance["name"])
_ResetWatcherDaemon()
......@@ -142,16 +123,13 @@ def TestInstanceAutomaticRestart(node, instance):
if not _InstanceRunning(node, inst_name):
raise qa_error.Error("Daemon didn't restart instance")
cmd = ['gnt-instance', 'info', inst_name]
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-instance", "info", inst_name])
def TestInstanceConsecutiveFailures(node, instance):
"""Test five consecutive instance failures.
"""
master = qa_config.GetMasterNode()
inst_name = qa_utils.ResolveInstanceName(instance["name"])
_ResetWatcherDaemon()
......@@ -168,6 +146,4 @@ def TestInstanceConsecutiveFailures(node, instance):
msg = "Instance started when it shouldn't"
raise qa_error.Error(msg)
cmd = ['gnt-instance', 'info', inst_name]
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-instance", "info", inst_name])
......@@ -26,17 +26,16 @@
from ganeti import utils
import qa_config
import qa_utils
from qa_utils import AssertEqual, StartSSH
from qa_utils import AssertCommand
def TestSshConnection():
"""Test SSH connection.
"""
for node in qa_config.get('nodes'):
AssertEqual(StartSSH(node['primary'], 'exit').wait(), 0)
for node in qa_config.get("nodes"):
AssertCommand("exit", node=node)
def TestGanetiCommands():
......@@ -60,7 +59,7 @@ def TestGanetiCommands():
cmd = ' && '.join([utils.ShellQuoteArgs(i) for i in cmds])
for node in qa_config.get('nodes'):
AssertEqual(StartSSH(node['primary'], cmd).wait(), 0)
AssertCommand(cmd, node=node)
def TestIcmpPing():
......@@ -74,14 +73,14 @@ def TestIcmpPing():
if qa_config.get("primary_ip_version") == 6:
pingprimary = "ping6"
for node in nodes:
check = []
for i in nodes:
cmd = [pingprimary] + pingargs + [i['primary']]
check = []
for i in nodes:
cmd = [pingprimary] + pingargs + [i["primary"]]
check.append(utils.ShellQuoteArgs(cmd))
if i.has_key("secondary"):
cmd = ["ping"] + pingargs + [i["secondary"]]
check.append(utils.ShellQuoteArgs(cmd))
if i.has_key('secondary'):
cmd = ["ping"] + pingargs + [i["secondary"]]
check.append(utils.ShellQuoteArgs(cmd))
cmdall = " && ".join(check)
cmdall = ' && '.join(check)
AssertEqual(StartSSH(node['primary'], cmdall).wait(), 0)
for node in nodes:
AssertCommand(cmdall, node=node)
This diff is collapsed.
......@@ -26,12 +26,10 @@ import qa_config
import qa_error
import qa_utils
from qa_utils import AssertEqual, AssertNotEqual, StartSSH
from qa_utils import AssertCommand
def _NodeAdd(node, readd=False):
master = qa_config.GetMasterNode()
if not readd and node.get('_added', False):
raise qa_error.Error("Node %s already in cluster" % node['primary'])
elif readd and not node.get('_added', False):
......@@ -43,18 +41,14 @@ def _NodeAdd(node, readd=False):
if readd:
cmd.append('--readd')
cmd.append(node['primary'])
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(cmd)
node['_added'] = True
def _NodeRemove(node):
master = qa_config.GetMasterNode()
cmd = ['gnt-node', 'remove', node['primary']]
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-node", "remove", node["primary"]])
node['_added'] = False
......@@ -93,20 +87,12 @@ def TestNodeReadd(node):
def TestNodeInfo():
"""gnt-node info"""
master = qa_config.GetMasterNode()
cmd = ['gnt-node', 'info']
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-node", "info"])
def TestNodeVolumes():
"""gnt-node volumes"""
master = qa_config.GetMasterNode()
cmd = ['gnt-node', 'volumes']
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-node", "volumes"])
def TestNodeStorage():
......@@ -115,16 +101,13 @@ def TestNodeStorage():
for storage_type in constants.VALID_STORAGE_TYPES:
# Test simple list
cmd = ["gnt-node", "list-storage", "--storage-type", storage_type]
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-node", "list-storage", "--storage-type", storage_type])
# Test all storage fields
cmd = ["gnt-node", "list-storage", "--storage-type", storage_type,
"--output=%s" % ",".join(list(constants.VALID_STORAGE_FIELDS) +
[constants.SF_NODE, constants.SF_TYPE])]
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(cmd)
# Get list of valid storage devices
cmd = ["gnt-node", "list-storage", "--storage-type", storage_type,
......@@ -141,8 +124,7 @@ def TestNodeStorage():
# Dummy modification without any changes
cmd = ["gnt-node", "modify-storage", node_name, storage_type, st_name]
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(cmd)
# Make sure we end up with the same value as before
if st_allocatable.lower() == "y":
......@@ -150,55 +132,36 @@ def TestNodeStorage():
else:
test_allocatable = ["yes", "no"]
if (constants.SF_ALLOCATABLE in
constants.MODIFIABLE_STORAGE_FIELDS.get(storage_type, [])):
assert_fn = AssertEqual
else:
assert_fn = AssertNotEqual
fail = (constants.SF_ALLOCATABLE not in
constants.MODIFIABLE_STORAGE_FIELDS.get(storage_type, []))
for i in test_allocatable:
cmd = ["gnt-node", "modify-storage", "--allocatable", i,
node_name, storage_type, st_name]
assert_fn(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-node", "modify-storage", "--allocatable", i,
node_name, storage_type, st_name], fail=fail)
# Test repair functionality
cmd = ["gnt-node", "repair-storage", node_name, storage_type, st_name]
if (constants.SO_FIX_CONSISTENCY in
constants.VALID_STORAGE_OPERATIONS.get(storage_type, [])):
assert_fn = AssertEqual
else:
assert_fn = AssertNotEqual
assert_fn(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
fail = (constants.SO_FIX_CONSISTENCY not in
constants.VALID_STORAGE_OPERATIONS.get(storage_type, []))
AssertCommand(["gnt-node", "repair-storage", node_name,
storage_type, st_name], fail=fail)
def TestNodeFailover(node, node2):
"""gnt-node failover"""
master = qa_config.GetMasterNode()
if qa_utils.GetNodeInstances(node2, secondaries=False):
raise qa_error.UnusableNodeError("Secondary node has at least one"
" primary instance. This test requires"
" it to have no primary instances.")
# Fail over to secondary node
cmd = ['gnt-node', 'failover', '-f', node['primary']]
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-node", "failover", "-f", node["primary"]])
# ... and back again.
cmd = ['gnt-node', 'failover', '-f', node2['primary']]
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-node", "failover", "-f", node2["primary"]])
def TestNodeEvacuate(node, node2):
"""gnt-node evacuate"""
master = qa_config.GetMasterNode()
node3 = qa_config.AcquireNode(exclude=[node, node2])
try:
if qa_utils.GetNodeInstances(node3, secondaries=True):
......@@ -207,32 +170,22 @@ def TestNodeEvacuate(node, node2):
" it to have no secondary instances.")
# Evacuate all secondary instances
cmd = ['gnt-node', 'evacuate', '-f',
"--new-secondary=%s" % node3['primary'], node2['primary']]
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-node", "evacuate", "-f",
"--new-secondary=%s" % node3["primary"], node2["primary"]])
# ... and back again.
cmd = ['gnt-node', 'evacuate', '-f',
"--new-secondary=%s" % node2['primary'], node3['primary']]
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-node", "evacuate", "-f",
"--new-secondary=%s" % node2["primary"], node3["primary"]])
finally:
qa_config.ReleaseNode(node3)
def TestNodeModify(node):
"""gnt-node modify"""
master = qa_config.GetMasterNode()
for flag in ["master-candidate", "drained", "offline"]:
for value in ["yes", "no"]:
cmd = ["gnt-node", "modify", "--force",
"--%s=%s" % (flag, value), node["primary"]]
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
cmd = ["gnt-node", "modify", "--master-candidate=yes", "--auto-promote",
node["primary"]]
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-node", "modify", "--force",
"--%s=%s" % (flag, value), node["primary"]])
AssertCommand(["gnt-node", "modify", "--master-candidate=yes",
"--auto-promote", node["primary"]])
......@@ -32,7 +32,7 @@ from ganeti import constants
import qa_config
import qa_utils
from qa_utils import AssertEqual, StartSSH
from qa_utils import AssertCommand
_TEMP_OS_NAME = "TEMP-Ganeti-QA-OS"
......@@ -41,26 +41,16 @@ _TEMP_OS_PATH = os.path.join(constants.OS_SEARCH_PATH[0], _TEMP_OS_NAME)
def TestOsList():
"""gnt-os list"""
master = qa_config.GetMasterNode()
cmd = ['gnt-os', 'list']
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-os", "list"])
def TestOsDiagnose():
"""gnt-os diagnose"""
master = qa_config.GetMasterNode()
cmd = ['gnt-os', 'diagnose']
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["gnt-os", "diagnose"])
def _TestOsModify(hvp_dict, expected_result=0):
def _TestOsModify(hvp_dict, fail=False):
"""gnt-os modify"""
master = qa_config.GetMasterNode()
cmd = ['gnt-os', 'modify']
for hv_name, hv_params in hvp_dict.items():
......@@ -71,24 +61,19 @@ def _TestOsModify(hvp_dict, expected_result=0):
cmd.append('%s:%s' % (hv_name, ','.join(options)))
cmd.append(_TEMP_OS_NAME)
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), expected_result)
AssertCommand(cmd, fail=fail)
def _TestOsStates():
"""gnt-os modify, more stuff"""
master = qa_config.GetMasterNode()
cmd = ["gnt-os", "modify"]
for param in ["hidden", "blacklisted"]:
for val in ["yes", "no"]:
new_cmd = cmd + ["--%s" % param, val, _TEMP_OS_NAME]
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(new_cmd)).wait(), 0)
AssertCommand(new_cmd)
# check that double-running the command is OK
AssertEqual(StartSSH(master["primary"],
utils.ShellQuoteArgs(new_cmd)).wait(), 0)
AssertCommand(new_cmd)
def _SetupTempOs(node, dir, valid):
......@@ -115,29 +100,25 @@ def _SetupTempOs(node, dir, valid):
(node["primary"],
["an invalid", "a valid"][int(valid)]))
AssertEqual(StartSSH(node['primary'], cmd).wait(), 0)
AssertCommand(cmd, node=node)
def _RemoveTempOs(node, dir):
"""Removes a temporary OS definition.
"""
cmd = ['rm', '-rf', dir]
AssertEqual(StartSSH(node['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
AssertCommand(["rm", "-rf", dir], node=node)
def _TestOs(mode):
"""Generic function for OS definition testing
"""
master = qa_config.GetMasterNode()
dir = _TEMP_OS_PATH
nodes = []
try:
i = 0
for node in qa_config.get('nodes'):
for i, node in enumerate(qa_config.get("nodes")):
nodes.append(node)
if mode == 0:
valid = False
......@@ -146,15 +127,8 @@ def _TestOs(mode):
else:
valid = bool(i % 2)
_SetupTempOs(node, dir, valid)
i += 1
cmd = ['gnt-os', 'diagnose']
result = StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait()
if mode == 1:
AssertEqual(result, 0)
else:
AssertEqual(result, 1)
AssertCommand(["gnt-os", "diagnose"], fail=not mode==1)
finally:
for node in nodes:
_RemoveTempOs(node, dir)
......@@ -196,7 +170,7 @@ def TestOsModifyInvalid():
"blahblahblubb": {"bar": ""},
}
return _TestOsModify(hv_dict, 1)
return _TestOsModify(hv_dict, fail=True)
def TestOsStates():
......
......@@ -20,14 +20,11 @@
"""
from ganeti import utils
from ganeti import constants
import qa_config
import qa_utils
import qa_rapi
from qa_utils import AssertEqual, StartSSH
from qa_utils import AssertCommand
_TEMP_TAG_NAMES = ["TEMP-Ganeti-QA-Tag%d" % i for i in range(3)]
......@@ -44,8 +41,6 @@ def _TestTags(kind, name):
"""Generic function for add-tags.
"""
master = qa_config.GetMasterNode()
def cmdfn(subcmd):
cmd = [_KIND_TO_COMMAND[kind], subcmd]
......@@ -54,21 +49,13 @@ def _TestTags(kind, name):
return cmd
cmd = cmdfn('add-tags') + _TEMP_TAG_NAMES
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
cmd = cmdfn('list-tags')
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
cmd = ['gnt-cluster', 'search-tags', _TEMP_TAG_RE]
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
cmd = cmdfn('remove-tags') + _TEMP_TAG_NAMES
AssertEqual(StartSSH(master['primary'],
utils.ShellQuoteArgs(cmd)).wait(), 0)
for cmd in [
cmdfn("add-tags") + _TEMP_TAG_NAMES,
cmdfn("list-tags"),
["gnt-cluster", "search-tags", _TEMP_TAG_RE],
cmdfn("remove-tags") + _TEMP_TAG_NAMES,
]:
AssertCommand(cmd)
if qa_rapi.Enabled():
qa_rapi.TestTags(kind, name, _TEMP_TAG_NAMES)
......
......@@ -101,6 +101,43 @@ def AssertMatch(string, pattern):
raise qa_error.Error("%r doesn't match /%r/" % (string, pattern))
def AssertCommand(cmd, fail=False, node=None):
"""Checks that a remote command succeeds.
@param cmd: either a string (the command to execute) or a list (to
be converted using L{utils.ShellQuoteArgs} into a string)
@type fail: boolean
@param fail: if the command is expected to fail instead of succeeding
@param node: if passed, it should be the node on which the command
should be executed, instead of the master node (can be either a
dict or a string)
"""
if node is None:
node = qa_config.GetMasterNode()
if isinstance(node, basestring):
nodename = node
else:
nodename = node["primary"]
if isinstance(cmd, basestring):
cmdstr = cmd
else:
cmdstr = utils.ShellQuoteArgs(cmd)
rcode = StartSSH(nodename, cmdstr).wait()
if fail:
if rcode == 0:
raise qa_error.Error("Command '%s' on node %s was expected to fail but"
" didn't" % (cmdstr, nodename))
else:
if rcode != 0:
raise qa_error.Error("Command '%s' on node %s failed, exit code %s" %
(cmdstr, nodename, rcode))
def GetSSHCommand(node, cmd, strict=True):
"""Builds SSH command to be executed.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment