diff --git a/test/cfgupgrade_unittest.py b/test/cfgupgrade_unittest.py index 7ca5c3d017f6986ed9712ced92ad1f43ce5500f8..4d38b26756f83c56c77bd91facd095b10d2a2281 100755 --- a/test/cfgupgrade_unittest.py +++ b/test/cfgupgrade_unittest.py @@ -39,7 +39,7 @@ import testutils def _RunUpgrade(path, dry_run, no_verify, ignore_hostname=True): cmd = [sys.executable, "%s/tools/cfgupgrade" % testutils.GetSourceDir(), - "--debug", "--force", "--path=%s" % path] + "--debug", "--force", "--path=%s" % path, "--confdir=%s" % path] if ignore_hostname: cmd.append("--ignore-hostname") @@ -67,6 +67,7 @@ class TestCfgupgrade(unittest.TestCase): 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) @@ -133,10 +134,19 @@ class TestCfgupgrade(unittest.TestCase): utils.WriteFile(self.config_path, data=serializer.DumpJson({})) self.assertRaises(Exception, _RunUpgrade, self.tmpdir, False, True) - def _TestSimpleUpgrade(self, from_version, dry_run): + def _TestSimpleUpgrade(self, from_version, dry_run, + file_storage_dir=None, + shared_file_storage_dir=None): + 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 + cfg = { "version": from_version, - "cluster": {}, + "cluster": cluster, "instances": {}, } self._CreateValidConfigDir() @@ -271,6 +281,44 @@ class TestCfgupgrade(unittest.TestCase): 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(constants.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(constants.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(constants.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(constants.BuildVersion(2, 0, 0), False) diff --git a/tools/cfgupgrade b/tools/cfgupgrade index 604d8aa29a89ab92853a807186a031f984b68688..ceaec118f20ecadef481b0716f052239ea352894 100755 --- a/tools/cfgupgrade +++ b/tools/cfgupgrade @@ -32,6 +32,8 @@ import os.path import sys import optparse import logging +import time +from cStringIO import StringIO from ganeti import constants from ganeti import serializer @@ -117,6 +119,10 @@ def main(): parser.add_option("--path", help="Convert configuration in this" " directory instead of '%s'" % pathutils.DATA_DIR, default=pathutils.DATA_DIR, dest="data_dir") + parser.add_option("--confdir", + help=("Use this directory instead of '%s'" % + pathutils.CONF_DIR), + default=pathutils.CONF_DIR, dest="conf_dir") parser.add_option("--no-verify", help="Do not verify configuration after upgrade", action="store_true", dest="no_verify", default=False) @@ -137,6 +143,7 @@ def main(): options.CDS_FILE = options.data_dir + "/cluster-domain-secret" options.SSCONF_MASTER_NODE = options.data_dir + "/ssconf_master_node" options.WATCHER_STATEFILE = options.data_dir + "/watcher.data" + options.FILE_STORAGE_PATHS_FILE = options.conf_dir + "/file-storage-paths" SetupLogging() @@ -164,6 +171,9 @@ def main(): raise Error(("%s does not seem to be a Ganeti configuration" " directory") % options.data_dir) + if not os.path.isdir(options.conf_dir): + raise Error("Not a directory: %s" % options.conf_dir) + config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH)) try: @@ -238,6 +248,37 @@ def main(): if not options.dry_run: utils.RemoveFile(options.WATCHER_STATEFILE) + # Write file storage paths + if not os.path.exists(options.FILE_STORAGE_PATHS_FILE): + cluster = config_data["cluster"] + file_storage_dir = cluster.get("file_storage_dir") + shared_file_storage_dir = cluster.get("shared_file_storage_dir") + del cluster + + logging.info("Ganeti 2.7 and later only allow whitelisted directories" + " for file storage; writing existing configuration values" + " into '%s'", + options.FILE_STORAGE_PATHS_FILE) + + if file_storage_dir: + logging.info("File storage directory: %s", file_storage_dir) + if shared_file_storage_dir: + logging.info("Shared file storage directory: %s", + shared_file_storage_dir) + + buf = StringIO() + buf.write("# List automatically generated from configuration by\n") + buf.write("# cfgupgrade at %s\n" % time.asctime()) + if file_storage_dir: + buf.write("%s\n" % file_storage_dir) + if shared_file_storage_dir: + buf.write("%s\n" % shared_file_storage_dir) + utils.WriteFile(file_name=options.FILE_STORAGE_PATHS_FILE, + data=buf.getvalue(), + mode=0600, + dry_run=options.dry_run, + backup=True) + try: logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH) utils.WriteFile(file_name=options.CONFIG_DATA_PATH,