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 for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]:
52 instance = objects.Instance(name="xen.example.com",
53 primary_node="node24828")
54 cons = cls.GetInstanceConsole(instance, {}, {})
55 self.assertTrue(cons.Validate())
56 self.assertEqual(cons.kind, constants.CONS_SSH)
57 self.assertEqual(cons.host, instance.primary_node)
58 self.assertEqual(cons.command[-1], instance.name)
61 class TestCreateConfigCpus(unittest.TestCase):
63 for cpu_mask in [None, ""]:
64 self.assertEqual(hv_xen._CreateConfigCpus(cpu_mask),
68 self.assertEqual(hv_xen._CreateConfigCpus(constants.CPU_PINNING_ALL),
72 self.assertEqual(hv_xen._CreateConfigCpus("9"), "cpu = \"9\"")
74 def testMultiple(self):
75 self.assertEqual(hv_xen._CreateConfigCpus("0-2,4,5-5:3:all"),
76 ("cpus = [ \"0,1,2,4,5\", \"3\", \"%s\" ]" %
77 constants.CPU_PINNING_ALL_XEN))
80 class TestGetCommand(testutils.GanetiTestCase):
81 def testDefault(self):
83 hv = hv_xen.XenHypervisor()
84 self.assertEqual(hv._GetCommand(), expected_cmd)
86 def testCommandExplicit(self):
87 """Test the case when the command is given as class parameter explicitly.
91 hv = hv_xen.XenHypervisor(_cmd=constants.XEN_CMD_XL)
92 self.assertEqual(hv._GetCommand(), expected_cmd)
94 def testCommandInvalid(self):
95 """Test the case an invalid command is given as class parameter explicitly.
98 hv = hv_xen.XenHypervisor(_cmd="invalidcommand")
99 self.assertRaises(errors.ProgrammerError, hv._GetCommand, None)
101 def testCommandHvparams(self):
103 test_hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
104 hv = hv_xen.XenHypervisor()
105 self.assertEqual(hv._GetCommand(hvparams=test_hvparams), expected_cmd)
107 def testCommandHvparamsInvalid(self):
109 hv = hv_xen.XenHypervisor()
110 self.assertRaises(KeyError, hv._GetCommand, test_hvparams)
112 def testCommandHvparamsCmdInvalid(self):
113 test_hvparams = {constants.HV_XEN_CMD: "invalidcommand"}
114 hv = hv_xen.XenHypervisor()
115 self.assertRaises(errors.ProgrammerError, hv._GetCommand, test_hvparams)
118 class TestParseInstanceList(testutils.GanetiTestCase):
120 data = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
123 self.assertEqual(hv_xen._ParseInstanceList(data.splitlines(), False), [])
126 result = hv_xen._ParseInstanceList(data.splitlines(), True)
127 self.assertEqual(len(result), 1)
128 self.assertEqual(len(result[0]), 6)
131 self.assertEqual(result[0][0], hv_xen._DOM0_NAME)
134 self.assertEqual(result[0][1], 0)
137 self.assertEqual(result[0][2], 1023)
140 self.assertEqual(result[0][3], 1)
143 self.assertEqual(result[0][4], "r-----")
146 self.assertAlmostEqual(result[0][5], 121152.6)
148 def testWrongLineFormat(self):
150 ["three fields only"],
151 ["name InvalidID 128 1 r----- 12345"],
156 hv_xen._ParseInstanceList(["Header would be here"] + lines, False)
157 except errors.HypervisorError, err:
158 self.assertTrue("Can't parse instance list" in str(err))
160 self.fail("Exception was not raised")
163 class TestGetInstanceList(testutils.GanetiTestCase):
165 return utils.RunResult(constants.EXIT_FAILURE, None,
166 "stdout", "stderr", None,
167 NotImplemented, NotImplemented)
169 def testTimeout(self):
170 fn = testutils.CallCounter(self._Fail)
172 hv_xen._GetInstanceList(fn, False, _timeout=0.1)
173 except errors.HypervisorError, err:
174 self.assertTrue("timeout exceeded" in str(err))
176 self.fail("Exception was not raised")
178 self.assertTrue(fn.Count() < 10,
179 msg="'xm list' was called too many times")
181 def _Success(self, stdout):
182 return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
183 NotImplemented, NotImplemented)
185 def testSuccess(self):
186 data = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
188 fn = testutils.CallCounter(compat.partial(self._Success, data))
190 result = hv_xen._GetInstanceList(fn, True, _timeout=0.1)
192 self.assertEqual(len(result), 4)
194 self.assertEqual(map(compat.fst, result), [
196 "server01.example.com",
197 "web3106215069.example.com",
198 "testinstance.example.com",
201 self.assertEqual(fn.Count(), 1)
204 class TestParseNodeInfo(testutils.GanetiTestCase):
206 self.assertEqual(hv_xen._ParseNodeInfo(""), {})
208 def testUnknownInput(self):
211 "something else goes",
214 self.assertEqual(hv_xen._ParseNodeInfo(data), {})
216 def testBasicInfo(self):
217 data = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
218 result = hv_xen._ParseNodeInfo(data)
219 self.assertEqual(result, {
223 "hv_version": (4, 0),
225 "memory_total": 16378,
229 class TestMergeInstanceInfo(testutils.GanetiTestCase):
231 self.assertEqual(hv_xen._MergeInstanceInfo({}, []), {})
233 def _FakeXmList(self, include_node):
235 (hv_xen._DOM0_NAME, NotImplemented, 4096, 7, NotImplemented,
237 ("inst1.example.com", NotImplemented, 2048, 4, NotImplemented,
241 def testMissingNodeInfo(self):
242 instance_list = self._FakeXmList(True)
243 result = hv_xen._MergeInstanceInfo({}, instance_list)
244 self.assertEqual(result, {
249 def testWithNodeInfo(self):
250 info = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
251 instance_list = self._FakeXmList(True)
252 result = hv_xen._GetNodeInfo(info, instance_list)
253 self.assertEqual(result, {
258 "hv_version": (4, 0),
262 "memory_total": 16378,
266 class TestGetConfigFileDiskData(unittest.TestCase):
267 def testLetterCount(self):
268 self.assertEqual(len(hv_xen._DISK_LETTERS), 26)
270 def testNoDisks(self):
271 self.assertEqual(hv_xen._GetConfigFileDiskData([], "hd"), [])
273 def testManyDisks(self):
274 for offset in [0, 1, 10]:
275 disks = [(objects.Disk(dev_type=constants.LD_LV), "/tmp/disk/%s" % idx)
276 for idx in range(len(hv_xen._DISK_LETTERS) + offset)]
279 result = hv_xen._GetConfigFileDiskData(disks, "hd")
280 self.assertEqual(result, [
281 "'phy:/tmp/disk/%s,hd%s,r'" % (idx, string.ascii_lowercase[idx])
282 for idx in range(len(hv_xen._DISK_LETTERS) + offset)
286 hv_xen._GetConfigFileDiskData(disks, "hd")
287 except errors.HypervisorError, err:
288 self.assertEqual(str(err), "Too many disks")
290 self.fail("Exception was not raised")
292 def testTwoLvDisksWithMode(self):
294 (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDWR),
296 (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDONLY),
300 result = hv_xen._GetConfigFileDiskData(disks, "hd")
301 self.assertEqual(result, [
302 "'phy:/tmp/diskFirst,hda,w'",
303 "'phy:/tmp/diskLast,hdb,r'",
306 def testFileDisks(self):
308 (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
309 physical_id=[constants.FD_LOOP]),
311 (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDONLY,
312 physical_id=[constants.FD_BLKTAP]),
314 (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
315 physical_id=[constants.FD_LOOP]),
317 (objects.Disk(dev_type=constants.LD_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 "'tap:aio:/tmp/diskLast,sdd,w'",
330 def testInvalidFileDisk(self):
332 (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
333 physical_id=["#unknown#"]),
337 self.assertRaises(KeyError, hv_xen._GetConfigFileDiskData, disks, "sd")
340 class TestXenHypervisorRunXen(unittest.TestCase):
344 def testCommandUnknown(self):
345 cmd = "#unknown command#"
346 self.assertFalse(cmd in constants.KNOWN_XEN_COMMANDS)
347 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
348 _run_cmd_fn=NotImplemented,
350 self.assertRaises(errors.ProgrammerError, hv._RunXen, [])
352 def testCommandValid(self):
354 mock_run_cmd = mock.Mock()
355 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
356 _run_cmd_fn=mock_run_cmd)
357 hv._RunXen([self.XEN_SUB_CMD])
358 mock_run_cmd.assert_called_with([xen_cmd, 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)
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 hv._GetInstanceList(True)
381 mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
383 def testFromHvparams(self):
384 expected_xen_cmd = "xl"
385 hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
386 mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
387 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
388 _run_cmd_fn=mock_run_cmd)
389 hv._GetInstanceList(True, hvparams=hvparams)
390 mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
393 class TestXenHypervisorListInstances(unittest.TestCase):
395 RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
398 def testDefaultXm(self):
399 expected_xen_cmd = "xm"
400 mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
401 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
402 _run_cmd_fn=mock_run_cmd)
404 mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
406 def testHvparamsXl(self):
407 expected_xen_cmd = "xl"
408 hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
409 mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
410 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
411 _run_cmd_fn=mock_run_cmd)
412 hv.ListInstances(hvparams=hvparams)
413 mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
416 class TestXenHypervisorCheckToolstack(unittest.TestCase):
419 self.tmpdir = tempfile.mkdtemp()
420 self.cfg_name = "xen_config"
421 self.cfg_path = utils.PathJoin(self.tmpdir, self.cfg_name)
422 self.hv = hv_xen.XenHypervisor()
425 shutil.rmtree(self.tmpdir)
427 def testBinaryNotFound(self):
428 RESULT_FAILED = utils.RunResult(1, None, "", "", "", None, None)
429 mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
430 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
431 _run_cmd_fn=mock_run_cmd)
432 result = hv._CheckToolstackBinary("xl")
433 self.assertFalse(result)
435 def testCheckToolstackXlConfigured(self):
436 RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
437 mock_run_cmd = mock.Mock(return_value=RESULT_OK)
438 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
439 _run_cmd_fn=mock_run_cmd)
440 result = hv._CheckToolstackXlConfigured()
441 self.assertTrue(result)
443 def testCheckToolstackXlNotConfigured(self):
444 RESULT_FAILED = utils.RunResult(
446 "ERROR: A different toolstack (xm) have been selected!",
448 mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
449 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
450 _run_cmd_fn=mock_run_cmd)
451 result = hv._CheckToolstackXlConfigured()
452 self.assertFalse(result)
454 def testCheckToolstackXlFails(self):
455 RESULT_FAILED = utils.RunResult(
457 "ERROR: The pink bunny hid the binary.",
459 mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
460 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
461 _run_cmd_fn=mock_run_cmd)
462 self.assertRaises(errors.HypervisorError, hv._CheckToolstackXlConfigured)
465 class TestXenHypervisorWriteConfigFile(unittest.TestCase):
467 self.tmpdir = tempfile.mkdtemp()
470 shutil.rmtree(self.tmpdir)
472 def testWriteError(self):
473 cfgdir = utils.PathJoin(self.tmpdir, "foobar")
475 hv = hv_xen.XenHypervisor(_cfgdir=cfgdir,
476 _run_cmd_fn=NotImplemented,
479 self.assertFalse(os.path.exists(cfgdir))
482 hv._WriteConfigFile("name", "data")
483 except errors.HypervisorError, err:
484 self.assertTrue(str(err).startswith("Cannot write Xen instance"))
486 self.fail("Exception was not raised")
489 class TestXenHypervisorVerify(unittest.TestCase):
492 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
493 self._result_ok = utils.RunResult(0, None, output, "", "", None, None)
495 def testVerify(self):
496 hvparams = {constants.HV_XEN_CMD : constants.XEN_CMD_XL}
497 mock_run_cmd = mock.Mock(return_value=self._result_ok)
498 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
499 _run_cmd_fn=mock_run_cmd)
500 hv._CheckToolstack = mock.Mock(return_value=True)
501 result = hv.Verify(hvparams)
502 self.assertTrue(result is None)
504 def testVerifyToolstackNotOk(self):
505 hvparams = {constants.HV_XEN_CMD : constants.XEN_CMD_XL}
506 mock_run_cmd = mock.Mock(return_value=self._result_ok)
507 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
508 _run_cmd_fn=mock_run_cmd)
509 hv._CheckToolstack = mock.Mock()
510 hv._CheckToolstack.side_effect = errors.HypervisorError("foo")
511 result = hv.Verify(hvparams)
512 self.assertTrue(result is not None)
514 def testVerifyFailing(self):
515 result_failed = utils.RunResult(1, None, "", "", "", None, None)
516 mock_run_cmd = mock.Mock(return_value=result_failed)
517 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
518 _run_cmd_fn=mock_run_cmd)
519 hv._CheckToolstack = mock.Mock(return_value=True)
521 self.assertTrue(result is not None)
524 class _TestXenHypervisor(object):
525 TARGET = NotImplemented
527 HVNAME = NotImplemented
528 VALID_HVPARAMS = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
531 super(_TestXenHypervisor, self).setUp()
533 self.tmpdir = tempfile.mkdtemp()
535 self.vncpw = "".join(random.sample(string.ascii_letters, 10))
537 self.vncpw_path = utils.PathJoin(self.tmpdir, "vncpw")
538 utils.WriteFile(self.vncpw_path, data=self.vncpw)
541 super(_TestXenHypervisor, self).tearDown()
543 shutil.rmtree(self.tmpdir)
545 def _GetHv(self, run_cmd=NotImplemented):
546 return self.TARGET(_cfgdir=self.tmpdir, _run_cmd_fn=run_cmd, _cmd=self.CMD)
548 def _SuccessCommand(self, stdout, cmd):
549 self.assertEqual(cmd[0], self.CMD)
551 return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
552 NotImplemented, NotImplemented)
554 def _FailingCommand(self, cmd):
555 self.assertEqual(cmd[0], self.CMD)
557 return utils.RunResult(constants.EXIT_FAILURE, None,
558 "", "This command failed", None,
559 NotImplemented, NotImplemented)
561 def _FakeTcpPing(self, expected, result, target, port, **kwargs):
562 self.assertEqual((target, port), expected)
565 def testReadingNonExistentConfigFile(self):
569 hv._ReadConfigFile("inst15780.example.com")
570 except errors.HypervisorError, err:
571 self.assertTrue(str(err).startswith("Failed to load Xen config file:"))
573 self.fail("Exception was not raised")
575 def testRemovingAutoConfigFile(self):
576 name = "inst8206.example.com"
577 cfgfile = utils.PathJoin(self.tmpdir, name)
578 autodir = utils.PathJoin(self.tmpdir, "auto")
579 autocfgfile = utils.PathJoin(autodir, name)
583 utils.WriteFile(autocfgfile, data="")
587 self.assertTrue(os.path.isfile(autocfgfile))
588 hv._WriteConfigFile(name, "content")
589 self.assertFalse(os.path.exists(autocfgfile))
590 self.assertEqual(utils.ReadFile(cfgfile), "content")
592 def _XenList(self, cmd):
593 self.assertEqual(cmd, [self.CMD, "list"])
595 # TODO: Use actual data from "xl" command
596 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
598 return self._SuccessCommand(output, cmd)
600 def testGetInstanceInfo(self):
601 hv = self._GetHv(run_cmd=self._XenList)
603 (name, instid, memory, vcpus, state, runtime) = \
604 hv.GetInstanceInfo("server01.example.com")
606 self.assertEqual(name, "server01.example.com")
607 self.assertEqual(instid, 1)
608 self.assertEqual(memory, 1024)
609 self.assertEqual(vcpus, 1)
610 self.assertEqual(state, "-b----")
611 self.assertAlmostEqual(runtime, 167643.2)
613 def testGetInstanceInfoDom0(self):
614 hv = self._GetHv(run_cmd=self._XenList)
616 # TODO: Not sure if this is actually used anywhere (can't find it), but the
617 # code supports querying for Dom0
618 (name, instid, memory, vcpus, state, runtime) = \
619 hv.GetInstanceInfo(hv_xen._DOM0_NAME)
621 self.assertEqual(name, "Domain-0")
622 self.assertEqual(instid, 0)
623 self.assertEqual(memory, 1023)
624 self.assertEqual(vcpus, 1)
625 self.assertEqual(state, "r-----")
626 self.assertAlmostEqual(runtime, 154706.1)
628 def testGetInstanceInfoUnknown(self):
629 hv = self._GetHv(run_cmd=self._XenList)
631 result = hv.GetInstanceInfo("unknown.example.com")
632 self.assertTrue(result is None)
634 def testGetAllInstancesInfo(self):
635 hv = self._GetHv(run_cmd=self._XenList)
637 result = hv.GetAllInstancesInfo()
639 self.assertEqual(map(compat.fst, result), [
640 "server01.example.com",
641 "web3106215069.example.com",
642 "testinstance.example.com",
645 def testListInstances(self):
646 hv = self._GetHv(run_cmd=self._XenList)
648 self.assertEqual(hv.ListInstances(), [
649 "server01.example.com",
650 "web3106215069.example.com",
651 "testinstance.example.com",
654 def _StartInstanceCommand(self, inst, paused, failcreate, cmd):
655 if cmd == [self.CMD, "info"]:
656 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
657 elif cmd == [self.CMD, "list"]:
658 output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
659 elif cmd[:2] == [self.CMD, "create"]:
661 cfgfile = utils.PathJoin(self.tmpdir, inst.name)
664 self.assertEqual(args, ["-p", cfgfile])
666 self.assertEqual(args, [cfgfile])
669 return self._FailingCommand(cmd)
673 self.fail("Unhandled command: %s" % (cmd, ))
675 return self._SuccessCommand(output, cmd)
677 def _MakeInstance(self):
678 # Copy default parameters
679 bep = objects.FillDict(constants.BEC_DEFAULTS, {})
680 hvp = objects.FillDict(constants.HVC_DEFAULTS[self.HVNAME], {})
682 # Override default VNC password file path
683 if constants.HV_VNC_PASSWORD_FILE in hvp:
684 hvp[constants.HV_VNC_PASSWORD_FILE] = self.vncpw_path
687 (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDWR),
688 utils.PathJoin(self.tmpdir, "disk0")),
689 (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDONLY),
690 utils.PathJoin(self.tmpdir, "disk1")),
693 inst = objects.Instance(name="server01.example.com",
694 hvparams=hvp, beparams=bep,
695 osparams={}, nics=[], os="deb1",
696 disks=map(compat.fst, disks))
701 def testStartInstance(self):
702 (inst, disks) = self._MakeInstance()
703 pathutils.LOG_XEN_DIR = self.tmpdir
705 for failcreate in [False, True]:
706 for paused in [False, True]:
707 run_cmd = compat.partial(self._StartInstanceCommand,
708 inst, paused, failcreate)
710 hv = self._GetHv(run_cmd=run_cmd)
712 # Ensure instance is not listed
713 self.assertTrue(inst.name not in hv.ListInstances())
715 # Remove configuration
716 cfgfile = utils.PathJoin(self.tmpdir, inst.name)
717 utils.RemoveFile(cfgfile)
720 self.assertRaises(errors.HypervisorError, hv.StartInstance,
722 # Check whether a stale config file is left behind
723 self.assertFalse(os.path.exists(cfgfile))
725 hv.StartInstance(inst, disks, paused)
726 # Check if configuration was updated
727 lines = utils.ReadFile(cfgfile).splitlines()
729 if constants.HV_VNC_PASSWORD_FILE in inst.hvparams:
730 self.assertTrue(("vncpasswd = '%s'" % self.vncpw) in lines)
732 extra = inst.hvparams[constants.HV_KERNEL_ARGS]
733 self.assertTrue(("extra = '%s'" % extra) in lines)
735 def _StopInstanceCommand(self, instance_name, force, fail, cmd):
736 if ((force and cmd[:2] == [self.CMD, "destroy"]) or
737 (not force and cmd[:2] == [self.CMD, "shutdown"])):
738 self.assertEqual(cmd[2:], [instance_name])
741 self.fail("Unhandled command: %s" % (cmd, ))
744 # Simulate a failing command
745 return self._FailingCommand(cmd)
747 return self._SuccessCommand(output, cmd)
749 def testStopInstance(self):
750 name = "inst4284.example.com"
751 cfgfile = utils.PathJoin(self.tmpdir, name)
752 cfgdata = "config file content\n"
754 for force in [False, True]:
755 for fail in [False, True]:
756 utils.WriteFile(cfgfile, data=cfgdata)
758 run_cmd = compat.partial(self._StopInstanceCommand, name, force, fail)
760 hv = self._GetHv(run_cmd=run_cmd)
762 self.assertTrue(os.path.isfile(cfgfile))
766 hv._StopInstance(name, force, None)
767 except errors.HypervisorError, err:
768 self.assertTrue(str(err).startswith("Failed to stop instance"))
770 self.fail("Exception was not raised")
771 self.assertEqual(utils.ReadFile(cfgfile), cfgdata,
772 msg=("Configuration was removed when stopping"
775 hv._StopInstance(name, force, None)
776 self.assertFalse(os.path.exists(cfgfile))
778 def _MigrateNonRunningInstCmd(self, cmd):
779 if cmd == [self.CMD, "list"]:
780 output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
782 self.fail("Unhandled command: %s" % (cmd, ))
784 return self._SuccessCommand(output, cmd)
786 def testMigrateInstanceNotRunning(self):
787 name = "nonexistinginstance.example.com"
788 target = constants.IP4_ADDRESS_LOCALHOST
791 hv = self._GetHv(run_cmd=self._MigrateNonRunningInstCmd)
793 for live in [False, True]:
795 hv._MigrateInstance(NotImplemented, name, target, port, live,
796 self.VALID_HVPARAMS, _ping_fn=NotImplemented)
797 except errors.HypervisorError, err:
798 self.assertEqual(str(err), "Instance not running, cannot migrate")
800 self.fail("Exception was not raised")
802 def _MigrateInstTargetUnreachCmd(self, cmd):
803 if cmd == [self.CMD, "list"]:
804 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
806 self.fail("Unhandled command: %s" % (cmd, ))
808 return self._SuccessCommand(output, cmd)
810 def testMigrateTargetUnreachable(self):
811 name = "server01.example.com"
812 target = constants.IP4_ADDRESS_LOCALHOST
815 hv = self._GetHv(run_cmd=self._MigrateInstTargetUnreachCmd)
816 hvparams = {constants.HV_XEN_CMD: self.CMD}
818 for live in [False, True]:
819 if self.CMD == constants.XEN_CMD_XL:
820 # TODO: Detect unreachable targets
824 hv._MigrateInstance(NotImplemented, name, target, port, live,
826 _ping_fn=compat.partial(self._FakeTcpPing,
827 (target, port), False))
828 except errors.HypervisorError, err:
829 wanted = "Remote host %s not" % target
830 self.assertTrue(str(err).startswith(wanted))
832 self.fail("Exception was not raised")
834 def _MigrateInstanceCmd(self, cluster_name, instance_name, target, port,
836 if cmd == [self.CMD, "list"]:
837 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
838 elif cmd[:2] == [self.CMD, "migrate"]:
839 if self.CMD == constants.XEN_CMD_XM:
840 args = ["-p", str(port)]
845 elif self.CMD == constants.XEN_CMD_XL:
847 "-s", constants.XL_SSH_CMD % cluster_name,
848 "-C", utils.PathJoin(self.tmpdir, instance_name),
852 self.fail("Unknown Xen command '%s'" % self.CMD)
854 args.extend([instance_name, target])
855 self.assertEqual(cmd[2:], args)
858 return self._FailingCommand(cmd)
862 self.fail("Unhandled command: %s" % (cmd, ))
864 return self._SuccessCommand(output, cmd)
866 def testMigrateInstance(self):
867 clustername = "cluster.example.com"
868 instname = "server01.example.com"
869 target = constants.IP4_ADDRESS_LOCALHOST
872 hvparams = {constants.HV_XEN_CMD: self.CMD}
874 for live in [False, True]:
875 for fail in [False, True]:
877 testutils.CallCounter(compat.partial(self._FakeTcpPing,
878 (target, port), True))
881 compat.partial(self._MigrateInstanceCmd,
882 clustername, instname, target, port, live,
885 hv = self._GetHv(run_cmd=run_cmd)
889 hv._MigrateInstance(clustername, instname, target, port, live,
890 hvparams, _ping_fn=ping_fn)
891 except errors.HypervisorError, err:
892 self.assertTrue(str(err).startswith("Failed to migrate instance"))
894 self.fail("Exception was not raised")
896 hv._MigrateInstance(clustername, instname, target, port, live,
897 hvparams, _ping_fn=ping_fn)
899 if self.CMD == constants.XEN_CMD_XM:
904 self.assertEqual(ping_fn.Count(), expected_pings)
906 def _GetNodeInfoCmd(self, fail, cmd):
907 if cmd == [self.CMD, "info"]:
909 return self._FailingCommand(cmd)
911 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
912 elif cmd == [self.CMD, "list"]:
914 self.fail("'xm list' shouldn't be called when 'xm info' failed")
916 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
918 self.fail("Unhandled command: %s" % (cmd, ))
920 return self._SuccessCommand(output, cmd)
922 def testGetNodeInfo(self):
923 run_cmd = compat.partial(self._GetNodeInfoCmd, False)
924 hv = self._GetHv(run_cmd=run_cmd)
925 result = hv.GetNodeInfo()
927 self.assertEqual(result["hv_version"], (4, 0))
928 self.assertEqual(result["memory_free"], 8004)
930 def testGetNodeInfoFailing(self):
931 run_cmd = compat.partial(self._GetNodeInfoCmd, True)
932 hv = self._GetHv(run_cmd=run_cmd)
933 self.assertTrue(hv.GetNodeInfo() is None)
936 def _MakeTestClass(cls, cmd):
937 """Makes a class for testing.
939 The returned class has structure as shown in the following pseudo code:
941 class Test{cls.__name__}{cmd}(_TestXenHypervisor, unittest.TestCase):
944 HVNAME = {Hypervisor name retrieved using class}
947 @param cls: Hypervisor class to be tested
949 @param cmd: Hypervisor command
951 @return: Class name and class object (not instance)
954 name = "Test%sCmd%s" % (cls.__name__, cmd.title())
955 bases = (_TestXenHypervisor, unittest.TestCase)
956 hvname = HVCLASS_TO_HVNAME[cls]
958 return (name, type(name, bases, dict(TARGET=cls, CMD=cmd, HVNAME=hvname)))
961 # Create test classes programmatically instead of manually to reduce the risk
962 # of forgetting some combinations
963 for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]:
964 for cmd in constants.KNOWN_XEN_COMMANDS:
965 (name, testcls) = _MakeTestClass(cls, cmd)
967 assert name not in locals()
969 locals()[name] = testcls
972 if __name__ == "__main__":
973 testutils.GanetiTestProgram()