Use os.statvfs to determine free disk space
authorHelga Velroyen <helgav@google.com>
Wed, 15 May 2013 09:45:40 +0000 (11:45 +0200)
committerHelga Velroyen <helgav@google.com>
Wed, 15 May 2013 12:02:21 +0000 (14:02 +0200)
This simplifies my previous commit (820bade90) by using os.statvfs
instead of parsing the output of 'df'.

Signed-off-by: Helga Velroyen <helgav@google.com>
Reviewed-by: Bernardo Dal Seno <bdalseno@google.com>

lib/storage/filestorage.py
test/py/ganeti.storage.filestorage_unittest.py

index 65c963c..3e64018 100644 (file)
 
 """
 
-from ganeti import errors
-from ganeti import utils
-
-DF_M_UNIT = 'M'
-DF_MIN_NUM_COLS = 4
-DF_NUM_LINES = 2
-
-
-def _ParseDfResult(dfresult):
-  """Parses the output of the call of the 'df' tool.
+import os
 
-     @type dfresult: string
-     @param dfresult: output of the 'df' call
-     @return: tuple (size, free) of the total and free disk space in MebiBytes
-  """
-  df_lines = dfresult.splitlines()
-  if len(df_lines) != DF_NUM_LINES:
-    raise errors.CommandError("'df' output has wrong number of lines: %s" %
-                              len(df_lines))
-  df_values = df_lines[1].strip().split()
-  if len(df_values) < DF_MIN_NUM_COLS:
-    raise errors.CommandError("'df' output does not have enough columns: %s" %
-                              len(df_values))
-  size_str = df_values[1]
-  if size_str[-1] != DF_M_UNIT:
-    raise errors.CommandError("'df': 'size' not given in Mebibytes.")
-  free_str = df_values[3]
-  if free_str[-1] != DF_M_UNIT:
-    raise errors.CommandError("'df': 'free' not given in Mebibytes.")
-  size = int(size_str[:-1])
-  free = int(free_str[:-1])
-  return (size, free)
+from ganeti import errors
 
 
-def GetSpaceInfo(path, _parsefn=_ParseDfResult):
+def GetSpaceInfo(path):
   """Retrieves the free and total space of the device where the file is
      located.
 
      @type path: string
      @param path: Path of the file whose embracing device's capacity is
        reported.
-     @type _parsefn: function
-     @param _parsefn: Function that parses the output of the 'df' command;
-       given as parameter to make this code more testable.
-     @return: a dictionary containing 'vg_size' and 'vg_free'
+     @return: a dictionary containing 'vg_size' and 'vg_free' given in MebiBytes
+
   """
-  cmd = ['df', '-BM', path]
-  result = utils.RunCmd(cmd)
-  if result.failed:
-    raise errors.CommandError("Failed to run 'df' command: %s - %s" %
-                              (result.fail_reason, result.output))
-  (size, free) = _parsefn(result.stdout)
-  return {"vg_size": size, "vg_free": free}
+  try:
+    result = os.statvfs(path)
+    free = (result.f_frsize * result.f_bavail) / (1024 * 1024)
+    size = (result.f_frsize * result.f_blocks) / (1024 * 1024)
+    return {"vg_size": size, "vg_free": free}
+  except OSError, e:
+    raise errors.CommandError("Failed to retrieve file system information about"
+                              " path: %s - %s" % (path, e.strerror))
index 03b03b2..53e1398 100755 (executable)
@@ -45,50 +45,6 @@ class TestFileStorageSpaceInfo(unittest.TestCase):
     """
     info = filestorage.GetSpaceInfo("/")
 
-  def testParseDfOutputValidInput(self):
-    """Tests that parsing of the output of 'df' works correctly.
-
-    """
-    valid_df_output = \
-      "Filesystem             1M-blocks   Used Available Use% Mounted on\n" \
-      "/dev/mapper/sysvg-root   161002M 58421M    94403M  39% /"
-    expected_size = 161002
-    expected_free = 94403
-
-    (size, free) = filestorage._ParseDfResult(valid_df_output)
-    self.assertEqual(expected_size, size,
-                     "Calculation of total size is incorrect.")
-    self.assertEqual(expected_free, free,
-                     "Calculation of free space is incorrect.")
-
-
-  def testParseDfOutputInvalidInput(self):
-    """Tests that parsing of the output of 'df' works correctly when invalid
-       input is given.
-
-    """
-    invalid_output_header_missing = \
-      "/dev/mapper/sysvg-root   161002M 58421M    94403M  39% /"
-    invalid_output_dataline_missing = \
-      "Filesystem             1M-blocks   Used Available Use% Mounted on\n"
-    invalid_output_wrong_num_columns = \
-      "Filesystem             1M-blocks Available\n" \
-      "/dev/mapper/sysvg-root   161002M    94403M"
-    invalid_output_units_wrong = \
-      "Filesystem             1M-blocks   Used Available Use% Mounted on\n" \
-      "/dev/mapper/sysvg-root   161002G 58421G    94403G  39% /"
-    invalid_output_units_missing = \
-      "Filesystem             1M-blocks   Used Available Use% Mounted on\n" \
-      "/dev/mapper/sysvg-root    161002  58421     94403  39% /"
-    invalid_outputs = [invalid_output_header_missing,
-                       invalid_output_dataline_missing,
-                       invalid_output_wrong_num_columns,
-                       invalid_output_units_wrong,
-                       invalid_output_units_missing]
-
-    for output in invalid_outputs:
-      self.assertRaises(errors.CommandError, filestorage._ParseDfResult, output)
-
 
 if __name__ == "__main__":
   testutils.GanetiTestProgram()