Commit 1c5456b5 authored by Hrvoje Ribicic's avatar Hrvoje Ribicic
Browse files

Merge branch 'stable-2.10' into stable-2.11

* stable-2.10
  Make QA fail if KVM hotplugging fails
  Always preserve QA command output
  Don't lose stdout/stderr in AssertCommand
  qa_utils: Allow passing fail=None to AssertCommand
  qa_utils: Make AssertCommand return stdout/stderr as well
  Allow plain/DRBD conversions regardless of lack of disks
  Add support for ipolicy modifications to mock config
Signed-off-by: default avatarHrvoje Ribicic <>
Reviewed-by: default avatarHelga Velroyen <>
parents 0e5e0f3a f879621b
......@@ -3282,11 +3282,16 @@ class LUInstanceSetParams(LogicalUnit):
"""Converts an instance from drbd to plain.
assert len(self.instance.secondary_nodes) == 1
assert self.instance.disk_template == constants.DT_DRBD8
assert len(self.instance.secondary_nodes) == 1 or not self.instance.disks
pnode_uuid = self.instance.primary_node
snode_uuid = self.instance.secondary_nodes[0]
# it will not be possible to calculate the snode_uuid later
snode_uuid = None
if self.instance.secondary_nodes:
snode_uuid = self.instance.secondary_nodes[0]
feedback_fn("Converting template to plain")
old_disks = AnnotateDiskParams(self.instance, self.instance.disks, self.cfg)
......@@ -521,6 +521,25 @@ def TestInstanceInfo(instance):
AssertCommand(["gnt-instance", "info",])
def _TestKVMHotplug(instance):
"""Tests hotplug modification commands, noting that they
args_to_try = [
["--net", "-1:add", "--hotplug"],
["--net", "-1:modify,mac=aa:bb:cc:dd:ee:ff", "--hotplug", "--force"],
["--net", "-1:remove", "--hotplug"],
["--disk", "-1:add,size=1G", "--hotplug"],
["--disk", "-1:remove", "--hotplug"],
for alist in args_to_try:
_, stdout, stderr = \
AssertCommand(["gnt-instance", "modify"] + alist + [])
if "failed" in stdout or "failed" in stderr:
raise qa_error.Error("Hotplugging command failed; please check output"
" for further information")
def TestInstanceModify(instance):
"""gnt-instance modify"""
......@@ -566,13 +585,7 @@ def TestInstanceModify(instance):
elif default_hv == constants.HT_KVM and \
["--net", "-1:add", "--hotplug"],
["--net", "-1:modify,mac=aa:bb:cc:dd:ee:ff", "--hotplug", "--force"],
["--net", "-1:remove", "--hotplug"],
["--disk", "-1:add,size=1G", "--hotplug"],
["--disk", "-1:remove", "--hotplug"],
for alist in args:
AssertCommand(["gnt-instance", "modify"] + alist + [])
......@@ -152,13 +152,33 @@ def _AssertRetCode(rcode, fail, cmdstr, nodename):
(cmdstr, nodename, rcode))
def _PrintCommandOutput(stdout, stderr):
"""Prints the output of commands, minimizing wasted space.
@type stdout: string
@type stderr: string
if stdout:
stdout_clean = stdout.rstrip('\n')
if stderr:
print "Stdout was:\n%s" % stdout_clean
print stdout_clean
if stderr:
print "Stderr was:"
print >> sys.stderr, stderr.rstrip('\n')
def AssertCommand(cmd, fail=False, node=None, log_cmd=True, max_seconds=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
@type fail: boolean or None
@param fail: if the command is expected to fail instead of succeeding,
or None if we don't care
@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)
......@@ -167,7 +187,7 @@ def AssertCommand(cmd, fail=False, node=None, log_cmd=True, max_seconds=None):
@type max_seconds: double
@param max_seconds: fail if the command takes more than C{max_seconds}
@return: the return code of the command
@return: the return code, stdout and stderr of the command
@raise qa_error.Error: if the command fails when it shouldn't or vice versa
......@@ -182,9 +202,18 @@ def AssertCommand(cmd, fail=False, node=None, log_cmd=True, max_seconds=None):
cmdstr = utils.ShellQuoteArgs(cmd)
start =
rcode = StartSSH(nodename, cmdstr, log_cmd=log_cmd).wait()
popen = StartSSH(nodename, cmdstr, log_cmd=log_cmd)
# Run the command
stdout, stderr = popen.communicate()
rcode = popen.returncode
duration_seconds = TimedeltaToTotalSeconds( - start)
_AssertRetCode(rcode, fail, cmdstr, nodename)
if fail is not None:
_AssertRetCode(rcode, fail, cmdstr, nodename)
if log_cmd:
_PrintCommandOutput(stdout, stderr)
if max_seconds is not None:
if duration_seconds > max_seconds:
......@@ -192,7 +221,7 @@ def AssertCommand(cmd, fail=False, node=None, log_cmd=True, max_seconds=None):
"Cmd '%s' took %f seconds, maximum of %f was exceeded" %
(cmdstr, duration_seconds, max_seconds))
return rcode
return rcode, stdout, stderr
def AssertRedirectedCommand(cmd, fail=False, node=None, log_cmd=True):
......@@ -295,7 +324,8 @@ def StartSSH(node, cmd, strict=True, log_cmd=True):
return StartLocalCommand(GetSSHCommand(node, cmd, strict=strict),
_nolog_opts=True, log_cmd=log_cmd)
_nolog_opts=True, log_cmd=log_cmd,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def StartMultiplexer(node):
......@@ -552,10 +582,10 @@ def GenericQueryTest(cmd, fields, namefield="name", test_unknown=True):
# Check exit code for listing unknown field
AssertEqual(AssertRedirectedCommand([cmd, "list",
rcode, _, _ = AssertRedirectedCommand([cmd, "list",
AssertEqual(rcode, constants.EXIT_UNKNOWN_FIELD)
def GenericQueryFieldsTest(cmd, fields):
......@@ -573,9 +603,9 @@ def GenericQueryFieldsTest(cmd, fields):
# Check exit code for listing unknown field
AssertEqual(AssertCommand([cmd, "list-fields", "field/does/not/exist"],
rcode, _, _ = AssertCommand([cmd, "list-fields", "field/does/not/exist"],
AssertEqual(rcode, constants.EXIT_UNKNOWN_FIELD)
def AddToEtcHosts(hostnames):
......@@ -2367,6 +2367,30 @@ class TestLUInstanceSetParams(CmdlibTestCase):
def testConvertDisklessDRBDToPlain(self):
constants.ISPECS_MIN, constants.ISPEC_DISK_COUNT, 0)
self.inst.disks = []
self.inst.disk_template = constants.DT_DRBD8
op = self.CopyOpCode(self.op,
self.assertEqual(self.inst.disk_template, constants.DT_PLAIN)
def testConvertDisklessPlainToDRBD(self):
constants.ISPECS_MIN, constants.ISPEC_DISK_COUNT, 0)
self.inst.disks = []
self.inst.disk_template = constants.DT_PLAIN
op = self.CopyOpCode(self.op,
self.assertEqual(self.inst.disk_template, constants.DT_DRBD8)
class TestLUInstanceChangeGroup(CmdlibTestCase):
def setUp(self):
......@@ -549,6 +549,26 @@ class ConfigMock(config.ConfigWriter):
cluster.enabled_disk_templates = list(enabled_disk_templates)
cluster.ipolicy[constants.IPOLICY_DTS] = list(enabled_disk_templates)
def SetIPolicyField(self, category, field, value):
"""Set a value of a desired ipolicy field.
@type category: one of L{constants.ISPECS_MAX}, L{constants.ISPECS_MIN},
@param category: Whether to change the default value, or the upper or lower
@type field: string
@param field: The field to change.
@type value: any
@param value: The value to assign.
if category not in [constants.ISPECS_MAX, constants.ISPECS_MIN,
raise ValueError("Invalid ipolicy category %s" % category)
ipolicy_dict = self.GetClusterInfo().ipolicy[constants.ISPECS_MINMAX][0]
ipolicy_dict[category][field] = value
def _OpenConfig(self, accept_foreign):
self._config_data = objects.ConfigData(
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