4 # Copyright (C) 2007, 2008, 2009 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
48 # Dictionary with instance old keys, and new hypervisor keys
50 'hvm_pae': constants.HV_PAE,
51 'vnc_bind_address': constants.HV_VNC_BIND_ADDRESS,
52 'initrd_path': constants.HV_INITRD_PATH,
53 'hvm_nic_type': constants.HV_NIC_TYPE,
54 'kernel_path': constants.HV_KERNEL_PATH,
55 'hvm_acpi': constants.HV_ACPI,
56 'hvm_cdrom_image_path': constants.HV_CDROM_IMAGE_PATH,
57 'hvm_boot_order': constants.HV_BOOT_ORDER,
58 'hvm_disk_type': constants.HV_DISK_TYPE,
61 # Instance beparams changes
63 'vcpus': constants.BE_VCPUS,
64 'memory': constants.BE_MEMORY,
65 'auto_balance': constants.BE_AUTO_BALANCE,
69 F_SERIAL = 'serial_no'
72 class Error(Exception):
73 """Generic exception"""
78 """Configures the logging module.
81 formatter = logging.Formatter("%(asctime)s: %(message)s")
83 stderr_handler = logging.StreamHandler()
84 stderr_handler.setFormatter(formatter)
86 stderr_handler.setLevel(logging.NOTSET)
88 stderr_handler.setLevel(logging.INFO)
90 stderr_handler.setLevel(logging.CRITICAL)
92 root_logger = logging.getLogger("")
93 root_logger.setLevel(logging.NOTSET)
94 root_logger.addHandler(stderr_handler)
103 program = os.path.basename(sys.argv[0])
106 parser = optparse.OptionParser(usage="%prog [--debug|--verbose] [--force]")
107 parser.add_option('--dry-run', dest='dry_run',
109 help="Try to do the conversion, but don't write"
111 parser.add_option(cli.FORCE_OPT)
112 parser.add_option(cli.DEBUG_OPT)
113 parser.add_option(cli.VERBOSE_OPT)
114 parser.add_option('--path', help="Convert configuration in this"
115 " directory instead of '%s'" % constants.DATA_DIR,
116 default=constants.DATA_DIR, dest="data_dir")
117 (options, args) = parser.parse_args()
119 # We need to keep filenames locally because they might be renamed between
121 options.CONFIG_DATA_PATH = options.data_dir + "/config.data"
122 options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
123 options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
124 options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
125 options.HMAC_CLUSTER_KEY = options.data_dir + "/hmac.key"
131 raise Error("No arguments expected")
133 if not options.force:
134 usertext = ("%s MUST be run on the master node. Is this the master"
135 " node and are ALL instances down?" % program)
136 if not cli.AskUser(usertext):
139 # Check whether it's a Ganeti configuration directory
140 if not (os.path.isfile(options.CONFIG_DATA_PATH) and
141 os.path.isfile(options.SERVER_PEM_PATH) or
142 os.path.isfile(options.KNOWN_HOSTS_PATH)):
143 raise Error(("%s does not seem to be a known Ganeti configuration"
144 " directory") % options.data_dir)
146 config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
149 config_version = config_data["version"]
151 raise Error("Unable to determine configuration version")
153 (config_major, config_minor, config_revision) = \
154 constants.SplitVersion(config_version)
156 logging.info("Found configuration version %s (%d.%d.%d)",
157 config_version, config_major, config_minor, config_revision)
159 if "config_version" in config_data["cluster"]:
160 raise Error("Inconsistent configuration: found config_version in"
161 " configuration file")
163 if config_major == 2 and config_minor == 0:
164 if config_revision != 0:
165 logging.warning("Config revision is not 0")
167 config_data["version"] = constants.BuildVersion(2, 1, 0)
170 logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
171 utils.WriteFile(file_name=options.CONFIG_DATA_PATH,
172 data=serializer.DumpJson(config_data),
174 dry_run=options.dry_run,
177 if not options.dry_run:
178 if not os.path.exists(options.RAPI_CERT_FILE):
179 logging.debug("Writing RAPI certificate to %s", options.RAPI_CERT_FILE)
180 bootstrap.GenerateSelfSignedSslCert(options.RAPI_CERT_FILE)
182 if not os.path.exists(options.HMAC_CLUSTER_KEY):
183 logging.debug("Writing HMAC key to %s", options.HMAC_CLUSTER_KEY)
184 bootstrap.GenerateHmacKey(options.HMAC_CLUSTER_KEY)
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:
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 :