blob: 9553e472457101b2ca286a1668f0cc6b7d023ccd [file] [log] [blame]
#!/usr/bin/python
#
# Copyright (C) 2006, 2007, 2010, 2012, 2013 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Script for unittesting the drbd module"""
import os
from ganeti import constants
from ganeti import errors
from ganeti.storage import drbd
from ganeti.storage import drbd_info
from ganeti.storage import drbd_cmdgen
import testutils
class TestDRBD8(testutils.GanetiTestCase):
def testGetVersion(self):
data = [
"version: 8.0.0 (api:76/proto:80)",
"version: 8.0.12 (api:76/proto:86-91)",
"version: 8.2.7 (api:88/proto:0-100)",
"version: 8.3.7.49 (api:188/proto:13-191)",
]
result = [
{
"k_major": 8,
"k_minor": 0,
"k_point": 0,
"api": 76,
"proto": 80,
},
{
"k_major": 8,
"k_minor": 0,
"k_point": 12,
"api": 76,
"proto": 86,
"proto2": "91",
},
{
"k_major": 8,
"k_minor": 2,
"k_point": 7,
"api": 88,
"proto": 0,
"proto2": "100",
},
{
"k_major": 8,
"k_minor": 3,
"k_point": 7,
"k_fix": "49",
"api": 188,
"proto": 13,
"proto2": "191",
}
]
for d, r in zip(data, result):
info = drbd.DRBD8Info.CreateFromLines([d])
self.assertEqual(info.GetVersion(), r)
self.assertEqual(info.GetVersionString(), d.replace("version: ", ""))
class TestDRBD8Runner(testutils.GanetiTestCase):
"""Testing case for drbd.DRBD8Dev"""
@staticmethod
def _has_disk(data, dname, mname, meta_index=0):
"""Check local disk corectness"""
retval = (
"local_dev" in data and
data["local_dev"] == dname and
"meta_dev" in data and
data["meta_dev"] == mname and
((meta_index is None and
"meta_index" not in data) or
("meta_index" in data and
data["meta_index"] == meta_index)
)
)
return retval
@staticmethod
def _has_net(data, local, remote):
"""Check network connection parameters"""
retval = (
"local_addr" in data and
data["local_addr"] == local and
"remote_addr" in data and
data["remote_addr"] == remote
)
return retval
def testParser83Creation(self):
"""Test drbdsetup show parser creation"""
drbd_info.DRBD83ShowInfo._GetShowParser()
def testParser84Creation(self):
"""Test drbdsetup show parser creation"""
drbd_info.DRBD84ShowInfo._GetShowParser()
def testParser80(self):
"""Test drbdsetup show parser for disk and network version 8.0"""
data = testutils.ReadTestData("bdev-drbd-8.0.txt")
result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
"/dev/xenvg/test.meta"),
"Wrong local disk info")
self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
("192.0.2.2", 11000)),
"Wrong network info (8.0.x)")
def testParser83(self):
"""Test drbdsetup show parser for disk and network version 8.3"""
data = testutils.ReadTestData("bdev-drbd-8.3.txt")
result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
"/dev/xenvg/test.meta"),
"Wrong local disk info")
self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
("192.0.2.2", 11000)),
"Wrong network info (8.3.x)")
def testParser84(self):
"""Test drbdsetup show parser for disk and network version 8.4"""
data = testutils.ReadTestData("bdev-drbd-8.4.txt")
result = drbd_info.DRBD84ShowInfo.GetDevInfo(data)
self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
"/dev/xenvg/test.meta"),
"Wrong local disk info")
self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
("192.0.2.2", 11000)),
"Wrong network info (8.4.x)")
def testParser84NoDiskParams(self):
"""Test drbdsetup show parser for 8.4 without disk params
The missing disk parameters occur after re-attaching a local disk but
before setting the disk params.
"""
data = testutils.ReadTestData("bdev-drbd-8.4-no-disk-params.txt")
result = drbd_info.DRBD84ShowInfo.GetDevInfo(data)
self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
"/dev/xenvg/test.meta", meta_index=None),
"Wrong local disk info")
self.failUnless(self._has_net(result, ("192.0.2.1", 11000),
("192.0.2.2", 11000)),
"Wrong network info (8.4.x)")
def testParserNetIP4(self):
"""Test drbdsetup show parser for IPv4 network"""
data = testutils.ReadTestData("bdev-drbd-net-ip4.txt")
result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
self.failUnless(("local_dev" not in result and
"meta_dev" not in result and
"meta_index" not in result),
"Should not find local disk info")
self.failUnless(self._has_net(result, ("192.0.2.1", 11002),
("192.0.2.2", 11002)),
"Wrong network info (IPv4)")
def testParserNetIP6(self):
"""Test drbdsetup show parser for IPv6 network"""
data = testutils.ReadTestData("bdev-drbd-net-ip6.txt")
result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
self.failUnless(("local_dev" not in result and
"meta_dev" not in result and
"meta_index" not in result),
"Should not find local disk info")
self.failUnless(self._has_net(result, ("2001:db8:65::1", 11048),
("2001:db8:66::1", 11048)),
"Wrong network info (IPv6)")
def testParserDisk(self):
"""Test drbdsetup show parser for disk"""
data = testutils.ReadTestData("bdev-drbd-disk.txt")
result = drbd_info.DRBD83ShowInfo.GetDevInfo(data)
self.failUnless(self._has_disk(result, "/dev/xenvg/test.data",
"/dev/xenvg/test.meta"),
"Wrong local disk info")
self.failUnless(("local_addr" not in result and
"remote_addr" not in result),
"Should not find network info")
def testBarriersOptions(self):
"""Test class method that generates drbdsetup options for disk barriers"""
# Tests that should fail because of wrong version/options combinations
should_fail = [
(8, 0, 12, "bfd", True),
(8, 0, 12, "fd", False),
(8, 0, 12, "b", True),
(8, 2, 7, "bfd", True),
(8, 2, 7, "b", True)
]
for vmaj, vmin, vrel, opts, meta in should_fail:
self.assertRaises(errors.BlockDeviceError,
drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
vmaj, vmin, vrel, opts, meta)
# get the valid options from the frozenset(frozenset()) in constants.
valid_options = [list(x)[0] for x in constants.DRBD_VALID_BARRIER_OPT]
# Versions that do not support anything
for vmaj, vmin, vrel in ((8, 0, 0), (8, 0, 11), (8, 2, 6)):
for opts in valid_options:
self.assertRaises(
errors.BlockDeviceError,
drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
vmaj, vmin, vrel, opts, True)
# Versions with partial support (testing only options that are supported)
tests = [
(8, 0, 12, "n", False, []),
(8, 0, 12, "n", True, ["--no-md-flushes"]),
(8, 2, 7, "n", False, []),
(8, 2, 7, "fd", False, ["--no-disk-flushes", "--no-disk-drain"]),
(8, 0, 12, "n", True, ["--no-md-flushes"]),
]
# Versions that support everything
for vmaj, vmin, vrel in ((8, 3, 0), (8, 3, 12)):
tests.append((vmaj, vmin, vrel, "bfd", True,
["--no-disk-barrier", "--no-disk-drain",
"--no-disk-flushes", "--no-md-flushes"]))
tests.append((vmaj, vmin, vrel, "n", False, []))
tests.append((vmaj, vmin, vrel, "b", True,
["--no-disk-barrier", "--no-md-flushes"]))
tests.append((vmaj, vmin, vrel, "fd", False,
["--no-disk-flushes", "--no-disk-drain"]))
tests.append((vmaj, vmin, vrel, "n", True, ["--no-md-flushes"]))
# Test execution
for test in tests:
vmaj, vmin, vrel, disabled_barriers, disable_meta_flush, expected = test
args = \
drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs(
vmaj, vmin, vrel,
disabled_barriers,
disable_meta_flush)
self.failUnless(set(args) == set(expected),
"For test %s, got wrong results %s" % (test, args))
# Unsupported or invalid versions
for vmaj, vmin, vrel in ((0, 7, 25), (9, 0, 0), (7, 0, 0), (8, 4, 0)):
self.assertRaises(errors.BlockDeviceError,
drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
vmaj, vmin, vrel, "n", True)
# Invalid options
for option in ("", "c", "whatever", "nbdfc", "nf"):
self.assertRaises(errors.BlockDeviceError,
drbd_cmdgen.DRBD83CmdGenerator._ComputeDiskBarrierArgs,
8, 3, 11, option, True)
class TestDRBD8Status(testutils.GanetiTestCase):
"""Testing case for DRBD8Dev /proc status"""
def setUp(self):
"""Read in txt data"""
testutils.GanetiTestCase.setUp(self)
proc_data = testutils.TestDataFilename("proc_drbd8.txt")
proc80e_data = testutils.TestDataFilename("proc_drbd80-emptyline.txt")
proc83_data = testutils.TestDataFilename("proc_drbd83.txt")
proc83_sync_data = testutils.TestDataFilename("proc_drbd83_sync.txt")
proc83_sync_krnl_data = \
testutils.TestDataFilename("proc_drbd83_sync_krnl2.6.39.txt")
proc84_data = testutils.TestDataFilename("proc_drbd84.txt")
proc84_sync_data = testutils.TestDataFilename("proc_drbd84_sync.txt")
proc84_emptyfirst_data = \
testutils.TestDataFilename("proc_drbd84_emptyfirst.txt")
self.proc80ev_data = \
testutils.TestDataFilename("proc_drbd80-emptyversion.txt")
self.drbd_info = drbd.DRBD8Info.CreateFromFile(filename=proc_data)
self.drbd_info80e = drbd.DRBD8Info.CreateFromFile(filename=proc80e_data)
self.drbd_info83 = drbd.DRBD8Info.CreateFromFile(filename=proc83_data)
self.drbd_info83_sync = \
drbd.DRBD8Info.CreateFromFile(filename=proc83_sync_data)
self.drbd_info83_sync_krnl = \
drbd.DRBD8Info.CreateFromFile(filename=proc83_sync_krnl_data)
self.drbd_info84 = drbd.DRBD8Info.CreateFromFile(filename=proc84_data)
self.drbd_info84_sync = \
drbd.DRBD8Info.CreateFromFile(filename=proc84_sync_data)
self.drbd_info84_emptyfirst = \
drbd.DRBD8Info.CreateFromFile(filename=proc84_emptyfirst_data)
def testIOErrors(self):
"""Test handling of errors while reading the proc file."""
temp_file = self._CreateTempFile()
os.unlink(temp_file)
self.failUnlessRaises(errors.BlockDeviceError,
drbd.DRBD8Info.CreateFromFile, filename=temp_file)
def testHelper(self):
"""Test reading usermode_helper in /sys."""
sys_drbd_helper = testutils.TestDataFilename("sys_drbd_usermode_helper.txt")
drbd_helper = drbd.DRBD8.GetUsermodeHelper(filename=sys_drbd_helper)
self.failUnlessEqual(drbd_helper, "/bin/true")
def testHelperIOErrors(self):
"""Test handling of errors while reading usermode_helper in /sys."""
temp_file = self._CreateTempFile()
os.unlink(temp_file)
self.failUnlessRaises(errors.BlockDeviceError,
drbd.DRBD8.GetUsermodeHelper, filename=temp_file)
def testMinorNotFound(self):
"""Test not-found-minor in /proc"""
self.failUnless(not self.drbd_info.HasMinorStatus(9))
self.failUnless(not self.drbd_info83.HasMinorStatus(9))
self.failUnless(not self.drbd_info80e.HasMinorStatus(3))
self.failUnless(not self.drbd_info84_emptyfirst.HasMinorStatus(0))
def testLineNotMatch(self):
"""Test wrong line passed to drbd_info.DRBD8Status"""
self.assertRaises(errors.BlockDeviceError, drbd_info.DRBD8Status, "foo")
def testMinor0(self):
"""Test connected, primary device"""
for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
stats = info.GetMinorStatus(0)
self.failUnless(stats.is_in_use)
self.failUnless(stats.is_connected and stats.is_primary and
stats.peer_secondary and stats.is_disk_uptodate)
def testMinor1(self):
"""Test connected, secondary device"""
for info in [self.drbd_info, self.drbd_info83, self.drbd_info84,
self.drbd_info84_emptyfirst]:
stats = info.GetMinorStatus(1)
self.failUnless(stats.is_in_use)
self.failUnless(stats.is_connected and stats.is_secondary and
stats.peer_primary and stats.is_disk_uptodate)
def testMinor2(self):
"""Test unconfigured device"""
for info in [self.drbd_info, self.drbd_info83,
self.drbd_info80e, self.drbd_info84,
self.drbd_info84_emptyfirst]:
stats = info.GetMinorStatus(2)
self.failIf(stats.is_in_use)
def testMinor4(self):
"""Test WFconn device"""
for info in [self.drbd_info, self.drbd_info83,
self.drbd_info84, self.drbd_info84_emptyfirst]:
stats = info.GetMinorStatus(4)
self.failUnless(stats.is_in_use)
self.failUnless(stats.is_wfconn and stats.is_primary and
stats.rrole == "Unknown" and
stats.is_disk_uptodate)
def testMinor6(self):
"""Test diskless device"""
for info in [self.drbd_info, self.drbd_info83,
self.drbd_info84, self.drbd_info84_emptyfirst]:
stats = info.GetMinorStatus(6)
self.failUnless(stats.is_in_use)
self.failUnless(stats.is_connected and stats.is_secondary and
stats.peer_primary and stats.is_diskless)
def testMinor8(self):
"""Test standalone device"""
for info in [self.drbd_info, self.drbd_info83, self.drbd_info84]:
stats = info.GetMinorStatus(8)
self.failUnless(stats.is_in_use)
self.failUnless(stats.is_standalone and
stats.rrole == "Unknown" and
stats.is_disk_uptodate)
def testDRBD83SyncFine(self):
stats = self.drbd_info83_sync.GetMinorStatus(3)
self.failUnless(stats.is_in_resync)
self.assertAlmostEqual(stats.sync_percent, 34.9)
def testDRBD83SyncBroken(self):
stats = self.drbd_info83_sync_krnl.GetMinorStatus(3)
self.failUnless(stats.is_in_resync)
self.assertAlmostEqual(stats.sync_percent, 2.4)
def testDRBD84Sync(self):
stats = self.drbd_info84_sync.GetMinorStatus(5)
self.failUnless(stats.is_in_resync)
self.assertAlmostEqual(stats.sync_percent, 68.5)
def testDRBDEmptyVersion(self):
self.assertRaises(errors.BlockDeviceError,
drbd.DRBD8Info.CreateFromFile,
filename=self.proc80ev_data)
class TestDRBD8Construction(testutils.GanetiTestCase):
def setUp(self):
"""Read in txt data"""
testutils.GanetiTestCase.setUp(self)
self.proc80_info = \
drbd_info.DRBD8Info.CreateFromFile(
filename=testutils.TestDataFilename("proc_drbd8.txt"))
self.proc83_info = \
drbd_info.DRBD8Info.CreateFromFile(
filename=testutils.TestDataFilename("proc_drbd83.txt"))
self.proc84_info = \
drbd_info.DRBD8Info.CreateFromFile(
filename=testutils.TestDataFilename("proc_drbd84.txt"))
self.test_unique_id = ("hosta.com", 123, "host2.com", 123, 0, "secret")
self.test_dyn_params = {
constants.DDP_LOCAL_IP: "192.0.2.1",
constants.DDP_LOCAL_MINOR: 0,
constants.DDP_REMOTE_IP: "192.0.2.2",
constants.DDP_REMOTE_MINOR: 0,
}
@testutils.patch_object(drbd.DRBD8, "GetProcInfo")
def testConstructionWith80Data(self, mock_create_from_file):
mock_create_from_file.return_value = self.proc80_info
inst = drbd.DRBD8Dev(self.test_unique_id, [], 123, {}, self.test_dyn_params)
self.assertEqual(inst._show_info_cls, drbd_info.DRBD83ShowInfo)
self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD83CmdGenerator))
@testutils.patch_object(drbd.DRBD8, "GetProcInfo")
def testConstructionWith83Data(self, mock_create_from_file):
mock_create_from_file.return_value = self.proc83_info
inst = drbd.DRBD8Dev(self.test_unique_id, [], 123, {}, self.test_dyn_params)
self.assertEqual(inst._show_info_cls, drbd_info.DRBD83ShowInfo)
self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD83CmdGenerator))
@testutils.patch_object(drbd.DRBD8, "GetProcInfo")
def testConstructionWith84Data(self, mock_create_from_file):
mock_create_from_file.return_value = self.proc84_info
inst = drbd.DRBD8Dev(self.test_unique_id, [], 123, {}, self.test_dyn_params)
self.assertEqual(inst._show_info_cls, drbd_info.DRBD84ShowInfo)
self.assertTrue(isinstance(inst._cmd_gen, drbd_cmdgen.DRBD84CmdGenerator))
if __name__ == "__main__":
testutils.GanetiTestProgram()