Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.block.drbd_unittest.py @ 74db37c0

History | View | Annotate | Download (15.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.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.12 (api:76/proto:86-91)"],
40
      ["version: 8.2.7 (api:88/proto:0-100)"],
41
      ["version: 8.3.7.49 (api:188/proto:13-191)"],
42
    ]
43
    result = [
44
      {
45
        "k_major": 8,
46
        "k_minor": 0,
47
        "k_point": 12,
48
        "api": 76,
49
        "proto": 86,
50
        "proto2": "91",
51
      },
52
      {
53
        "k_major": 8,
54
        "k_minor": 2,
55
        "k_point": 7,
56
        "api": 88,
57
        "proto": 0,
58
        "proto2": "100",
59
      },
60
      {
61
        "k_major": 8,
62
        "k_minor": 3,
63
        "k_point": 7,
64
        "api": 188,
65
        "proto": 13,
66
        "proto2": "191",
67
      }
68
    ]
69
    for d, r in zip(data, result):
70
      info = drbd.DRBD8Info.CreateFromLines(d)
71
      self.assertEqual(info.GetVersion(), r)
72

    
73

    
74
class TestDRBD8Runner(testutils.GanetiTestCase):
75
  """Testing case for drbd.DRBD8"""
76

    
77
  @staticmethod
78
  def _has_disk(data, dname, mname):
79
    """Check local disk corectness"""
80
    retval = (
81
      "local_dev" in data and
82
      data["local_dev"] == dname and
83
      "meta_dev" in data and
84
      data["meta_dev"] == mname and
85
      "meta_index" in data and
86
      data["meta_index"] == 0
87
      )
88
    return retval
89

    
90
  @staticmethod
91
  def _has_net(data, local, remote):
92
    """Check network connection parameters"""
93
    retval = (
94
      "local_addr" in data and
95
      data["local_addr"] == local and
96
      "remote_addr" in data and
97
      data["remote_addr"] == remote
98
      )
99
    return retval
100

    
101
  def testParser83Creation(self):
102
    """Test drbdsetup show parser creation"""
103
    drbd_info.DRBD83ShowInfo._GetShowParser()
104

    
105
  def testParser84Creation(self):
106
    """Test drbdsetup show parser creation"""
107
    drbd_info.DRBD84ShowInfo._GetShowParser()
108

    
109
  def testParser80(self):
110
    """Test drbdsetup show parser for disk and network version 8.0"""
111
    data = testutils.ReadTestData("bdev-drbd-8.0.txt")
112
    result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
113
    self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
114
                                   "/dev/xenvg/test.meta"),
115
                    "Wrong local disk info")
116
    self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
117
                                  ("192.0.2.2", 11000)),
118
                    "Wrong network info (8.0.x)")
119

    
120
  def testParser83(self):
121
    """Test drbdsetup show parser for disk and network version 8.3"""
122
    data = testutils.ReadTestData("bdev-drbd-8.3.txt")
123
    result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
124
    self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
125
                                   "/dev/xenvg/test.meta"),
126
                    "Wrong local disk info")
127
    self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
128
                                  ("192.0.2.2", 11000)),
129
                    "Wrong network info (8.3.x)")
130

    
131
  def testParser84(self):
132
    """Test drbdsetup show parser for disk and network version 8.4"""
133
    data = testutils.ReadTestData("bdev-drbd-8.4.txt")
134
    result = drbd_info.DRBD84ShowInfo.GetDevInfo(data)
135
    self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
136
                                   "/dev/xenvg/test.meta"),
137
                    "Wrong local disk info")
138
    self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
139
                                  ("192.0.2.2", 11000)),
140
                    "Wrong network info (8.4.x)")
141

    
142
  def testParserNetIP4(self):
143
    """Test drbdsetup show parser for IPv4 network"""
144
    data = testutils.ReadTestData("bdev-drbd-net-ip4.txt")
145
    result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
146
    self.failUnless(("local_dev" not in result and
147
                     "meta_dev" not in result and
148
                     "meta_index" not in result),
149
                    "Should not find local disk info")
150
    self.failUnless(self._has_net(result, ("192.0.2.1", 11002),
151
                                  ("192.0.2.2", 11002)),
152
                    "Wrong network info (IPv4)")
153

    
154
  def testParserNetIP6(self):
155
    """Test drbdsetup show parser for IPv6 network"""
