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_RDONLY,
315 physical_id=[constants.FD_BLKTAP2]),
317 (objects.Disk(dev_type=constants.DT_FILE, mode=constants.DISK_RDWR,
318 physical_id=[constants.FD_BLKTAP]),
322 result = hv_xen._GetConfigFileDiskData(disks, "sd")
323 self.assertEqual(result, [
324 "'file:/tmp/diskFirst,sda,w'",
325 "'tap:aio:/tmp/diskTwo,sdb,r'",
326 "'file:/tmp/diskThree,sdc,w'",
327 "'tap2:tapdisk:aio:/tmp/diskFour,sdd,r'",
328 "'tap:aio:/tmp/diskLast,sde,w'",
331 def testInvalidFileDisk(self):
333 (objects.Disk(dev_type=constants.DT_FILE, mode=constants.DISK_RDWR,
334 physical_id=["#unknown#"]),
338 self.assertRaises(KeyError, hv_xen._GetConfigFileDiskData, disks, "sd")
341 class TestXenHypervisorRunXen(unittest.TestCase):
345 def testCommandUnknown(self):
346 cmd = "#unknown command#"
347 self.assertFalse(cmd in constants.KNOWN_XEN_COMMANDS)
348 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
349 _run_cmd_fn=NotImplemented,
351 self.assertRaises(errors.ProgrammerError, hv._RunXen, [], None)
353 def testCommandNoHvparams(self):
354 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
355 _run_cmd_fn=NotImplemented)
357 self.assertRaises(errors.HypervisorError, hv._RunXen, [self.XEN_SUB_CMD],
360 def testCommandFromHvparams(self):
361 expected_xen_cmd = "xl"
362 hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
363 mock_run_cmd = mock.Mock()
364 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
365 _run_cmd_fn=mock_run_cmd)
366 hv._RunXen([self.XEN_SUB_CMD], hvparams=hvparams)
367 mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_SUB_CMD])
370 class TestXenHypervisorGetInstanceList(unittest.TestCase):
372 RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
375 def testNoHvparams(self):
376 expected_xen_cmd = "xm"
377 mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
378 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
379 _run_cmd_fn=mock_run_cmd)
380 self.assertRaises(errors.HypervisorError, hv._GetInstanceList, True, None)
382 def testFromHvparams(self):
383 expected_xen_cmd = "xl"
384 hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
385 mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
386 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
387 _run_cmd_fn=mock_run_cmd)
388 hv._GetInstanceList(True, hvparams)
389 mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
392 class TestXenHypervisorListInstances(unittest.TestCase):
394 RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
397 def testNoHvparams(self):
398 expected_xen_cmd = "xm"
399 mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
400 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
401 _run_cmd_fn=mock_run_cmd)
402 self.assertRaises(errors.HypervisorError, hv.ListInstances)
404 def testHvparamsXl(self):
405 expected_xen_cmd = "xl"
406 hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
407 mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
408 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
409 _run_cmd_fn=mock_run_cmd)
410 hv.ListInstances(hvparams=hvparams)
411 mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
414 class TestXenHypervisorCheckToolstack(unittest.TestCase):
417 self.tmpdir = tempfile.mkdtemp()
418 self.cfg_name = "xen_config"
419 self.cfg_path = utils.PathJoin(self.tmpdir, self.cfg_name)
420 self.hv = hv_xen.XenHypervisor()
423 shutil.rmtree(self.tmpdir)
425 def testBinaryNotFound(self):
426 RESULT_FAILED = utils.RunResult(1, None, "", "", "", None, None)
427 mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
428 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
429 _run_cmd_fn=mock_run_cmd)
430 result = hv._CheckToolstackBinary("xl")
431 self.assertFalse(result)
433 def testCheckToolstackXlConfigured(self):
434 RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
435 mock_run_cmd = mock.Mock(return_value=RESULT_OK)
436 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
437 _run_cmd_fn=mock_run_cmd)
438 result = hv._CheckToolstackXlConfigured()
439 self.assertTrue(result)
441 def testCheckToolstackXlNotConfigured(self):
442 RESULT_FAILED = utils.RunResult(
444 "ERROR: A different toolstack (xm) have been selected!",
446 mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
447 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
448 _run_cmd_fn=mock_run_cmd)
449 result = hv._CheckToolstackXlConfigured()
450 self.assertFalse(result)
452 def testCheckToolstackXlFails(self):
453 RESULT_FAILED = utils.RunResult(
455 "ERROR: The pink bunny hid the binary.",
457 mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
458 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
459 _run_cmd_fn=mock_run_cmd)
460 self.assertRaises(errors.HypervisorError, hv._CheckToolstackXlConfigured)
463 class TestXenHypervisorWriteConfigFile(unittest.TestCase):
465 self.tmpdir = tempfile.mkdtemp()
468 shutil.rmtree(self.tmpdir)
470 def testWriteError(self):
471 cfgdir = utils.PathJoin(self.tmpdir, "foobar")
473 hv = hv_xen.XenHypervisor(_cfgdir=cfgdir,
474 _run_cmd_fn=NotImplemented,
477 self.assertFalse(os.path.exists(cfgdir))
480 hv._WriteConfigFile("name", "data")
481 except errors.HypervisorError, err:
482 self.assertTrue(str(err).startswith("Cannot write Xen instance"))
484 self.fail("Exception was not raised")
487 class TestXenHypervisorVerify(unittest.TestCase):
490 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
491 self._result_ok = utils.RunResult(0, None, output, "", "", None, None)
493 def testVerify(self):
494 hvparams = {constants.HV_XEN_CMD : constants.XEN_CMD_XL}
495 mock_run_cmd = mock.Mock(return_value=self._result_ok)
496 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
497 _run_cmd_fn=mock_run_cmd)
498 hv._CheckToolstack = mock.Mock(return_value=True)
499 result = hv.Verify(hvparams)
500 self.assertTrue(result is None)
502 def testVerifyToolstackNotOk(self):
503 hvparams = {constants.HV_XEN_CMD : constants.XEN_CMD_XL}
504 mock_run_cmd = mock.Mock(return_value=self._result_ok)
505 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
506 _run_cmd_fn=mock_run_cmd)
507 hv._CheckToolstack = mock.Mock()
508 hv._CheckToolstack.side_effect = errors.HypervisorError("foo")
509 result = hv.Verify(hvparams)
510 self.assertTrue(result is not None)
512 def testVerifyFailing(self):
513 result_failed = utils.RunResult(1, None, "", "", "", None, None)
514 mock_run_cmd = mock.Mock(return_value=result_failed)
515 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
516 _run_cmd_fn=mock_run_cmd)
517 hv._CheckToolstack = mock.Mock(return_value=True)
519 self.assertTrue(result is not None)
522 class _TestXenHypervisor(object):
523 TARGET = NotImplemented
525 HVNAME = NotImplemented
526 VALID_HVPARAMS = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
529 super(_TestXenHypervisor, self).setUp()
531 self.tmpdir = tempfile.mkdtemp()
533 self.vncpw = "".join(random.sample(string.ascii_letters, 10))
535 self.vncpw_path = utils.PathJoin(self.tmpdir, "vncpw")
536 utils.WriteFile(self.vncpw_path, data=self.vncpw)
539 super(_TestXenHypervisor, self).tearDown()
541 shutil.rmtree(self.tmpdir)
543 def _GetHv(self, run_cmd=NotImplemented):
544 return self.TARGET(_cfgdir=self.tmpdir, _run_cmd_fn=run_cmd, _cmd=self.CMD)
546 def _SuccessCommand(self, stdout, cmd):
547 self.assertEqual(cmd[0], self.CMD)
549 return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
550 NotImplemented, NotImplemented)
552 def _FailingCommand(self, cmd):
553 self.assertEqual(cmd[0], self.CMD)
555 return utils.RunResult(constants.EXIT_FAILURE, None,
556 "", "This command failed", None,
557 NotImplemented, NotImplemented)
559 def _FakeTcpPing(self, expected, result, target, port, **kwargs):
560 self.assertEqual((target, port), expected)
563 def testReadingNonExistentConfigFile(self):
567 hv._ReadConfigFile("inst15780.example.com")
568 except errors.HypervisorError, err:
569 self.assertTrue(str(err).startswith("Failed to load Xen config file:"))
571 self.fail("Exception was not raised")
573 def testRemovingAutoConfigFile(self):
574 name = "inst8206.example.com"
575 cfgfile = utils.PathJoin(self.tmpdir, name)
576 autodir = utils.PathJoin(self.tmpdir, "auto")
577 autocfgfile = utils.PathJoin(autodir, name)
581 utils.WriteFile(autocfgfile, data="")
585 self.assertTrue(os.path.isfile(autocfgfile))
586 hv._WriteConfigFile(name, "content")
587 self.assertFalse(os.path.exists(autocfgfile))
588 self.assertEqual(utils.ReadFile(cfgfile), "content")
590 def _XenList(self, cmd):
591 self.assertEqual(cmd, [self.CMD, "list"])
593 # TODO: Use actual data from "xl" command
594 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
596 return self._SuccessCommand(output, cmd)
598 def testGetInstanceInfo(self):
599 hv = self._GetHv(run_cmd=self._XenList)
601 (name, instid, memory, vcpus, state, runtime) = \
602 hv.GetInstanceInfo("server01.example.com")
604 self.assertEqual(name, "server01.example.com")
605 self.assertEqual(instid, 1)
606 self.assertEqual(memory, 1024)
607 self.assertEqual(vcpus, 1)
608 self.assertEqual(state, "-b----")
609 self.assertAlmostEqual(runtime, 167643.2)
611 def testGetInstanceInfoDom0(self):
612 hv = self._GetHv(run_cmd=self._XenList)
614 # TODO: Not sure if this is actually used anywhere (can't find it), but the
615 # code supports querying for Dom0
616 (name, instid, memory, vcpus, state, runtime) = \
617 hv.GetInstanceInfo(hv_xen._DOM0_NAME)
619 self.assertEqual(name, "Domain-0")
620 self.assertEqual(instid, 0)
621 self.assertEqual(memory, 1023)
622 self.assertEqual(vcpus, 1)
623 self.assertEqual(state, "r-----")
624 self.assertAlmostEqual(runtime, 154706.1)
626 def testGetInstanceInfoUnknown(self):
627 hv = self._GetHv(run_cmd=self._XenList)
629 result = hv.GetInstanceInfo("unknown.example.com")
630 self.assertTrue(result is None)
632 def testGetAllInstancesInfo(self):
633 hv = self._GetHv(run_cmd=self._XenList)
635 result = hv.GetAllInstancesInfo()
637 self.assertEqual(map(compat.fst, result), [
638 "server01.example.com",
639 "web3106215069.example.com",
640 "testinstance.example.com",
643 def testListInstances(self):
644 hv = self._GetHv(run_cmd=self._XenList)
646 self.assertEqual(hv.ListInstances(), [
647 "server01.example.com",
648 "web3106215069.example.com",
649 "testinstance.example.com",
652 def _StartInstanceCommand(self, inst, paused, failcreate, cmd):
653 if cmd == [self.CMD, "info"]:
654 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
655 elif cmd == [self.CMD, "list"]:
656 output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
657 elif cmd[:2] == [self.CMD, "create"]:
659 cfgfile = utils.PathJoin(self.tmpdir, inst.name)
662 self.assertEqual(args, ["-p", cfgfile])
664 self.assertEqual(args, [cfgfile])
667 return self._FailingCommand(cmd)
671 self.fail("Unhandled command: %s" % (cmd, ))
673 return self._SuccessCommand(output, cmd)
675 def _MakeInstance(self):
676 # Copy default parameters
677 bep = objects.FillDict(constants.BEC_DEFAULTS, {})
678 hvp = objects.FillDict(constants.HVC_DEFAULTS[self.HVNAME], {})
680 # Override default VNC password file path
681 if constants.HV_VNC_PASSWORD_FILE in hvp:
682 hvp[constants.HV_VNC_PASSWORD_FILE] = self.vncpw_path
685 (objects.Disk(dev_type=constants.DT_PLAIN, mode=constants.DISK_RDWR),
686 utils.PathJoin(self.tmpdir, "disk0")),
687 (objects.Disk(dev_type=constants.DT_PLAIN, mode=constants.DISK_RDONLY),
688 utils.PathJoin(self.tmpdir, "disk1")),
691 inst = objects.Instance(name="server01.example.com",
692 hvparams=hvp, beparams=bep,
693 osparams={}, nics=[], os="deb1",
694 disks=map(compat.fst, disks))
699 def testStartInstance(self):
700 (inst, disks) = self._MakeInstance()
701 pathutils.LOG_XEN_DIR = self.tmpdir
703 for failcreate in [False, True]:
704 for paused in [False, True]:
705 run_cmd = compat.partial(self._StartInstanceCommand,
706 inst, paused, failcreate)
708 hv = self._GetHv(run_cmd=run_cmd)
710 # Ensure instance is not listed
711 self.assertTrue(inst.name not in hv.ListInstances())
713 # Remove configuration
714 cfgfile = utils.PathJoin(self.tmpdir, inst.name)
715 utils.RemoveFile(cfgfile)
718 self.assertRaises(errors.HypervisorError, hv.StartInstance,
720 # Check whether a stale config file is left behind
721 self.assertFalse(os.path.exists(cfgfile))
723 hv.StartInstance(inst, disks, paused)
724 # Check if configuration was updated
725 lines = utils.ReadFile(cfgfile).splitlines()
727 if constants.HV_VNC_PASSWORD_FILE in inst.hvparams:
728 self.assertTrue(("vncpasswd = '%s'" % self.vncpw) in lines)
730 extra = inst.hvparams[constants.HV_KERNEL_ARGS]
731 self.assertTrue(("extra = '%s'" % extra) in lines)
733 def _StopInstanceCommand(self, instance_name, force, fail, cmd):
734 if (cmd == [self.CMD, "list"]):
735 output = "Name ID Mem VCPUs State Time(s)\n" \
736 "Domain-0 0 1023 1 r----- 142691.0\n" \
737 "%s 417 128 1 r----- 3.2\n" % instance_name
738 elif cmd[:2] == [self.CMD, "destroy"]:
739 self.assertEqual(cmd[2:], [instance_name])
741 elif not force and cmd[:3] == [self.CMD, "shutdown", "-w"]:
742 self.assertEqual(cmd[3:], [instance_name])
745 self.fail("Unhandled command: %s" % (cmd, ))
748 # Simulate a failing command
749 return self._FailingCommand(cmd)
751 return self._SuccessCommand(output, cmd)
753 def testStopInstance(self):
754 name = "inst4284.example.com"
755 cfgfile = utils.PathJoin(self.tmpdir, name)
756 cfgdata = "config file content\n"
758 for force in [False, True]:
759 for fail in [False, True]:
760 utils.WriteFile(cfgfile, data=cfgdata)
762 run_cmd = compat.partial(self._StopInstanceCommand, name, force, fail)
764 hv = self._GetHv(run_cmd=run_cmd)
766 self.assertTrue(os.path.isfile(cfgfile))
770 hv._StopInstance(name, force, None)
771 except errors.HypervisorError, err:
772 self.assertTrue(str(err).startswith("listing instances failed"),
775 self.fail("Exception was not raised")
776 self.assertEqual(utils.ReadFile(cfgfile), cfgdata,
777 msg=("Configuration was removed when stopping"
780 hv._StopInstance(name, force, None)
781 self.assertFalse(os.path.exists(cfgfile))
783 def _MigrateNonRunningInstCmd(self, cmd):
784 if cmd == [self.CMD, "list"]:
785 output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
787 self.fail("Unhandled command: %s" % (cmd, ))
789 return self._SuccessCommand(output, cmd)
791 def testMigrateInstanceNotRunning(self):
792 name = "nonexistinginstance.example.com"
793 target = constants.IP4_ADDRESS_LOCALHOST
796 hv = self._GetHv(run_cmd=self._MigrateNonRunningInstCmd)
798 for live in [False, True]:
800 hv._MigrateInstance(NotImplemented, name, target, port, live,
801 self.VALID_HVPARAMS, _ping_fn=NotImplemented)
802 except errors.HypervisorError, err:
803 self.assertEqual(str(err), "Instance not running, cannot migrate")
805 self.fail("Exception was not raised")
807 def _MigrateInstTargetUnreachCmd(self, cmd):
808 if cmd == [self.CMD, "list"]:
809 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
811 self.fail("Unhandled command: %s" % (cmd, ))
813 return self._SuccessCommand(output, cmd)
815 def testMigrateTargetUnreachable(self):
816 name = "server01.example.com"
817 target = constants.IP4_ADDRESS_LOCALHOST
820 hv = self._GetHv(run_cmd=self._MigrateInstTargetUnreachCmd)
821 hvparams = {constants.HV_XEN_CMD: self.CMD}
823 for live in [False, True]:
824 if self.CMD == constants.XEN_CMD_XL:
825 # TODO: Detect unreachable targets
829 hv._MigrateInstance(NotImplemented, name, target, port, live,
831 _ping_fn=compat.partial(self._FakeTcpPing,
832 (target, port), False))
833 except errors.HypervisorError, err:
834 wanted = "Remote host %s not" % target
835 self.assertTrue(str(err).startswith(wanted))
837 self.fail("Exception was not raised")
839 def _MigrateInstanceCmd(self, cluster_name, instance_name, target, port,
841 if cmd == [self.CMD, "list"]:
842 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
843 elif cmd[:2] == [self.CMD, "migrate"]:
844 if self.CMD == constants.XEN_CMD_XM:
845 args = ["-p", str(port)]
850 elif self.CMD == constants.XEN_CMD_XL:
852 "-s", constants.XL_SSH_CMD % cluster_name,
853 "-C", utils.PathJoin(self.tmpdir, instance_name),
857 self.fail("Unknown Xen command '%s'" % self.CMD)
859 args.extend([instance_name, target])
860 self.assertEqual(cmd[2:], args)
863 return self._FailingCommand(cmd)
867 self.fail("Unhandled command: %s" % (cmd, ))
869 return self._SuccessCommand(output, cmd)
871 def testMigrateInstance(self):
872 clustername = "cluster.example.com"
873 instname = "server01.example.com"
874 target = constants.IP4_ADDRESS_LOCALHOST
877 hvparams = {constants.HV_XEN_CMD: self.CMD}
879 for live in [False, True]:
880 for fail in [False, True]:
882 testutils.CallCounter(compat.partial(self._FakeTcpPing,
883 (target, port), True))
886 compat.partial(self._MigrateInstanceCmd,
887 clustername, instname, target, port, live,
890 hv = self._GetHv(run_cmd=run_cmd)
894 hv._MigrateInstance(clustername, instname, target, port, live,
895 hvparams, _ping_fn=ping_fn)
896 except errors.HypervisorError, err:
897 self.assertTrue(str(err).startswith("Failed to migrate instance"))
899 self.fail("Exception was not raised")
901 hv._MigrateInstance(clustername, instname, target, port, live,
902 hvparams, _ping_fn=ping_fn)
904 if self.CMD == constants.XEN_CMD_XM:
909 self.assertEqual(ping_fn.Count(), expected_pings)
911 def _GetNodeInfoCmd(self, fail, cmd):
912 if cmd == [self.CMD, "info"]:
914 return self._FailingCommand(cmd)
916 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
917 elif cmd == [self.CMD, "list"]:
919 self.fail("'xm list' shouldn't be called when 'xm info' failed")
921 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
923 self.fail("Unhandled command: %s" % (cmd, ))
925 return self._SuccessCommand(output, cmd)
927 def testGetNodeInfo(self):
928 run_cmd = compat.partial(self._GetNodeInfoCmd, False)
929 hv = self._GetHv(run_cmd=run_cmd)
930 result = hv.GetNodeInfo()
932 self.assertEqual(result["hv_version"], (4, 0))
933 self.assertEqual(result["memory_free"], 8004)
935 def testGetNodeInfoFailing(self):
936 run_cmd = compat.partial(self._GetNodeInfoCmd, True)
937 hv = self._GetHv(run_cmd=run_cmd)
938 self.assertTrue(hv.GetNodeInfo() is None)
941 def _MakeTestClass(cls, cmd):
942 """Makes a class for testing.
944 The returned class has structure as shown in the following pseudo code:
946 class Test{cls.__name__}{cmd}(_TestXenHypervisor, unittest.TestCase):
949 HVNAME = {Hypervisor name retrieved using class}
952 @param cls: Hypervisor class to be tested
954 @param cmd: Hypervisor command
956 @return: Class name and class object (not instance)
959 name = "Test%sCmd%s" % (cls.__name__, cmd.title())
960 bases = (_TestXenHypervisor, unittest.TestCase)
961 hvname = HVCLASS_TO_HVNAME[cls]
963 return (name, type(name, bases, dict(TARGET=cls, CMD=cmd, HVNAME=hvname)))
966 # Create test classes programmatically instead of manually to reduce the risk
967 # of forgetting some combinations
968 for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]:
969 for cmd in constants.KNOWN_XEN_COMMANDS:
970 (name, testcls) = _MakeTestClass(cls, cmd)
972 assert name not in locals()
974 locals()[name] = testcls
977 if __name__ == "__main__":
978 testutils.GanetiTestProgram()