Revision 75bf3149

b/lib/backend.py
726 726
  return res
727 727

  
728 728

  
729
def _VerifyHypervisors(what, vm_capable, result, all_hvparams,
730
                       get_hv_fn=hypervisor.GetHypervisor):
731
  """Verifies the hypervisor. Appends the results to the 'results' list.
732

  
733
  @type what: C{dict}
734
  @param what: a dictionary of things to check
735
  @type vm_capable: boolean
736
  @param vm_capable: whether or not this not is vm capable
737
  @type result: dict
738
  @param result: dictionary of verification results; results of the
739
    verifications in this function will be added here
740
  @type all_hvparams: dict of dict of string
741
  @param all_hvparams: dictionary mapping hypervisor names to hvparams
742
  @type get_hv_fn: function
743
  @param get_hv_fn: function to retrieve the hypervisor, to improve testability
744

  
745
  """
746
  if not vm_capable:
747
    return
748

  
749
  if constants.NV_HYPERVISOR in what:
750
    result[constants.NV_HYPERVISOR] = {}
751
    for hv_name in what[constants.NV_HYPERVISOR]:
752
      hvparams = all_hvparams[hv_name]
753
      try:
754
        val = get_hv_fn(hv_name).Verify(hvparams=hvparams)
755
      except errors.HypervisorError, err:
756
        val = "Error while checking hypervisor: %s" % str(err)
757
      result[constants.NV_HYPERVISOR][hv_name] = val
758

  
759

  
760
def _VerifyHvparams(what, vm_capable, result,
761
                    get_hv_fn=hypervisor.GetHypervisor):
762
  """Verifies the hvparams. Appends the results to the 'results' list.
763

  
764
  @type what: C{dict}
765
  @param what: a dictionary of things to check
766
  @type vm_capable: boolean
767
  @param vm_capable: whether or not this not is vm capable
768
  @type result: dict
769
  @param result: dictionary of verification results; results of the
770
    verifications in this function will be added here
771
  @type get_hv_fn: function
772
  @param get_hv_fn: function to retrieve the hypervisor, to improve testability
773

  
774
  """
775
  if not vm_capable:
776
    return
777

  
778
  if constants.NV_HVPARAMS in what:
779
    result[constants.NV_HVPARAMS] = []
780
    for source, hv_name, hvparms in what[constants.NV_HVPARAMS]:
781
      try:
782
        logging.info("Validating hv %s, %s", hv_name, hvparms)
783
        get_hv_fn(hv_name).ValidateParameters(hvparms)
784
      except errors.HypervisorError, err:
785
        result[constants.NV_HVPARAMS].append((source, hv_name, str(err)))
786

  
787

  
729 788
def VerifyNode(what, cluster_name):
730 789
  """Verify the status of the local node.
731 790

  
......
750 809
      - node-net-test: list of nodes we should check node daemon port
751 810
        connectivity with
752 811
      - hypervisor: list with hypervisors to run the verify for
812

  
753 813
  @rtype: dict
754 814
  @return: a dictionary with the same keys as the input dict, and
755 815
      values representing the result of the checks
......
760 820
  port = netutils.GetDaemonPort(constants.NODED)
761 821
  vm_capable = my_name not in what.get(constants.NV_VMNODES, [])
762 822

  
763
  if constants.NV_HYPERVISOR in what and vm_capable:
764
    result[constants.NV_HYPERVISOR] = tmp = {}
765
    for hv_name in what[constants.NV_HYPERVISOR]:
766
      try:
767
        val = hypervisor.GetHypervisor(hv_name).Verify()
768
      except errors.HypervisorError, err:
769
        val = "Error while checking hypervisor: %s" % str(err)
770
      tmp[hv_name] = val
771

  
772
  if constants.NV_HVPARAMS in what and vm_capable:
773
    result[constants.NV_HVPARAMS] = tmp = []
774
    for source, hv_name, hvparms in what[constants.NV_HVPARAMS]:
775
      try:
776
        logging.info("Validating hv %s, %s", hv_name, hvparms)
777
        hypervisor.GetHypervisor(hv_name).ValidateParameters(hvparms)
778
      except errors.HypervisorError, err:
779
        tmp.append((source, hv_name, str(err)))
823
  _VerifyHypervisors(what, vm_capable, result, all_hvparams)
824
  _VerifyHvparams(what, vm_capable, result)
780 825

  
781 826
  if constants.NV_FILELIST in what:
782 827
    fingerprints = utils.FingerprintFiles(map(vcluster.LocalizeVirtualPath,
b/lib/hypervisor/hv_base.py
263 263

  
264 264
    return (cls.ANCILLARY_FILES, cls.ANCILLARY_FILES_OPT)
265 265

  
266
  def Verify(self):
266
  def Verify(self, hvparams=None):
267 267
    """Verify the hypervisor.
