Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.hypervisor.hv_xen_unittest.py @ 8ef418bb

History | View | Annotate | Download (30.9 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_xen"""
23

    
24
import string # pylint: disable=W0402
25
import unittest
26
import tempfile
27
import shutil
28
import random
29
import os
30
import mock
31

    
32
from ganeti import constants
33
from ganeti import objects
34
from ganeti import pathutils
35
from ganeti import hypervisor
36
from ganeti import utils
37
from ganeti import errors
38
from ganeti import compat
39

    
40
from ganeti.hypervisor import hv_xen
41

    
42
import testutils
43

    
44

    
45
# Map from hypervisor class to hypervisor name
46
HVCLASS_TO_HVNAME = utils.InvertDict(hypervisor._HYPERVISOR_MAP)
47

    
48

    
49
class TestConsole(unittest.TestCase):
50
  def test(self):
51
    hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
52
    for cls in [hv_xen.XenPvmHypervisor(), hv_xen.XenHvmHypervisor()]:
53
      instance = objects.Instance(name="xen.example.com",
54
                                  primary_node="node24828")
55
      cons = cls.GetInstanceConsole(instance, hvparams, {})
56
      self.assertTrue(cons.Validate())
57
      self.assertEqual(cons.kind, constants.CONS_SSH)
58
      self.assertEqual(cons.host, instance.primary_node)
59
      self.assertEqual(cons.command[-1], instance.name)
60

    
61

    
62
class TestCreateConfigCpus(unittest.TestCase):
63
  def testEmpty(self):
64
    for cpu_mask in [None, ""]:
65
      self.assertEqual(hv_xen._CreateConfigCpus(cpu_mask),
66
                       "cpus = [  ]")
67

    
68
  def testAll(self):
69
    self.assertEqual(hv_xen._CreateConfigCpus(constants.CPU_PINNING_ALL),
70
                     None)
71

    
72
  def testOne(self):
73
    self.assertEqual(hv_xen._CreateConfigCpus("9"), "cpu = \"9\"")
74

    
75
  def testMultiple(self):
76
    self.assertEqual(hv_xen._CreateConfigCpus("0-2,4,5-5:3:all"),
77
                     ("cpus = [ \"0,1,2,4,5\", \"3\", \"%s\" ]" %
78
                      constants.CPU_PINNING_ALL_XEN))
79

    
80

    
81
class TestGetCommand(testutils.GanetiTestCase):
82
  def testCommandExplicit(self):
83
    """Test the case when the command is given as class parameter explicitly.
84

85
    """
86
    expected_cmd = "xl"
87
    hv = hv_xen.XenHypervisor(_cmd=constants.XEN_CMD_XL)
88
    self.assertEqual(hv._GetCommand(None), expected_cmd)
89

    
90
  def testCommandInvalid(self):
91
    """Test the case an invalid command is given as class parameter explicitly.
92

93
    """
94
    hv = hv_xen.XenHypervisor(_cmd="invalidcommand")
95
    self.assertRaises(errors.ProgrammerError, hv._GetCommand, None)
96

    
97
  def testCommandHvparams(self):
98
    expected_cmd = "xl"
99
    test_hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
100
    hv = hv_xen.XenHypervisor()
101
    self.assertEqual(hv._GetCommand(test_hvparams), expected_cmd)
102

    
103
  def testCommandHvparamsInvalid(self):
104
    test_hvparams = {}
105
    hv = hv_xen.XenHypervisor()
106
    self.assertRaises(errors.HypervisorError, hv._GetCommand, test_hvparams)
107

    
108
  def testCommandHvparamsCmdInvalid(self):
109
    test_hvparams = {constants.HV_XEN_CMD: "invalidcommand"}
110
    hv = hv_xen.XenHypervisor()
111
    self.assertRaises(errors.ProgrammerError, hv._GetCommand, test_hvparams)
112

    
113

    
114
class TestParseInstanceList(testutils.GanetiTestCase):
115
  def test(self):
116
    data = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
117

    
118
    # Exclude node
119
    self.assertEqual(hv_xen._ParseInstanceList(data.splitlines(), False), [])
120

    
121
    # Include node
122
    result = hv_xen._ParseInstanceList(data.splitlines(), True)
123
    self.assertEqual(len(result), 1)
124
    self.assertEqual(len(result[0]), 6)
125

    
126
    # Name
127
    self.assertEqual(result[0][0], hv_xen._DOM0_NAME)
128

    
129
    # ID
130
    self.assertEqual(result[0][1], 0)
131

    
132
    # Memory
133
    self.assertEqual(result[0][2], 1023)
134

    
135
    # VCPUs
136
    self.assertEqual(result[0][3], 1)
137

    
138
    # State
139
    self.assertEqual(result[0][4], "r-----")
140

    
141
    # Time
142
    self.assertAlmostEqual(result[0][5], 121152.6)
143

    
144
  def testWrongLineFormat(self):
145
    tests = [
146
      ["three fields only"],
147
      ["name InvalidID 128 1 r----- 12345"],
148
      ]
149

    
150
    for lines in tests:
151
      try:
152
        hv_xen._ParseInstanceList(["Header would be here"] + lines, False)
153
      except errors.HypervisorError, err:
154
        self.assertTrue("Can't parse instance list" in str(err))
155
      else:
156
        self.fail("Exception was not raised")
157

    
158

    
159
class TestGetInstanceList(testutils.GanetiTestCase):
160
  def _Fail(self):
161
    return utils.RunResult(constants.EXIT_FAILURE, None,
162
                           "stdout", "stderr", None,
163
                           NotImplemented, NotImplemented)
164

    
165
  def testTimeout(self):
166
    fn = testutils.CallCounter(self._Fail)
167
    try:
168
      hv_xen._GetInstanceList(fn, False, _timeout=0.1)
169
    except errors.HypervisorError, err:
170
      self.assertTrue("timeout exceeded" in str(err))
171
    else:
172
      self.fail("Exception was not raised")
173

    
174
    self.assertTrue(fn.Count() < 10,
175
                    msg="'xm list' was called too many times")
176

    
177
  def _Success(self, stdout):
178
    return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
179
                           NotImplemented, NotImplemented)
180

    
181
  def testSuccess(self):
182
    data = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
183

    
184
    fn = testutils.CallCounter(compat.partial(self._Success, data))
185

    
186
    result = hv_xen._GetInstanceList(fn, True, _timeout=0.1)
187

    
188
    self.assertEqual(len(result), 4)
189

    
190
    self.assertEqual(map(compat.fst, result), [
191
      "Domain-0",
192
      "server01.example.com",
193
      "web3106215069.example.com",
194
      "testinstance.example.com",
195
      ])
196

    
197
    self.assertEqual(fn.Count(), 1)
198

    
199

    
200
class TestParseNodeInfo(testutils.GanetiTestCase):
201
  def testEmpty(self):
202
    self.assertEqual(hv_xen._ParseNodeInfo(""), {})
203

    
204
  def testUnknownInput(self):
205
    data = "\n".join([
206
      "foo bar",
207
      "something else goes",
208
      "here",
209
      ])
210
    self.assertEqual(hv_xen._ParseNodeInfo(data), {})
211

    
212
  def testBasicInfo(self):
213
    data = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
214
    result = hv_xen._ParseNodeInfo(data)
215
    self.assertEqual(result, {
216
      "cpu_nodes": 1,
217
      "cpu_sockets": 2,
218
      "cpu_total": 4,
219
      "hv_version": (4, 0),
220
      "memory_free": 8004,
221
      "memory_total": 16378,
222
      })
223

    
224

    
225
class TestMergeInstanceInfo(testutils.GanetiTestCase):
226
  def testEmpty(self):
227
    self.assertEqual(hv_xen._MergeInstanceInfo({}, []), {})
228

    
229
  def _FakeXmList(self, include_node):
230
    return [
231
      (hv_xen._DOM0_NAME, NotImplemented, 4096, 7, NotImplemented,
232
       NotImplemented),
233
      ("inst1.example.com", NotImplemented, 2048, 4, NotImplemented,
234
       NotImplemented),
235
      ]
236

    
237
  def testMissingNodeInfo(self):
238
    instance_list = self._FakeXmList(True)
239
    result = hv_xen._MergeInstanceInfo({}, instance_list)
240
    self.assertEqual(result, {
241
      "memory_dom0": 4096,
242
      "dom0_cpus": 7,
243
      })
244

    
245
  def testWithNodeInfo(self):
246
    info = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
247
    instance_list = self._FakeXmList(True)
248
    result = hv_xen._GetNodeInfo(info, instance_list)
249
    self.assertEqual(result, {
250
      "cpu_nodes": 1,
251
      "cpu_sockets": 2,
252
      "cpu_total": 4,
253
      "dom0_cpus": 7,
254
      "hv_version": (4, 0),
255
      "memory_dom0": 4096,
256
      "memory_free": 8004,
257
      "memory_hv": 2230,
258
      "memory_total": 16378,
259
      })
260

    
261

    
262
class TestGetConfigFileDiskData(unittest.TestCase):
263
  def testLetterCount(self):
264
    self.assertEqual(len(hv_xen._DISK_LETTERS), 26)
265

    
266
  def testNoDisks(self):
267
    self.assertEqual(hv_xen._GetConfigFileDiskData([], "hd"), [])
268

    
269
  def testManyDisks(self):
270
    for offset in [0, 1, 10]:
271
      disks = [(objects.Disk(dev_type=constants.LD_LV), "/tmp/disk/%s" % idx)
272
               for idx in range(len(hv_xen._DISK_LETTERS) + offset)]
273

    
274
      if offset == 0:
275
        result = hv_xen._GetConfigFileDiskData(disks, "hd")
276
        self.assertEqual(result, [
277
          "'phy:/tmp/disk/%s,hd%s,r'" % (idx, string.ascii_lowercase[idx])
278
          for idx in range(len(hv_xen._DISK_LETTERS) + offset)
279
          ])
280
      else:
281
        try:
282
          hv_xen._GetConfigFileDiskData(disks, "hd")
283
        except errors.HypervisorError, err:
284
          self.assertEqual(str(err), "Too many disks")
285
        else:
286
          self.fail("Exception was not raised")
287

    
288
  def testTwoLvDisksWithMode(self):
289
    disks = [
290
      (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDWR),
291
       "/tmp/diskFirst"),
292
      (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDONLY),
293
       "/tmp/diskLast"),
294
      ]
295

    
296
    result = hv_xen._GetConfigFileDiskData(disks, "hd")
297
    self.assertEqual(result, [
298
      "'phy:/tmp/diskFirst,hda,w'",
299
      "'phy:/tmp/diskLast,hdb,r'",
300
      ])
301

    
302
  def testFileDisks(self):
303
    disks = [
304
      (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
305
                    physical_id=[constants.FD_LOOP]),
306
       "/tmp/diskFirst"),
307
      (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDONLY,
308
                    physical_id=[constants.FD_BLKTAP]),
309
       "/tmp/diskTwo"),
310
      (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
311
                    physical_id=[constants.FD_LOOP]),
312
       "/tmp/diskThree"),
313
      (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
314
                    physical_id=[constants.FD_BLKTAP]),
315
       "/tmp/diskLast"),
316
      ]
317

    
318
    result = hv_xen._GetConfigFileDiskData(disks, "sd")
319
    self.assertEqual(result, [
320
      "'file:/tmp/diskFirst,sda,w'",
321
      "'tap:aio:/tmp/diskTwo,sdb,r'",
322
      "'file:/tmp/diskThree,sdc,w'",
323
      "'tap:aio:/tmp/diskLast,sdd,w'",
324
      ])
325

    
326
  def testInvalidFileDisk(self):
327
    disks = [
328
      (objects.Disk(dev_type=constants.LD_FILE, mode=constants.DISK_RDWR,
329
                    physical_id=["#unknown#"]),
330
       "/tmp/diskinvalid"),
331
      ]
332

    
333
    self.assertRaises(KeyError, hv_xen._GetConfigFileDiskData, disks, "sd")
334

    
335

    
336
class TestXenHypervisorRunXen(unittest.TestCase):
337

    
338
  XEN_SUB_CMD = "help"
339

    
340
  def testCommandUnknown(self):
341
    cmd = "#unknown command#"
342
    self.assertFalse(cmd in constants.KNOWN_XEN_COMMANDS)
343
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
344
                              _run_cmd_fn=NotImplemented,
345
                              _cmd=cmd)
346
    self.assertRaises(errors.ProgrammerError, hv._RunXen, [], None)
347

    
348
  def testCommandNoHvparams(self):
349
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
350
                              _run_cmd_fn=NotImplemented)
351
    hvparams = None
352
    self.assertRaises(errors.HypervisorError, hv._RunXen, [self.XEN_SUB_CMD],
353
                      hvparams)
354

    
355
  def testCommandFromHvparams(self):
356
    expected_xen_cmd = "xl"
357
    hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
358
    mock_run_cmd = mock.Mock()
359
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
360
                              _run_cmd_fn=mock_run_cmd)
361
    hv._RunXen([self.XEN_SUB_CMD], hvparams=hvparams)
362
    mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_SUB_CMD])
363

    
364

    
365
class TestXenHypervisorGetInstanceList(unittest.TestCase):
366

    
367
  RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
368
  XEN_LIST = "list"
369

    
370
  def testNoHvparams(self):
371
    expected_xen_cmd = "xm"
372
    mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
373
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
374
                              _run_cmd_fn=mock_run_cmd)
375
    self.assertRaises(errors.HypervisorError, hv._GetInstanceList, True, None)
376

    
377
  def testFromHvparams(self):
378
    expected_xen_cmd = "xl"
379
    hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
380
    mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
381
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
382
                              _run_cmd_fn=mock_run_cmd)
383
    hv._GetInstanceList(True, hvparams)
384
    mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
385

    
386

    
387
class TestXenHypervisorListInstances(unittest.TestCase):
388

    
389
  RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
390
  XEN_LIST = "list"
391

    
392
  def testNoHvparams(self):
393
    expected_xen_cmd = "xm"
394
    mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
395
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
396
                              _run_cmd_fn=mock_run_cmd)
397
    self.assertRaises(errors.HypervisorError, hv.ListInstances)
398

    
399
  def testHvparamsXl(self):
400
    expected_xen_cmd = "xl"
401
    hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
402
    mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
403
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
404
                              _run_cmd_fn=mock_run_cmd)
405
    hv.ListInstances(hvparams=hvparams)
406
    mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
407

    
408

    
409
class TestXenHypervisorCheckToolstack(unittest.TestCase):
410

    
411
  def setUp(self):
412
    self.tmpdir = tempfile.mkdtemp()
413
    self.cfg_name = "xen_config"
414
    self.cfg_path = utils.PathJoin(self.tmpdir, self.cfg_name)
415
    self.hv = hv_xen.XenHypervisor()
416

    
417
  def tearDown(self):
418
    shutil.rmtree(self.tmpdir)
419

    
420
  def testBinaryNotFound(self):
421
    RESULT_FAILED = utils.RunResult(1, None, "", "", "", None, None)
422
    mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
423
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
424
                              _run_cmd_fn=mock_run_cmd)
425
    result = hv._CheckToolstackBinary("xl")
426
    self.assertFalse(result)
427

    
428
  def testCheckToolstackXlConfigured(self):
429
    RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
430
    mock_run_cmd = mock.Mock(return_value=RESULT_OK)
431
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
432
                              _run_cmd_fn=mock_run_cmd)
433
    result = hv._CheckToolstackXlConfigured()
434
    self.assertTrue(result)
435

    
436
  def testCheckToolstackXlNotConfigured(self):
437
    RESULT_FAILED = utils.RunResult(
438
        1, None, "",
439
        "ERROR:  A different toolstack (xm) have been selected!",
440
        "", None, None)
441
    mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
442
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
443
                              _run_cmd_fn=mock_run_cmd)
444
    result = hv._CheckToolstackXlConfigured()
445
    self.assertFalse(result)
446

    
447
  def testCheckToolstackXlFails(self):
448
    RESULT_FAILED = utils.RunResult(
449
        1, None, "",
450
        "ERROR: The pink bunny hid the binary.",
451
        "", None, None)
452
    mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
453
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
454
                              _run_cmd_fn=mock_run_cmd)
455
    self.assertRaises(errors.HypervisorError, hv._CheckToolstackXlConfigured)
456

    
457

    
458
class TestXenHypervisorWriteConfigFile(unittest.TestCase):
459
  def setUp(self):
460
    self.tmpdir = tempfile.mkdtemp()
461

    
462
  def tearDown(self):
463
    shutil.rmtree(self.tmpdir)
464

    
465
  def testWriteError(self):
466
    cfgdir = utils.PathJoin(self.tmpdir, "foobar")
467

    
468
    hv = hv_xen.XenHypervisor(_cfgdir=cfgdir,
469
                              _run_cmd_fn=NotImplemented,
470
                              _cmd=NotImplemented)
471

    
472
    self.assertFalse(os.path.exists(cfgdir))
473

    
474
    try:
475
      hv._WriteConfigFile("name", "data")
476
    except errors.HypervisorError, err:
477
      self.assertTrue(str(err).startswith("Cannot write Xen instance"))
478
    else:
479
      self.fail("Exception was not raised")
480

    
481

    
482
class TestXenHypervisorVerify(unittest.TestCase):
483

    
484
  def setUp(self):
485
    output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
486
    self._result_ok = utils.RunResult(0, None, output, "", "", None, None)
487

    
488
  def testVerify(self):
489
    hvparams = {constants.HV_XEN_CMD : constants.XEN_CMD_XL}
490
    mock_run_cmd = mock.Mock(return_value=self._result_ok)
491
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
492
                              _run_cmd_fn=mock_run_cmd)
493
    hv._CheckToolstack = mock.Mock(return_value=True)
494
    result = hv.Verify(hvparams)
495
    self.assertTrue(result is None)
496

    
497
  def testVerifyToolstackNotOk(self):
498
    hvparams = {constants.HV_XEN_CMD : constants.XEN_CMD_XL}
499
    mock_run_cmd = mock.Mock(return_value=self._result_ok)
500
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
501
                              _run_cmd_fn=mock_run_cmd)
502
    hv._CheckToolstack = mock.Mock()
503
    hv._CheckToolstack.side_effect = errors.HypervisorError("foo")
504
    result = hv.Verify(hvparams)
505
    self.assertTrue(result is not None)
506

    
507
  def testVerifyFailing(self):
508
    result_failed = utils.RunResult(1, None, "", "", "", None, None)
509
    mock_run_cmd = mock.Mock(return_value=result_failed)
510
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
511
                              _run_cmd_fn=mock_run_cmd)
512
    hv._CheckToolstack = mock.Mock(return_value=True)
513
    result = hv.Verify()
514
    self.assertTrue(result is not None)
515

    
516

    
517
class _TestXenHypervisor(object):
518
  TARGET = NotImplemented
519
  CMD = NotImplemented
520
  HVNAME = NotImplemented
521
  VALID_HVPARAMS = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
522

    
523
  def setUp(self):
524
    super(_TestXenHypervisor, self).setUp()
525

    
526
    self.tmpdir = tempfile.mkdtemp()
527

    
528
    self.vncpw = "".join(random.sample(string.ascii_letters, 10))
529

    
530
    self.vncpw_path = utils.PathJoin(self.tmpdir, "vncpw")
531
    utils.WriteFile(self.vncpw_path, data=self.vncpw)
532

    
533
  def tearDown(self):
534
    super(_TestXenHypervisor, self).tearDown()
535

    
536
    shutil.rmtree(self.tmpdir)
537

    
538
  def _GetHv(self, run_cmd=NotImplemented):
539
    return self.TARGET(_cfgdir=self.tmpdir, _run_cmd_fn=run_cmd, _cmd=self.CMD)
540

    
541
  def _SuccessCommand(self, stdout, cmd):
542
    self.assertEqual(cmd[0], self.CMD)
543

    
544
    return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
545
                           NotImplemented, NotImplemented)
546

    
547
  def _FailingCommand(self, cmd):
548
    self.assertEqual(cmd[0], self.CMD)
549

    
550
    return utils.RunResult(constants.EXIT_FAILURE, None,
551
                           "", "This command failed", None,
552
                           NotImplemented, NotImplemented)
553

    
554
  def _FakeTcpPing(self, expected, result, target, port, **kwargs):
555
    self.assertEqual((target, port), expected)
556
    return result
557

    
558
  def testReadingNonExistentConfigFile(self):
559
    hv = self._GetHv()
560

    
561
    try:
562
      hv._ReadConfigFile("inst15780.example.com")
563
    except errors.HypervisorError, err:
564
      self.assertTrue(str(err).startswith("Failed to load Xen config file:"))
565
    else:
566
      self.fail("Exception was not raised")
567

    
568
  def testRemovingAutoConfigFile(self):
569
    name = "inst8206.example.com"
570
    cfgfile = utils.PathJoin(self.tmpdir, name)
571
    autodir = utils.PathJoin(self.tmpdir, "auto")
572
    autocfgfile = utils.PathJoin(autodir, name)
573

    
574
    os.mkdir(autodir)
575

    
576
    utils.WriteFile(autocfgfile, data="")
577

    
578
    hv = self._GetHv()
579

    
580
    self.assertTrue(os.path.isfile(autocfgfile))
581
    hv._WriteConfigFile(name, "content")
582
    self.assertFalse(os.path.exists(autocfgfile))
583
    self.assertEqual(utils.ReadFile(cfgfile), "content")
584

    
585
  def _XenList(self, cmd):
586
    self.assertEqual(cmd, [self.CMD, "list"])
587

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

    
591
    return self._SuccessCommand(output, cmd)
592

    
593
  def testGetInstanceInfo(self):
594
    hv = self._GetHv(run_cmd=self._XenList)
595

    
596
    (name, instid, memory, vcpus, state, runtime) = \
597
      hv.GetInstanceInfo("server01.example.com")
598

    
599
    self.assertEqual(name, "server01.example.com")
600
    self.assertEqual(instid, 1)
601
    self.assertEqual(memory, 1024)
602
    self.assertEqual(vcpus, 1)
603
    self.assertEqual(state, "-b----")
604
    self.assertAlmostEqual(runtime, 167643.2)
605

    
606
  def testGetInstanceInfoDom0(self):
607
    hv = self._GetHv(run_cmd=self._XenList)
608

    
609
    # TODO: Not sure if this is actually used anywhere (can't find it), but the
610
    # code supports querying for Dom0
611
    (name, instid, memory, vcpus, state, runtime) = \
612
      hv.GetInstanceInfo(hv_xen._DOM0_NAME)
613

    
614
    self.assertEqual(name, "Domain-0")
615
    self.assertEqual(instid, 0)
616
    self.assertEqual(memory, 1023)
617
    self.assertEqual(vcpus, 1)
618
    self.assertEqual(state, "r-----")
619
    self.assertAlmostEqual(runtime, 154706.1)
620

    
621
  def testGetInstanceInfoUnknown(self):
622
    hv = self._GetHv(run_cmd=self._XenList)
623

    
624
    result = hv.GetInstanceInfo("unknown.example.com")
625
    self.assertTrue(result is None)
626

    
627
  def testGetAllInstancesInfo(self):
628
    hv = self._GetHv(run_cmd=self._XenList)
629

    
630
    result = hv.GetAllInstancesInfo()
631

    
632
    self.assertEqual(map(compat.fst, result), [
633
      "server01.example.com",
634
      "web3106215069.example.com",
635
      "testinstance.example.com",
636
      ])
637

    
638
  def testListInstances(self):
639
    hv = self._GetHv(run_cmd=self._XenList)
640

    
641
    self.assertEqual(hv.ListInstances(), [
642
      "server01.example.com",
643
      "web3106215069.example.com",
644
      "testinstance.example.com",
645
      ])
646

    
647
  def _StartInstanceCommand(self, inst, paused, failcreate, cmd):
648
    if cmd == [self.CMD, "info"]:
649
      output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
650
    elif cmd == [self.CMD, "list"]:
651
      output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
652
    elif cmd[:2] == [self.CMD, "create"]:
653
      args = cmd[2:]
654
      cfgfile = utils.PathJoin(self.tmpdir, inst.name)
655

    
656
      if paused:
657
        self.assertEqual(args, ["-p", cfgfile])
658
      else:
659
        self.assertEqual(args, [cfgfile])
660

    
661
      if failcreate:
662
        return self._FailingCommand(cmd)
663

    
664
      output = ""
665
    else:
666
      self.fail("Unhandled command: %s" % (cmd, ))
667

    
668
    return self._SuccessCommand(output, cmd)
669

    
670
  def _MakeInstance(self):
671
    # Copy default parameters
672
    bep = objects.FillDict(constants.BEC_DEFAULTS, {})
673
    hvp = objects.FillDict(constants.HVC_DEFAULTS[self.HVNAME], {})
674

    
675
    # Override default VNC password file path
676
    if constants.HV_VNC_PASSWORD_FILE in hvp:
677
      hvp[constants.HV_VNC_PASSWORD_FILE] = self.vncpw_path
678

    
679
    disks = [
680
      (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDWR),
681
       utils.PathJoin(self.tmpdir, "disk0")),
682
      (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDONLY),
683
       utils.PathJoin(self.tmpdir, "disk1")),
684
      ]
685

    
686
    inst = objects.Instance(name="server01.example.com",
687
                            hvparams=hvp, beparams=bep,
688
                            osparams={}, nics=[], os="deb1",
689
                            disks=map(compat.fst, disks))
690
    inst.UpgradeConfig()
691

    
692
    return (inst, disks)
693

    
694
  def testStartInstance(self):
695
    (inst, disks) = self._MakeInstance()
696
    pathutils.LOG_XEN_DIR = self.tmpdir
697

    
698
    for failcreate in [False, True]:
699
      for paused in [False, True]:
700
        run_cmd = compat.partial(self._StartInstanceCommand,
701
                                 inst, paused, failcreate)
702

    
703
        hv = self._GetHv(run_cmd=run_cmd)
704

    
705
        # Ensure instance is not listed
706
        self.assertTrue(inst.name not in hv.ListInstances())
707

    
708
        # Remove configuration
709
        cfgfile = utils.PathJoin(self.tmpdir, inst.name)
710
        utils.RemoveFile(cfgfile)
711

    
712
        if failcreate:
713
          self.assertRaises(errors.HypervisorError, hv.StartInstance,
714
                            inst, disks, paused)
715
          # Check whether a stale config file is left behind
716
          self.assertFalse(os.path.exists(cfgfile))
717
        else:
718
          hv.StartInstance(inst, disks, paused)
719
          # Check if configuration was updated
720
          lines = utils.ReadFile(cfgfile).splitlines()
721

    
722
        if constants.HV_VNC_PASSWORD_FILE in inst.hvparams:
723
          self.assertTrue(("vncpasswd = '%s'" % self.vncpw) in lines)
724
        else:
725
          extra = inst.hvparams[constants.HV_KERNEL_ARGS]
726
          self.assertTrue(("extra = '%s'" % extra) in lines)
727

    
728
  def _StopInstanceCommand(self, instance_name, force, fail, cmd):
729
    if ((force and cmd[:2] == [self.CMD, "destroy"]) or
730
        (not force and cmd[:2] == [self.CMD, "shutdown"])):
731
      self.assertEqual(cmd[2:], [instance_name])
732
      output = ""
733
    else:
734
      self.fail("Unhandled command: %s" % (cmd, ))
735

    
736
    if fail:
737
      # Simulate a failing command
738
      return self._FailingCommand(cmd)
739
    else:
740
      return self._SuccessCommand(output, cmd)
741

    
742
  def testStopInstance(self):
743
    name = "inst4284.example.com"
744
    cfgfile = utils.PathJoin(self.tmpdir, name)
745
    cfgdata = "config file content\n"
746

    
747
    for force in [False, True]:
748
      for fail in [False, True]:
749
        utils.WriteFile(cfgfile, data=cfgdata)
750

    
751
        run_cmd = compat.partial(self._StopInstanceCommand, name, force, fail)
752

    
753
        hv = self._GetHv(run_cmd=run_cmd)
754

    
755
        self.assertTrue(os.path.isfile(cfgfile))
756

    
757
        if fail:
758
          try:
759
            hv._StopInstance(name, force, None)
760
          except errors.HypervisorError, err:
761
            self.assertTrue(str(err).startswith("Failed to stop instance"))
762
          else:
763
            self.fail("Exception was not raised")
764
          self.assertEqual(utils.ReadFile(cfgfile), cfgdata,
765
                           msg=("Configuration was removed when stopping"
766
                                " instance failed"))
767
        else:
768
          hv._StopInstance(name, force, None)
769
          self.assertFalse(os.path.exists(cfgfile))
770

    
771
  def _MigrateNonRunningInstCmd(self, cmd):
772
    if cmd == [self.CMD, "list"]:
773
      output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
774
    else:
775
      self.fail("Unhandled command: %s" % (cmd, ))
776

    
777
    return self._SuccessCommand(output, cmd)
778

    
779
  def testMigrateInstanceNotRunning(self):
780
    name = "nonexistinginstance.example.com"
781
    target = constants.IP4_ADDRESS_LOCALHOST
782
    port = 14618
783

    
784
    hv = self._GetHv(run_cmd=self._MigrateNonRunningInstCmd)
785

    
786
    for live in [False, True]:
787
      try:
788
        hv._MigrateInstance(NotImplemented, name, target, port, live,
789
                            self.VALID_HVPARAMS, _ping_fn=NotImplemented)
790
      except errors.HypervisorError, err:
791
        self.assertEqual(str(err), "Instance not running, cannot migrate")
792
      else:
793
        self.fail("Exception was not raised")
794

    
795
  def _MigrateInstTargetUnreachCmd(self, cmd):
796
    if cmd == [self.CMD, "list"]:
797
      output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
798
    else:
799
      self.fail("Unhandled command: %s" % (cmd, ))
800

    
801
    return self._SuccessCommand(output, cmd)
802

    
803
  def testMigrateTargetUnreachable(self):
804
    name = "server01.example.com"
805
    target = constants.IP4_ADDRESS_LOCALHOST
806
    port = 28349
807

    
808
    hv = self._GetHv(run_cmd=self._MigrateInstTargetUnreachCmd)
809
    hvparams = {constants.HV_XEN_CMD: self.CMD}
810

    
811
    for live in [False, True]:
812
      if self.CMD == constants.XEN_CMD_XL:
813
        # TODO: Detect unreachable targets
814
        pass
815
      else:
816
        try:
817
          hv._MigrateInstance(NotImplemented, name, target, port, live,
818
                              hvparams,
819
                              _ping_fn=compat.partial(self._FakeTcpPing,
820
                                                      (target, port), False))
821
        except errors.HypervisorError, err:
822
          wanted = "Remote host %s not" % target
823
          self.assertTrue(str(err).startswith(wanted))
824
        else:
825
          self.fail("Exception was not raised")
826

    
827
  def _MigrateInstanceCmd(self, cluster_name, instance_name, target, port,
828
                          live, fail, cmd):
829
    if cmd == [self.CMD, "list"]:
830
      output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
831
    elif cmd[:2] == [self.CMD, "migrate"]:
832
      if self.CMD == constants.XEN_CMD_XM:
833
        args = ["-p", str(port)]
834

    
835
        if live:
836
          args.append("-l")
837

    
838
      elif self.CMD == constants.XEN_CMD_XL:
839
        args = [
840
          "-s", constants.XL_SSH_CMD % cluster_name,
841
          "-C", utils.PathJoin(self.tmpdir, instance_name),
842
          ]
843

    
844
      else:
845
        self.fail("Unknown Xen command '%s'" % self.CMD)
846

    
847
      args.extend([instance_name, target])
848
      self.assertEqual(cmd[2:], args)
849

    
850
      if fail:
851
        return self._FailingCommand(cmd)
852

    
853
      output = ""
854
    else:
855
      self.fail("Unhandled command: %s" % (cmd, ))
856

    
857
    return self._SuccessCommand(output, cmd)
858

    
859
  def testMigrateInstance(self):
860
    clustername = "cluster.example.com"
861
    instname = "server01.example.com"
862
    target = constants.IP4_ADDRESS_LOCALHOST
863
    port = 22364
864

    
865
    hvparams = {constants.HV_XEN_CMD: self.CMD}
866

    
867
    for live in [False, True]:
868
      for fail in [False, True]:
869
        ping_fn = \
870
          testutils.CallCounter(compat.partial(self._FakeTcpPing,
871
                                               (target, port), True))
872

    
873
        run_cmd = \
874
          compat.partial(self._MigrateInstanceCmd,
875
                         clustername, instname, target, port, live,
876
                         fail)
877

    
878
        hv = self._GetHv(run_cmd=run_cmd)
879

    
880
        if fail:
881
          try:
882
            hv._MigrateInstance(clustername, instname, target, port, live,
883
                                hvparams, _ping_fn=ping_fn)
884
          except errors.HypervisorError, err:
885
            self.assertTrue(str(err).startswith("Failed to migrate instance"))
886
          else:
887
            self.fail("Exception was not raised")
888
        else:
889
          hv._MigrateInstance(clustername, instname, target, port, live,
890
                              hvparams, _ping_fn=ping_fn)
891

    
892
        if self.CMD == constants.XEN_CMD_XM:
893
          expected_pings = 1
894
        else:
895
          expected_pings = 0
896

    
897
        self.assertEqual(ping_fn.Count(), expected_pings)
898

    
899
  def _GetNodeInfoCmd(self, fail, cmd):
900
    if cmd == [self.CMD, "info"]:
901
      if fail:
902
        return self._FailingCommand(cmd)
903
      else:
904
        output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
905
    elif cmd == [self.CMD, "list"]:
906
      if fail:
907
        self.fail("'xm list' shouldn't be called when 'xm info' failed")
908
      else:
909
        output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
910
    else:
911
      self.fail("Unhandled command: %s" % (cmd, ))
912

    
913
    return self._SuccessCommand(output, cmd)
914

    
915
  def testGetNodeInfo(self):
916
    run_cmd = compat.partial(self._GetNodeInfoCmd, False)
917
    hv = self._GetHv(run_cmd=run_cmd)
918
    result = hv.GetNodeInfo()
919

    
920
    self.assertEqual(result["hv_version"], (4, 0))
921
    self.assertEqual(result["memory_free"], 8004)
922

    
923
  def testGetNodeInfoFailing(self):
924
    run_cmd = compat.partial(self._GetNodeInfoCmd, True)
925
    hv = self._GetHv(run_cmd=run_cmd)
926
    self.assertTrue(hv.GetNodeInfo() is None)
927

    
928

    
929
def _MakeTestClass(cls, cmd):
930
  """Makes a class for testing.
931

932
  The returned class has structure as shown in the following pseudo code:
933

934
    class Test{cls.__name__}{cmd}(_TestXenHypervisor, unittest.TestCase):
935
      TARGET = {cls}
936
      CMD = {cmd}
937
      HVNAME = {Hypervisor name retrieved using class}
938

939
  @type cls: class
940
  @param cls: Hypervisor class to be tested
941
  @type cmd: string
942
  @param cmd: Hypervisor command
943
  @rtype: tuple
944
  @return: Class name and class object (not instance)
945

946
  """
947
  name = "Test%sCmd%s" % (cls.__name__, cmd.title())
948
  bases = (_TestXenHypervisor, unittest.TestCase)
949
  hvname = HVCLASS_TO_HVNAME[cls]
950

    
951
  return (name, type(name, bases, dict(TARGET=cls, CMD=cmd, HVNAME=hvname)))
952

    
953

    
954
# Create test classes programmatically instead of manually to reduce the risk
955
# of forgetting some combinations
956
for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]:
957
  for cmd in constants.KNOWN_XEN_COMMANDS:
958
    (name, testcls) = _MakeTestClass(cls, cmd)
959

    
960
    assert name not in locals()
961

    
962
    locals()[name] = testcls
963

    
964

    
965
if __name__ == "__main__":
966
  testutils.GanetiTestProgram()