Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.hypervisor.hv_xen_unittest.py @ 06c2fb4a

History | View | Annotate | Download (24.7 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_RDONLY,
278
                    physical_id=[constants.FD_BLKTAP2]),
279
       "/tmp/diskFour"),
280
      (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
281
                    physical_id=[constants.FD_BLKTAP]),
282
       "/tmp/diskLast"),
283
      ]
284

    
285
    result = hv_xen._GetConfigFileDiskData(disks, "sd")
286
    self.assertEqual(result, [
287
      "'file:/tmp/diskFirst,sda,w'",
288
      "'tap:aio:/tmp/diskTwo,sdb,r'",
289
      "'file:/tmp/diskThree,sdc,w'",
290
      "'tap2:tapdisk:aio:/tmp/diskFour,sdd,r'",
291
      "'tap:aio:/tmp/diskLast,sde,w'",
292
      ])
293

    
294
  def testInvalidFileDisk(self):
295
    disks = [
296
      (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
297
                    physical_id=["#unknown#"]),
298
       "/tmp/diskinvalid"),
299
      ]
300

    
301
    self.assertRaises(KeyError, hv_xen._GetConfigFileDiskData, disks, "sd")
302

    
303

    
304
class TestXenHypervisorUnknownCommand(unittest.TestCase):
305
  def test(self):
306
    cmd = "#unknown command#"
307
    self.assertFalse(cmd in constants.KNOWN_XEN_COMMANDS)
308
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
309
                              _run_cmd_fn=NotImplemented,
310
                              _cmd=cmd)
311
    self.assertRaises(errors.ProgrammerError, hv._RunXen, [])
312

    
313

    
314
class TestXenHypervisorWriteConfigFile(unittest.TestCase):
315
  def setUp(self):
316
    self.tmpdir = tempfile.mkdtemp()
317

    
318
  def tearDown(self):
319
    shutil.rmtree(self.tmpdir)
320

    
321
  def testWriteError(self):
322
    cfgdir = utils.PathJoin(self.tmpdir, "foobar")
323

    
324
    hv = hv_xen.XenHypervisor(_cfgdir=cfgdir,
325
                              _run_cmd_fn=NotImplemented,
326
                              _cmd=NotImplemented)
327

    
328
    self.assertFalse(os.path.exists(cfgdir))
329

    
330
    try:
331
      hv._WriteConfigFile("name", "data")
332
    except errors.HypervisorError, err:
333
      self.assertTrue(str(err).startswith("Cannot write Xen instance"))
334
    else:
335
      self.fail("Exception was not raised")
336

    
337

    
338
class _TestXenHypervisor(object):
339
  TARGET = NotImplemented
340
  CMD = NotImplemented
341
  HVNAME = NotImplemented
342

    
343
  def setUp(self):
344
    super(_TestXenHypervisor, self).setUp()
345

    
346
    self.tmpdir = tempfile.mkdtemp()
347

    
348
    self.vncpw = "".join(random.sample(string.ascii_letters, 10))
349

    
350
    self.vncpw_path = utils.PathJoin(self.tmpdir, "vncpw")
351
    utils.WriteFile(self.vncpw_path, data=self.vncpw)
352

    
353
  def tearDown(self):
354
    super(_TestXenHypervisor, self).tearDown()
355

    
356
    shutil.rmtree(self.tmpdir)
357

    
358
  def _GetHv(self, run_cmd=NotImplemented):
359
    return self.TARGET(_cfgdir=self.tmpdir, _run_cmd_fn=run_cmd, _cmd=self.CMD)
360

    
361
  def _SuccessCommand(self, stdout, cmd):
362
    self.assertEqual(cmd[0], self.CMD)
363

    
364
    return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
365
                           NotImplemented, NotImplemented)
366

    
367
  def _FailingCommand(self, cmd):
368
    self.assertEqual(cmd[0], self.CMD)
369

    
370
    return utils.RunResult(constants.EXIT_FAILURE, None,
371
                           "", "This command failed", None,
372
                           NotImplemented, NotImplemented)
373

    
374
  def _FakeTcpPing(self, expected, result, target, port, **kwargs):
375
    self.assertEqual((target, port), expected)
376
    return result
