4 # Copyright (C) 2006, 2007, 2010 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"""
28 from ganeti import bdev
29 from ganeti import errors
30 from ganeti import constants
31 from ganeti import utils
36 class TestBaseDRBD(testutils.GanetiTestCase):
37 def testGetVersion(self):
39 ["version: 8.0.12 (api:76/proto:86-91)"],
40 ["version: 8.2.7 (api:88/proto:0-100)"],
41 ["version: 8.3.7.49 (api:188/proto:13-191)"],
69 for d,r in zip(data, result):
70 self.assertEqual(bdev.BaseDRBD._GetVersion(d), r)
73 class TestDRBD8Runner(testutils.GanetiTestCase):
74 """Testing case for DRBD8"""
77 def _has_disk(data, dname, mname):
78 """Check local disk corectness"""
80 "local_dev" in data and
81 data["local_dev"] == dname and
82 "meta_dev" in data and
83 data["meta_dev"] == mname and
84 "meta_index" in data and
85 data["meta_index"] == 0
90 def _has_net(data, local, remote):
91 """Check network connection parameters"""
93 "local_addr" in data and
94 data["local_addr"] == local and
95 "remote_addr" in data and
96 data["remote_addr"] == remote
100 def testParserCreation(self):
101 """Test drbdsetup show parser creation"""
102 bdev.DRBD8._GetShowParser()
104 def testParser80(self):
105 """Test drbdsetup show parser for disk and network version 8.0"""
106 data = self._ReadTestData("bdev-drbd-8.0.txt")
107 result = bdev.DRBD8._GetDevInfo(data)
108 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
109 "/dev/xenvg/test.meta"),
110 "Wrong local disk info")
111 self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
112 ("192.0.2.2", 11000)),
113 "Wrong network info (8.0.x)")
115 def testParser83(self):
116 """Test drbdsetup show parser for disk and network version 8.3"""
117 data = self._ReadTestData("bdev-drbd-8.3.txt")
118 result = bdev.DRBD8._GetDevInfo(data)
119 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
120 "/dev/xenvg/test.meta"),
121 "Wrong local disk info")
122 self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
123 ("192.0.2.2", 11000)),
124 "Wrong network info (8.0.x)")
126 def testParserNetIP4(self):
127 """Test drbdsetup show parser for IPv4 network"""
128 data = self._ReadTestData("bdev-drbd-net-ip4.txt")
129 result = bdev.DRBD8._GetDevInfo(data)
130 self.failUnless(("local_dev" not in result and
131 "meta_dev" not in result and
132 "meta_index" not in result),
133 "Should not find local disk info")
134 self.failUnless(self._has_net(result, ("192.0.2.1", 11002),
135 ("192.0.2.2", 11002)),
136 "Wrong network info (IPv4)")
138 def testParserNetIP6(self):
139 """Test drbdsetup show parser for IPv6 network"""
140 data = self._ReadTestData("bdev-drbd-net-ip6.txt")
141 result = bdev.DRBD8._GetDevInfo(data)
142 self.failUnless(("local_dev" not in result and
143 "meta_dev" not in result and
144 "meta_index" not in result),
145 "Should not find local disk info")
146 self.failUnless(self._has_net(result, ("2001:db8:65::1", 11048),
147 ("2001:db8:66::1", 11048)),
148 "Wrong network info (IPv6)")
150 def testParserDisk(self):
151 """Test drbdsetup show parser for disk"""
152 data = self._ReadTestData("bdev-drbd-disk.txt")
153 result = bdev.DRBD8._GetDevInfo(data)
154 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
155 "/dev/xenvg/test.meta"),
156 "Wrong local disk info")
157 self.failUnless(("local_addr" not in result and
158 "remote_addr" not in result),
159 "Should not find network info")
161 def testBarriersOptions(self):
162 """Test class method that generates drbdsetup options for disk barriers"""
163 # Tests that should fail because of wrong version/options combinations
165 (8, 0, 12, "bfd", True),
166 (8, 0, 12, "fd", False),
167 (8, 0, 12, "b", True),
168 (8, 2, 7, "bfd", True),
172 for vmaj, vmin, vrel, opts, meta in should_fail:
173 self.assertRaises(errors.BlockDeviceError,
174 bdev.DRBD8._ComputeDiskBarrierArgs,
175 vmaj, vmin, vrel, opts, meta)
177 # get the valid options from the frozenset(frozenset()) in constants.
178 valid_options = [list(x)[0] for x in constants.DRBD_VALID_BARRIER_OPT]
180 # Versions that do not support anything
181 for vmaj, vmin, vrel in ((8, 0, 0), (8, 0, 11), (8, 2, 6)):
182 for opts in valid_options:
183 self.assertRaises(errors.BlockDeviceError,
184 bdev.DRBD8._ComputeDiskBarrierArgs,
185 vmaj, vmin, vrel, opts, True)
187 # Versions with partial support (testing only options that are supported)
189 (8, 0, 12, "n", False, []),
190 (8, 0, 12, "n", True, ["--no-md-flushes"]),
191 (8, 2, 7, "n", False, []),
192 (8, 2, 7, "fd", False, ["--no-disk-flushes", "--no-disk-drain"]),
193 (8, 0, 12, "n", True, ["--no-md-flushes"]),
196 # Versions that support everything
197 for vmaj, vmin, vrel in ((8, 3, 0), (8, 3, 12)):
198 tests.append((vmaj, vmin, vrel, "bfd", True,
199 ["--no-disk-barrier", "--no-disk-drain",
200 "--no-disk-flushes", "--no-md-flushes"]))
201 tests.append((vmaj, vmin, vrel, "n", False, []))
202 tests.append((vmaj, vmin, vrel, "b", True,
203 ["--no-disk-barrier", "--no-md-flushes"]))
204 tests.append((vmaj, vmin, vrel, "fd", False,
205 ["--no-disk-flushes", "--no-disk-drain"]))
206 tests.append((vmaj, vmin, vrel, "n", True, ["--no-md-flushes"]))
210 vmaj, vmin, vrel, disabled_barriers, disable_meta_flush, expected = test
212 bdev.DRBD8._ComputeDiskBarrierArgs(vmaj, vmin, vrel,
215 self.failUnless(set(args) == set(expected),
216 "For test %s, got wrong results %s" % (test, args))
218 # Unsupported or invalid versions
219 for vmaj, vmin, vrel in ((0, 7, 25), (9, 0, 0), (7, 0, 0), (8, 4, 0)):
220 self.assertRaises(errors.BlockDeviceError,
221 bdev.DRBD8._ComputeDiskBarrierArgs,
222 vmaj, vmin, vrel, "n", True)
225 for option in ("", "c", "whatever", "nbdfc", "nf"):
226 self.assertRaises(errors.BlockDeviceError,
227 bdev.DRBD8._ComputeDiskBarrierArgs,
228 8, 3, 11, option, True)
231 class TestDRBD8Status(testutils.GanetiTestCase):
232 """Testing case for DRBD8 /proc status"""
235 """Read in txt data"""
236 testutils.GanetiTestCase.setUp(self)
237 proc_data = self._TestDataFilename("proc_drbd8.txt")
238 proc80e_data = self._TestDataFilename("proc_drbd80-emptyline.txt")
239 proc83_data = self._TestDataFilename("proc_drbd83.txt")
240 proc83_sync_data = self._TestDataFilename("proc_drbd83_sync.txt")
241 proc83_sync_krnl_data = \
242 self._TestDataFilename("proc_drbd83_sync_krnl2.6.39.txt")
243 self.proc_data = bdev.DRBD8._GetProcData(filename=proc_data)
244 self.proc80e_data = bdev.DRBD8._GetProcData(filename=proc80e_data)
245 self.proc83_data = bdev.DRBD8._GetProcData(filename=proc83_data)
246 self.proc83_sync_data = bdev.DRBD8._GetProcData(filename=proc83_sync_data)
247 self.proc83_sync_krnl_data = \
248 bdev.DRBD8._GetProcData(filename=proc83_sync_krnl_data)
249 self.mass_data = bdev.DRBD8._MassageProcData(self.proc_data)
250 self.mass80e_data = bdev.DRBD8._MassageProcData(self.proc80e_data)
251 self.mass83_data = bdev.DRBD8._MassageProcData(self.proc83_data)
252 self.mass83_sync_data = bdev.DRBD8._MassageProcData(self.proc83_sync_data)
253 self.mass83_sync_krnl_data = \
254 bdev.DRBD8._MassageProcData(self.proc83_sync_krnl_data)
256 def testIOErrors(self):
257 """Test handling of errors while reading the proc file."""
258 temp_file = self._CreateTempFile()
260 self.failUnlessRaises(errors.BlockDeviceError,
261 bdev.DRBD8._GetProcData, filename=temp_file)
263 def testHelper(self):
264 """Test reading usermode_helper in /sys."""
265 sys_drbd_helper = self._TestDataFilename("sys_drbd_usermode_helper.txt")
266 drbd_helper = bdev.DRBD8.GetUsermodeHelper(filename=sys_drbd_helper)
267 self.failUnlessEqual(drbd_helper, "/bin/true")
269 def testHelperIOErrors(self):
270 """Test handling of errors while reading usermode_helper in /sys."""
271 temp_file = self._CreateTempFile()
273 self.failUnlessRaises(errors.BlockDeviceError,
274 bdev.DRBD8.GetUsermodeHelper, filename=temp_file)
276 def testMinorNotFound(self):
277 """Test not-found-minor in /proc"""
278 self.failUnless(9 not in self.mass_data)
279 self.failUnless(9 not in self.mass83_data)
280 self.failUnless(3 not in self.mass80e_data)
282 def testLineNotMatch(self):
283 """Test wrong line passed to DRBD8Status"""
284 self.assertRaises(errors.BlockDeviceError, bdev.DRBD8Status, "foo")
286 def testMinor0(self):
287 """Test connected, primary device"""
288 for data in [self.mass_data, self.mass83_data]:
289 stats = bdev.DRBD8Status(data[0])
290 self.failUnless(stats.is_in_use)
291 self.failUnless(stats.is_connected and stats.is_primary and
292 stats.peer_secondary and stats.is_disk_uptodate)
294 def testMinor1(self):
295 """Test connected, secondary device"""
296 for data in [self.mass_data, self.mass83_data]:
297 stats = bdev.DRBD8Status(data[1])
298 self.failUnless(stats.is_in_use)
299 self.failUnless(stats.is_connected and stats.is_secondary and
300 stats.peer_primary and stats.is_disk_uptodate)
302 def testMinor2(self):
303 """Test unconfigured device"""
304 for data in [self.mass_data, self.mass83_data, self.mass80e_data]:
305 stats = bdev.DRBD8Status(data[2])
306 self.failIf(stats.is_in_use)
308 def testMinor4(self):
309 """Test WFconn device"""
310 for data in [self.mass_data, self.mass83_data]:
311 stats = bdev.DRBD8Status(data[4])
312 self.failUnless(stats.is_in_use)
313 self.failUnless(stats.is_wfconn and stats.is_primary and
314 stats.rrole == 'Unknown' and
315 stats.is_disk_uptodate)
317 def testMinor6(self):
318 """Test diskless device"""
319 for data in [self.mass_data, self.mass83_data]:
320 stats = bdev.DRBD8Status(data[6])
321 self.failUnless(stats.is_in_use)
322 self.failUnless(stats.is_connected and stats.is_secondary and
323 stats.peer_primary and stats.is_diskless)
325 def testMinor8(self):
326 """Test standalone device"""
327 for data in [self.mass_data, self.mass83_data]:
328 stats = bdev.DRBD8Status(data[8])
329 self.failUnless(stats.is_in_use)
330 self.failUnless(stats.is_standalone and
331 stats.rrole == 'Unknown' and
332 stats.is_disk_uptodate)
334 def testDRBD83SyncFine(self):
335 stats = bdev.DRBD8Status(self.mass83_sync_data[3])
336 self.failUnless(stats.is_in_resync)
337 self.failUnless(stats.sync_percent is not None)
339 def testDRBD83SyncBroken(self):
340 stats = bdev.DRBD8Status(self.mass83_sync_krnl_data[3])
341 self.failUnless(stats.is_in_resync)
342 self.failUnless(stats.sync_percent is not None)
345 class TestRADOSBlockDevice(testutils.GanetiTestCase):
346 def test_ParseRbdShowmappedOutput(self):
347 volume_name = "abc9778-8e8ace5b.rbd.disk0"
349 ("0\trbd\te69f28e5-9817.rbd.disk0\t-\t/dev/rbd0\n"
350 "1\t/dev/rbd0\tabc9778-8e8ace5b.rbd.disk0\t-\t/dev/rbd16\n"
351 "line\twith\tfewer\tfields\n"
354 output_no_matches = \
355 ("0\trbd\te69f28e5-9817.rbd.disk0\t-\t/dev/rbd0\n"
356 "1\trbd\tabcdef01-9817.rbd.disk0\t-\t/dev/rbd10\n"
357 "2\trbd\tcdef0123-9817.rbd.disk0\t-\t/dev/rbd12\n"
358 "something\twith\tfewer\tfields"
360 output_extra_matches = \
361 ("0\t/dev/rbd0\tabc9778-8e8ace5b.rbd.disk0\t-\t/dev/rbd11\n"
362 "1\trbd\te69f28e5-9817.rbd.disk0\t-\t/dev/rbd0\n"
363 "2\t/dev/rbd0\tabc9778-8e8ace5b.rbd.disk0\t-\t/dev/rbd16\n"
364 "something\twith\tfewer\tfields"
367 parse_function = bdev.RADOSBlockDevice._ParseRbdShowmappedOutput
368 self.assertEqual(parse_function(output_ok, volume_name), "/dev/rbd16")
369 self.assertRaises(errors.BlockDeviceError, parse_function,
370 output_empty, volume_name)
371 self.assertEqual(parse_function(output_no_matches, volume_name), None)
372 self.assertRaises(errors.BlockDeviceError, parse_function,
373 output_extra_matches, volume_name)
376 class TestCheckFileStoragePath(unittest.TestCase):
377 def testNonAbsolute(self):
378 for i in ["", "tmp", "foo/bar/baz"]:
379 self.assertRaises(errors.FileStoragePathError,
380 bdev._CheckFileStoragePath, i, ["/tmp"])
382 self.assertRaises(errors.FileStoragePathError,
383 bdev._CheckFileStoragePath, "/tmp", ["tmp", "xyz"])
385 def testNoAllowed(self):
386 self.assertRaises(errors.FileStoragePathError,
387 bdev._CheckFileStoragePath, "/tmp", [])
389 def testNoAdditionalPathComponent(self):
390 self.assertRaises(errors.FileStoragePathError,
391 bdev._CheckFileStoragePath, "/tmp/foo", ["/tmp/foo"])
393 def testAllowed(self):
394 bdev._CheckFileStoragePath("/tmp/foo/a", ["/tmp/foo"])
395 bdev._CheckFileStoragePath("/tmp/foo/a/x", ["/tmp/foo"])
398 class TestLoadAllowedFileStoragePaths(testutils.GanetiTestCase):
399 def testDevNull(self):
400 self.assertEqual(bdev.LoadAllowedFileStoragePaths("/dev/null"), [])
402 def testNonExistantFile(self):
403 filename = "/tmp/this/file/does/not/exist"
404 assert not os.path.exists(filename)
405 self.assertEqual(bdev.LoadAllowedFileStoragePaths(filename), [])
408 tmpfile = self._CreateTempFile()
410 utils.WriteFile(tmpfile, data="""
411 # This is a test file
417 self.assertEqual(bdev.LoadAllowedFileStoragePaths(tmpfile), [
424 if __name__ == "__main__":
425 testutils.GanetiTestProgram()