KVM: move tap control functions to a submodule
authorApollon Oikonomopoulos <apoikos@gmail.com>
Thu, 3 Apr 2014 08:12:48 +0000 (11:12 +0300)
committerHrvoje Ribicic <riba@google.com>
Mon, 7 Apr 2014 09:48:02 +0000 (11:48 +0200)
Move all tap-related functionality to the hv_kvm.netdev submodule. We
rename _OpenTap to OpenTap, since it will now be used as a public
function.

Also, change the hv_kvm tests to import the new code.

Signed-off-by: Apollon Oikonomopoulos <apoikos@gmail.com>
Reviewed-by: Hrvoje Ribicic <riba@google.com>

Makefile.am
lib/hypervisor/hv_kvm/__init__.py
lib/hypervisor/hv_kvm/netdev.py [new file with mode: 0644]
test/py/ganeti.hypervisor.hv_kvm_unittest.py

index 647fa5f..5904936 100644 (file)
@@ -442,7 +442,8 @@ hypervisor_PYTHON = \
 
 hypervisor_hv_kvm_PYTHON = \
   lib/hypervisor/hv_kvm/__init__.py \
-  lib/hypervisor/hv_kvm/monitor.py
+  lib/hypervisor/hv_kvm/monitor.py \
+  lib/hypervisor/hv_kvm/netdev.py
 
 storage_PYTHON = \
        lib/storage/__init__.py \
index bf14b8f..227dafd 100644 (file)
@@ -31,8 +31,6 @@ import tempfile
 import time
 import logging
 import pwd
-import struct
-import fcntl
 import shutil
 import urllib2
 from bitarray import bitarray
@@ -59,21 +57,12 @@ from ganeti.utils import wrapper as utils_wrapper
 
 from ganeti.hypervisor.hv_kvm.monitor import QmpConnection, QmpMessage, \
                                              MonitorSocket
+from ganeti.hypervisor.hv_kvm.netdev import OpenTap
 
 
 _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
 _KVM_START_PAUSED_FLAG = "-S"
 
