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 parser.add_option("--no-verify",
94 help="Do not verify configuration after upgrade",
95 action="store_true", dest="no_verify", default=False)
96 (options, args) = parser.parse_args()
98 # We need to keep filenames locally because they might be renamed between
100 options.data_dir = os.path.abspath(options.data_dir)
101 options.CONFIG_DATA_PATH = options.data_dir + "/config.data"
102 options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
103 options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
104 options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
105 options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
106 options.CDS_FILE = options.data_dir + "/cluster-domain-secret"
112 raise Error("No arguments expected")
114 if not options.force:
115 usertext = ("%s MUST be run on the master node. Is this the master"
116 " node and are ALL instances down?" % program)
117 if not cli.AskUser(usertext):
118 sys.exit(constants.EXIT_FAILURE)
120 # Check whether it's a Ganeti configuration directory
121 if not (os.path.isfile(options.CONFIG_DATA_PATH) and
122 os.path.isfile(options.SERVER_PEM_PATH) and
123 os.path.isfile(options.KNOWN_HOSTS_PATH)):
124 raise Error(("%s does not seem to be a Ganeti configuration"
125 " directory") % options.data_dir)
127 config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
130 config_version = config_data["version"]
132 raise Error("Unable to determine configuration version")
134 (config_major, config_minor, config_revision) = \
135 constants.SplitVersion(config_version)
137 logging.info("Found configuration version %s (%d.%d.%d)",
138 config_version, config_major, config_minor, config_revision)
140 if "config_version" in config_data["cluster"]:
141 raise Error("Inconsistent configuration: found config_version in"
142 " configuration file")
144 # Upgrade from 2.0/2.1 to 2.2
145 if config_major == 2 and config_minor in (0, 1):
146 if config_revision != 0:
147 logging.warning("Config revision is %s, not 0", config_revision)
149 config_data["version"] = constants.BuildVersion(2, 2, 0)
151 elif config_major == 2 and config_minor == 2:
152 logging.info("No changes necessary")
155 raise Error("Configuration version %d.%d.%d not supported by this tool" %
156 (config_major, config_minor, config_revision))
159 logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
160 utils.WriteFile(file_name=options.CONFIG_DATA_PATH,
161 data=serializer.DumpJson(config_data),
163 dry_run=options.dry_run,
166 if not options.dry_run:
167 bootstrap.GenerateClusterCrypto(False, False, False, False,
168 nodecert_file=options.SERVER_PEM_PATH,
169 rapicert_file=options.RAPI_CERT_FILE,
170 hmackey_file=options.CONFD_HMAC_KEY,
171 cds_file=options.CDS_FILE)
174 logging.critical("Writing configuration failed. It is probably in an"
175 " inconsistent state and needs manual intervention.")
178 # test loading the config file
179 if not (options.dry_run or options.no_verify):
180 logging.info("Testing the new config file...")
181 cfg = config.ConfigWriter(cfg_file=options.CONFIG_DATA_PATH,
183 # if we reached this, it's all fine
184 vrfy = cfg.VerifyConfig()
186 logging.error("Errors after conversion:")
188 logging.error(" - %s", item)
190 logging.info("File loaded successfully")
193 if __name__ == "__main__":
196 # vim: set foldmethod=marker :