Revision b98bf262

b/lib/cmdlib.py
33 33
import platform
34 34
import logging
35 35
import copy
36
import OpenSSL
36 37

  
37 38
from ganeti import ssh
38 39
from ganeti import utils
......
848 849
  return faulty
849 850

  
850 851

  
852
def _FormatTimestamp(secs):
853
  """Formats a Unix timestamp with the local timezone.
854

  
855
  """
856
  return time.strftime("%F %T %Z", time.gmtime(secs))
857

  
858

  
851 859
class LUPostInitCluster(LogicalUnit):
852 860
  """Logical unit for running hooks after cluster initialization.
853 861

  
......
939 947
    return master
940 948

  
941 949

  
950
def _VerifyCertificateInner(filename, expired, not_before, not_after, now,
951
                            warn_days=constants.SSL_CERT_EXPIRATION_WARN,
952
                            error_days=constants.SSL_CERT_EXPIRATION_ERROR):
953
  """Verifies certificate details for LUVerifyCluster.
954

  
955
  """
956
  if expired:
957
    msg = "Certificate %s is expired" % filename
958

  
959
    if not_before is not None and not_after is not None:
960
      msg += (" (valid from %s to %s)" %
961
              (_FormatTimestamp(not_before),
962
               _FormatTimestamp(not_after)))
963
    elif not_before is not None:
964
      msg += " (valid from %s)" % _FormatTimestamp(not_before)
965
    elif not_after is not None:
966
      msg += " (valid until %s)" % _FormatTimestamp(not_after)
967

  
968
    return (LUVerifyCluster.ETYPE_ERROR, msg)
969

  
970
  elif not_before is not None and not_before > now:
971
    return (LUVerifyCluster.ETYPE_WARNING,
972
            "Certificate %s not yet valid (valid from %s)" %
973
            (filename, _FormatTimestamp(not_before)))
974

  
975
  elif not_after is not None:
976
    remaining_days = int((not_after - now) / (24 * 3600))
977

  
978
    msg = ("Certificate %s expires in %d days" % (filename, remaining_days))
979

  
980
    if remaining_days <= error_days:
981
      return (LUVerifyCluster.ETYPE_ERROR, msg)
982

  
983
    if remaining_days <= warn_days:
984
      return (LUVerifyCluster.ETYPE_WARNING, msg)
985

  
986
  return (None, None)
987

  
988

  
989
def _VerifyCertificate(filename):
990
  """Verifies a certificate for LUVerifyCluster.
991

  
992
  @type filename: string
993
  @param filename: Path to PEM file
994

  
995
  """
996
  try:
997
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
998
                                           utils.ReadFile(filename))
999
  except Exception, err: # pylint: disable-msg=W0703
1000
    return (LUVerifyCluster.ETYPE_ERROR,
1001
            "Failed to load X509 certificate %s: %s" % (filename, err))
1002

  
1003
  # Depending on the pyOpenSSL version, this can just return (None, None)
1004
  (not_before, not_after) = utils.GetX509CertValidity(cert)
1005

  
1006
  return _VerifyCertificateInner(filename, cert.has_expired(),
1007
                                 not_before, not_after, time.time())
1008

  
1009

  
942 1010
class LUVerifyCluster(LogicalUnit):
943 1011
  """Verifies the cluster status.
944 1012

  
......
953 1021
  TINSTANCE = "instance"
954 1022

  
955 1023
  ECLUSTERCFG = (TCLUSTER, "ECLUSTERCFG")
1024
  ECLUSTERCERT = (TCLUSTER, "ECLUSTERCERT")
956 1025
  EINSTANCEBADNODE = (TINSTANCE, "EINSTANCEBADNODE")
957 1026
  EINSTANCEDOWN = (TINSTANCE, "EINSTANCEDOWN")
958 1027
  EINSTANCELAYOUT = (TINSTANCE, "EINSTANCELAYOUT")
......
1315 1384
    for msg in self.cfg.VerifyConfig():
1316 1385
      _ErrorIf(True, self.ECLUSTERCFG, None, msg)
1317 1386

  
1387
    # Check the cluster certificates
1388
    for cert_filename in constants.ALL_CERT_FILES:
1389
      (errcode, msg) = _VerifyCertificate(cert_filename)
1390
      _ErrorIf(errcode, self.ECLUSTERCERT, None, msg, code=errcode)
1391

  
1318 1392
    vg_name = self.cfg.GetVGName()
1319 1393
    hypervisors = self.cfg.GetClusterInfo().enabled_hypervisors
1320 1394
    nodelist = utils.NiceSort(self.cfg.GetNodeList())
