Revision 6d4a1656
b/lib/cli.py | ||
---|---|---|
79 | 79 |
"MASTER_NETDEV_OPT", |
80 | 80 |
"MC_OPT", |
81 | 81 |
"NET_OPT", |
82 |
"NEW_CLUSTER_CERT_OPT", |
|
83 |
"NEW_HMAC_KEY_OPT", |
|
84 |
"NEW_RAPI_CERT_OPT", |
|
82 | 85 |
"NEW_SECONDARY_OPT", |
83 | 86 |
"NIC_PARAMS_OPT", |
84 | 87 |
"NODE_LIST_OPT", |
... | ... | |
102 | 105 |
"OFFLINE_OPT", |
103 | 106 |
"OS_OPT", |
104 | 107 |
"OS_SIZE_OPT", |
108 |
"RAPI_CERT_OPT", |
|
105 | 109 |
"READD_OPT", |
106 | 110 |
"REBOOT_TYPE_OPT", |
107 | 111 |
"SECONDARY_IP_OPT", |
... | ... | |
860 | 864 |
help="Release the locks on the secondary" |
861 | 865 |
" node(s) early") |
862 | 866 |
|
867 |
NEW_CLUSTER_CERT_OPT = cli_option("--new-cluster-certificate", |
|
868 |
dest="new_cluster_cert", |
|
869 |
default=False, action="store_true", |
|
870 |
help="Generate a new cluster certificate") |
|
871 |
|
|
872 |
RAPI_CERT_OPT = cli_option("--rapi-certificate", dest="rapi_cert", |
|
873 |
default=None, |
|
874 |
help="File containing new RAPI certificate") |
|
875 |
|
|
876 |
NEW_RAPI_CERT_OPT = cli_option("--new-rapi-certificate", dest="new_rapi_cert", |
|
877 |
default=None, action="store_true", |
|
878 |
help=("Generate a new self-signed RAPI" |
|
879 |
" certificate")) |
|
880 |
|
|
881 |
NEW_HMAC_KEY_OPT = cli_option("--new-hmac-key", dest="new_hmac_key", |
|
882 |
default=False, action="store_true", |
|
883 |
help="Create a new HMAC key") |
|
884 |
|
|
863 | 885 |
|
864 | 886 |
def _ParseArgs(argv, commands, aliases): |
865 | 887 |
"""Parser for the command line arguments. |
b/man/gnt-cluster.sgml | ||
---|---|---|
540 | 540 |
<sbr> |
541 | 541 |
<arg choice="opt">--nic-parameters <replaceable>nic-param</replaceable>=<replaceable>value</replaceable><arg rep="repeat" choice="opt">,<replaceable>nic-param</replaceable>=<replaceable>value</replaceable></arg></arg> |
542 | 542 |
<sbr> |
543 |
<arg>-C <replaceable>candidate_pool_size</replaceable></arg> |
|
543 |
<arg choice="opt">-C <replaceable>candidate_pool_size</replaceable></arg>
|
|
544 | 544 |
|
545 | 545 |
</cmdsynopsis> |
546 | 546 |
|
... | ... | |
558 | 558 |
</para> |
559 | 559 |
|
560 | 560 |
<para> |
561 |
The <option>-C</option> options specifies the
|
|
561 |
The <option>-C</option> option specifies the |
|
562 | 562 |
<varname>candidate_pool_size</varname> cluster parameter. This |
563 | 563 |
is the number of nodes that the master will try to keep as |
564 | 564 |
<literal>master_candidates</literal>. For more details about |
... | ... | |
704 | 704 |
</refsect2> |
705 | 705 |
|
706 | 706 |
<refsect2> |
707 |
<title>RENEW-CRYPTO</title> |
|
708 |
|
|
709 |
<cmdsynopsis> |
|
710 |
<command>renew-crypto</command> |
|
711 |
<arg>-f</arg> |
|
712 |
<sbr> |
|
713 |
<arg choice="opt">--new-cluster-certificate</arg> |
|
714 |
<arg choice="opt">--new-hmac-key</arg> |
|
715 |
<sbr> |
|
716 |
<arg choice="opt">--new-rapi-certificate</arg> |
|
717 |
<arg choice="opt">--rapi-certificate <replaceable>rapi-cert</replaceable></arg> |
|
718 |
</cmdsynopsis> |
|
719 |
|
|
720 |
<para> |
|
721 |
This command will stop all |
|
722 |
Ganeti daemons in the cluster and start them again once the new |
|
723 |
certificates and keys are replicated. The options |
|
724 |
<option>--new-cluster-certificate</option> and |
|
725 |
<option>--new-hmac-key</option> can be used to regenerate the |
|
726 |
cluster-internal SSL certificate respective the HMAC key used by |
|
727 |
<citerefentry> |
|
728 |
<refentrytitle>ganeti-confd</refentrytitle><manvolnum>8</manvolnum> |
|
729 |
</citerefentry>. To generate a new self-signed RAPI certificate (used |
|
730 |
by <citerefentry> |
|
731 |
<refentrytitle>ganeti-rapi</refentrytitle><manvolnum>8</manvolnum> |
|
732 |
</citerefentry>) specify <option>--new-rapi-certificate</option>. If |
|
733 |
you want to use your own certificate, e.g. one signed by a certificate |
|
734 |
authority (CA), pass its filename to |
|
735 |
<option>--rapi-certificate</option>. |
|
736 |
</para> |
|
737 |
</refsect2> |
|
738 |
|
|
739 |
<refsect2> |
|
707 | 740 |
<title>REPAIR-DISK-SIZES</title> |
708 | 741 |
|
709 | 742 |
<cmdsynopsis> |
b/qa/ganeti-qa.py | ||
---|---|---|
88 | 88 |
"""Runs tests related to gnt-cluster. |
89 | 89 |
|
90 | 90 |
""" |
91 |
if qa_config.TestEnabled("cluster-renew-crypto"): |
|
92 |
RunTest(qa_cluster.TestClusterRenewCrypto) |
|
93 |
|
|
91 | 94 |
if qa_config.TestEnabled('cluster-verify'): |
92 | 95 |
RunTest(qa_cluster.TestClusterVerify) |
93 | 96 |
|
... | ... | |
115 | 118 |
RunTest(qa_rapi.TestVersion) |
116 | 119 |
RunTest(qa_rapi.TestEmptyCluster) |
117 | 120 |
|
121 |
|
|
118 | 122 |
def RunOsTests(): |
119 | 123 |
"""Runs all tests related to gnt-os. |
120 | 124 |
|
... | ... | |
176 | 180 |
if qa_rapi.Enabled(): |
177 | 181 |
RunTest(qa_rapi.TestInstance, instance) |
178 | 182 |
|
183 |
|
|
179 | 184 |
def RunExportImportTests(instance, pnode): |
180 | 185 |
"""Tries to export and import the instance. |
181 | 186 |
|
b/qa/qa-sample.json | ||
---|---|---|
45 | 45 |
"cluster-command": true, |
46 | 46 |
"cluster-copyfile": true, |
47 | 47 |
"cluster-master-failover": true, |
48 |
"cluster-renew-crypto": true, |
|
48 | 49 |
"cluster-destroy": true, |
49 | 50 |
"cluster-rename": true, |
50 | 51 |
|
b/qa/qa_cluster.py | ||
---|---|---|
25 | 25 |
|
26 | 26 |
import tempfile |
27 | 27 |
|
28 |
from ganeti import constants |
|
29 |
from ganeti import bootstrap |
|
28 | 30 |
from ganeti import utils |
29 | 31 |
|
30 | 32 |
import qa_config |
31 | 33 |
import qa_utils |
32 | 34 |
import qa_error |
33 | 35 |
|
34 |
from qa_utils import AssertEqual, StartSSH |
|
36 |
from qa_utils import AssertEqual, AssertNotEqual, StartSSH
|
|
35 | 37 |
|
36 | 38 |
|
37 | 39 |
def _RemoveFileFromAllNodes(filename): |
... | ... | |
144 | 146 |
utils.ShellQuoteArgs(cmd)).wait(), 0) |
145 | 147 |
|
146 | 148 |
|
149 |
def TestClusterRenewCrypto(): |
|
150 |
"""gnt-cluster renew-crypto""" |
|
151 |
master = qa_config.GetMasterNode() |
|
152 |
|
|
153 |
# Conflicting options |
|
154 |
cmd = ["gnt-cluster", "renew-crypto", "--force", |
|
155 |
"--new-cluster-certificate", "--new-hmac-key", |
|
156 |
"--new-rapi-certificate", "--rapi-certificate=/dev/null"] |
|
157 |
AssertNotEqual(StartSSH(master["primary"], |
|
158 |
utils.ShellQuoteArgs(cmd)).wait(), 0) |
|
159 |
|
|
160 |
# Invalid RAPI certificate |
|
161 |
cmd = ["gnt-cluster", "renew-crypto", "--force", |
|
162 |
"--rapi-certificate=/dev/null"] |
|
163 |
AssertNotEqual(StartSSH(master["primary"], |
|
164 |
utils.ShellQuoteArgs(cmd)).wait(), 0) |
|
165 |
|
|
166 |
# Custom RAPI certificate |
|
167 |
fh = tempfile.NamedTemporaryFile() |
|
168 |
|
|
169 |
# Ensure certificate doesn't cause "gnt-cluster verify" to complain |
|
170 |
validity = constants.SSL_CERT_EXPIRATION_WARN * 3 |
|
171 |
|
|
172 |
bootstrap.GenerateSelfSignedSslCert(fh.name, validity=validity) |
|
173 |
|
|
174 |
tmpcert = qa_utils.UploadFile(master["primary"], fh.name) |
|
175 |
try: |
|
176 |
cmd = ["gnt-cluster", "renew-crypto", "--force", |
|
177 |
"--rapi-certificate=%s" % tmpcert] |
|
178 |
AssertEqual(StartSSH(master["primary"], |
|
179 |
utils.ShellQuoteArgs(cmd)).wait(), 0) |
|
180 |
finally: |
|
181 |
cmd = ["rm", "-f", tmpcert] |
|
182 |
AssertEqual(StartSSH(master["primary"], |
|
183 |
utils.ShellQuoteArgs(cmd)).wait(), 0) |
|
184 |
|
|
185 |
# Normal case |
|
186 |
cmd = ["gnt-cluster", "renew-crypto", "--force", |
|
187 |
"--new-cluster-certificate", "--new-hmac-key", |
|
188 |
"--new-rapi-certificate"] |
|
189 |
AssertEqual(StartSSH(master["primary"], |
|
190 |
utils.ShellQuoteArgs(cmd)).wait(), 0) |
|
191 |
|
|
192 |
|
|
147 | 193 |
def TestClusterBurnin(): |
148 | 194 |
"""Burnin""" |
149 | 195 |
master = qa_config.GetMasterNode() |
b/scripts/gnt-cluster | ||
---|---|---|
29 | 29 |
import sys |
30 | 30 |
import os.path |
31 | 31 |
import time |
32 |
import OpenSSL |
|
32 | 33 |
|
33 | 34 |
from ganeti.cli import * |
34 | 35 |
from ganeti import opcodes |
... | ... | |
493 | 494 |
ToStdout("%s %s", path, tag) |
494 | 495 |
|
495 | 496 |
|
497 |
def _RenewCrypto(new_cluster_cert, new_rapi_cert, rapi_cert_filename, |
|
498 |
new_hmac_key, force): |
|
499 |
"""Renews cluster certificates, keys and secrets. |
|
500 |
|
|
501 |
@type new_cluster_cert: bool |
|
502 |
@param new_cluster_cert: Whether to generate a new cluster certificate |
|
503 |
@type new_rapi_cert: bool |
|
504 |
@param new_rapi_cert: Whether to generate a new RAPI certificate |
|
505 |
@type rapi_cert_filename: string |
|
506 |
@param rapi_cert_filename: Path to file containing new RAPI certificate |
|
507 |
@type new_hmac_key: bool |
|
508 |
@param new_hmac_key: Whether to generate a new HMAC key |
|
509 |
@type force: bool |
|
510 |
@param force: Whether to ask user for confirmation |
|
511 |
|
|
512 |
""" |
|
513 |
assert new_cluster_cert or new_rapi_cert or rapi_cert_filename or new_hmac_key |
|
514 |
|
|
515 |
if new_rapi_cert and rapi_cert_filename: |
|
516 |
ToStderr("Only one of the --new-rapi-certficate and --rapi-certificate" |
|
517 |
" options can be specified at the same time.") |
|
518 |
return 1 |
|
519 |
|
|
520 |
if rapi_cert_filename: |
|
521 |
# Read and verify new certificate |
|
522 |
try: |
|
523 |
rapi_cert_pem = utils.ReadFile(rapi_cert_filename) |
|
524 |
|
|
525 |
OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, |
|
526 |
rapi_cert_pem) |
|
527 |
except Exception, err: # pylint: disable-msg=W0703 |
|
528 |
ToStderr("Can't load new RAPI certificate from %s: %s" % |
|
529 |
(rapi_cert_filename, str(err))) |
|
530 |
return 1 |
|
531 |
|
|
532 |
try: |
|
533 |
OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, rapi_cert_pem) |
|
534 |
except Exception, err: # pylint: disable-msg=W0703 |
|
535 |
ToStderr("Can't load new RAPI private key from %s: %s" % |
|
536 |
(rapi_cert_filename, str(err))) |
|
537 |
return 1 |
|
538 |
|
|
539 |
else: |
|
540 |
rapi_cert_pem = None |
|
541 |
|
|
542 |
if not force: |
|
543 |
usertext = ("This requires all daemons on all nodes to be restarted and" |
|
544 |
" may take some time. Continue?") |
|
545 |
if not AskUser(usertext): |
|
546 |
return 1 |
|
547 |
|
|
548 |
def _RenewCryptoInner(ctx): |
|
549 |
ctx.feedback_fn("Updating certificates and keys") |
|
550 |
bootstrap.GenerateClusterCrypto(new_cluster_cert, new_rapi_cert, |
|
551 |
new_hmac_key, |
|
552 |
rapi_cert_pem=rapi_cert_pem) |
|
553 |
|
|
554 |
files_to_copy = [] |
|
555 |
|
|
556 |
if new_cluster_cert: |
|
557 |
files_to_copy.append(constants.SSL_CERT_FILE) |
|
558 |
|
|
559 |
if new_rapi_cert or rapi_cert_pem: |
|
560 |
files_to_copy.append(constants.RAPI_CERT_FILE) |
|
561 |
|
|
562 |
if new_hmac_key: |
|
563 |
files_to_copy.append(constants.HMAC_CLUSTER_KEY) |
|
564 |
|
|
565 |
if files_to_copy: |
|
566 |
for node_name in ctx.nonmaster_nodes: |
|
567 |
ctx.feedback_fn("Copying %s to %s" % |
|
568 |
(", ".join(files_to_copy), node_name)) |
|
569 |
for file_name in files_to_copy: |
|
570 |
ctx.ssh.CopyFileToNode(node_name, file_name) |
|
571 |
|
|
572 |
RunWhileClusterStopped(ToStdout, _RenewCryptoInner) |
|
573 |
|
|
574 |
ToStdout("All requested certificates and keys have been replaced." |
|
575 |
" Running \"gnt-cluster verify\" now is recommended.") |
|
576 |
|
|
577 |
return 0 |
|
578 |
|
|
579 |
|
|
580 |
def RenewCrypto(opts, args): |
|
581 |
"""Renews cluster certificates, keys and secrets. |
|
582 |
|
|
583 |
""" |
|
584 |
return _RenewCrypto(opts.new_cluster_cert, |
|
585 |
opts.new_rapi_cert, |
|
586 |
opts.rapi_cert, |
|
587 |
opts.new_hmac_key, |
|
588 |
opts.force) |
|
589 |
|
|
590 |
|
|
496 | 591 |
def SetClusterParams(opts, args): |
497 | 592 |
"""Modify the cluster. |
498 | 593 |
|
... | ... | |
512 | 607 |
|
513 | 608 |
vg_name = opts.vg_name |
514 | 609 |
if not opts.lvm_storage and opts.vg_name: |
515 |
ToStdout("Options --no-lvm-storage and --vg-name conflict.")
|
|
610 |
ToStderr("Options --no-lvm-storage and --vg-name conflict.")
|
|
516 | 611 |
return 1 |
517 |
elif not opts.lvm_storage: |
|
518 |
vg_name = '' |
|
612 |
|
|
613 |
if not opts.lvm_storage: |
|
614 |
vg_name = "" |
|
519 | 615 |
|
520 | 616 |
hvlist = opts.enabled_hypervisors |
521 | 617 |
if hvlist is not None: |
... | ... | |
692 | 788 |
NIC_PARAMS_OPT, NOLVM_STORAGE_OPT, VG_NAME_OPT], |
693 | 789 |
"[opts...]", |
694 | 790 |
"Alters the parameters of the cluster"), |
791 |
"renew-crypto": ( |
|
792 |
RenewCrypto, ARGS_NONE, |
|
793 |
[NEW_CLUSTER_CERT_OPT, NEW_RAPI_CERT_OPT, RAPI_CERT_OPT, NEW_HMAC_KEY_OPT, |
|
794 |
FORCE_OPT], |
|
795 |
"[opts...]", |
|
796 |
"Renews cluster certificates, keys and secrets"), |
|
695 | 797 |
} |
696 | 798 |
|
799 |
|
|
697 | 800 |
if __name__ == '__main__': |
698 | 801 |
sys.exit(GenericMain(commands, override={"tag_type": constants.TAG_CLUSTER})) |
Also available in: Unified diff