Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.hypervisor.hv_xen_unittest.py @ 75bf3149

History | View | Annotate | Download (30.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_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
    for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]:
52
      instance = objects.Instance(name="xen.example.com",
53
                                  primary_node="node24828")
54
      cons = cls.GetInstanceConsole(instance, {}, {})
55
      self.assertTrue(cons.Validate())
56
      self.assertEqual(cons.kind, constants.CONS_SSH)
57
      self.assertEqual(cons.host, instance.primary_node)
58
      self.assertEqual(cons.command[-1], instance.name)
59

    
60

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

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

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

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

    
79

    
80
class TestGetCommand(testutils.GanetiTestCase):
81
  def testDefault(self):
82
    expected_cmd = "xm"
83
    hv = hv_xen.XenHypervisor()
84
    self.assertEqual(hv._GetCommand(), expected_cmd)
85

    
86
  def testCommandExplicit(self):
87
    """Test the case when the command is given as class parameter explicitly.
88

89
    """
90
    expected_cmd = "xl"
91
    hv = hv_xen.XenHypervisor(_cmd=constants.XEN_CMD_XL)
92
    self.assertEqual(hv._GetCommand(), expected_cmd)
93

    
94
  def testCommandInvalid(self):
95
    """Test the case an invalid command is given as class parameter explicitly.
96

97
    """
98
    hv = hv_xen.XenHypervisor(_cmd="invalidcommand")
99
    self.assertRaises(errors.ProgrammerError, hv._GetCommand, None)
100

    
101
  def testCommandHvparams(self):
102
    expected_cmd = "xl"
103
    test_hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
104
    hv = hv_xen.XenHypervisor()
105
    self.assertEqual(hv._GetCommand(hvparams=test_hvparams), expected_cmd)
106

    
107
  def testCommandHvparamsInvalid(self):
108
    test_hvparams = {}
109
    hv = hv_xen.XenHypervisor()
110
    self.assertRaises(KeyError, hv._GetCommand, test_hvparams)
111

    
112
  def testCommandHvparamsCmdInvalid(self):
113
    test_hvparams = {constants.HV_XEN_CMD: "invalidcommand"}
114
    hv = hv_xen.XenHypervisor()
115
    self.assertRaises(errors.ProgrammerError, hv._GetCommand, test_hvparams)
116

    
117

    
118
class TestParseXmList(testutils.GanetiTestCase):
119
  def test(self):
120
    data = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
121

    
122
    # Exclude node
123
    self.assertEqual(hv_xen._ParseXmList(data.splitlines(), False), [])
124

    
125
    # Include node
126
    result = hv_xen._ParseXmList(data.splitlines(), True)
127
    self.assertEqual(len(result), 1)
128
    self.assertEqual(len(result[0]), 6)
129

    
130
    # Name
131
    self.assertEqual(result[0][0], hv_xen._DOM0_NAME)
132

    
133
    # ID
134
    self.assertEqual(result[0][1], 0)
135

    
136
    # Memory
137
    self.assertEqual(result[0][2], 1023)
138

    
139
    # VCPUs
140
    self.assertEqual(result[0][3], 1)
141

    
142
    # State
143
    self.assertEqual(result[0][4], "r-----")
144

    
145
    # Time
146
    self.assertAlmostEqual(result[0][5], 121152.6)
147

    
148
  def testWrongLineFormat(self):
149
    tests = [
150
      ["three fields only"],
151
      ["name InvalidID 128 1 r----- 12345"],
152
      ]
153

    
154
    for lines in tests:
155
      try:
156
        hv_xen._ParseXmList(["Header would be here"] + lines, False)
157
      except errors.HypervisorError, err:
158
        self.assertTrue("Can't parse output of xm list" in str(err))
159
      else:
160
        self.fail("Exception was not raised")
161

    
162

    
163
class TestGetInstanceList(testutils.GanetiTestCase):
164
  def _Fail(self):
165
    return utils.RunResult(constants.EXIT_FAILURE, None,
166
                           "stdout", "stderr", None,
167
                           NotImplemented, NotImplemented)
168

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

    
178
    self.assertTrue(fn.Count() < 10,
179
                    msg="'xm list' was called too many times")
