Commit 3840729d authored by Iustin Pop's avatar Iustin Pop
Browse files

Add unittest for DRBD8 drdbsetup show parser

This patch changes the bdev.DRBD8._GetDevInfo to take a string instead
of a minor, separates the `drbdsetup show` invocation into a new
separate method (bdev.DRBD8._GetShowData) and modifies the rest of the
DRBD8 class to make the appropriate calls.

It also adds a unittest script and data files for testing various cases
of device output.

Reviewed-by: imsnah
parent f3a55c90
...@@ -1737,19 +1737,27 @@ class DRBD8(BaseDRBD): ...@@ -1737,19 +1737,27 @@ class DRBD8(BaseDRBD):
return bnf return bnf
@classmethod @classmethod
def _GetDevInfo(cls, minor): def _GetShowData(cls, minor):
"""Get details about a given DRBD minor. """Return the `drbdsetup show` data for a minor.
This return, if available, the local backing device (as a path)
and the local and remote (ip, port) information.
""" """
data = {}
result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"]) result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
if result.failed: if result.failed:
logger.Error("Can't display the drbd config: %s" % result.fail_reason) logger.Error("Can't display the drbd config: %s" % result.fail_reason)
return data return None
out = result.stdout return result.stdout
@classmethod
def _GetDevInfo(cls, out):
"""Parse details about a given DRBD minor.
This return, if available, the local backing device (as a path)
and the local and remote (ip, port) information from a string
containing the output of the `drbdsetup show` command as returned
by _GetShowData.
"""
data = {}
if not out: if not out:
return data return data
...@@ -1881,7 +1889,7 @@ class DRBD8(BaseDRBD): ...@@ -1881,7 +1889,7 @@ class DRBD8(BaseDRBD):
timeout = time.time() + 10 timeout = time.time() + 10
ok = False ok = False
while time.time() < timeout: while time.time() < timeout:
info = cls._GetDevInfo(minor) info = cls._GetDevInfo(cls._GetShowData(minor))
if not "local_addr" in info or not "remote_addr" in info: if not "local_addr" in info or not "remote_addr" in info:
time.sleep(1) time.sleep(1)
continue continue
...@@ -1904,7 +1912,7 @@ class DRBD8(BaseDRBD): ...@@ -1904,7 +1912,7 @@ class DRBD8(BaseDRBD):
raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren") raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren")
if len(devices) != 2: if len(devices) != 2:
raise errors.BlockDeviceError("Need two devices for AddChildren") raise errors.BlockDeviceError("Need two devices for AddChildren")
info = self._GetDevInfo(self.minor) info = self._GetDevInfo(self._GetShowData(self.minor))
if "local_dev" in info: if "local_dev" in info:
raise errors.BlockDeviceError("DRBD8 already attached to a local disk") raise errors.BlockDeviceError("DRBD8 already attached to a local disk")
backend, meta = devices backend, meta = devices
...@@ -1930,7 +1938,7 @@ class DRBD8(BaseDRBD): ...@@ -1930,7 +1938,7 @@ class DRBD8(BaseDRBD):
raise errors.BlockDeviceError("Can't attach to drbd8 during" raise errors.BlockDeviceError("Can't attach to drbd8 during"
" RemoveChildren") " RemoveChildren")
# early return if we don't actually have backing storage # early return if we don't actually have backing storage
info = self._GetDevInfo(self.minor) info = self._GetDevInfo(self._GetShowData(self.minor))
if "local_dev" not in info: if "local_dev" not in info:
return return
if len(self._children) != 2: if len(self._children) != 2:
...@@ -2083,7 +2091,7 @@ class DRBD8(BaseDRBD): ...@@ -2083,7 +2091,7 @@ class DRBD8(BaseDRBD):
""" """
for minor in self._GetUsedDevs(): for minor in self._GetUsedDevs():
info = self._GetDevInfo(minor) info = self._GetDevInfo(self._GetShowData(minor))
match_l = self._MatchesLocal(info) match_l = self._MatchesLocal(info)
match_r = self._MatchesNet(info) match_r = self._MatchesNet(info)
if match_l and match_r: if match_l and match_r:
...@@ -2093,8 +2101,9 @@ class DRBD8(BaseDRBD): ...@@ -2093,8 +2101,9 @@ class DRBD8(BaseDRBD):
(self._lhost, self._lport, (self._lhost, self._lport,
self._rhost, self._rport), self._rhost, self._rport),
"C") "C")
if res_r and self._MatchesNet(self._GetDevInfo(minor)): if res_r:
break if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
break
# the weakest case: we find something that is only net attached # the weakest case: we find something that is only net attached
# even though we were passed some children at init time # even though we were passed some children at init time
if match_r and "local_dev" not in info: if match_r and "local_dev" not in info:
...@@ -2114,7 +2123,7 @@ class DRBD8(BaseDRBD): ...@@ -2114,7 +2123,7 @@ class DRBD8(BaseDRBD):
# None) # None)
if (self._AssembleNet(minor, (self._lhost, self._lport, if (self._AssembleNet(minor, (self._lhost, self._lport,
self._rhost, self._rport), "C") and self._rhost, self._rport), "C") and
self._MatchesNet(self._GetDevInfo(minor))): self._MatchesNet(self._GetDevInfo(self._GetShowData(minor)))):
break break
else: else:
......
TESTS = \ TESTS = \
ganeti.config_unittest.py \ ganeti.config_unittest.py \
ganeti.hooks_unittest.py \ ganeti.hooks_unittest.py \
ganeti.utils_unittest.py ganeti.utils_unittest.py \
ganeti.bdev_unittest.py
TESTS_ENVIRONMENT = PYTHONPATH=.:$(top_builddir) TESTS_ENVIRONMENT = PYTHONPATH=.:$(top_builddir)
check-am: do-pre-check check-am: do-pre-check
......
disk {
size 0s _is_default; # bytes
on-io-error detach;
fencing dont-care _is_default;
}
net {
timeout 60 _is_default; # 1/10 seconds
max-epoch-size 16384;
max-buffers 16384;
unplug-watermark 128 _is_default;
connect-int 10 _is_default; # seconds
ping-int 10 _is_default; # seconds
sndbuf-size 8388608; # bytes
ko-count 0 _is_default;
after-sb-0pri disconnect _is_default;
after-sb-1pri disconnect _is_default;
after-sb-2pri disconnect _is_default;
rr-conflict disconnect _is_default;
ping-timeout 5 _is_default; # 1/10 seconds
}
syncer {
rate 30720k; # bytes/second
after -1 _is_default;
al-extents 257;
}
protocol A;
_this_host {
device "/dev/drbd63";
disk "/dev/xenvg/test.data";
meta-disk "/dev/xenvg/test.meta" [ 0 ];
address 192.168.1.1:11000;
}
_remote_host {
address 192.168.1.2:11000;
}
disk {
size 0s _is_default; # bytes
on-io-error detach;
fencing dont-care _is_default;
}
syncer {
rate 250k _is_default; # bytes/second
after -1 _is_default;
al-extents 257;
}
_this_host {
device "/dev/drbd58";
disk "/dev/xenvg/test.data";
meta-disk "/dev/xenvg/test.meta" [ 0 ];
}
net {
timeout 60 _is_default; # 1/10 seconds
max-epoch-size 2048 _is_default;
max-buffers 2048 _is_default;
unplug-watermark 128 _is_default;
connect-int 10 _is_default; # seconds
ping-int 10 _is_default; # seconds
sndbuf-size 131070 _is_default; # bytes
ko-count 0 _is_default;
after-sb-0pri disconnect _is_default;
after-sb-1pri disconnect _is_default;
after-sb-2pri disconnect _is_default;
rr-conflict disconnect _is_default;
ping-timeout 5 _is_default; # 1/10 seconds
}
syncer {
rate 250k _is_default; # bytes/second
after -1 _is_default;
al-extents 127 _is_default;
}
protocol C;
_this_host {
device "/dev/drbd59";
address 192.168.1.1:11002;
}
_remote_host {
address 192.168.1.2:11002;
}
#!/usr/bin/python
#
# Copyright (C) 2006, 2007 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
"""Script for unittesting the bdev module"""
import unittest
from ganeti import bdev
class TestDRBD8Runner(unittest.TestCase):
"""Testing case for DRBD8"""
@staticmethod
def _has_disk(data, dname, mname):
"""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" in data and
data["meta_index"] == 0
)
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 testParserCreation(self):
"""Test drbdsetup show parser creation"""
bdev.DRBD8._GetShowParser()
def testParserBoth(self):
"""Test drbdsetup show parser for disk and network"""
data = open("data/bdev-both.txt").read()
result = bdev.DRBD8._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.168.1.1", 11000),
("192.168.1.2", 11000)),
"Wrong network info")
def testParserNet(self):
"""Test drbdsetup show parser for disk and network"""
data = open("data/bdev-net.txt").read()
result = bdev.DRBD8._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.168.1.1", 11002),
("192.168.1.2", 11002)),
"Wrong network info")
def testParserDisk(self):
"""Test drbdsetup show parser for disk and network"""
data = open("data/bdev-disk.txt").read()
result = bdev.DRBD8._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")
if __name__ == '__main__':
unittest.main()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment