Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ 76917d97

History | View | Annotate | Download (6.3 kB)

1 0006af7d Michael Hanselmann
#!/usr/bin/python
2 0006af7d Michael Hanselmann
#
3 0006af7d Michael Hanselmann
4 aeefe835 Iustin Pop
# Copyright (C) 2007, 2008, 2009, 2010 Google Inc.
5 0006af7d Michael Hanselmann
#
6 0006af7d Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 0006af7d Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 0006af7d Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 0006af7d Michael Hanselmann
# (at your option) any later version.
10 0006af7d Michael Hanselmann
#
11 0006af7d Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 0006af7d Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 0006af7d Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 0006af7d Michael Hanselmann
# General Public License for more details.
15 0006af7d Michael Hanselmann
#
16 0006af7d Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 0006af7d Michael Hanselmann
# along with this program; if not, write to the Free Software
18 0006af7d Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 0006af7d Michael Hanselmann
# 02110-1301, USA.
20 0006af7d Michael Hanselmann
21 0006af7d Michael Hanselmann
22 0006af7d Michael Hanselmann
"""Tool to upgrade the configuration file.
23 0006af7d Michael Hanselmann
24 a421fdeb Iustin Pop
This code handles only the types supported by simplejson. As an
25 a421fdeb Iustin Pop
example, 'set' is a 'list'.
26 0006af7d Michael Hanselmann
27 0006af7d Michael Hanselmann
"""
28 0006af7d Michael Hanselmann
29 0006af7d Michael Hanselmann
30 0006af7d Michael Hanselmann
import os
31 0006af7d Michael Hanselmann
import os.path
32 0006af7d Michael Hanselmann
import sys
33 0006af7d Michael Hanselmann
import optparse
34 eda37a5a Michael Hanselmann
import logging
35 0006af7d Michael Hanselmann
36 95e4a814 Michael Hanselmann
from ganeti import constants
37 95e4a814 Michael Hanselmann
from ganeti import serializer
38 319856a9 Michael Hanselmann
from ganeti import utils
39 f97c7901 Michael Hanselmann
from ganeti import cli
40 a421fdeb Iustin Pop
from ganeti import bootstrap
41 ac4d25b6 Iustin Pop
from ganeti import config
42 0006af7d Michael Hanselmann
43 0006af7d Michael Hanselmann
44 319856a9 Michael Hanselmann
options = None
45 319856a9 Michael Hanselmann
args = None
46 0006af7d Michael Hanselmann
47 6f285030 Iustin Pop
48 319856a9 Michael Hanselmann
class Error(Exception):
49 319856a9 Michael Hanselmann
  """Generic exception"""
50 319856a9 Michael Hanselmann
  pass
51 0006af7d Michael Hanselmann
52 0006af7d Michael Hanselmann
53 eda37a5a Michael Hanselmann
def SetupLogging():
54 eda37a5a Michael Hanselmann
  """Configures the logging module.
55 eda37a5a Michael Hanselmann
56 eda37a5a Michael Hanselmann
  """
57 eda37a5a Michael Hanselmann
  formatter = logging.Formatter("%(asctime)s: %(message)s")
58 eda37a5a Michael Hanselmann
59 eda37a5a Michael Hanselmann
  stderr_handler = logging.StreamHandler()
60 eda37a5a Michael Hanselmann
  stderr_handler.setFormatter(formatter)
61 eda37a5a Michael Hanselmann
  if options.debug:
62 eda37a5a Michael Hanselmann
    stderr_handler.setLevel(logging.NOTSET)
63 eda37a5a Michael Hanselmann
  elif options.verbose:
64 eda37a5a Michael Hanselmann
    stderr_handler.setLevel(logging.INFO)
65 eda37a5a Michael Hanselmann
  else:
66 eda37a5a Michael Hanselmann
    stderr_handler.setLevel(logging.CRITICAL)
67 eda37a5a Michael Hanselmann
68 eda37a5a Michael Hanselmann
  root_logger = logging.getLogger("")
69 eda37a5a Michael Hanselmann
  root_logger.setLevel(logging.NOTSET)
70 eda37a5a Michael Hanselmann
  root_logger.addHandler(stderr_handler)
71 eda37a5a Michael Hanselmann
72 eda37a5a Michael Hanselmann
73 6d691282 Michael Hanselmann
def main():
74 6d691282 Michael Hanselmann
  """Main program.
75 6d691282 Michael Hanselmann
76 6d691282 Michael Hanselmann
  """
77 7260cfbe Iustin Pop
  global options, args # pylint: disable-msg=W0603
78 6d691282 Michael Hanselmann
79 319856a9 Michael Hanselmann
  program = os.path.basename(sys.argv[0])
