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
35 class TestBaseDRBD(testutils.GanetiTestCase):
36 def testGetVersion(self):
38 ["version: 8.0.12 (api:76/proto:86-91)"],
39 ["version: 8.2.7 (api:88/proto:0-100)"],
40 ["version: 8.3.7.49 (api:188/proto:13-191)"],
68 for d,r in zip(data, result):
69 self.assertEqual(bdev.BaseDRBD._GetVersion(d), r)
72 class TestDRBD8Runner(testutils.GanetiTestCase):
73 """Testing case for DRBD8"""
76 def _has_disk(data, dname, mname):
77 """Check local disk corectness"""
79 "local_dev" in data and
80 data["local_dev"] == dname and
81 "meta_dev" in data and
82 data["meta_dev"] == mname and
83 "meta_index" in data and
84 data["meta_index"] == 0
89 def _has_net(data, local, remote):
90 """Check network connection parameters"""
92 "local_addr" in data and
93 data["local_addr"] == local and
94 "remote_addr" in data and
95 data["remote_addr"] == remote
99 def testParserCreation(self):
100 """Test drbdsetup show parser creation"""
101 bdev.DRBD8._GetShowParser()
103 def testParser80(self):
104 """Test drbdsetup show parser for disk and network version 8.0"""
105 data = self._ReadTestData("bdev-drbd-8.0.txt")
106 result = bdev.DRBD8._GetDevInfo(data)
107 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
108 "/dev/xenvg/test.meta"),
109 "Wrong local disk info")
110 self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
111 ("192.0.2.2", 11000)),
112 "Wrong network info (8.0.x)")
114 def testParser83(self):
115 """Test drbdsetup show parser for disk and network version 8.3"""
116 data = self._ReadTestData("bdev-drbd-8.3.txt")
117 result = bdev.DRBD8._GetDevInfo(data)
118 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
119 "/dev/xenvg/test.meta"),
120 "Wrong local disk info")
121 self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
122 ("192.0.2.2", 11000)),
123 "Wrong network info (8.0.x)")
125 def testParserNetIP4(self):
126 """Test drbdsetup show parser for IPv4 network"""
127 data = self._ReadTestData("bdev-drbd-net-ip4.txt")
128 result = bdev.DRBD8._GetDevInfo(data)
129 self.failUnless(("local_dev" not in result and
130 "meta_dev" not in result and
131 "meta_index" not in result),
132 "Should not find local disk info")
133 self.failUnless(self._has_net(result, ("192.0.2.1", 11002),
134 ("192.0.2.2", 11002)),
135 "Wrong network info (IPv4)")
137 def testParserNetIP6(self):
138 """Test drbdsetup show parser for IPv6 network"""
139 data = self._ReadTestData("bdev-drbd-net-ip6.txt")
140 result = bdev.DRBD8._GetDevInfo(data)
141 self.failUnless(("local_dev" not in result and
142 "meta_dev" not in result and
143 "meta_index" not in result),
144 "Should not find local disk info")
145 self.failUnless(self._has_net(result, ("2001:db8:65::1", 11048),
146 ("2001:db8:66::1", 11048)),
147 "Wrong network info (IPv6)")
149 def testParserDisk(self):
150 """Test drbdsetup show parser for disk"""
151 data = self._ReadTestData("bdev-drbd-disk.txt")
152 result = bdev.DRBD8._GetDevInfo(data)
153 self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
154 "/dev/xenvg/test.meta"),
155 "Wrong local disk info")
156 self.failUnless(("local_addr" not in result and
157 "remote_addr" not in result),
158 "Should not find network info")
160 def testBarriersOptions(self):
161 """Test class method that generates drbdsetup options for disk barriers"""
162 # Tests that should fail because of wrong version/options combinations
164 (8, 0, 12, "bfd", True),
165 (8, 0, 12, "fd", False),
166 (8, 0, 12, "b", True),
167 (8, 2, 7, "bfd", True),
171 for vmaj, vmin, vrel, opts, meta in should_fail:
172 self.assertRaises(errors.BlockDeviceError,
173 bdev.DRBD8._ComputeDiskBarrierArgs,
174 vmaj, vmin, vrel, opts, meta)
176 # get the valid options from the frozenset(frozenset()) in constants.
177 valid_options = [list(x)[0] for x in constants.DRBD_VALID_BARRIER_OPT]
179 # Versions that do not support anything
180 for vmaj, vmin, vrel in ((8, 0, 0), (8, 0, 11), (8, 2, 6)):
181 for opts in valid_options:
182 self.assertRaises(errors.BlockDeviceError,
183 bdev.DRBD8._ComputeDiskBarrierArgs,
184 vmaj, vmin, vrel, opts, True)
186 # Versions with partial support (testing only options that are supported)
188 (8, 0, 12, "n", False, []),
189 (8, 0, 12, "n", True, ["--no-md-flushes"]),
190 (8, 2, 7, "n", False, []),
191 (8, 2, 7, "fd", False, ["--no-disk-flushes", "--no-disk-drain"]),
192 (8, 0, 12, "n", True, ["--no-md-flushes"]),
195 # Versions that support everything
196 for vmaj, vmin, vrel in ((8, 3, 0), (8, 3, 12)):
197 tests.append((vmaj, vmin, vrel, "bfd", True,
198 ["--no-disk-barrier", "--no-disk-drain",
199 "--no-disk-flushes", "--no-md-flushes"]))
200 tests.append((vmaj, vmin, vrel, "n", False, []))
201 tests.append((vmaj, vmin, vrel, "b", True,
202 ["--no-disk-barrier", "--no-md-flushes"]))
203 tests.append((vmaj, vmin, vrel, "fd", False,
204 ["--no-disk-flushes", "--no-disk-drain"]))
205 tests.append((vmaj, vmin, vrel, "n", True, ["--no-md-flushes"]))
209 vmaj, vmin, vrel, disabled_barriers, disable_meta_flush, expected = test
211 bdev.DRBD8._ComputeDiskBarrierArgs(vmaj, vmin, vrel,
214 self.failUnless(set(args) == set(expected),
215 "For test %s, got wrong results %s" % (test, args))
217 # Unsupported or invalid versions
218 for vmaj, vmin, vrel in ((0, 7, 25), (9, 0, 0), (7, 0, 0), (8, 4, 0)):
219 self.assertRaises(errors.BlockDeviceError,
220 bdev.DRBD8._ComputeDiskBarrierArgs,
221 vmaj, vmin, vrel, "n", True)
224 for option in ("", "c", "whatever", "nbdfc", "nf"):
225 self.assertRaises(errors.BlockDeviceError,
226 bdev.DRBD8._ComputeDiskBarrierArgs,
227 8, 3, 11, option, True)
230 class TestDRBD8Status(testutils.GanetiTestCase):
231 """Testing case for DRBD8 /proc status"""
234 """Read in txt data"""
235 testutils.GanetiTestCase.setUp(self)
236 proc_data = self._TestDataFilename("proc_drbd8.txt")
237 proc80e_data = self._TestDataFilename("proc_drbd80-emptyline.txt")
238 proc83_data = self._TestDataFilename("proc_drbd83.txt")
239 proc83_sync_data = self._TestDataFilename("proc_drbd83_sync.txt")
240 proc83_sync_krnl_data = \
241 self._TestDataFilename("proc_drbd83_sync_krnl2.6.39.txt")
242 self.proc_data = bdev.DRBD8._GetProcData(filename=proc_data)
243 self.proc80e_data = bdev.DRBD8._GetProcData(filename=proc80e_data)
244 self.proc83_data = bdev.DRBD8._GetProcData(filename=proc83_data)
245 self.proc83_sync_data = bdev.DRBD8._GetProcData(filename=proc83_sync_data)
246 self.proc83_sync_krnl_data = \
247 bdev.DRBD8._GetProcData(filename=proc83_sync_krnl_data)
248 self.mass_data = bdev.DRBD8._MassageProcData(self.proc_data)
249 self.mass80e_data = bdev.DRBD8._MassageProcData(self.proc80e_data)
250 self.mass83_data = bdev.DRBD8._MassageProcData(self.proc83_data)
251 self.mass83_sync_data = bdev.DRBD8._MassageProcData(self.proc83_sync_data)
252 self.mass83_sync_krnl_data = \
253 bdev.DRBD8._MassageProcData(self.proc83_sync_krnl_data)
255 def testIOErrors(self):
256 """Test handling of errors while reading the proc file."""
257 temp_file = self._CreateTempFile()
259 self.failUnlessRaises(errors.BlockDeviceError,
260 bdev.DRBD8._GetProcData, filename=temp_file)
262 def testHelper(self):
263 """Test reading usermode_helper in /sys."""
264 sys_drbd_helper = self._TestDataFilename("sys_drbd_usermode_helper.txt")
265 drbd_helper = bdev.DRBD8.GetUsermodeHelper(filename=sys_drbd_helper)
266 self.failUnlessEqual(drbd_helper, "/bin/true")
268 def testHelperIOErrors(self):
269 """Test handling of errors while reading usermode_helper in /sys."""
270 temp_file = self._CreateTempFile()
272 self.failUnlessRaises(errors.BlockDeviceError,
273 bdev.DRBD8.GetUsermodeHelper, filename=temp_file)
275 def testMinorNotFound(self):
276 """Test not-found-minor in /proc"""
277 self.failUnless(9 not in self.mass_data)
278 self.failUnless(9 not in self.mass83_data)
279 self.failUnless(3 not in self.mass80e_data)
281 def testLineNotMatch(self):
282 """Test wrong line passed to DRBD8Status"""
283 self.assertRaises(errors.BlockDeviceError, bdev.DRBD8Status, "foo")
285 def testMinor0(self):
286 """Test connected, primary device"""
287 for data in [self.mass_data, self.mass83_data]:
288 stats = bdev.DRBD8Status(data[0])
289 self.failUnless(stats.is_in_use)
290 self.failUnless(stats.is_connected and stats.is_primary and
291 stats.peer_secondary and stats.is_disk_uptodate)
293 def testMinor1(self):
294 """Test connected, secondary device"""
295 for data in [self.mass_data, self.mass83_data]:
296 stats = bdev.DRBD8Status(data[1])
297 self.failUnless(stats.is_in_use)
298 self.failUnless(stats.is_connected and stats.is_secondary and
299 stats.peer_primary and stats.is_disk_uptodate)
301 def testMinor2(self):
302 """Test unconfigured device"""
303 for data in [self.mass_data, self.mass83_data, self.mass80e_data]:
304 stats = bdev.DRBD8Status(data[2])
305 self.failIf(stats.is_in_use)
307 def testMinor4(self):
308 """Test WFconn device"""
309 for data in [self.mass_data, self.mass83_data]:
310 stats = bdev.DRBD8Status(data[4])
311 self.failUnless(stats.is_in_use)
312 self.failUnless(stats.is_wfconn and stats.is_primary and
313 stats.rrole == 'Unknown' and
314 stats.is_disk_uptodate)
316 def testMinor6(self):
317 """Test diskless device"""
318 for data in [self.mass_data, self.mass83_data]:
319 stats = bdev.DRBD8Status(data[6])
320 self.failUnless(stats.is_in_use)
321 self.failUnless(stats.is_connected and stats.is_secondary and
322 stats.peer_primary and stats.is_diskless)
324 def testMinor8(self):
325 """Test standalone device"""
326 for data in [self.mass_data, self.mass83_data]:
327 stats = bdev.DRBD8Status(data[8])
328 self.failUnless(stats.is_in_use)
329 self.failUnless(stats.is_standalone and
330 stats.rrole == 'Unknown' and
331 stats.is_disk_uptodate)
333 def testDRBD83SyncFine(self):
334 stats = bdev.DRBD8Status(self.mass83_sync_data[3])
335 self.failUnless(stats.is_in_resync)
336 self.failUnless(stats.sync_percent is not None)
338 def testDRBD83SyncBroken(self):
339 stats = bdev.DRBD8Status(self.mass83_sync_krnl_data[3])
340 self.failUnless(stats.is_in_resync)
341 self.failUnless(stats.sync_percent is not None)
344 class TestRADOSBlockDevice(testutils.GanetiTestCase):
345 def test_ParseRbdShowmappedOutput(self):
346 volume_name = "abc9778-8e8ace5b.rbd.disk0"
348 ("0\trbd\te69f28e5-9817.rbd.disk0\t-\t/dev/rbd0\n"
349 "1\t/dev/rbd0\tabc9778-8e8ace5b.rbd.disk0\t-\t/dev/rbd16\n"
350 "line\twith\tfewer\tfields\n"
353 output_no_matches = \
354 ("0\trbd\te69f28e5-9817.rbd.disk0\t-\t/dev/rbd0\n"
355 "1\trbd\tabcdef01-9817.rbd.disk0\t-\t/dev/rbd10\n"
356 "2\trbd\tcdef0123-9817.rbd.disk0\t-\t/dev/rbd12\n"
357 "something\twith\tfewer\tfields"
359 output_extra_matches = \
360 ("0\t/dev/rbd0\tabc9778-8e8ace5b.rbd.disk0\t-\t/dev/rbd11\n"
361 "1\trbd\te69f28e5-9817.rbd.disk0\t-\t/dev/rbd0\n"
362 "2\t/dev/rbd0\tabc9778-8e8ace5b.rbd.disk0\t-\t/dev/rbd16\n"
363 "something\twith\tfewer\tfields"
366 parse_function = bdev.RADOSBlockDevice._ParseRbdShowmappedOutput
367 self.assertEqual(parse_function(output_ok, volume_name), "/dev/rbd16")
368 self.assertRaises(errors.BlockDeviceError, parse_function,
369 output_empty, volume_name)
370 self.assertEqual(parse_function(output_no_matches, volume_name), None)
371 self.assertRaises(errors.BlockDeviceError, parse_function,
372 output_extra_matches, volume_name)
375 if __name__ == '__main__':
376 testutils.GanetiTestProgram()