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