Commit 820bade9 authored by Helga Velroyen's avatar Helga Velroyen

Backend function for file storage space reporting

This adds functionality to retrieve disk space information
for file storage. It calls the 'df' tool and parses its
output to extract the total and free amount of disk space
on the disk where the given path is located.
The code is not integrated yet, but thoroughly unit-tested.
Signed-off-by: default avatarHelga Velroyen <helgav@google.com>
Reviewed-by: default avatarBernardo dal Seno <bdalseno@google.com>
parent 199b241c
......@@ -322,7 +322,8 @@ storage_PYTHON = \
lib/storage/container.py \
lib/storage/drbd.py \
lib/storage/drbd_info.py \
lib/storage/drbd_cmdgen.py
lib/storage/drbd_cmdgen.py \
lib/storage/filestorage.py
rapi_PYTHON = \
lib/rapi/__init__.py \
......@@ -1191,6 +1192,7 @@ python_tests = \
test/py/ganeti.server.rapi_unittest.py \
test/py/ganeti.ssconf_unittest.py \
test/py/ganeti.ssh_unittest.py \
test/py/ganeti.storage.filestorage_unittest.py \
test/py/ganeti.tools.burnin_unittest.py \
test/py/ganeti.tools.ensure_dirs_unittest.py \
test/py/ganeti.tools.node_daemon_setup_unittest.py \
......
#
#
# Copyright (C) 2013 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
"""File storage functions.
"""
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.
@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)
def GetSpaceInfo(path, _parsefn=_ParseDfResult):
"""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'
"""
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}
#!/usr/bin/python
#
# Copyright (C) 2013 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
"""Script for unittesting the ganeti.storage.file module"""
import unittest
from ganeti import errors
from ganeti.storage import filestorage
import testutils
class TestFileStorageSpaceInfo(unittest.TestCase):
def testSpaceInfoPathInvalid(self):
"""Tests that an error is raised when the given file is not existing.
"""
self.assertRaises(errors.CommandError, filestorage.GetSpaceInfo,
"/path/does/not/exist/")
def testSpaceInfoPathValid(self):
"""Tests that the 'df' command is run if the file is valid.
"""
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()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment