diff mbox

[2/5] KVM test: Add helpers to control the TAP/bridge

Message ID 1306988589-17085-3-git-send-email-lmr@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Lucas Meneghel Rodrigues June 2, 2011, 4:23 a.m. UTC
This patch adds some helpers to assist virt test to setup the bridge or
macvtap based guest networking.

Changes from v1:
 * Fixed undefined variable errors on the exception class definitions

Changes from v2:
 * On RHEL5, the io operation TUNGETFEATURES = 0x800454cf may return an
integer bigger than the max signed integer, triggering an OverflowError.
Let's catch that exception and make vnet_hdr_probe() return False in
case it happens.

Signed-off-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Lucas Meneghel Rodrigues <lmr@redhat.com>

fixup
---
 client/virt/virt_utils.py |  212 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 212 insertions(+), 0 deletions(-)
diff mbox

Patch

diff --git a/client/virt/virt_utils.py b/client/virt/virt_utils.py
index 0f02f3d..0700509 100644
--- a/client/virt/virt_utils.py
+++ b/client/virt/virt_utils.py
@@ -6,6 +6,7 @@  KVM test utility functions.
 
 import time, string, random, socket, os, signal, re, logging, commands, cPickle
 import fcntl, shelve, ConfigParser, threading, sys, UserDict, inspect
+import struct
 from autotest_lib.client.bin import utils, os_dep
 from autotest_lib.client.common_lib import error, logging_config
 import rss_client, aexpect
@@ -15,6 +16,20 @@  try:
 except ImportError:
     KOJI_INSTALLED = False
 
+# From include/linux/sockios.h
+SIOCSIFHWADDR = 0x8924
+SIOCGIFHWADDR = 0x8927
+SIOCSIFFLAGS = 0x8914
+SIOCGIFINDEX = 0x8933
+SIOCBRADDIF = 0x89a2
+# From linux/include/linux/if_tun.h
+TUNSETIFF = 0x400454ca
+TUNGETIFF = 0x800454d2
+TUNGETFEATURES = 0x800454cf
+IFF_UP = 0x1
+IFF_TAP = 0x0002
+IFF_NO_PI = 0x1000
+IFF_VNET_HDR = 0x4000
 
 def _lock_file(filename):
     f = open(filename, "w")
@@ -36,6 +51,80 @@  def is_vm(obj):
     return obj.__class__.__name__ == "VM"
 
 
