Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.hypervisor.hv_xen_unittest.py @ 8351df2f

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
    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
  VALID_HVPARAMS = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
528

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

    
532
    self.tmpdir = tempfile.mkdtemp()
533

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

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

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

    
542
    shutil.rmtree(self.tmpdir)
543

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

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

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

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

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

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

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

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

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

    
580
    os.mkdir(autodir)
581

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

    
584
    hv = self._GetHv()
585

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

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

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

    
597
    return self._SuccessCommand(output, cmd)
598

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

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

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

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

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

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

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

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

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

    
636
    result = hv.GetAllInstancesInfo()
637

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

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

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

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

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

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

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

    
674
    return self._SuccessCommand(output, cmd)
675

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

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

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

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

    
698
    return (inst, disks)
699

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
783
    return self._SuccessCommand(output, cmd)
784

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

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

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

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

    
807
    return self._SuccessCommand(output, cmd)
808

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

    
814
    hv = self._GetHv(run_cmd=self._MigrateInstTargetUnreachCmd)
815
    hvparams = {constants.HV_XEN_CMD: self.CMD}
816

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

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

    
841
        if live:
842
          args.append("-l")
843

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

    
850
      else:
851
        self.fail("Unknown Xen command '%s'" % self.CMD)
852

    
853
      args.extend([instance_name, target])
854
      self.assertEqual(cmd[2:], args)
855

    
856
      if fail:
857
        return self._FailingCommand(cmd)
858

    
859
      output = ""
860
    else:
861
      self.fail("Unhandled command: %s" % (cmd, ))
862

    
863
    return self._SuccessCommand(output, cmd)
864

    
865
  def testMigrateInstance(self):
866
    clustername = "cluster.example.com"
867
    instname = "server01.example.com"
868
    target = constants.IP4_ADDRESS_LOCALHOST
869
    port = 22364
870

    
871
    hvparams = {constants.HV_XEN_CMD: self.CMD}
872

    
873
    for live in [False, True]:
874
      for fail in [False, True]:
875
        ping_fn = \
876
          testutils.CallCounter(compat.partial(self._FakeTcpPing,
877
                                               (target, port), True))
878

    
879
        run_cmd = \
880
          compat.partial(self._MigrateInstanceCmd,
881
                         clustername, instname, target, port, live,
882
                         fail)
883

    
884
        hv = self._GetHv(run_cmd=run_cmd)
885

    
886
        if fail:
887
          try:
888
            hv._MigrateInstance(clustername, instname, target, port, live,
889
                                hvparams, _ping_fn=ping_fn)
890
          except errors.HypervisorError, err:
891
            self.assertTrue(str(err).startswith("Failed to migrate instance"))
892
          else:
893
            self.fail("Exception was not raised")
894
        else:
895
          hv._MigrateInstance(clustername, instname, target, port, live,
896
                              hvparams, _ping_fn=ping_fn)
897

    
898
        if self.CMD == constants.XEN_CMD_XM:
899
          expected_pings = 1
900
        else:
901
          expected_pings = 0
902

    
903
        self.assertEqual(ping_fn.Count(), expected_pings)
904

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

    
919
    return self._SuccessCommand(output, cmd)
920

    
921
  def testGetNodeInfo(self):
922
    run_cmd = compat.partial(self._GetNodeInfoCmd, False)
923
    hv = self._GetHv(run_cmd=run_cmd)
924
    result = hv.GetNodeInfo()
925

    
926
    self.assertEqual(result["hv_version"], (4, 0))
927
    self.assertEqual(result["memory_free"], 8004)
928

    
929
  def testGetNodeInfoFailing(self):
930
    run_cmd = compat.partial(self._GetNodeInfoCmd, True)
931
    hv = self._GetHv(run_cmd=run_cmd)
932
    self.assertTrue(hv.GetNodeInfo() is None)
933

    
934

    
935
def _MakeTestClass(cls, cmd):
936
  """Makes a class for testing.
937

938
  The returned class has structure as shown in the following pseudo code:
939

940
    class Test{cls.__name__}{cmd}(_TestXenHypervisor, unittest.TestCase):
941
      TARGET = {cls}
942
      CMD = {cmd}
943
      HVNAME = {Hypervisor name retrieved using class}
944

945
  @type cls: class
946
  @param cls: Hypervisor class to be tested
947
  @type cmd: string
948
  @param cmd: Hypervisor command
949
  @rtype: tuple
950
  @return: Class name and class object (not instance)
951

952
  """
953
  name = "Test%sCmd%s" % (cls.__name__, cmd.title())
954
  bases = (_TestXenHypervisor, unittest.TestCase)
955
  hvname = HVCLASS_TO_HVNAME[cls]
956

    
957
  return (name, type(name, bases, dict(TARGET=cls, CMD=cmd, HVNAME=hvname)))
958

    
959

    
960
# Create test classes programmatically instead of manually to reduce the risk
961
# of forgetting some combinations
962
for cls in [hv_xen.XenPvmHypervisor, hv_xen.XenHvmHypervisor]:
963
  for cmd in constants.KNOWN_XEN_COMMANDS:
964
    (name, testcls) = _MakeTestClass(cls, cmd)
965

    
966
    assert name not in locals()
967

    
968
    locals()[name] = testcls
969

    
970

    
971
if __name__ == "__main__":
972
  testutils.GanetiTestProgram()