| #!/usr/bin/python |
| # |
| |
| # Copyright (C) 2010, 2012, 2013 Google Inc. |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: |
| # |
| # 1. Redistributions of source code must retain the above copyright notice, |
| # this list of conditions and the following disclaimer. |
| # |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
| # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| |
| """Script for testing tools/cfgupgrade""" |
| |
| import os |
| import sys |
| import unittest |
| import shutil |
| import tempfile |
| import operator |
| |
| from ganeti import constants |
| from ganeti import utils |
| from ganeti import serializer |
| from ganeti import netutils |
| |
| from ganeti.utils import version |
| |
| import testutils |
| |
| |
| def GetMinimalConfig(): |
| return { |
| "version": constants.CONFIG_VERSION, |
| "cluster": { |
| "master_node": "node1-uuid", |
| "ipolicy": None |
| }, |
| "instances": {}, |
| "networks": {}, |
| "nodegroups": {}, |
| "nodes": { |
| "node1-uuid": { |
| "name": "node1", |
| "uuid": "node1-uuid" |
| } |
| }, |
| } |
| |
| |
| 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] |
| |
| if ignore_hostname: |
| cmd.append("--ignore-hostname") |
| if dry_run: |
| 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: |
| raise Exception("cfgupgrade failed: %s, output %r" % |
| (result.fail_reason, result.output)) |
| |
| |
| class TestCfgupgrade(unittest.TestCase): |
| def setUp(self): |
| self.tmpdir = tempfile.mkdtemp() |
| |
| self.config_path = utils.PathJoin(self.tmpdir, "config.data") |
| self.noded_cert_path = utils.PathJoin(self.tmpdir, "server.pem") |
| self.rapi_cert_path = utils.PathJoin(self.tmpdir, "rapi.pem") |
| self.rapi_users_path = utils.PathJoin(self.tmpdir, "rapi", "users") |
| self.rapi_users_path_pre24 = utils.PathJoin(self.tmpdir, "rapi_users") |
| self.known_hosts_path = utils.PathJoin(self.tmpdir, "known_hosts") |
| self.confd_hmac_path = utils.PathJoin(self.tmpdir, "hmac.key") |
| self.cds_path = utils.PathJoin(self.tmpdir, "cluster-domain-secret") |
| self.ss_master_node_path = utils.PathJoin(self.tmpdir, "ssconf_master_node") |
| self.file_storage_paths = utils.PathJoin(self.tmpdir, "file-storage-paths") |
| |
| def tearDown(self): |
| shutil.rmtree(self.tmpdir) |
| |
| def _LoadConfig(self): |
| return serializer.LoadJson(utils.ReadFile(self.config_path)) |
| |
| def _LoadTestDataConfig(self, filename): |
| return serializer.LoadJson(testutils.ReadTestData(filename)) |
| |
| def _CreateValidConfigDir(self): |
| utils.WriteFile(self.noded_cert_path, data="") |
| utils.WriteFile(self.known_hosts_path, data="") |
| utils.WriteFile(self.ss_master_node_path, |
| data="node.has.another.name.example.net") |
| |
| def testNoConfigDir(self): |
| self.assertFalse(utils.ListVisibleFiles(self.tmpdir)) |
| self.assertRaises(Exception, _RunUpgrade, self.tmpdir, False, True) |
| self.assertRaises(Exception, _RunUpgrade, self.tmpdir, True, True) |
| |
| def testWrongHostname(self): |
| self._CreateValidConfigDir() |
| |
| utils.WriteFile(self.config_path, |
| data=serializer.DumpJson(GetMinimalConfig())) |
| |
| hostname = netutils.GetHostname().name |
| assert hostname != utils.ReadOneLineFile(self.ss_master_node_path) |
| |
| self.assertRaises(Exception, _RunUpgrade, self.tmpdir, False, True, |
| ignore_hostname=False) |
| |
| def testCorrectHostname(self): |
| self._CreateValidConfigDir() |
| |
| utils.WriteFile(self.config_path, |
| data=serializer.DumpJson(GetMinimalConfig())) |
| |
| utils.WriteFile(self.ss_master_node_path, |
| data="%s\n" % netutils.GetHostname().name) |
| |
| _RunUpgrade(self.tmpdir, False, True, ignore_hostname=False) |
| |
| def testInconsistentConfig(self): |
| self._CreateValidConfigDir() |
| # There should be no "config_version" |
| cfg = GetMinimalConfig() |
| cfg["version"] = 0 |
| cfg["cluster"]["config_version"] = 0 |
| utils.WriteFile(self.config_path, data=serializer.DumpJson(cfg)) |
| self.assertRaises(Exception, _RunUpgrade, self.tmpdir, False, True) |
| |
| def testInvalidConfig(self): |
| self._CreateValidConfigDir() |
| # Missing version from config |
| utils.WriteFile(self.config_path, data=serializer.DumpJson({})) |
| self.assertRaises(Exception, _RunUpgrade, self.tmpdir, False, True) |
| |
| def _TestUpgradeFromFile(self, filename, dry_run): |
| cfg = self._LoadTestDataConfig(filename) |
| self._TestUpgradeFromData(cfg, dry_run) |
| |
| def _TestSimpleUpgrade(self, from_version, dry_run, |
| file_storage_dir=None, |
| shared_file_storage_dir=None): |
| cfg = GetMinimalConfig() |
| cfg["version"] = from_version |
| cluster = cfg["cluster"] |
| |
| if file_storage_dir: |
| cluster["file_storage_dir"] = file_storage_dir |
| if shared_file_storage_dir: |
| cluster["shared_file_storage_dir"] = shared_file_storage_dir |
| |
| self._TestUpgradeFromData(cfg, dry_run) |
| |
| def _TestUpgradeFromData(self, cfg, dry_run): |
| assert "version" in cfg |
| from_version = cfg["version"] |
| self._CreateValidConfigDir() |
| utils.WriteFile(self.config_path, data=serializer.DumpJson(cfg)) |
| |
| self.assertFalse(os.path.isfile(self.rapi_cert_path)) |
| self.assertFalse(os.path.isfile(self.confd_hmac_path)) |
| self.assertFalse(os.path.isfile(self.cds_path)) |
| |
| _RunUpgrade(self.tmpdir, dry_run, True) |
| |
| if dry_run: |
| expversion = from_version |
| checkfn = operator.not_ |
| else: |
| expversion = constants.CONFIG_VERSION |
| checkfn = operator.truth |
| |
| self.assert_(checkfn(os.path.isfile(self.rapi_cert_path))) |
| self.assert_(checkfn(os.path.isfile(self.confd_hmac_path))) |
| self.assert_(checkfn(os.path.isfile(self.cds_path))) |
| |
| newcfg = self._LoadConfig() |
| self.assertEqual(newcfg["version"], expversion) |
| |
| def testRapiUsers(self): |
| self.assertFalse(os.path.exists(self.rapi_users_path)) |
| self.assertFalse(os.path.exists(self.rapi_users_path_pre24)) |
| self.assertFalse(os.path.exists(os.path.dirname(self.rapi_users_path))) |
| |
| utils.WriteFile(self.rapi_users_path_pre24, data="some user\n") |
| self._TestSimpleUpgrade(version.BuildVersion(2, 3, 0), False) |
| |
| self.assertTrue(os.path.isdir(os.path.dirname(self.rapi_users_path))) |
| self.assert_(os.path.islink(self.rapi_users_path_pre24)) |
| self.assert_(os.path.isfile(self.rapi_users_path)) |
| self.assertEqual(os.readlink(self.rapi_users_path_pre24), |
| self.rapi_users_path) |
| for path in [self.rapi_users_path, self.rapi_users_path_pre24]: |
| self.assertEqual(utils.ReadFile(path), "some user\n") |
| |
| def testRapiUsers24AndAbove(self): |
| self.assertFalse(os.path.exists(self.rapi_users_path)) |
| self.assertFalse(os.path.exists(self.rapi_users_path_pre24)) |
| |
| os.mkdir(os.path.dirname(self.rapi_users_path)) |
| utils.WriteFile(self.rapi_users_path, data="other user\n") |
| self._TestSimpleUpgrade(version.BuildVersion(2, 3, 0), False) |
| |
| self.assert_(os.path.islink(self.rapi_users_path_pre24)) |
| self.assert_(os.path.isfile(self.rapi_users_path)) |
| self.assertEqual(os.readlink(self.rapi_users_path_pre24), |
| self.rapi_users_path) |
| for path in [self.rapi_users_path, self.rapi_users_path_pre24]: |
| self.assertEqual(utils.ReadFile(path), "other user\n") |
| |
| def testRapiUsersExistingSymlink(self): |
| self.assertFalse(os.path.exists(self.rapi_users_path)) |
| self.assertFalse(os.path.exists(self.rapi_users_path_pre24)) |
| |
| os.mkdir(os.path.dirname(self.rapi_users_path)) |
| os.symlink(self.rapi_users_path, self.rapi_users_path_pre24) |
| utils.WriteFile(self.rapi_users_path, data="hello world\n") |
| |
| self._TestSimpleUpgrade(version.BuildVersion(2, 2, 0), False) |
| |
| self.assert_(os.path.isfile(self.rapi_users_path) and |
| not os.path.islink(self.rapi_users_path)) |
| self.assert_(os.path.islink(self.rapi_users_path_pre24)) |
| self.assertEqual(os.readlink(self.rapi_users_path_pre24), |
| self.rapi_users_path) |
| for path in [self.rapi_users_path, self.rapi_users_path_pre24]: |
| self.assertEqual(utils.ReadFile(path), "hello world\n") |
| |
| def testRapiUsersExistingTarget(self): |
| self.assertFalse(os.path.exists(self.rapi_users_path)) |
| self.assertFalse(os.path.exists(self.rapi_users_path_pre24)) |
| |
| os.mkdir(os.path.dirname(self.rapi_users_path)) |
| utils.WriteFile(self.rapi_users_path, data="other user\n") |
| utils.WriteFile(self.rapi_users_path_pre24, data="hello world\n") |
| |
| self.assertRaises(Exception, self._TestSimpleUpgrade, |
| version.BuildVersion(2, 2, 0), False) |
| |
| for path in [self.rapi_users_path, self.rapi_users_path_pre24]: |
| self.assert_(os.path.isfile(path) and not os.path.islink(path)) |
| self.assertEqual(utils.ReadFile(self.rapi_users_path), "other user\n") |
| self.assertEqual(utils.ReadFile(self.rapi_users_path_pre24), |
| "hello world\n") |
| |
| def testRapiUsersDryRun(self): |
| self.assertFalse(os.path.exists(self.rapi_users_path)) |
| self.assertFalse(os.path.exists(self.rapi_users_path_pre24)) |
| |
| utils.WriteFile(self.rapi_users_path_pre24, data="some user\n") |
| self._TestSimpleUpgrade(version.BuildVersion(2, 3, 0), True) |
| |
| self.assertFalse(os.path.isdir(os.path.dirname(self.rapi_users_path))) |
| self.assertTrue(os.path.isfile(self.rapi_users_path_pre24) and |
| not os.path.islink(self.rapi_users_path_pre24)) |
| self.assertFalse(os.path.exists(self.rapi_users_path)) |
| |
| def testRapiUsers24AndAboveDryRun(self): |
| self.assertFalse(os.path.exists(self.rapi_users_path)) |
| self.assertFalse(os.path.exists(self.rapi_users_path_pre24)) |
| |
| os.mkdir(os.path.dirname(self.rapi_users_path)) |
| utils.WriteFile(self.rapi_users_path, data="other user\n") |
| self._TestSimpleUpgrade(version.BuildVersion(2, 3, 0), True) |
| |
| self.assertTrue(os.path.isfile(self.rapi_users_path) and |
| not os.path.islink(self.rapi_users_path)) |
| self.assertFalse(os.path.exists(self.rapi_users_path_pre24)) |
| self.assertEqual(utils.ReadFile(self.rapi_users_path), "other user\n") |
| |
| def testRapiUsersExistingSymlinkDryRun(self): |
| self.assertFalse(os.path.exists(self.rapi_users_path)) |
| self.assertFalse(os.path.exists(self.rapi_users_path_pre24)) |
| |
| os.mkdir(os.path.dirname(self.rapi_users_path)) |
| os.symlink(self.rapi_users_path, self.rapi_users_path_pre24) |
| utils.WriteFile(self.rapi_users_path, data="hello world\n") |
| |
| self._TestSimpleUpgrade(version.BuildVersion(2, 2, 0), True) |
| |
| self.assertTrue(os.path.islink(self.rapi_users_path_pre24)) |
| self.assertTrue(os.path.isfile(self.rapi_users_path) and |
| not os.path.islink(self.rapi_users_path)) |
| self.assertEqual(os.readlink(self.rapi_users_path_pre24), |
| self.rapi_users_path) |
| for path in [self.rapi_users_path, self.rapi_users_path_pre24]: |
| self.assertEqual(utils.ReadFile(path), "hello world\n") |
| |
| def testFileStoragePathsDryRun(self): |
| self.assertFalse(os.path.exists(self.file_storage_paths)) |
| |
| self._TestSimpleUpgrade(version.BuildVersion(2, 6, 0), True, |
| file_storage_dir=self.tmpdir, |
| shared_file_storage_dir="/tmp") |
| |
| self.assertFalse(os.path.exists(self.file_storage_paths)) |
| |
| def testFileStoragePathsBoth(self): |
| self.assertFalse(os.path.exists(self.file_storage_paths)) |
| |
| self._TestSimpleUpgrade(version.BuildVersion(2, 6, 0), False, |
| file_storage_dir=self.tmpdir, |
| shared_file_storage_dir="/tmp") |
| |
| lines = utils.ReadFile(self.file_storage_paths).splitlines() |
| self.assertTrue(lines.pop(0).startswith("# ")) |
| self.assertTrue(lines.pop(0).startswith("# cfgupgrade")) |
| self.assertEqual(lines.pop(0), self.tmpdir) |
| self.assertEqual(lines.pop(0), "/tmp") |
| self.assertFalse(lines) |
| self.assertEqual(os.stat(self.file_storage_paths).st_mode & 0777, |
| 0600, msg="Wrong permissions") |
| |
| def testFileStoragePathsSharedOnly(self): |
| self.assertFalse(os.path.exists(self.file_storage_paths)) |
| |
| self._TestSimpleUpgrade(version.BuildVersion(2, 5, 0), False, |
| file_storage_dir=None, |
| shared_file_storage_dir=self.tmpdir) |
| |
| lines = utils.ReadFile(self.file_storage_paths).splitlines() |
| self.assertTrue(lines.pop(0).startswith("# ")) |
| self.assertTrue(lines.pop(0).startswith("# cfgupgrade")) |
| self.assertEqual(lines.pop(0), self.tmpdir) |
| self.assertFalse(lines) |
| |
| def testUpgradeFrom_2_0(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 0, 0), False) |
| |
| def testUpgradeFrom_2_1(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 1, 0), False) |
| |
| def testUpgradeFrom_2_2(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 2, 0), False) |
| |
| def testUpgradeFrom_2_3(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 3, 0), False) |
| |
| def testUpgradeFrom_2_4(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 4, 0), False) |
| |
| def testUpgradeFrom_2_5(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 5, 0), False) |
| |
| def testUpgradeFrom_2_6(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 6, 0), False) |
| |
| def testUpgradeFrom_2_7(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 7, 0), False) |
| |
| def testUpgradeFullConfigFrom_2_7(self): |
| self._TestUpgradeFromFile("cluster_config_2.7.json", False) |
| |
| def testUpgradeFullConfigFrom_2_8(self): |
| self._TestUpgradeFromFile("cluster_config_2.8.json", False) |
| |
| def testUpgradeCurrent(self): |
| self._TestSimpleUpgrade(constants.CONFIG_VERSION, False) |
| |
| def _RunDowngradeUpgrade(self): |
| oldconf = self._LoadConfig() |
| _RunUpgrade(self.tmpdir, False, True, downgrade=True) |
| _RunUpgrade(self.tmpdir, False, True) |
| newconf = self._LoadConfig() |
| self.assertEqual(oldconf, newconf) |
| |
| def testDowngrade(self): |
| self._TestSimpleUpgrade(constants.CONFIG_VERSION, False) |
| self._RunDowngradeUpgrade() |
| |
| def testDowngradeFullConfig(self): |
| """Test for upgrade + downgrade combination.""" |
| # This test can work only with the previous version of a configuration! |
| oldconfname = "cluster_config_2.9.json" |
| self._TestUpgradeFromFile(oldconfname, False) |
| _RunUpgrade(self.tmpdir, False, True, downgrade=True) |
| oldconf = self._LoadTestDataConfig(oldconfname) |
| newconf = self._LoadConfig() |
| |
| # downgrade from 2.10 to 2.9 does not add physical_id to disks, which is ok |
| # TODO (2.11): Remove this code, it's not required to downgrade from 2.11 |
| # to 2.10 |
| def RemovePhysicalId(disk): |
| if "children" in disk: |
| for d in disk["children"]: |
| RemovePhysicalId(d) |
| if "physical_id" in disk: |
| del disk["physical_id"] |
| |
| for inst in oldconf["instances"].values(): |
| for disk in inst["disks"]: |
| RemovePhysicalId(disk) |
| |
| self.assertEqual(oldconf, newconf) |
| |
| def testDowngradeFullConfigBackwardFrom_2_7(self): |
| """Test for upgrade + downgrade + upgrade combination.""" |
| self._TestUpgradeFromFile("cluster_config_2.7.json", False) |
| self._RunDowngradeUpgrade() |
| |
| def _RunDowngradeTwice(self): |
| """Make sure that downgrade is idempotent.""" |
| _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 testDowngradeTwice(self): |
| self._TestSimpleUpgrade(constants.CONFIG_VERSION, False) |
| self._RunDowngradeTwice() |
| |
| def testDowngradeTwiceFullConfigFrom_2_7(self): |
| self._TestUpgradeFromFile("cluster_config_2.7.json", False) |
| self._RunDowngradeTwice() |
| |
| def testUpgradeDryRunFrom_2_0(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 0, 0), True) |
| |
| def testUpgradeDryRunFrom_2_1(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 1, 0), True) |
| |
| def testUpgradeDryRunFrom_2_2(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 2, 0), True) |
| |
| def testUpgradeDryRunFrom_2_3(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 3, 0), True) |
| |
| def testUpgradeDryRunFrom_2_4(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 4, 0), True) |
| |
| def testUpgradeDryRunFrom_2_5(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 5, 0), True) |
| |
| def testUpgradeDryRunFrom_2_6(self): |
| self._TestSimpleUpgrade(version.BuildVersion(2, 6, 0), True) |
| |
| 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() |