80 319856a9 Michael Hanselmann
81 0006af7d Michael Hanselmann
  # Option parsing
82 95e4a814 Michael Hanselmann
  parser = optparse.OptionParser(usage="%prog [--debug|--verbose] [--force]")
83 60edf71e Michael Hanselmann
  parser.add_option('--dry-run', dest='dry_run',
84 60edf71e Michael Hanselmann
                    action="store_true",
85 f4bc1f2c Michael Hanselmann
                    help="Try to do the conversion, but don't write"
86 f4bc1f2c Michael Hanselmann
                         " output file")
87 f97c7901 Michael Hanselmann
  parser.add_option(cli.FORCE_OPT)
88 eda37a5a Michael Hanselmann
  parser.add_option(cli.DEBUG_OPT)
89 9cdb9578 Iustin Pop
  parser.add_option(cli.VERBOSE_OPT)
90 ac4d25b6 Iustin Pop
  parser.add_option('--path', help="Convert configuration in this"
91 ac4d25b6 Iustin Pop
                    " directory instead of '%s'" % constants.DATA_DIR,
92 ac4d25b6 Iustin Pop
                    default=constants.DATA_DIR, dest="data_dir")
93 02e1292d Michael Hanselmann
  parser.add_option("--no-verify",
94 02e1292d Michael Hanselmann
                    help="Do not verify configuration after upgrade",
95 02e1292d Michael Hanselmann
                    action="store_true", dest="no_verify", default=False)
96 0006af7d Michael Hanselmann
  (options, args) = parser.parse_args()
97 0006af7d Michael Hanselmann
98 ac4d25b6 Iustin Pop
  # We need to keep filenames locally because they might be renamed between
99 ac4d25b6 Iustin Pop
  # versions.
100 0cddd44d Iustin Pop
  options.data_dir = os.path.abspath(options.data_dir)
101 ac4d25b6 Iustin Pop
  options.CONFIG_DATA_PATH = options.data_dir + "/config.data"
102 ac4d25b6 Iustin Pop
  options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
103 ac4d25b6 Iustin Pop
  options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
104 ac4d25b6 Iustin Pop
  options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
105 6b7d5878 Michael Hanselmann
  options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
106 fc0726b9 Michael Hanselmann
  options.CDS_FILE = options.data_dir + "/cluster-domain-secret"
107 ac4d25b6 Iustin Pop
108 eda37a5a Michael Hanselmann
  SetupLogging()
109 eda37a5a Michael Hanselmann
110 0006af7d Michael Hanselmann
  # Option checking
111 0006af7d Michael Hanselmann
  if args:
112 95e4a814 Michael Hanselmann
    raise Error("No arguments expected")
113 0006af7d Michael Hanselmann
114 319856a9 Michael Hanselmann
  if not options.force:
115 a421fdeb Iustin Pop
    usertext = ("%s MUST be run on the master node. Is this the master"
116 a421fdeb Iustin Pop
                " node and are ALL instances down?" % program)
117 f97c7901 Michael Hanselmann
    if not cli.AskUser(usertext):
118 a9221f09 Michael Hanselmann
      sys.exit(constants.EXIT_FAILURE)
119 319856a9 Michael Hanselmann
120 95e4a814 Michael Hanselmann
  # Check whether it's a Ganeti configuration directory
121 ac4d25b6 Iustin Pop
  if not (os.path.isfile(options.CONFIG_DATA_PATH) and
122 30acff6c Michael Hanselmann
          os.path.isfile(options.SERVER_PEM_PATH) and
123 ac4d25b6 Iustin Pop
          os.path.isfile(options.KNOWN_HOSTS_PATH)):
124 a9221f09 Michael Hanselmann
    raise Error(("%s does not seem to be a Ganeti configuration"
125 ac4d25b6 Iustin Pop
                 " directory") % options.data_dir)
126 95e4a814 Michael Hanselmann
127 11c31f5c Michael Hanselmann
  config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
128 a421fdeb Iustin Pop
129 11c31f5c Michael Hanselmann
  try:
130 11c31f5c Michael Hanselmann
    config_version = config_data["version"]
131 11c31f5c Michael Hanselmann
  except KeyError:
132 11c31f5c Michael Hanselmann
    raise Error("Unable to determine configuration version")
133 0006af7d Michael Hanselmann
134 11c31f5c Michael Hanselmann
  (config_major, config_minor, config_revision) = \
135 11c31f5c Michael Hanselmann
    constants.SplitVersion(config_version)
