Commit 3039e2dc authored by Helga Velroyen's avatar Helga Velroyen

gnt-cluster {init, modify} --file-storage-dir

This patch implements consistent usage and behavior of
the --file-storage-dir option in 'gnt-cluster init'
and 'gnt-cluster modify'. It includes a bunch of unit
tests as well.

Additionally, it enables the previously written unit
tests for 'instance_storage', which were forgotten to
be included in the Makefile so far.
Signed-off-by: default avatarHelga Velroyen <helgav@google.com>
Reviewed-by: default avatarThomas Thrainer <thomasth@google.com>
parent 738436bf
......@@ -1228,6 +1228,8 @@ python_tests = \
test/py/ganeti.client.gnt_instance_unittest.py \
test/py/ganeti.client.gnt_job_unittest.py \
test/py/ganeti.cmdlib_unittest.py \
test/py/ganeti.cmdlib.cluster_unittest.py \
test/py/ganeti.cmdlib.instance_storage_unittest.py \
test/py/ganeti.compat_unittest.py \
test/py/ganeti.confd.client_unittest.py \
test/py/ganeti.config_unittest.py \
......
......@@ -30,6 +30,7 @@ import logging
import time
import tempfile
from ganeti.cmdlib import cluster
from ganeti import rpc
from ganeti import ssh
from ganeti import utils
......@@ -41,6 +42,7 @@ from ganeti import ssconf
from ganeti import serializer
from ganeti import hypervisor
from ganeti.storage import drbd
from ganeti.storage import filestorage
from ganeti import netutils
from ganeti import luxi
from ganeti import jstore
......@@ -334,17 +336,6 @@ def RunNodeSetupCmd(cluster_name, node, basecmd, debug, verbose,
_WaitForSshDaemon(node, netutils.GetDaemonPort(constants.SSH), family)
def _PrepareFileStorage(enabled_disk_templates, file_storage_dir):
"""Checks if file storage is enabled and inits the dir.
"""
if utils.storage.IsFileStorageEnabled(enabled_disk_templates):
file_storage_dir = _InitFileStorageDir(file_storage_dir)
else:
file_storage_dir = ""
return file_storage_dir
def _InitFileStorageDir(file_storage_dir):
"""Initialize if needed the file storage.
......@@ -372,10 +363,37 @@ def _InitFileStorageDir(file_storage_dir):
" a directory." % file_storage_dir,
errors.ECODE_ENVIRON)
# FIXME: check here if the file_storage_dir is in the set of allowed dirs
return file_storage_dir
def _PrepareFileStorage(
enabled_disk_templates, file_storage_dir, init_fn=_InitFileStorageDir,
acceptance_fn=None):
"""Checks if file storage is enabled and inits the dir.
"""
if file_storage_dir is None:
file_storage_dir = pathutils.DEFAULT_FILE_STORAGE_DIR
if not acceptance_fn:
acceptance_fn = \
lambda path: filestorage.CheckFileStoragePathAcceptance(
path, exact_match_ok=True)
cluster.CheckFileStoragePathVsEnabledDiskTemplates(
logging.warning, file_storage_dir, enabled_disk_templates)
file_storage_enabled = constants.DT_FILE in enabled_disk_templates
if file_storage_enabled:
try:
acceptance_fn(file_storage_dir)
except errors.FileStoragePathError as e:
raise errors.OpPrereqError(str(e))
result_file_storage_dir = init_fn(file_storage_dir)
else:
result_file_storage_dir = file_storage_dir
return result_file_storage_dir
def _InitCheckEnabledDiskTemplates(enabled_disk_templates):
"""Checks the sanity of the enabled disk templates.
......
......@@ -1300,7 +1300,7 @@ GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir",
"wide) for storing the file-based disks [%s]" %
pathutils.DEFAULT_FILE_STORAGE_DIR,
metavar="DIR",
default=pathutils.DEFAULT_FILE_STORAGE_DIR)
default=None)
GLOBAL_SHARED_FILEDIR_OPT = cli_option(
"--shared-file-storage-dir",
......
......@@ -996,7 +996,8 @@ def SetClusterParams(opts, args):
opts.ipolicy_disk_templates is not None or
opts.ipolicy_vcpu_ratio is not None or
opts.ipolicy_spindle_ratio is not None or
opts.modify_etc_hosts is not None):
opts.modify_etc_hosts is not None or
opts.file_storage_dir is not None):
ToStderr("Please give at least one of the parameters.")
return 1
......@@ -1120,6 +1121,7 @@ def SetClusterParams(opts, args):
disk_state=disk_state,
enabled_disk_templates=enabled_disk_templates,
force=opts.force,
file_storage_dir=opts.file_storage_dir,
)
SubmitOrSend(op, opts)
return 0
......@@ -1626,7 +1628,7 @@ commands = {
NODE_PARAMS_OPT, USE_EXTERNAL_MIP_SCRIPT, DISK_PARAMS_OPT, HV_STATE_OPT,
DISK_STATE_OPT] + SUBMIT_OPTS +
[ENABLED_DISK_TEMPLATES_OPT, IPOLICY_STD_SPECS_OPT, MODIFY_ETCHOSTS_OPT] +
INSTANCE_POLICY_OPTS,
INSTANCE_POLICY_OPTS + [GLOBAL_FILEDIR_OPT],
"[opts...]",
"Alters the parameters of the cluster"),
"renew-crypto": (
......
......@@ -609,6 +609,35 @@ def _ValidateNetmask(cfg, netmask):
(netmask), errors.ECODE_INVAL)
def CheckFileStoragePathVsEnabledDiskTemplates(
logging_warn_fn, file_storage_dir, enabled_disk_templates):
"""Checks whether the given file storage directory is acceptable.
@type logging_warn_fn: function
@param logging_warn_fn: function which accepts a string and logs it
@type file_storage_dir: string
@param file_storage_dir: the directory to be used for file-based instances
@type enabled_disk_templates: list of string
@param enabled_disk_templates: the list of enabled disk templates
Note: This function is public, because it is also used in bootstrap.py.
"""
file_storage_enabled = constants.DT_FILE in enabled_disk_templates
if file_storage_dir is not None:
if file_storage_dir == "":
if file_storage_enabled:
raise errors.OpPrereqError("Unsetting the file storage directory"
" while having file storage enabled"
" is not permitted.")
else:
if not file_storage_enabled:
logging_warn_fn("Specified a file storage directory, although file"
" storage is not enabled.")
else:
raise errors.ProgrammerError("Received file storage dir with value"
" 'None'.")
class LUClusterSetParams(LogicalUnit):
"""Change the parameters of the cluster.
......@@ -752,6 +781,10 @@ class LUClusterSetParams(LogicalUnit):
self._CheckVgName(vm_capable_node_uuids, enabled_disk_templates,
new_enabled_disk_templates)
if self.op.file_storage_dir is not None:
CheckFileStoragePathVsEnabledDiskTemplates(
self.LogWarning, self.op.file_storage_dir, enabled_disk_templates)
if self.op.drbd_helper:
# checks given drbd helper on all nodes
helpers = self.rpc.call_drbd_helper(node_uuids)
......@@ -998,6 +1031,17 @@ class LUClusterSetParams(LogicalUnit):
raise errors.OpPrereqError("Please specify a volume group when"
" enabling lvm-based disk-templates.")
def _SetFileStorageDir(self, feedback_fn):
"""Set the file storage directory.
"""
if self.op.file_storage_dir is not None:
if self.cluster.file_storage_dir == self.op.file_storage_dir:
feedback_fn("Global file storage dir already set to value '%s'"
% self.cluster.file_storage_dir)
else:
self.cluster.file_storage_dir = self.op.file_storage_dir
def Exec(self, feedback_fn):
"""Change the parameters of the cluster.
......@@ -1007,6 +1051,7 @@ class LUClusterSetParams(LogicalUnit):
list(set(self.op.enabled_disk_templates))
self._SetVgName(feedback_fn)
self._SetFileStorageDir(feedback_fn)
if self.op.drbd_helper is not None:
if not constants.DT_DRBD8 in self.cluster.enabled_disk_templates:
......
......@@ -963,6 +963,8 @@ class OpClusterSetParams(OpCode):
"List of enabled disk templates"),
("modify_etc_hosts", None, ht.TMaybeBool,
"Whether the cluster can modify and keep in sync the /etc/hosts files"),
("file_storage_dir", None, ht.TMaybeString,
"Default directory for storing file-backed disks"),
]
OP_RESULT = ht.TNone
......
......@@ -174,15 +174,18 @@ def TestClusterInit(rapi_user, rapi_secret):
fh.close()
# Initialize cluster
enabled_disk_templates = qa_config.GetEnabledDiskTemplates()
cmd = [
"gnt-cluster", "init",
"--primary-ip-version=%d" % qa_config.get("primary_ip_version", 4),
"--enabled-hypervisors=%s" % ",".join(qa_config.GetEnabledHypervisors()),
"--enabled-disk-templates=%s" %
",".join(qa_config.GetEnabledDiskTemplates()),
"--file-storage-dir=%s" %
qa_config.get("file-storage-dir", pathutils.DEFAULT_FILE_STORAGE_DIR),
",".join(enabled_disk_templates),
]
if constants.DT_FILE in enabled_disk_templates:
cmd.append(
"--file-storage-dir=%s" %
qa_config.get("file-storage-dir", pathutils.DEFAULT_FILE_STORAGE_DIR))
for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count",
"nic-count"):
......
......@@ -185,6 +185,7 @@ $(genOpCode "OpCode"
, pUseExternalMipScript
, pEnabledDiskTemplates
, pModifyEtcHosts
, pGlobalFileStorageDir
])
, ("OpClusterRedistConf", [])
, ("OpClusterActivateMasterIp", [])
......
......@@ -105,6 +105,7 @@ module Ganeti.OpParams
, pOptDiskTemplate
, pFileDriver
, pFileStorageDir
, pGlobalFileStorageDir
, pVgName
, pEnabledHypervisors
, pHypervisor
......@@ -787,6 +788,10 @@ pFileDriver = optionalField $ simpleField "file_driver" [t| FileDriver |]
pFileStorageDir :: Field
pFileStorageDir = optionalNEStringField "file_storage_dir"
-- | Global directory for storing file-backed disks.
pGlobalFileStorageDir :: Field
pGlobalFileStorageDir = optionalNEStringField "file_storage_dir"
-- | Volume group name.
pVgName :: Field
pVgName = optionalStringField "vg_name"
......
......@@ -163,7 +163,8 @@ instance Arbitrary OpCodes.OpCode where
arbitrary <*> arbitrary <*> arbitrary <*>
emptyMUD <*> emptyMUD <*> arbitrary <*>
arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*>
arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*>
genMaybe (genName >>= mkNonEmpty)
"OP_CLUSTER_REDIST_CONF" -> pure OpCodes.OpClusterRedistConf
"OP_CLUSTER_ACTIVATE_MASTER_IP" ->
pure OpCodes.OpClusterActivateMasterIp
......
......@@ -28,29 +28,71 @@ import unittest
from ganeti import bootstrap
from ganeti import constants
from ganeti import errors
from ganeti import pathutils
import testutils
import mock
class TestPrepareFileStorage(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.tmpdir = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(self.tmpdir)
def testFileStorageEnabled(self):
enabled_disk_templates = [constants.DT_FILE]
def enableFileStorage(self, enable):
self.enabled_disk_templates = []
if enable:
self.enabled_disk_templates.append(constants.DT_FILE)
else:
# anything != DT_FILE would do here
self.enabled_disk_templates.append(constants.DT_DISKLESS)
def testFallBackToDefaultPathAcceptedFileStorageEnabled(self):
expected_file_storage_dir = pathutils.DEFAULT_FILE_STORAGE_DIR
acceptance_fn = mock.Mock()
init_fn = mock.Mock(return_value=expected_file_storage_dir)
self.enableFileStorage(True)
file_storage_dir = bootstrap._PrepareFileStorage(
enabled_disk_templates, self.tmpdir)
self.enabled_disk_templates, None, acceptance_fn=acceptance_fn,
init_fn=init_fn)
self.assertEqual(expected_file_storage_dir, file_storage_dir)
acceptance_fn.assert_called_with(expected_file_storage_dir)
init_fn.assert_called_with(expected_file_storage_dir)
def testPathAcceptedFileStorageEnabled(self):
acceptance_fn = mock.Mock()
init_fn = mock.Mock(return_value=self.tmpdir)
self.enableFileStorage(True)
file_storage_dir = bootstrap._PrepareFileStorage(
self.enabled_disk_templates, self.tmpdir, acceptance_fn=acceptance_fn,
init_fn=init_fn)
self.assertEqual(self.tmpdir, file_storage_dir)
acceptance_fn.assert_called_with(self.tmpdir)
init_fn.assert_called_with(self.tmpdir)
def testFileStorageDisabled(self):
# anything != DT_FILE would do here
enabled_disk_templates = [constants.DT_PLAIN]
def testPathAcceptedFileStorageDisabled(self):
acceptance_fn = mock.Mock()
init_fn = mock.Mock()
self.enableFileStorage(False)
file_storage_dir = bootstrap._PrepareFileStorage(
enabled_disk_templates, self.tmpdir)
self.assertEqual('', file_storage_dir)
self.enabled_disk_templates, self.tmpdir, acceptance_fn=acceptance_fn,
init_fn=init_fn)
self.assertEqual(self.tmpdir, file_storage_dir)
self.assertFalse(init_fn.called)
self.assertFalse(acceptance_fn.called)
def testPathNotAccepted(self):
acceptance_fn = mock.Mock()
acceptance_fn.side_effect = errors.FileStoragePathError
init_fn = mock.Mock()
self.enableFileStorage(True)
self.assertRaises(errors.OpPrereqError, bootstrap._PrepareFileStorage,
self.enabled_disk_templates, self.tmpdir, acceptance_fn=acceptance_fn,
init_fn=init_fn)
acceptance_fn.assert_called_with(self.tmpdir)
class TestInitCheckEnabledDiskTemplates(unittest.TestCase):
......
#!/usr/bin/python
#
# Copyright (C) 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
# 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.
"""Script for unittesting the cmdlib module 'cluster'"""
import unittest
from ganeti.cmdlib import cluster
from ganeti import constants
from ganeti import errors
import testutils
import mock
class TestCheckFileStoragePath(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.log_warning = mock.Mock()
def enableFileStorage(self, file_storage_enabled):
if file_storage_enabled:
self.enabled_disk_templates = [constants.DT_FILE]
else:
# anything != 'file' would do here
self.enabled_disk_templates = [constants.DT_DISKLESS]
def testNone(self):
self.enableFileStorage(True)
self.assertRaises(
errors.ProgrammerError,
cluster.CheckFileStoragePathVsEnabledDiskTemplates,
self.log_warning, None, self.enabled_disk_templates)
def testNotEmptyAndEnabled(self):
self.enableFileStorage(True)
cluster.CheckFileStoragePathVsEnabledDiskTemplates(
self.log_warning, "/some/path", self.enabled_disk_templates)
def testNotEnabled(self):
self.enableFileStorage(False)
cluster.CheckFileStoragePathVsEnabledDiskTemplates(
self.log_warning, "/some/path", self.enabled_disk_templates)
self.assertTrue(self.log_warning.called)
def testEmptyAndEnabled(self):
self.enableFileStorage(True)
self.assertRaises(
errors.OpPrereqError,
cluster.CheckFileStoragePathVsEnabledDiskTemplates,
self.log_warning, "", self.enabled_disk_templates)
def testEmptyAndDisabled(self):
self.enableFileStorage(False)
cluster.CheckFileStoragePathVsEnabledDiskTemplates(
NotImplemented, "", self.enabled_disk_templates)
if __name__ == "__main__":
testutils.GanetiTestProgram()
File mode changed from 100644 to 100755
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment