X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/fcad7225e3fc47b0972890aa3122468c7cd1c49c..fb44c6dbca84285813fd3278b14687ddc41de678:/lib/client/gnt_cluster.py?ds=sidebyside diff --git a/lib/client/gnt_cluster.py b/lib/client/gnt_cluster.py index 120179c..f3ada54 100644 --- a/lib/client/gnt_cluster.py +++ b/lib/client/gnt_cluster.py @@ -20,7 +20,7 @@ """Cluster related commands""" -# pylint: disable-msg=W0401,W0613,W0614,C0103 +# pylint: disable=W0401,W0613,W0614,C0103 # W0401: Wildcard import ganeti.cli # W0613: Unused argument, since all functions follow the same API # W0614: Unused import %s from wildcard import (since we need cli) @@ -223,6 +223,32 @@ def RenameCluster(opts, args): return 0 +def ActivateMasterIp(opts, args): + """Activates the master IP. + + """ + op = opcodes.OpClusterActivateMasterIp() + SubmitOpCode(op) + return 0 + + +def DeactivateMasterIp(opts, args): + """Deactivates the master IP. + + """ + if not opts.confirm: + usertext = ("This will disable the master IP. All the open connections to" + " the master IP will be closed. To reach the master you will" + " need to use its node IP." + " Continue?") + if not AskUser(usertext): + return 1 + + op = opcodes.OpClusterDeactivateMasterIp() + SubmitOpCode(op) + return 0 + + def RedistributeConfig(opts, args): """Forces push of the cluster configuration. @@ -476,12 +502,23 @@ def VerifyCluster(opts, args): jex.AddJobId(None, status, job_id) results = jex.GetResults() - bad_cnt = len([row for row in results if not row[0]]) - if bad_cnt == 0: + + (bad_jobs, bad_results) = \ + map(len, + # Convert iterators to lists + map(list, + # Count errors + map(compat.partial(itertools.ifilterfalse, bool), + # Convert result to booleans in a tuple + zip(*((job_success, len(op_results) == 1 and op_results[0]) + for (job_success, op_results) in results))))) + + if bad_jobs == 0 and bad_results == 0: rcode = constants.EXIT_SUCCESS else: - ToStdout("%s job(s) failed while verifying the cluster.", bad_cnt) rcode = constants.EXIT_FAILURE + if bad_jobs > 0: + ToStdout("%s job(s) failed while verifying the cluster.", bad_jobs) return rcode @@ -611,7 +648,7 @@ def MasterPing(opts, args): cl = GetClient() cl.QueryClusterInfo() return 0 - except Exception: # pylint: disable-msg=W0703 + except Exception: # pylint: disable=W0703 return 1 @@ -635,9 +672,45 @@ def SearchTags(opts, args): ToStdout("%s %s", path, tag) -def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename, - new_confd_hmac_key, new_cds, cds_filename, - force): +def _ReadAndVerifyCert(cert_filename, verify_private_key=False): + """Reads and verifies an X509 certificate. + + @type cert_filename: string + @param cert_filename: the path of the file containing the certificate to + verify encoded in PEM format + @type verify_private_key: bool + @param verify_private_key: whether to verify the private key in addition to + the public certificate + @rtype: string + @return: a string containing the PEM-encoded certificate. + + """ + try: + pem = utils.ReadFile(cert_filename) + except IOError, err: + raise errors.X509CertError(cert_filename, + "Unable to read certificate: %s" % str(err)) + + try: + OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pem) + except Exception, err: + raise errors.X509CertError(cert_filename, + "Unable to load certificate: %s" % str(err)) + + if verify_private_key: + try: + OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, pem) + except Exception, err: + raise errors.X509CertError(cert_filename, + "Unable to load private key: %s" % str(err)) + + return pem + + +def _RenewCrypto(new_cluster_cert, new_rapi_cert, #pylint: disable=R0911 + rapi_cert_filename, new_spice_cert, spice_cert_filename, + spice_cacert_filename, new_confd_hmac_key, new_cds, + cds_filename, force): """Renews cluster certificates, keys and secrets. @type new_cluster_cert: bool @@ -646,6 +719,13 @@ def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename, @param new_rapi_cert: Whether to generate a new RAPI certificate @type rapi_cert_filename: string @param rapi_cert_filename: Path to file containing new RAPI certificate + @type new_spice_cert: bool + @param new_spice_cert: Whether to generate a new SPICE certificate + @type spice_cert_filename: string + @param spice_cert_filename: Path to file containing new SPICE certificate + @type spice_cacert_filename: string + @param spice_cacert_filename: Path to file containing the certificate of the + CA that signed the SPICE certificate @type new_confd_hmac_key: bool @param new_confd_hmac_key: Whether to generate a new HMAC key @type new_cds: bool @@ -657,7 +737,7 @@ def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename, """ if new_rapi_cert and rapi_cert_filename: - ToStderr("Only one of the --new-rapi-certficate and --rapi-certificate" + ToStderr("Only one of the --new-rapi-certificate and --rapi-certificate" " options can be specified at the same time.") return 1 @@ -667,32 +747,31 @@ def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename, " the same time.") return 1 - if rapi_cert_filename: - # Read and verify new certificate - try: - rapi_cert_pem = utils.ReadFile(rapi_cert_filename) - - OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, - rapi_cert_pem) - except Exception, err: # pylint: disable-msg=W0703 - ToStderr("Can't load new RAPI certificate from %s: %s" % - (rapi_cert_filename, str(err))) - return 1 + if new_spice_cert and (spice_cert_filename or spice_cacert_filename): + ToStderr("When using --new-spice-certificate, the --spice-certificate" + " and --spice-ca-certificate must not be used.") + return 1 - try: - OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, rapi_cert_pem) - except Exception, err: # pylint: disable-msg=W0703 - ToStderr("Can't load new RAPI private key from %s: %s" % - (rapi_cert_filename, str(err))) - return 1 + if bool(spice_cacert_filename) ^ bool(spice_cert_filename): + ToStderr("Both --spice-certificate and --spice-ca-certificate must be" + " specified.") + return 1 - else: - rapi_cert_pem = None + rapi_cert_pem, spice_cert_pem, spice_cacert_pem = (None, None, None) + try: + if rapi_cert_filename: + rapi_cert_pem = _ReadAndVerifyCert(rapi_cert_filename, True) + if spice_cert_filename: + spice_cert_pem = _ReadAndVerifyCert(spice_cert_filename, True) + spice_cacert_pem = _ReadAndVerifyCert(spice_cacert_filename) + except errors.X509CertError, err: + ToStderr("Unable to load X509 certificate from %s: %s", err[0], err[1]) + return 1 if cds_filename: try: cds = utils.ReadFile(cds_filename) - except Exception, err: # pylint: disable-msg=W0703 + except Exception, err: # pylint: disable=W0703 ToStderr("Can't load new cluster domain secret from %s: %s" % (cds_filename, str(err))) return 1 @@ -707,10 +786,14 @@ def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename, def _RenewCryptoInner(ctx): ctx.feedback_fn("Updating certificates and keys") - bootstrap.GenerateClusterCrypto(new_cluster_cert, new_rapi_cert, + bootstrap.GenerateClusterCrypto(new_cluster_cert, + new_rapi_cert, + new_spice_cert, new_confd_hmac_key, new_cds, rapi_cert_pem=rapi_cert_pem, + spice_cert_pem=spice_cert_pem, + spice_cacert_pem=spice_cacert_pem, cds=cds) files_to_copy = [] @@ -721,6 +804,10 @@ def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename, if new_rapi_cert or rapi_cert_pem: files_to_copy.append(constants.RAPI_CERT_FILE) + if new_spice_cert or spice_cert_pem: + files_to_copy.append(constants.SPICE_CERT_FILE) + files_to_copy.append(constants.SPICE_CACERT_FILE) + if new_confd_hmac_key: files_to_copy.append(constants.CONFD_HMAC_KEY) @@ -749,6 +836,9 @@ def RenewCrypto(opts, args): return _RenewCrypto(opts.new_cluster_cert, opts.new_rapi_cert, opts.rapi_cert, + opts.new_spice_cert, + opts.spice_cert, + opts.spice_cacert, opts.new_confd_hmac_key, opts.new_cluster_domain_secret, opts.cluster_domain_secret, @@ -1337,7 +1427,8 @@ commands = { RenewCrypto, ARGS_NONE, [NEW_CLUSTER_CERT_OPT, NEW_RAPI_CERT_OPT, RAPI_CERT_OPT, NEW_CONFD_HMAC_KEY_OPT, FORCE_OPT, - NEW_CLUSTER_DOMAIN_SECRET_OPT, CLUSTER_DOMAIN_SECRET_OPT], + NEW_CLUSTER_DOMAIN_SECRET_OPT, CLUSTER_DOMAIN_SECRET_OPT, + NEW_SPICE_CERT_OPT, SPICE_CERT_OPT, SPICE_CACERT_OPT], "[opts...]", "Renews cluster certificates, keys and secrets"), "epo": ( @@ -1346,6 +1437,11 @@ commands = { SHUTDOWN_TIMEOUT_OPT, POWER_DELAY_OPT], "[opts...] [args]", "Performs an emergency power-off on given args"), + "activate-master-ip": ( + ActivateMasterIp, ARGS_NONE, [], "", "Activates the master IP"), + "deactivate-master-ip": ( + DeactivateMasterIp, ARGS_NONE, [CONFIRM_OPT], "", + "Deactivates the master IP"), }