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