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.storage import drbd
30 from ganeti.storage import drbd_info
31 from ganeti.storage 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.DRBD8Dev"""
88 def _has_disk(data, dname, mname, meta_index=0):
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 is None and
96 "meta_index" not in data) or
97 ("meta_index" in data and
98 data["meta_index"] == meta_index)
104 def _has_net(data, local, remote):
105 """Check network connection parameters"""
107 "local_addr" in data and
108 data["local_addr"] == local and
109 "remote_addr" in data and
110 data["remote_addr"] == remote
114 def testParser83Creation(self):
115 """Test drbdsetup show parser creation"""
116 drbd_info.DRBD83ShowInfo._GetShowParser()
118 def testParser84Creation(self):
119 """Test drbdsetup show parser creation"""
120 drbd_info.DRBD84ShowInfo._GetShowParser()
122 def testParser80(self):
123 """Test drbdsetup show parser for disk and network version 8.0"""
124 data = testutils.ReadTestData("bdev-drbd-8.0.txt")
125 result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
126 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
127 "/dev/xenvg/test.meta"),
128 "Wrong local disk info")
129 self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
130 ("192.0.2.2", 11000)),
131 "Wrong network info (8.0.x)")
133 def testParser83(self):
134 """Test drbdsetup show parser for disk and network version 8.3"""
135 data = testutils.ReadTestData("bdev-drbd-8.3.txt")
136 result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
137 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
138 "/dev/xenvg/test.meta"),
139 "Wrong local disk info")
140 self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
141 ("192.0.2.2", 11000)),
142 "Wrong network info (8.3.x)")
144 def testParser84(self):
145 """Test drbdsetup show parser for disk and network version 8.4"""
146 data = testutils.ReadTestData("bdev-drbd-8.4.txt")
147 result = drbd_info.DRBD84ShowInfo.GetDevInfo(data)
148 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
149 "/dev/xenvg/test.meta"),
150 "Wrong local disk info")
151 self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
152 ("192.0.2.2", 11000)),
153 "Wrong network info (8.4.x)")
155 def testParser84NoDiskParams(self):
156 """Test drbdsetup show parser for 8.4 without disk params
158 The missing disk parameters occur after re-attaching a local disk but
159 before setting the disk params.
162 data = testutils.ReadTestData("bdev-drbd-8.4-no-disk-params.txt")
163 result = drbd_info.DRBD84ShowInfo.GetDevInfo(data)
164 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
165 "/dev/xenvg/test.meta", meta_index=None),
166 "Wrong local disk info")
167 self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
168 ("192.0.2.2", 11000)),
169 "Wrong network info (8.4.x)")
171 def testParserNetIP4(self):
172 """Test drbdsetup show parser for IPv4 network"""
173 data = testutils.ReadTestData("bdev-drbd-net-ip4.txt")
174 result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
175 self.failUnless(("local_dev" not in result and
176 "meta_dev" not in result and
177 "meta_index" not in result),
178 "Should not find local disk info")
179 self.failUnless(self._has_net(result, ("192.0.2.1", 11002),
180 ("192.0.2.2", 11002)),
181 "Wrong network info (IPv4)")
183 def testParserNetIP6(self):
184 """Test drbdsetup show parser for IPv6 network"""
185 data = testutils.ReadTestData("bdev-drbd-net-ip6.txt")
186 result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
187 self.failUnless(("local_dev" not in result and
188 "meta_dev" not in result and
189 "meta_index" not in result),
190 "Should not find local disk info")
191 self.failUnless(self._has_net(result, ("2001:db8:65::1", 11048),
192 ("2001:db8:66::1", 11048)),
193 "Wrong network info (IPv6)")
195 def testParserDisk(self):
196 """Test drbdsetup show parser for disk"""
197 data = testutils.ReadTestData("bdev-drbd-disk.txt")
198 result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
199 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
200 "/dev/xenvg/test.meta"),
201 "Wrong local disk info")
202 self.failUnless(("local_addr" not in result and
203 "remote_addr" not in result),
204 "Should not find network info")
206 def testBarriersOptions(self):
207 """Test class method that generates drbdsetup options for disk barriers"""
208 # Tests that should fail because of wrong version/options combinations
210 (8, 0, 12, "bfd", True),
211 (8, 0, 12, "fd", False),
212 (8, 0, 12, "b", True),
213 (8, 2, 7, "bfd", True),
217 for vmaj, vmin, vrel, opts, meta in should_fail:
218 self.assertRaises(errors.BlockDeviceError,
219 drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
220 vmaj, vmin, vrel, opts, meta)
222 # get the valid options from the frozenset(frozenset()) in constants.
223 valid_options = [list(x)[0] for x in constants.DRBD_VALID_BARRIER_OPT]
225 # Versions that do not support anything
226 for vmaj, vmin, vrel in ((8, 0, 0), (8, 0, 11), (8, 2, 6)):
227 for opts in valid_options:
229 errors.BlockDeviceError,
230 drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
231 vmaj, vmin, vrel, opts, True)
233 # Versions with partial support (testing only options that are supported)
235 (8, 0, 12, "n", False, []),
236 (8, 0, 12, "n", True, ["--no-md-flushes"]),
237 (8, 2, 7, "n", False, []),
238 (8, 2, 7, "fd", False, ["--no-disk-flushes", "--no-disk-drain"]),
239 (8, 0, 12, "n", True, ["--no-md-flushes"]),
242 # Versions that support everything
243 for vmaj, vmin, vrel in ((8, 3, 0), (8, 3, 12)):
244 tests.append((vmaj, vmin, vrel, "bfd", True,
245 ["--no-disk-barrier", "--no-disk-drain",
246 "--no-disk-flushes", "--no-md-flushes"]))
247 tests.append((vmaj, vmin, vrel, "n", False, []))
248 tests.append((vmaj, vmin, vrel, "b", True,
249 ["--no-disk-barrier", "--no-md-flushes"]))
250 tests.append((vmaj, vmin, vrel, "fd", False,
251 ["--no-disk-flushes", "--no-disk-drain"]))
252 tests.append((vmaj, vmin, vrel, "n", True, ["--no-md-flushes"]))
256 vmaj, vmin, vrel, disabled_barriers, disable_meta_flush, expected = test
258 drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs(
262 self.failUnless(set(args) == set(expected),
263 "For test %s, got wrong results %s" % (test, args))
265 # Unsupported or invalid versions
266 for vmaj, vmin, vrel in ((0, 7, 25), (9, 0, 0), (7, 0, 0), (8, 4, 0)):
267 self.assertRaises(errors.BlockDeviceError,
268 drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
269 vmaj, vmin, vrel, "n", True)
272 for option in ("", "c", "whatever", "nbdfc", "nf"):
273 self.assertRaises(errors.BlockDeviceError,
274 drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
275 8, 3, 11, option, True)
278 class TestDRBD8Status(testutils.GanetiTestCase):
279 """Testing case for DRBD8Dev /proc status"""
282 """Read in txt data"""
283 testutils.GanetiTestCase.setUp(self)
284 proc_data = testutils.TestDataFilename("proc_drbd8.txt")
285 proc80e_data = testutils.TestDataFilename("proc_drbd80-emptyline.txt")
286 proc83_data = testutils.TestDataFilename("proc_drbd83.txt")
287 proc83_sync_data = testutils.TestDataFilename("proc_drbd83_sync.txt")
288 proc83_sync_krnl_data = \
289 testutils.TestDataFilename("proc_drbd83_sync_krnl2.6.39.txt")
290 proc84_data = testutils.TestDataFilename("proc_drbd84.txt")
291 proc84_sync_data = testutils.TestDataFilename("proc_drbd84_sync.txt")
293 self.proc80ev_data = \
294 testutils.TestDataFilename("proc_drbd80-emptyversion.txt")
296 self.drbd_info = drbd.DRBD8Info.CreateFromFile(filename=proc_data)
297 self.drbd_info80e = drbd.DRBD8Info.CreateFromFile(filename=proc80e_data)
298 self.drbd_info83 = drbd.DRBD8Info.CreateFromFile(filename=proc83_data)
299 self.drbd_info83_sync = \
300 drbd.DRBD8Info.CreateFromFile(filename=proc83_sync_data)
301 self.drbd_info83_sync_krnl = \
302 drbd.DRBD8Info.CreateFromFile(filename=proc83_sync_krnl_data)
303 self.drbd_info84 = drbd.DRBD8Info.CreateFromFile(filename=proc84_data)
304 self.drbd_info84_sync = \
305 drbd.DRBD8Info.CreateFromFile(filename=proc84_sync_data)
307 def testIOErrors(self):
308 """Test handling of errors while reading the proc file."""
309 temp_file = self._CreateTempFile()
311 self.failUnlessRaises(errors.BlockDeviceError,
312 drbd.DRBD8Info.CreateFromFile, filename=temp_file)
314 def testHelper(self):
315 """Test reading usermode_helper in /sys."""
316 sys_drbd_helper = testutils.TestDataFilename("sys_drbd_usermode_helper.txt")
317 drbd_helper = drbd.DRBD8.GetUsermodeHelper(filename=sys_drbd_helper)
318 self.failUnlessEqual(drbd_helper, "/bin/true")
320 def testHelperIOErrors(self):
321 """Test handling of errors while reading usermode_helper in /sys."""
322 temp_file = self._CreateTempFile()
324 self.failUnlessRaises(errors.BlockDeviceError,
325 drbd.DRBD8.GetUsermodeHelper, filename=temp_file)
327 def testMinorNotFound(self):
328 """Test not-found-minor in /proc"""
329 self.failUnless(not self.drbd_info.HasMinorStatus(9))
330 self.failUnless(not self.drbd_info83.HasMinorStatus(9))
331 self.failUnless(not self.drbd_info80e.HasMinorStatus(3))
333 def testLineNotMatch(self):
334 """Test wrong line passed to drbd_info.DRBD8Status"""
335 self.assertRaises(errors.BlockDeviceError, drbd_info.DRBD8Status, "foo")
337 def testMinor0(self):
338 """Test connected, primary device"""
339 for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
340 stats = info.GetMinorStatus(0)
341 self.failUnless(stats.is_in_use)
342 self.failUnless(stats.is_connected and stats.is_primary and
343 stats.peer_secondary and stats.is_disk_uptodate)
345 def testMinor1(self):
346 """Test connected, secondary device"""
347 for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
348 stats = info.GetMinorStatus(1)
349 self.failUnless(stats.is_in_use)
350 self.failUnless(stats.is_connected and stats.is_secondary and
351 stats.peer_primary and stats.is_disk_uptodate)
353 def testMinor2(self):
354 """Test unconfigured device"""
355 for info in [self.drbd_info, self.drbd_info83,
356 self.drbd_info80e, self.drbd_info84]:
357 stats = info.GetMinorStatus(2)
358 self.failIf(stats.is_in_use)
360 def testMinor4(self):
361 """Test WFconn device"""
362 for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
363 stats = info.GetMinorStatus(4)
364 self.failUnless(stats.is_in_use)
365 self.failUnless(stats.is_wfconn and stats.is_primary and
366 stats.rrole == "Unknown" and
367 stats.is_disk_uptodate)
369 def testMinor6(self):
370 """Test diskless device"""
371 for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
372 stats = info.GetMinorStatus(6)
373 self.failUnless(stats.is_in_use)
374 self.failUnless(stats.is_connected and stats.is_secondary and
375 stats.peer_primary and stats.is_diskless)
377 def testMinor8(self):
378 """Test standalone device"""
379 for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
380 stats = info.GetMinorStatus(8)
381 self.failUnless(stats.is_in_use)
382 self.failUnless(stats.is_standalone and
383 stats.rrole == "Unknown" and
384 stats.is_disk_uptodate)
386 def testDRBD83SyncFine(self):
387 stats = self.drbd_info83_sync.GetMinorStatus(3)
388 self.failUnless(stats.is_in_resync)
389 self.assertAlmostEqual(stats.sync_percent, 34.9)
391 def testDRBD83SyncBroken(self):
392 stats = self.drbd_info83_sync_krnl.GetMinorStatus(3)
393 self.failUnless(stats.is_in_resync)
394 self.assertAlmostEqual(stats.sync_percent, 2.4)
396 def testDRBD84Sync(self):
397 stats = self.drbd_info84_sync.GetMinorStatus(5)
398 self.failUnless(stats.is_in_resync)
399 self.assertAlmostEqual(stats.sync_percent, 68.5)
401 def testDRBDEmptyVersion(self):
402 self.assertRaises(errors.BlockDeviceError,
403 drbd.DRBD8Info.CreateFromFile,
404 filename=self.proc80ev_data)
407 class TestDRBD8Construction(testutils.GanetiTestCase):
409 """Read in txt data"""
410 testutils.GanetiTestCase.setUp(self)
412 drbd_info.DRBD8Info.CreateFromFile(
413 filename=testutils.TestDataFilename("proc_drbd8.txt"))
415 drbd_info.DRBD8Info.CreateFromFile(
416 filename=testutils.TestDataFilename("proc_drbd83.txt"))
418 drbd_info.DRBD8Info.CreateFromFile(
419 filename=testutils.TestDataFilename("proc_drbd84.txt"))
421 self.test_unique_id = ("hosta.com", 123, "host2.com", 123, 0, "secret")
423 @testutils.patch_object(drbd.DRBD8, "GetProcInfo")
424 def testConstructionWith80Data(self, mock_create_from_file):
425 mock_create_from_file.return_value = self.proc80_info
427 inst = drbd.DRBD8Dev(self.test_unique_id, [], 123, {})
428 self.assertEqual(inst._show_info_cls, drbd_info.DRBD83ShowInfo)
429 self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD83CmdGenerator))
431 @testutils.patch_object(drbd.DRBD8, "GetProcInfo")
432 def testConstructionWith83Data(self, mock_create_from_file):
433 mock_create_from_file.return_value = self.proc83_info
435 inst = drbd.DRBD8Dev(self.test_unique_id, [], 123, {})
436 self.assertEqual(inst._show_info_cls, drbd_info.DRBD83ShowInfo)
437 self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD83CmdGenerator))
439 @testutils.patch_object(drbd.DRBD8, "GetProcInfo")
440 def testConstructionWith84Data(self, mock_create_from_file):
441 mock_create_from_file.return_value = self.proc84_info
443 inst = drbd.DRBD8Dev(self.test_unique_id, [], 123, {})
444 self.assertEqual(inst._show_info_cls, drbd_info.DRBD84ShowInfo)
445 self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD84CmdGenerator))
448 if __name__ == "__main__":
449 testutils.GanetiTestProgram()