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