Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.hypervisor.hv_xen_unittest.py @ 2edc1c79

History | View | Annotate | Download (19 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 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 testReadingNonExistentConfigFile(self):
370
    hv = self._GetHv()
371

    
372
    try:
373
      hv._ReadConfigFile("inst15780.example.com")
374
    except errors.HypervisorError, err:
375
      self.assertTrue(str(err).startswith("Failed to load Xen config file:"))
376
    else:
377
      self.fail("Exception was not raised")
378

    
379
  def testRemovingAutoConfigFile(self):
380
    name = "inst8206.example.com"
381
    cfgfile = utils.PathJoin(self.tmpdir, name)
382
    autodir = utils.PathJoin(self.tmpdir, "auto")
383
    autocfgfile = utils.PathJoin(autodir, name)
384

    
385
    os.mkdir(autodir)
386

    
387
    utils.WriteFile(autocfgfile, data="")
388

    
389
    hv = self._GetHv()
390

    
391
    self.assertTrue(os.path.isfile(autocfgfile))
392
    hv._WriteConfigFile(name, "content")
393
    self.assertFalse(os.path.exists(autocfgfile))
394
    self.assertEqual(utils.ReadFile(cfgfile), "content")
395

    
396
  def _XenList(self, cmd):
397
    self.assertEqual(cmd, [self.CMD, "list"])
398

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

    
402
    return self._SuccessCommand(output, cmd)
403

    
404
  def testGetInstanceInfo(self):
405
    hv = self._GetHv(run_cmd=self._XenList)
406

    
407
    (name, instid, memory, vcpus, state, runtime) = \
408
      hv.GetInstanceInfo("server01.example.com")
409

    
410
    self.assertEqual(name, "server01.example.com")
411
    self.assertEqual(instid, 1)
412
    self.assertEqual(memory, 1024)
413
    self.assertEqual(vcpus, 1)
414
    self.assertEqual(state, "-b----")
415
    self.assertAlmostEqual(runtime, 167643.2)
416

    
417
  def testGetInstanceInfoDom0(self):
418
    hv = self._GetHv(run_cmd=self._XenList)
419

    
420
    # TODO: Not sure if this is actually used anywhere (can't find it), but the
421
    # code supports querying for Dom0
422
    (name, instid, memory, vcpus, state, runtime) = \
423
      hv.GetInstanceInfo(hv_xen._DOM0_NAME)
424

    
425
    self.assertEqual(name, "Domain-0")
426
    self.assertEqual(instid, 0)
427
    self.assertEqual(memory, 1023)
428
    self.assertEqual(vcpus, 1)
429
    self.assertEqual(state, "r-----")
430
    self.assertAlmostEqual(runtime, 154706.1)
431

    
432
  def testGetInstanceInfoUnknown(self):
433
    hv = self._GetHv(run_cmd=self._XenList)
434

    
435
    result = hv.GetInstanceInfo("unknown.example.com")
436
    self.assertTrue(result is None)
437

    
438
  def testGetAllInstancesInfo(self):
439
    hv = self._GetHv(run_cmd=self._XenList)
440

    
441
    result = hv.GetAllInstancesInfo()
442

    
443
    self.assertEqual(map(compat.fst, result), [
444
      "server01.example.com",
445
      "web3106215069.example.com",
446
      "testinstance.example.com",
447
      ])
448

    
449
  def testListInstances(self):
450
    hv = self._GetHv(run_cmd=self._XenList)
451

    
452
    self.assertEqual(hv.ListInstances(), [
453
      "server01.example.com",
454
      "web3106215069.example.com",
455
      "testinstance.example.com",
456
      ])
457

    
458
  def testVerify(self):
459
    output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
460
    hv = self._GetHv(run_cmd=compat.partial(self._SuccessCommand,
461
                                            output))
462
    self.assertTrue(hv.Verify() is None)
463

    
464
  def testVerifyFailing(self):
465
    hv = self._GetHv(run_cmd=self._FailingCommand)
466
    self.assertTrue("failed:" in hv.Verify())
467

    
468
  def _StartInstanceCommand(self, inst, paused, failcreate, cmd):
469
    if cmd == [self.CMD, "info"]:
470
      output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
471
    elif cmd == [self.CMD, "list"]:
472
      output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
473
    elif cmd[:2] == [self.CMD, "create"]:
474
      args = cmd[2:]
475
      cfgfile = utils.PathJoin(self.tmpdir, inst.name)
476

    
477
      if paused:
478
        self.assertEqual(args, ["-p", cfgfile])
479
      else:
480
        self.assertEqual(args, [cfgfile])
481

    
482
      if failcreate:
483
        return self._FailingCommand(cmd)
484

    
485
      output = ""
486
    else:
487
      self.fail("Unhandled command: %s" % (cmd, ))
488

    
489
    return self._SuccessCommand(output, cmd)
490
    #return self._FailingCommand(cmd)
491

    
492
  def _MakeInstance(self):
493
    # Copy default parameters
494
    bep = objects.FillDict(constants.BEC_DEFAULTS, {})
495
    hvp = objects.FillDict(constants.HVC_DEFAULTS[self.HVNAME], {})
496

    
497
    # Override default VNC password file path
498
    if constants.HV_VNC_PASSWORD_FILE in hvp:
499
      hvp[constants.HV_VNC_PASSWORD_FILE] = self.vncpw_path
500

    
501
    disks = [
502
      (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDWR),
503
       utils.PathJoin(self.tmpdir, "disk0")),
504
      (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDONLY),
505
       utils.PathJoin(self.tmpdir, "disk1")),
