root / test / py / ganeti.storage.bdev_unittest.py @ cde49218
History | View | Annotate | Download (11.2 kB)
1 |
#!/usr/bin/python
|
---|---|
2 |
#
|
3 |
|
4 |
# Copyright (C) 2006, 2007, 2010, 2012, 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 bdev module"""
|
23 |
|
24 |
|
25 |
import os |
26 |
import random |
27 |
import unittest |
28 |
|
29 |
from ganeti import compat |
30 |
from ganeti import constants |
31 |
from ganeti import errors |
32 |
from ganeti import objects |
33 |
from ganeti import utils |
34 |
from ganeti.storage import bdev |
35 |
|
36 |
import testutils |
37 |
|
38 |
|
39 |
class TestRADOSBlockDevice(testutils.GanetiTestCase): |
40 |
def setUp(self): |
41 |
"""Set up input data"""
|
42 |
testutils.GanetiTestCase.setUp(self)
|
43 |
|
44 |
self.plain_output_old_ok = \
|
45 |
testutils.ReadTestData("bdev-rbd/plain_output_old_ok.txt")
|
46 |
self.plain_output_old_no_matches = \
|
47 |
testutils.ReadTestData("bdev-rbd/plain_output_old_no_matches.txt")
|
48 |
self.plain_output_old_extra_matches = \
|
49 |
testutils.ReadTestData("bdev-rbd/plain_output_old_extra_matches.txt")
|
50 |
self.plain_output_old_empty = \
|
51 |
testutils.ReadTestData("bdev-rbd/plain_output_old_empty.txt")
|
52 |
self.plain_output_new_ok = \
|
53 |
testutils.ReadTestData("bdev-rbd/plain_output_new_ok.txt")
|
54 |
self.plain_output_new_no_matches = \
|
55 |
testutils.ReadTestData("bdev-rbd/plain_output_new_no_matches.txt")
|
56 |
self.plain_output_new_extra_matches = \
|
57 |
testutils.ReadTestData("bdev-rbd/plain_output_new_extra_matches.txt")
|
58 |
# This file is completely empty, and as such it's not shipped.
|
59 |
self.plain_output_new_empty = "" |
60 |
self.json_output_ok = testutils.ReadTestData("bdev-rbd/json_output_ok.txt") |
61 |
self.json_output_no_matches = \
|
62 |
testutils.ReadTestData("bdev-rbd/json_output_no_matches.txt")
|
63 |
self.json_output_extra_matches = \
|
64 |
testutils.ReadTestData("bdev-rbd/json_output_extra_matches.txt")
|
65 |
self.json_output_empty = \
|
66 |
testutils.ReadTestData("bdev-rbd/json_output_empty.txt")
|
67 |
self.output_invalid = testutils.ReadTestData("bdev-rbd/output_invalid.txt") |
68 |
|
69 |
self.volume_name = "d7ab910a-4933-4ffe-88d0-faf2ce31390a.rbd.disk0" |
70 |
|
71 |
def test_ParseRbdShowmappedJson(self): |
72 |
parse_function = bdev.RADOSBlockDevice._ParseRbdShowmappedJson |
73 |
|
74 |
self.assertEqual(parse_function(self.json_output_ok, self.volume_name), |
75 |
"/dev/rbd3")
|
76 |
self.assertEqual(parse_function(self.json_output_empty, self.volume_name), |
77 |
None)
|
78 |
self.assertEqual(parse_function(self.json_output_no_matches, |
79 |
self.volume_name), None) |
80 |
self.assertRaises(errors.BlockDeviceError, parse_function,
|
81 |
self.json_output_extra_matches, self.volume_name) |
82 |
self.assertRaises(errors.BlockDeviceError, parse_function,
|
83 |
self.output_invalid, self.volume_name) |
84 |
|
85 |
def test_ParseRbdShowmappedPlain(self): |
86 |
parse_function = bdev.RADOSBlockDevice._ParseRbdShowmappedPlain |
87 |
|
88 |
self.assertEqual(parse_function(self.plain_output_new_ok, |
89 |
self.volume_name), "/dev/rbd3") |
90 |
self.assertEqual(parse_function(self.plain_output_old_ok, |
91 |
self.volume_name), "/dev/rbd3") |
92 |
self.assertEqual(parse_function(self.plain_output_new_empty, |
93 |
self.volume_name), None) |
94 |
self.assertEqual(parse_function(self.plain_output_old_empty, |
95 |
self.volume_name), None) |
96 |
self.assertEqual(parse_function(self.plain_output_new_no_matches, |
97 |
self.volume_name), None) |
98 |
self.assertEqual(parse_function(self.plain_output_old_no_matches, |
99 |
self.volume_name), None) |
100 |
self.assertRaises(errors.BlockDeviceError, parse_function,
|
101 |
self.plain_output_new_extra_matches, self.volume_name) |
102 |
self.assertRaises(errors.BlockDeviceError, parse_function,
|
103 |
self.plain_output_old_extra_matches, self.volume_name) |
104 |
self.assertRaises(errors.BlockDeviceError, parse_function,
|
105 |
self.output_invalid, self.volume_name) |
106 |
|
107 |
class TestComputeWrongFileStoragePathsInternal(unittest.TestCase): |
108 |
def testPaths(self): |
109 |
paths = bdev._GetForbiddenFileStoragePaths() |
110 |
|
111 |
for path in ["/bin", "/usr/local/sbin", "/lib64", "/etc", "/sys"]: |
112 |
self.assertTrue(path in paths) |
113 |
|
114 |
self.assertEqual(set(map(os.path.normpath, paths)), paths) |
115 |
|
116 |
def test(self): |
117 |
vfsp = bdev._ComputeWrongFileStoragePaths |
118 |
self.assertEqual(vfsp([]), [])
|
119 |
self.assertEqual(vfsp(["/tmp"]), []) |
120 |
self.assertEqual(vfsp(["/bin/ls"]), ["/bin/ls"]) |
121 |
self.assertEqual(vfsp(["/bin"]), ["/bin"]) |
122 |
self.assertEqual(vfsp(["/usr/sbin/vim", "/srv/file-storage"]), |
123 |
["/usr/sbin/vim"])
|
124 |
|
125 |
|
126 |
class TestComputeWrongFileStoragePaths(testutils.GanetiTestCase): |
127 |
def test(self): |
128 |
tmpfile = self._CreateTempFile()
|
129 |
|
130 |
utils.WriteFile(tmpfile, data="""
|
131 |
/tmp
|
132 |
x/y///z/relative
|
133 |
# This is a test file
|
134 |
/srv/storage
|
135 |
/bin
|
136 |
/usr/local/lib32/
|
137 |
relative/path
|
138 |
""")
|
139 |
|
140 |
self.assertEqual(bdev.ComputeWrongFileStoragePaths(_filename=tmpfile), [
|
141 |
"/bin",
|
142 |
"/usr/local/lib32",
|
143 |
"relative/path",
|
144 |
"x/y/z/relative",
|
145 |
]) |
146 |
|
147 |
|
148 |
class TestCheckFileStoragePathInternal(unittest.TestCase): |
149 |
def testNonAbsolute(self): |
150 |
for i in ["", "tmp", "foo/bar/baz"]: |
151 |
self.assertRaises(errors.FileStoragePathError,
|
152 |
bdev._CheckFileStoragePath, i, ["/tmp"])
|
153 |
|
154 |
self.assertRaises(errors.FileStoragePathError,
|
155 |
bdev._CheckFileStoragePath, "/tmp", ["tmp", "xyz"]) |
156 |
|
157 |
def testNoAllowed(self): |
158 |
self.assertRaises(errors.FileStoragePathError,
|
159 |
bdev._CheckFileStoragePath, "/tmp", [])
|
160 |
|
161 |
def testNoAdditionalPathComponent(self): |
162 |
self.assertRaises(errors.FileStoragePathError,
|
163 |
bdev._CheckFileStoragePath, "/tmp/foo", ["/tmp/foo"]) |
164 |
|
165 |
def testAllowed(self): |
166 |
bdev._CheckFileStoragePath("/tmp/foo/a", ["/tmp/foo"]) |
167 |
bdev._CheckFileStoragePath("/tmp/foo/a/x", ["/tmp/foo"]) |
168 |
|
169 |
|
170 |
class TestCheckFileStoragePath(testutils.GanetiTestCase): |
171 |
def testNonExistantFile(self): |
172 |
filename = "/tmp/this/file/does/not/exist"
|
173 |
assert not os.path.exists(filename) |
174 |
self.assertRaises(errors.FileStoragePathError,
|
175 |
bdev.CheckFileStoragePath, "/bin/", _filename=filename)
|
176 |
self.assertRaises(errors.FileStoragePathError,
|
177 |
bdev.CheckFileStoragePath, "/srv/file-storage",
|
178 |
_filename=filename) |
179 |
|
180 |
def testAllowedPath(self): |
181 |
tmpfile = self._CreateTempFile()
|
182 |
|
183 |
utils.WriteFile(tmpfile, data="""
|
184 |
/srv/storage
|
185 |
""")
|
186 |
|
187 |
bdev.CheckFileStoragePath("/srv/storage/inst1", _filename=tmpfile)
|
188 |
|
189 |
# No additional path component
|
190 |
self.assertRaises(errors.FileStoragePathError,
|
191 |
bdev.CheckFileStoragePath, "/srv/storage",
|
192 |
_filename=tmpfile) |
193 |
|
194 |
# Forbidden path
|
195 |
self.assertRaises(errors.FileStoragePathError,
|
196 |
bdev.CheckFileStoragePath, "/usr/lib64/xyz",
|
197 |
_filename=tmpfile) |
198 |
|
199 |
|
200 |
class TestLoadAllowedFileStoragePaths(testutils.GanetiTestCase): |
201 |
def testDevNull(self): |
202 |
self.assertEqual(bdev._LoadAllowedFileStoragePaths("/dev/null"), []) |
203 |
|
204 |
def testNonExistantFile(self): |
205 |
filename = "/tmp/this/file/does/not/exist"
|
206 |
assert not os.path.exists(filename) |
207 |
self.assertEqual(bdev._LoadAllowedFileStoragePaths(filename), [])
|
208 |
|
209 |
def test(self): |
210 |
tmpfile = self._CreateTempFile()
|
211 |
|
212 |
utils.WriteFile(tmpfile, data="""
|
213 |
# This is a test file
|
214 |
/tmp
|
215 |
/srv/storage
|
216 |
relative/path
|
217 |
""")
|
218 |
|
219 |
self.assertEqual(bdev._LoadAllowedFileStoragePaths(tmpfile), [
|
220 |
"/tmp",
|
221 |
"/srv/storage",
|
222 |
"relative/path",
|
223 |
]) |
224 |
|
225 |
|
226 |
class TestExclusiveStoragePvs(unittest.TestCase): |
227 |
"""Test cases for functions dealing with LVM PV and exclusive storage"""
|
228 |
# Allowance for rounding
|
229 |
_EPS = 1e-4
|
230 |
_MARGIN = constants.PART_MARGIN + constants.PART_RESERVED + _EPS |
231 |
|
232 |
@staticmethod
|
233 |
def _GenerateRandomPvInfo(rnd, name, vg): |
234 |
# Granularity is .01 MiB
|
235 |
size = rnd.randint(1024 * 100, 10 * 1024 * 1024 * 100) |
236 |
if rnd.choice([False, True]): |
237 |
free = float(rnd.randint(0, size)) / 100.0 |
238 |
else:
|
239 |
free = float(size) / 100.0 |
240 |
size = float(size) / 100.0 |
241 |
attr = "a-"
|
242 |
return objects.LvmPvInfo(name=name, vg_name=vg, size=size, free=free,
|
243 |
attributes=attr) |
244 |
|
245 |
def testGetStdPvSize(self): |
246 |
"""Test cases for bdev.LogicalVolume._GetStdPvSize()"""
|
247 |
rnd = random.Random(9517)
|
248 |
for _ in range(0, 50): |
249 |
# Identical volumes
|
250 |
pvi = self._GenerateRandomPvInfo(rnd, "disk", "myvg") |
251 |
onesize = bdev.LogicalVolume._GetStdPvSize([pvi]) |
252 |
self.assertTrue(onesize <= pvi.size)
|
253 |
self.assertTrue(onesize > pvi.size * (1 - self._MARGIN)) |
254 |
for length in range(2, 10): |
255 |
n_size = bdev.LogicalVolume._GetStdPvSize([pvi] * length) |
256 |
self.assertEqual(onesize, n_size)
|
257 |
|
258 |
# Mixed volumes
|
259 |
for length in range(1, 10): |
260 |
pvlist = [self._GenerateRandomPvInfo(rnd, "disk", "myvg") |
261 |
for _ in range(0, length)] |
262 |
std_size = bdev.LogicalVolume._GetStdPvSize(pvlist) |
263 |
self.assertTrue(compat.all(std_size <= pvi.size for pvi in pvlist)) |
264 |
self.assertTrue(compat.any(std_size > pvi.size * (1 - self._MARGIN) |
265 |
for pvi in pvlist)) |
266 |
pvlist.append(pvlist[0])
|
267 |
p1_size = bdev.LogicalVolume._GetStdPvSize(pvlist) |
268 |
self.assertEqual(std_size, p1_size)
|
269 |
|
270 |
def testComputeNumPvs(self): |
271 |
"""Test cases for bdev.LogicalVolume._ComputeNumPvs()"""
|
272 |
rnd = random.Random(8067)
|
273 |
for _ in range(0, 1000): |
274 |
pvlist = [self._GenerateRandomPvInfo(rnd, "disk", "myvg")] |
275 |
lv_size = float(rnd.randint(10 * 100, 1024 * 1024 * 100)) / 100.0 |
276 |
num_pv = bdev.LogicalVolume._ComputeNumPvs(lv_size, pvlist) |
277 |
std_size = bdev.LogicalVolume._GetStdPvSize(pvlist) |
278 |
self.assertTrue(num_pv >= 1) |
279 |
self.assertTrue(num_pv * std_size >= lv_size)
|
280 |
self.assertTrue((num_pv - 1) * std_size < lv_size * (1 + self._EPS)) |
281 |
|
282 |
def testGetEmptyPvNames(self): |
283 |
"""Test cases for bdev.LogicalVolume._GetEmptyPvNames()"""
|
284 |
rnd = random.Random(21126)
|
285 |
for _ in range(0, 100): |
286 |
num_pvs = rnd.randint(1, 20) |
287 |
pvlist = [self._GenerateRandomPvInfo(rnd, "disk%d" % n, "myvg") |
288 |
for n in range(0, num_pvs)] |
289 |
for num_req in range(1, num_pvs + 2): |
290 |
epvs = bdev.LogicalVolume._GetEmptyPvNames(pvlist, num_req) |
291 |
epvs_set = compat.UniqueFrozenset(epvs) |
292 |
if len(epvs) > 1: |
293 |
self.assertEqual(len(epvs), len(epvs_set)) |
294 |
for pvi in pvlist: |
295 |
if pvi.name in epvs_set: |
296 |
self.assertEqual(pvi.size, pvi.free)
|
297 |
else:
|
298 |
# There should be no remaining empty PV when less than the
|
299 |
# requeste number of PVs has been returned
|
300 |
self.assertTrue(len(epvs) == num_req or pvi.free != pvi.size) |
301 |
|
302 |
|
303 |
if __name__ == "__main__": |
304 |
testutils.GanetiTestProgram() |