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

Source Code for Module ganeti.tools.common

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2014 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  """Common functions for tool scripts. 
 31   
 32  """ 
 33   
 34  import logging 
 35  import OpenSSL 
 36  import os 
 37  import time 
 38  from cStringIO import StringIO 
 39   
 40  from ganeti import constants 
 41  from ganeti import errors 
 42  from ganeti import pathutils 
 43  from ganeti import utils 
 44  from ganeti import serializer 
 45  from ganeti import ssconf 
 46  from ganeti import ssh 
 47   
 48   
49 -def VerifyOptions(parser, opts, args):
50 """Verifies options and arguments for correctness. 51 52 """ 53 if args: 54 parser.error("No arguments are expected") 55 56 return opts
57 58
59 -def _VerifyCertificateStrong(cert_pem, error_fn, 60 _check_fn=utils.CheckNodeCertificate):
61 """Verifies a certificate against the local node daemon certificate. 62 63 Includes elaborate tests of encodings etc., and returns formatted 64 certificate. 65 66 @type cert_pem: string 67 @param cert_pem: Certificate and key in PEM format 68 @type error_fn: callable 69 @param error_fn: function to call in case of an error 70 @rtype: string 71 @return: Formatted key and certificate 72 73 """ 74 try: 75 cert = \ 76 OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem) 77 except Exception, err: 78 raise error_fn("(stdin) Unable to load certificate: %s" % err) 79 80 try: 81 key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, cert_pem) 82 except OpenSSL.crypto.Error, err: 83 raise error_fn("(stdin) Unable to load private key: %s" % err) 84 85 # Check certificate with given key; this detects cases where the key given on 86 # stdin doesn't match the certificate also given on stdin 87 try: 88 utils.X509CertKeyCheck(cert, key) 89 except OpenSSL.SSL.Error: 90 raise error_fn("(stdin) Certificate is not signed with given key") 91 92 # Standard checks, including check against an existing local certificate 93 # (no-op if that doesn't exist) 94 _check_fn(cert) 95 96 key_encoded = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) 97 cert_encoded = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, 98 cert) 99 complete_cert_encoded = key_encoded + cert_encoded 100 if not cert_pem == complete_cert_encoded: 101 logging.error("The certificate differs after being reencoded. Please" 102 " renew the certificates cluster-wide to prevent future" 103 " inconsistencies.") 104 105 # Format for storing on disk 106 buf = StringIO() 107 buf.write(cert_pem) 108 return buf.getvalue()
109 110
111 -def _VerifyCertificateSoft(cert_pem, error_fn, 112 _check_fn=utils.CheckNodeCertificate):
113 """Verifies a certificate against the local node daemon certificate. 114 115 @type cert_pem: string 116 @param cert_pem: Certificate in PEM format (no key) 117 118 """ 119 try: 120 OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, cert_pem) 121 except OpenSSL.crypto.Error, err: 122 pass 123 else: 124 raise error_fn("No private key may be given") 125 126 try: 127 cert = \ 128 OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem) 129 except Exception, err: 130 raise errors.X509CertError("(stdin)", 131 "Unable to load certificate: %s" % err) 132 133 _check_fn(cert)
134 135
136 -def VerifyCertificateSoft(data, error_fn, _verify_fn=_VerifyCertificateSoft):
137 """Verifies cluster certificate if existing. 138 139 @type data: dict 140 @type error_fn: callable 141 @param error_fn: function to call in case of an error 142 @rtype: string 143 @return: Formatted key and certificate 144 145 """ 146 cert = data.get(constants.SSHS_NODE_DAEMON_CERTIFICATE) 147 if cert: 148 _verify_fn(cert, error_fn)
149 150
151 -def VerifyCertificateStrong(data, error_fn, 152 _verify_fn=_VerifyCertificateStrong):
153 """Verifies cluster certificate. Throws error when not existing. 154 155 @type data: dict 156 @type error_fn: callable 157 @param error_fn: function to call in case of an error 158 @rtype: string 159 @return: Formatted key and certificate 160 161 """ 162 cert = data.get(constants.NDS_NODE_DAEMON_CERTIFICATE) 163 if not cert: 164 raise error_fn("Node daemon certificate must be specified") 165 166 return _verify_fn(cert, error_fn)
167 168
169 -def VerifyClusterName(data, error_fn, cluster_name_constant, 170 _verify_fn=ssconf.VerifyClusterName):
171 """Verifies cluster name. 172 173 @type data: dict 174 175 """ 176 name = data.get(cluster_name_constant) 177 if name: 178 _verify_fn(name) 179 else: 180 raise error_fn("Cluster name must be specified") 181 182 return name
183 184
185 -def LoadData(raw, data_check):
186 """Parses and verifies input data. 187 188 @rtype: dict 189 190 """ 191 result = None 192 try: 193 result = serializer.LoadAndVerifyJson(raw, data_check) 194 logging.debug("Received data: %s", serializer.DumpJson(result)) 195 except Exception as e: 196 logging.warn("Received data is not valid json: %s.", str(raw)) 197 raise e 198 return result
199 200
201 -def GenerateRootSshKeys(key_type, key_bits, error_fn, _suffix="", 202 _homedir_fn=None):
203 """Generates root's SSH keys for this node. 204 205 """ 206 ssh.InitSSHSetup(key_type, key_bits, error_fn=error_fn, 207 _homedir_fn=_homedir_fn, _suffix=_suffix)
208 209
210 -def GenerateClientCertificate( 211 data, error_fn, client_cert=pathutils.NODED_CLIENT_CERT_FILE, 212 signing_cert=pathutils.NODED_CERT_FILE):
213 """Regenerates the client certificate of the node. 214 215 @type data: string 216 @param data: the JSON-formated input data 217 218 """ 219 if not os.path.exists(signing_cert): 220 raise error_fn("The signing certificate '%s' cannot be found." 221 % signing_cert) 222 223 # TODO: This sets the serial number to the number of seconds 224 # since epoch. This is technically not a correct serial number 225 # (in the way SSL is supposed to be used), but it serves us well 226 # enough for now, as we don't have any infrastructure for keeping 227 # track of the number of signed certificates yet. 228 serial_no = int(time.time()) 229 230 # The hostname of the node is provided with the input data. 231 hostname = data.get(constants.NDS_NODE_NAME) 232 if not hostname: 233 raise error_fn("No hostname found.") 234 235 utils.GenerateSignedSslCert(client_cert, serial_no, signing_cert, 236 common_name=hostname)
237