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