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  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions are 
  9  # met: 
 10  # 
 11  # 1. Redistributions of source code must retain the above copyright notice, 
 12  # this list of conditions and the following disclaimer. 
 13  # 
 14  # 2. Redistributions in binary form must reproduce the above copyright 
 15  # notice, this list of conditions and the following disclaimer in the 
 16  # documentation and/or other materials provided with the distribution. 
 17  # 
 18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 29   
 30  """Script to configure the node daemon. 
 31   
 32  """ 
 33   
 34  import os 
 35  import os.path 
 36  import optparse 
 37  import sys 
 38  import logging 
 39  import OpenSSL 
 40  from cStringIO import StringIO 
 41   
 42  from ganeti import cli 
 43  from ganeti import constants 
 44  from ganeti import errors 
 45  from ganeti import pathutils 
 46  from ganeti import utils 
 47  from ganeti import serializer 
 48  from ganeti import runtime 
 49  from ganeti import ht 
 50  from ganeti import ssconf 
 51   
 52   
 53  _DATA_CHECK = ht.TStrictDict(False, True, { 
 54    constants.NDS_CLUSTER_NAME: ht.TNonEmptyString, 
 55    constants.NDS_NODE_DAEMON_CERTIFICATE: ht.TNonEmptyString, 
 56    constants.NDS_SSCONF: ht.TDictOf(ht.TNonEmptyString, ht.TString), 
 57    constants.NDS_START_NODE_DAEMON: ht.TBool, 
 58    }) 
 59   
 60   
