Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.storage.drbd_unittest.py @ 8520f4b9

History | View | Annotate | Download (16.9 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

    
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
426

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

    
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
434

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

    
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
442

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

    
447

    
448
if __name__ == "__main__":
449
  testutils.GanetiTestProgram()