Package ganeti :: Package tools :: Module node_daemon_setup
[hide private]
[frames] | no frames]

Source Code for Module ganeti.tools.node_daemon_setup

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2012 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  """Script to configure the node daemon. 
 22   
 23  """ 
 24   
 25  import os 
 26  import os.path 
 27  import optparse 
 28  import sys 
 29  import logging 
 30  import OpenSSL 
 31  from cStringIO import StringIO 
 32   
 33  from ganeti import cli 
 34  from ganeti import constants 
 35  from ganeti import errors 
 36  from ganeti import pathutils 
 37  from ganeti import utils 
 38  from ganeti import serializer 
 39  from ganeti import runtime 
 40  from ganeti import ht 
 41  from ganeti import ssconf 
 42   
 43   
 44  _DATA_CHECK = ht.TStrictDict(False, True, { 
 45    constants.NDS_CLUSTER_NAME: ht.TNonEmptyString, 
 46    constants.NDS_NODE_DAEMON_CERTIFICATE: ht.TNonEmptyString, 
 47    constants.NDS_SSCONF: ht.TDictOf(ht.TNonEmptyString, ht.TString), 
 48    constants.NDS_START_NODE_DAEMON: ht.TBool, 
 49    }) 
 50   
 51   
52 -class SetupError(errors.GenericError):
53 """Local class for reporting errors. 54 55 """
56 57
58 -def ParseOptions():
59 """Parses the options passed to the program. 60 61 @return: Options and arguments 62 63 """ 64 parser = optparse.OptionParser(usage="%prog [--dry-run]", 65 prog=os.path.basename(sys.argv[0])) 66 parser.add_option(cli.DEBUG_OPT) 67 parser.add_option(cli.VERBOSE_OPT) 68 parser.add_option(cli.DRY_RUN_OPT) 69 70 (opts, args) = parser.parse_args() 71 72 return VerifyOptions(parser, opts, args)
73 74
75 -def VerifyOptions(parser, opts, args):
76 """Verifies options and arguments for correctness. 77 78 """ 79 if args: 80 parser.error("No arguments are expected") 81 82 return opts
83 84
85 -def _VerifyCertificate(cert_pem, _check_fn=utils.CheckNodeCertificate):
86 """Verifies a certificate against the local node daemon certificate. 87 88 @type cert_pem: string 89 @param cert_pem: Certificate and key in PEM format 90 @rtype: string 91 @return: Formatted key and certificate 92 93 """ 94 try: 95 cert = \ 96 OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem) 97 except Exception, err: 98 raise errors.X509CertError("(stdin)", 99 "Unable to load certificate: %s" % err) 100 101 try: 102 key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, cert_pem) 103 except OpenSSL.crypto.Error, err: 104 raise errors.X509CertError("(stdin)", 105 "Unable to load private key: %s" % err) 106 107 # Check certificate with given key; this detects cases where the key given on 108 # stdin doesn't match the certificate also given on stdin 109 x509_check_fn = utils.PrepareX509CertKeyCheck(cert, key) 110 try: 111 x509_check_fn() 112 except OpenSSL.SSL.Error: 113 raise errors.X509CertError("(stdin)", 114 "Certificate is not signed with given key") 115 116 # Standard checks, including check against an existing local certificate 117 # (no-op if that doesn't exist) 118 _check_fn(cert) 119 120 # Format for storing on disk 121 buf = StringIO() 122 buf.write(OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)) 123 buf.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)) 124 return buf.getvalue()
125 126
127 -def VerifyCertificate(data, _verify_fn=_VerifyCertificate):
128 """Verifies cluster certificate. 129 130 @type data: dict 131 @rtype: string 132 @return: Formatted key and certificate 133 134 """ 135 cert = data.get(constants.NDS_NODE_DAEMON_CERTIFICATE) 136 if not cert: 137 raise SetupError("Node daemon certificate must be specified") 138 139 return _verify_fn(cert)
140 141
142 -def VerifyClusterName(data, _verify_fn=ssconf.VerifyClusterName):
143 """Verifies cluster name. 144 145 @type data: dict 146 @rtype: string 147 @return: Cluster name 148 149 """ 150 name = data.get(constants.NDS_CLUSTER_NAME) 151 if not name: 152 raise SetupError("Cluster name must be specified") 153 154 _verify_fn(name) 155 156 return name
157 158
159 -def VerifySsconf(data, cluster_name, _verify_fn=ssconf.VerifyKeys):
160 """Verifies ssconf names. 161 162 @type data: dict 163 164 """ 165 items = data.get(constants.NDS_SSCONF) 166 167 if not items: 168 raise SetupError("Ssconf values must be specified") 169 170 # TODO: Should all keys be required? Right now any subset of valid keys is 171 # accepted. 172 _verify_fn(items.keys()) 173 174 if items.get(constants.SS_CLUSTER_NAME) != cluster_name: 175 raise SetupError("Cluster name in ssconf does not match") 176 177 return items
178 179
180 -def LoadData(raw):
181 """Parses and verifies input data. 182 183 @rtype: dict 184 185 """ 186 return serializer.LoadAndVerifyJson(raw, _DATA_CHECK)
187 188
189 -def Main():
190 """Main routine. 191 192 """ 193 opts = ParseOptions() 194 195 utils.SetupToolLogging(opts.debug, opts.verbose) 196 197 try: 198 getent = runtime.GetEnts() 199 200 data = LoadData(sys.stdin.read()) 201 202 cluster_name = VerifyClusterName(data) 203 cert_pem = VerifyCertificate(data) 204 ssdata = VerifySsconf(data, cluster_name) 205 206 logging.info("Writing ssconf files ...") 207 ssconf.WriteSsconfFiles(ssdata, dry_run=opts.dry_run) 208 209 logging.info("Writing node daemon certificate ...") 210 utils.WriteFile(pathutils.NODED_CERT_FILE, data=cert_pem, 211 mode=pathutils.NODED_CERT_MODE, 212 uid=getent.masterd_uid, gid=getent.masterd_gid, 213 dry_run=opts.dry_run) 214 215 if (data.get(constants.NDS_START_NODE_DAEMON) and # pylint: disable=E1103 216 not opts.dry_run): 217 logging.info("Restarting node daemon ...") 218 219 cmd = ("%s stop-all; %s start %s" % 220 (pathutils.DAEMON_UTIL, pathutils.DAEMON_UTIL, constants.NODED)) 221 222 result = utils.RunCmd(cmd, interactive=True) 223 if result.failed: 224 raise SetupError("Could not start the node daemon, command '%s'" 225 " failed: %s" % (result.cmd, result.fail_reason)) 226 227 logging.info("Node daemon successfully configured") 228 except Exception, err: # pylint: disable=W0703 229 logging.debug("Caught unhandled exception", exc_info=True) 230 231 (retcode, message) = cli.FormatError(err) 232 logging.error(message) 233 234 return retcode 235 else: 236 return constants.EXIT_SUCCESS
237