4 # Copyright (C) 2007, 2008 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 example, "set"
38 from ganeti import constants
39 from ganeti import serializer
40 from ganeti import utils
41 from ganeti import cli
44 # We need to keep filenames locally because they might be renamed between
46 CONFIG_DATA_PATH = constants.DATA_DIR + "/config.data"
47 SERVER_PEM_PATH = constants.DATA_DIR + "/server.pem"
48 KNOWN_HOSTS_PATH = constants.DATA_DIR + "/known_hosts"
49 SSCONF_CLUSTER_NAME_PATH = constants.DATA_DIR + "/ssconf_cluster_name"
50 SSCONF_CONFIG_VERSION_PATH = constants.DATA_DIR + "/ssconf_config_version"
55 # Unique object to identify calls without default value
59 class Error(Exception):
60 """Generic exception"""
64 def ReadFile(file_name, default=NoDefault):
68 logging.debug("Reading %s", file_name)
70 fh = open(file_name, 'r')
72 if default is not NoDefault and err.errno == errno.ENOENT:
82 def WriteFile(file_name, data):
83 """Writes a configuration file.
86 logging.debug("Writing %s", file_name)
87 utils.WriteFile(file_name=file_name, data=data, mode=0600,
88 dry_run=options.dry_run, backup=True)
92 """Configures the logging module.
95 formatter = logging.Formatter("%(asctime)s: %(message)s")
97 stderr_handler = logging.StreamHandler()
98 stderr_handler.setFormatter(formatter)
100 stderr_handler.setLevel(logging.NOTSET)
101 elif options.verbose:
102 stderr_handler.setLevel(logging.INFO)
104 stderr_handler.setLevel(logging.CRITICAL)
106 root_logger = logging.getLogger("")
107 root_logger.setLevel(logging.NOTSET)
108 root_logger.addHandler(stderr_handler)
117 program = os.path.basename(sys.argv[0])
120 parser = optparse.OptionParser(usage="%prog [--debug|--verbose] [--force]")
121 parser.add_option('--dry-run', dest='dry_run',
123 help="Try to do the conversion, but don't write"
125 parser.add_option(cli.FORCE_OPT)
126 parser.add_option(cli.DEBUG_OPT)
127 parser.add_option('--verbose', dest='verbose',
129 help="Verbose output")
130 (options, args) = parser.parse_args()
136 raise Error("No arguments expected")
138 if not options.force:
139 usertext = ("%s MUST run on the master node. Is this the master"
141 if not cli.AskUser(usertext):
144 # Check whether it's a Ganeti configuration directory
145 if not (os.path.isfile(CONFIG_DATA_PATH) and
146 os.path.isfile(SERVER_PEM_PATH) or
147 os.path.isfile(KNOWN_HOSTS_PATH)):
148 raise Error(("%s does not seem to be a known Ganeti configuration"
149 " directory") % constants.DATA_DIR)
151 config_version = ReadFile(SSCONF_CONFIG_VERSION_PATH, "1.2").strip()
152 logging.info("Found configuration version %s", config_version)
154 config_data = serializer.LoadJson(ReadFile(CONFIG_DATA_PATH))
157 if config_version == "1.2":
158 logging.info("Found a Ganeti 1.2 configuration")
160 old_config_version = config_data["cluster"].get("config_version", None)
161 logging.info("Found old configuration version %s", old_config_version)
162 if old_config_version not in (3, ):
163 raise Error("Unsupported configuration version: %s" %
166 # Make sure no instance uses remote_raid1 anymore
167 remote_raid1_instances = []
168 for instance in config_data["instances"]:
169 if instance["disk_template"] == "remote_raid1":
170 remote_raid1_instances.append(instance["name"])
171 if remote_raid1_instances:
172 for name in remote_raid1_instances:
173 logging.error("Instance %s still using remote_raid1 disk template")
174 raise Error("Unable to convert configuration as long as there are"
175 " instances using remote_raid1 disk template")
177 # The configuration version will be stored in a ssconf file
178 if 'config_version' in config_data['cluster']:
179 del config_data['cluster']['config_version']
181 # Build content of new known_hosts file
182 cluster_name = ReadFile(SSCONF_CLUSTER_NAME_PATH).rstrip()
183 cluster_key = config_data['cluster']['rsahostkeypub']
184 known_hosts = "%s ssh-rsa %s\n" % (cluster_name, cluster_key)
187 logging.info("Found a Ganeti 2.0 configuration")
189 if "config_version" in config_data["cluster"]:
190 raise Error("Inconsistent configuration: found config_data in"
191 " configuration file")
195 config_version_str = "%s\n" % constants.BuildVersion(2, 0, 0)
197 logging.info("Writing configuration file")
198 WriteFile(CONFIG_DATA_PATH, serializer.DumpJson(config_data))
200 logging.info("Writing configuration version %s",
201 config_version_str.strip())
202 WriteFile(SSCONF_CONFIG_VERSION_PATH, config_version_str)
204 if known_hosts is not None:
205 logging.info("Writing SSH known_hosts file (%s)", known_hosts.strip())
206 WriteFile(KNOWN_HOSTS_PATH, known_hosts)
208 logging.critical("Writing configuration failed. It is proably in an"
209 " inconsistent state and needs manual intervention.")
213 if __name__ == "__main__":
216 # vim: set foldmethod=marker :