156
    data = testutils.ReadTestData("bdev-drbd-net-ip6.txt")
157
    result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
158
    self.failUnless(("local_dev" not in result and
159
                     "meta_dev" not in result and
160
                     "meta_index" not in result),
161
                    "Should not find local disk info")
162
    self.failUnless(self._has_net(result, ("2001:db8:65::1", 11048),
163
                                  ("2001:db8:66::1", 11048)),
164
                    "Wrong network info (IPv6)")
165

    
166
  def testParserDisk(self):
167
    """Test drbdsetup show parser for disk"""
168
    data = testutils.ReadTestData("bdev-drbd-disk.txt")
169
    result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
170
    self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
171
                                   "/dev/xenvg/test.meta"),
172
                    "Wrong local disk info")
173
    self.failUnless(("local_addr" not in result and
174
                     "remote_addr" not in result),
175
                    "Should not find network info")
176

    
177
  def testBarriersOptions(self):
178
    """Test class method that generates drbdsetup options for disk barriers"""
179
    # Tests that should fail because of wrong version/options combinations
180
    should_fail = [
181
      (8, 0, 12, "bfd", True),
182
      (8, 0, 12, "fd", False),
183
      (8, 0, 12, "b", True),
184
      (8, 2, 7, "bfd", True),
185
      (8, 2, 7, "b", True)
186
    ]
187

    
188
    for vmaj, vmin, vrel, opts, meta in should_fail:
189
      self.assertRaises(errors.BlockDeviceError,
190
                        drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
191
                        vmaj, vmin, vrel, opts, meta)
192

    
193
    # get the valid options from the frozenset(frozenset()) in constants.
194
    valid_options = [list(x)[0] for x in constants.DRBD_VALID_BARRIER_OPT]
195

    
196
    # Versions that do not support anything
197
    for vmaj, vmin, vrel in ((8, 0, 0), (8, 0, 11), (8, 2, 6)):
198
      for opts in valid_options:
199
        self.assertRaises(
200
          errors.BlockDeviceError,
201
          drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
202
          vmaj, vmin, vrel, opts, True)
203

    
204
    # Versions with partial support (testing only options that are supported)
205
    tests = [
206
      (8, 0, 12, "n", False, []),
207
      (8, 0, 12, "n", True, ["--no-md-flushes"]),
208
      (8, 2, 7, "n", False, []),
209
      (8, 2, 7, "fd", False, ["--no-disk-flushes", "--no-disk-drain"]),
210
      (8, 0, 12, "n", True, ["--no-md-flushes"]),
211
      ]
212

    
213
    # Versions that support everything
214
    for vmaj, vmin, vrel in ((8, 3, 0), (8, 3, 12)):
215
      tests.append((vmaj, vmin, vrel, "bfd", True,
216
                    ["--no-disk-barrier", "--no-disk-drain",
217
                     "--no-disk-flushes", "--no-md-flushes"]))
218
      tests.append((vmaj, vmin, vrel, "n", False, []))
219
      tests.append((vmaj, vmin, vrel, "b", True,
220
                    ["--no-disk-barrier", "--no-md-flushes"]))
221
      tests.append((vmaj, vmin, vrel, "fd", False,
222
                    ["--no-disk-flushes", "--no-disk-drain"]))
223
      tests.append((vmaj, vmin, vrel, "n", True, ["--no-md-flushes"]))
224

    
225
    # Test execution
226
    for test in tests:
227
      vmaj, vmin, vrel, disabled_barriers, disable_meta_flush, expected = test
228
      args = \
229
        drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs(
230
          vmaj, vmin, vrel,
231
          disabled_barriers,
232
          disable_meta_flush)
233
      self.failUnless(set(args) == set(expected),
234
                      "For test %s, got wrong results %s" % (test, args))
235

    
236
    # Unsupported or invalid versions
237
    for vmaj, vmin, vrel in ((0, 7, 25), (9, 0, 0), (7, 0, 0), (8, 4, 0)):
238
      self.assertRaises(errors.BlockDeviceError,
239
                        drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
240
                        vmaj, vmin, vrel, "n", True)
241

    
242
    # Invalid options
243
    for option in ("", "c", "whatever", "nbdfc", "nf"):
244
      self.assertRaises(errors.BlockDeviceError,
245
                        drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
246
                        8, 3, 11, option, True)
247

    
248

    
249
class TestDRBD8Status(testutils.GanetiTestCase):
250
  """Testing case for DRBD8 /proc status"""
