Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.block.drbd_unittest.py @ 47e0abee

History | View | Annotate | Download (16.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.block import drbd
30
from ganeti.block import drbd_info
31
from ganeti.block 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):
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" in data and
96
      data["meta_index"] == 0
97
      )
98
    return retval
99

    
100
  @staticmethod
101
  def _has_net(data, local, remote):
102
    """Check network connection parameters"""
103
    retval = (
104
      "local_addr" in data and
105
      data["local_addr"] == local and
106
      "remote_addr" in data and
107
      data["remote_addr"] == remote
108
      )
109
    return retval
110

    
111
  def testParser83Creation(self):
112
    """Test drbdsetup show parser creation"""
113
    drbd_info.DRBD83ShowInfo._GetShowParser()
114

    
115
  def testParser84Creation(self):
116
    """Test drbdsetup show parser creation"""
117
    drbd_info.DRBD84ShowInfo._GetShowParser()
118

    
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)")
129

    
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)")
140

    
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)")
151

    
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)")
163

    
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)")
175

    
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")
186

    
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
190
    should_fail = [
191
      (8, 0, 12, "bfd", True),
192
      (8, 0, 12, "fd", False),
193
      (8, 0, 12, "b", True),
194
      (8, 2, 7, "bfd", True),
195
      (8, 2, 7, "b", True)
196
    ]
197

    
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)
202

    
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]
205

    
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:
209
        self.assertRaises(
210
          errors.BlockDeviceError,
211
          drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
212
          vmaj, vmin, vrel, opts, True)
213

    
214
    # Versions with partial support (testing only options that are supported)
215
    tests = [
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"]),
221
      ]
222

    
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"]))
234

    
235
    # Test execution
236
    for test in tests:
237
      vmaj, vmin, vrel, disabled_barriers, disable_meta_flush, expected = test
238
      args = \
239
        drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs(
240
          vmaj, vmin, vrel,
241
          disabled_barriers,
242
          disable_meta_flush)
243
      self.failUnless(set(args) == set(expected),
244
                      "For test %s, got wrong results %s" % (test, args))
245

    
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)
251

    
252
    # Invalid options
253
    for option in ("", "c", "whatever", "nbdfc", "nf"):
254
      self.assertRaises(errors.BlockDeviceError,
255
                        drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
256
                        8, 3, 11, option, True)
257

    
258

    
259
class TestDRBD8Status(testutils.GanetiTestCase):
260
  """Testing case for DRBD8Dev /proc status"""
261

    
262
  def setUp(self):
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")
273

    
274
    self.proc80ev_data = \
275
      testutils.TestDataFilename("proc_drbd80-emptyversion.txt")
276

    
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)
287

    
288
  def testIOErrors(self):
289
    """Test handling of errors while reading the proc file."""
290
    temp_file = self._CreateTempFile()
291
    os.unlink(temp_file)
292
    self.failUnlessRaises(errors.BlockDeviceError,
293
                          drbd.DRBD8Info.CreateFromFile, filename=temp_file)
294

    
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")
300

    
301
  def testHelperIOErrors(self):
302
    """Test handling of errors while reading usermode_helper in /sys."""
303
    temp_file = self._CreateTempFile()
304
    os.unlink(temp_file)
305
    self.failUnlessRaises(errors.BlockDeviceError,
306
                          drbd.DRBD8.GetUsermodeHelper, filename=temp_file)
307

    
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))
313

    
314
  def testLineNotMatch(self):
315
    """Test wrong line passed to drbd_info.DRBD8Status"""
316
    self.assertRaises(errors.BlockDeviceError, drbd_info.DRBD8Status, "foo")
317

    
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)
325

    
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)
333

    
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)
340

    
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)
349

    
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)
357

    
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)
366

    
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)
371

    
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)
376

    
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)
381

    
382
  def testDRBDEmptyVersion(self):
383
    self.assertRaises(errors.BlockDeviceError,
384
                      drbd.DRBD8Info.CreateFromFile,
385
                      filename=self.proc80ev_data)
386

    
387

    
388
class TestDRBD8Construction(testutils.GanetiTestCase):
389
  def setUp(self):
390
    """Read in txt data"""
391
    testutils.GanetiTestCase.setUp(self)
392
    self.proc80_info = \
393
      drbd_info.DRBD8Info.CreateFromFile(
394
        filename=testutils.TestDataFilename("proc_drbd8.txt"))
395
    self.proc83_info = \
396
      drbd_info.DRBD8Info.CreateFromFile(
397
        filename=testutils.TestDataFilename("proc_drbd83.txt"))
398
    self.proc84_info = \
399
      drbd_info.DRBD8Info.CreateFromFile(
400
        filename=testutils.TestDataFilename("proc_drbd84.txt"))
401

    
402
    self.test_unique_id = ("hosta.com", 123, "host2.com", 123, 0, "secret")
403

    
404
  @testutils.patch_object(drbd.DRBD8, "GetProcInfo")
405
  def testConstructionWith80Data(self, mock_create_from_file):
406
    mock_create_from_file.return_value = self.proc80_info
407

    
408
    inst = drbd.DRBD8Dev(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))
411

    
412
  @testutils.patch_object(drbd.DRBD8, "GetProcInfo")
413
  def testConstructionWith83Data(self, mock_create_from_file):
414
    mock_create_from_file.return_value = self.proc83_info
415

    
416
    inst = drbd.DRBD8Dev(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))
419

    
420
  @testutils.patch_object(drbd.DRBD8, "GetProcInfo")
421
  def testConstructionWith84Data(self, mock_create_from_file):
422
    mock_create_from_file.return_value = self.proc84_info
423

    
424
    inst = drbd.DRBD8Dev(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))
427

    
428

    
429
if __name__ == "__main__":
430
  testutils.GanetiTestProgram()