4 # Copyright (C) 2007, 2008, 2009, 2010 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Tool to upgrade the configuration file.
24 This code handles only the types supported by simplejson. As an
25 example, 'set' is a 'list'.
36 from ganeti import constants
37 from ganeti import serializer
38 from ganeti import utils
39 from ganeti import cli
40 from ganeti import bootstrap
41 from ganeti import config
48 class Error(Exception):
49 """Generic exception"""
54 """Configures the logging module.
57 formatter = logging.Formatter("%(asctime)s: %(message)s")
59 stderr_handler = logging.StreamHandler()
60 stderr_handler.setFormatter(formatter)
62 stderr_handler.setLevel(logging.NOTSET)
64 stderr_handler.setLevel(logging.INFO)
66 stderr_handler.setLevel(logging.CRITICAL)
68 root_logger = logging.getLogger("")
69 root_logger.setLevel(logging.NOTSET)
70 root_logger.addHandler(stderr_handler)
77 global options, args # pylint: disable-msg=W0603
79 program = os.path.basename(sys.argv[0])
82 parser = optparse.OptionParser(usage="%prog [--debug|--verbose] [--force]")
83 parser.add_option('--dry-run', dest='dry_run',
85 help="Try to do the conversion, but don't write"
87 parser.add_option(cli.FORCE_OPT)
88 parser.add_option(cli.DEBUG_OPT)
89 parser.add_option(cli.VERBOSE_OPT)
90 parser.add_option('--path', help="Convert configuration in this"
91 " directory instead of '%s'" % constants.DATA_DIR,
92 default=constants.DATA_DIR, dest="data_dir")
93 (options, args) = parser.parse_args()
95 # We need to keep filenames locally because they might be renamed between
97 options.data_dir = os.path.abspath(options.data_dir)
98 options.CONFIG_DATA_PATH = options.data_dir + "/config.data"
99 options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
100 options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
101 options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
102 options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
103 options.CDS_FILE = options.data_dir + "/cluster-domain-secret"
109 raise Error("No arguments expected")
111 if not options.force:
112 usertext = ("%s MUST be run on the master node. Is this the master"
113 " node and are ALL instances down?" % program)
114 if not cli.AskUser(usertext):
115 sys.exit(constants.EXIT_FAILURE)
117 # Check whether it's a Ganeti configuration directory
118 if not (os.path.isfile(options.CONFIG_DATA_PATH) and
119 os.path.isfile(options.SERVER_PEM_PATH) or
120 os.path.isfile(options.KNOWN_HOSTS_PATH)):
121 raise Error(("%s does not seem to be a Ganeti configuration"
122 " directory") % options.data_dir)
124 config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
127 config_version = config_data["version"]
129 raise Error("Unable to determine configuration version")
131 (config_major, config_minor, config_revision) = \
132 constants.SplitVersion(config_version)
134 logging.info("Found configuration version %s (%d.%d.%d)",
135 config_version, config_major, config_minor, config_revision)
137 if "config_version" in config_data["cluster"]:
138 raise Error("Inconsistent configuration: found config_version in"
139 " configuration file")
141 # Upgrade from 2.0/2.1 to 2.2
142 if config_major == 2 and config_minor in (0, 1):
143 if config_revision != 0:
144 logging.warning("Config revision is %s, not 0", config_revision)
146 config_data["version"] = constants.BuildVersion(2, 2, 0)
148 elif config_major == 2 and config_minor == 2:
149 logging.info("No changes necessary")
152 raise Error("Configuration version %d.%d.%d not supported by this tool" %
153 (config_major, config_minor, config_revision))
156 logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
157 utils.WriteFile(file_name=options.CONFIG_DATA_PATH,
158 data=serializer.DumpJson(config_data),
160 dry_run=options.dry_run,
163 if not options.dry_run:
164 bootstrap.GenerateClusterCrypto(False, False, False, False,
165 nodecert_file=options.SERVER_PEM_PATH,
166 rapicert_file=options.RAPI_CERT_FILE,
167 hmackey_file=options.CONFD_HMAC_KEY,
168 cds_file=options.CDS_FILE)
171 logging.critical("Writing configuration failed. It is probably in an"
172 " inconsistent state and needs manual intervention.")
175 # test loading the config file
176 if not options.dry_run:
177 logging.info("Testing the new config file...")
178 cfg = config.ConfigWriter(cfg_file=options.CONFIG_DATA_PATH,
180 # if we reached this, it's all fine
181 vrfy = cfg.VerifyConfig()
183 logging.error("Errors after conversion:")
185 logging.error(" - %s", item)
187 logging.info("File loaded successfully")
190 if __name__ == "__main__":
193 # vim: set foldmethod=marker :