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'.
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
47 # Dictionary with instance old keys, and new hypervisor keys
49 'hvm_pae': constants.HV_PAE,
50 'vnc_bind_address': constants.HV_VNC_BIND_ADDRESS,
51 'initrd_path': constants.HV_INITRD_PATH,
52 'hvm_nic_type': constants.HV_NIC_TYPE,
53 'kernel_path': constants.HV_KERNEL_PATH,
54 'hvm_acpi': constants.HV_ACPI,
55 'hvm_cdrom_image_path': constants.HV_CDROM_IMAGE_PATH,
56 'hvm_boot_order': constants.HV_BOOT_ORDER,
57 'hvm_disk_type': constants.HV_DISK_TYPE,
60 # Instance beparams changes
62 'vcpus': constants.BE_VCPUS,
63 'memory': constants.BE_MEMORY,
64 'auto_balance': constants.BE_AUTO_BALANCE,
68 F_SERIAL = 'serial_no'
71 class Error(Exception):
72 """Generic exception"""
77 """Configures the logging module.
80 formatter = logging.Formatter("%(asctime)s: %(message)s")
82 stderr_handler = logging.StreamHandler()
83 stderr_handler.setFormatter(formatter)
85 stderr_handler.setLevel(logging.NOTSET)
87 stderr_handler.setLevel(logging.INFO)
89 stderr_handler.setLevel(logging.CRITICAL)
91 root_logger = logging.getLogger("")
92 root_logger.setLevel(logging.NOTSET)
93 root_logger.addHandler(stderr_handler)
100 global options, args # pylint: disable-msg=W0603
102 program = os.path.basename(sys.argv[0])
105 parser = optparse.OptionParser(usage="%prog [--debug|--verbose] [--force]")
106 parser.add_option('--dry-run', dest='dry_run',
108 help="Try to do the conversion, but don't write"
110 parser.add_option(cli.FORCE_OPT)
111 parser.add_option(cli.DEBUG_OPT)
112 parser.add_option(cli.VERBOSE_OPT)
113 parser.add_option('--path', help="Convert configuration in this"
114 " directory instead of '%s'" % constants.DATA_DIR,
115 default=constants.DATA_DIR, dest="data_dir")
116 (options, args) = parser.parse_args()
118 # We need to keep filenames locally because they might be renamed between
120 options.CONFIG_DATA_PATH = options.data_dir + "/config.data"
121 options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
122 options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
123 options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
124 options.HMAC_CLUSTER_KEY = options.data_dir + "/hmac.key"
130 raise Error("No arguments expected")
132 if not options.force:
133 usertext = ("%s MUST be run on the master node. Is this the master"
134 " node and are ALL instances down?" % program)
135 if not cli.AskUser(usertext):
138 # Check whether it's a Ganeti configuration directory
139 if not (os.path.isfile(options.CONFIG_DATA_PATH) and
140 os.path.isfile(options.SERVER_PEM_PATH) or
141 os.path.isfile(options.KNOWN_HOSTS_PATH)):
142 raise Error(("%s does not seem to be a known Ganeti configuration"
143 " directory") % options.data_dir)
145 config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
148 config_version = config_data["version"]
150 raise Error("Unable to determine configuration version")
152 (config_major, config_minor, config_revision) = \
153 constants.SplitVersion(config_version)
155 logging.info("Found configuration version %s (%d.%d.%d)",
156 config_version, config_major, config_minor, config_revision)
158 if "config_version" in config_data["cluster"]:
159 raise Error("Inconsistent configuration: found config_version in"
160 " configuration file")
162 if config_major == 2 and config_minor == 0:
163 if config_revision != 0:
164 logging.warning("Config revision is not 0")
166 config_data["version"] = constants.BuildVersion(2, 1, 0)
169 logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
170 utils.WriteFile(file_name=options.CONFIG_DATA_PATH,
171 data=serializer.DumpJson(config_data),
173 dry_run=options.dry_run,
176 if not options.dry_run:
177 if not os.path.exists(options.RAPI_CERT_FILE):
178 logging.debug("Writing RAPI certificate to %s", options.RAPI_CERT_FILE)
179 utils.GenerateSelfSignedSslCert(options.RAPI_CERT_FILE)
181 if not os.path.exists(options.HMAC_CLUSTER_KEY):
182 logging.debug("Writing HMAC key to %s", options.HMAC_CLUSTER_KEY)
183 bootstrap.GenerateHmacKey(options.HMAC_CLUSTER_KEY)
186 logging.critical("Writing configuration failed. It is probably in an"
187 " inconsistent state and needs manual intervention.")
190 # test loading the config file
191 if not options.dry_run:
192 logging.info("Testing the new config file...")
193 cfg = config.ConfigWriter(cfg_file=options.CONFIG_DATA_PATH,
195 # if we reached this, it's all fine
196 vrfy = cfg.VerifyConfig()
198 logging.error("Errors after conversion:")
200 logging.error(" - %s", item)
202 logging.info("File loaded successfully")
205 if __name__ == "__main__":
208 # vim: set foldmethod=marker :