Now LXC hypervisor requires LXC >= 1.0.0 for all commands used by it. Adding _VerifyLXCCommands() to provide checking for each commands existence and version satisfaction.
Signed-off-by: Yuto KAWAMURA(kawamuray) <[email protected]> --- lib/hypervisor/hv_lxc.py | 66 ++++++++++++++++++++++++++++ test/py/ganeti.hypervisor.hv_lxc_unittest.py | 50 +++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/lib/hypervisor/hv_lxc.py b/lib/hypervisor/hv_lxc.py index 3e1269e..f7c54c6 100644 --- a/lib/hypervisor/hv_lxc.py +++ b/lib/hypervisor/hv_lxc.py @@ -27,6 +27,7 @@ import os import os.path import logging import sys +import re from ganeti import constants from ganeti import errors # pylint: disable=W0611 @@ -59,6 +60,15 @@ class LXCHypervisor(hv_base.BaseHypervisor): _PROC_CGROUPS_FILE = "/proc/cgroups" _PROC_SELF_CGROUP_FILE = "/proc/self/cgroup" + _LXC_MIN_VERSION_REQUIRED = "1.0.0" + _LXC_COMMANDS_REQUIRED = [ + "lxc-console", + "lxc-ls", + "lxc-start", + "lxc-stop", + "lxc-wait", + ] + _DEVS = [ "c 1:3", # /dev/null "c 1:5", # /dev/zero @@ -87,6 +97,9 @@ class LXCHypervisor(hv_base.BaseHypervisor): constants.HV_LXC_STARTUP_WAIT: hv_base.OPT_NONNEGATIVE_INT_CHECK, } + # Let beta version following micro version, but don't care about it + _LXC_VERSION_RE = re.compile(r"^(\d+)\.(\d+)\.(\d+)") + def __init__(self): hv_base.BaseHypervisor.__init__(self) utils.EnsureDirs([ @@ -699,6 +712,57 @@ class LXCHypervisor(hv_base.BaseHypervisor): user=constants.SSH_CONSOLE_USER, command=["lxc-console", "-n", instance.name]) + @classmethod + def _ParseLXCVersion(cls, version_string): + """Return a parsed result of lxc version string. + + @return tuple of major, minor and micro version number + @rtype tuple(int, int, int) + + """ + match = cls._LXC_VERSION_RE.match(version_string) + return tuple(map(int, match.groups())) if match else None + + @classmethod + def _VerifyLXCCommands(cls): + """Verify the validity of lxc command line tools. + + """ + version_required = cls._ParseLXCVersion(cls._LXC_MIN_VERSION_REQUIRED) + msgs = [] + for cmd in cls._LXC_COMMANDS_REQUIRED: + try: + # lxc-ls needs special checking procedure. + # there is two different version of lxc-ls, one is written in python + # and the other is written in shell script. + # we have to ensure python version of lxc-ls command is installed. + if cmd == "lxc-ls": + help_string = utils.RunCmd(["lxc-ls", "--help"]).output + if "--running" not in help_string: + # shell script version has no --running switch + msgs.append("Python version of 'lxc-ls' command is required." + " Maybe lxc was installed without --enable-python") + else: + result = utils.RunCmd([cmd, "--version"]) + if result.failed: + msgs.append("Can't get version info from %s: %s" % + (cmd, result.output)) + else: + version_str = result.stdout.strip() + version = cls._ParseLXCVersion(version_str) + if version: + if version < version_required: + msgs.append("LXC version >= %s is required but command %s has" + " version %s" % + (cls._LXC_MIN_VERSION_REQUIRED, cmd, version_str)) + else: + msgs.append("Can't parse version info from %s output: %s" % + (cmd, version_str)) + except errors.OpExecError: + msgs.append("Required command %s not found" % cmd) + + return msgs + def Verify(self, hvparams=None): """Verify the hypervisor. @@ -721,6 +785,8 @@ class LXCHypervisor(hv_base.BaseHypervisor): except errors.HypervisorError, err: msgs.append(str(err)) + msgs.extend(self._VerifyLXCCommands()) + return self._FormatVerifyResults(msgs) @classmethod diff --git a/test/py/ganeti.hypervisor.hv_lxc_unittest.py b/test/py/ganeti.hypervisor.hv_lxc_unittest.py index 63f9711..42b25ba 100755 --- a/test/py/ganeti.hypervisor.hv_lxc_unittest.py +++ b/test/py/ganeti.hypervisor.hv_lxc_unittest.py @@ -24,6 +24,7 @@ import unittest from ganeti import constants +from ganeti import errors from ganeti import objects from ganeti import hypervisor from ganeti import utils @@ -173,5 +174,54 @@ class TestCgroupReadData(unittest.TestCase): getval_mock.return_value = "128" self.assertEqual(self.hv._GetCgroupMemoryLimit("instance1"), 128) +class TestVerifyLXCCommands(unittest.TestCase): + def setUp(self): + runcmd_mock = mock.Mock(return_value="") + self.RunCmdPatch = patch_object(utils, "RunCmd", runcmd_mock) + self.RunCmdPatch.start() + version_patch = patch_object(LXCHypervisor, "_LXC_MIN_VERSION_REQUIRED", + "1.2.3") + self._LXC_MIN_VERSION_REQUIRED_Patch = version_patch + self._LXC_MIN_VERSION_REQUIRED_Patch.start() + self.hvc = LXCHypervisor + + def tearDown(self): + self.RunCmdPatch.stop() + self._LXC_MIN_VERSION_REQUIRED_Patch.stop() + + def testParseLXCVersion(self): + self.assertEqual(self.hvc._ParseLXCVersion("1.0.0"), (1, 0, 0)) + self.assertEqual(self.hvc._ParseLXCVersion("1.0.0.alpha1"), (1, 0, 0)) + self.assertEqual(self.hvc._ParseLXCVersion("1.0"), None) + self.assertEqual(self.hvc._ParseLXCVersion("1.2a.0"), None) + + @patch_object(LXCHypervisor, "_LXC_COMMANDS_REQUIRED", ["lxc-stop"]) + def testCommandVersion(self): + utils.RunCmd.return_value = RunResultOk("1.2.3\n") + self.assertFalse(self.hvc._VerifyLXCCommands()) + utils.RunCmd.return_value = RunResultOk("1.10.0\n") + self.assertFalse(self.hvc._VerifyLXCCommands()) + utils.RunCmd.return_value = RunResultOk("1.2.2\n") + self.assertTrue(self.hvc._VerifyLXCCommands()) + + @patch_object(LXCHypervisor, "_LXC_COMMANDS_REQUIRED", ["lxc-stop"]) + def testCommandVersionInvalid(self): + utils.RunCmd.return_value = utils.RunResult(1, None, "", "", [], None, None) + self.assertTrue(self.hvc._VerifyLXCCommands()) + utils.RunCmd.return_value = RunResultOk("1.2a.0\n") + self.assertTrue(self.hvc._VerifyLXCCommands()) + + @patch_object(LXCHypervisor, "_LXC_COMMANDS_REQUIRED", ["lxc-stop"]) + def testCommandNotExists(self): + utils.RunCmd.side_effect = errors.OpExecError + self.assertTrue(self.hvc._VerifyLXCCommands()) + + @patch_object(LXCHypervisor, "_LXC_COMMANDS_REQUIRED", ["lxc-ls"]) + def testVerifyLXCLs(self): + utils.RunCmd.return_value = RunResultOk("garbage\n--running\ngarbage") + self.assertFalse(self.hvc._VerifyLXCCommands()) + utils.RunCmd.return_value = RunResultOk("foo") + self.assertTrue(self.hvc._VerifyLXCCommands()) + if __name__ == "__main__": testutils.GanetiTestProgram() -- 1.8.5.5