251

    
252
  def setUp(self):
253
    """Read in txt data"""
254
    testutils.GanetiTestCase.setUp(self)
255
    proc_data = testutils.TestDataFilename("proc_drbd8.txt")
256
    proc80e_data = testutils.TestDataFilename("proc_drbd80-emptyline.txt")
257
    proc83_data = testutils.TestDataFilename("proc_drbd83.txt")
258
    proc83_sync_data = testutils.TestDataFilename("proc_drbd83_sync.txt")
259
    proc83_sync_krnl_data = \
260
      testutils.TestDataFilename("proc_drbd83_sync_krnl2.6.39.txt")
261
    proc84_data = testutils.TestDataFilename("proc_drbd84.txt")
262
    proc84_sync_data = testutils.TestDataFilename("proc_drbd84_sync.txt")
263

    
264
    self.proc80ev_data = \
265
      testutils.TestDataFilename("proc_drbd80-emptyversion.txt")
266

    
267
    self.drbd_info = drbd.DRBD8Info.CreateFromFile(filename=proc_data)
268
    self.drbd_info80e = drbd.DRBD8Info.CreateFromFile(filename=proc80e_data)
269
    self.drbd_info83 = drbd.DRBD8Info.CreateFromFile(filename=proc83_data)
270
    self.drbd_info83_sync = \
271
      drbd.DRBD8Info.CreateFromFile(filename=proc83_sync_data)
272
    self.drbd_info83_sync_krnl = \
273
      drbd.DRBD8Info.CreateFromFile(filename=proc83_sync_krnl_data)
274
    self.drbd_info84 = drbd.DRBD8Info.CreateFromFile(filename=proc84_data)
275
    self.drbd_info84_sync = \
276
      drbd.DRBD8Info.CreateFromFile(filename=proc84_sync_data)
277

    
278
  def testIOErrors(self):
279
    """Test handling of errors while reading the proc file."""
280
    temp_file = self._CreateTempFile()
281
    os.unlink(temp_file)
282
    self.failUnlessRaises(errors.BlockDeviceError,
283
                          drbd.DRBD8Info.CreateFromFile, filename=temp_file)
284

    
285
  def testHelper(self):
286
    """Test reading usermode_helper in /sys."""
287
    sys_drbd_helper = testutils.TestDataFilename("sys_drbd_usermode_helper.txt")
288
    drbd_helper = drbd.DRBD8.GetUsermodeHelper(filename=sys_drbd_helper)
289
    self.failUnlessEqual(drbd_helper, "/bin/true")
290

    
291
  def testHelperIOErrors(self):
292
    """Test handling of errors while reading usermode_helper in /sys."""
293
    temp_file = self._CreateTempFile()
294
    os.unlink(temp_file)
295
    self.failUnlessRaises(errors.BlockDeviceError,
296
                          drbd.DRBD8.GetUsermodeHelper, filename=temp_file)
297

    
298
  def testMinorNotFound(self):
299
    """Test not-found-minor in /proc"""
300
    self.failUnless(not self.drbd_info.HasMinorStatus(9))
301
    self.failUnless(not self.drbd_info83.HasMinorStatus(9))
302
    self.failUnless(not self.drbd_info80e.HasMinorStatus(3))
303

    
304
  def testLineNotMatch(self):
305
    """Test wrong line passed to drbd_info.DRBD8Status"""
306
    self.assertRaises(errors.BlockDeviceError, drbd_info.DRBD8Status, "foo")
307

    
308
  def testMinor0(self):
309
    """Test connected, primary device"""
310
    for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
311
      stats = info.GetMinorStatus(0)
312
      self.failUnless(stats.is_in_use)
313
      self.failUnless(stats.is_connected and stats.is_primary and
314
                      stats.peer_secondary and stats.is_disk_uptodate)
315

    
316
  def testMinor1(self):
317
    """Test connected, secondary device"""
318
    for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
319
      stats = info.GetMinorStatus(1)
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_disk_uptodate)
323

    
324
  def testMinor2(self):
325
    """Test unconfigured device"""
326
    for info in [self.drbd_info, self.drbd_info83,
327
                 self.drbd_info80e, self.drbd_info84]:
328
      stats = info.GetMinorStatus(2)
329
      self.failIf(stats.is_in_use)
330

    
331
  def testMinor4(self):