136 319856a9 Michael Hanselmann
137 11c31f5c Michael Hanselmann
  logging.info("Found configuration version %s (%d.%d.%d)",
138 11c31f5c Michael Hanselmann
               config_version, config_major, config_minor, config_revision)
139 319856a9 Michael Hanselmann
140 11c31f5c Michael Hanselmann
  if "config_version" in config_data["cluster"]:
141 11c31f5c Michael Hanselmann
    raise Error("Inconsistent configuration: found config_version in"
142 11c31f5c Michael Hanselmann
                " configuration file")
143 95e4a814 Michael Hanselmann
144 a9221f09 Michael Hanselmann
  # Upgrade from 2.0/2.1 to 2.2
145 a9221f09 Michael Hanselmann
  if config_major == 2 and config_minor in (0, 1):
146 aeb0c953 Michael Hanselmann
    if config_revision != 0:
147 a9221f09 Michael Hanselmann
      logging.warning("Config revision is %s, not 0", config_revision)
148 aeb0c953 Michael Hanselmann
149 a9221f09 Michael Hanselmann
    config_data["version"] = constants.BuildVersion(2, 2, 0)
150 a9221f09 Michael Hanselmann
151 a9221f09 Michael Hanselmann
  elif config_major == 2 and config_minor == 2:
152 a9221f09 Michael Hanselmann
    logging.info("No changes necessary")
153 a9221f09 Michael Hanselmann
154 a9221f09 Michael Hanselmann
  else:
155 a9221f09 Michael Hanselmann
    raise Error("Configuration version %d.%d.%d not supported by this tool" %
156 a9221f09 Michael Hanselmann
                (config_major, config_minor, config_revision))
157 aeb0c953 Michael Hanselmann
158 11c31f5c Michael Hanselmann
  try:
159 11c31f5c Michael Hanselmann
    logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
160 11c31f5c Michael Hanselmann
    utils.WriteFile(file_name=options.CONFIG_DATA_PATH,
161 11c31f5c Michael Hanselmann
                    data=serializer.DumpJson(config_data),
162 11c31f5c Michael Hanselmann
                    mode=0600,
163 11c31f5c Michael Hanselmann
                    dry_run=options.dry_run,
164 11c31f5c Michael Hanselmann
                    backup=True)
165 a421fdeb Iustin Pop
166 a421fdeb Iustin Pop
    if not options.dry_run:
167 af2ae1c0 Iustin Pop
      bootstrap.GenerateClusterCrypto(False, False, False, False,
168 aeefe835 Iustin Pop
                                      nodecert_file=options.SERVER_PEM_PATH,
169 aeefe835 Iustin Pop
                                      rapicert_file=options.RAPI_CERT_FILE,
170 fc0726b9 Michael Hanselmann
                                      hmackey_file=options.CONFD_HMAC_KEY,
171 fc0726b9 Michael Hanselmann
                                      cds_file=options.CDS_FILE)
172 aeb0c953 Michael Hanselmann
173 a9221f09 Michael Hanselmann
  except Exception:
174 11c31f5c Michael Hanselmann
    logging.critical("Writing configuration failed. It is probably in an"
175 95e4a814 Michael Hanselmann
                     " inconsistent state and needs manual intervention.")
176 95e4a814 Michael Hanselmann
    raise
177 0006af7d Michael Hanselmann
178 ac4d25b6 Iustin Pop
  # test loading the config file
179 02e1292d Michael Hanselmann
  if not (options.dry_run or options.no_verify):
180 ac4d25b6 Iustin Pop
    logging.info("Testing the new config file...")
181 ac4d25b6 Iustin Pop
    cfg = config.ConfigWriter(cfg_file=options.CONFIG_DATA_PATH,
182 ac4d25b6 Iustin Pop
                              offline=True)
183 ac4d25b6 Iustin Pop
    # if we reached this, it's all fine
184 ac4d25b6 Iustin Pop
    vrfy = cfg.VerifyConfig()
185 ac4d25b6 Iustin Pop
    if vrfy:
186 ac4d25b6 Iustin Pop
      logging.error("Errors after conversion:")
187 ac4d25b6 Iustin Pop
      for item in vrfy:
188 07b8a2b5 Iustin Pop
        logging.error(" - %s", item)
189 ac4d25b6 Iustin Pop
    del cfg
190 ac4d25b6 Iustin Pop
    logging.info("File loaded successfully")
191 ac4d25b6 Iustin Pop
192 6d691282 Michael Hanselmann
193 6d691282 Michael Hanselmann
if __name__ == "__main__":
194 6d691282 Michael Hanselmann
  main()
195 6d691282 Michael Hanselmann
196 319856a9 Michael Hanselmann
# vim: set foldmethod=marker :