180

    
181
  def _Success(self, stdout):
182
    return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
183
                           NotImplemented, NotImplemented)
184

    
185
  def testSuccess(self):
186
    data = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
187

    
188
    fn = testutils.CallCounter(compat.partial(self._Success, data))
189

    
190
    result = hv_xen._GetInstanceList(fn, True, _timeout=0.1)
191

    
192
    self.assertEqual(len(result), 4)
193

    
194
    self.assertEqual(map(compat.fst, result), [
195
      "Domain-0",
196
      "server01.example.com",
197
      "web3106215069.example.com",
198
      "testinstance.example.com",
199
      ])
200

    
201
    self.assertEqual(fn.Count(), 1)
202

    
203

    
204
class TestParseNodeInfo(testutils.GanetiTestCase):
205
  def testEmpty(self):
206
    self.assertEqual(hv_xen._ParseNodeInfo(""), {})
207

    
208
  def testUnknownInput(self):
209
    data = "\n".join([
210
      "foo bar",
211
      "something else goes",
212
      "here",
213
      ])
214
    self.assertEqual(hv_xen._ParseNodeInfo(data), {})
215

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

    
228

    
229
class TestMergeInstanceInfo(testutils.GanetiTestCase):
230
  def testEmpty(self):
231
    self.assertEqual(hv_xen._MergeInstanceInfo({}, lambda _: []), {})
232

    
233
  def _FakeXmList(self, include_node):
234
    self.assertTrue(include_node)
235
    return [
236
      (hv_xen._DOM0_NAME, NotImplemented, 4096, 7, NotImplemented,
237
       NotImplemented),
238
      ("inst1.example.com", NotImplemented, 2048, 4, NotImplemented,
239
       NotImplemented),
240
      ]
241

    
242
  def testMissingNodeInfo(self):
243
    result = hv_xen._MergeInstanceInfo({}, self._FakeXmList)
244
    self.assertEqual(result, {
245
      "memory_dom0": 4096,
246
      "dom0_cpus": 7,
247
      })
248

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

    
264

    
265
class TestGetConfigFileDiskData(unittest.TestCase):
266
  def testLetterCount(self):
267
    self.assertEqual(len(hv_xen._DISK_LETTERS), 26)
268

    
269
  def testNoDisks(self):
270
    self.assertEqual(hv_xen._GetConfigFileDiskData([], "hd"), [])
271

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

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

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

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

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

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

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

    
336
    self.assertRaises(KeyError, hv_xen._GetConfigFileDiskData, disks, "sd")
337

    
338

    
339
class TestXenHypervisorRunXen(unittest.TestCase):
340

    
341
  XEN_SUB_CMD = "help"
342

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

    
351
  def testCommandValid(self):
352
    xen_cmd = "xm"
353
    mock_run_cmd = mock.Mock()
354
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
355
                              _run_cmd_fn=mock_run_cmd)
356
    hv._RunXen([self.XEN_SUB_CMD])
357
    mock_run_cmd.assert_called_with([xen_cmd, self.XEN_SUB_CMD])
358

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

    
368

    
369
class TestXenHypervisorGetInstanceList(unittest.TestCase):
370

    
371
  RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
372
  XEN_LIST = "list"
373

    
374
  def testOk(self):
375
    expected_xen_cmd = "xm"
376
    mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
377
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
378
                              _run_cmd_fn=mock_run_cmd)
379
    hv._GetInstanceList(True)
380
    mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
381

    
382
  def testFromHvparams(self):
383
    expected_xen_cmd = "xl"
384
    hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
385
    mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
386
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
387
                              _run_cmd_fn=mock_run_cmd)
388
    hv._GetInstanceList(True, hvparams=hvparams)
389
    mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
390

    
391

    
392
class TestXenHypervisorListInstances(unittest.TestCase):
393

    
394
  RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
395
  XEN_LIST = "list"
396

    
397
  def testDefaultXm(self):
398
    expected_xen_cmd = "xm"
399
    mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
400
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
401
                              _run_cmd_fn=mock_run_cmd)
402
    hv.ListInstances()
403
    mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
