Revision 5d94c034

b/lib/storage/filestorage.py
23 23

  
24 24
"""
25 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.
26
import os
36 27

  
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)
28
from ganeti import errors
58 29

  
59 30

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

  
64 35
     @type path: string
65 36
     @param path: Path of the file whose embracing device's capacity is
66 37
       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'
38
     @return: a dictionary containing 'vg_size' and 'vg_free' given in MebiBytes
39

  
71 40
  """
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}
41
  try:
42
    result = os.statvfs(path)
43
    free = (result.f_frsize * result.f_bavail) / (1024 * 1024)
44
    size = (result.f_frsize * result.f_blocks) / (1024 * 1024)
45
    return {"vg_size": size, "vg_free": free}
46
  except OSError, e:
47
    raise errors.CommandError("Failed to retrieve file system information about"
48
                              " path: %s - %s" % (path, e.strerror))
b/test/py/ganeti.storage.filestorage_unittest.py
45 45
    """
46 46
    info = filestorage.GetSpaceInfo("/")
47 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 48

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

Also available in: Unified diff