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