268 268

  
269
    @type hvparams: dict of strings
270
    @param hvparams: hypervisor parameters to be verified against
271

  
269 272
    @return: Problem description if something is wrong, C{None} otherwise
270 273

  
271 274
    """
b/lib/hypervisor/hv_chroot.py
276 276
                                   user=constants.SSH_CONSOLE_USER,
277 277
                                   command=["chroot", root_dir])
278 278

  
279
  def Verify(self):
279
  def Verify(self, hvparams=None):
280 280
    """Verify the hypervisor.
281 281

  
282 282
    For the chroot manager, it just checks the existence of the base dir.
283 283

  
284
    @type hvparams: dict of strings
285
    @param hvparams: hypervisor parameters to be verified against, not used
286
      in for chroot
287

  
284 288
    @return: Problem description if something is wrong, C{None} otherwise
285 289

  
286 290
    """
b/lib/hypervisor/hv_fake.py
233 233
                                   message=("Console not available for fake"
234 234
                                            " hypervisor"))
235 235

  
236
  def Verify(self):
236
  def Verify(self, hvparams=None):
237 237
    """Verify the hypervisor.
238 238

  
239 239
    For the fake hypervisor, it just checks the existence of the base
240 240
    dir.
241 241

  
242
    @type hvparams: dict of strings
243
    @param hvparams: hypervisor parameters to be verified against; not used
244
      for fake hypervisors
245

  
242 246
    @return: Problem description if something is wrong, C{None} otherwise
243 247

  
244 248
    """
b/lib/hypervisor/hv_kvm.py
2044 2044
                                   message=("No serial shell for instance %s" %
2045 2045
                                            instance.name))
2046 2046

  
2047
  def Verify(self):
2047
  def Verify(self, hvparams=None):
2048 2048
    """Verify the hypervisor.
2049 2049

  
2050 2050
    Check that the required binaries exist.
2051 2051

  
2052
    @type hvparams: dict of strings
2053
    @param hvparams: hypervisor parameters to be verified against, not used here
2054

  
2052 2055
    @return: Problem description if something is wrong, C{None} otherwise
2053 2056

  
2054 2057
    """
b/lib/hypervisor/hv_lxc.py
417 417
                                   user=constants.SSH_CONSOLE_USER,
418 418
                                   command=["lxc-console", "-n", instance.name])
419 419

  
420
  def Verify(self):
420
  def Verify(self, hvparams=None):
421 421
    """Verify the hypervisor.
422 422

  
423 423
    For the LXC manager, it just checks the existence of the base dir.
424 424

  
425
    @type hvparams: dict of strings
426
    @param hvparams: hypervisor parameters to be verified against; not used here
427

  
425 428
    @return: Problem description if something is wrong, C{None} otherwise
426 429

  
427 430
    """
b/lib/hypervisor/hv_xen.py
630 630
                                   command=[pathutils.XEN_CONSOLE_WRAPPER,
631 631
                                            constants.XEN_CMD, instance.name])
632 632

  
633
  def Verify(self):
633
  def Verify(self, hvparams=None):
634 634
    """Verify the hypervisor.
