Revision 820bade9

b/Makefile.am
322 322
	lib/storage/container.py \
323 323
	lib/storage/drbd.py \
324 324
	lib/storage/drbd_info.py \
325
	lib/storage/drbd_cmdgen.py
325
	lib/storage/drbd_cmdgen.py \
326
	lib/storage/filestorage.py
326 327

  
327 328
rapi_PYTHON = \
328 329
	lib/rapi/__init__.py \
......
1191 1192
	test/py/ganeti.server.rapi_unittest.py \
1192 1193
	test/py/ganeti.ssconf_unittest.py \
1193 1194
	test/py/ganeti.ssh_unittest.py \
1195
	test/py/ganeti.storage.filestorage_unittest.py \
1194 1196
	test/py/ganeti.tools.burnin_unittest.py \
1195 1197
	test/py/ganeti.tools.ensure_dirs_unittest.py \
1196 1198
	test/py/ganeti.tools.node_daemon_setup_unittest.py \
b/lib/storage/filestorage.py
1
#
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
"""File storage functions.
23

  
24
"""
25

  
26
from ganeti import errors
27
from ganeti import utils
28

  
29
DF_M_UNIT = 'M'
30
DF_MIN_NUM_COLS = 4
31
DF_NUM_LINES = 2
32

  
33

  
34
def _ParseDfResult(dfresult):
35
  """Parses the output of the call of the 'df' tool.
36

  
37
     @type dfresult: string
38
     @param dfresult: output of the 'df' call
39
     @return: tuple (size, free) of the total and free disk space in MebiBytes
40
  """
41
  df_lines = dfresult.splitlines()
42
  if len(df_lines) != DF_NUM_LINES:
43
    raise errors.CommandError("'df' output has wrong number of lines: %s" %
44
                              len(df_lines))
45
  df_values = df_lines[1].strip().split()
46
  if len(df_values) < DF_MIN_NUM_COLS:
47
    raise errors.CommandError("'df' output does not have enough columns: %s" %
48
                              len(df_values))
49
  size_str = df_values[1]
50
  if size_str[-1] != DF_M_UNIT:
51
    raise errors.CommandError("'df': 'size' not given in Mebibytes.")
52
  free_str = df_values[3]
53
  if free_str[-1] != DF_M_UNIT:
54
    raise errors.CommandError("'df': 'free' not given in Mebibytes.")
55
  size = int(size_str[:-1])
56
  free = int(free_str[:-1])
57
  return (size, free)
58

  
59

  
60
def GetSpaceInfo(path, _parsefn=_ParseDfResult):
61
  """Retrieves the free and total space of the device where the file is
62
     located.
63

  
64
     @type path: string
65
     @param path: Path of the file whose embracing device's capacity is
66
       reported.
67
     @type _parsefn: function
68
     @param _parsefn: Function that parses the output of the 'df' command;
69
       given as parameter to make this code more testable.
70
     @return: a dictionary containing 'vg_size' and 'vg_free'
71
  """
72
  cmd = ['df', '-BM', path]
73
  result = utils.RunCmd(cmd)
74
  if result.failed:
75
    raise errors.CommandError("Failed to run 'df' command: %s - %s" %
76
                              (result.fail_reason, result.output))
77
  (size, free) = _parsefn(result.stdout)
78
  return {"vg_size": size, "vg_free": free}
b/test/py/ganeti.storage.filestorage_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 ganeti.storage.file module"""
23

  
24

  
25
import unittest
26

  
27
from ganeti import errors
28
from ganeti.storage import filestorage
29

  
30
import testutils
31

  
32

  
33
class TestFileStorageSpaceInfo(unittest.TestCase):
34

  
35
  def testSpaceInfoPathInvalid(self):
36
    """Tests that an error is raised when the given file is not existing.
37

  
38
    """
39
    self.assertRaises(errors.CommandError, filestorage.GetSpaceInfo,
40
                      "/path/does/not/exist/")
41

  
42
  def testSpaceInfoPathValid(self):
43
    """Tests that the 'df' command is run if the file is valid.
44

  
45
    """
46
    info = filestorage.GetSpaceInfo("/")
47

  
48
  def testParseDfOutputValidInput(self):
49
    """Tests that parsing of the output of 'df' works correctly.
50

  
51
    """
52
    valid_df_output = \
53
      "Filesystem             1M-blocks   Used Available Use% Mounted on\n" \
54
      "/dev/mapper/sysvg-root   161002M 58421M    94403M  39% /"
55
    expected_size = 161002
56
    expected_free = 94403
57

  
58
    (size, free) = filestorage._ParseDfResult(valid_df_output)
59
    self.assertEqual(expected_size, size,
60
                     "Calculation of total size is incorrect.")
61
    self.assertEqual(expected_free, free,
62
                     "Calculation of free space is incorrect.")
63

  
64

  
65
  def testParseDfOutputInvalidInput(self):
66
    """Tests that parsing of the output of 'df' works correctly when invalid
67
       input is given.
68

  
69
    """
70
    invalid_output_header_missing = \
71
      "/dev/mapper/sysvg-root   161002M 58421M    94403M  39% /"
72
    invalid_output_dataline_missing = \
73
      "Filesystem             1M-blocks   Used Available Use% Mounted on\n"
74
    invalid_output_wrong_num_columns = \
75
      "Filesystem             1M-blocks Available\n" \
76
      "/dev/mapper/sysvg-root   161002M    94403M"
77
    invalid_output_units_wrong = \
78
      "Filesystem             1M-blocks   Used Available Use% Mounted on\n" \
79
      "/dev/mapper/sysvg-root   161002G 58421G    94403G  39% /"
80
    invalid_output_units_missing = \
81
      "Filesystem             1M-blocks   Used Available Use% Mounted on\n" \
82
      "/dev/mapper/sysvg-root    161002  58421     94403  39% /"
83
    invalid_outputs = [invalid_output_header_missing,
84
                       invalid_output_dataline_missing,
85
                       invalid_output_wrong_num_columns,
86
                       invalid_output_units_wrong,
87
                       invalid_output_units_missing]
88

  
89
    for output in invalid_outputs:
90
      self.assertRaises(errors.CommandError, filestorage._ParseDfResult, output)
91

  
92

  
93
if __name__ == "__main__":
94
  testutils.GanetiTestProgram()

Also available in: Unified diff