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

Reply via email to