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 = \
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 \
......
......@@ -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)]
......
#
#
# 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
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):
......
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