4 # Copyright (C) 2006, 2007, 2010, 2012, 2013 Google Inc.
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.
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.
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
22 """Script for unittesting the bdev module"""
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
39 class TestRADOSBlockDevice(testutils.GanetiTestCase):
41 """Set up input data"""
42 testutils.GanetiTestCase.setUp(self)
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")
69 self.volume_name = "d7ab910a-4933-4ffe-88d0-faf2ce31390a.rbd.disk0"
71 def test_ParseRbdShowmappedJson(self):
72 parse_function = bdev.RADOSBlockDevice._ParseRbdShowmappedJson
74 self.assertEqual(parse_function(self.json_output_ok, self.volume_name),
76 self.assertEqual(parse_function(self.json_output_empty, self.volume_name),
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)
85 def test_ParseRbdShowmappedPlain(self):
86 parse_function = bdev.RADOSBlockDevice._ParseRbdShowmappedPlain
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)
107 class TestExclusiveStoragePvs(unittest.TestCase):
108 """Test cases for functions dealing with LVM PV and exclusive storage"""
109 # Allowance for rounding
111 _MARGIN = constants.PART_MARGIN + constants.PART_RESERVED + _EPS
114 def _GenerateRandomPvInfo(rnd, name, vg):
115 # Granularity is .01 MiB
116 size = rnd.randint(1024 * 100, 10 * 1024 * 1024 * 100)
117 if rnd.choice([False, True]):
118 free = float(rnd.randint(0, size)) / 100.0
120 free = float(size) / 100.0
121 size = float(size) / 100.0
123 return objects.LvmPvInfo(name=name, vg_name=vg, size=size, free=free,
126 def testGetStdPvSize(self):
127 """Test cases for bdev.LogicalVolume._GetStdPvSize()"""
128 rnd = random.Random(9517)
129 for _ in range(0, 50):
131 pvi = self._GenerateRandomPvInfo(rnd, "disk", "myvg")
132 onesize = bdev.LogicalVolume._GetStdPvSize([pvi])
133 self.assertTrue(onesize <= pvi.size)
134 self.assertTrue(onesize > pvi.size * (1 - self._MARGIN))
135 for length in range(2, 10):
136 n_size = bdev.LogicalVolume._GetStdPvSize([pvi] * length)
137 self.assertEqual(onesize, n_size)
140 for length in range(1, 10):
141 pvlist = [self._GenerateRandomPvInfo(rnd, "disk", "myvg")
142 for _ in range(0, length)]
143 std_size = bdev.LogicalVolume._GetStdPvSize(pvlist)
144 self.assertTrue(compat.all(std_size <= pvi.size for pvi in pvlist))
145 self.assertTrue(compat.any(std_size > pvi.size * (1 - self._MARGIN)
147 pvlist.append(pvlist[0])
148 p1_size = bdev.LogicalVolume._GetStdPvSize(pvlist)
149 self.assertEqual(std_size, p1_size)
151 def testComputeNumPvs(self):
152 """Test cases for bdev.LogicalVolume._ComputeNumPvs()"""
153 rnd = random.Random(8067)
154 for _ in range(0, 1000):
155 pvlist = [self._GenerateRandomPvInfo(rnd, "disk", "myvg")]
156 lv_size = float(rnd.randint(10 * 100, 1024 * 1024 * 100)) / 100.0
157 num_pv = bdev.LogicalVolume._ComputeNumPvs(lv_size, pvlist)
158 std_size = bdev.LogicalVolume._GetStdPvSize(pvlist)
159 self.assertTrue(num_pv >= 1)
160 self.assertTrue(num_pv * std_size >= lv_size)
161 self.assertTrue((num_pv - 1) * std_size < lv_size * (1 + self._EPS))
163 def testGetEmptyPvNames(self):
164 """Test cases for bdev.LogicalVolume._GetEmptyPvNames()"""
165 rnd = random.Random(21126)
166 for _ in range(0, 100):
167 num_pvs = rnd.randint(1, 20)
168 pvlist = [self._GenerateRandomPvInfo(rnd, "disk%d" % n, "myvg")
169 for n in range(0, num_pvs)]
170 for num_req in range(1, num_pvs + 2):
171 epvs = bdev.LogicalVolume._GetEmptyPvNames(pvlist, num_req)
172 epvs_set = compat.UniqueFrozenset(epvs)
174 self.assertEqual(len(epvs), len(epvs_set))
176 if pvi.name in epvs_set:
177 self.assertEqual(pvi.size, pvi.free)
179 # There should be no remaining empty PV when less than the
180 # requeste number of PVs has been returned
181 self.assertTrue(len(epvs) == num_req or pvi.free != pvi.size)
184 class TestLogicalVolume(unittest.TestCase):
185 """Tests for bdev.LogicalVolume."""
186 def testParseLvInfoLine(self):
187 """Tests for LogicalVolume._ParseLvInfoLine."""
189 " toomuch#-wi-ao#253#3#4096.00#2#/dev/abc(20)",
190 " -wi-ao#253#3#4096.00#/dev/abc(20)",
191 " -wi-a#253#3#4096.00#2#/dev/abc(20)",
192 " -wi-ao#25.3#3#4096.00#2#/dev/abc(20)",
193 " -wi-ao#twenty#3#4096.00#2#/dev/abc(20)",
194 " -wi-ao#253#3.1#4096.00#2#/dev/abc(20)",
195 " -wi-ao#253#three#4096.00#2#/dev/abc(20)",
196 " -wi-ao#253#3#four#2#/dev/abc(20)",
197 " -wi-ao#253#3#4096..00#2#/dev/abc(20)",
198 " -wi-ao#253#3#4096.00#2.0#/dev/abc(20)",
199 " -wi-ao#253#3#4096.00#two#/dev/abc(20)",
201 for broken in broken_lines:
202 self.assertRaises(errors.BlockDeviceError,
203 bdev.LogicalVolume._ParseLvInfoLine, broken, "#")
205 # Examples of good lines from "lvs":
206 # -wi-ao|253|3|4096.00|2|/dev/sdb(144),/dev/sdc(0)
207 # -wi-a-|253|4|4096.00|1|/dev/sdb(208)
209 ("-wi-ao", 253, 3, 4096.00, 2, ["/dev/abc"]),
210 ("-wi-a-", 253, 7, 4096.00, 4, ["/dev/abc"]),
211 ("-ri-a-", 253, 4, 4.00, 5, ["/dev/abc", "/dev/def"]),
212 ("-wc-ao", 15, 18, 4096.00, 32, ["/dev/abc", "/dev/def", "/dev/ghi0"]),
216 pvs = ",".join("%s(%s)" % (d, i * 12) for (i, d) in enumerate(exp[-1]))
217 lvs_line = (sep.join((" %s", "%d", "%d", "%.2f", "%d", "%s")) %
218 (exp[0:-1] + (pvs,)))
219 parsed = bdev.LogicalVolume._ParseLvInfoLine(lvs_line, sep)
220 self.assertEqual(parsed, exp)
223 def _FakeRunCmd(success, stdout):
228 return lambda cmd: utils.RunResult(exit_code, None, stdout, "", cmd,
229 utils.process._TIMEOUT_NONE, 5)
231 def testGetLvInfo(self):
232 """Tests for LogicalVolume._GetLvInfo."""
233 self.assertRaises(errors.BlockDeviceError, bdev.LogicalVolume._GetLvInfo,
235 _run_cmd=self._FakeRunCmd(False, "Fake error msg"))
236 self.assertRaises(errors.BlockDeviceError, bdev.LogicalVolume._GetLvInfo,
237 "fake_path", _run_cmd=self._FakeRunCmd(True, ""))
238 self.assertRaises(errors.BlockDeviceError, bdev.LogicalVolume._GetLvInfo,
239 "fake_path", _run_cmd=self._FakeRunCmd(True, "BadStdOut"))
240 good_line = " -wi-ao|253|3|4096.00|2|/dev/abc(20)"
241 fake_cmd = self._FakeRunCmd(True, good_line)
242 good_res = bdev.LogicalVolume._GetLvInfo("fake_path", _run_cmd=fake_cmd)
243 # If the same line is repeated, the result should be the same
248 fake_cmd = self._FakeRunCmd(True, "\n".join(lines))
249 same_res = bdev.LogicalVolume._GetLvInfo("fake_path", fake_cmd)
250 self.assertEqual(same_res, good_res)
252 # Complex multi-line examples
253 one_line = " -wi-ao|253|3|4096.00|2|/dev/sda(20),/dev/sdb(50),/dev/sdc(0)"
254 fake_cmd = self._FakeRunCmd(True, one_line)
255 one_res = bdev.LogicalVolume._GetLvInfo("fake_path", _run_cmd=fake_cmd)
256 # These should give the same results
258 (" -wi-ao|253|3|4096.00|2|/dev/sda(30),/dev/sdb(50)\n"
259 " -wi-ao|253|3|4096.00|2|/dev/sdb(200),/dev/sdc(300)"),
260 (" -wi-ao|253|3|4096.00|2|/dev/sda(0)\n"
261 " -wi-ao|253|3|4096.00|2|/dev/sdb(20)\n"
262 " -wi-ao|253|3|4096.00|2|/dev/sdc(30)"),
263 (" -wi-ao|253|3|4096.00|2|/dev/sda(20)\n"
264 " -wi-ao|253|3|4096.00|2|/dev/sdb(50),/dev/sdc(0)"),
266 fake_cmd = self._FakeRunCmd(True, multi_lines)
267 multi_res = bdev.LogicalVolume._GetLvInfo("fake_path", _run_cmd=fake_cmd)
268 self.assertEqual(multi_res, one_res)
271 if __name__ == "__main__":
272 testutils.GanetiTestProgram()