-# TUN/TAP driver constants, taken from <linux/if_tun.h>
-# They are architecture-independent and already hardcoded in qemu-kvm source,
-# so we can safely include them here.
-TUNSETIFF = 0x400454ca
-TUNGETIFF = 0x800454d2
-TUNGETFEATURES = 0x800454cf
-IFF_TAP = 0x0002
-IFF_NO_PI = 0x1000
-IFF_VNET_HDR = 0x4000
-
 #: SPICE parameters which depend on L{constants.HV_KVM_SPICE_BIND}
 _SPICE_ADDITIONAL_PARAMS = frozenset([
   constants.HV_KVM_SPICE_IP_VERSION,
@@ -244,95 +233,6 @@ def _AnalyzeSerializedRuntime(serialized_runtime):
   return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
 
 
-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:
-   1. TUNGETFEATURES ioctl() *must* be implemented
-   2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
-   3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
-      drivers/net/tun.c there is no way to test this until after the tap device
-      has been created using TUNSETIFF, and there is no way to change the
-      IFF_VNET_HDR flag after creating the interface, catch-22! However both
-      TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
-      thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
-
-   @type fd: int
-   @param fd: the file descriptor of /dev/net/tun
-
-  """
-  flags = _features_fn(fd)
-
-  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, name=""):
-  """Open a new tap device and return its file descriptor.
-
-  This is intended to be used by a qemu-type hypervisor together with the -net
-  tap,fd=<fd> command line parameter.
-
-  @type vnet_hdr: boolean
-  @param vnet_hdr: Enable the VNET Header
-
-  @type name: string
-  @param name: name for the TAP interface being created; if an empty
-               string is passed, the OS will generate a unique name
-
-  @return: (ifname, tapfd)
-  @rtype: tuple
-
-  """
-  try:
-    tapfd = os.open("/dev/net/tun", os.O_RDWR)
-  except EnvironmentError:
-    raise errors.HypervisorError("Failed to open /dev/net/tun")
-
-  flags = IFF_TAP | IFF_NO_PI
-
-  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
-    flags |= IFF_VNET_HDR
-
-  # The struct ifreq ioctl request (see netdevice(7))
-  ifr = struct.pack("16sh", name, flags)
-
-  try:
-    res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
-  except EnvironmentError, err:
-    raise errors.HypervisorError("Failed to allocate a new TAP device: %s" %
-                                 err)
-
-  # Get the interface name from the ioctl
-  ifname = struct.unpack("16sh", res)[0].strip("\x00")
-  return (ifname, tapfd)
-
-
 class HeadRequest(urllib2.Request):
   def get_method(self):
     return "HEAD"
@@ -1689,8 +1589,8 @@ class KVMHypervisor(hv_base.BaseHypervisor):
       kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
 
       for nic_seq, nic in enumerate(kvm_nics):
-        tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr,
-                                  name=self._GenerateTapName(nic))
+        tapname, tapfd = OpenTap(vnet_hdr=vnet_hdr,
+                                 name=self._GenerateTapName(nic))
         tapfds.append(tapfd)
         taps.append(tapname)
         if kvm_supports_netdev:
@@ -2003,7 +1903,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
       cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
                 (hex(device.pci), kvm_devid, kvm_devid)]
     elif dev_type == constants.HOTPLUG_TARGET_NIC:
-      (tap, fd) = _OpenTap()
+      (tap, fd) = OpenTap()
       self._ConfigureNIC(instance, seq, device, tap)
       self._PassTapFd(instance, fd, device)
       cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
diff --git a/lib/hypervisor/hv_kvm/netdev.py b/lib/hypervisor/hv_kvm/netdev.py
new file mode 100644 (file)
index 0000000..dbbd6ce
--- /dev/null
@@ -0,0 +1,131 @@
+#
+#
+
+# Copyright (C) 2014 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.
+
+
+"""KVM hypervisor tap device helpers
+
+"""
+
+import os
+import logging
+import struct
+import fcntl
+
+from ganeti import errors
+
+
+# TUN/TAP driver constants, taken from <linux/if_tun.h>
+# They are architecture-independent and already hardcoded in qemu-kvm source,
+# so we can safely include them here.
+TUNSETIFF = 0x400454ca
+TUNGETIFF = 0x800454d2
+TUNGETFEATURES = 0x800454cf
+IFF_TAP = 0x0002
+IFF_NO_PI = 0x1000
+IFF_VNET_HDR = 0x4000
+
+
+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:
+   1. TUNGETFEATURES ioctl() *must* be implemented
+   2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
+   3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
+      drivers/net/tun.c there is no way to test this until after the tap device
+      has been created using TUNSETIFF, and there is no way to change the
+      IFF_VNET_HDR flag after creating the interface, catch-22! However both
+      TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
+      thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
+
+   @type fd: int
+   @param fd: the file descriptor of /dev/net/tun
+
+  """
+  flags = _features_fn(fd)
+
+  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, name=""):
+  """Open a new tap device and return its file descriptor.
+
+  This is intended to be used by a qemu-type hypervisor together with the -net
+  tap,fd=<fd> command line parameter.
+
+  @type vnet_hdr: boolean
+  @param vnet_hdr: Enable the VNET Header
+
+  @type name: string
+  @param name: name for the TAP interface being created; if an empty
+               string is passed, the OS will generate a unique name
+
+  @return: (ifname, tapfd)
+  @rtype: tuple
+
+  """
+  try:
+    tapfd = os.open("/dev/net/tun", os.O_RDWR)
+  except EnvironmentError:
+    raise errors.HypervisorError("Failed to open /dev/net/tun")
+
+  flags = IFF_TAP | IFF_NO_PI
+
+  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
+    flags |= IFF_VNET_HDR
+
+  # The struct ifreq ioctl request (see netdevice(7))
+  ifr = struct.pack("16sh", name, flags)
+
+  try:
+    res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
+  except EnvironmentError, err:
+    raise errors.HypervisorError("Failed to allocate a new TAP device: %s" %
+                                 err)
+
+  # Get the interface name from the ioctl
+  ifname = struct.unpack("16sh", res)[0].strip("\x00")
+  return (ifname, tapfd)
index 22ef952..2fc3c33 100755 (executable)
@@ -38,6 +38,7 @@ from ganeti import utils
 from ganeti import pathutils
 
 from ganeti.hypervisor import hv_kvm
+import ganeti.hypervisor.hv_kvm.netdev as netdev
 
 import testutils
 
@@ -348,11 +349,11 @@ 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())
+    result = netdev._GetTunFeatures(tmpfile.fileno())
     self.assertTrue(result is None)
 
   def _FakeIoctl(self, features, fd, request, buf):
-    self.assertEqual(request, hv_kvm.TUNGETFEATURES)
+    self.assertEqual(request, netdev.TUNGETFEATURES)
 
     (reqno, ) = struct.unpack("I", buf)
     self.assertEqual(reqno, 0)
@@ -363,9 +364,9 @@ class TestGetTunFeatures(unittest.TestCase):
     tmpfile = tempfile.NamedTemporaryFile()
     fd = tmpfile.fileno()
 
-    for features in [0, hv_kvm.IFF_VNET_HDR]:
+    for features in [0, netdev.IFF_VNET_HDR]:
       fn = compat.partial(self._FakeIoctl, features)
-      result = hv_kvm._GetTunFeatures(fd, _ioctl=fn)
+      result = netdev._GetTunFeatures(fd, _ioctl=fn)
       self.assertEqual(result, features)
 
 
@@ -378,10 +379,10 @@ class TestProbeTapVnetHdr(unittest.TestCase):
     tmpfile = tempfile.NamedTemporaryFile()
     fd = tmpfile.fileno()
 
-    for flags in [0, hv_kvm.IFF_VNET_HDR]:
+    for flags in [0, netdev.IFF_VNET_HDR]:
       fn = compat.partial(self._FakeTunFeatures, fd, flags)
 
-      result = hv_kvm._ProbeTapVnetHdr(fd, _features_fn=fn)
+      result = netdev._ProbeTapVnetHdr(fd, _features_fn=fn)
       if flags == 0:
         self.assertFalse(result)
       else:
@@ -391,7 +392,7 @@ class TestProbeTapVnetHdr(unittest.TestCase):
     tmpfile = tempfile.NamedTemporaryFile()
     fd = tmpfile.fileno()
 
-    self.assertFalse(hv_kvm._ProbeTapVnetHdr(fd, _features_fn=lambda _: None))
+    self.assertFalse(netdev._ProbeTapVnetHdr(fd, _features_fn=lambda _: None))
 
 
 class TestGenerateDeviceKVMId(unittest.TestCase):