404

    
405
  def testHvparamsXl(self):
406
    expected_xen_cmd = "xl"
407
    hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
408
    mock_run_cmd = mock.Mock( return_value=self.RESULT_OK )
409
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
410
                              _run_cmd_fn=mock_run_cmd)
411
    hv.ListInstances(hvparams=hvparams)
412
    mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
413

    
414

    
415
class TestXenHypervisorCheckToolstack(unittest.TestCase):
416

    
417
  def setUp(self):
418
    self.tmpdir = tempfile.mkdtemp()
419
    self.cfg_name = "xen_config"
420
    self.cfg_path = utils.PathJoin(self.tmpdir, self.cfg_name)
421
    self.hv = hv_xen.XenHypervisor()
422

    
423
  def tearDown(self):
424
    shutil.rmtree(self.tmpdir)
425

    
426
  def testBinaryNotFound(self):
427
    RESULT_FAILED = utils.RunResult(1, None, "", "", "", None, None)
428
    mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
429
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
430
                              _run_cmd_fn=mock_run_cmd)
431
    result = hv._CheckToolstackBinary("xl")
432
    self.assertFalse(result)
433

    
434
  def testCheckToolstackXlConfigured(self):
435
    RESULT_OK = utils.RunResult(0, None, "", "", "", None, None)
436
    mock_run_cmd = mock.Mock(return_value=RESULT_OK)
437
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
438
                              _run_cmd_fn=mock_run_cmd)
439
    result = hv._CheckToolstackXlConfigured()
440
    self.assertTrue(result)
441

    
442
  def testCheckToolstackXlNotConfigured(self):
443
    RESULT_FAILED = utils.RunResult(
444
        1, None, "",
445
        "ERROR:  A different toolstack (xm) have been selected!",
446
        "", None, None)
447
    mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
448
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
449
                              _run_cmd_fn=mock_run_cmd)
450
    result = hv._CheckToolstackXlConfigured()
451
    self.assertFalse(result)
452

    
453
  def testCheckToolstackXlFails(self):
454
    RESULT_FAILED = utils.RunResult(
455
        1, None, "",
456
        "ERROR: The pink bunny hid the binary.",
457
        "", None, None)
458
    mock_run_cmd = mock.Mock(return_value=RESULT_FAILED)
459
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
460
                              _run_cmd_fn=mock_run_cmd)
461
    self.assertRaises(errors.HypervisorError, hv._CheckToolstackXlConfigured)
462

    
463

    
464
class TestXenHypervisorWriteConfigFile(unittest.TestCase):
465
  def setUp(self):
466
    self.tmpdir = tempfile.mkdtemp()
467

    
468
  def tearDown(self):
469
    shutil.rmtree(self.tmpdir)
470

    
471
  def testWriteError(self):
472
    cfgdir = utils.PathJoin(self.tmpdir, "foobar")
473

    
474
    hv = hv_xen.XenHypervisor(_cfgdir=cfgdir,
475
                              _run_cmd_fn=NotImplemented,
476
                              _cmd=NotImplemented)
477

    
478
    self.assertFalse(os.path.exists(cfgdir))
479

    
480
    try:
481
      hv._WriteConfigFile("name", "data")
482
    except errors.HypervisorError, err:
483
      self.assertTrue(str(err).startswith("Cannot write Xen instance"))
484
    else:
485
      self.fail("Exception was not raised")
486

    
487

    
488
class TestXenHypervisorVerify(unittest.TestCase):
489

    
490
  def setUp(self):
491
    output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
492
    self._result_ok = utils.RunResult(0, None, output, "", "", None, None)
493

    
494
  def testVerify(self):
495
    hvparams = {constants.HV_XEN_CMD : constants.XEN_CMD_XL}
496
    mock_run_cmd = mock.Mock(return_value=self._result_ok)
497
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
498
                              _run_cmd_fn=mock_run_cmd)
499
    hv._CheckToolstack = mock.Mock(return_value=True)
500
    result = hv.Verify(hvparams)
501
    self.assertTrue(result is None)
502

    
503
  def testVerifyToolstackNotOk(self):
