Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.hypervisor.hv_xen_unittest.py @ cd3b4ff4

History | View | Annotate | Download (31 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-uuid")
55
      node = objects.Node(name="node24828", uuid="node24828-uuid")
56
      cons = cls.GetInstanceConsole(instance, node, hvparams, {})
57
      self.assertTrue(cons.Validate())
58
      self.assertEqual(cons.kind, constants.CONS_SSH)
59
      self.assertEqual(cons.host, node.name)
60
      self.assertEqual(cons.command[-1], instance.name)
61

    
62

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

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

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

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

    
81

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

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

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

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

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

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

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

    
114

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

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

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

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

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

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

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

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

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

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

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

    
159

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

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

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

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

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

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

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

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

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

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

    
200

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

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

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

    
225

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

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

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

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

    
262

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

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

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

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

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

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

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

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

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

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

    
336

    
337
class TestXenHypervisorRunXen(unittest.TestCase):
338

    
339
  XEN_SUB_CMD = "help"
340

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

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

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

    
365

    
366
class TestXenHypervisorGetInstanceList(unittest.TestCase):
367

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

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

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

    
387

    
388
class TestXenHypervisorListInstances(unittest.TestCase):
389

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

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

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

    
409

    
410
class TestXenHypervisorCheckToolstack(unittest.TestCase):
411

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

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

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

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

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

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

    
458

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

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

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

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

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

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

    
482

    
483
class TestXenHypervisorVerify(unittest.TestCase):
484

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

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

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

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

    
517

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

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

    
527
    self.tmpdir = tempfile.mkdtemp()
528

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

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

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

    
537
    shutil.rmtree(self.tmpdir)
538

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

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

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

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

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

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

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

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

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

    
575
    os.mkdir(autodir)
576

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

    
579
    hv = self._GetHv()
580

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

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

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

    
592
    return self._SuccessCommand(output, cmd)
593

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

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

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

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

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

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

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

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

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

    
631
    result = hv.GetAllInstancesInfo()
632

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

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

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

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

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

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

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

    
669
    return self._SuccessCommand(output, cmd)
670

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

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

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

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

    
693
    return (inst, disks)
694

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
778
    return self._SuccessCommand(output, cmd)
779

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

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

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

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

    
802
    return self._SuccessCommand(output, cmd)
803

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

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

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

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

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

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

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

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

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

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

    
858
    return self._SuccessCommand(output, cmd)
859

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

    
866
    hvparams = {constants.HV_XEN_CMD: self.CMD}
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
                                hvparams, _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
                              hvparams, _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()