b/lib/constants.py
582 582
NV_NODESETUP = "nodesetup"
583 583
NV_TIME = "time"
584 584

  
585
# SSL certificate check constants (in days)
586
SSL_CERT_EXPIRATION_WARN = 30
587
SSL_CERT_EXPIRATION_ERROR = 7
588

  
585 589
# Allocator framework constants
586 590
IALLOCATOR_VERSION = 2
587 591
IALLOCATOR_DIR_IN = "in"
b/test/ganeti.cmdlib_unittest.py
25 25
import os
26 26
import unittest
27 27
import time
28
import Queue
28
import tempfile
29
import shutil
29 30

  
30 31
from ganeti import cmdlib
31 32
from ganeti import errors
......
33 34
import testutils
34 35

  
35 36

  
36
if __name__ == '__main__':
37
class TestCertVerification(testutils.GanetiTestCase):
38
  def setUp(self):
39
    testutils.GanetiTestCase.setUp(self)
40

  
41
    self.tmpdir = tempfile.mkdtemp()
42

  
43
  def tearDown(self):
44
    shutil.rmtree(self.tmpdir)
45

  
46
  def testVerifyCertificate(self):
47
    cmdlib._VerifyCertificate(self._TestDataFilename("cert1.pem"))
48

  
49
    nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
50

  
51
    (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
52
    self.assertEqual(errcode, cmdlib.LUVerifyCluster.ETYPE_ERROR)
53

  
54
    # Try to load non-certificate file
55
    invalid_cert = self._TestDataFilename("bdev-net1.txt")
56
    (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
57
    self.assertEqual(errcode, cmdlib.LUVerifyCluster.ETYPE_ERROR)
58

  
59

  
60
class TestVerifyCertificateInner(unittest.TestCase):
61
  FAKEFILE = "/tmp/fake/cert/file.pem"
62

  
63
  def test(self):
64
    vci = cmdlib._VerifyCertificateInner
65

  
66
    # Valid
67
    self.assertEqual(vci(self.FAKEFILE, False, 1263916313, 1298476313,
68
                         1266940313, warn_days=30, error_days=7),
69
                     (None, None))
70

  
71
    # Not yet valid
72
    (errcode, msg) = vci(self.FAKEFILE, False, 1266507600, 1267544400,
73
                         1266075600, warn_days=30, error_days=7)
74
    self.assertEqual(errcode, cmdlib.LUVerifyCluster.ETYPE_WARNING)
75

  
76
    # Expiring soon
77
    (errcode, msg) = vci(self.FAKEFILE, False, 1266507600, 1267544400,
78
                         1266939600, warn_days=30, error_days=7)
79
    self.assertEqual(errcode, cmdlib.LUVerifyCluster.ETYPE_ERROR)
80

  
81
    (errcode, msg) = vci(self.FAKEFILE, False, 1266507600, 1267544400,
82
                         1266939600, warn_days=30, error_days=1)
83
    self.assertEqual(errcode, cmdlib.LUVerifyCluster.ETYPE_WARNING)
84

  
85
    (errcode, msg) = vci(self.FAKEFILE, False, 1266507600, None,
86
                         1266939600, warn_days=30, error_days=7)
87
    self.assertEqual(errcode, None)
88

  
89
    # Expired
90
    (errcode, msg) = vci(self.FAKEFILE, True, 1266507600, 1267544400,
91
                         1266939600, warn_days=30, error_days=7)
92
    self.assertEqual(errcode, cmdlib.LUVerifyCluster.ETYPE_ERROR)
93

  
94
    (errcode, msg) = vci(self.FAKEFILE, True, None, 1267544400,
95
                         1266939600, warn_days=30, error_days=7)
96
    self.assertEqual(errcode, cmdlib.LUVerifyCluster.ETYPE_ERROR)
97

  
98
    (errcode, msg) = vci(self.FAKEFILE, True, 1266507600, None,
99
                         1266939600, warn_days=30, error_days=7)
100
    self.assertEqual(errcode, cmdlib.LUVerifyCluster.ETYPE_ERROR)
101

  
102
    (errcode, msg) = vci(self.FAKEFILE, True, None, None,
103
                         1266939600, warn_days=30, error_days=7)
104
    self.assertEqual(errcode, cmdlib.LUVerifyCluster.ETYPE_ERROR)
105

  
106

  
107
if __name__ == "__main__":
37 108
  testutils.GanetiTestProgram()
b/test/ganeti.constants_unittest.py
64 64
    self.failUnless(constants.NODE_MAX_CLOCK_SKEW <
65 65
                    (0.8 * constants.CONFD_MAX_CLOCK_SKEW))
66 66

  
67
  def testSslCertExpiration(self):
68
    self.failUnless(constants.SSL_CERT_EXPIRATION_ERROR <
69
                    constants.SSL_CERT_EXPIRATION_WARN)
70

  
67 71

  
68 72
class TestParameterNames(unittest.TestCase):
69 73
  """HV/BE parameter tests"""

Also available in: Unified diff