377

    
378
  def testReadingNonExistentConfigFile(self):
379
    hv = self._GetHv()
380

    
381
    try:
382
      hv._ReadConfigFile("inst15780.example.com")
383
    except errors.HypervisorError, err:
384
      self.assertTrue(str(err).startswith("Failed to load Xen config file:"))
385
    else:
386
      self.fail("Exception was not raised")
387

    
388
  def testRemovingAutoConfigFile(self):
389
    name = "inst8206.example.com"
390
    cfgfile = utils.PathJoin(self.tmpdir, name)
391
    autodir = utils.PathJoin(self.tmpdir, "auto")
392
    autocfgfile = utils.PathJoin(autodir, name)
393

    
394
    os.mkdir(autodir)
395

    
396
    utils.WriteFile(autocfgfile, data="")
397

    
398
    hv = self._GetHv()
399

    
400
    self.assertTrue(os.path.isfile(autocfgfile))
401
    hv._WriteConfigFile(name, "content")
402
    self.assertFalse(os.path.exists(autocfgfile))
403
    self.assertEqual(utils.ReadFile(cfgfile), "content")
404

    
405
  def _XenList(self, cmd):
406
    self.assertEqual(cmd, [self.CMD, "list"])
407

    
408
    # TODO: Use actual data from "xl" command
409
    output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
410

    
411
    return self._SuccessCommand(output, cmd)
412

    
413
  def testGetInstanceInfo(self):
414
    hv = self._GetHv(run_cmd=self._XenList)
415

    
416
    (name, instid, memory, vcpus, state, runtime) = \
417
      hv.GetInstanceInfo("server01.example.com")
418

    
419
    self.assertEqual(name, "server01.example.com")
420
    self.assertEqual(instid, 1)
421
    self.assertEqual(memory, 1024)
422
    self.assertEqual(vcpus, 1)
423
    self.assertEqual(state, "-b----")
424
    self.assertAlmostEqual(runtime, 167643.2)
425

    
426
  def testGetInstanceInfoDom0(self):
427
    hv = self._GetHv(run_cmd=self._XenList)
428

    
429
    # TODO: Not sure if this is actually used anywhere (can't find it), but the
430
    # code supports querying for Dom0
431
    (name, instid, memory, vcpus, state, runtime) = \
432
      hv.GetInstanceInfo(hv_xen._DOM0_NAME)
433

    
434
    self.assertEqual(name, "Domain-0")
435
    self.assertEqual(instid, 0)
436
    self.assertEqual(memory, 1023)
437
    self.assertEqual(vcpus, 1)
438
    self.assertEqual(state, "r-----")
439
    self.assertAlmostEqual(runtime, 154706.1)
440

    
441
  def testGetInstanceInfoUnknown(self):
442
    hv = self._GetHv(run_cmd=self._XenList)
443

    
444
    result = hv.GetInstanceInfo("unknown.example.com")
445
    self.assertTrue(result is None)
446

    
447
  def testGetAllInstancesInfo(self):
448
    hv = self._GetHv(run_cmd=self._XenList)
449

    
450
    result = hv.GetAllInstancesInfo()
451

    
452
    self.assertEqual(map(compat.fst, result), [
453
      "server01.example.com",
454
      "web3106215069.example.com",
455
      "testinstance.example.com",
456
      ])
457

    
458
  def testListInstances(self):
459
    hv = self._GetHv(run_cmd=self._XenList)
460

    
461
    self.assertEqual(hv.ListInstances(), [
462
      "server01.example.com",
463
      "web3106215069.example.com",
464
      "testinstance.example.com",
465
      ])
466

    
467
  def testVerify(self):
468
    output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
469
    hv = self._GetHv(run_cmd=compat.partial(self._SuccessCommand,
470
                                            output))
471
    self.assertTrue(hv.Verify() is None)
472

    
473
  def testVerifyFailing(self):
474
    hv = self._GetHv(run_cmd=self._FailingCommand)
475
    self.assertTrue("failed:" in hv.Verify())
476

    
477
  def _StartInstanceCommand(self, inst, paused, failcreate, cmd):
478
    if cmd == [self.CMD, "info"]:
479
      output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
480
    elif cmd == [self.CMD, "list"]:
