Revision f942a838
b/Makefile.am | ||
---|---|---|
322 | 322 |
test/data/proc_drbd83.txt |
323 | 323 |
|
324 | 324 |
python_tests = \ |
325 |
test/ganeti.backend_unittest.py \ |
|
325 | 326 |
test/ganeti.bdev_unittest.py \ |
326 | 327 |
test/ganeti.cli_unittest.py \ |
327 | 328 |
test/ganeti.cmdlib_unittest.py \ |
b/daemons/ganeti-noded | ||
---|---|---|
820 | 820 |
(hvname, hvparams) = params |
821 | 821 |
return backend.ValidateHVParams(hvname, hvparams) |
822 | 822 |
|
823 |
# Crypto |
|
824 |
|
|
825 |
@staticmethod |
|
826 |
def perspective_create_x509_certificate(params): |
|
827 |
"""Creates a new X509 certificate for SSL/TLS. |
|
828 |
|
|
829 |
""" |
|
830 |
(validity, ) = params |
|
831 |
return backend.CreateX509Certificate(validity) |
|
832 |
|
|
833 |
@staticmethod |
|
834 |
def perspective_remove_x509_certificate(params): |
|
835 |
"""Removes a X509 certificate. |
|
836 |
|
|
837 |
""" |
|
838 |
(name, ) = params |
|
839 |
return backend.RemoveX509Certificate(name) |
|
840 |
|
|
823 | 841 |
|
824 | 842 |
def CheckNoded(_, args): |
825 | 843 |
"""Initial checks whether to run or exit with a failure. |
... | ... | |
870 | 888 |
dirs = [(val, constants.RUN_DIRS_MODE) for val in constants.SUB_RUN_DIRS] |
871 | 889 |
dirs.append((constants.LOG_OS_DIR, 0750)) |
872 | 890 |
dirs.append((constants.LOCK_DIR, 1777)) |
891 |
dirs.append((constants.CRYPTO_KEYS_DIR, constants.CRYPTO_KEYS_DIR_MODE)) |
|
873 | 892 |
daemon.GenericMain(constants.NODED, parser, dirs, CheckNoded, ExecNoded, |
874 | 893 |
default_ssl_cert=constants.NODED_CERT_FILE, |
875 | 894 |
default_ssl_key=constants.NODED_CERT_FILE) |
b/lib/backend.py | ||
---|---|---|
63 | 63 |
constants.DATA_DIR, |
64 | 64 |
constants.JOB_QUEUE_ARCHIVE_DIR, |
65 | 65 |
constants.QUEUE_DIR, |
66 |
constants.CRYPTO_KEYS_DIR, |
|
66 | 67 |
]) |
68 |
_MAX_SSL_CERT_VALIDITY = 7 * 24 * 60 * 60 |
|
69 |
_X509_KEY_FILE = "key" |
|
70 |
_X509_CERT_FILE = "cert" |
|
67 | 71 |
|
68 | 72 |
|
69 | 73 |
class RPCFail(Exception): |
... | ... | |
385 | 389 |
|
386 | 390 |
""" |
387 | 391 |
_CleanDirectory(constants.DATA_DIR) |
392 |
_CleanDirectory(constants.CRYPTO_KEYS_DIR) |
|
388 | 393 |
JobQueuePurge() |
389 | 394 |
|
390 | 395 |
if modify_ssh_setup: |
... | ... | |
2510 | 2515 |
utils.RemoveFile(constants.CLUSTER_CONF_FILE) |
2511 | 2516 |
|
2512 | 2517 |
|
2518 |
def _GetX509Filenames(cryptodir, name): |
|
2519 |
"""Returns the full paths for the private key and certificate. |
|
2520 |
|
|
2521 |
""" |
|
2522 |
return (utils.PathJoin(cryptodir, name), |
|
2523 |
utils.PathJoin(cryptodir, name, _X509_KEY_FILE), |
|
2524 |
utils.PathJoin(cryptodir, name, _X509_CERT_FILE)) |
|
2525 |
|
|
2526 |
|
|
2527 |
def CreateX509Certificate(validity, cryptodir=constants.CRYPTO_KEYS_DIR): |
|
2528 |
"""Creates a new X509 certificate for SSL/TLS. |
|
2529 |
|
|
2530 |
@type validity: int |
|
2531 |
@param validity: Validity in seconds |
|
2532 |
@rtype: tuple; (string, string) |
|
2533 |
@return: Certificate name and public part |
|
2534 |
|
|
2535 |
""" |
|
2536 |
(key_pem, cert_pem) = \ |
|
2537 |
utils.GenerateSelfSignedX509Cert(utils.HostInfo.SysName(), |
|
2538 |
min(validity, _MAX_SSL_CERT_VALIDITY)) |
|
2539 |
|
|
2540 |
cert_dir = tempfile.mkdtemp(dir=cryptodir, |
|
2541 |
prefix="x509-%s-" % utils.TimestampForFilename()) |
|
2542 |
try: |
|
2543 |
name = os.path.basename(cert_dir) |
|
2544 |
assert len(name) > 5 |
|
2545 |
|
|
2546 |
(_, key_file, cert_file) = _GetX509Filenames(cryptodir, name) |
|
2547 |
|
|
2548 |
utils.WriteFile(key_file, mode=0400, data=key_pem) |
|
2549 |
utils.WriteFile(cert_file, mode=0400, data=cert_pem) |
|
2550 |
|
|
2551 |
# Never return private key as it shouldn't leave the node |
|
2552 |
return (name, cert_pem) |
|
2553 |
except Exception: |
|
2554 |
shutil.rmtree(cert_dir, ignore_errors=True) |
|
2555 |
raise |
|
2556 |
|
|
2557 |
|
|
2558 |
def RemoveX509Certificate(name, cryptodir=constants.CRYPTO_KEYS_DIR): |
|
2559 |
"""Removes a X509 certificate. |
|
2560 |
|
|
2561 |
@type name: string |
|
2562 |
@param name: Certificate name |
|
2563 |
|
|
2564 |
""" |
|
2565 |
(cert_dir, key_file, cert_file) = _GetX509Filenames(cryptodir, name) |
|
2566 |
|
|
2567 |
utils.RemoveFile(key_file) |
|
2568 |
utils.RemoveFile(cert_file) |
|
2569 |
|
|
2570 |
try: |
|
2571 |
os.rmdir(cert_dir) |
|
2572 |
except EnvironmentError, err: |
|
2573 |
_Fail("Cannot remove certificate directory '%s': %s", |
|
2574 |
cert_dir, err) |
|
2575 |
|
|
2576 |
|
|
2513 | 2577 |
def _FindDisks(nodes_ip, disks): |
2514 | 2578 |
"""Sets the physical ID on disks and returns the block devices. |
2515 | 2579 |
|
b/lib/constants.py | ||
---|---|---|
91 | 91 |
RUN_DIRS_MODE = 0755 |
92 | 92 |
SOCKET_DIR = RUN_GANETI_DIR + "/socket" |
93 | 93 |
SOCKET_DIR_MODE = 0700 |
94 |
CRYPTO_KEYS_DIR = RUN_GANETI_DIR + "/crypto" |
|
95 |
CRYPTO_KEYS_DIR_MODE = 0700 |
|
94 | 96 |
# keep RUN_GANETI_DIR first here, to make sure all get created when the node |
95 | 97 |
# daemon is started (this takes care of RUN_DIR being tmpfs) |
96 | 98 |
SUB_RUN_DIRS = [ RUN_GANETI_DIR, BDEV_CACHE_DIR, DISK_LINKS_DIR ] |
b/lib/rpc.py | ||
---|---|---|
1081 | 1081 |
""" |
1082 | 1082 |
return self._SingleNodeCall(node, "node_demote_from_mc", []) |
1083 | 1083 |
|
1084 |
|
|
1085 | 1084 |
def call_node_powercycle(self, node, hypervisor): |
1086 | 1085 |
"""Tries to powercycle a node. |
1087 | 1086 |
|
... | ... | |
1090 | 1089 |
""" |
1091 | 1090 |
return self._SingleNodeCall(node, "node_powercycle", [hypervisor]) |
1092 | 1091 |
|
1093 |
|
|
1094 | 1092 |
def call_test_delay(self, node_list, duration): |
1095 | 1093 |
"""Sleep for a fixed time on given node(s). |
1096 | 1094 |
|
... | ... | |
1189 | 1187 |
hv_full = objects.FillDict(cluster.hvparams.get(hvname, {}), hvparams) |
1190 | 1188 |
return self._MultiNodeCall(node_list, "hypervisor_validate_params", |
1191 | 1189 |
[hvname, hv_full]) |
1190 |
|
|
1191 |
def call_create_x509_certificate(self, node, validity): |
|
1192 |
"""Creates a new X509 certificate for SSL/TLS. |
|
1193 |
|
|
1194 |
This is a single-node call. |
|
1195 |
|
|
1196 |
@type validity: int |
|
1197 |
@param validity: Validity in seconds |
|
1198 |
|
|
1199 |
""" |
|
1200 |
return self._SingleNodeCall(node, "create_x509_certificate", [validity]) |
|
1201 |
|
|
1202 |
def call_remove_x509_certificate(self, node, name): |
|
1203 |
"""Removes a X509 certificate. |
|
1204 |
|
|
1205 |
This is a single-node call. |
|
1206 |
|
|
1207 |
@type name: string |
|
1208 |
@param name: Certificate name |
|
1209 |
|
|
1210 |
""" |
|
1211 |
return self._SingleNodeCall(node, "remove_x509_certificate", [name]) |
b/test/ganeti.backend_unittest.py | ||
---|---|---|
1 |
#!/usr/bin/python |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2010 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.backend""" |
|
23 |
|
|
24 |
import os |
|
25 |
import sys |
|
26 |
import shutil |
|
27 |
import tempfile |
|
28 |
import unittest |
|
29 |
|
|
30 |
from ganeti import utils |
|
31 |
from ganeti import backend |
|
32 |
|
|
33 |
import testutils |
|
34 |
|
|
35 |
|
|
36 |
class TestX509Certificates(unittest.TestCase): |
|
37 |
def setUp(self): |
|
38 |
self.tmpdir = tempfile.mkdtemp() |
|
39 |
|
|
40 |
def tearDown(self): |
|
41 |
shutil.rmtree(self.tmpdir) |
|
42 |
|
|
43 |
def test(self): |
|
44 |
(name, cert_pem) = backend.CreateX509Certificate(300, cryptodir=self.tmpdir) |
|
45 |
|
|
46 |
self.assertEqual(utils.ReadFile(os.path.join(self.tmpdir, name, |
|
47 |
backend._X509_CERT_FILE)), |
|
48 |
cert_pem) |
|
49 |
self.assert_(0 < os.path.getsize(os.path.join(self.tmpdir, name, |
|
50 |
backend._X509_KEY_FILE))) |
|
51 |
|
|
52 |
(name2, cert_pem2) = \ |
|
53 |
backend.CreateX509Certificate(300, cryptodir=self.tmpdir) |
|
54 |
|
|
55 |
backend.RemoveX509Certificate(name, cryptodir=self.tmpdir) |
|
56 |
backend.RemoveX509Certificate(name2, cryptodir=self.tmpdir) |
|
57 |
|
|
58 |
self.assertEqual(utils.ListVisibleFiles(self.tmpdir), []) |
|
59 |
|
|
60 |
def testNonEmpty(self): |
|
61 |
(name, _) = backend.CreateX509Certificate(300, cryptodir=self.tmpdir) |
|
62 |
|
|
63 |
utils.WriteFile(utils.PathJoin(self.tmpdir, name, "hello-world"), |
|
64 |
data="Hello World") |
|
65 |
|
|
66 |
self.assertRaises(backend.RPCFail, backend.RemoveX509Certificate, |
|
67 |
name, cryptodir=self.tmpdir) |
|
68 |
|
|
69 |
self.assertEqual(utils.ListVisibleFiles(self.tmpdir), [name]) |
|
70 |
|
|
71 |
|
|
72 |
if __name__ == "__main__": |
|
73 |
testutils.GanetiTestProgram() |
Also available in: Unified diff