Revision d8784f7d
b/lib/hypervisor/hv_xen.py | ||
---|---|---|
336 | 336 |
self._cmd = _cmd |
337 | 337 |
|
338 | 338 |
def _GetCommand(self): |
339 |
"""Returns Xen command to use. |
|
340 |
|
|
341 |
""" |
|
339 | 342 |
if self._cmd is None: |
340 | 343 |
# TODO: Make command a hypervisor parameter |
341 | 344 |
cmd = constants.XEN_CMD |
... | ... | |
663 | 666 |
@param live: perform a live migration |
664 | 667 |
|
665 | 668 |
""" |
666 |
if self.GetInstanceInfo(instance.name) is None: |
|
669 |
port = instance.hvparams[constants.HV_MIGRATION_PORT] |
|
670 |
|
|
671 |
# TODO: Pass cluster name via RPC |
|
672 |
cluster_name = ssconf.SimpleStore().GetClusterName() |
|
673 |
|
|
674 |
return self._MigrateInstance(cluster_name, instance.name, target, port, |
|
675 |
live) |
|
676 |
|
|
677 |
def _MigrateInstance(self, cluster_name, instance_name, target, port, live, |
|
678 |
_ping_fn=netutils.TcpPing): |
|
679 |
"""Migrate an instance to a target node. |
|
680 |
|
|
681 |
@see: L{MigrateInstance} for details |
|
682 |
|
|
683 |
""" |
|
684 |
if self.GetInstanceInfo(instance_name) is None: |
|
667 | 685 |
raise errors.HypervisorError("Instance not running, cannot migrate") |
668 | 686 |
|
669 |
port = instance.hvparams[constants.HV_MIGRATION_PORT]
|
|
687 |
cmd = self._GetCommand()
|
|
670 | 688 |
|
671 |
if (self._cmd == constants.XEN_CMD_XM and
|
|
672 |
not netutils.TcpPing(target, port, live_port_needed=True)):
|
|
689 |
if (cmd == constants.XEN_CMD_XM and |
|
690 |
not _ping_fn(target, port, live_port_needed=True)):
|
|
673 | 691 |
raise errors.HypervisorError("Remote host %s not listening on port" |
674 | 692 |
" %s, cannot migrate" % (target, port)) |
675 | 693 |
|
676 | 694 |
args = ["migrate"] |
677 | 695 |
|
678 |
if self._cmd == constants.XEN_CMD_XM:
|
|
696 |
if cmd == constants.XEN_CMD_XM: |
|
679 | 697 |
args.extend(["-p", "%d" % port]) |
680 | 698 |
if live: |
681 | 699 |
args.append("-l") |
682 | 700 |
|
683 |
elif self._cmd == constants.XEN_CMD_XL: |
|
684 |
cluster_name = ssconf.SimpleStore().GetClusterName() |
|
685 |
args.extend(["-s", constants.XL_SSH_CMD % cluster_name]) |
|
686 |
args.extend(["-C", self._ConfigFileName(instance.name)]) |
|
701 |
elif cmd == constants.XEN_CMD_XL: |
|
702 |
args.extend([ |
|
703 |
"-s", constants.XL_SSH_CMD % cluster_name, |
|
704 |
"-C", self._ConfigFileName(instance_name), |
|
705 |
]) |
|
687 | 706 |
|
688 | 707 |
else: |
689 |
raise errors.HypervisorError("Unsupported xen command: %s" % self._cmd)
|
|
708 |
raise errors.HypervisorError("Unsupported Xen command: %s" % self._cmd)
|
|
690 | 709 |
|
691 |
args.extend([instance.name, target])
|
|
710 |
args.extend([instance_name, target])
|
|
692 | 711 |
|
693 | 712 |
result = self._RunXen(args) |
694 | 713 |
if result.failed: |
695 | 714 |
raise errors.HypervisorError("Failed to migrate instance %s: %s" % |
696 |
(instance.name, result.output))
|
|
715 |
(instance_name, result.output))
|
|
697 | 716 |
|
698 | 717 |
def FinalizeMigrationSource(self, instance, success, live): |
699 | 718 |
"""Finalize the instance migration on the source node. |
b/test/py/ganeti.hypervisor.hv_xen_unittest.py | ||
---|---|---|
366 | 366 |
"", "This command failed", None, |
367 | 367 |
NotImplemented, NotImplemented) |
368 | 368 |
|
369 |
def _FakeTcpPing(self, expected, result, target, port, **kwargs): |
|
370 |
self.assertEqual((target, port), expected) |
|
371 |
return result |
|
372 |
|
|
369 | 373 |
def testReadingNonExistentConfigFile(self): |
370 | 374 |
hv = self._GetHv() |
371 | 375 |
|
... | ... | |
588 | 592 |
hv._StopInstance(name, force) |
589 | 593 |
self.assertFalse(os.path.exists(cfgfile)) |
590 | 594 |
|
595 |
def _MigrateNonRunningInstCmd(self, cmd): |
|
596 |
if cmd == [self.CMD, "list"]: |
|
597 |
output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt") |
|
598 |
else: |
|
599 |
self.fail("Unhandled command: %s" % (cmd, )) |
|
600 |
|
|
601 |
return self._SuccessCommand(output, cmd) |
|
602 |
|
|
603 |
def testMigrateInstanceNotRunning(self): |
|
604 |
name = "nonexistinginstance.example.com" |
|
605 |
target = constants.IP4_ADDRESS_LOCALHOST |
|
606 |
port = 14618 |
|
607 |
|
|
608 |
hv = self._GetHv(run_cmd=self._MigrateNonRunningInstCmd) |
|
609 |
|
|
610 |
for live in [False, True]: |
|
611 |
try: |
|
612 |
hv._MigrateInstance(NotImplemented, name, target, port, live, |
|
613 |
_ping_fn=NotImplemented) |
|
614 |
except errors.HypervisorError, err: |
|
615 |
self.assertEqual(str(err), "Instance not running, cannot migrate") |
|
616 |
else: |
|
617 |
self.fail("Exception was not raised") |
|
618 |
|
|
619 |
def _MigrateInstTargetUnreachCmd(self, cmd): |
|
620 |
if cmd == [self.CMD, "list"]: |
|
621 |
output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt") |
|
622 |
else: |
|
623 |
self.fail("Unhandled command: %s" % (cmd, )) |
|
624 |
|
|
625 |
return self._SuccessCommand(output, cmd) |
|
626 |
|
|
627 |
def testMigrateTargetUnreachable(self): |
|
628 |
name = "server01.example.com" |
|
629 |
target = constants.IP4_ADDRESS_LOCALHOST |
|
630 |
port = 28349 |
|
631 |
|
|
632 |
hv = self._GetHv(run_cmd=self._MigrateInstTargetUnreachCmd) |
|
633 |
|
|
634 |
for live in [False, True]: |
|
635 |
if self.CMD == constants.XEN_CMD_XL: |
|
636 |
# TODO: Detect unreachable targets |
|
637 |
pass |
|
638 |
else: |
|
639 |
try: |
|
640 |
hv._MigrateInstance(NotImplemented, name, target, port, live, |
|
641 |
_ping_fn=compat.partial(self._FakeTcpPing, |
|
642 |
(target, port), False)) |
|
643 |
except errors.HypervisorError, err: |
|
644 |
wanted = "Remote host %s not" % target |
|
645 |
self.assertTrue(str(err).startswith(wanted)) |
|
646 |
else: |
|
647 |
self.fail("Exception was not raised") |
|
648 |
|
|
649 |
def _MigrateInstanceCmd(self, cluster_name, instance_name, target, port, |
|
650 |
live, fail, cmd): |
|
651 |
if cmd == [self.CMD, "list"]: |
|
652 |
output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt") |
|
653 |
elif cmd[:2] == [self.CMD, "migrate"]: |
|
654 |
if self.CMD == constants.XEN_CMD_XM: |
|
655 |
args = ["-p", str(port)] |
|
656 |
|
|
657 |
if live: |
|
658 |
args.append("-l") |
|
659 |
|
|
660 |
elif self.CMD == constants.XEN_CMD_XL: |
|
661 |
args = [ |
|
662 |
"-s", constants.XL_SSH_CMD % cluster_name, |
|
663 |
"-C", utils.PathJoin(self.tmpdir, instance_name), |
|
664 |
] |
|
665 |
|
|
666 |
else: |
|
667 |
self.fail("Unknown Xen command '%s'" % self.CMD) |
|
668 |
|
|
669 |
args.extend([instance_name, target]) |
|
670 |
self.assertEqual(cmd[2:], args) |
|
671 |
|
|
672 |
if fail: |
|
673 |
return self._FailingCommand(cmd) |
|
674 |
|
|
675 |
output = "" |
|
676 |
else: |
|
677 |
self.fail("Unhandled command: %s" % (cmd, )) |
|
678 |
|
|
679 |
return self._SuccessCommand(output, cmd) |
|
680 |
|
|
681 |
def testMigrateInstance(self): |
|
682 |
clustername = "cluster.example.com" |
|
683 |
instname = "server01.example.com" |
|
684 |
target = constants.IP4_ADDRESS_LOCALHOST |
|
685 |
port = 22364 |
|
686 |
|
|
687 |
for live in [False, True]: |
|
688 |
for fail in [False, True]: |
|
689 |
ping_fn = \ |
|
690 |
testutils.CallCounter(compat.partial(self._FakeTcpPing, |
|
691 |
(target, port), True)) |
|
692 |
|
|
693 |
run_cmd = \ |
|
694 |
compat.partial(self._MigrateInstanceCmd, |
|
695 |
clustername, instname, target, port, live, |
|
696 |
fail) |
|
697 |
|
|
698 |
hv = self._GetHv(run_cmd=run_cmd) |
|
699 |
|
|
700 |
if fail: |
|
701 |
try: |
|
702 |
hv._MigrateInstance(clustername, instname, target, port, live, |
|
703 |
_ping_fn=ping_fn) |
|
704 |
except errors.HypervisorError, err: |
|
705 |
self.assertTrue(str(err).startswith("Failed to migrate instance")) |
|
706 |
else: |
|
707 |
self.fail("Exception was not raised") |
|
708 |
else: |
|
709 |
hv._MigrateInstance(clustername, instname, target, port, live, |
|
710 |
_ping_fn=ping_fn) |
|
711 |
|
|
712 |
if self.CMD == constants.XEN_CMD_XM: |
|
713 |
expected_pings = 1 |
|
714 |
else: |
|
715 |
expected_pings = 0 |
|
716 |
|
|
717 |
self.assertEqual(ping_fn.Count(), expected_pings) |
|
718 |
|
|
591 | 719 |
|
592 | 720 |
def _MakeTestClass(cls, cmd): |
593 | 721 |
"""Makes a class for testing. |
Also available in: Unified diff