506
      ]
507

    
508
    inst = objects.Instance(name="server01.example.com",
509
                            hvparams=hvp, beparams=bep,
510
                            osparams={}, nics=[], os="deb1",
511
                            disks=map(compat.fst, disks))
512
    inst.UpgradeConfig()
513

    
514
    return (inst, disks)
515

    
516
  def testStartInstance(self):
517
    (inst, disks) = self._MakeInstance()
518

    
519
    for failcreate in [False, True]:
520
      for paused in [False, True]:
521
        run_cmd = compat.partial(self._StartInstanceCommand,
522
                                 inst, paused, failcreate)
523

    
524
        hv = self._GetHv(run_cmd=run_cmd)
525

    
526
        # Ensure instance is not listed
527
        self.assertTrue(inst.name not in hv.ListInstances())
528

    
529
        # Remove configuration
530
        cfgfile = utils.PathJoin(self.tmpdir, inst.name)
531
        utils.RemoveFile(cfgfile)
532

    
533
        if failcreate:
534
          self.assertRaises(errors.HypervisorError, hv.StartInstance,
535
                            inst, disks, paused)
536
        else:
537
          hv.StartInstance(inst, disks, paused)
538

    
539
        # Check if configuration was updated
540
        lines = utils.ReadFile(cfgfile).splitlines()
541

    
542
        if constants.HV_VNC_PASSWORD_FILE in inst.hvparams:
543
          self.assertTrue(("vncpasswd = '%s'" % self.vncpw) in lines)
544
        else:
545
          extra = inst.hvparams[constants.HV_KERNEL_ARGS]
546
          self.assertTrue(("extra = '%s'" % extra) in lines)
547

    
548
  def _StopInstanceCommand(self, instance_name, force, fail, cmd):
549
    if ((force and cmd[:2] == [self.CMD, "destroy"]) or
550
        (not force and cmd[:2] == [self.CMD, "shutdown"])):
551
      self.assertEqual(cmd[2:], [instance_name])
552
      output = ""
553
    else:
554
      self.fail("Unhandled command: %s" % (cmd, ))
555

    
556
    if fail:
557
      # Simulate a failing command
558
      return self._FailingCommand(cmd)
559
    else:
560
      return self._SuccessCommand(output, cmd)
561

    
562
  def testStopInstance(self):
563
    name = "inst4284.example.com"
564
    cfgfile = utils.PathJoin(self.tmpdir, name)
565
    cfgdata = "config file content\n"
566

    
567
    for force in [False, True]:
568
      for fail in [False, True]:
569
        utils.WriteFile(cfgfile, data=cfgdata)
570

    
571
        run_cmd = compat.partial(self._StopInstanceCommand, name, force, fail)
572

    
573
        hv = self._GetHv(run_cmd=run_cmd)
574

    
575
        self.assertTrue(os.path.isfile(cfgfile))
576

    
577
        if fail:
578
          try:
579
            hv._StopInstance(name, force)
580
          except errors.HypervisorError, err:
581
            self.assertTrue(str(err).startswith("Failed to stop instance"))
582
          else:
583
            self.fail("Exception was not raised")
584
          self.assertEqual(utils.ReadFile(cfgfile), cfgdata,
585
                           msg=("Configuration was removed when stopping"
586
                                " instance failed"))
587
        else:
588
          hv._StopInstance(name, force)
589
          self.assertFalse(os.path.exists(cfgfile))
590

    
591

    
592
def _MakeTestClass(cls, cmd):
593
  """Makes a class for testing.
594

595
  The returned class has structure as shown in the following pseudo code:
596

597
    class Test{cls.__name__}{cmd}(_TestXenHypervisor, unittest.TestCase):
598
      TARGET = {cls}
599
      CMD = {cmd}
600
      HVNAME = {Hypervisor name retrieved using class}
601

602
  @type cls: class
603
  @param cls: Hypervisor class to be tested
604
  @type cmd: string
605
  @param cmd: Hypervisor command
606
  @rtype: tuple
607
  @return: Class name and class object (not instance)
608

609
  """
610
  name = "Test%sCmd%s" % (cls.__name__, cmd.title())
611
  bases = (_TestXenHypervisor, unittest.TestCase)
612
  hvname = HVCLASS_TO_HVNAME[cls]
613

    
614
  return (name, type(name, bases, dict(TARGET=cls, CMD=cmd, HVNAME=hvname)))
615

    
616

    
617
# Create test classes programmatically instead of manually to reduce the risk
618
# of forgetting some combinations
619
for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]:
620
  for cmd in constants.KNOWN_XEN_COMMANDS:
621
    (name, testcls) = _MakeTestClass(cls, cmd)
622

    
623
    assert name not in locals()
624

    
625
    locals()[name] = testcls
626

    
627

    
628
if __name__ == "__main__":
629
  testutils.GanetiTestProgram()