61 -class SetupError(errors.GenericError):
62 """Local class for reporting errors. 63 64 """
65 66
67 -def ParseOptions():
68 """Parses the options passed to the program. 69 70 @return: Options and arguments 71 72 """ 73 parser = optparse.OptionParser(usage="%prog [--dry-run]", 74 prog=os.path.basename(sys.argv[0])) 75 parser.add_option(cli.DEBUG_OPT) 76 parser.add_option(cli.VERBOSE_OPT) 77 parser.add_option(cli.DRY_RUN_OPT) 78 79 (opts, args) = parser.parse_args() 80 81 return VerifyOptions(parser, opts, args)
82 83
84 -def VerifyOptions(parser, opts, args):
85 """Verifies options and arguments for correctness. 86 87 """ 88 if args: 89 parser.error("No arguments are expected") 90 91 return opts
92 93
94 -def _VerifyCertificate(cert_pem, _check_fn=utils.CheckNodeCertificate):
95 """Verifies a certificate against the local node daemon certificate. 96 97 @type cert_pem: string 98 @param cert_pem: Certificate and key in PEM format 99 @rtype: string 100 @return: Formatted key and certificate 101 102 """ 103 try: 104 cert = \ 105 OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem) 106 except Exception, err: 107 raise errors.X509CertError("(stdin)", 108 "Unable to load certificate: %s" % err) 109 110 try: 111 key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, cert_pem) 112 except OpenSSL.crypto.Error, err: 113 raise errors.X509CertError("(stdin)", 114 "Unable to load private key: %s" % err) 115 116 # Check certificate with given key; this detects cases where the key given on 117 # stdin doesn't match the certificate also given on stdin 118 x509_check_fn = utils.PrepareX509CertKeyCheck(cert, key) 119 try: 120 x509_check_fn() 121 except OpenSSL.SSL.Error: 122 raise errors.X509CertError("(stdin)", 123 "Certificate is not signed with given key") 124 125 # Standard checks, including check against an existing local certificate 126 # (no-op if that doesn't exist) 127 _check_fn(cert) 128 129 key_encoded = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) 130 cert_encoded = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, 131 cert) 132 complete_cert_encoded = key_encoded + cert_encoded 133 if not cert_pem == complete_cert_encoded: 134 logging.error("The certificate differs after being reencoded. Please" 135 " renew the certificates cluster-wide to prevent future" 136 " inconsistencies.") 137 138 # Format for storing on disk 139 buf = StringIO() 140 buf.write(cert_pem) 141 return buf.getvalue()
142 143
144 -def VerifyCertificate(data, _verify_fn=_VerifyCertificate):
145 """Verifies cluster certificate. 146 147 @type data: dict 148 @rtype: string 149 @return: Formatted key and certificate 150 151 """ 152 cert = data.get(constants.NDS_NODE_DAEMON_CERTIFICATE) 153 if not cert: 154 raise SetupError("Node daemon certificate must be specified") 155 156 return _verify_fn(cert)
157 158
159 -def VerifyClusterName(data, _verify_fn=ssconf.VerifyClusterName):
160 """Verifies cluster name. 161 162 @type data: dict 163 @rtype: string 164 @return: Cluster name 165 166 """ 167 name = data.get(constants.NDS_CLUSTER_NAME) 168 if not name: 169 raise SetupError("Cluster name must be specified") 170 171 _verify_fn(name) 172 173 return name
174 175
176 -def VerifySsconf(data, cluster_name, _verify_fn=ssconf.VerifyKeys):
177 """Verifies ssconf names. 178 179 @type data: dict 180 181 """ 182 items = data.get(constants.NDS_SSCONF) 183 184 if not items: 185 raise SetupError("Ssconf values must be specified") 186 187 # TODO: Should all keys be required? Right now any subset of valid keys is 188 # accepted. 189 _verify_fn(items.keys()) 190 191 if items.get(constants.SS_CLUSTER_NAME) != cluster_name: 192 raise SetupError("Cluster name in ssconf does not match") 193 194 return items
195 196
197 -def LoadData(raw):
198 """Parses and verifies input data. 199 200 @rtype: dict 201 202 """ 203 return serializer.LoadAndVerifyJson(raw, _DATA_CHECK)
204 205
206 -def Main():
207 """Main routine. 208 209 """ 210 opts = ParseOptions() 211 212 utils.SetupToolLogging(opts.debug, opts.verbose) 213 214 try: 215 getent = runtime.GetEnts() 216 217 data = LoadData(sys.stdin.read()) 218 219 cluster_name = VerifyClusterName(data) 220 cert_pem = VerifyCertificate(data) 221 ssdata = VerifySsconf(data, cluster_name) 222 223 logging.info("Writing ssconf files ...") 224 ssconf.WriteSsconfFiles(ssdata, dry_run=opts.dry_run) 225 226 logging.info("Writing node daemon certificate ...") 227 utils.WriteFile(pathutils.NODED_CERT_FILE, data=cert_pem, 228 mode=pathutils.NODED_CERT_MODE, 229 uid=getent.masterd_uid, gid=getent.masterd_gid, 230 dry_run=opts.dry_run) 231 232 if (data.get(constants.NDS_START_NODE_DAEMON) and # pylint: disable=E1103 233 not opts.dry_run): 234 logging.info("Restarting node daemon ...") 235 236 stop_cmd = "%s stop-all" % pathutils.DAEMON_UTIL 237 noded_cmd = "%s start %s" % (pathutils.DAEMON_UTIL, constants.NODED) 238 mond_cmd = "" 239 if constants.ENABLE_MOND: 240 mond_cmd = "%s start %s" % (pathutils.DAEMON_UTIL, constants.MOND) 241 242 cmd = "; ".join([stop_cmd, noded_cmd, mond_cmd]) 243 244 result = utils.RunCmd(cmd, interactive=True) 245 if result.failed: 246 raise SetupError("Could not start the node daemons, command '%s'" 247 " failed: %s" % (result.cmd, result.fail_reason)) 248 249 logging.info("Node daemon successfully configured") 250 except Exception, err: # pylint: disable=W0703 251 logging.debug("Caught unhandled exception", exc_info=True) 252 253 (retcode, message) = cli.FormatError(err) 254 logging.error(message) 255 256 return retcode 257 else: 258 return constants.EXIT_SUCCESS
259