Revision 3039e2dc
b/Makefile.am | ||
---|---|---|
1228 | 1228 |
test/py/ganeti.client.gnt_instance_unittest.py \ |
1229 | 1229 |
test/py/ganeti.client.gnt_job_unittest.py \ |
1230 | 1230 |
test/py/ganeti.cmdlib_unittest.py \ |
1231 |
test/py/ganeti.cmdlib.cluster_unittest.py \ |
|
1232 |
test/py/ganeti.cmdlib.instance_storage_unittest.py \ |
|
1231 | 1233 |
test/py/ganeti.compat_unittest.py \ |
1232 | 1234 |
test/py/ganeti.confd.client_unittest.py \ |
1233 | 1235 |
test/py/ganeti.config_unittest.py \ |
b/lib/bootstrap.py | ||
---|---|---|
30 | 30 |
import time |
31 | 31 |
import tempfile |
32 | 32 |
|
33 |
from ganeti.cmdlib import cluster |
|
33 | 34 |
from ganeti import rpc |
34 | 35 |
from ganeti import ssh |
35 | 36 |
from ganeti import utils |
... | ... | |
41 | 42 |
from ganeti import serializer |
42 | 43 |
from ganeti import hypervisor |
43 | 44 |
from ganeti.storage import drbd |
45 |
from ganeti.storage import filestorage |
|
44 | 46 |
from ganeti import netutils |
45 | 47 |
from ganeti import luxi |
46 | 48 |
from ganeti import jstore |
... | ... | |
334 | 336 |
_WaitForSshDaemon(node, netutils.GetDaemonPort(constants.SSH), family) |
335 | 337 |
|
336 | 338 |
|
337 |
def _PrepareFileStorage(enabled_disk_templates, file_storage_dir): |
|
338 |
"""Checks if file storage is enabled and inits the dir. |
|
339 |
|
|
340 |
""" |
|
341 |
if utils.storage.IsFileStorageEnabled(enabled_disk_templates): |
|
342 |
file_storage_dir = _InitFileStorageDir(file_storage_dir) |
|
343 |
else: |
|
344 |
file_storage_dir = "" |
|
345 |
return file_storage_dir |
|
346 |
|
|
347 |
|
|
348 | 339 |
def _InitFileStorageDir(file_storage_dir): |
349 | 340 |
"""Initialize if needed the file storage. |
350 | 341 |
|
... | ... | |
372 | 363 |
" a directory." % file_storage_dir, |
373 | 364 |
errors.ECODE_ENVIRON) |
374 | 365 |
|
375 |
# FIXME: check here if the file_storage_dir is in the set of allowed dirs |
|
376 | 366 |
return file_storage_dir |
377 | 367 |
|
378 | 368 |
|
369 |
def _PrepareFileStorage( |
|
370 |
enabled_disk_templates, file_storage_dir, init_fn=_InitFileStorageDir, |
|
371 |
acceptance_fn=None): |
|
372 |
"""Checks if file storage is enabled and inits the dir. |
|
373 |
|
|
374 |
""" |
|
375 |
if file_storage_dir is None: |
|
376 |
file_storage_dir = pathutils.DEFAULT_FILE_STORAGE_DIR |
|
377 |
if not acceptance_fn: |
|
378 |
acceptance_fn = \ |
|
379 |
lambda path: filestorage.CheckFileStoragePathAcceptance( |
|
380 |
path, exact_match_ok=True) |
|
381 |
|
|
382 |
cluster.CheckFileStoragePathVsEnabledDiskTemplates( |
|
383 |
logging.warning, file_storage_dir, enabled_disk_templates) |
|
384 |
|
|
385 |
file_storage_enabled = constants.DT_FILE in enabled_disk_templates |
|
386 |
if file_storage_enabled: |
|
387 |
try: |
|
388 |
acceptance_fn(file_storage_dir) |
|
389 |
except errors.FileStoragePathError as e: |
|
390 |
raise errors.OpPrereqError(str(e)) |
|
391 |
result_file_storage_dir = init_fn(file_storage_dir) |
|
392 |
else: |
|
393 |
result_file_storage_dir = file_storage_dir |
|
394 |
return result_file_storage_dir |
|
395 |
|
|
396 |
|
|
379 | 397 |
def _InitCheckEnabledDiskTemplates(enabled_disk_templates): |
380 | 398 |
"""Checks the sanity of the enabled disk templates. |
381 | 399 |
|
b/lib/cli.py | ||
---|---|---|
1300 | 1300 |
"wide) for storing the file-based disks [%s]" % |
1301 | 1301 |
pathutils.DEFAULT_FILE_STORAGE_DIR, |
1302 | 1302 |
metavar="DIR", |
1303 |
default=pathutils.DEFAULT_FILE_STORAGE_DIR)
|
|
1303 |
default=None)
|
|
1304 | 1304 |
|
1305 | 1305 |
GLOBAL_SHARED_FILEDIR_OPT = cli_option( |
1306 | 1306 |
"--shared-file-storage-dir", |
b/lib/client/gnt_cluster.py | ||
---|---|---|
996 | 996 |
opts.ipolicy_disk_templates is not None or |
997 | 997 |
opts.ipolicy_vcpu_ratio is not None or |
998 | 998 |
opts.ipolicy_spindle_ratio is not None or |
999 |
opts.modify_etc_hosts is not None): |
|
999 |
opts.modify_etc_hosts is not None or |
|
1000 |
opts.file_storage_dir is not None): |
|
1000 | 1001 |
ToStderr("Please give at least one of the parameters.") |
1001 | 1002 |
return 1 |
1002 | 1003 |
|
... | ... | |
1120 | 1121 |
disk_state=disk_state, |
1121 | 1122 |
enabled_disk_templates=enabled_disk_templates, |
1122 | 1123 |
force=opts.force, |
1124 |
file_storage_dir=opts.file_storage_dir, |
|
1123 | 1125 |
) |
1124 | 1126 |
SubmitOrSend(op, opts) |
1125 | 1127 |
return 0 |
... | ... | |
1626 | 1628 |
NODE_PARAMS_OPT, USE_EXTERNAL_MIP_SCRIPT, DISK_PARAMS_OPT, HV_STATE_OPT, |
1627 | 1629 |
DISK_STATE_OPT] + SUBMIT_OPTS + |
1628 | 1630 |
[ENABLED_DISK_TEMPLATES_OPT, IPOLICY_STD_SPECS_OPT, MODIFY_ETCHOSTS_OPT] + |
1629 |
INSTANCE_POLICY_OPTS, |
|
1631 |
INSTANCE_POLICY_OPTS + [GLOBAL_FILEDIR_OPT],
|
|
1630 | 1632 |
"[opts...]", |
1631 | 1633 |
"Alters the parameters of the cluster"), |
1632 | 1634 |
"renew-crypto": ( |
b/lib/cmdlib/cluster.py | ||
---|---|---|
609 | 609 |
(netmask), errors.ECODE_INVAL) |
610 | 610 |
|
611 | 611 |
|
612 |
def CheckFileStoragePathVsEnabledDiskTemplates( |
|
613 |
logging_warn_fn, file_storage_dir, enabled_disk_templates): |
|
614 |
"""Checks whether the given file storage directory is acceptable. |
|
615 |
|
|
616 |
@type logging_warn_fn: function |
|
617 |
@param logging_warn_fn: function which accepts a string and logs it |
|
618 |
@type file_storage_dir: string |
|
619 |
@param file_storage_dir: the directory to be used for file-based instances |
|
620 |
@type enabled_disk_templates: list of string |
|
621 |
@param enabled_disk_templates: the list of enabled disk templates |
|
622 |
|
|
623 |
Note: This function is public, because it is also used in bootstrap.py. |
|
624 |
""" |
|
625 |
file_storage_enabled = constants.DT_FILE in enabled_disk_templates |
|
626 |
if file_storage_dir is not None: |
|
627 |
if file_storage_dir == "": |
|
628 |
if file_storage_enabled: |
|
629 |
raise errors.OpPrereqError("Unsetting the file storage directory" |
|
630 |
" while having file storage enabled" |
|
631 |
" is not permitted.") |
|
632 |
else: |
|
633 |
if not file_storage_enabled: |
|
634 |
logging_warn_fn("Specified a file storage directory, although file" |
|
635 |
" storage is not enabled.") |
|
636 |
else: |
|
637 |
raise errors.ProgrammerError("Received file storage dir with value" |
|
638 |
" 'None'.") |
|
639 |
|
|
640 |
|
|
612 | 641 |
class LUClusterSetParams(LogicalUnit): |
613 | 642 |
"""Change the parameters of the cluster. |
614 | 643 |
|
... | ... | |
752 | 781 |
self._CheckVgName(vm_capable_node_uuids, enabled_disk_templates, |
753 | 782 |
new_enabled_disk_templates) |
754 | 783 |
|
784 |
if self.op.file_storage_dir is not None: |
|
785 |
CheckFileStoragePathVsEnabledDiskTemplates( |
|
786 |
self.LogWarning, self.op.file_storage_dir, enabled_disk_templates) |
|
787 |
|
|
755 | 788 |
if self.op.drbd_helper: |
756 | 789 |
# checks given drbd helper on all nodes |
757 | 790 |
helpers = self.rpc.call_drbd_helper(node_uuids) |
... | ... | |
998 | 1031 |
raise errors.OpPrereqError("Please specify a volume group when" |
999 | 1032 |
" enabling lvm-based disk-templates.") |
1000 | 1033 |
|
1034 |
def _SetFileStorageDir(self, feedback_fn): |
|
1035 |
"""Set the file storage directory. |
|
1036 |
|
|
1037 |
""" |
|
1038 |
if self.op.file_storage_dir is not None: |
|
1039 |
if self.cluster.file_storage_dir == self.op.file_storage_dir: |
|
1040 |
feedback_fn("Global file storage dir already set to value '%s'" |
|
1041 |
% self.cluster.file_storage_dir) |
|
1042 |
else: |
|
1043 |
self.cluster.file_storage_dir = self.op.file_storage_dir |
|
1044 |
|
|
1001 | 1045 |
def Exec(self, feedback_fn): |
1002 | 1046 |
"""Change the parameters of the cluster. |
1003 | 1047 |
|
... | ... | |
1007 | 1051 |
list(set(self.op.enabled_disk_templates)) |
1008 | 1052 |
|
1009 | 1053 |
self._SetVgName(feedback_fn) |
1054 |
self._SetFileStorageDir(feedback_fn) |
|
1010 | 1055 |
|
1011 | 1056 |
if self.op.drbd_helper is not None: |
1012 | 1057 |
if not constants.DT_DRBD8 in self.cluster.enabled_disk_templates: |
b/lib/opcodes.py | ||
---|---|---|
963 | 963 |
"List of enabled disk templates"), |
964 | 964 |
("modify_etc_hosts", None, ht.TMaybeBool, |
965 | 965 |
"Whether the cluster can modify and keep in sync the /etc/hosts files"), |
966 |
("file_storage_dir", None, ht.TMaybeString, |
|
967 |
"Default directory for storing file-backed disks"), |
|
966 | 968 |
] |
967 | 969 |
OP_RESULT = ht.TNone |
968 | 970 |
|
b/qa/qa_cluster.py | ||
---|---|---|
174 | 174 |
fh.close() |
175 | 175 |
|
176 | 176 |
# Initialize cluster |
177 |
enabled_disk_templates = qa_config.GetEnabledDiskTemplates() |
|
177 | 178 |
cmd = [ |
178 | 179 |
"gnt-cluster", "init", |
179 | 180 |
"--primary-ip-version=%d" % qa_config.get("primary_ip_version", 4), |
180 | 181 |
"--enabled-hypervisors=%s" % ",".join(qa_config.GetEnabledHypervisors()), |
181 | 182 |
"--enabled-disk-templates=%s" % |
182 |
",".join(qa_config.GetEnabledDiskTemplates()), |
|
183 |
"--file-storage-dir=%s" % |
|
184 |
qa_config.get("file-storage-dir", pathutils.DEFAULT_FILE_STORAGE_DIR), |
|
183 |
",".join(enabled_disk_templates), |
|
185 | 184 |
] |
185 |
if constants.DT_FILE in enabled_disk_templates: |
|
186 |
cmd.append( |
|
187 |
"--file-storage-dir=%s" % |
|
188 |
qa_config.get("file-storage-dir", pathutils.DEFAULT_FILE_STORAGE_DIR)) |
|
186 | 189 |
|
187 | 190 |
for spec_type in ("mem-size", "disk-size", "disk-count", "cpu-count", |
188 | 191 |
"nic-count"): |
b/src/Ganeti/OpCodes.hs | ||
---|---|---|
185 | 185 |
, pUseExternalMipScript |
186 | 186 |
, pEnabledDiskTemplates |
187 | 187 |
, pModifyEtcHosts |
188 |
, pGlobalFileStorageDir |
|
188 | 189 |
]) |
189 | 190 |
, ("OpClusterRedistConf", []) |
190 | 191 |
, ("OpClusterActivateMasterIp", []) |
b/src/Ganeti/OpParams.hs | ||
---|---|---|
105 | 105 |
, pOptDiskTemplate |
106 | 106 |
, pFileDriver |
107 | 107 |
, pFileStorageDir |
108 |
, pGlobalFileStorageDir |
|
108 | 109 |
, pVgName |
109 | 110 |
, pEnabledHypervisors |
110 | 111 |
, pHypervisor |
... | ... | |
787 | 788 |
pFileStorageDir :: Field |
788 | 789 |
pFileStorageDir = optionalNEStringField "file_storage_dir" |
789 | 790 |
|
791 |
-- | Global directory for storing file-backed disks. |
|
792 |
pGlobalFileStorageDir :: Field |
|
793 |
pGlobalFileStorageDir = optionalNEStringField "file_storage_dir" |
|
794 |
|
|
790 | 795 |
-- | Volume group name. |
791 | 796 |
pVgName :: Field |
792 | 797 |
pVgName = optionalStringField "vg_name" |
b/test/hs/Test/Ganeti/OpCodes.hs | ||
---|---|---|
163 | 163 |
arbitrary <*> arbitrary <*> arbitrary <*> |
164 | 164 |
emptyMUD <*> emptyMUD <*> arbitrary <*> |
165 | 165 |
arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> |
166 |
arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary |
|
166 |
arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> |
|
167 |
genMaybe (genName >>= mkNonEmpty) |
|
167 | 168 |
"OP_CLUSTER_REDIST_CONF" -> pure OpCodes.OpClusterRedistConf |
168 | 169 |
"OP_CLUSTER_ACTIVATE_MASTER_IP" -> |
169 | 170 |
pure OpCodes.OpClusterActivateMasterIp |
b/test/py/ganeti.bootstrap_unittest.py | ||
---|---|---|
28 | 28 |
from ganeti import bootstrap |
29 | 29 |
from ganeti import constants |
30 | 30 |
from ganeti import errors |
31 |
from ganeti import pathutils |
|
31 | 32 |
|
32 | 33 |
import testutils |
34 |
import mock |
|
33 | 35 |
|
34 | 36 |
|
35 | 37 |
class TestPrepareFileStorage(unittest.TestCase): |
36 | 38 |
def setUp(self): |
39 |
unittest.TestCase.setUp(self) |
|
37 | 40 |
self.tmpdir = tempfile.mkdtemp() |
38 | 41 |
|
39 | 42 |
def tearDown(self): |
40 | 43 |
shutil.rmtree(self.tmpdir) |
41 | 44 |
|
42 |
def testFileStorageEnabled(self): |
|
43 |
enabled_disk_templates = [constants.DT_FILE] |
|
45 |
def enableFileStorage(self, enable): |
|
46 |
self.enabled_disk_templates = [] |
|
47 |
if enable: |
|
48 |
self.enabled_disk_templates.append(constants.DT_FILE) |
|
49 |
else: |
|
50 |
# anything != DT_FILE would do here |
|
51 |
self.enabled_disk_templates.append(constants.DT_DISKLESS) |
|
52 |
|
|
53 |
def testFallBackToDefaultPathAcceptedFileStorageEnabled(self): |
|
54 |
expected_file_storage_dir = pathutils.DEFAULT_FILE_STORAGE_DIR |
|
55 |
acceptance_fn = mock.Mock() |
|
56 |
init_fn = mock.Mock(return_value=expected_file_storage_dir) |
|
57 |
self.enableFileStorage(True) |
|
44 | 58 |
file_storage_dir = bootstrap._PrepareFileStorage( |
45 |
enabled_disk_templates, self.tmpdir) |
|
59 |
self.enabled_disk_templates, None, acceptance_fn=acceptance_fn, |
|
60 |
init_fn=init_fn) |
|
61 |
self.assertEqual(expected_file_storage_dir, file_storage_dir) |
|
62 |
acceptance_fn.assert_called_with(expected_file_storage_dir) |
|
63 |
init_fn.assert_called_with(expected_file_storage_dir) |
|
64 |
|
|
65 |
def testPathAcceptedFileStorageEnabled(self): |
|
66 |
acceptance_fn = mock.Mock() |
|
67 |
init_fn = mock.Mock(return_value=self.tmpdir) |
|
68 |
self.enableFileStorage(True) |
|
69 |
file_storage_dir = bootstrap._PrepareFileStorage( |
|
70 |
self.enabled_disk_templates, self.tmpdir, acceptance_fn=acceptance_fn, |
|
71 |
init_fn=init_fn) |
|
46 | 72 |
self.assertEqual(self.tmpdir, file_storage_dir) |
73 |
acceptance_fn.assert_called_with(self.tmpdir) |
|
74 |
init_fn.assert_called_with(self.tmpdir) |
|
47 | 75 |
|
48 |
def testFileStorageDisabled(self): |
|
49 |
# anything != DT_FILE would do here |
|
50 |
enabled_disk_templates = [constants.DT_PLAIN] |
|
76 |
def testPathAcceptedFileStorageDisabled(self): |
|
77 |
acceptance_fn = mock.Mock() |
|
78 |
init_fn = mock.Mock() |
|
79 |
self.enableFileStorage(False) |
|
51 | 80 |
file_storage_dir = bootstrap._PrepareFileStorage( |
52 |
enabled_disk_templates, self.tmpdir) |
|
53 |
self.assertEqual('', file_storage_dir) |
|
81 |
self.enabled_disk_templates, self.tmpdir, acceptance_fn=acceptance_fn, |
|
82 |
init_fn=init_fn) |
|
83 |
self.assertEqual(self.tmpdir, file_storage_dir) |
|
84 |
self.assertFalse(init_fn.called) |
|
85 |
self.assertFalse(acceptance_fn.called) |
|
86 |
|
|
87 |
def testPathNotAccepted(self): |
|
88 |
acceptance_fn = mock.Mock() |
|
89 |
acceptance_fn.side_effect = errors.FileStoragePathError |
|
90 |
init_fn = mock.Mock() |
|
91 |
self.enableFileStorage(True) |
|
92 |
self.assertRaises(errors.OpPrereqError, bootstrap._PrepareFileStorage, |
|
93 |
self.enabled_disk_templates, self.tmpdir, acceptance_fn=acceptance_fn, |
|
94 |
init_fn=init_fn) |
|
95 |
acceptance_fn.assert_called_with(self.tmpdir) |
|
54 | 96 |
|
55 | 97 |
|
56 | 98 |
class TestInitCheckEnabledDiskTemplates(unittest.TestCase): |
b/test/py/ganeti.cmdlib.cluster_unittest.py | ||
---|---|---|
1 |
#!/usr/bin/python |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2013 Google Inc. |
|
5 |
# |
|
6 |
# This program is free software; you can redistribute it and/or modify |
|
7 |
# it under the terms of the GNU General Public License as published by |
|
8 |
# the Free Software Foundation; either version 2 of the License, or |
|
9 |
# (at your option) any later version. |
|
10 |
# |
|
11 |
# This program is distributed in the hope that it will be useful, but |
|
12 |
# WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
# General Public License for more details. |
|
15 |
# |
|
16 |
# You should have received a copy of the GNU General Public License |
|
17 |
# along with this program; if not, write to the Free Software |
|
18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
19 |
# 02110-1301, USA. |
|
20 |
|
|
21 |
|
|
22 |
"""Script for unittesting the cmdlib module 'cluster'""" |
|
23 |
|
|
24 |
|
|
25 |
import unittest |
|
26 |
|
|
27 |
from ganeti.cmdlib import cluster |
|
28 |
from ganeti import constants |
|
29 |
from ganeti import errors |
|
30 |
|
|
31 |
import testutils |
|
32 |
import mock |
|
33 |
|
|
34 |
|
|
35 |
class TestCheckFileStoragePath(unittest.TestCase): |
|
36 |
|
|
37 |
def setUp(self): |
|
38 |
unittest.TestCase.setUp(self) |
|
39 |
self.log_warning = mock.Mock() |
|
40 |
|
|
41 |
def enableFileStorage(self, file_storage_enabled): |
|
42 |
if file_storage_enabled: |
|
43 |
self.enabled_disk_templates = [constants.DT_FILE] |
|
44 |
else: |
|
45 |
# anything != 'file' would do here |
|
46 |
self.enabled_disk_templates = [constants.DT_DISKLESS] |
|
47 |
|
|
48 |
def testNone(self): |
|
49 |
self.enableFileStorage(True) |
|
50 |
self.assertRaises( |
|
51 |
errors.ProgrammerError, |
|
52 |
cluster.CheckFileStoragePathVsEnabledDiskTemplates, |
|
53 |
self.log_warning, None, self.enabled_disk_templates) |
|
54 |
|
|
55 |
def testNotEmptyAndEnabled(self): |
|
56 |
self.enableFileStorage(True) |
|
57 |
cluster.CheckFileStoragePathVsEnabledDiskTemplates( |
|
58 |
self.log_warning, "/some/path", self.enabled_disk_templates) |
|
59 |
|
|
60 |
def testNotEnabled(self): |
|
61 |
self.enableFileStorage(False) |
|
62 |
cluster.CheckFileStoragePathVsEnabledDiskTemplates( |
|
63 |
self.log_warning, "/some/path", self.enabled_disk_templates) |
|
64 |
self.assertTrue(self.log_warning.called) |
|
65 |
|
|
66 |
def testEmptyAndEnabled(self): |
|
67 |
self.enableFileStorage(True) |
|
68 |
self.assertRaises( |
|
69 |
errors.OpPrereqError, |
|
70 |
cluster.CheckFileStoragePathVsEnabledDiskTemplates, |
|
71 |
self.log_warning, "", self.enabled_disk_templates) |
|
72 |
|
|
73 |
def testEmptyAndDisabled(self): |
|
74 |
self.enableFileStorage(False) |
|
75 |
cluster.CheckFileStoragePathVsEnabledDiskTemplates( |
|
76 |
NotImplemented, "", self.enabled_disk_templates) |
|
77 |
|
|
78 |
|
|
79 |
if __name__ == "__main__": |
|
80 |
testutils.GanetiTestProgram() |
Also available in: Unified diff