Split retrieving supported features into a dedicated function which can be mocked. Tests are added for both “_ProbeTapVnetHdr” and “_GetTunFeatures”.
Signed-off-by: Michael Hanselmann <[email protected]> --- lib/hypervisor/hv_kvm.py | 40 +++++++++++++++------- test/py/ganeti.hypervisor.hv_kvm_unittest.py | 51 ++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/lib/hypervisor/hv_kvm.py b/lib/hypervisor/hv_kvm.py index 1fe502e..bf39d57 100644 --- a/lib/hypervisor/hv_kvm.py +++ b/lib/hypervisor/hv_kvm.py @@ -80,7 +80,24 @@ _SPICE_ADDITIONAL_PARAMS = frozenset([ ]) -def _ProbeTapVnetHdr(fd): +def _GetTunFeatures(fd, _ioctl=fcntl.ioctl): + """Retrieves supported TUN features from file descriptor. + + @see: L{_ProbeTapVnetHdr} + + """ + req = struct.pack("I", 0) + try: + buf = _ioctl(fd, TUNGETFEATURES, req) + except EnvironmentError, err: + logging.warning("ioctl(TUNGETFEATURES) failed: %s" % err) + return None + else: + (flags, ) = struct.unpack("I", buf) + return flags + + +def _ProbeTapVnetHdr(fd, _features_fn=_GetTunFeatures): """Check whether to enable the IFF_VNET_HDR flag. To do this, _all_ of the following conditions must be met: @@ -97,20 +114,19 @@ def _ProbeTapVnetHdr(fd): @param fd: the file descriptor of /dev/net/tun """ - req = struct.pack("I", 0) - try: - res = fcntl.ioctl(fd, TUNGETFEATURES, req) - except EnvironmentError: - logging.warning("TUNGETFEATURES ioctl() not implemented") - return False + flags = _features_fn(fd) - tunflags = struct.unpack("I", res)[0] - if tunflags & IFF_VNET_HDR: - return True - else: - logging.warning("Host does not support IFF_VNET_HDR, not enabling") + if flags is None: + # Not supported return False + result = bool(flags & IFF_VNET_HDR) + + if not result: + logging.warning("Kernel does not support IFF_VNET_HDR, not enabling") + + return result + def _OpenTap(vnet_hdr=True): """Open a new tap device and return its file descriptor. diff --git a/test/py/ganeti.hypervisor.hv_kvm_unittest.py b/test/py/ganeti.hypervisor.hv_kvm_unittest.py index b7816f2..c21c749 100755 --- a/test/py/ganeti.hypervisor.hv_kvm_unittest.py +++ b/test/py/ganeti.hypervisor.hv_kvm_unittest.py @@ -26,6 +26,7 @@ import tempfile import unittest import socket import os +import struct from ganeti import serializer from ganeti import constants @@ -315,5 +316,55 @@ class TestHelpRegexps(testutils.GanetiTestCase): self.assertFalse(boot_re.search(help_01590)) +class TestGetTunFeatures(unittest.TestCase): + def testWrongIoctl(self): + tmpfile = tempfile.NamedTemporaryFile() + # A file does not have the right ioctls, so this must always fail + result = hv_kvm._GetTunFeatures(tmpfile.fileno()) + self.assertTrue(result is None) + + def _FakeIoctl(self, features, fd, request, buf): + self.assertEqual(request, hv_kvm.TUNGETFEATURES) + + (reqno, ) = struct.unpack("I", buf) + self.assertEqual(reqno, 0) + + return struct.pack("I", features) + + def test(self): + tmpfile = tempfile.NamedTemporaryFile() + fd = tmpfile.fileno() + + for features in [0, hv_kvm.IFF_VNET_HDR]: + fn = compat.partial(self._FakeIoctl, features) + result = hv_kvm._GetTunFeatures(fd, _ioctl=fn) + self.assertEqual(result, features) + + +class TestProbeTapVnetHdr(unittest.TestCase): + def _FakeTunFeatures(self, expected_fd, flags, fd): + self.assertEqual(fd, expected_fd) + return flags + + def test(self): + tmpfile = tempfile.NamedTemporaryFile() + fd = tmpfile.fileno() + + for flags in [0, hv_kvm.IFF_VNET_HDR]: + fn = compat.partial(self._FakeTunFeatures, fd, flags) + + result = hv_kvm._ProbeTapVnetHdr(fd, _features_fn=fn) + if flags == 0: + self.assertFalse(result) + else: + self.assertTrue(result) + + def testUnsupported(self): + tmpfile = tempfile.NamedTemporaryFile() + fd = tmpfile.fileno() + + self.assertFalse(hv_kvm._ProbeTapVnetHdr(fd, _features_fn=lambda _: None)) + + if __name__ == "__main__": testutils.GanetiTestProgram() -- 1.8.1
