From 0006af7d2914cf74d7808286414fa273a3da56b2 Mon Sep 17 00:00:00 2001 From: Michael Hanselmann <hansmi@google.com> Date: Thu, 2 Aug 2007 13:45:56 +0000 Subject: [PATCH] Add configuration upgrade utility. Reviewed-by: iustinp --- lib/constants.py | 2 +- tools/Makefile.am | 2 +- tools/cfgupgrade | 211 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+), 2 deletions(-) create mode 100755 tools/cfgupgrade diff --git a/lib/constants.py b/lib/constants.py index 4477a4325..2a196a72e 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -22,7 +22,7 @@ """Module holding different constants.""" # various versions -CONFIG_VERSION = 2 +CONFIG_VERSION = 3 PROTOCOL_VERSION = 2 RELEASE_VERSION = "1.2a1" OS_API_VERSION = 4 diff --git a/tools/Makefile.am b/tools/Makefile.am index 12995fa7f..20db9ec63 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1 +1 @@ -dist_pkgdata_SCRIPTS = lvmstrap burnin cfgshell +dist_pkgdata_SCRIPTS = lvmstrap burnin cfgshell cfgupgrade diff --git a/tools/cfgupgrade b/tools/cfgupgrade new file mode 100755 index 000000000..562dc15c2 --- /dev/null +++ b/tools/cfgupgrade @@ -0,0 +1,211 @@ +#!/usr/bin/python +# + +# Copyright (C) 2007 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. + +The upgrade is done by unpickling the configuration file into custom classes +derivating from dict. We then update the configuration by modifying these +dicts. To save the configuration, it's pickled into a buffer and unpickled +again using the Ganeti objects before being finally pickled into a file. + +Not using the custom classes wouldn't allow us to rename or remove attributes +between versions without loosing their values. + +""" + + +import os +import os.path +import sys +import optparse +import cPickle +import tempfile +from cStringIO import StringIO + +from ganeti import objects + +class Error(Exception): + """Generic exception""" + pass + + +def _BaseFindGlobal(module, name): + """Helper function for the other FindGlobal functions. + + """ + return getattr(sys.modules[module], name) + + +# Internal config representation +class UpgradeDict(dict): + """Base class for internal config classes. + + """ + def __setstate__(self, state): + self.update(state) + + def __getstate__(self): + return self.copy() + + +class UpgradeConfigData(UpgradeDict): pass +class UpgradeCluster(UpgradeDict): pass +class UpgradeNode(UpgradeDict): pass +class UpgradeInstance(UpgradeDict): pass +class UpgradeDisk(UpgradeDict): pass +class UpgradeNIC(UpgradeDict): pass +class UpgradeOS(UpgradeDict): pass + + +_ClassMap = { + objects.ConfigData: UpgradeConfigData, + objects.Cluster: UpgradeCluster, + objects.Node: UpgradeNode, + objects.Instance: UpgradeInstance, + objects.Disk: UpgradeDisk, + objects.NIC: UpgradeNIC, + objects.OS: UpgradeOS, +} + +# Build mapping dicts +WriteMapping = dict() +ReadMapping = dict() +for key, value in _ClassMap.iteritems(): + WriteMapping[value.__name__] = key + ReadMapping[key.__name__] = value + + +# Read config +def _ReadFindGlobal(module, name): + """Wraps Ganeti config classes to internal ones. + + """ + if module == "ganeti.objects" and name in ReadMapping: + return ReadMapping[name] + + return _BaseFindGlobal(module, name) + + +def ReadConfig(path): + """Reads configuration file. + + """ + f = open(path, 'r') + try: + loader = cPickle.Unpickler(f) + loader.find_global = _ReadFindGlobal + data = loader.load() + finally: + f.close() + + return data + + +# Write config +def _WriteFindGlobal(module, name): + """Maps our internal config classes to Ganeti's. + + """ + if module == "__main__" and name in WriteMapping: + return WriteMapping[name] + + return _BaseFindGlobal(module, name) + + +def WriteConfig(path, data): + """Writes the configuration file. + + """ + buf = StringIO() + + # Write intermediate representation + dumper = cPickle.Pickler(buf, cPickle.HIGHEST_PROTOCOL) + dumper.dump(data) + del dumper + + # Convert back to Ganeti objects + buf.seek(0) + loader = cPickle.Unpickler(buf) + loader.find_global = _WriteFindGlobal + data = loader.load() + + # Write target file + (fd, name) = tempfile.mkstemp(dir=os.path.dirname(path)) + f = os.fdopen(fd, 'w') + try: + try: + dumper = cPickle.Pickler(f, cPickle.HIGHEST_PROTOCOL) + dumper.dump(data) + f.flush() + 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'] = set() + + # 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__": + # Option parsing + parser = optparse.OptionParser() + 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") + + config = ReadConfig(cfg_file) + + UpdateFromVersion2To3(config) + + if options.verbose: + import pprint + pprint.pprint(config) + + WriteConfig(cfg_file, config) -- GitLab