diff --git a/test/py/cfgupgrade_unittest.py b/test/py/cfgupgrade_unittest.py index edfd11e35560a0fe19fea5db0f8f7ae560358b44..311a68f478351f8ebd655dfac705ce0927b022aa 100755 --- a/test/py/cfgupgrade_unittest.py +++ b/test/py/cfgupgrade_unittest.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -# Copyright (C) 2010, 2012 Google Inc. +# Copyright (C) 2010, 2012, 2013 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 @@ -37,7 +37,8 @@ from ganeti import netutils import testutils -def _RunUpgrade(path, dry_run, no_verify, ignore_hostname=True): +def _RunUpgrade(path, dry_run, no_verify, ignore_hostname=True, + downgrade=False): cmd = [sys.executable, "%s/tools/cfgupgrade" % testutils.GetSourceDir(), "--debug", "--force", "--path=%s" % path, "--confdir=%s" % path] @@ -47,6 +48,8 @@ def _RunUpgrade(path, dry_run, no_verify, ignore_hostname=True): cmd.append("--dry-run") if no_verify: cmd.append("--no-verify") + if downgrade: + cmd.append("--downgrade") result = utils.RunCmd(cmd, cwd=os.getcwd()) if result.failed: @@ -347,6 +350,22 @@ class TestCfgupgrade(unittest.TestCase): def testUpgradeCurrent(self): self._TestSimpleUpgrade(constants.CONFIG_VERSION, False) + def testDowngrade(self): + self._TestSimpleUpgrade(constants.CONFIG_VERSION, False) + oldconf = self._LoadConfig() + _RunUpgrade(self.tmpdir, False, True, downgrade=True) + _RunUpgrade(self.tmpdir, False, True) + newconf = self._LoadConfig() + self.assertEqual(oldconf, newconf) + + def testDowngradeTwice(self): + self._TestSimpleUpgrade(constants.CONFIG_VERSION, False) + _RunUpgrade(self.tmpdir, False, True, downgrade=True) + oldconf = self._LoadConfig() + _RunUpgrade(self.tmpdir, False, True, downgrade=True) + newconf = self._LoadConfig() + self.assertEqual(oldconf, newconf) + def testUpgradeDryRunFrom_2_0(self): self._TestSimpleUpgrade(constants.BuildVersion(2, 0, 0), True) @@ -371,6 +390,12 @@ class TestCfgupgrade(unittest.TestCase): def testUpgradeCurrentDryRun(self): self._TestSimpleUpgrade(constants.CONFIG_VERSION, True) + def testDowngradeDryRun(self): + self._TestSimpleUpgrade(constants.CONFIG_VERSION, False) + oldconf = self._LoadConfig() + _RunUpgrade(self.tmpdir, True, True, downgrade=True) + newconf = self._LoadConfig() + self.assertEqual(oldconf["version"], newconf["version"]) if __name__ == "__main__": testutils.GanetiTestProgram() diff --git a/tools/cfgupgrade b/tools/cfgupgrade index 571185a49fc82bf2ca22a6836925840995dbc3fb..8e5002837e580cd59627912cc0fe373cd9732d04 100755 --- a/tools/cfgupgrade +++ b/tools/cfgupgrade @@ -53,6 +53,10 @@ args = None TARGET_MAJOR = 2 #: Target minor version we will upgrade to TARGET_MINOR = 7 +#: Target major version for downgrade +DOWNGRADE_MAJOR = 2 +#: Target minor version for downgrade +DOWNGRADE_MINOR = 7 class Error(Exception): @@ -215,6 +219,27 @@ def UpgradeAll(config_data): UpgradeInstances(config_data) +def DowngradeStorageTypes(cluster): + # Remove storage types to downgrade to 2.7 + if "enabled_storage_types" in cluster: + logging.warning("Removing cluster storage types; value = %s", + utils.CommaJoin(cluster["enabled_storage_types"])) + del cluster["enabled_storage_types"] + + +def DowngradeCluster(config_data): + cluster = config_data.get("cluster", None) + if cluster is None: + raise Error("Cannot find cluster") + DowngradeStorageTypes(cluster) + + +def DowngradeAll(config_data): + # Any code specific to a particular version should be labeled that way, so + # it can be removed when updating to the next version. + DowngradeCluster(config_data) + + def main(): """Main program. @@ -243,6 +268,9 @@ def main(): parser.add_option("--no-verify", help="Do not verify configuration after upgrade", action="store_true", dest="no_verify", default=False) + parser.add_option("--downgrade", + help="Downgrade to the previous stable version", + action="store_true", dest="downgrade", default=False) (options, args) = parser.parse_args() # We need to keep filenames locally because they might be renamed between @@ -267,6 +295,8 @@ def main(): # Option checking if args: raise Error("No arguments expected") + if options.downgrade and not options.no_verify: + options.no_verify = True # Check master name if not (CheckHostname(options.SSCONF_MASTER_NODE) or options.ignore_hostname): @@ -274,10 +304,19 @@ def main(): sys.exit(constants.EXIT_FAILURE) if not options.force: - usertext = ("Please make sure you have read the upgrade notes for" - " Ganeti %s (available in the UPGRADE file and included" - " in other documentation formats). Continue with upgrading" - " configuration?" % constants.RELEASE_VERSION) + if options.downgrade: + usertext = ("The configuration is going to be DOWNGRADED to version %s.%s" + " Some configuration data might be removed if they don't fit" + " in the old format. Please make sure you have read the" + " upgrade notes (available in the UPGRADE file and included" + " in other documentation formats) to understand what they" + " are. Continue with *DOWNGRADING* the configuration?" % + (DOWNGRADE_MAJOR, DOWNGRADE_MINOR)) + else: + usertext = ("Please make sure you have read the upgrade notes for" + " Ganeti %s (available in the UPGRADE file and included" + " in other documentation formats). Continue with upgrading" + " configuration?" % constants.RELEASE_VERSION) if not cli.AskUser(usertext): sys.exit(constants.EXIT_FAILURE) @@ -308,8 +347,17 @@ def main(): raise Error("Inconsistent configuration: found config_version in" " configuration file") + # Downgrade to the previous stable version + if options.downgrade: + if config_major != TARGET_MAJOR or config_minor != TARGET_MINOR: + raise Error("Downgrade supported only from the latest version (%s.%s)," + " found %s (%s.%s.%s) instead" % + (TARGET_MAJOR, TARGET_MINOR, config_version, config_major, + config_minor, config_revision)) + DowngradeAll(config_data) + # Upgrade from 2.{0..6} to 2.7 - if config_major == 2 and config_minor in (0, 1, 2, 3, 4, 5, 6): + elif config_major == 2 and config_minor in (0, 1, 2, 3, 4, 5, 6): if config_revision != 0: logging.warning("Config revision is %s, not 0", config_revision) UpgradeAll(config_data) @@ -362,12 +410,18 @@ def main(): logging.info("File loaded successfully after upgrading") del cfg + if options.downgrade: + action = "downgraded" + out_ver = "%s.%s" % (DOWNGRADE_MAJOR, DOWNGRADE_MINOR) + else: + action = "upgraded" + out_ver = constants.RELEASE_VERSION if all_ok: - cli.ToStderr("Configuration successfully upgraded to version %s.", - constants.RELEASE_VERSION) + cli.ToStderr("Configuration successfully %s to version %s.", + action, out_ver) else: - cli.ToStderr("Configuration upgraded to version %s, but there are errors." - "\nPlease review the file.", constants.RELEASE_VERSION) + cli.ToStderr("Configuration %s to version %s, but there are errors." + "\nPlease review the file.", action, out_ver) if __name__ == "__main__":