Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.hypervisor.hv_xen_unittest.py @ 3fc1dc2f

History | View | Annotate | Download (24.5 kB)

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()