1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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 from ganeti.tools import common
52
53
54 _DATA_CHECK = ht.TStrictDict(False, True, {
55 constants.NDS_CLUSTER_NAME: ht.TNonEmptyString,
56 constants.NDS_NODE_DAEMON_CERTIFICATE: ht.TNonEmptyString,
57 constants.NDS_SSCONF: ht.TDictOf(ht.TNonEmptyString, ht.TString),
58 constants.NDS_START_NODE_DAEMON: ht.TBool,
59 constants.NDS_NODE_NAME: ht.TString,
60 })
61
62
64 """Local class for reporting errors.
65
66 """
67
68
70 """Parses the options passed to the program.
71
72 @return: Options and arguments
73
74 """
75 parser = optparse.OptionParser(usage="%prog [--dry-run]",
76 prog=os.path.basename(sys.argv[0]))
77 parser.add_option(cli.DEBUG_OPT)
78 parser.add_option(cli.VERBOSE_OPT)
79 parser.add_option(cli.DRY_RUN_OPT)
80
81 (opts, args) = parser.parse_args()
82
83 return VerifyOptions(parser, opts, args)
84
85
87 """Verifies options and arguments for correctness.
88
89 """
90 if args:
91 parser.error("No arguments are expected")
92
93 return opts
94
95
97 """Verifies a certificate against the local node daemon certificate.
98
99 @type cert_pem: string
100 @param cert_pem: Certificate and key in PEM format
101 @rtype: string
102 @return: Formatted key and certificate
103
104 """
105 try:
106 cert = \
107 OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert_pem)
108 except Exception, err:
109 raise errors.X509CertError("(stdin)",
110 "Unable to load certificate: %s" % err)
111
112 try:
113 key = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, cert_pem)
114 except OpenSSL.crypto.Error, err:
115 raise errors.X509CertError("(stdin)",
116 "Unable to load private key: %s" % err)
117
118
119
120 try:
121 utils.X509CertKeyCheck(cert, key)
122 except OpenSSL.SSL.Error:
123 raise errors.X509CertError("(stdin)",
124 "Certificate is not signed with given key")
125
126
127
128 _check_fn(cert)
129
130 key_encoded = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
131 cert_encoded = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
132 cert)
133 complete_cert_encoded = key_encoded + cert_encoded
134 if not cert_pem == complete_cert_encoded:
135 logging.error("The certificate differs after being reencoded. Please"
136 " renew the certificates cluster-wide to prevent future"
137 " inconsistencies.")
138
139
140 buf = StringIO()
141 buf.write(cert_pem)
142 return buf.getvalue()
143
144
146 """Verifies cluster certificate.
147
148 @type data: dict
149 @rtype: string
150 @return: Formatted key and certificate
151
152 """
153 cert = data.get(constants.NDS_NODE_DAEMON_CERTIFICATE)
154 if not cert:
155 raise SetupError("Node daemon certificate must be specified")
156
157 return _verify_fn(cert)
158
159
161 """Verifies cluster name.
162
163 @type data: dict
164 @rtype: string
165 @return: Cluster name
166
167 """
168 name = data.get(constants.NDS_CLUSTER_NAME)
169 if not name:
170 raise SetupError("Cluster name must be specified")
171
172 _verify_fn(name)
173
174 return name
175
176
178 """Verifies ssconf names.
179
180 @type data: dict
181
182 """
183 items = data.get(constants.NDS_SSCONF)
184
185 if not items:
186 raise SetupError("Ssconf values must be specified")
187
188
189
190 _verify_fn(items.keys())
191
192 if items.get(constants.SS_CLUSTER_NAME) != cluster_name:
193 raise SetupError("Cluster name in ssconf does not match")
194
195 return items
196
197
205
206
208 """Main routine.
209
210 """
211 opts = ParseOptions()
212
213 utils.SetupToolLogging(opts.debug, opts.verbose)
214
215 try:
216 getent = runtime.GetEnts()
217
218 data = LoadData(sys.stdin.read())
219
220 cluster_name = VerifyClusterName(data)
221 cert_pem = VerifyCertificate(data)
222 ssdata = VerifySsconf(data, cluster_name)
223
224 logging.info("Writing ssconf files ...")
225 ssconf.WriteSsconfFiles(ssdata, dry_run=opts.dry_run)
226
227 logging.info("Writing node daemon certificate ...")
228 utils.WriteFile(pathutils.NODED_CERT_FILE, data=cert_pem,
229 mode=pathutils.NODED_CERT_MODE,
230 uid=getent.masterd_uid, gid=getent.masterd_gid,
231 dry_run=opts.dry_run)
232 common.GenerateClientCertificate(data, SetupError)
233
234 if (data.get(constants.NDS_START_NODE_DAEMON) and
235 not opts.dry_run):
236 logging.info("Restarting node daemon ...")
237
238 stop_cmd = "%s stop-all" % pathutils.DAEMON_UTIL
239 noded_cmd = "%s start %s" % (pathutils.DAEMON_UTIL, constants.NODED)
240 mond_cmd = ""
241 if constants.ENABLE_MOND:
242 mond_cmd = "%s start %s" % (pathutils.DAEMON_UTIL, constants.MOND)
243
244 cmd = "; ".join([stop_cmd, noded_cmd, mond_cmd])
245
246 result = utils.RunCmd(cmd, interactive=True)
247 if result.failed:
248 raise SetupError("Could not start the node daemons, command '%s'"
249 " failed: %s" % (result.cmd, result.fail_reason))
250
251 logging.info("Node daemon successfully configured")
252 except Exception, err:
253 logging.debug("Caught unhandled exception", exc_info=True)
254
255 (retcode, message) = cli.FormatError(err)
256 logging.error(message)
257
258 return retcode
259 else:
260 return constants.EXIT_SUCCESS
261