504
    hvparams = {constants.HV_XEN_CMD : constants.XEN_CMD_XL}
505
    mock_run_cmd = mock.Mock(return_value=self._result_ok)
506
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
507
                              _run_cmd_fn=mock_run_cmd)
508
    hv._CheckToolstack = mock.Mock()
509
    hv._CheckToolstack.side_effect = errors.HypervisorError("foo")
510
    result = hv.Verify(hvparams)
511
    self.assertTrue(result is not None)
512

    
513
  def testVerifyFailing(self):
514
    result_failed = utils.RunResult(1, None, "", "", "", None, None)
515
    mock_run_cmd = mock.Mock(return_value=result_failed)
516
    hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented,
517
                              _run_cmd_fn=mock_run_cmd)
518
    hv._CheckToolstack = mock.Mock(return_value=True)
519
    result = hv.Verify()
520
    self.assertTrue(result is not None)
521

    
522

    
523
class _TestXenHypervisor(object):
524
  TARGET = NotImplemented
525
  CMD = NotImplemented
526
  HVNAME = NotImplemented
527

    
528
  def setUp(self):
529
    super(_TestXenHypervisor, self).setUp()
530

    
531
    self.tmpdir = tempfile.mkdtemp()
532

    
533
    self.vncpw = "".join(random.sample(string.ascii_letters, 10))
534

    
535
    self.vncpw_path = utils.PathJoin(self.tmpdir, "vncpw")
536
    utils.WriteFile(self.vncpw_path, data=self.vncpw)
537

    
538
  def tearDown(self):
539
    super(_TestXenHypervisor, self).tearDown()
540

    
541
    shutil.rmtree(self.tmpdir)
542

    
543
  def _GetHv(self, run_cmd=NotImplemented):
544
    return self.TARGET(_cfgdir=self.tmpdir, _run_cmd_fn=run_cmd, _cmd=self.CMD)
545

    
546
  def _SuccessCommand(self, stdout, cmd):
547
    self.assertEqual(cmd[0], self.CMD)
548

    
549
    return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
550
                           NotImplemented, NotImplemented)
551

    
552
  def _FailingCommand(self, cmd):
553
    self.assertEqual(cmd[0], self.CMD)
554

    
555
    return utils.RunResult(constants.EXIT_FAILURE, None,
556
                           "", "This command failed", None,
557
                           NotImplemented, NotImplemented)
558

    
559
  def _FakeTcpPing(self, expected, result, target, port, **kwargs):
560
    self.assertEqual((target, port), expected)
561
    return result
562

    
563
  def testReadingNonExistentConfigFile(self):
564
    hv = self._GetHv()
565

    
566
    try:
567
      hv._ReadConfigFile("inst15780.example.com")
568
    except errors.HypervisorError, err:
569
      self.assertTrue(str(err).startswith("Failed to load Xen config file:"))
570
    else:
571
      self.fail("Exception was not raised")
572

    
573
  def testRemovingAutoConfigFile(self):
574
    name = "inst8206.example.com"
575
    cfgfile = utils.PathJoin(self.tmpdir, name)
576
    autodir = utils.PathJoin(self.tmpdir, "auto")
577
    autocfgfile = utils.PathJoin(autodir, name)
578

    
579
    os.mkdir(autodir)
580

    
581
    utils.WriteFile(autocfgfile, data="")
582

    
583
    hv = self._GetHv()
584

    
585
    self.assertTrue(os.path.isfile(autocfgfile))
586
    hv._WriteConfigFile(name, "content")
587
    self.assertFalse(os.path.exists(autocfgfile))
588
    self.assertEqual(utils.ReadFile(cfgfile), "content")
589

    
590
  def _XenList(self, cmd):
591
    self.assertEqual(cmd, [self.CMD, "list"])
592

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

    
596
    return self._SuccessCommand(output, cmd)
597

    
598
  def testGetInstanceInfo(self):
599
    hv = self._GetHv(run_cmd=self._XenList)
600

    
601
    (name, instid, memory, vcpus, state, runtime) = \
602
      hv.GetInstanceInfo("server01.example.com")
603

    
604
    self.assertEqual(name, "server01.example.com")