481
      output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
482
    elif cmd[:2] == [self.CMD, "create"]:
483
      args = cmd[2:]
484
      cfgfile = utils.PathJoin(self.tmpdir, inst.name)
485

    
486
      if paused:
487
        self.assertEqual(args, ["-p", cfgfile])
488
      else:
489
        self.assertEqual(args, [cfgfile])
490

    
491
      if failcreate:
492
        return self._FailingCommand(cmd)
493

    
494
      output = ""
495
    else:
496
      self.fail("Unhandled command: %s" % (cmd, ))
497

    
498
    return self._SuccessCommand(output, cmd)
499

    
500
  def _MakeInstance(self):
501
    # Copy default parameters
502
    bep = objects.FillDict(constants.BEC_DEFAULTS, {})
503
    hvp = objects.FillDict(constants.HVC_DEFAULTS[self.HVNAME], {})
504

    
505
    # Override default VNC password file path
506
    if constants.HV_VNC_PASSWORD_FILE in hvp:
507
      hvp[constants.HV_VNC_PASSWORD_FILE] = self.vncpw_path
508

    
509
    disks = [
510
      (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDWR),
511
       utils.PathJoin(self.tmpdir, "disk0")),
512
      (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDONLY),
513
       utils.PathJoin(self.tmpdir, "disk1")),
514
      ]
515

    
516
    inst = objects.Instance(name="server01.example.com",
517
                            hvparams=hvp, beparams=bep,
518
                            osparams={}, nics=[], os="deb1",
519
                            disks=map(compat.fst, disks))
520
    inst.UpgradeConfig()
521

    
522
    return (inst, disks)
523

    
524
  def testStartInstance(self):
525
    (inst, disks) = self._MakeInstance()
526
    pathutils.LOG_XEN_DIR = self.tmpdir
527

    
528
    for failcreate in [False, True]:
529
      for paused in [False, True]:
530
        run_cmd = compat.partial(self._StartInstanceCommand,
531
                                 inst, paused, failcreate)
532

    
533
        hv = self._GetHv(run_cmd=run_cmd)
534

    
535
        # Ensure instance is not listed
536
        self.assertTrue(inst.name not in hv.ListInstances())
537

    
538
        # Remove configuration
539
        cfgfile = utils.PathJoin(self.tmpdir, inst.name)
540
        utils.RemoveFile(cfgfile)
541

    
542
        if failcreate:
543
          self.assertRaises(errors.HypervisorError, hv.StartInstance,
544
                            inst, disks, paused)
545
          # Check whether a stale config file is left behind
546
          self.assertFalse(os.path.exists(cfgfile))
547
        else:
548
          hv.StartInstance(inst, disks, paused)
549
          # Check if configuration was updated
550
          lines = utils.ReadFile(cfgfile).splitlines()
551

    
552
        if constants.HV_VNC_PASSWORD_FILE in inst.hvparams:
553
          self.assertTrue(("vncpasswd = '%s'" % self.vncpw) in lines)
554
        else:
555
          extra = inst.hvparams[constants.HV_KERNEL_ARGS]
556
          self.assertTrue(("extra = '%s'" % extra) in lines)
557

    
558
  def _StopInstanceCommand(self, instance_name, force, fail, cmd):
559
    if (cmd == [self.CMD, "list"]):
560
      output = "Name  ID  Mem  VCPUs  State  Time(s)\n" \
561
        "Domain-0  0  1023  1  r-----  142691.0\n" \
562
        "%s  417  128  1  r-----  3.2\n" % instance_name
563
    elif cmd[:2] == [self.CMD, "destroy"]:
564
      self.assertEqual(cmd[2:], [instance_name])
565
      output = ""
566
    elif not force and cmd[:3] == [self.CMD, "shutdown", "-w"]:
567
      self.assertEqual(cmd[3:], [instance_name])
568
      output = ""
569
    else:
570
      self.fail("Unhandled command: %s" % (cmd, ))
571

    
572
    if fail:
573
      # Simulate a failing command
574
      return self._FailingCommand(cmd)
575
    else:
576
      return self._SuccessCommand(output, cmd)
577

    
578
  def testStopInstance(self):
579
    name = "inst4284.example.com"