635 635

  
636 636
    For Xen, this verifies that the xend process is running.
637 637

  
638
    @type hvparams: dict of strings
639
    @param hvparams: hypervisor parameters to be verified against
640

  
638 641
    @return: Problem description if something is wrong, C{None} otherwise
639 642

  
640 643
    """
641
    result = self._RunXen(["info"])
644
    if hvparams is None:
645
      return "Could not verify the hypervisor, because no hvparams were" \
646
             " provided."
647

  
648
    if constants.HV_XEN_CMD in hvparams:
649
      xen_cmd = hvparams[constants.HV_XEN_CMD]
650
      try:
651
        self._CheckToolstack(xen_cmd)
652
      except errors.HypervisorError:
653
        return "The configured xen toolstack '%s' is not available on this" \
654
               " node." % xen_cmd
655

  
656
    result = self._RunXen(["info"], hvparams=hvparams)
642 657
    if result.failed:
643
      return "'xm info' failed: %s, %s" % (result.fail_reason, result.output)
658
      return "Retrieving information from xen failed: %s, %s" % \
659
        (result.fail_reason, result.output)
644 660

  
645 661
    return None
646 662

  
......
800 816
    finally:
801 817
      utils.RunCmd([constants.XEN_CMD, "debug", "R"])
802 818

  
819
  def _CheckToolstack(self, xen_cmd):
820
    """Check whether the given toolstack is available on the node.
821

  
822
    @type xen_cmd: string
823
    @param xen_cmd: xen command (e.g. 'xm' or 'xl')
824

  
825
    """
826
    binary_found = self._CheckToolstackBinary(xen_cmd)
827
    if not binary_found:
828
      raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd)
829
    elif xen_cmd == constants.XEN_CMD_XL:
830
      if not self._CheckToolstackXlConfigured():
831
        raise errors.HypervisorError("Toolstack '%s' is not enabled on this"
832
                                     "node." % xen_cmd)
833

  
834
  def _CheckToolstackBinary(self, xen_cmd):
835
    """Checks whether the xen command's binary is found on the machine.
836

  
837
    """
838
    if xen_cmd not in constants.KNOWN_XEN_COMMANDS:
839
      raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd)
840
    result = self._run_cmd_fn(["which", xen_cmd])
841
    return not result.failed
842

  
843
  def _CheckToolstackXlConfigured(self):
844
    """Checks whether xl is enabled on an xl-capable node.
845

  
846
    @rtype: bool
847
    @returns: C{True} if 'xl' is enabled, C{False} otherwise
848

  
849
    """
850
    result = self._run_cmd_fn([constants.XEN_CMD_XL, "help"])
851
    if not result.failed:
852
      return True
853
    elif result.failed:
854
      if "toolstack" in result.stderr:
855
        return False
856
      # xl fails for some other reason than the toolstack
857
      else:
858
        raise errors.HypervisorError("Cannot run xen ('%s'). Error: %s."
859
                                     % (constants.XEN_CMD_XL, result.stderr))
860

  
803 861

  
804 862
class XenPvmHypervisor(XenHypervisor):
805 863
  """Xen PVM hypervisor interface"""
b/lib/server/noded.py
738 738
    """Run a verify sequence on this node.