605
    self.assertEqual(instid, 1)
606
    self.assertEqual(memory, 1024)
607
    self.assertEqual(vcpus, 1)
608
    self.assertEqual(state, "-b----")
609
    self.assertAlmostEqual(runtime, 167643.2)
610

    
611
  def testGetInstanceInfoDom0(self):
612
    hv = self._GetHv(run_cmd=self._XenList)
613

    
614
    # TODO: Not sure if this is actually used anywhere (can't find it), but the
615
    # code supports querying for Dom0
616
    (name, instid, memory, vcpus, state, runtime) = \
617
      hv.GetInstanceInfo(hv_xen._DOM0_NAME)
618

    
619
    self.assertEqual(name, "Domain-0")
620
    self.assertEqual(instid, 0)
621
    self.assertEqual(memory, 1023)
622
    self.assertEqual(vcpus, 1)
623
    self.assertEqual(state, "r-----")
624
    self.assertAlmostEqual(runtime, 154706.1)
625

    
626
  def testGetInstanceInfoUnknown(self):
627
    hv = self._GetHv(run_cmd=self._XenList)
628

    
629
    result = hv.GetInstanceInfo("unknown.example.com")
630
    self.assertTrue(result is None)
631

    
632
  def testGetAllInstancesInfo(self):
633
    hv = self._GetHv(run_cmd=self._XenList)
634

    
635
    result = hv.GetAllInstancesInfo()
636

    
637
    self.assertEqual(map(compat.fst, result), [
638
      "server01.example.com",
639
      "web3106215069.example.com",
640
      "testinstance.example.com",
641
      ])
642

    
643
  def testListInstances(self):
644
    hv = self._GetHv(run_cmd=self._XenList)
645

    
646
    self.assertEqual(hv.ListInstances(), [
647
      "server01.example.com",
648
      "web3106215069.example.com",
649
      "testinstance.example.com",
650
      ])
651

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

    
661
      if paused:
662
        self.assertEqual(args, ["-p", cfgfile])
663
      else:
664
        self.assertEqual(args, [cfgfile])
665

    
666
      if failcreate:
667
        return self._FailingCommand(cmd)
668

    
669
      output = ""
670
    else:
671
      self.fail("Unhandled command: %s" % (cmd, ))
672

    
673
    return self._SuccessCommand(output, cmd)
674

    
675
  def _MakeInstance(self):
676
    # Copy default parameters
677
    bep = objects.FillDict(constants.BEC_DEFAULTS, {})
678
    hvp = objects.FillDict(constants.HVC_DEFAULTS[self.HVNAME], {})
679

    
680
    # Override default VNC password file path
681
    if constants.HV_VNC_PASSWORD_FILE in hvp:
682
      hvp[constants.HV_VNC_PASSWORD_FILE] = self.vncpw_path
683

    
684
    disks = [
685
      (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDWR),
686
       utils.PathJoin(self.tmpdir, "disk0")),
687
      (objects.Disk(dev_type=constants.LD_LV, mode=constants.DISK_RDONLY),
688
       utils.PathJoin(self.tmpdir, "disk1")),
689
      ]
690

    
691
    inst = objects.Instance(name="server01.example.com",
692
                            hvparams=hvp, beparams=bep,
693
                            osparams={}, nics=[], os="deb1",
694
                            disks=map(compat.fst, disks))
695
    inst.UpgradeConfig()
696

    
697
    return (inst, disks)
698

    
699
  def testStartInstance(self):
700
    (inst, disks) = self._MakeInstance()
701
    pathutils.LOG_XEN_DIR = self.tmpdir
702

    
703
    for failcreate in [False, True]:
704
      for paused in [False, True]:
705
        run_cmd = compat.partial(self._StartInstanceCommand,
706
                                 inst, paused, failcreate)
707

    
708
        hv = self._GetHv(run_cmd=run_cmd)
709

    
710
        # Ensure instance is not listed
711
        self.assertTrue(inst.name not in hv.ListInstances())
712

    
713
        # Remove configuration
714
        cfgfile = utils.PathJoin(self.tmpdir, inst.name)
715
        utils.RemoveFile(cfgfile)