580
    cfgfile = utils.PathJoin(self.tmpdir, name)
581
    cfgdata = "config file content\n"
582

    
583
    for force in [False, True]:
584
      for fail in [False, True]:
585
        utils.WriteFile(cfgfile, data=cfgdata)
586

    
587
        run_cmd = compat.partial(self._StopInstanceCommand, name, force, fail)
588

    
589
        hv = self._GetHv(run_cmd=run_cmd)
590

    
591
        self.assertTrue(os.path.isfile(cfgfile))
592

    
593
        if fail:
594
          try:
595
            hv._StopInstance(name, force)
596
          except errors.HypervisorError, err:
597
            self.assertTrue(str(err).startswith("xm list failed"),
598
                            msg=str(err))
599
          else:
600
            self.fail("Exception was not raised")
601
          self.assertEqual(utils.ReadFile(cfgfile), cfgdata,
602
                           msg=("Configuration was removed when stopping"
603
                                " instance failed"))
604
        else:
605
          hv._StopInstance(name, force)
606
          self.assertFalse(os.path.exists(cfgfile))
607

    
608
  def _MigrateNonRunningInstCmd(self, cmd):
609
    if cmd == [self.CMD, "list"]:
610
      output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
611
    else:
612
      self.fail("Unhandled command: %s" % (cmd, ))
613

    
614
    return self._SuccessCommand(output, cmd)
615

    
616
  def testMigrateInstanceNotRunning(self):
617
    name = "nonexistinginstance.example.com"
618
    target = constants.IP4_ADDRESS_LOCALHOST
619
    port = 14618
620

    
621
    hv = self._GetHv(run_cmd=self._MigrateNonRunningInstCmd)
622

    
623
    for live in [False, True]:
624
      try:
625
        hv._MigrateInstance(NotImplemented, name, target, port, live,
626
                            _ping_fn=NotImplemented)
627
      except errors.HypervisorError, err:
628
        self.assertEqual(str(err), "Instance not running, cannot migrate")
629
      else:
630
        self.fail("Exception was not raised")
631

    
632
  def _MigrateInstTargetUnreachCmd(self, cmd):
633
    if cmd == [self.CMD, "list"]:
634
      output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
635
    else:
636
      self.fail("Unhandled command: %s" % (cmd, ))
637

    
638
    return self._SuccessCommand(output, cmd)
639

    
640
  def testMigrateTargetUnreachable(self):
641
    name = "server01.example.com"
642
    target = constants.IP4_ADDRESS_LOCALHOST
643
    port = 28349
644

    
645
    hv = self._GetHv(run_cmd=self._MigrateInstTargetUnreachCmd)
646

    
647
    for live in [False, True]:
648
      if self.CMD == constants.XEN_CMD_XL:
649
        # TODO: Detect unreachable targets
650
        pass
651
      else:
652
        try:
653
          hv._MigrateInstance(NotImplemented, name, target, port, live,
654
                              _ping_fn=compat.partial(self._FakeTcpPing,
655
                                                      (target, port), False))
656
        except errors.HypervisorError, err:
657
          wanted = "Remote host %s not" % target
658
          self.assertTrue(str(err).startswith(wanted))
659
        else:
660
          self.fail("Exception was not raised")
661

    
662
  def _MigrateInstanceCmd(self, cluster_name, instance_name, target, port,
663
                          live, fail, cmd):
664
    if cmd == [self.CMD, "list"]:
665
      output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
666
    elif cmd[:2] == [self.CMD, "migrate"]:
667
      if self.CMD == constants.XEN_CMD_XM:
668
        args = ["-p", str(port)]
669

    
670
        if live:
671
          args.append("-l")
672

    
673
      elif self.CMD == constants.XEN_CMD_XL:
674
        args = [
675
          "-s", constants.XL_SSH_CMD % cluster_name,
676
          "-C", utils.PathJoin(self.tmpdir, instance_name),
677
          ]
678

    
679
      else:
680
        self.fail("Unknown Xen command '%s'" % self.CMD)
681

    
682
      args.extend([instance_name, target])
683
      self.assertEqual(cmd[2:], args)
684

    
685
      if fail:
686
        return self._FailingCommand(cmd)
