Commit d80cb8c4 authored by Michael Hanselmann's avatar Michael Hanselmann

Merge branch 'stable-2.1' into devel-2.1

* stable-2.1:
  Bump version to 2.1.0~rc4
  KVM: fix pylint warning
  KVM: be more resilient on broken migration answers
  Add unittests for cli.GenerateTable
  cli: Fix bug when not using headers
  daemon-util: Fix quoting issue
  Bump version to 2.1.0~rc3
parents c7c568b4 0ea0ce20
......@@ -2,7 +2,7 @@
m4_define([gnt_version_major], [2])
m4_define([gnt_version_minor], [1])
m4_define([gnt_version_revision], [0])
m4_define([gnt_version_suffix], [~rc2])
m4_define([gnt_version_suffix], [~rc4])
m4_define([gnt_version_full],
m4_format([%d.%d.%d%s],
gnt_version_major, gnt_version_minor,
......
......@@ -62,7 +62,7 @@ start() {
local ucname=$(tr a-z A-Z <<< ${name#ganeti-})
# Read $<daemon>_ARGS and $EXTRA_<daemon>_ARGS
eval local args="\$${ucname}_ARGS \$EXTRA_${ucname}_ARGS"
eval local args="\"\$${ucname}_ARGS \$EXTRA_${ucname}_ARGS\""
start-stop-daemon --start --quiet --oknodo \
--pidfile $(_daemon_pidfile $name) \
......
......@@ -1575,14 +1575,17 @@ def GenerateTable(headers, fields, separator, data,
for idx, name in enumerate(fields):
hdr = headers[name]
if separator is None:
if idx == len(fields) - 1 and not numfields.Matches(name):
mlens[idx] = 0
else:
mlens[idx] = max(mlens[idx], len(hdr))
mlens[idx] = max(mlens[idx], len(hdr))
args.append(mlens[idx])
args.append(hdr)
result.append(format % tuple(args))
if separator is None:
assert len(mlens) == len(fields)
if fields and not numfields.Matches(fields[-1]):
mlens[-1] = 0
for line in data:
args = []
if line is None:
......
......@@ -80,6 +80,8 @@ class KVMHypervisor(hv_base.BaseHypervisor):
_MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
re.M | re.I)
_MIGRATION_INFO_MAX_BAD_ANSWERS = 5
_MIGRATION_INFO_RETRY_DELAY = 2
_KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
......@@ -675,26 +677,37 @@ class KVMHypervisor(hv_base.BaseHypervisor):
info_command = 'info migrate'
done = False
broken_answers = 0
while not done:
result = self._CallMonitorCommand(instance_name, info_command)
match = self._MIGRATION_STATUS_RE.search(result.stdout)
if not match:
raise errors.HypervisorError("Unknown 'info migrate' result: %s" %
result.stdout)
broken_answers += 1
if not result.stdout:
logging.info("KVM: empty 'info migrate' result")
else:
logging.warning("KVM: unknown 'info migrate' result: %s",
result.stdout)
time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
else:
status = match.group(1)
if status == 'completed':
done = True
elif status == 'active':
time.sleep(2)
# reset the broken answers count
broken_answers = 0
time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
elif status == 'failed' or status == 'cancelled':
if not live:
self._CallMonitorCommand(instance_name, 'cont')
raise errors.HypervisorError("Migration %s at the kvm level" %
status)
else:
logging.info("KVM: unknown migration status '%s'", status)
time.sleep(2)
logging.warning("KVM: unknown migration status '%s'", status)
broken_answers += 1
time.sleep(self._MIGRATION_INFO_RETRY_DELAY)
if broken_answers >= self._MIGRATION_INFO_MAX_BAD_ANSWERS:
raise errors.HypervisorError("Too many 'info migrate' broken answers")
utils.KillProcess(pid)
self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
......
......@@ -124,5 +124,127 @@ class TestToStream(unittest.TestCase):
cli._ToStream(buf, "foo %s %s", "a", "b")
self.failUnlessEqual(buf.getvalue(), "foo a b\n")
class TestGenerateTable(unittest.TestCase):
HEADERS = dict([("f%s" % i, "Field%s" % i) for i in range(5)])
FIELDS1 = ["f1", "f2"]
DATA1 = [
["abc", 1234],
["foobar", 56],
["b", -14],
]
def _test(self, headers, fields, separator, data,
numfields, unitfields, units, expected):
table = cli.GenerateTable(headers, fields, separator, data,
numfields=numfields, unitfields=unitfields,
units=units)
self.assertEqual(table, expected)
def testPlain(self):
exp = [
"Field1 Field2",
"abc 1234",
"foobar 56",
"b -14",
]
self._test(self.HEADERS, self.FIELDS1, None, self.DATA1,
None, None, "m", exp)
def testNoFields(self):
self._test(self.HEADERS, [], None, [[], []],
None, None, "m", ["", "", ""])
self._test(None, [], None, [[], []],
None, None, "m", ["", ""])
def testSeparator(self):
for sep in ["#", ":", ",", "^", "!", "%", "|", "###", "%%", "!!!", "||"]:
exp = [
"Field1%sField2" % sep,
"abc%s1234" % sep,
"foobar%s56" % sep,
"b%s-14" % sep,
]
self._test(self.HEADERS, self.FIELDS1, sep, self.DATA1,
None, None, "m", exp)
def testNoHeader(self):
exp = [
"abc 1234",
"foobar 56",
"b -14",
]
self._test(None, self.FIELDS1, None, self.DATA1,
None, None, "m", exp)
def testUnknownField(self):
headers = {
"f1": "Field1",
}
exp = [
"Field1 UNKNOWN",
"abc 1234",
"foobar 56",
"b -14",
]
self._test(headers, ["f1", "UNKNOWN"], None, self.DATA1,
None, None, "m", exp)
def testNumfields(self):
fields = ["f1", "f2", "f3"]
data = [
["abc", 1234, 0],
["foobar", 56, 3],
["b", -14, "-"],
]
exp = [
"Field1 Field2 Field3",
"abc 1234 0",
"foobar 56 3",
"b -14 -",
]
self._test(self.HEADERS, fields, None, data,
["f2", "f3"], None, "m", exp)
def testUnitfields(self):
expnosep = [
"Field1 Field2 Field3",
"abc 1234 0M",
"foobar 56 3M",
"b -14 -",
]
expsep = [
"Field1:Field2:Field3",
"abc:1234:0M",
"foobar:56:3M",
"b:-14:-",
]
for sep, expected in [(None, expnosep), (":", expsep)]:
fields = ["f1", "f2", "f3"]
data = [
["abc", 1234, 0],
["foobar", 56, 3],
["b", -14, "-"],
]
self._test(self.HEADERS, fields, sep, data,
["f2", "f3"], ["f3"], "h", expected)
def testUnusual(self):
data = [
["%", "xyz"],
["%%", "abc"],
]
exp = [
"Field1 Field2",
"% xyz",
"%% abc",
]
self._test(self.HEADERS, ["f1", "f2"], None, data,
None, None, "m", exp)
if __name__ == '__main__':
testutils.GanetiTestProgram()
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