From 3840729daa88e268c09cf64665b6769d426a40c8 Mon Sep 17 00:00:00 2001 From: Iustin Pop <iustin@google.com> Date: Mon, 7 Jan 2008 11:22:40 +0000 Subject: [PATCH] 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 --- lib/bdev.py | 39 +++++++++------ test/Makefile.am | 4 +- test/data/bdev-both.txt | 35 +++++++++++++ test/data/bdev-disk.txt | 15 ++++++ test/data/bdev-net.txt | 28 +++++++++++ test/ganeti.bdev_unittest.py | 96 ++++++++++++++++++++++++++++++++++++ 6 files changed, 201 insertions(+), 16 deletions(-) create mode 100644 test/data/bdev-both.txt create mode 100644 test/data/bdev-disk.txt create mode 100644 test/data/bdev-net.txt create mode 100755 test/ganeti.bdev_unittest.py diff --git a/lib/bdev.py b/lib/bdev.py index 5069bb2fe..8b2797191 100644 --- a/lib/bdev.py +++ b/lib/bdev.py @@ -1737,19 +1737,27 @@ class DRBD8(BaseDRBD): return bnf @classmethod - def _GetDevInfo(cls, minor): - """Get 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. + def _GetShowData(cls, minor): + """Return the `drbdsetup show` data for a minor. """ - data = {} result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"]) if result.failed: logger.Error("Can't display the drbd config: %s" % result.fail_reason) - return data - out = result.stdout + return None + 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: return data @@ -1881,7 +1889,7 @@ class DRBD8(BaseDRBD): timeout = time.time() + 10 ok = False 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: time.sleep(1) continue @@ -1904,7 +1912,7 @@ class DRBD8(BaseDRBD): raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren") if len(devices) != 2: 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: raise errors.BlockDeviceError("DRBD8 already attached to a local disk") backend, meta = devices @@ -1930,7 +1938,7 @@ class DRBD8(BaseDRBD): raise errors.BlockDeviceError("Can't attach to drbd8 during" " RemoveChildren") # 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: return if len(self._children) != 2: @@ -2083,7 +2091,7 @@ class DRBD8(BaseDRBD): """ for minor in self._GetUsedDevs(): - info = self._GetDevInfo(minor) + info = self._GetDevInfo(self._GetShowData(minor)) match_l = self._MatchesLocal(info) match_r = self._MatchesNet(info) if match_l and match_r: @@ -2093,8 +2101,9 @@ class DRBD8(BaseDRBD): (self._lhost, self._lport, self._rhost, self._rport), "C") - if res_r and self._MatchesNet(self._GetDevInfo(minor)): - break + if res_r: + if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))): + break # the weakest case: we find something that is only net attached # even though we were passed some children at init time if match_r and "local_dev" not in info: @@ -2114,7 +2123,7 @@ class DRBD8(BaseDRBD): # None) if (self._AssembleNet(minor, (self._lhost, self._lport, self._rhost, self._rport), "C") and - self._MatchesNet(self._GetDevInfo(minor))): + self._MatchesNet(self._GetDevInfo(self._GetShowData(minor)))): break else: diff --git a/test/Makefile.am b/test/Makefile.am index 83c7b9b00..bd221006c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,7 +1,9 @@ TESTS = \ ganeti.config_unittest.py \ ganeti.hooks_unittest.py \ - ganeti.utils_unittest.py + ganeti.utils_unittest.py \ + ganeti.bdev_unittest.py + TESTS_ENVIRONMENT = PYTHONPATH=.:$(top_builddir) check-am: do-pre-check diff --git a/test/data/bdev-both.txt b/test/data/bdev-both.txt new file mode 100644 index 000000000..b40f5e831 --- /dev/null +++ b/test/data/bdev-both.txt @@ -0,0 +1,35 @@ +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; +} diff --git a/test/data/bdev-disk.txt b/test/data/bdev-disk.txt new file mode 100644 index 000000000..caed5009c --- /dev/null +++ b/test/data/bdev-disk.txt @@ -0,0 +1,15 @@ +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 ]; +} diff --git a/test/data/bdev-net.txt b/test/data/bdev-net.txt new file mode 100644 index 000000000..eae1f4c0d --- /dev/null +++ b/test/data/bdev-net.txt @@ -0,0 +1,28 @@ +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; +} diff --git a/test/ganeti.bdev_unittest.py b/test/ganeti.bdev_unittest.py new file mode 100755 index 000000000..5a775c796 --- /dev/null +++ b/test/ganeti.bdev_unittest.py @@ -0,0 +1,96 @@ +#!/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() -- GitLab