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'.
37 from ganeti import constants
38 from ganeti import serializer
39 from ganeti import utils
40 from ganeti import cli
41 from ganeti import bootstrap
42 from ganeti import config
49 class Error(Exception):
50 """Generic exception"""
55 """Configures the logging module.
58 formatter = logging.Formatter("%(asctime)s: %(message)s")
60 stderr_handler = logging.StreamHandler()
61 stderr_handler.setFormatter(formatter)
63 stderr_handler.setLevel(logging.NOTSET)
65 stderr_handler.setLevel(logging.INFO)
67 stderr_handler.setLevel(logging.CRITICAL)
69 root_logger = logging.getLogger("")
70 root_logger.setLevel(logging.NOTSET)
71 root_logger.addHandler(stderr_handler)
74 def Cluster22To23(cluster):
75 """Upgrades the cluster object from 2.2 to 2.3.
78 logging.info("Upgrading the cluster object")
79 if "primary_ip_family" not in cluster:
80 # Add primary ip family to config
81 cluster["primary_ip_family"] = socket.AF_INET
88 global options, args # pylint: disable-msg=W0603
90 program = os.path.basename(sys.argv[0])
93 parser = optparse.OptionParser(usage="%prog [--debug|--verbose] [--force]")
94 parser.add_option('--dry-run', dest='dry_run',
96 help="Try to do the conversion, but don't write"
98 parser.add_option(cli.FORCE_OPT)
99 parser.add_option(cli.DEBUG_OPT)
100 parser.add_option(cli.VERBOSE_OPT)
101 parser.add_option('--path', help="Convert configuration in this"
102 " directory instead of '%s'" % constants.DATA_DIR,
103 default=constants.DATA_DIR, dest="data_dir")
104 parser.add_option("--no-verify",
105 help="Do not verify configuration after upgrade",
106 action="store_true", dest="no_verify", default=False)
107 (options, args) = parser.parse_args()
109 # We need to keep filenames locally because they might be renamed between
111 options.data_dir = os.path.abspath(options.data_dir)
112 options.CONFIG_DATA_PATH = options.data_dir + "/config.data"
113 options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
114 options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
115 options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
116 options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
117 options.CDS_FILE = options.data_dir + "/cluster-domain-secret"
123 raise Error("No arguments expected")
125 if not options.force:
126 usertext = ("%s MUST be run on the master node. Is this the master"
127 " node and are ALL instances down?" % program)
128 if not cli.AskUser(usertext):
129 sys.exit(constants.EXIT_FAILURE)
131 # Check whether it's a Ganeti configuration directory
132 if not (os.path.isfile(options.CONFIG_DATA_PATH) and
133 os.path.isfile(options.SERVER_PEM_PATH) and
134 os.path.isfile(options.KNOWN_HOSTS_PATH)):
135 raise Error(("%s does not seem to be a Ganeti configuration"
136 " directory") % options.data_dir)
138 config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
141 config_version = config_data["version"]
143 raise Error("Unable to determine configuration version")
145 (config_major, config_minor, config_revision) = \
146 constants.SplitVersion(config_version)
148 logging.info("Found configuration version %s (%d.%d.%d)",
149 config_version, config_major, config_minor, config_revision)
151 if "config_version" in config_data["cluster"]:
152 raise Error("Inconsistent configuration: found config_version in"
153 " configuration file")
155 # Upgrade from 2.0/2.1 to 2.2
156 if config_major == 2 and config_minor in (0, 1):
157 if config_revision != 0:
158 logging.warning("Config revision is %s, not 0", config_revision)
160 config_data["version"] = constants.BuildVersion(2, 2, 0)
162 elif config_major == 2 and config_minor == 2:
163 logging.info("No changes necessary")
164 # TODO: For Ganeti 2.3 uncomment the following line
165 # Cluster22To23(config_data["cluster"])
168 raise Error("Configuration version %d.%d.%d not supported by this tool" %
169 (config_major, config_minor, config_revision))
172 logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
173 utils.WriteFile(file_name=options.CONFIG_DATA_PATH,
174 data=serializer.DumpJson(config_data),
176 dry_run=options.dry_run,
179 if not options.dry_run:
180 bootstrap.GenerateClusterCrypto(False, False, False, False,
181 nodecert_file=options.SERVER_PEM_PATH,
182 rapicert_file=options.RAPI_CERT_FILE,
183 hmackey_file=options.CONFD_HMAC_KEY,
184 cds_file=options.CDS_FILE)
187 logging.critical("Writing configuration failed. It is probably in an"
188 " inconsistent state and needs manual intervention.")
191 # test loading the config file
192 if not (options.dry_run or options.no_verify):
193 logging.info("Testing the new config file...")
194 cfg = config.ConfigWriter(cfg_file=options.CONFIG_DATA_PATH,
196 # if we reached this, it's all fine
197 vrfy = cfg.VerifyConfig()
199 logging.error("Errors after conversion:")
201 logging.error(" - %s", item)
203 logging.info("File loaded successfully")
206 if __name__ == "__main__":
209 # vim: set foldmethod=marker :