When file storage is used this file is now mandatory.
---
test/cfgupgrade_unittest.py | 52 ++++++++++++++++++++++++++++++++++++++++--
tools/cfgupgrade | 41 +++++++++++++++++++++++++++++++++
2 files changed, 90 insertions(+), 3 deletions(-)
diff --git a/test/cfgupgrade_unittest.py b/test/cfgupgrade_unittest.py
index 7ca5c3d..9764b6e 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,42 @@ 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)
+
+ 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 604d8aa..5aeb7e4 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=0644,
+ 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,
--
1.7.7.3