716

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

    
727
        if constants.HV_VNC_PASSWORD_FILE in inst.hvparams:
728
          self.assertTrue(("vncpasswd = '%s'" % self.vncpw) in lines)
729
        else:
730
          extra = inst.hvparams[constants.HV_KERNEL_ARGS]
731
          self.assertTrue(("extra = '%s'" % extra) in lines)
732

    
733
  def _StopInstanceCommand(self, instance_name, force, fail, cmd):
734
    if ((force and cmd[:2] == [self.CMD, "destroy"]) or
735
        (not force and cmd[:2] == [self.CMD, "shutdown"])):
736
      self.assertEqual(cmd[2:], [instance_name])
737
      output = ""
738
    else:
739
      self.fail("Unhandled command: %s" % (cmd, ))
740

    
741
    if fail:
742
      # Simulate a failing command
743
      return self._FailingCommand(cmd)
744
    else:
745
      return self._SuccessCommand(output, cmd)
746

    
747
  def testStopInstance(self):
748
    name = "inst4284.example.com"
749
    cfgfile = utils.PathJoin(self.tmpdir, name)
750
    cfgdata = "config file content\n"
751

    
752
    for force in [False, True]:
753
      for fail in [False, True]:
754
        utils.WriteFile(cfgfile, data=cfgdata)
755

    
756
        run_cmd = compat.partial(self._StopInstanceCommand, name, force, fail)
757

    
758
        hv = self._GetHv(run_cmd=run_cmd)
759

    
760
        self.assertTrue(os.path.isfile(cfgfile))
761

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

    
776
  def _MigrateNonRunningInstCmd(self, cmd):
777
    if cmd == [self.CMD, "list"]:
778
      output = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
779
    else:
780
      self.fail("Unhandled command: %s" % (cmd, ))
781

    
782
    return self._SuccessCommand(output, cmd)
783

    
784
  def testMigrateInstanceNotRunning(self):
785
    name = "nonexistinginstance.example.com"
786
    target = constants.IP4_ADDRESS_LOCALHOST
787
    port = 14618
788

    
789
    hv = self._GetHv(run_cmd=self._MigrateNonRunningInstCmd)
790

    
791
    for live in [False, True]:
792
      try:
793
        hv._MigrateInstance(NotImplemented, name, target, port, live,
794
                            _ping_fn=NotImplemented)
795
      except errors.HypervisorError, err:
796
        self.assertEqual(str(err), "Instance not running, cannot migrate")
797
      else:
798
        self.fail("Exception was not raised")
799

    
800
  def _MigrateInstTargetUnreachCmd(self, cmd):
801
    if cmd == [self.CMD, "list"]:
802
      output = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
803
    else:
804
      self.fail("Unhandled command: %s" % (cmd, ))
805

    
806
    return self._SuccessCommand(output, cmd)
807

    
808
  def testMigrateTargetUnreachable(self):
809
    name = "server01.example.com"
810
    target = constants.IP4_ADDRESS_LOCALHOST
811
    port = 28349
812

    
813
    hv = self._GetHv(run_cmd=self._MigrateInstTargetUnreachCmd)
814

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

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

    
838
        if live:
839
          args.append("-l")
840

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

    
847
      else:
848
        self.fail("Unknown Xen command '%s'" % self.CMD)
849

    
850
      args.extend([instance_name, target])
851
      self.assertEqual(cmd[2:], args)
852

    
853
      if fail:
854
        return self._FailingCommand(cmd)
855

    
856
      output = ""
857
    else:
858
      self.fail("Unhandled command: %s" % (cmd, ))
859

    
860
    return self._SuccessCommand(output, cmd)
861

    
862
  def testMigrateInstance(self):
863
    clustername = "cluster.example.com"
864
    instname = "server01.example.com"
865
    target = constants.IP4_ADDRESS_LOCALHOST
866
    port = 22364
867

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

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

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

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

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

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

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

    
914
    return self._SuccessCommand(output, cmd)
915

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

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

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

    
929

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

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

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

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

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

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

    
954

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

    
961
    assert name not in locals()
962

    
963
    locals()[name] = testcls
964

    
965

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