Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ 88b02ee9

History | View | Annotate | Download (7 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 fdd9ac5b Michael Hanselmann
  options.RAPI_USERS_FILE = options.data_dir + "/rapi/users"
106 fdd9ac5b Michael Hanselmann
  options.RAPI_USERS_FILE_PRE24 = options.data_dir + "/rapi_users"
107 6b7d5878 Michael Hanselmann
  options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
108 fc0726b9 Michael Hanselmann
  options.CDS_FILE = options.data_dir + "/cluster-domain-secret"
109 ac4d25b6 Iustin Pop
110 eda37a5a Michael Hanselmann
  SetupLogging()
111 eda37a5a Michael Hanselmann
112 0006af7d Michael Hanselmann
  # Option checking
113 0006af7d Michael Hanselmann
  if args:
114 95e4a814 Michael Hanselmann
    raise Error("No arguments expected")
115 0006af7d Michael Hanselmann
116 319856a9 Michael Hanselmann
  if not options.force:
117 a421fdeb Iustin Pop
    usertext = ("%s MUST be run on the master node. Is this the master"
118 a421fdeb Iustin Pop
                " node and are ALL instances down?" % program)
119 f97c7901 Michael Hanselmann
    if not cli.AskUser(usertext):
120 a9221f09 Michael Hanselmann
      sys.exit(constants.EXIT_FAILURE)
121 319856a9 Michael Hanselmann
122 95e4a814 Michael Hanselmann
  # Check whether it's a Ganeti configuration directory
123 ac4d25b6 Iustin Pop
  if not (os.path.isfile(options.CONFIG_DATA_PATH) and
124 30acff6c Michael Hanselmann
          os.path.isfile(options.SERVER_PEM_PATH) and
125 ac4d25b6 Iustin Pop
          os.path.isfile(options.KNOWN_HOSTS_PATH)):
126 a9221f09 Michael Hanselmann
    raise Error(("%s does not seem to be a Ganeti configuration"
127 ac4d25b6 Iustin Pop
                 " directory") % options.data_dir)
128 95e4a814 Michael Hanselmann
129 11c31f5c Michael Hanselmann
  config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
130 a421fdeb Iustin Pop
131 11c31f5c Michael Hanselmann
  try:
132 11c31f5c Michael Hanselmann
    config_version = config_data["version"]
133 11c31f5c Michael Hanselmann
  except KeyError:
134 11c31f5c Michael Hanselmann
    raise Error("Unable to determine configuration version")
135 0006af7d Michael Hanselmann
136 11c31f5c Michael Hanselmann
  (config_major, config_minor, config_revision) = \
137 11c31f5c Michael Hanselmann
    constants.SplitVersion(config_version)
138 319856a9 Michael Hanselmann
139 11c31f5c Michael Hanselmann
  logging.info("Found configuration version %s (%d.%d.%d)",
140 11c31f5c Michael Hanselmann
               config_version, config_major, config_minor, config_revision)
141 319856a9 Michael Hanselmann
142 11c31f5c Michael Hanselmann
  if "config_version" in config_data["cluster"]:
143 11c31f5c Michael Hanselmann
    raise Error("Inconsistent configuration: found config_version in"
144 11c31f5c Michael Hanselmann
                " configuration file")
145 95e4a814 Michael Hanselmann
146 a8ee6e94 Michael Hanselmann
  # Upgrade from 2.0/2.1/2.2 to 2.3
147 a8ee6e94 Michael Hanselmann
  if config_major == 2 and config_minor in (0, 1, 2):
148 aeb0c953 Michael Hanselmann
    if config_revision != 0:
149 a9221f09 Michael Hanselmann
      logging.warning("Config revision is %s, not 0", config_revision)
150 aeb0c953 Michael Hanselmann
151 a8ee6e94 Michael Hanselmann
    config_data["version"] = constants.BuildVersion(2, 3, 0)
152 a9221f09 Michael Hanselmann
153 a8ee6e94 Michael Hanselmann
  elif config_major == 2 and config_minor == 3:
154 a9221f09 Michael Hanselmann
    logging.info("No changes necessary")
155 a9221f09 Michael Hanselmann
156 a9221f09 Michael Hanselmann
  else:
157 a9221f09 Michael Hanselmann
    raise Error("Configuration version %d.%d.%d not supported by this tool" %
158 a9221f09 Michael Hanselmann
                (config_major, config_minor, config_revision))
159 aeb0c953 Michael Hanselmann
160 fdd9ac5b Michael Hanselmann
  if os.path.isfile(options.RAPI_USERS_FILE_PRE24):
161 fdd9ac5b Michael Hanselmann
    logging.info("Found pre-2.4 RAPI users file at %s, renaming to %s",
162 fdd9ac5b Michael Hanselmann
                 options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE)
163 fdd9ac5b Michael Hanselmann
    utils.RenameFile(options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE,
164 fdd9ac5b Michael Hanselmann
                     mkdir=True, mkdir_mode=0750)
165 fdd9ac5b Michael Hanselmann
166 fdd9ac5b Michael Hanselmann
  # Create a symlink for RAPI users file
167 fdd9ac5b Michael Hanselmann
  if not os.path.islink(options.RAPI_USERS_FILE_PRE24):
168 fdd9ac5b Michael Hanselmann
    logging.info("Creating symlink from %s to %s",
169 fdd9ac5b Michael Hanselmann
                 options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE)
170 fdd9ac5b Michael Hanselmann
    os.symlink(options.RAPI_USERS_FILE, options.RAPI_USERS_FILE_PRE24)
171 fdd9ac5b Michael Hanselmann
172 11c31f5c Michael Hanselmann
  try:
173 11c31f5c Michael Hanselmann
    logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
174 11c31f5c Michael Hanselmann
    utils.WriteFile(file_name=options.CONFIG_DATA_PATH,
175 11c31f5c Michael Hanselmann
                    data=serializer.DumpJson(config_data),
176 11c31f5c Michael Hanselmann
                    mode=0600,
177 11c31f5c Michael Hanselmann
                    dry_run=options.dry_run,
178 11c31f5c Michael Hanselmann
                    backup=True)
179 a421fdeb Iustin Pop
180 a421fdeb Iustin Pop
    if not options.dry_run:
181 af2ae1c0 Iustin Pop
      bootstrap.GenerateClusterCrypto(False, False, False, False,
182 aeefe835 Iustin Pop
                                      nodecert_file=options.SERVER_PEM_PATH,
183 aeefe835 Iustin Pop
                                      rapicert_file=options.RAPI_CERT_FILE,
184 fc0726b9 Michael Hanselmann
                                      hmackey_file=options.CONFD_HMAC_KEY,
185 fc0726b9 Michael Hanselmann
                                      cds_file=options.CDS_FILE)
186 aeb0c953 Michael Hanselmann
187 a9221f09 Michael Hanselmann
  except Exception:
188 11c31f5c Michael Hanselmann
    logging.critical("Writing configuration failed. It is probably in an"
189 95e4a814 Michael Hanselmann
                     " inconsistent state and needs manual intervention.")
190 95e4a814 Michael Hanselmann
    raise
191 0006af7d Michael Hanselmann
192 ac4d25b6 Iustin Pop
  # test loading the config file
193 02e1292d Michael Hanselmann
  if not (options.dry_run or options.no_verify):
194 ac4d25b6 Iustin Pop
    logging.info("Testing the new config file...")
195 ac4d25b6 Iustin Pop
    cfg = config.ConfigWriter(cfg_file=options.CONFIG_DATA_PATH,
196 ac4d25b6 Iustin Pop
                              offline=True)
197 ac4d25b6 Iustin Pop
    # if we reached this, it's all fine
198 ac4d25b6 Iustin Pop
    vrfy = cfg.VerifyConfig()
199 ac4d25b6 Iustin Pop
    if vrfy:
200 ac4d25b6 Iustin Pop
      logging.error("Errors after conversion:")
201 ac4d25b6 Iustin Pop
      for item in vrfy:
202 07b8a2b5 Iustin Pop
        logging.error(" - %s", item)
203 ac4d25b6 Iustin Pop
    del cfg
204 ac4d25b6 Iustin Pop
    logging.info("File loaded successfully")
205 ac4d25b6 Iustin Pop
206 6d691282 Michael Hanselmann
207 6d691282 Michael Hanselmann
if __name__ == "__main__":
208 6d691282 Michael Hanselmann
  main()