#!/usr/bin/python # # Copyright (C) 2007, 2008 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. """Tool to upgrade the configuration file. This code handles only the types supported by simplejson. As an example, "set" is a "list". Old Pickle based configurations files are converted to JSON during the process. """ import os import os.path import sys import re import optparse import tempfile import simplejson from ganeti import utils from ganeti import cli options = None args = None class Error(Exception): """Generic exception""" pass # {{{ Support for old Pickle files class UpgradeDict(dict): """Base class for internal config classes. """ def __setstate__(self, state): self.update(state) def __getstate__(self): return self.copy() def FindGlobal(module, name): """Wraps Ganeti config classes to internal ones. This function may only return types supported by simplejson. """ if module == "ganeti.objects": return UpgradeDict elif module == "__builtin__" and name == "set": return list return getattr(sys.modules[module], name) def ReadPickleFile(f): """Reads an old Pickle configuration. """ import cPickle loader = cPickle.Unpickler(f) loader.find_global = FindGlobal return loader.load() def IsPickleFile(f): """Checks whether a file is using the Pickle format. """ magic = f.read(128) try: return not re.match('^\s*\{', magic) finally: f.seek(-len(magic), 1) # }}} def ReadJsonFile(f): """Reads a JSON file. """ return simplejson.load(f) def ReadConfig(path): """Reads configuration file. """ f = open(path, 'r') try: if IsPickleFile(f): return ReadPickleFile(f) else: return ReadJsonFile(f) finally: f.close() def WriteConfig(path, data): """Writes the configuration file. """ if not options.dry_run: utils.CreateBackup(path) (fd, name) = tempfile.mkstemp(dir=os.path.dirname(path)) f = os.fdopen(fd, 'w') try: try: simplejson.dump(data, f) f.flush() if options.dry_run: os.unlink(name) else: os.rename(name, path) except: os.unlink(name) raise finally: f.close() def UpdateFromVersion2To3(cfg): """Updates the configuration from version 2 to 3. """ if cfg['cluster']['config_version'] != 2: return # Add port pool if 'tcpudp_port_pool' not in cfg['cluster']: cfg['cluster']['tcpudp_port_pool'] = [] # Add bridge settings if 'default_bridge' not in cfg['cluster']: cfg['cluster']['default_bridge'] = 'xen-br0' for inst in cfg['instances'].values(): for nic in inst['nics']: if 'bridge' not in nic: nic['bridge'] = None cfg['cluster']['config_version'] = 3 # Main program if __name__ == "__main__": program = os.path.basename(sys.argv[0]) # Option parsing parser = optparse.OptionParser(usage="%prog [options] ") parser.add_option('--dry-run', dest='dry_run', action="store_true", help="Try to do the conversion, but don't write" " output file") parser.add_option(cli.FORCE_OPT) parser.add_option('--verbose', dest='verbose', action="store_true", help="Verbose output") (options, args) = parser.parse_args() # Option checking if args: cfg_file = args[0] else: raise Error("Configuration file not specified") if not options.force: usertext = ("%s MUST run on the master node. Is this the master" " node?" % program) if not cli.AskUser(usertext): sys.exit(1) config = ReadConfig(cfg_file) if options.verbose: import pprint print "Before upgrade:" pprint.pprint(config) print UpdateFromVersion2To3(config) if options.verbose: print "After upgrade:" pprint.pprint(config) print WriteConfig(cfg_file, config) print "The configuration file has been updated successfully. Please run" print " gnt-cluster copyfile %s" % cfg_file print "now." # vim: set foldmethod=marker :