Statistics
| Branch: | Tag: | Revision:

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