Commit 3d942d8 broke instance migration (“self._cmd” was set to None). This patch fixes that issue, refactors “MigrateInstance” for testing and adds those tests.
Signed-off-by: Michael Hanselmann <[email protected]> --- lib/hypervisor/hv_xen.py | 43 ++++++--- test/py/ganeti.hypervisor.hv_xen_unittest.py | 128 +++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 12 deletions(-) diff --git a/lib/hypervisor/hv_xen.py b/lib/hypervisor/hv_xen.py index 3dc9355..11f9544 100644 --- a/lib/hypervisor/hv_xen.py +++ b/lib/hypervisor/hv_xen.py @@ -336,6 +336,9 @@ class XenHypervisor(hv_base.BaseHypervisor): self._cmd = _cmd def _GetCommand(self): + """Returns Xen command to use. + + """ if self._cmd is None: # TODO: Make command a hypervisor parameter cmd = constants.XEN_CMD @@ -663,37 +666,53 @@ class XenHypervisor(hv_base.BaseHypervisor): @param live: perform a live migration """ - if self.GetInstanceInfo(instance.name) is None: + port = instance.hvparams[constants.HV_MIGRATION_PORT] + + # TODO: Pass cluster name via RPC + cluster_name = ssconf.SimpleStore().GetClusterName() + + return self._MigrateInstance(cluster_name, instance.name, target, port, + live) + + def _MigrateInstance(self, cluster_name, instance_name, target, port, live, + _ping_fn=netutils.TcpPing): + """Migrate an instance to a target node. + + @see: L{MigrateInstance} for details + + """ + if self.GetInstanceInfo(instance_name) is None: raise errors.HypervisorError("Instance not running, cannot migrate") - port = instance.hvparams[constants.HV_MIGRATION_PORT] + cmd = self._GetCommand() - if (self._cmd == constants.XEN_CMD_XM and - not netutils.TcpPing(target, port, live_port_needed=True)): + if (cmd == constants.XEN_CMD_XM and + not _ping_fn(target, port, live_port_needed=True)): raise errors.HypervisorError("Remote host %s not listening on port" " %s, cannot migrate" % (target, port)) args = ["migrate"] - if self._cmd == constants.XEN_CMD_XM: + if cmd == constants.XEN_CMD_XM: args.extend(["-p", "%d" % port]) if live: args.append("-l") - elif self._cmd == constants.XEN_CMD_XL: - cluster_name = ssconf.SimpleStore().GetClusterName() - args.extend(["-s", constants.XL_SSH_CMD % cluster_name]) - args.extend(["-C", self._ConfigFileName(instance.name)]) + elif cmd == constants.XEN_CMD_XL: + args.extend([ + "-s", constants.XL_SSH_CMD % cluster_name, + "-C", self._ConfigFileName(instance_name), + ]) else: - raise errors.HypervisorError("Unsupported xen command: %s" % self._cmd) + raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd) - args.extend([instance.name, target]) + args.extend([instance_name, target]) result = self._RunXen(args) if result.failed: raise errors.HypervisorError("Failed to migrate instance %s: %s" % - (instance.name, result.output)) + (instance_name, result.output)) def FinalizeMigrationSource(self, instance, success, live): """Finalize the instance migration on the source node. diff --git a/test/py/ganeti.hypervisor.hv_xen_unittest.py b/test/py/ganeti.hypervisor.hv_xen_unittest.py index cc45a94..057ac76 100755 --- a/test/py/ganeti.hypervisor.hv_xen_unittest.py +++ b/test/py/ganeti.hypervisor.hv_xen_unittest.py @@ -366,6 +366,10 @@ class _TestXenHypervisor(object): "", "This command failed", None, NotImplemented, NotImplemented) + def _FakeTcpPing(self, expected, result, target, port, **kwargs): + self.assertEqual((target, port), expected) + return result + def testReadingNonExistentConfigFile(self): hv = self._GetHv() @@ -588,6 +592,130 @@ class _TestXenHypervisor(object): hv._StopInstance(name, force) self.assertFalse(os.path.exists(cfgfile)) + def _MigrateNonRunningInstCmd(self, cmd): + if cmd == [self.CMD, "list"]: + output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt") + else: + self.fail("Unhandled command: %s" % (cmd, )) + + return self._SuccessCommand(output, cmd) + + def testMigrateInstanceNotRunning(self): + name = "nonexistinginstance.example.com" + target = constants.IP4_ADDRESS_LOCALHOST + port = 14618 + + hv = self._GetHv(run_cmd=self._MigrateNonRunningInstCmd) + + for live in [False, True]: + try: + hv._MigrateInstance(NotImplemented, name, target, port, live, + _ping_fn=NotImplemented) + except errors.HypervisorError, err: + self.assertEqual(str(err), "Instance not running, cannot migrate") + else: + self.fail("Exception was not raised") + + def _MigrateInstTargetUnreachCmd(self, cmd): + if cmd == [self.CMD, "list"]: + output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt") + else: + self.fail("Unhandled command: %s" % (cmd, )) + + return self._SuccessCommand(output, cmd) + + def testMigrateTargetUnreachable(self): + name = "server01.example.com" + target = constants.IP4_ADDRESS_LOCALHOST + port = 28349 + + hv = self._GetHv(run_cmd=self._MigrateInstTargetUnreachCmd) + + for live in [False, True]: + if self.CMD == constants.XEN_CMD_XL: + # TODO: Detect unreachable targets + pass + else: + try: + hv._MigrateInstance(NotImplemented, name, target, port, live, + _ping_fn=compat.partial(self._FakeTcpPing, + (target, port), False)) + except errors.HypervisorError, err: + wanted = "Remote host %s not" % target + self.assertTrue(str(err).startswith(wanted)) + else: + self.fail("Exception was not raised") + + def _MigrateInstanceCmd(self, cluster_name, instance_name, target, port, + live, fail, cmd): + if cmd == [self.CMD, "list"]: + output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt") + elif cmd[:2] == [self.CMD, "migrate"]: + if self.CMD == constants.XEN_CMD_XM: + args = ["-p", str(port)] + + if live: + args.append("-l") + + elif self.CMD == constants.XEN_CMD_XL: + args = [ + "-s", constants.XL_SSH_CMD % cluster_name, + "-C", utils.PathJoin(self.tmpdir, instance_name), + ] + + else: + self.fail("Unknown Xen command '%s'" % self.CMD) + + args.extend([instance_name, target]) + self.assertEqual(cmd[2:], args) + + if fail: + return self._FailingCommand(cmd) + + output = "" + else: + self.fail("Unhandled command: %s" % (cmd, )) + + return self._SuccessCommand(output, cmd) + + def testMigrateInstance(self): + clustername = "cluster.example.com" + instname = "server01.example.com" + target = constants.IP4_ADDRESS_LOCALHOST + port = 22364 + + for live in [False, True]: + for fail in [False, True]: + ping_fn = \ + testutils.CallCounter(compat.partial(self._FakeTcpPing, + (target, port), True)) + + run_cmd = \ + compat.partial(self._MigrateInstanceCmd, + clustername, instname, target, port, live, + fail) + + hv = self._GetHv(run_cmd=run_cmd) + + if fail: + try: + hv._MigrateInstance(clustername, instname, target, port, live, + _ping_fn=ping_fn) + except errors.HypervisorError, err: + self.assertTrue(str(err).startswith("Failed to migrate instance")) + else: + self.fail("Exception was not raised") + else: + hv._MigrateInstance(clustername, instname, target, port, live, + _ping_fn=ping_fn) + + if self.CMD == constants.XEN_CMD_XM: + expected_pings = 1 + else: + expected_pings = 0 + + self.assertEqual(ping_fn.Count(), expected_pings) + def _MakeTestClass(cls, cmd): """Makes a class for testing. -- 1.8.1
