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