739 739

  
740 740
    """
741
    return backend.VerifyNode(params[0], params[1])
741
    (what, cluster_name) = params
742
    return backend.VerifyNode(what, cluster_name)
742 743

  
743 744
  @classmethod
744 745
  def perspective_node_verify_light(cls, params):
b/test/py/ganeti.backend_unittest.py
77 77

  
78 78

  
79 79
class TestNodeVerify(testutils.GanetiTestCase):
80

  
81
  def setUp(self):
82
    testutils.GanetiTestCase.setUp(self)
83
    self._mock_hv = None
84

  
85
  def _GetHypervisor(self, hv_name):
86
    self._mock_hv = hypervisor.GetHypervisor(hv_name)
87
    self._mock_hv.ValidateParameters = mock.Mock()
88
    self._mock_hv.Verify = mock.Mock()
89
    return self._mock_hv
90

  
80 91
  def testMasterIPLocalhost(self):
81 92
    # this a real functional test, but requires localhost to be reachable
82 93
    local_data = (netutils.Hostname.GetSysName(),
83 94
                  constants.IP4_ADDRESS_LOCALHOST)
84
    result = backend.VerifyNode({constants.NV_MASTERIP: local_data}, None)
95
    result = backend.VerifyNode({constants.NV_MASTERIP: local_data}, None, {})
85 96
    self.failUnless(constants.NV_MASTERIP in result,
86 97
                    "Master IP data not returned")
87 98
    self.failUnless(result[constants.NV_MASTERIP], "Cannot reach localhost")
......
92 103
    bad_data =  ("master.example.com", "192.0.2.1")
93 104
    # we just test that whatever TcpPing returns, VerifyNode returns too
94 105
    netutils.TcpPing = lambda a, b, source=None: False
95
    result = backend.VerifyNode({constants.NV_MASTERIP: bad_data}, None)
106
    result = backend.VerifyNode({constants.NV_MASTERIP: bad_data}, None, {})
96 107
    self.failUnless(constants.NV_MASTERIP in result,
97 108
                    "Master IP data not returned")
98 109
    self.failIf(result[constants.NV_MASTERIP],
99 110
                "Result from netutils.TcpPing corrupted")
100 111

  
112
  def testVerifyHvparams(self):
113
    test_hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
114
    test_what = {constants.NV_HVPARAMS: \
115
        [("mynode", constants.HT_XEN_PVM, test_hvparams)]}
116
    result = {}
117
    backend._VerifyHvparams(test_what, True, result,
118
                            get_hv_fn=self._GetHypervisor)
119
    self._mock_hv.ValidateParameters.assert_called_with(test_hvparams)
120

  
121
  def testVerifyHypervisors(self):
122
    hvname = constants.HT_XEN_PVM
123
    hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL}
124
    all_hvparams = {hvname: hvparams}
125
    test_what = {constants.NV_HYPERVISOR: [hvname]}
126
    result = {}
127
    backend._VerifyHypervisors(
128
        test_what, True, result, all_hvparams=all_hvparams,
129
        get_hv_fn=self._GetHypervisor)
130
    self._mock_hv.Verify.assert_called_with(hvparams=hvparams)
131

  
101 132

  
102 133
def _DefRestrictedCmdOwner():
103 134
  return (os.getuid(), os.getgid())
b/test/py/ganeti.hypervisor.hv_xen_unittest.py
412 412
    mock_run_cmd.assert_called_with([expected_xen_cmd, self.XEN_LIST])
413 413

  
414 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

  
415 464
class TestXenHypervisorWriteConfigFile(unittest.TestCase):
416 465
  def setUp(self):
417 466
    self.tmpdir = tempfile.mkdtemp()
......
436 485
      self.fail("Exception was not raised")
437 486

  
438 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

  
439 523
class _TestXenHypervisor(object):
440 524
  TARGET = NotImplemented
441 525
  CMD = NotImplemented
......
565 649
      "testinstance.example.com",
566 650
      ])
567 651

  
568
  def testVerify(self):
569
    output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
570
    hv = self._GetHv(run_cmd=compat.partial(self._SuccessCommand,
571
                                            output))
572
    self.assertTrue(hv.Verify() is None)
573

  
574
  def testVerifyFailing(self):
575
    hv = self._GetHv(run_cmd=self._FailingCommand)
576
    self.assertTrue("failed:" in hv.Verify())
577

  
578 652
  def _StartInstanceCommand(self, inst, paused, failcreate, cmd):
579 653
    if cmd == [self.CMD, "info"]:
580 654
      output = testutils.ReadTestData("xen-xm-info-4.0.1.txt")

Also available in: Unified diff