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 drbd module"""
27 from ganeti import constants
28 from ganeti import errors
29 from ganeti.block import drbd
30 from ganeti.block import drbd_info
31 from ganeti.block import drbd_cmdgen
36 class TestDRBD8(testutils.GanetiTestCase):
37 def testGetVersion(self):
39 "version: 8.0.0 (api:76/proto:80)",
40 "version: 8.0.12 (api:76/proto:86-91)",
41 "version: 8.2.7 (api:88/proto:0-100)",
42 "version: 8.3.7.49 (api:188/proto:13-191)",
78 for d, r in zip(data, result):
79 info = drbd.DRBD8Info.CreateFromLines([d])
80 self.assertEqual(info.GetVersion(), r)
81 self.assertEqual(info.GetVersionString(), d.replace("version: ", ""))
84 class TestDRBD8Runner(testutils.GanetiTestCase):
85 """Testing case for drbd.DRBD8"""
88 def _has_disk(data, dname, mname):
89 """Check local disk corectness"""
91 "local_dev" in data and
92 data["local_dev"] == dname and
93 "meta_dev" in data and
94 data["meta_dev"] == mname and
95 "meta_index" in data and
96 data["meta_index"] == 0
101 def _has_net(data, local, remote):
102 """Check network connection parameters"""
104 "local_addr" in data and
105 data["local_addr"] == local and
106 "remote_addr" in data and
107 data["remote_addr"] == remote
111 def testParser83Creation(self):
112 """Test drbdsetup show parser creation"""
113 drbd_info.DRBD83ShowInfo._GetShowParser()
115 def testParser84Creation(self):
116 """Test drbdsetup show parser creation"""
117 drbd_info.DRBD84ShowInfo._GetShowParser()
119 def testParser80(self):
120 """Test drbdsetup show parser for disk and network version 8.0"""
121 data = testutils.ReadTestData("bdev-drbd-8.0.txt")
122 result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
123 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
124 "/dev/xenvg/test.meta"),
125 "Wrong local disk info")
126 self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
127 ("192.0.2.2", 11000)),
128 "Wrong network info (8.0.x)")
130 def testParser83(self):
131 """Test drbdsetup show parser for disk and network version 8.3"""
132 data = testutils.ReadTestData("bdev-drbd-8.3.txt")
133 result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
134 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
135 "/dev/xenvg/test.meta"),
136 "Wrong local disk info")
137 self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
138 ("192.0.2.2", 11000)),
139 "Wrong network info (8.3.x)")
141 def testParser84(self):
142 """Test drbdsetup show parser for disk and network version 8.4"""
143 data = testutils.ReadTestData("bdev-drbd-8.4.txt")
144 result = drbd_info.DRBD84ShowInfo.GetDevInfo(data)
145 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
146 "/dev/xenvg/test.meta"),
147 "Wrong local disk info")
148 self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
149 ("192.0.2.2", 11000)),
150 "Wrong network info (8.4.x)")
152 def testParserNetIP4(self):
153 """Test drbdsetup show parser for IPv4 network"""
154 data = testutils.ReadTestData("bdev-drbd-net-ip4.txt")
155 result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
156 self.failUnless(("local_dev" not in result and
157 "meta_dev" not in result and
158 "meta_index" not in result),
159 "Should not find local disk info")
160 self.failUnless(self._has_net(result, ("192.0.2.1", 11002),
161 ("192.0.2.2", 11002)),
162 "Wrong network info (IPv4)")
164 def testParserNetIP6(self):
165 """Test drbdsetup show parser for IPv6 network"""
166 data = testutils.ReadTestData("bdev-drbd-net-ip6.txt")
167 result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
168 self.failUnless(("local_dev" not in result and
169 "meta_dev" not in result and
170 "meta_index" not in result),
171 "Should not find local disk info")
172 self.failUnless(self._has_net(result, ("2001:db8:65::1", 11048),
173 ("2001:db8:66::1", 11048)),
174 "Wrong network info (IPv6)")
176 def testParserDisk(self):
177 """Test drbdsetup show parser for disk"""
178 data = testutils.ReadTestData("bdev-drbd-disk.txt")
179 result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
180 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
181 "/dev/xenvg/test.meta"),
182 "Wrong local disk info")
183 self.failUnless(("local_addr" not in result and
184 "remote_addr" not in result),
185 "Should not find network info")
187 def testBarriersOptions(self):
188 """Test class method that generates drbdsetup options for disk barriers"""
189 # Tests that should fail because of wrong version/options combinations
191 (8, 0, 12, "bfd", True),
192 (8, 0, 12, "fd", False),
193 (8, 0, 12, "b", True),
194 (8, 2, 7, "bfd", True),
198 for vmaj, vmin, vrel, opts, meta in should_fail:
199 self.assertRaises(errors.BlockDeviceError,
200 drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
201 vmaj, vmin, vrel, opts, meta)
203 # get the valid options from the frozenset(frozenset()) in constants.
204 valid_options = [list(x)[0] for x in constants.DRBD_VALID_BARRIER_OPT]
206 # Versions that do not support anything
207 for vmaj, vmin, vrel in ((8, 0, 0), (8, 0, 11), (8, 2, 6)):
208 for opts in valid_options:
210 errors.BlockDeviceError,
211 drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
212 vmaj, vmin, vrel, opts, True)
214 # Versions with partial support (testing only options that are supported)
216 (8, 0, 12, "n", False, []),
217 (8, 0, 12, "n", True, ["--no-md-flushes"]),
218 (8, 2, 7, "n", False, []),
219 (8, 2, 7, "fd", False, ["--no-disk-flushes", "--no-disk-drain"]),
220 (8, 0, 12, "n", True, ["--no-md-flushes"]),
223 # Versions that support everything
224 for vmaj, vmin, vrel in ((8, 3, 0), (8, 3, 12)):
225 tests.append((vmaj, vmin, vrel, "bfd", True,
226 ["--no-disk-barrier", "--no-disk-drain",
227 "--no-disk-flushes", "--no-md-flushes"]))
228 tests.append((vmaj, vmin, vrel, "n", False, []))
229 tests.append((vmaj, vmin, vrel, "b", True,
230 ["--no-disk-barrier", "--no-md-flushes"]))
231 tests.append((vmaj, vmin, vrel, "fd", False,
232 ["--no-disk-flushes", "--no-disk-drain"]))
233 tests.append((vmaj, vmin, vrel, "n", True, ["--no-md-flushes"]))
237 vmaj, vmin, vrel, disabled_barriers, disable_meta_flush, expected = test
239 drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs(
243 self.failUnless(set(args) == set(expected),
244 "For test %s, got wrong results %s" % (test, args))
246 # Unsupported or invalid versions
247 for vmaj, vmin, vrel in ((0, 7, 25), (9, 0, 0), (7, 0, 0), (8, 4, 0)):
248 self.assertRaises(errors.BlockDeviceError,
249 drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
250 vmaj, vmin, vrel, "n", True)
253 for option in ("", "c", "whatever", "nbdfc", "nf"):
254 self.assertRaises(errors.BlockDeviceError,
255 drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
256 8, 3, 11, option, True)
259 class TestDRBD8Status(testutils.GanetiTestCase):
260 """Testing case for DRBD8 /proc status"""
263 """Read in txt data"""
264 testutils.GanetiTestCase.setUp(self)
265 proc_data = testutils.TestDataFilename("proc_drbd8.txt")
266 proc80e_data = testutils.TestDataFilename("proc_drbd80-emptyline.txt")
267 proc83_data = testutils.TestDataFilename("proc_drbd83.txt")
268 proc83_sync_data = testutils.TestDataFilename("proc_drbd83_sync.txt")
269 proc83_sync_krnl_data = \
270 testutils.TestDataFilename("proc_drbd83_sync_krnl2.6.39.txt")
271 proc84_data = testutils.TestDataFilename("proc_drbd84.txt")
272 proc84_sync_data = testutils.TestDataFilename("proc_drbd84_sync.txt")
274 self.proc80ev_data = \
275 testutils.TestDataFilename("proc_drbd80-emptyversion.txt")
277 self.drbd_info = drbd.DRBD8Info.CreateFromFile(filename=proc_data)
278 self.drbd_info80e = drbd.DRBD8Info.CreateFromFile(filename=proc80e_data)
279 self.drbd_info83 = drbd.DRBD8Info.CreateFromFile(filename=proc83_data)
280 self.drbd_info83_sync = \
281 drbd.DRBD8Info.CreateFromFile(filename=proc83_sync_data)
282 self.drbd_info83_sync_krnl = \
283 drbd.DRBD8Info.CreateFromFile(filename=proc83_sync_krnl_data)
284 self.drbd_info84 = drbd.DRBD8Info.CreateFromFile(filename=proc84_data)
285 self.drbd_info84_sync = \
286 drbd.DRBD8Info.CreateFromFile(filename=proc84_sync_data)
288 def testIOErrors(self):
289 """Test handling of errors while reading the proc file."""
290 temp_file = self._CreateTempFile()
292 self.failUnlessRaises(errors.BlockDeviceError,
293 drbd.DRBD8Info.CreateFromFile, filename=temp_file)
295 def testHelper(self):
296 """Test reading usermode_helper in /sys."""
297 sys_drbd_helper = testutils.TestDataFilename("sys_drbd_usermode_helper.txt")
298 drbd_helper = drbd.DRBD8.GetUsermodeHelper(filename=sys_drbd_helper)
299 self.failUnlessEqual(drbd_helper, "/bin/true")
301 def testHelperIOErrors(self):
302 """Test handling of errors while reading usermode_helper in /sys."""
303 temp_file = self._CreateTempFile()
305 self.failUnlessRaises(errors.BlockDeviceError,
306 drbd.DRBD8.GetUsermodeHelper, filename=temp_file)
308 def testMinorNotFound(self):
309 """Test not-found-minor in /proc"""
310 self.failUnless(not self.drbd_info.HasMinorStatus(9))
311 self.failUnless(not self.drbd_info83.HasMinorStatus(9))
312 self.failUnless(not self.drbd_info80e.HasMinorStatus(3))
314 def testLineNotMatch(self):
315 """Test wrong line passed to drbd_info.DRBD8Status"""
316 self.assertRaises(errors.BlockDeviceError, drbd_info.DRBD8Status, "foo")
318 def testMinor0(self):
319 """Test connected, primary device"""
320 for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
321 stats = info.GetMinorStatus(0)
322 self.failUnless(stats.is_in_use)
323 self.failUnless(stats.is_connected and stats.is_primary and
324 stats.peer_secondary and stats.is_disk_uptodate)
326 def testMinor1(self):
327 """Test connected, secondary device"""
328 for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
329 stats = info.GetMinorStatus(1)
330 self.failUnless(stats.is_in_use)
331 self.failUnless(stats.is_connected and stats.is_secondary and
332 stats.peer_primary and stats.is_disk_uptodate)
334 def testMinor2(self):
335 """Test unconfigured device"""
336 for info in [self.drbd_info, self.drbd_info83,
337 self.drbd_info80e, self.drbd_info84]:
338 stats = info.GetMinorStatus(2)
339 self.failIf(stats.is_in_use)
341 def testMinor4(self):
342 """Test WFconn device"""
343 for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
344 stats = info.GetMinorStatus(4)
345 self.failUnless(stats.is_in_use)
346 self.failUnless(stats.is_wfconn and stats.is_primary and
347 stats.rrole == "Unknown" and
348 stats.is_disk_uptodate)
350 def testMinor6(self):
351 """Test diskless device"""
352 for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
353 stats = info.GetMinorStatus(6)
354 self.failUnless(stats.is_in_use)
355 self.failUnless(stats.is_connected and stats.is_secondary and
356 stats.peer_primary and stats.is_diskless)
358 def testMinor8(self):
359 """Test standalone device"""
360 for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
361 stats = info.GetMinorStatus(8)
362 self.failUnless(stats.is_in_use)
363 self.failUnless(stats.is_standalone and
364 stats.rrole == "Unknown" and
365 stats.is_disk_uptodate)
367 def testDRBD83SyncFine(self):
368 stats = self.drbd_info83_sync.GetMinorStatus(3)
369 self.failUnless(stats.is_in_resync)
370 self.assertAlmostEqual(stats.sync_percent, 34.9)
372 def testDRBD83SyncBroken(self):
373 stats = self.drbd_info83_sync_krnl.GetMinorStatus(3)
374 self.failUnless(stats.is_in_resync)
375 self.assertAlmostEqual(stats.sync_percent, 2.4)
377 def testDRBD84Sync(self):
378 stats = self.drbd_info84_sync.GetMinorStatus(5)
379 self.failUnless(stats.is_in_resync)
380 self.assertAlmostEqual(stats.sync_percent, 68.5)
382 def testDRBDEmptyVersion(self):
383 self.assertRaises(errors.BlockDeviceError,
384 drbd.DRBD8Info.CreateFromFile,
385 filename=self.proc80ev_data)
388 class TestDRBD8Construction(testutils.GanetiTestCase):
390 """Read in txt data"""
391 testutils.GanetiTestCase.setUp(self)
393 drbd_info.DRBD8Info.CreateFromFile(
394 filename=testutils.TestDataFilename("proc_drbd8.txt"))
396 drbd_info.DRBD8Info.CreateFromFile(
397 filename=testutils.TestDataFilename("proc_drbd83.txt"))
399 drbd_info.DRBD8Info.CreateFromFile(
400 filename=testutils.TestDataFilename("proc_drbd84.txt"))
402 self.test_unique_id = ("hosta.com", 123, "host2.com", 123, 0, "secret")
404 @testutils.patch_object(drbd_info.DRBD8Info, "CreateFromFile")
405 def testConstructionWith80Data(self, mock_create_from_file):
406 mock_create_from_file.return_value = self.proc80_info
408 inst = drbd.DRBD8(self.test_unique_id, [], 123, {})
409 self.assertEqual(inst._show_info_cls, drbd_info.DRBD83ShowInfo)
410 self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD83CmdGenerator))
412 @testutils.patch_object(drbd_info.DRBD8Info, "CreateFromFile")
413 def testConstructionWith83Data(self, mock_create_from_file):
414 mock_create_from_file.return_value = self.proc83_info
416 inst = drbd.DRBD8(self.test_unique_id, [], 123, {})
417 self.assertEqual(inst._show_info_cls, drbd_info.DRBD83ShowInfo)
418 self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD83CmdGenerator))
420 @testutils.patch_object(drbd_info.DRBD8Info, "CreateFromFile")
421 def testConstructionWith84Data(self, mock_create_from_file):
422 mock_create_from_file.return_value = self.proc84_info
424 inst = drbd.DRBD8(self.test_unique_id, [], 123, {})
425 self.assertEqual(inst._show_info_cls, drbd_info.DRBD84ShowInfo)
426 self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD84CmdGenerator))
429 if __name__ == "__main__":
430 testutils.GanetiTestProgram()