4 # Copyright (C) 2011, 2013 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Script for testing ganeti.hypervisor.hv_xen"""
24 import string # pylint: disable=W0402
32 from ganeti import constants
33 from ganeti import objects
34 from ganeti import pathutils
35 from ganeti import hypervisor
36 from ganeti import utils
37 from ganeti import errors
38 from ganeti import compat
40 from ganeti.hypervisor import hv_xen
45 # Map from hypervisor class to hypervisor name
46 HVCLASS_TO_HVNAME = utils.InvertDict(hypervisor._HYPERVISOR_MAP)
49 class TestConsole(unittest.TestCase):
51 hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
52 for cls in [hv_xen.XenPvmHypervisor(), hv_xen.XenHvmHypervisor()]:
53 instance = objects.Instance(name="xen.example.com",
54 primary_node="node24828-uuid")
55 node = objects.Node(name="node24828", uuid="node24828-uuid")
56 cons = cls.GetInstanceConsole(instance, node, hvparams, {})
57 self.assertTrue(cons.Validate())
58 self.assertEqual(cons.kind, constants.CONS_SSH)
59 self.assertEqual(cons.host, node.name)
60 self.assertEqual(cons.command[-1], instance.name)
63 class TestCreateConfigCpus(unittest.TestCase):
65 for cpu_mask in [None, ""]:
66 self.assertEqual(hv_xen._CreateConfigCpus(cpu_mask),
70 self.assertEqual(hv_xen._CreateConfigCpus(constants.CPU_PINNING_ALL),
74 self.assertEqual(hv_xen._CreateConfigCpus("9"), "cpu = \"9\"")
76 def testMultiple(self):
77 self.assertEqual(hv_xen._CreateConfigCpus("0-2,4,5-5:3:all"),
78 ("cpus = [ \"0,1,2,4,5\", \"3\", \"%s\" ]" %
79 constants.CPU_PINNING_ALL_XEN))
82 class TestGetCommand(testutils.GanetiTestCase):
83 def testCommandExplicit(self):
84 """Test the case when the command is given as class parameter explicitly.
88 hv = hv_xen.XenHypervisor(_cmd=constants.XEN_CMD_XL)
89 self.assertEqual(hv._GetCommand(None), expected_cmd)
91 def testCommandInvalid(self):
92 """Test the case an invalid command is given as class parameter explicitly.
95 hv = hv_xen.XenHypervisor(_cmd="invalidcommand")
96 self.assertRaises(errors.ProgrammerError, hv._GetCommand, None)
98 def testCommandHvparams(self):
100 test_hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
101 hv = hv_xen.XenHypervisor()
102 self.assertEqual(hv._GetCommand(test_hvparams), expected_cmd)
104 def testCommandHvparamsInvalid(self):
106 hv = hv_xen.XenHypervisor()
107 self.assertRaises(errors.HypervisorError, hv._GetCommand, test_hvparams)
109 def testCommandHvparamsCmdInvalid(self):
110 test_hvparams = {constants.HV_XEN_CMD: "invalidcommand"}
111 hv = hv_xen.XenHypervisor()
112 self.assertRaises(errors.ProgrammerError, hv._GetCommand, test_hvparams)
115 class TestParseInstanceList(testutils.GanetiTestCase):
117 data = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
120 self.assertEqual(hv_xen._ParseInstanceList(data.splitlines(), False), [])
123 result = hv_xen._ParseInstanceList(data.splitlines(), True)
124 self.assertEqual(len(result), 1)
125 self.assertEqual(len(result[0]), 6)
128 self.assertEqual(result[0][0], hv_xen._DOM0_NAME)
131 self.assertEqual(result[0][1], 0)
134 self.assertEqual(result[0][2], 1023)
137 self.assertEqual(result[0][3], 1)
140 self.assertEqual(result[0][4], "r-----")
143 self.assertAlmostEqual(result[0][5], 121152.6)
145 def testWrongLineFormat(self):
147 ["three fields only"],
148 ["name InvalidID 128 1 r----- 12345"],
153 hv_xen._ParseInstanceList(["Header would be here"] + lines, False)
154 except errors.HypervisorError, err:
155 self.assertTrue("Can't parse instance list" in str(err))
157 self.fail("Exception was not raised")
160 class TestGetInstanceList(testutils.GanetiTestCase):
162 return utils.RunResult(constants.EXIT_FAILURE, None,
163 "stdout", "stderr", None,
164 NotImplemented, NotImplemented)
166 def testTimeout(self):
167 fn = testutils.CallCounter(self._Fail)
169 hv_xen._GetInstanceList(fn, False, _timeout=0.1)
170 except errors.HypervisorError, err:
171 self.assertTrue("timeout exceeded" in str(err))
173 self.fail("Exception was not raised")
175 self.assertTrue(fn.Count() < 10,
176 msg="'xm list' was called too many times")
178 def _Success(self, stdout):
179 return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
180 NotImplemented, NotImplemented)
182 def testSuccess(self):
183 data = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
185 fn = testutils.CallCounter(compat.partial(self._Success, data))
187 result = hv_xen._GetInstanceList(fn, True, _timeout=0.1)
189 self.assertEqual(len(result), 4)
191 self.assertEqual(map(compat.fst, result), [
193 "server01.example.com",
194 "web3106215069.example.com",
195 "testinstance.example.com",
198 self.assertEqual(fn.Count(), 1)
201 class TestParseNodeInfo(testutils.GanetiTestCase):
203 self.assertEqual(hv_xen._ParseNodeInfo(""), {})
205 def testUnknownInput(self):
208 "something else goes",
211 self.assertEqual(hv_xen._ParseNodeInfo(data), {})
213 def testBasicInfo(self):
214 data = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
215 result = hv_xen._ParseNodeInfo(data)
216 self.assertEqual(result, {
220 "hv_version": (4, 0),
222 "memory_total": 16378,
226 class TestMergeInstanceInfo(testutils.GanetiTestCase):
228 self.assertEqual(hv_xen._MergeInstanceInfo({}, []), {})
230 def _FakeXmList(self, include_node):
232 (hv_xen._DOM0_NAME, NotImplemented, 4096, 7, NotImplemented,
234 ("inst1.example.com", NotImplemented, 2048, 4, NotImplemented,
238 def testMissingNodeInfo(self):
239 instance_list = self._FakeXmList(True)
240 result = hv_xen._MergeInstanceInfo({}, instance_list)
241 self.assertEqual(result, {
246 def testWithNodeInfo(self):
247 info = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
248 instance_list = self._FakeXmList(True)
249 result = hv_xen._GetNodeInfo(info, instance_list)
250 self.assertEqual(result, {
255 "hv_version": (4, 0),
259 "memory_total": 16378,
263 class TestGetConfigFileDiskData(unittest.TestCase):
264 def testLetterCount(self):
265 self.assertEqual(len(hv_xen._DISK_LETTERS), 26)
267 def testNoDisks(self):
268 self.assertEqual(hv_xen._GetConfigFileDiskData([], "hd"), [])
270 def testManyDisks(self):
271 for offset in [0, 1, 10]:
272 disks = [(objects.Disk(dev_type=constants.DT_PLAIN), "/tmp/disk/%s" % idx)
273 for idx in range(len(hv_xen._DISK_LETTERS) + offset)]
276 result = hv_xen._GetConfigFileDiskData(disks, "hd")
277 self.assertEqual(result, [
278 "'phy:/tmp/disk/%s,hd%s,r'" % (idx, string.ascii_lowercase[idx])
279 for idx in range(len(hv_xen._DISK_LETTERS) + offset)
283 hv_xen._GetConfigFileDiskData(disks, "hd")
284 except errors.HypervisorError, err:
285 self.assertEqual(str(err), "Too many disks")
287 self.fail("Exception was not raised")
289 def testTwoLvDisksWithMode(self):
291 (objects.Disk(dev_type=constants.DT_PLAIN, mode=constants.DISK_RDWR),
293 (objects.Disk(dev_type=constants.DT_PLAIN, mode=constants.DISK_RDONLY),
297 result = hv_xen._GetConfigFileDiskData(disks, "hd")
298 self.assertEqual(result, [
299 "'phy:/tmp/diskFirst,hda,w'",
300 "'phy:/tmp/diskLast,hdb,r'",
303 def testFileDisks(self):
305 (objects.Disk(dev_type=constants.DT_FILE, mode=constants.DISK_RDWR,
306 physical_id=[constants.FD_LOOP]),
308 (objects.Disk(dev_type=constants.DT_FILE, mode=constants.DISK_RDONLY,
309 physical_id=[constants.FD_BLKTAP]),
311 (objects.Disk(dev_type=constants.DT_FILE, mode=constants.DISK_RDWR,
312 physical_id=[constants.FD_LOOP]),
314 (objects.Disk(dev_type=constants.DT_FILE, mode=constants.DISK_RDWR,
315 physical_id=[constants.FD_BLKTAP]),
319 result = hv_xen._GetConfigFileDiskData(disks, "sd")
320 self.assertEqual(result, [
321 "'file:/tmp/diskFirst,sda,w'",
322 "'tap:aio:/tmp/diskTwo,sdb,r'",
323 "'file:/tmp/diskThree,sdc,w'",
324 "'tap:aio:/tmp/diskLast,sdd,w'",
327 def testInvalidFileDisk(self):
329 (objects.Disk(dev_type=constants.DT_FILE, mode=constants.DISK_RDWR,
330 physical_id=["#unknown#"]),
334 self.assertRaises(KeyError, hv_xen._GetConfigFileDiskData, disks, "sd")
337 class TestXenHypervisorRunXen(unittest.TestCase):
341 def testCommandUnknown(self):
342 cmd = "#unknown command#"
343 self.assertFalse(cmd in constants.KNOWN_XEN_COMMANDS)
344 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
345 _run_cmd_fn=NotImplemented,
347 self.assertRaises(errors.ProgrammerError, hv._RunXen, [], None)
349 def testCommandNoHvparams(self):
350 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
351 _run_cmd_fn=NotImplemented)
353 self.assertRaises(errors.HypervisorError, hv._RunXen, [self.XEN_SUB_CMD],
356 def testCommandFromHvparams(self):
357 expected_xen_cmd = "xl"
358 hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
359 mock_run_cmd = mock.Mock()
360 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
361 _run_cmd_fn=mock_run_cmd)
362 hv._RunXen([self.XEN_SUB_CMD], hvparams=hvparams)
363 mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_SUB_CMD])
366 class TestXenHypervisorGetInstanceList(unittest.TestCase):
368 RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
371 def testNoHvparams(self):
372 expected_xen_cmd = "xm"
373 mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
374 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
375 _run_cmd_fn=mock_run_cmd)
376 self.assertRaises(errors.HypervisorError, hv._GetInstanceList, True, None)
378 def testFromHvparams(self):
379 expected_xen_cmd = "xl"
380 hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
381 mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
382 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
383 _run_cmd_fn=mock_run_cmd)
384 hv._GetInstanceList(True, hvparams)
385 mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
388 class TestXenHypervisorListInstances(unittest.TestCase):
390 RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
393 def testNoHvparams(self):
394 expected_xen_cmd = "xm"
395 mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
396 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
397 _run_cmd_fn=mock_run_cmd)
398 self.assertRaises(errors.HypervisorError, hv.ListInstances)
400 def testHvparamsXl(self):
401 expected_xen_cmd = "xl"
402 hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
403 mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
404 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
405 _run_cmd_fn=mock_run_cmd)
406 hv.ListInstances(hvparams=hvparams)
407 mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
410 class TestXenHypervisorCheckToolstack(unittest.TestCase):
413 self.tmpdir = tempfile.mkdtemp()
414 self.cfg_name = "xen_config"
415 self.cfg_path = utils.PathJoin(self.tmpdir, self.cfg_name)
416 self.hv = hv_xen.XenHypervisor()
419 shutil.rmtree(self.tmpdir)
421 def testBinaryNotFound(self):
422 RESULT_FAILED = utils.RunResult(1, None, "", "", "", None, None)
423 mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
424 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
425 _run_cmd_fn=mock_run_cmd)
426 result = hv._CheckToolstackBinary("xl")
427 self.assertFalse(result)
429 def testCheckToolstackXlConfigured(self):
430 RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
431 mock_run_cmd = mock.Mock(return_value=RESULT_OK)
432 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
433 _run_cmd_fn=mock_run_cmd)
434 result = hv._CheckToolstackXlConfigured()
435 self.assertTrue(result)
437 def testCheckToolstackXlNotConfigured(self):
438 RESULT_FAILED = utils.RunResult(
440 "ERROR: A different toolstack (xm) have been selected!",
442 mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
443 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
444 _run_cmd_fn=mock_run_cmd)
445 result = hv._CheckToolstackXlConfigured()
446 self.assertFalse(result)
448 def testCheckToolstackXlFails(self):
449 RESULT_FAILED = utils.RunResult(
451 "ERROR: The pink bunny hid the binary.",
453 mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
454 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
455 _run_cmd_fn=mock_run_cmd)
456 self.assertRaises(errors.HypervisorError, hv._CheckToolstackXlConfigured)
459 class TestXenHypervisorWriteConfigFile(unittest.TestCase):
461 self.tmpdir = tempfile.mkdtemp()
464 shutil.rmtree(self.tmpdir)
466 def testWriteError(self):
467 cfgdir = utils.PathJoin(self.tmpdir, "foobar")
469 hv = hv_xen.XenHypervisor(_cfgdir=cfgdir,
470 _run_cmd_fn=NotImplemented,
473 self.assertFalse(os.path.exists(cfgdir))
476 hv._WriteConfigFile("name", "data")
477 except errors.HypervisorError, err:
478 self.assertTrue(str(err).startswith("Cannot write Xen instance"))
480 self.fail("Exception was not raised")
483 class TestXenHypervisorVerify(unittest.TestCase):
486 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
487 self._result_ok = utils.RunResult(0, None, output, "", "", None, None)
489 def testVerify(self):
490 hvparams = {constants.HV_XEN_CMD : constants.XEN_CMD_XL}
491 mock_run_cmd = mock.Mock(return_value=self._result_ok)
492 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
493 _run_cmd_fn=mock_run_cmd)
494 hv._CheckToolstack = mock.Mock(return_value=True)
495 result = hv.Verify(hvparams)
496 self.assertTrue(result is None)
498 def testVerifyToolstackNotOk(self):
499 hvparams = {constants.HV_XEN_CMD : constants.XEN_CMD_XL}
500 mock_run_cmd = mock.Mock(return_value=self._result_ok)
501 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
502 _run_cmd_fn=mock_run_cmd)
503 hv._CheckToolstack = mock.Mock()
504 hv._CheckToolstack.side_effect = errors.HypervisorError("foo")
505 result = hv.Verify(hvparams)
506 self.assertTrue(result is not None)
508 def testVerifyFailing(self):
509 result_failed = utils.RunResult(1, None, "", "", "", None, None)
510 mock_run_cmd = mock.Mock(return_value=result_failed)
511 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
512 _run_cmd_fn=mock_run_cmd)
513 hv._CheckToolstack = mock.Mock(return_value=True)
515 self.assertTrue(result is not None)
518 class _TestXenHypervisor(object):
519 TARGET = NotImplemented
521 HVNAME = NotImplemented
522 VALID_HVPARAMS = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
525 super(_TestXenHypervisor, self).setUp()
527 self.tmpdir = tempfile.mkdtemp()
529 self.vncpw = "".join(random.sample(string.ascii_letters, 10))
531 self.vncpw_path = utils.PathJoin(self.tmpdir, "vncpw")
532 utils.WriteFile(self.vncpw_path, data=self.vncpw)
535 super(_TestXenHypervisor, self).tearDown()
537 shutil.rmtree(self.tmpdir)
539 def _GetHv(self, run_cmd=NotImplemented):
540 return self.TARGET(_cfgdir=self.tmpdir, _run_cmd_fn=run_cmd, _cmd=self.CMD)
542 def _SuccessCommand(self, stdout, cmd):
543 self.assertEqual(cmd[0], self.CMD)
545 return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
546 NotImplemented, NotImplemented)
548 def _FailingCommand(self, cmd):
549 self.assertEqual(cmd[0], self.CMD)
551 return utils.RunResult(constants.EXIT_FAILURE, None,
552 "", "This command failed", None,
553 NotImplemented, NotImplemented)
555 def _FakeTcpPing(self, expected, result, target, port, **kwargs):
556 self.assertEqual((target, port), expected)
559 def testReadingNonExistentConfigFile(self):
563 hv._ReadConfigFile("inst15780.example.com")
564 except errors.HypervisorError, err:
565 self.assertTrue(str(err).startswith("Failed to load Xen config file:"))
567 self.fail("Exception was not raised")
569 def testRemovingAutoConfigFile(self):
570 name = "inst8206.example.com"
571 cfgfile = utils.PathJoin(self.tmpdir, name)
572 autodir = utils.PathJoin(self.tmpdir, "auto")
573 autocfgfile = utils.PathJoin(autodir, name)
577 utils.WriteFile(autocfgfile, data="")
581 self.assertTrue(os.path.isfile(autocfgfile))
582 hv._WriteConfigFile(name, "content")
583 self.assertFalse(os.path.exists(autocfgfile))
584 self.assertEqual(utils.ReadFile(cfgfile), "content")
586 def _XenList(self, cmd):
587 self.assertEqual(cmd, [self.CMD, "list"])
589 # TODO: Use actual data from "xl" command
590 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
592 return self._SuccessCommand(output, cmd)
594 def testGetInstanceInfo(self):
595 hv = self._GetHv(run_cmd=self._XenList)
597 (name, instid, memory, vcpus, state, runtime) = \
598 hv.GetInstanceInfo("server01.example.com")
600 self.assertEqual(name, "server01.example.com")
601 self.assertEqual(instid, 1)
602 self.assertEqual(memory, 1024)
603 self.assertEqual(vcpus, 1)
604 self.assertEqual(state, "-b----")
605 self.assertAlmostEqual(runtime, 167643.2)
607 def testGetInstanceInfoDom0(self):
608 hv = self._GetHv(run_cmd=self._XenList)
610 # TODO: Not sure if this is actually used anywhere (can't find it), but the
611 # code supports querying for Dom0
612 (name, instid, memory, vcpus, state, runtime) = \
613 hv.GetInstanceInfo(hv_xen._DOM0_NAME)
615 self.assertEqual(name, "Domain-0")
616 self.assertEqual(instid, 0)
617 self.assertEqual(memory, 1023)
618 self.assertEqual(vcpus, 1)
619 self.assertEqual(state, "r-----")
620 self.assertAlmostEqual(runtime, 154706.1)
622 def testGetInstanceInfoUnknown(self):
623 hv = self._GetHv(run_cmd=self._XenList)
625 result = hv.GetInstanceInfo("unknown.example.com")
626 self.assertTrue(result is None)
628 def testGetAllInstancesInfo(self):
629 hv = self._GetHv(run_cmd=self._XenList)
631 result = hv.GetAllInstancesInfo()
633 self.assertEqual(map(compat.fst, result), [
634 "server01.example.com",
635 "web3106215069.example.com",
636 "testinstance.example.com",
639 def testListInstances(self):
640 hv = self._GetHv(run_cmd=self._XenList)
642 self.assertEqual(hv.ListInstances(), [
643 "server01.example.com",
644 "web3106215069.example.com",
645 "testinstance.example.com",
648 def _StartInstanceCommand(self, inst, paused, failcreate, cmd):
649 if cmd == [self.CMD, "info"]:
650 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
651 elif cmd == [self.CMD, "list"]:
652 output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
653 elif cmd[:2] == [self.CMD, "create"]:
655 cfgfile = utils.PathJoin(self.tmpdir, inst.name)
658 self.assertEqual(args, ["-p", cfgfile])
660 self.assertEqual(args, [cfgfile])
663 return self._FailingCommand(cmd)
667 self.fail("Unhandled command: %s" % (cmd, ))
669 return self._SuccessCommand(output, cmd)
671 def _MakeInstance(self):
672 # Copy default parameters
673 bep = objects.FillDict(constants.BEC_DEFAULTS, {})
674 hvp = objects.FillDict(constants.HVC_DEFAULTS[self.HVNAME], {})
676 # Override default VNC password file path
677 if constants.HV_VNC_PASSWORD_FILE in hvp:
678 hvp[constants.HV_VNC_PASSWORD_FILE] = self.vncpw_path
681 (objects.Disk(dev_type=constants.DT_PLAIN, mode=constants.DISK_RDWR),
682 utils.PathJoin(self.tmpdir, "disk0")),
683 (objects.Disk(dev_type=constants.DT_PLAIN, mode=constants.DISK_RDONLY),
684 utils.PathJoin(self.tmpdir, "disk1")),
687 inst = objects.Instance(name="server01.example.com",
688 hvparams=hvp, beparams=bep,
689 osparams={}, nics=[], os="deb1",
690 disks=map(compat.fst, disks))
695 def testStartInstance(self):
696 (inst, disks) = self._MakeInstance()
697 pathutils.LOG_XEN_DIR = self.tmpdir
699 for failcreate in [False, True]:
700 for paused in [False, True]:
701 run_cmd = compat.partial(self._StartInstanceCommand,
702 inst, paused, failcreate)
704 hv = self._GetHv(run_cmd=run_cmd)
706 # Ensure instance is not listed
707 self.assertTrue(inst.name not in hv.ListInstances())
709 # Remove configuration
710 cfgfile = utils.PathJoin(self.tmpdir, inst.name)
711 utils.RemoveFile(cfgfile)
714 self.assertRaises(errors.HypervisorError, hv.StartInstance,
716 # Check whether a stale config file is left behind
717 self.assertFalse(os.path.exists(cfgfile))
719 hv.StartInstance(inst, disks, paused)
720 # Check if configuration was updated
721 lines = utils.ReadFile(cfgfile).splitlines()
723 if constants.HV_VNC_PASSWORD_FILE in inst.hvparams:
724 self.assertTrue(("vncpasswd = '%s'" % self.vncpw) in lines)
726 extra = inst.hvparams[constants.HV_KERNEL_ARGS]
727 self.assertTrue(("extra = '%s'" % extra) in lines)
729 def _StopInstanceCommand(self, instance_name, force, fail, cmd):
730 if ((force and cmd[:2] == [self.CMD, "destroy"]) or
731 (not force and cmd[:2] == [self.CMD, "shutdown"])):
732 self.assertEqual(cmd[2:], [instance_name])
735 self.fail("Unhandled command: %s" % (cmd, ))
738 # Simulate a failing command
739 return self._FailingCommand(cmd)
741 return self._SuccessCommand(output, cmd)
743 def testStopInstance(self):
744 name = "inst4284.example.com"
745 cfgfile = utils.PathJoin(self.tmpdir, name)
746 cfgdata = "config file content\n"
748 for force in [False, True]:
749 for fail in [False, True]:
750 utils.WriteFile(cfgfile, data=cfgdata)
752 run_cmd = compat.partial(self._StopInstanceCommand, name, force, fail)
754 hv = self._GetHv(run_cmd=run_cmd)
756 self.assertTrue(os.path.isfile(cfgfile))
760 hv._StopInstance(name, force, None)
761 except errors.HypervisorError, err:
762 self.assertTrue(str(err).startswith("Failed to stop instance"))
764 self.fail("Exception was not raised")
765 self.assertEqual(utils.ReadFile(cfgfile), cfgdata,
766 msg=("Configuration was removed when stopping"
769 hv._StopInstance(name, force, None)
770 self.assertFalse(os.path.exists(cfgfile))
772 def _MigrateNonRunningInstCmd(self, cmd):
773 if cmd == [self.CMD, "list"]:
774 output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
776 self.fail("Unhandled command: %s" % (cmd, ))
778 return self._SuccessCommand(output, cmd)
780 def testMigrateInstanceNotRunning(self):
781 name = "nonexistinginstance.example.com"
782 target = constants.IP4_ADDRESS_LOCALHOST
785 hv = self._GetHv(run_cmd=self._MigrateNonRunningInstCmd)
787 for live in [False, True]:
789 hv._MigrateInstance(NotImplemented, name, target, port, live,
790 self.VALID_HVPARAMS, _ping_fn=NotImplemented)
791 except errors.HypervisorError, err:
792 self.assertEqual(str(err), "Instance not running, cannot migrate")
794 self.fail("Exception was not raised")
796 def _MigrateInstTargetUnreachCmd(self, cmd):
797 if cmd == [self.CMD, "list"]:
798 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
800 self.fail("Unhandled command: %s" % (cmd, ))
802 return self._SuccessCommand(output, cmd)
804 def testMigrateTargetUnreachable(self):
805 name = "server01.example.com"
806 target = constants.IP4_ADDRESS_LOCALHOST
809 hv = self._GetHv(run_cmd=self._MigrateInstTargetUnreachCmd)
810 hvparams = {constants.HV_XEN_CMD: self.CMD}
812 for live in [False, True]:
813 if self.CMD == constants.XEN_CMD_XL:
814 # TODO: Detect unreachable targets
818 hv._MigrateInstance(NotImplemented, name, target, port, live,
820 _ping_fn=compat.partial(self._FakeTcpPing,
821 (target, port), False))
822 except errors.HypervisorError, err:
823 wanted = "Remote host %s not" % target
824 self.assertTrue(str(err).startswith(wanted))
826 self.fail("Exception was not raised")
828 def _MigrateInstanceCmd(self, cluster_name, instance_name, target, port,
830 if cmd == [self.CMD, "list"]:
831 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
832 elif cmd[:2] == [self.CMD, "migrate"]:
833 if self.CMD == constants.XEN_CMD_XM:
834 args = ["-p", str(port)]
839 elif self.CMD == constants.XEN_CMD_XL:
841 "-s", constants.XL_SSH_CMD % cluster_name,
842 "-C", utils.PathJoin(self.tmpdir, instance_name),
846 self.fail("Unknown Xen command '%s'" % self.CMD)
848 args.extend([instance_name, target])
849 self.assertEqual(cmd[2:], args)
852 return self._FailingCommand(cmd)
856 self.fail("Unhandled command: %s" % (cmd, ))
858 return self._SuccessCommand(output, cmd)
860 def testMigrateInstance(self):
861 clustername = "cluster.example.com"
862 instname = "server01.example.com"
863 target = constants.IP4_ADDRESS_LOCALHOST
866 hvparams = {constants.HV_XEN_CMD: self.CMD}
868 for live in [False, True]:
869 for fail in [False, True]:
871 testutils.CallCounter(compat.partial(self._FakeTcpPing,
872 (target, port), True))
875 compat.partial(self._MigrateInstanceCmd,
876 clustername, instname, target, port, live,
879 hv = self._GetHv(run_cmd=run_cmd)
883 hv._MigrateInstance(clustername, instname, target, port, live,
884 hvparams, _ping_fn=ping_fn)
885 except errors.HypervisorError, err:
886 self.assertTrue(str(err).startswith("Failed to migrate instance"))
888 self.fail("Exception was not raised")
890 hv._MigrateInstance(clustername, instname, target, port, live,
891 hvparams, _ping_fn=ping_fn)
893 if self.CMD == constants.XEN_CMD_XM:
898 self.assertEqual(ping_fn.Count(), expected_pings)
900 def _GetNodeInfoCmd(self, fail, cmd):
901 if cmd == [self.CMD, "info"]:
903 return self._FailingCommand(cmd)
905 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
906 elif cmd == [self.CMD, "list"]:
908 self.fail("'xm list' shouldn't be called when 'xm info' failed")
910 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
912 self.fail("Unhandled command: %s" % (cmd, ))
914 return self._SuccessCommand(output, cmd)
916 def testGetNodeInfo(self):
917 run_cmd = compat.partial(self._GetNodeInfoCmd, False)
918 hv = self._GetHv(run_cmd=run_cmd)
919 result = hv.GetNodeInfo()
921 self.assertEqual(result["hv_version"], (4, 0))
922 self.assertEqual(result["memory_free"], 8004)
924 def testGetNodeInfoFailing(self):
925 run_cmd = compat.partial(self._GetNodeInfoCmd, True)
926 hv = self._GetHv(run_cmd=run_cmd)
927 self.assertTrue(hv.GetNodeInfo() is None)
930 def _MakeTestClass(cls, cmd):
931 """Makes a class for testing.
933 The returned class has structure as shown in the following pseudo code:
935 class Test{cls.__name__}{cmd}(_TestXenHypervisor, unittest.TestCase):
938 HVNAME = {Hypervisor name retrieved using class}
941 @param cls: Hypervisor class to be tested
943 @param cmd: Hypervisor command
945 @return: Class name and class object (not instance)
948 name = "Test%sCmd%s" % (cls.__name__, cmd.title())
949 bases = (_TestXenHypervisor, unittest.TestCase)
950 hvname = HVCLASS_TO_HVNAME[cls]
952 return (name, type(name, bases, dict(TARGET=cls, CMD=cmd, HVNAME=hvname)))
955 # Create test classes programmatically instead of manually to reduce the risk
956 # of forgetting some combinations
957 for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]:
958 for cmd in constants.KNOWN_XEN_COMMANDS:
959 (name, testcls) = _MakeTestClass(cls, cmd)
961 assert name not in locals()
963 locals()[name] = testcls
966 if __name__ == "__main__":
967 testutils.GanetiTestProgram()