root / test / py / ganeti.storage.drbd_unittest.py @ 3d835d1b
History | View | Annotate | Download (17.2 kB)
1 |
#!/usr/bin/python
|
---|---|
2 |
#
|
3 |
|
4 |
# Copyright (C) 2006, 2007, 2010, 2012, 2013 Google Inc.
|
5 |
#
|
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.
|
10 |
#
|
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.
|
15 |
#
|
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
|
19 |
# 02110-1301, USA.
|
20 |
|
21 |
|
22 |
"""Script for unittesting the drbd module"""
|
23 |
|
24 |
|
25 |
import os |
26 |
|
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 |
32 |
|
33 |
import testutils |
34 |
|
35 |
|
36 |
class TestDRBD8(testutils.GanetiTestCase): |
37 |
def testGetVersion(self): |
38 |
data = [ |
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)",
|
43 |
] |
44 |
result = [ |
45 |
{ |
46 |
"k_major": 8, |
47 |
"k_minor": 0, |
48 |
"k_point": 0, |
49 |
"api": 76, |
50 |
"proto": 80, |
51 |
}, |
52 |
{ |
53 |
"k_major": 8, |
54 |
"k_minor": 0, |
55 |
"k_point": 12, |
56 |
"api": 76, |
57 |
"proto": 86, |
58 |
"proto2": "91", |
59 |
}, |
60 |
{ |
61 |
"k_major": 8, |
62 |
"k_minor": 2, |
63 |
"k_point": 7, |
64 |
"api": 88, |
65 |
"proto": 0, |
66 |
"proto2": "100", |
67 |
}, |
68 |
{ |
69 |
"k_major": 8, |
70 |
"k_minor": 3, |
71 |
"k_point": 7, |
72 |
"k_fix": "49", |
73 |
"api": 188, |
74 |
"proto": 13, |
75 |
"proto2": "191", |
76 |
} |
77 |
] |
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: ", "")) |
82 |
|
83 |
|
84 |
class TestDRBD8Runner(testutils.GanetiTestCase): |
85 |
"""Testing case for drbd.DRBD8Dev"""
|
86 |
|
87 |
@staticmethod
|
88 |
def _has_disk(data, dname, mname, meta_index=0): |
89 |
"""Check local disk corectness"""
|
90 |
retval = ( |
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)
|
99 |
) |
100 |
) |
101 |
return retval
|
102 |
|
103 |
@staticmethod
|
104 |
def _has_net(data, local, remote): |
105 |
"""Check network connection parameters"""
|
106 |
retval = ( |
107 |
"local_addr" in data and |
108 |
data["local_addr"] == local and |
109 |
"remote_addr" in data and |
110 |
data["remote_addr"] == remote
|
111 |
) |
112 |
return retval
|
113 |
|
114 |
def testParser83Creation(self): |
115 |
"""Test drbdsetup show parser creation"""
|
116 |
drbd_info.DRBD83ShowInfo._GetShowParser() |
117 |
|
118 |
def testParser84Creation(self): |
119 |
"""Test drbdsetup show parser creation"""
|
120 |
drbd_info.DRBD84ShowInfo._GetShowParser() |
121 |
|
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)")
|
132 |
|
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)")
|
143 |
|
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)")
|
154 |
|
155 |
def testParser84NoDiskParams(self): |
156 |
"""Test drbdsetup show parser for 8.4 without disk params
|
157 |
|
158 |
The missing disk parameters occur after re-attaching a local disk but
|
159 |
before setting the disk params.
|
160 |
|
161 |
"""
|
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)")
|
170 |
|
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)")
|
182 |
|
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)")
|
194 |
|
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")
|
205 |
|
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
|
209 |
should_fail = [ |
210 |
(8, 0, 12, "bfd", True), |
211 |
(8, 0, 12, "fd", False), |
212 |
(8, 0, 12, "b", True), |
213 |
(8, 2, 7, "bfd", True), |
214 |
(8, 2, 7, "b", True) |
215 |
] |
216 |
|
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) |
221 |
|
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] |
224 |
|
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: |
228 |
self.assertRaises(
|
229 |
errors.BlockDeviceError, |
230 |
drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs, |
231 |
vmaj, vmin, vrel, opts, True)
|
232 |
|
233 |
# Versions with partial support (testing only options that are supported)
|
234 |
tests = [ |
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"]), |
240 |
] |
241 |
|
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"])) |
253 |
|
254 |
# Test execution
|
255 |
for test in tests: |
256 |
vmaj, vmin, vrel, disabled_barriers, disable_meta_flush, expected = test |
257 |
args = \ |
258 |
drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs( |
259 |
vmaj, vmin, vrel, |
260 |
disabled_barriers, |
261 |
disable_meta_flush) |
262 |
self.failUnless(set(args) == set(expected), |
263 |
"For test %s, got wrong results %s" % (test, args))
|
264 |
|
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) |
270 |
|
271 |
# Invalid options
|
272 |
for option in ("", "c", "whatever", "nbdfc", "nf"): |
273 |
self.assertRaises(errors.BlockDeviceError,
|
274 |
drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs, |
275 |
8, 3, 11, option, True) |
276 |
|
277 |
|
278 |
class TestDRBD8Status(testutils.GanetiTestCase): |
279 |
"""Testing case for DRBD8Dev /proc status"""
|
280 |
|
281 |
def setUp(self): |
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")
|
292 |
|
293 |
self.proc80ev_data = \
|
294 |
testutils.TestDataFilename("proc_drbd80-emptyversion.txt")
|
295 |
|
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) |
306 |
|
307 |
def testIOErrors(self): |
308 |
"""Test handling of errors while reading the proc file."""
|
309 |
temp_file = self._CreateTempFile()
|
310 |
os.unlink(temp_file) |
311 |
self.failUnlessRaises(errors.BlockDeviceError,
|
312 |
drbd.DRBD8Info.CreateFromFile, filename=temp_file) |
313 |
|
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") |
319 |
|
320 |
def testHelperIOErrors(self): |
321 |
"""Test handling of errors while reading usermode_helper in /sys."""
|
322 |
temp_file = self._CreateTempFile()
|
323 |
os.unlink(temp_file) |
324 |
self.failUnlessRaises(errors.BlockDeviceError,
|
325 |
drbd.DRBD8.GetUsermodeHelper, filename=temp_file) |
326 |
|
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)) |
332 |
|
333 |
def testLineNotMatch(self): |
334 |
"""Test wrong line passed to drbd_info.DRBD8Status"""
|
335 |
self.assertRaises(errors.BlockDeviceError, drbd_info.DRBD8Status, "foo") |
336 |
|
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)
|
344 |
|
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)
|
352 |
|
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)
|
359 |
|
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) |
368 |
|
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)
|
376 |
|
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) |
385 |
|
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) |
390 |
|
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) |
395 |
|
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) |
400 |
|
401 |
def testDRBDEmptyVersion(self): |
402 |
self.assertRaises(errors.BlockDeviceError,
|
403 |
drbd.DRBD8Info.CreateFromFile, |
404 |
filename=self.proc80ev_data)
|
405 |
|
406 |
|
407 |
class TestDRBD8Construction(testutils.GanetiTestCase): |
408 |
def setUp(self): |
409 |
"""Read in txt data"""
|
410 |
testutils.GanetiTestCase.setUp(self)
|
411 |
self.proc80_info = \
|
412 |
drbd_info.DRBD8Info.CreateFromFile( |
413 |
filename=testutils.TestDataFilename("proc_drbd8.txt"))
|
414 |
self.proc83_info = \
|
415 |
drbd_info.DRBD8Info.CreateFromFile( |
416 |
filename=testutils.TestDataFilename("proc_drbd83.txt"))
|
417 |
self.proc84_info = \
|
418 |
drbd_info.DRBD8Info.CreateFromFile( |
419 |
filename=testutils.TestDataFilename("proc_drbd84.txt"))
|
420 |
|
421 |
self.test_unique_id = ("hosta.com", 123, "host2.com", 123, 0, "secret") |
422 |
self.test_dyn_params = {
|
423 |
constants.DDP_LOCAL_IP: "192.0.2.1",
|
424 |
constants.DDP_LOCAL_MINOR: 0,
|
425 |
constants.DDP_REMOTE_IP: "192.0.2.2",
|
426 |
constants.DDP_REMOTE_MINOR: 0,
|
427 |
} |
428 |
|
429 |
@testutils.patch_object(drbd.DRBD8, "GetProcInfo") |
430 |
def testConstructionWith80Data(self, mock_create_from_file): |
431 |
mock_create_from_file.return_value = self.proc80_info
|
432 |
|
433 |
inst = drbd.DRBD8Dev(self.test_unique_id, [], 123, {}, self.test_dyn_params) |
434 |
self.assertEqual(inst._show_info_cls, drbd_info.DRBD83ShowInfo)
|
435 |
self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD83CmdGenerator)) |
436 |
|
437 |
@testutils.patch_object(drbd.DRBD8, "GetProcInfo") |
438 |
def testConstructionWith83Data(self, mock_create_from_file): |
439 |
mock_create_from_file.return_value = self.proc83_info
|
440 |
|
441 |
inst = drbd.DRBD8Dev(self.test_unique_id, [], 123, {}, self.test_dyn_params) |
442 |
self.assertEqual(inst._show_info_cls, drbd_info.DRBD83ShowInfo)
|
443 |
self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD83CmdGenerator)) |
444 |
|
445 |
@testutils.patch_object(drbd.DRBD8, "GetProcInfo") |
446 |
def testConstructionWith84Data(self, mock_create_from_file): |
447 |
mock_create_from_file.return_value = self.proc84_info
|
448 |
|
449 |
inst = drbd.DRBD8Dev(self.test_unique_id, [], 123, {}, self.test_dyn_params) |
450 |
self.assertEqual(inst._show_info_cls, drbd_info.DRBD84ShowInfo)
|
451 |
self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD84CmdGenerator)) |
452 |
|
453 |
|
454 |
if __name__ == "__main__": |
455 |
testutils.GanetiTestProgram() |