687

    
688
      output = ""
689
    else:
690
      self.fail("Unhandled command: %s" % (cmd, ))
691

    
692
    return self._SuccessCommand(output, cmd)
693

    
694
  def testMigrateInstance(self):
695
    clustername = "cluster.example.com"
696
    instname = "server01.example.com"
697
    target = constants.IP4_ADDRESS_LOCALHOST
698
    port = 22364
699

    
700
    for live in [False, True]:
701
      for fail in [False, True]:
702
        ping_fn = \
703
          testutils.CallCounter(compat.partial(self._FakeTcpPing,
704
                                               (target, port), True))
705

    
706
        run_cmd = \
707
          compat.partial(self._MigrateInstanceCmd,
708
                         clustername, instname, target, port, live,
709
                         fail)
710

    
711
        hv = self._GetHv(run_cmd=run_cmd)
712

    
713
        if fail:
714
          try:
715
            hv._MigrateInstance(clustername, instname, target, port, live,
716
                                _ping_fn=ping_fn)
717
          except errors.HypervisorError, err:
718
            self.assertTrue(str(err).startswith("Failed to migrate instance"))
719
          else:
720
            self.fail("Exception was not raised")
721
        else:
722
          hv._MigrateInstance(clustername, instname, target, port, live,
723
                              _ping_fn=ping_fn)
724

    
725
        if self.CMD == constants.XEN_CMD_XM:
726
          expected_pings = 1
727
        else:
728
          expected_pings = 0
729

    
730
        self.assertEqual(ping_fn.Count(), expected_pings)
731

    
732
  def _GetNodeInfoCmd(self, fail, cmd):
733
    if cmd == [self.CMD, "info"]:
734
      if fail:
735
        return self._FailingCommand(cmd)
736
      else:
737
        output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
738
    elif cmd == [self.CMD, "list"]:
739
      if fail:
740
        self.fail("'xm list' shouldn't be called when 'xm info' failed")
741
      else:
742
        output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
743
    else:
744
      self.fail("Unhandled command: %s" % (cmd, ))
745

    
746
    return self._SuccessCommand(output, cmd)
747

    
748
  def testGetNodeInfo(self):
749
    run_cmd = compat.partial(self._GetNodeInfoCmd, False)
750
    hv = self._GetHv(run_cmd=run_cmd)
751
    result = hv.GetNodeInfo()
752

    
753
    self.assertEqual(result["hv_version"], (4, 0))
754
    self.assertEqual(result["memory_free"], 8004)
755

    
756
  def testGetNodeInfoFailing(self):
757
    run_cmd = compat.partial(self._GetNodeInfoCmd, True)
758
    hv = self._GetHv(run_cmd=run_cmd)
759
    self.assertTrue(hv.GetNodeInfo() is None)
760

    
761

    
762
def _MakeTestClass(cls, cmd):
763
  """Makes a class for testing.
764

765
  The returned class has structure as shown in the following pseudo code:
766

767
    class Test{cls.__name__}{cmd}(_TestXenHypervisor, unittest.TestCase):
768
      TARGET = {cls}
769
      CMD = {cmd}
770
      HVNAME = {Hypervisor name retrieved using class}
771

772
  @type cls: class
773
  @param cls: Hypervisor class to be tested
774
  @type cmd: string
775
  @param cmd: Hypervisor command
776
  @rtype: tuple
777
  @return: Class name and class object (not instance)
778

779
  """
780
  name = "Test%sCmd%s" % (cls.__name__, cmd.title())
781
  bases = (_TestXenHypervisor, unittest.TestCase)
782
  hvname = HVCLASS_TO_HVNAME[cls]
783

    
784
  return (name, type(name, bases, dict(TARGET=cls, CMD=cmd, HVNAME=hvname)))
785

    
786

    
787
# Create test classes programmatically instead of manually to reduce the risk
788
# of forgetting some combinations
789
for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]:
790
  for cmd in constants.KNOWN_XEN_COMMANDS:
791
    (name, testcls) = _MakeTestClass(cls, cmd)
792

    
793
    assert name not in locals()
794

    
795
    locals()[name] = testcls
796

    
797

    
798
if __name__ == "__main__":
799
  testutils.GanetiTestProgram()