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