Commit 5f2d6a8f authored by Apollon Oikonomopoulos's avatar Apollon Oikonomopoulos Committed by Hrvoje Ribicic

KVM: move tap control functions to a submodule

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: default avatarApollon Oikonomopoulos <apoikos@gmail.com>
Reviewed-by: default avatarHrvoje Ribicic <riba@google.com>
parent 67308d3b
...@@ -442,7 +442,8 @@ hypervisor_PYTHON = \ ...@@ -442,7 +442,8 @@ hypervisor_PYTHON = \
hypervisor_hv_kvm_PYTHON = \ hypervisor_hv_kvm_PYTHON = \
lib/hypervisor/hv_kvm/__init__.py \ 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 = \ storage_PYTHON = \
lib/storage/__init__.py \ lib/storage/__init__.py \
......
...@@ -31,8 +31,6 @@ import tempfile ...@@ -31,8 +31,6 @@ import tempfile
import time import time
import logging import logging
import pwd import pwd
import struct
import fcntl
import shutil import shutil
import urllib2 import urllib2
from bitarray import bitarray from bitarray import bitarray
...@@ -59,21 +57,12 @@ from ganeti.utils import wrapper as utils_wrapper ...@@ -59,21 +57,12 @@ from ganeti.utils import wrapper as utils_wrapper
from ganeti.hypervisor.hv_kvm.monitor import QmpConnection, QmpMessage, \ from ganeti.hypervisor.hv_kvm.monitor import QmpConnection, QmpMessage, \
MonitorSocket MonitorSocket
from ganeti.hypervisor.hv_kvm.netdev import OpenTap
_KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge" _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
_KVM_START_PAUSED_FLAG = "-S" _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 parameters which depend on L{constants.HV_KVM_SPICE_BIND}
_SPICE_ADDITIONAL_PARAMS = frozenset([ _SPICE_ADDITIONAL_PARAMS = frozenset([
constants.HV_KVM_SPICE_IP_VERSION, constants.HV_KVM_SPICE_IP_VERSION,
...@@ -244,95 +233,6 @@ def _AnalyzeSerializedRuntime(serialized_runtime): ...@@ -244,95 +233,6 @@ def _AnalyzeSerializedRuntime(serialized_runtime):
return (kvm_cmd, kvm_nics, hvparams, kvm_disks) 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): class HeadRequest(urllib2.Request):
def get_method(self): def get_method(self):
return "HEAD" return "HEAD"
...@@ -1689,8 +1589,8 @@ class KVMHypervisor(hv_base.BaseHypervisor): ...@@ -1689,8 +1589,8 @@ class KVMHypervisor(hv_base.BaseHypervisor):
kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp) kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
for nic_seq, nic in enumerate(kvm_nics): for nic_seq, nic in enumerate(kvm_nics):
tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr, tapname, tapfd = OpenTap(vnet_hdr=vnet_hdr,
name=self._GenerateTapName(nic)) name=self._GenerateTapName(nic))
tapfds.append(tapfd) tapfds.append(tapfd)
taps.append(tapname) taps.append(tapname)
if kvm_supports_netdev: if kvm_supports_netdev:
...@@ -2003,7 +1903,7 @@ class KVMHypervisor(hv_base.BaseHypervisor): ...@@ -2003,7 +1903,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" % cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
(hex(device.pci), kvm_devid, kvm_devid)] (hex(device.pci), kvm_devid, kvm_devid)]
elif dev_type == constants.HOTPLUG_TARGET_NIC: elif dev_type == constants.HOTPLUG_TARGET_NIC:
(tap, fd) = _OpenTap() (tap, fd) = OpenTap()
self._ConfigureNIC(instance, seq, device, tap) self._ConfigureNIC(instance, seq, device, tap)
self._PassTapFd(instance, fd, device) self._PassTapFd(instance, fd, device)
cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)] cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
......
#
#
# 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)
...@@ -38,6 +38,7 @@ from ganeti import utils ...@@ -38,6 +38,7 @@ from ganeti import utils
from ganeti import pathutils from ganeti import pathutils
from ganeti.hypervisor import hv_kvm from ganeti.hypervisor import hv_kvm
import ganeti.hypervisor.hv_kvm.netdev as netdev
import testutils import testutils
...@@ -348,11 +349,11 @@ class TestGetTunFeatures(unittest.TestCase): ...@@ -348,11 +349,11 @@ class TestGetTunFeatures(unittest.TestCase):
def testWrongIoctl(self): def testWrongIoctl(self):
tmpfile = tempfile.NamedTemporaryFile() tmpfile = tempfile.NamedTemporaryFile()
# A file does not have the right ioctls, so this must always fail # 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) self.assertTrue(result is None)
def _FakeIoctl(self, features, fd, request, buf): def _FakeIoctl(self, features, fd, request, buf):
self.assertEqual(request, hv_kvm.TUNGETFEATURES) self.assertEqual(request, netdev.TUNGETFEATURES)
(reqno, ) = struct.unpack("I", buf) (reqno, ) = struct.unpack("I", buf)
self.assertEqual(reqno, 0) self.assertEqual(reqno, 0)
...@@ -363,9 +364,9 @@ class TestGetTunFeatures(unittest.TestCase): ...@@ -363,9 +364,9 @@ class TestGetTunFeatures(unittest.TestCase):
tmpfile = tempfile.NamedTemporaryFile() tmpfile = tempfile.NamedTemporaryFile()
fd = tmpfile.fileno() 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) fn = compat.partial(self._FakeIoctl, features)
result = hv_kvm._GetTunFeatures(fd, _ioctl=fn) result = netdev._GetTunFeatures(fd, _ioctl=fn)
self.assertEqual(result, features) self.assertEqual(result, features)
...@@ -378,10 +379,10 @@ class TestProbeTapVnetHdr(unittest.TestCase): ...@@ -378,10 +379,10 @@ class TestProbeTapVnetHdr(unittest.TestCase):
tmpfile = tempfile.NamedTemporaryFile() tmpfile = tempfile.NamedTemporaryFile()
fd = tmpfile.fileno() 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) 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: if flags == 0:
self.assertFalse(result) self.assertFalse(result)
else: else:
...@@ -391,7 +392,7 @@ class TestProbeTapVnetHdr(unittest.TestCase): ...@@ -391,7 +392,7 @@ class TestProbeTapVnetHdr(unittest.TestCase):
tmpfile = tempfile.NamedTemporaryFile() tmpfile = tempfile.NamedTemporaryFile()
fd = tmpfile.fileno() 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): class TestGenerateDeviceKVMId(unittest.TestCase):
......
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