+class NetError(Exception):
+    pass
+
+
+class TAPModuleError(NetError):
+    def __init__(self, devname):
+        NetError.__init__(self, devname)
+        self.devname = devname
+
+    def __str__(self):
+        return "Can't open %s" % self.devname
+
+class TAPNotExistError(NetError):
+    def __init__(self, ifname):
+        NetError.__init__(self, ifname)
+        self.ifname = ifname
+
+    def __str__(self):
+        return "Interface %s does not exist" % self.ifname
+
+
+class TAPCreationError(NetError):
+    def __init__(self, ifname, details=None):
+        NetError.__init__(self, ifname, details)
+        self.ifname = ifname
+        self.details = details
+
+    def __str__(self):
+        e_msg = "Cannot create TAP device %s" % self.ifname
+        if self.details is not None:
+            e_msg += ": %s" % self.details
+        return e_msg
+
+
+class TAPBringUpError(NetError):
+    def __init__(self, ifname):
+        NetError.__init__(self, ifname)
+        self.ifname = ifname
+
+    def __str__(self):
+        return "Cannot bring up TAP %s" % self.ifname
+
+
+class BRAddIfError(NetError):
+    def __init__(self, ifname, brname, details):
+        NetError.__init__(self, ifname, brname, details)
+        self.ifname = ifname
+        self.brname = brname
+        self.details = details
+
+    def __str__(self):
+        return ("Can not add if %s to bridge %s: %s" %
+                (self.ifname, self.brname, self.details))
+
+
+class HwAddrSetError(NetError):
+    def __init__(self, ifname, mac):
+        NetError.__init__(self, ifname, mac)
+        self.ifname = ifname
+        self.mac = mac
+
+    def __str__(self):
+        return "Can not set mac %s to interface %s" % (self.mac, self.ifname)
+
+
+class HwAddrGetError(NetError):
+    def __init__(self, ifname):
+        NetError.__init__(self, ifname)
+        self.ifname = ifname
+
+    def __str__(self):
+        return "Can not get mac of interface %s" % self.ifname
+
+
 class Env(UserDict.IterableUserDict):
     """
     A dict-like object containing global objects used by tests.
@@ -2311,3 +2400,126 @@  def install_host_kernel(job, params):
     else:
         logging.info('Chose %s, using the current kernel for the host',
                      install_type)
+
+
+def if_nametoindex(ifname):
+    """
+    Map an interface name into its corresponding index.
+    Returns 0 on error, as 0 is not a valid index
+
+    @param ifname: interface name
+    """
+    index = 0
+    ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+    ifr = struct.pack("16si", ifname, 0)
+    r = fcntl.ioctl(ctrl_sock, SIOCGIFINDEX, ifr)
+    index = struct.unpack("16si", r)[1]
+    ctrl_sock.close()
+    return index
+
+
+def vnet_hdr_probe(tapfd):
+    """
+    Check if the IFF_VNET_HDR is support by tun.
+
+    @param tapfd: the file descriptor of /dev/net/tun
+    """
+    u = struct.pack("I", 0)
+    try:
+        r = fcntl.ioctl(tapfd, TUNGETFEATURES, u)
+    except OverflowError:
+        return False
+    flags = struct.unpack("I", r)[0]
+    if flags & IFF_VNET_HDR:
+        return True
+    else:
+        return False
+
+
+def open_tap(devname, ifname, vnet_hdr=True):
+    """
+    Open a tap device and returns its file descriptor which is used by
+    fd=<fd> parameter of qemu-kvm.
+
+    @param ifname: TAP interface name
+    @param vnet_hdr: Whether enable the vnet header
+    """
+    try:
+        tapfd = os.open(devname, os.O_RDWR)
+    except OSError, e:
+        raise TAPModuleError(devname, "open", e)
+    flags = IFF_TAP | IFF_NO_PI
+    if vnet_hdr and vnet_hdr_probe(tapfd):
+        flags |= IFF_VNET_HDR
+
+    ifr = struct.pack("16sh", ifname, flags)
+    try:
+        r = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
+    except IOError, details:
+        raise TAPCreationError(ifname, details)
+    ifname = struct.unpack("16sh", r)[0].strip("\x00")
+    return tapfd
+
+
+def add_to_bridge(ifname, brname):
+    """
+    Add a TAP device to bridge
+
+    @param ifname: Name of TAP device
+    @param brname: Name of the bridge
+    """
+    ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+    index = if_nametoindex(ifname)
+    if index == 0:
+        raise TAPNotExistError(ifname)
+    ifr = struct.pack("16si", brname, index)
+    try:
+        r = fcntl.ioctl(ctrl_sock, SIOCBRADDIF, ifr)
+    except IOError, details:
+        raise BRAddIfError(ifname, brname, details)
+    ctrl_sock.close()
+
+
+def bring_up_ifname(ifname):
+    """
+    Bring up an interface
+
+    @param ifname: Name of the interface
+    """
+    ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+    ifr = struct.pack("16si", ifname, IFF_UP)
+    try:
+        fcntl.ioctl(ctrl_sock, SIOCSIFFLAGS, ifr)
+    except IOError:
+        raise TAPBringUpError(ifname)
+    ctrl_sock.close()
+
+
+def if_set_macaddress(ifname, mac):
+    """
+    Set the mac address for an interface
+
+    @param ifname: Name of the interface
+    @mac: Mac address
+    """
+    ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+
+    ifr = struct.pack("256s", ifname)
+    try:
+        mac_dev = fcntl.ioctl(ctrl_sock, SIOCGIFHWADDR, ifr)[18:24]
+        mac_dev = ":".join(["%02x" % ord(m) for m in mac_dev])
+    except IOError, e:
+        raise HwAddrGetError(ifname)
+
+    if mac_dev.lower() == mac.lower():
+        return
+
+    ifr = struct.pack("16sH14s", ifname, 1,
+                      "".join([chr(int(m, 16)) for m in mac.split(":")]))
+    try:
+        fcntl.ioctl(ctrl_sock, SIOCSIFHWADDR, ifr)
+    except IOError, e:
+        logging.info(e)
+        raise HwAddrSetError(ifname, mac)
+    ctrl_sock.close()
+