332
    """Test WFconn device"""
333
    for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
334
      stats = info.GetMinorStatus(4)
335
      self.failUnless(stats.is_in_use)
336
      self.failUnless(stats.is_wfconn and stats.is_primary and
337
                      stats.rrole == "Unknown" and
338
                      stats.is_disk_uptodate)
339

    
340
  def testMinor6(self):
341
    """Test diskless device"""
342
    for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
343
      stats = info.GetMinorStatus(6)
344
      self.failUnless(stats.is_in_use)
345
      self.failUnless(stats.is_connected and stats.is_secondary and
346
                      stats.peer_primary and stats.is_diskless)
347

    
348
  def testMinor8(self):
349
    """Test standalone device"""
350
    for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
351
      stats = info.GetMinorStatus(8)
352
      self.failUnless(stats.is_in_use)
353
      self.failUnless(stats.is_standalone and
354
                      stats.rrole == "Unknown" and
355
                      stats.is_disk_uptodate)
356

    
357
  def testDRBD83SyncFine(self):
358
    stats = self.drbd_info83_sync.GetMinorStatus(3)
359
    self.failUnless(stats.is_in_resync)
360
    self.assertAlmostEqual(stats.sync_percent, 34.9)
361

    
362
  def testDRBD83SyncBroken(self):
363
    stats = self.drbd_info83_sync_krnl.GetMinorStatus(3)
364
    self.failUnless(stats.is_in_resync)
365
    self.assertAlmostEqual(stats.sync_percent, 2.4)
366

    
367
  def testDRBD84Sync(self):
368
    stats = self.drbd_info84_sync.GetMinorStatus(5)
369
    self.failUnless(stats.is_in_resync)
370
    self.assertAlmostEqual(stats.sync_percent, 68.5)
371

    
372
  def testDRBDEmptyVersion(self):
373
    self.assertRaises(errors.BlockDeviceError,
374
                      drbd.DRBD8Info.CreateFromFile,
375
                      filename=self.proc80ev_data)
376

    
377

    
378
class TestDRBD8Construction(testutils.GanetiTestCase):
379
  def setUp(self):
380
    """Read in txt data"""
381
    testutils.GanetiTestCase.setUp(self)
382
    self.proc80_info = \
383
      drbd_info.DRBD8Info.CreateFromFile(
384
        filename=testutils.TestDataFilename("proc_drbd8.txt"))
385
    self.proc83_info = \
386
      drbd_info.DRBD8Info.CreateFromFile(
387
        filename=testutils.TestDataFilename("proc_drbd83.txt"))
388
    self.proc84_info = \
389
      drbd_info.DRBD8Info.CreateFromFile(
390
        filename=testutils.TestDataFilename("proc_drbd84.txt"))
391

    
392
    self.test_unique_id = ("hosta.com", 123, "host2.com", 123, 0, "secret")
393

    
394
  @testutils.patch_object(drbd_info.DRBD8Info, "CreateFromFile")
395
  def testConstructionWith80Data(self, mock_create_from_file):
396
    mock_create_from_file.return_value = self.proc80_info
397

    
398
    inst = drbd.DRBD8(self.test_unique_id, [], 123, {})
399
    self.assertEqual(inst._show_info_cls, drbd_info.DRBD83ShowInfo)
400
    self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD83CmdGenerator))
401

    
402
  @testutils.patch_object(drbd_info.DRBD8Info, "CreateFromFile")
403
  def testConstructionWith83Data(self, mock_create_from_file):
404
    mock_create_from_file.return_value = self.proc83_info
405

    
406
    inst = drbd.DRBD8(self.test_unique_id, [], 123, {})
407
    self.assertEqual(inst._show_info_cls, drbd_info.DRBD83ShowInfo)
408
    self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD83CmdGenerator))
409

    
410
  @testutils.patch_object(drbd_info.DRBD8Info, "CreateFromFile")
411
  def testConstructionWith84Data(self, mock_create_from_file):
412
    mock_create_from_file.return_value = self.proc84_info
413

    
414
    inst = drbd.DRBD8(self.test_unique_id, [], 123, {})
415
    self.assertEqual(inst._show_info_cls, drbd_info.DRBD84ShowInfo)
416
    # FIXME: add assertion for right class here!
417
    self.assertEqual(inst._cmd_gen, None)
418

    
419

    
420
if __name__ == "__main__":
421
  testutils.GanetiTestProgram()