Test "GetNodeInfo" for Xen hypervisor
[ganeti-github.git] / test / py / ganeti.hypervisor.hv_xen_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2011, 2013 Google Inc.
5 #
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.
10 #
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.
15 #
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
19 # 02110-1301, USA.
20
21
22 """Script for testing ganeti.hypervisor.hv_lxc"""
23
24 import string # pylint: disable=W0402
25 import unittest
26 import tempfile
27 import shutil
28 import random
29 import os
30
31 from ganeti import constants
32 from ganeti import objects
33 from ganeti import hypervisor
34 from ganeti import utils
35 from ganeti import errors
36 from ganeti import compat
37
38 from ganeti.hypervisor import hv_xen
39
40 import testutils
41
42
43 # Map from hypervisor class to hypervisor name
44 HVCLASS_TO_HVNAME = utils.InvertDict(hypervisor._HYPERVISOR_MAP)
45
46
47 class TestConsole(unittest.TestCase):
48 def test(self):
49 for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]:
50 instance = objects.Instance(name="xen.example.com",
51 primary_node="node24828")
52 cons = cls.GetInstanceConsole(instance, {}, {})
53 self.assertTrue(cons.Validate())
54 self.assertEqual(cons.kind, constants.CONS_SSH)
55 self.assertEqual(cons.host, instance.primary_node)
56 self.assertEqual(cons.command[-1], instance.name)
57
58
59 class TestCreateConfigCpus(unittest.TestCase):
60 def testEmpty(self):
61 for cpu_mask in [None, ""]:
62 self.assertEqual(hv_xen._CreateConfigCpus(cpu_mask),
63 "cpus = [ ]")
64
65 def testAll(self):
66 self.assertEqual(hv_xen._CreateConfigCpus(constants.CPU_PINNING_ALL),
67 None)
68
69 def testOne(self):
70 self.assertEqual(hv_xen._CreateConfigCpus("9"), "cpu = \"9\"")
71
72 def testMultiple(self):
73 self.assertEqual(hv_xen._CreateConfigCpus("0-2,4,5-5:3:all"),
74 ("cpus = [ \"0,1,2,4,5\", \"3\", \"%s\" ]" %
75 constants.CPU_PINNING_ALL_XEN))
76
77
78 class TestParseXmList(testutils.GanetiTestCase):
79 def test(self):
80 data = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
81
82 # Exclude node
83 self.assertEqual(hv_xen._ParseXmList(data.splitlines(), False), [])
84
85 # Include node
86 result = hv_xen._ParseXmList(data.splitlines(), True)
87 self.assertEqual(len(result), 1)
88 self.assertEqual(len(result[0]), 6)
89
90 # Name
91 self.assertEqual(result[0][0], hv_xen._DOM0_NAME)
92
93 # ID
94 self.assertEqual(result[0][1], 0)
95
96 # Memory
97 self.assertEqual(result[0][2], 1023)
98
99 # VCPUs
100 self.assertEqual(result[0][3], 1)
101
102 # State
103 self.assertEqual(result[0][4], "r-----")
104
105 # Time
106 self.assertAlmostEqual(result[0][5], 121152.6)
107
108 def testWrongLineFormat(self):
109 tests = [
110 ["three fields only"],
111 ["name InvalidID 128 1 r----- 12345"],
112 ]
113
114 for lines in tests:
115 try:
116 hv_xen._ParseXmList(["Header would be here"] + lines, False)
117 except errors.HypervisorError, err:
118 self.assertTrue("Can't parse output of xm list" in str(err))
119 else:
120 self.fail("Exception was not raised")
121
122
123 class TestGetXmList(testutils.GanetiTestCase):
124 def _Fail(self):
125 return utils.RunResult(constants.EXIT_FAILURE, None,
126 "stdout", "stderr", None,
127 NotImplemented, NotImplemented)
128
129 def testTimeout(self):
130 fn = testutils.CallCounter(self._Fail)
131 try:
132 hv_xen._GetXmList(fn, False, _timeout=0.1)
133 except errors.HypervisorError, err:
134 self.assertTrue("timeout exceeded" in str(err))
135 else:
136 self.fail("Exception was not raised")
137
138 self.assertTrue(fn.Count() < 10,
139 msg="'xm list' was called too many times")
140
141 def _Success(self, stdout):
142 return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
143 NotImplemented, NotImplemented)
144
145 def testSuccess(self):
146 data = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
147
148 fn = testutils.CallCounter(compat.partial(self._Success, data))
149
150 result = hv_xen._GetXmList(fn, True, _timeout=0.1)
151
152 self.assertEqual(len(result), 4)
153
154 self.assertEqual(map(compat.fst, result), [
155 "Domain-0",
156 "server01.example.com",
157 "web3106215069.example.com",
158 "testinstance.example.com",
159 ])
160
161 self.assertEqual(fn.Count(), 1)
162
163
164 class TestParseNodeInfo(testutils.GanetiTestCase):
165 def testEmpty(self):
166 self.assertEqual(hv_xen._ParseNodeInfo(""), {})
167
168 def testUnknownInput(self):
169 data = "\n".join([
170 "foo bar",
171 "something else goes",
172 "here",
173 ])
174 self.assertEqual(hv_xen._ParseNodeInfo(data), {})
175
176 def testBasicInfo(self):
177 data = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
178 result = hv_xen._ParseNodeInfo(data)
179 self.assertEqual(result, {
180 "cpu_nodes": 1,
181 "cpu_sockets": 2,
182 "cpu_total": 4,
183 "hv_version": (4, 0),
184 "memory_free": 8004,
185 "memory_total": 16378,
186 })
187
188
189 class TestMergeInstanceInfo(testutils.GanetiTestCase):
190 def testEmpty(self):
191 self.assertEqual(hv_xen._MergeInstanceInfo({}, lambda _: []), {})
192
193 def _FakeXmList(self, include_node):
194 self.assertTrue(include_node)
195 return [
196 (hv_xen._DOM0_NAME, NotImplemented, 4096, 7, NotImplemented,
197 NotImplemented),
198 ("inst1.example.com", NotImplemented, 2048, 4, NotImplemented,
199 NotImplemented),
200 ]
201
202 def testMissingNodeInfo(self):
203 result = hv_xen._MergeInstanceInfo({}, self._FakeXmList)
204 self.assertEqual(result, {
205 "memory_dom0": 4096,
206 "dom0_cpus": 7,
207 })
208
209 def testWithNodeInfo(self):
210 info = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
211 result = hv_xen._GetNodeInfo(info, self._FakeXmList)
212 self.assertEqual(result, {
213 "cpu_nodes": 1,
214 "cpu_sockets": 2,
215 "cpu_total": 4,
216 "dom0_cpus": 7,
217 "hv_version": (4, 0),
218 "memory_dom0": 4096,
219 "memory_free": 8004,
220 "memory_hv": 2230,
221 "memory_total": 16378,
222 })
223
224
225 class TestGetConfigFileDiskData(unittest.TestCase):
226 def testLetterCount(self):
227 self.assertEqual(len(hv_xen._DISK_LETTERS), 26)
228
229 def testNoDisks(self):
230 self.assertEqual(hv_xen._GetConfigFileDiskData([], "hd"), [])
231
232 def testManyDisks(self):
233 for offset in [0, 1, 10]:
234 disks = [(objects.Disk(dev_type=constants.LD_LV), "/tmp/disk/%s" % idx)
235 for idx in range(len(hv_xen._DISK_LETTERS) + offset)]
236
237 if offset == 0:
238 result = hv_xen._GetConfigFileDiskData(disks, "hd")
239 self.assertEqual(result, [
240 "'phy:/tmp/disk/%s,hd%s,r'" % (idx, string.ascii_lowercase[idx])
241 for idx in range(len(hv_xen._DISK_LETTERS) + offset)
242 ])
243 else:
244 try:
245 hv_xen._GetConfigFileDiskData(disks, "hd")
246 except errors.HypervisorError, err:
247 self.assertEqual(str(err), "Too many disks")
248 else:
249 self.fail("Exception was not raised")
250
251 def testTwoLvDisksWithMode(self):
252 disks = [
253 (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDWR),
254 "/tmp/diskFirst"),
255 (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDONLY),
256 "/tmp/diskLast"),
257 ]
258
259 result = hv_xen._GetConfigFileDiskData(disks, "hd")
260 self.assertEqual(result, [
261 "'phy:/tmp/diskFirst,hda,w'",
262 "'phy:/tmp/diskLast,hdb,r'",
263 ])
264
265 def testFileDisks(self):
266 disks = [
267 (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
268 physical_id=[constants.FD_LOOP]),
269 "/tmp/diskFirst"),
270 (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDONLY,
271 physical_id=[constants.FD_BLKTAP]),
272 "/tmp/diskTwo"),
273 (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
274 physical_id=[constants.FD_LOOP]),
275 "/tmp/diskThree"),
276 (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
277 physical_id=[constants.FD_BLKTAP]),
278 "/tmp/diskLast"),
279 ]
280
281 result = hv_xen._GetConfigFileDiskData(disks, "sd")
282 self.assertEqual(result, [
283 "'file:/tmp/diskFirst,sda,w'",
284 "'tap:aio:/tmp/diskTwo,sdb,r'",
285 "'file:/tmp/diskThree,sdc,w'",
286 "'tap:aio:/tmp/diskLast,sdd,w'",
287 ])
288
289 def testInvalidFileDisk(self):
290 disks = [
291 (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
292 physical_id=["#unknown#"]),
293 "/tmp/diskinvalid"),
294 ]
295
296 self.assertRaises(KeyError, hv_xen._GetConfigFileDiskData, disks, "sd")
297
298
299 class TestXenHypervisorUnknownCommand(unittest.TestCase):
300 def test(self):
301 cmd = "#unknown command#"
302 self.assertFalse(cmd in constants.KNOWN_XEN_COMMANDS)
303 hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
304 _run_cmd_fn=NotImplemented,
305 _cmd=cmd)
306 self.assertRaises(errors.ProgrammerError, hv._RunXen, [])
307
308
309 class TestXenHypervisorWriteConfigFile(unittest.TestCase):
310 def setUp(self):
311 self.tmpdir = tempfile.mkdtemp()
312
313 def tearDown(self):
314 shutil.rmtree(self.tmpdir)
315
316 def testWriteError(self):
317 cfgdir = utils.PathJoin(self.tmpdir, "foobar")
318
319 hv = hv_xen.XenHypervisor(_cfgdir=cfgdir,
320 _run_cmd_fn=NotImplemented,
321 _cmd=NotImplemented)
322
323 self.assertFalse(os.path.exists(cfgdir))
324
325 try:
326 hv._WriteConfigFile("name", "data")
327 except errors.HypervisorError, err:
328 self.assertTrue(str(err).startswith("Cannot write Xen instance"))
329 else:
330 self.fail("Exception was not raised")
331
332
333 class _TestXenHypervisor(object):
334 TARGET = NotImplemented
335 CMD = NotImplemented
336 HVNAME = NotImplemented
337
338 def setUp(self):
339 super(_TestXenHypervisor, self).setUp()
340
341 self.tmpdir = tempfile.mkdtemp()
342
343 self.vncpw = "".join(random.sample(string.ascii_letters, 10))
344
345 self.vncpw_path = utils.PathJoin(self.tmpdir, "vncpw")
346 utils.WriteFile(self.vncpw_path, data=self.vncpw)
347
348 def tearDown(self):
349 super(_TestXenHypervisor, self).tearDown()
350
351 shutil.rmtree(self.tmpdir)
352
353 def _GetHv(self, run_cmd=NotImplemented):
354 return self.TARGET(_cfgdir=self.tmpdir, _run_cmd_fn=run_cmd, _cmd=self.CMD)
355
356 def _SuccessCommand(self, stdout, cmd):
357 self.assertEqual(cmd[0], self.CMD)
358
359 return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
360 NotImplemented, NotImplemented)
361
362 def _FailingCommand(self, cmd):
363 self.assertEqual(cmd[0], self.CMD)
364
365 return utils.RunResult(constants.EXIT_FAILURE, None,
366 "", "This command failed", None,
367 NotImplemented, NotImplemented)
368
369 def _FakeTcpPing(self, expected, result, target, port, **kwargs):
370 self.assertEqual((target, port), expected)
371 return result
372
373 def testReadingNonExistentConfigFile(self):
374 hv = self._GetHv()
375
376 try:
377 hv._ReadConfigFile("inst15780.example.com")
378 except errors.HypervisorError, err:
379 self.assertTrue(str(err).startswith("Failed to load Xen config file:"))
380 else:
381 self.fail("Exception was not raised")
382
383 def testRemovingAutoConfigFile(self):
384 name = "inst8206.example.com"
385 cfgfile = utils.PathJoin(self.tmpdir, name)
386 autodir = utils.PathJoin(self.tmpdir, "auto")
387 autocfgfile = utils.PathJoin(autodir, name)
388
389 os.mkdir(autodir)
390
391 utils.WriteFile(autocfgfile, data="")
392
393 hv = self._GetHv()
394
395 self.assertTrue(os.path.isfile(autocfgfile))
396 hv._WriteConfigFile(name, "content")
397 self.assertFalse(os.path.exists(autocfgfile))
398 self.assertEqual(utils.ReadFile(cfgfile), "content")
399
400 def _XenList(self, cmd):
401 self.assertEqual(cmd, [self.CMD, "list"])
402
403 # TODO: Use actual data from "xl" command
404 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
405
406 return self._SuccessCommand(output, cmd)
407
408 def testGetInstanceInfo(self):
409 hv = self._GetHv(run_cmd=self._XenList)
410
411 (name, instid, memory, vcpus, state, runtime) = \
412 hv.GetInstanceInfo("server01.example.com")
413
414 self.assertEqual(name, "server01.example.com")
415 self.assertEqual(instid, 1)
416 self.assertEqual(memory, 1024)
417 self.assertEqual(vcpus, 1)
418 self.assertEqual(state, "-b----")
419 self.assertAlmostEqual(runtime, 167643.2)
420
421 def testGetInstanceInfoDom0(self):
422 hv = self._GetHv(run_cmd=self._XenList)
423
424 # TODO: Not sure if this is actually used anywhere (can't find it), but the
425 # code supports querying for Dom0
426 (name, instid, memory, vcpus, state, runtime) = \
427 hv.GetInstanceInfo(hv_xen._DOM0_NAME)
428
429 self.assertEqual(name, "Domain-0")
430 self.assertEqual(instid, 0)
431 self.assertEqual(memory, 1023)
432 self.assertEqual(vcpus, 1)
433 self.assertEqual(state, "r-----")
434 self.assertAlmostEqual(runtime, 154706.1)
435
436 def testGetInstanceInfoUnknown(self):
437 hv = self._GetHv(run_cmd=self._XenList)
438
439 result = hv.GetInstanceInfo("unknown.example.com")
440 self.assertTrue(result is None)
441
442 def testGetAllInstancesInfo(self):
443 hv = self._GetHv(run_cmd=self._XenList)
444
445 result = hv.GetAllInstancesInfo()
446
447 self.assertEqual(map(compat.fst, result), [
448 "server01.example.com",
449 "web3106215069.example.com",
450 "testinstance.example.com",
451 ])
452
453 def testListInstances(self):
454 hv = self._GetHv(run_cmd=self._XenList)
455
456 self.assertEqual(hv.ListInstances(), [
457 "server01.example.com",
458 "web3106215069.example.com",
459 "testinstance.example.com",
460 ])
461
462 def testVerify(self):
463 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
464 hv = self._GetHv(run_cmd=compat.partial(self._SuccessCommand,
465 output))
466 self.assertTrue(hv.Verify() is None)
467
468 def testVerifyFailing(self):
469 hv = self._GetHv(run_cmd=self._FailingCommand)
470 self.assertTrue("failed:" in hv.Verify())
471
472 def _StartInstanceCommand(self, inst, paused, failcreate, cmd):
473 if cmd == [self.CMD, "info"]:
474 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
475 elif cmd == [self.CMD, "list"]:
476 output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
477 elif cmd[:2] == [self.CMD, "create"]:
478 args = cmd[2:]
479 cfgfile = utils.PathJoin(self.tmpdir, inst.name)
480
481 if paused:
482 self.assertEqual(args, ["-p", cfgfile])
483 else:
484 self.assertEqual(args, [cfgfile])
485
486 if failcreate:
487 return self._FailingCommand(cmd)
488
489 output = ""
490 else:
491 self.fail("Unhandled command: %s" % (cmd, ))
492
493 return self._SuccessCommand(output, cmd)
494 #return self._FailingCommand(cmd)
495
496 def _MakeInstance(self):
497 # Copy default parameters
498 bep = objects.FillDict(constants.BEC_DEFAULTS, {})
499 hvp = objects.FillDict(constants.HVC_DEFAULTS[self.HVNAME], {})
500
501 # Override default VNC password file path
502 if constants.HV_VNC_PASSWORD_FILE in hvp:
503 hvp[constants.HV_VNC_PASSWORD_FILE] = self.vncpw_path
504
505 disks = [
506 (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDWR),
507 utils.PathJoin(self.tmpdir, "disk0")),
508 (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDONLY),
509 utils.PathJoin(self.tmpdir, "disk1")),
510 ]
511
512 inst = objects.Instance(name="server01.example.com",
513 hvparams=hvp, beparams=bep,
514 osparams={}, nics=[], os="deb1",
515 disks=map(compat.fst, disks))
516 inst.UpgradeConfig()
517
518 return (inst, disks)
519
520 def testStartInstance(self):
521 (inst, disks) = self._MakeInstance()
522
523 for failcreate in [False, True]:
524 for paused in [False, True]:
525 run_cmd = compat.partial(self._StartInstanceCommand,
526 inst, paused, failcreate)
527
528 hv = self._GetHv(run_cmd=run_cmd)
529
530 # Ensure instance is not listed
531 self.assertTrue(inst.name not in hv.ListInstances())
532
533 # Remove configuration
534 cfgfile = utils.PathJoin(self.tmpdir, inst.name)
535 utils.RemoveFile(cfgfile)
536
537 if failcreate:
538 self.assertRaises(errors.HypervisorError, hv.StartInstance,
539 inst, disks, paused)
540 else:
541 hv.StartInstance(inst, disks, paused)
542
543 # Check if configuration was updated
544 lines = utils.ReadFile(cfgfile).splitlines()
545
546 if constants.HV_VNC_PASSWORD_FILE in inst.hvparams:
547 self.assertTrue(("vncpasswd = '%s'" % self.vncpw) in lines)
548 else:
549 extra = inst.hvparams[constants.HV_KERNEL_ARGS]
550 self.assertTrue(("extra = '%s'" % extra) in lines)
551
552 def _StopInstanceCommand(self, instance_name, force, fail, cmd):
553 if ((force and cmd[:2] == [self.CMD, "destroy"]) or
554 (not force and cmd[:2] == [self.CMD, "shutdown"])):
555 self.assertEqual(cmd[2:], [instance_name])
556 output = ""
557 else:
558 self.fail("Unhandled command: %s" % (cmd, ))
559
560 if fail:
561 # Simulate a failing command
562 return self._FailingCommand(cmd)
563 else:
564 return self._SuccessCommand(output, cmd)
565
566 def testStopInstance(self):
567 name = "inst4284.example.com"
568 cfgfile = utils.PathJoin(self.tmpdir, name)
569 cfgdata = "config file content\n"
570
571 for force in [False, True]:
572 for fail in [False, True]:
573 utils.WriteFile(cfgfile, data=cfgdata)
574
575 run_cmd = compat.partial(self._StopInstanceCommand, name, force, fail)
576
577 hv = self._GetHv(run_cmd=run_cmd)
578
579 self.assertTrue(os.path.isfile(cfgfile))
580
581 if fail:
582 try:
583 hv._StopInstance(name, force)
584 except errors.HypervisorError, err:
585 self.assertTrue(str(err).startswith("Failed to stop instance"))
586 else:
587 self.fail("Exception was not raised")
588 self.assertEqual(utils.ReadFile(cfgfile), cfgdata,
589 msg=("Configuration was removed when stopping"
590 " instance failed"))
591 else:
592 hv._StopInstance(name, force)
593 self.assertFalse(os.path.exists(cfgfile))
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
719 def _GetNodeInfoCmd(self, fail, cmd):
720 if cmd == [self.CMD, "info"]:
721 if fail:
722 return self._FailingCommand(cmd)
723 else:
724 output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
725 elif cmd == [self.CMD, "list"]:
726 if fail:
727 self.fail("'xm list' shouldn't be called when 'xm info' failed")
728 else:
729 output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
730 else:
731 self.fail("Unhandled command: %s" % (cmd, ))
732
733 return self._SuccessCommand(output, cmd)
734
735 def testGetNodeInfo(self):
736 run_cmd = compat.partial(self._GetNodeInfoCmd, False)
737 hv = self._GetHv(run_cmd=run_cmd)
738 result = hv.GetNodeInfo()
739
740 self.assertEqual(result["hv_version"], (4, 0))
741 self.assertEqual(result["memory_free"], 8004)
742
743 def testGetNodeInfoFailing(self):
744 run_cmd = compat.partial(self._GetNodeInfoCmd, True)
745 hv = self._GetHv(run_cmd=run_cmd)
746 self.assertTrue(hv.GetNodeInfo() is None)
747
748
749 def _MakeTestClass(cls, cmd):
750 """Makes a class for testing.
751
752 The returned class has structure as shown in the following pseudo code:
753
754 class Test{cls.__name__}{cmd}(_TestXenHypervisor, unittest.TestCase):
755 TARGET = {cls}
756 CMD = {cmd}
757 HVNAME = {Hypervisor name retrieved using class}
758
759 @type cls: class
760 @param cls: Hypervisor class to be tested
761 @type cmd: string
762 @param cmd: Hypervisor command
763 @rtype: tuple
764 @return: Class name and class object (not instance)
765
766 """
767 name = "Test%sCmd%s" % (cls.__name__, cmd.title())
768 bases = (_TestXenHypervisor, unittest.TestCase)
769 hvname = HVCLASS_TO_HVNAME[cls]
770
771 return (name, type(name, bases, dict(TARGET=cls, CMD=cmd, HVNAME=hvname)))
772
773
774 # Create test classes programmatically instead of manually to reduce the risk
775 # of forgetting some combinations
776 for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]:
777 for cmd in constants.KNOWN_XEN_COMMANDS:
778 (name, testcls) = _MakeTestClass(cls, cmd)
779
780 assert name not in locals()
781
782 locals()[name] = testcls
783
784
785 if __name__ == "__main__":
786 testutils.GanetiTestProgram()