From: Yunping Zheng <[email protected]>
This patch support using macvtap nic in test, when using macvtap nic
better have a switch support macvtap, if not, need have a host with
two nic device when using this patch.
if you want to use macvtap in your test, just configure
"nettype = macvtap"
in your configure file.
Signed-off-by: Yunping Zheng <[email protected]>
---
virttest/arch.py | 22 +++-
virttest/qemu_vm.py | 44 ++++---
virttest/utils_net.py | 310 +++++++++++++++++++++++++++++++++++++++++++++++++-
virttest/virt_vm.py | 5 +-
4 files changed, 355 insertions(+), 26 deletions(-)
diff --git a/virttest/arch.py b/virttest/arch.py
index 5bfbbcb..a17e73e 100644
--- a/virttest/arch.py
+++ b/virttest/arch.py
@@ -7,7 +7,12 @@ if ARCH == "ppc64":
# From include/linux/sockios.h
SIOCSIFHWADDR = 0x8924
SIOCGIFHWADDR = 0x8927
+ SIOCGIFFLAGS = 0x8913
SIOCSIFFLAGS = 0x8914
+ SIOCGIFADDR = 0x8915
+ SIOCSIFADDR = 0x8916
+ SIOCGIFNETMASK = 0x891B
+ SIOCSIFNETMASK = 0x891C
SIOCGIFINDEX = 0x8933
SIOCBRADDIF = 0x89a2
SIOCBRDELIF = 0x89a3
@@ -22,12 +27,17 @@ if ARCH == "ppc64":
IFF_UP = 0x1
else:
# From include/linux/sockios.h
- SIOCSIFHWADDR = 0x8924
- SIOCGIFHWADDR = 0x8927
- SIOCSIFFLAGS = 0x8914
- SIOCGIFINDEX = 0x8933
- SIOCBRADDIF = 0x89a2
- SIOCBRDELIF = 0x89a3
+ SIOCSIFHWADDR = 0x8924
+ SIOCGIFHWADDR = 0x8927
+ SIOCGIFFLAGS = 0x8913
+ SIOCSIFFLAGS = 0x8914
+ SIOCGIFADDR = 0x8915
+ SIOCSIFADDR = 0x8916
+ SIOCGIFNETMASK = 0x891B
+ SIOCSIFNETMASK = 0x891C
+ SIOCGIFINDEX = 0x8933
+ SIOCBRADDIF = 0x89a2
+ SIOCBRDELIF = 0x89a3
# From linux/include/linux/if_tun.h
TUNSETIFF = 0x400454ca
TUNGETIFF = 0x800454d2
diff --git a/virttest/qemu_vm.py b/virttest/qemu_vm.py
index 4906ced..c9189c4 100644
--- a/virttest/qemu_vm.py
+++ b/virttest/qemu_vm.py
@@ -10,7 +10,6 @@ from autotest.client import utils
import utils_misc, virt_vm, test_setup, storage, qemu_monitor, aexpect
import qemu_virtio_port, remote, data_dir, utils_net
-
class QemuSegFaultError(virt_vm.VMError):
def __init__(self, crash_message):
virt_vm.VMError.__init__(self, crash_message)
@@ -638,6 +637,8 @@ class VM(virt_vm.BaseVM):
mode = 'tap'
elif nettype == 'network':
mode = 'tap'
+ elif nettype == 'macvtap':
+ mode = 'tap'
elif nettype == 'user':
mode = 'user'
else:
@@ -1556,20 +1557,29 @@ class VM(virt_vm.BaseVM):
def _nic_tap_add_helper(self, nic):
- nic.tapfd = str(utils_net.open_tap("/dev/net/tun", nic.ifname,
- vnet_hdr=True))
- logging.debug("Adding VM %s NIC ifname %s to bridge %s", self.name,
- nic.ifname, nic.netdst)
- if nic.nettype == 'bridge':
- utils_net.add_to_bridge(nic.ifname, nic.netdst)
- utils_net.bring_up_ifname(nic.ifname)
+ if nic.nettype == 'macvtap':
+ logging.info("Adding macvtap ifname: %s" , nic.ifname)
+ utils_net.add_nic_macvtap(nic)
+ else:
+ nic.tapfd = str(utils_net.open_tap("/dev/net/tun", nic.ifname,
+ vnet_hdr=True))
+ logging.debug("Adding VM %s NIC ifname %s to bridge %s",
+ self.name, nic.ifname, nic.netdst)
+ if nic.nettype == 'bridge':
+ utils_net.add_to_bridge(nic.ifname, nic.netdst)
+ utils_net.bring_up_ifname(nic.ifname)
def _nic_tap_remove_helper(self, nic):
- logging.debug("Removing VM %s NIC ifname %s from bridge %s", self.name,
- nic.ifname, nic.netdst)
try:
- os.close(int(nic.tapfd))
+ if nic.nettype == 'macvtap':
+ logging.info("Remove macvtap ifname %s", nic.ifname)
+ tap = utils_net.Macvtap(nic.ifname)
+ tap.delete()
+ else:
+ logging.debug("Removing VM %s NIC ifname %s from bridge %s",
+ self.name, nic.ifname, nic.netdst)
+ os.close(int(nic.tapfd))
except TypeError:
pass
@@ -1703,7 +1713,9 @@ class VM(virt_vm.BaseVM):
logging.debug("Copying mac for nic %s from VM %s"
% (nic.nic_name, mac_source.name))
nic.mac = mac_source.get_mac_address(nic.nic_name)
- if nic.nettype == 'bridge' or nic.nettype == 'network':
+ if (nic.nettype == 'bridge'
+ or nic.nettype == 'network'
+ or nic.nettype == "macvtap"):
self._nic_tap_add_helper(nic)
elif nic.nettype == 'user':
logging.info("Assuming dependencies met for "
@@ -2149,6 +2161,10 @@ class VM(virt_vm.BaseVM):
self.process.get_pid())
finally:
+ for nic in self.virtnet:
+ if nic.nettype == 'macvtap':
+ tap = utils_net.Macvtap(nic.ifname)
+ tap.delete()
self._cleanup(free_mac_addresses)
@@ -2324,7 +2340,7 @@ class VM(virt_vm.BaseVM):
nic.set_if_none('netdev_id', utils_misc.generate_random_id())
nic.set_if_none('ifname', self.virtnet.generate_ifname(nic_index))
nic.set_if_none('nettype', 'bridge')
- if nic.nettype == 'bridge': # implies tap
+ if nic.nettype == 'bridge' or nic.nettype == 'macvtap': # implies tap
# destination is required, hard-code reasonable default if unset
# nic.set_if_none('netdst', 'virbr0')
# tapfd allocated/set in activate because requires system resources
@@ -2406,6 +2422,8 @@ class VM(virt_vm.BaseVM):
# assume this will puke if netdst unset
if not nic.netdst is None:
utils_net.add_to_bridge(nic.ifname, nic.netdst)
+ elif nic.nettype == 'macvtap':
+ Pass
elif nic.nettype == 'user':
attach_cmd += " user,id=%s" % nic.device_id
else: # unsupported nettype
diff --git a/virttest/utils_net.py b/virttest/utils_net.py
index 56a462d..9605f6d 100644
--- a/virttest/utils_net.py
+++ b/virttest/utils_net.py
@@ -1,9 +1,15 @@
import openvswitch, re, os, socket, fcntl, struct, logging, random
+import ctypes, math, time
import shelve, commands
-from autotest.client import utils
+from autotest.client import utils, os_dep
from autotest.client.shared import error
import propcan, utils_misc, arch
+SYSFS_NET_PATH = "/sys/class/net"
+PROCFS_NET_PATH = "/proc/net/dev"
+#globals
+sock = None
+sockfd =None
class NetError(Exception):
pass
@@ -175,6 +181,283 @@ class DbNoLockError(NetError):
return "Attempt made to access database with improper locking"
+def warp_init_del(func):
+ def new_func(*args, **argkw):
+ globals()["sock"] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ globals()["sockfd"] = globals()["sock"].fileno()
+ try:
+ return func(*args, **argkw)
+ finally:
+ globals()["sock"].close()
+ globals()["sock"] = None
+ globals()["sockfd"] = None
+ return new_func
+
+
+class Interface(object):
+ ''' Class representing a Linux network device. '''
+
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<%s %s at 0x%x>" % (self.__class__.__name__,
+ self.name, id(self))
+
+ @warp_init_del
+ def up(self):
+ '''
+ Bring up the bridge interface. Equivalent to ifconfig [iface] up.
+ '''
+
+ # Get existing device flags
+ ifreq = struct.pack('16sh', self.name, 0)
+ flags = struct.unpack('16sh',
+ fcntl.ioctl(sockfd, arch.SIOCGIFFLAGS, ifreq))[1]
+
+ # Set new flags
+ flags = flags | arch.IFF_UP
+ ifreq = struct.pack('16sh', self.name, flags)
+ fcntl.ioctl(sockfd, arch.SIOCSIFFLAGS, ifreq)
+
+ @warp_init_del
+ def down(self):
+ '''
+ Bring up the bridge interface. Equivalent to ifconfig [iface] down.
+ '''
+
+ # Get existing device flags
+ ifreq = struct.pack('16sh', self.name, 0)
+ flags = struct.unpack('16sh',
+ fcntl.ioctl(sockfd, arch.SIOCGIFFLAGS, ifreq))[1]
+
+ # Set new flags
+ flags = flags & ~arch.IFF_UP
+ ifreq = struct.pack('16sh', self.name, flags)
+ fcntl.ioctl(sockfd, arch.SIOCSIFFLAGS, ifreq)
+
+ @warp_init_del
+ def is_up(self):
+ '''
+ Return True if the interface is up, False otherwise.
+ '''
+ # Get existing device flags
+ ifreq = struct.pack('16sh', self.name, 0)
+ flags = struct.unpack('16sh',
+ fcntl.ioctl(sockfd, arch.SIOCGIFFLAGS, ifreq))[1]
+
+ # Set new flags
+ if flags & arch.IFF_UP:
+ return True
+ else:
+ return False
+
+ @warp_init_del
+ def get_mac(self):
+ '''
+ Obtain the device's mac address.
+ '''
+ ifreq = struct.pack('16sH14s', self.name, socket.AF_UNIX, '\x00'*14)
+ res = fcntl.ioctl(sockfd, arch.SIOCGIFHWADDR, ifreq)
+ address = struct.unpack('16sH14s', res)[2]
+ mac = struct.unpack('6B8x', address)
+
+ return ":".join(['%02X' % i for i in mac])
+
+ @warp_init_del
+ def set_mac(self, newmac):
+ '''
+ Set the device's mac address. Device must be down for this to
+ succeed.
+ '''
+ macbytes = [int(i, 16) for i in newmac.split(':')]
+ ifreq = struct.pack('16sH6B8x', self.name, socket.AF_UNIX, *macbytes)
+ fcntl.ioctl(sockfd, arch.SIOCSIFHWADDR, ifreq)
+
+ @warp_init_del
+ def get_ip(self):
+ """
+ Get ip address of this interface
+ """
+ ifreq = struct.pack('16sH14s', self.name, socket.AF_INET, '\x00'*14)
+ try:
+ res = fcntl.ioctl(sockfd, arch.SIOCGIFADDR, ifreq)
+ except IOError:
+ return None
+ ip = struct.unpack('16sH2x4s8x', res)[2]
+
+ return socket.inet_ntoa(ip)
+
+ @warp_init_del
+ def set_ip(self, newip):
+ """
+ Set the ip address of the interface
+ """
+ ipbytes = socket.inet_aton(newip)
+ ifreq = struct.pack('16sH2s4s8s', self.name,
+ socket.AF_INET, '\x00'*2, ipbytes, '\x00'*8)
+ fcntl.ioctl(sockfd, arch.SIOCSIFADDR, ifreq)
+
+ @warp_init_del
+ def get_netmask(self):
+ """
+ Get ip network netmask
+ """
+ ifreq = struct.pack('16sH14s', self.name, socket.AF_INET, '\x00'*14)
+ try:
+ res = fcntl.ioctl(sockfd, arch.SIOCGIFNETMASK, ifreq)
+ except IOError:
+ return 0
+ netmask = socket.ntohl(struct.unpack('16sH2xI8x', res)[2])
+
+ return 32 - int(math.log(ctypes.c_uint32(~netmask).value + 1, 2))
+
+ @warp_init_del
+ def set_netmask(self, netmask):
+ """
+ Set netmask
+ """
+ netmask = ctypes.c_uint32(~((2 ** (32 - netmask)) - 1)).value
+ nmbytes = socket.htonl(netmask)
+ ifreq = struct.pack('16sH2si8s', self.name,
+ socket.AF_INET, '\x00'*2, nmbytes, '\x00'*8)
+ fcntl.ioctl(sockfd, arch.SIOCSIFNETMASK, ifreq)
+
+ @warp_init_del
+ def get_index(self):
+ '''
+ Convert an interface name to an index value.
+ '''
+ ifreq = struct.pack('16si', self.name, 0)
+ res = fcntl.ioctl(sockfd, arch.SIOCGIFINDEX, ifreq)
+ return struct.unpack("16si", res)[1]
+
+ @warp_init_del
+ def get_stats(self):
+ """
+ Get the status information of the Interface
+ """
+ spl_re = re.compile("\s+")
+
+ fp = open(PROCFS_NET_PATH)
+ # Skip headers
+ fp.readline()
+ fp.readline()
+ while True:
+ data = fp.readline()
+ if not data:
+ return None
+
+ name, stats_str = data.split(":")
+ if name.strip() != self.name:
+ continue
+
+ stats = [int(a) for a in spl_re.split(stats_str.strip())]
+ break
+
+ titles = ["rx_bytes", "rx_packets", "rx_errs", "rx_drop", "rx_fifo",
+ "rx_frame", "rx_compressed", "rx_multicast", "tx_bytes",
+ "tx_packets", "tx_errs", "tx_drop", "tx_fifo", "tx_colls",
+ "tx_carrier", "tx_compressed"]
+ return dict(zip(titles, stats))
+
+ def is_brport(self):
+ """
+ Check Whether this Interface is a bridge port_to_br
+ """
+ path = os.path.join(SYSFS_NET_PATH, self.name)
+ if os.path.exists(os.path.join(path, "brport")):
+ return True
+ else:
+ return False
+
+
+class Macvtap(Interface):
+ """
+ class of macvtap, base Interface
+ """
+ def __init__(self, tapname=None):
+ if tapname == None:
+ self.tapname = "macvtap" + utils_misc.generate_random_id()
+ else:
+ self.tapname = tapname
+ Interface.__init__(self, self.tapname)
+
+ def get_tapname(self):
+ return self.tapname
+
+ def ip_link_ctl(self, parmas, ignore_status=False):
+ return utils.run(os_dep.command("ip"), timeout=10,
+ ignore_status=ignore_status, verbose=False,
+ args=parmas)
+
+ def create(self, device, mode="vepa"):
+ """
+ Create a macvtap device, only when the device is not exists
+ """
+ path = os.path.join(SYSFS_NET_PATH, self.tapname)
+ if os.path.exists(path):
+ return
+
+ self.ip_link_ctl(["link", "add", "link", device, "name",
+ self.tapname,"type", "macvtap", "mode", mode])
+
+ def delete(self):
+ path = os.path.join(SYSFS_NET_PATH, self.tapname)
+ if os.path.exists(path):
+ self.ip_link_ctl(["link", "delete", self.tapname])
+
+ def open(self):
+ device = "/dev/tap%s" % self.get_index()
+ try:
+ return os.open(device, os.O_RDWR)
+ except OSError, e:
+ raise TAPModuleError(device, "open", e)
+
+
+def add_nic_macvtap(nic, base_interface=None):
+ """
+ Add a macvtap nic, if you not have a macvtap switch in you env, run
+ this type case you need at least two nic on you host
+ If you not assign the base_interface will use the first physical interface
+ which is not a brport and up to create macvtap
+ """
+ tap_base_device = None
+
+ (dev_int, _) = get_net_if(div_phy_virt=True)
+ if not dev_int:
+ raise TAPCreationError("Cannot get physical interface on the host")
+
+ if base_interface:
+ if not base_interface in dev_int:
+ err_msg = "Macvtap must created base on a physical interface"
+ raise TAPCreationError(err_msg)
+ base_inter = Interface(base_interface)
+ if (not base_inter.is_brport()) and base_inter.is_up():
+ tap_base_device = base_interface
+ else:
+ for interface in dev_int:
+ base_inter = Interface(interface)
+ if base_inter.is_brport():
+ continue
+ if base_inter.is_up():
+ tap_base_device = interface
+ break
+
+ if not tap_base_device:
+ err_msg = "Not find a valid physical interface to create macvtap, "
+ err_msg += "make sure the interface is up and not belog to any bridge."
+ raise TAPCreationError(nic.ifname, err_msg)
+
+ tap = Macvtap(nic.ifname)
+ tap.create(tap_base_device)
+ tap.set_mac(nic.mac)
+ nic.tapfd = tap.open()
+ time.sleep(3)
+ tap.up()
+
+
+
class Bridge(object):
def get_structure(self):
"""
@@ -382,16 +665,33 @@ def local_runner_status(cmd, timeout=None):
return utils.run(cmd, verbose=False, timeout=timeout).exit_status
-def get_net_if(runner=None):
+def get_net_if(runner=None, div_phy_virt=False):
"""
- @param output: Output form ip link command.
+ @param runner: command runner.
+ @param div_phy_virt: if set true, will return a tuple division real
+ physical interface and virtual interface
@return: List of network interfaces.
"""
if runner is None:
runner = local_runner
cmd = "ip link"
result = runner(cmd)
- return re.findall("^\d+: (\S+?)[@:].*$", result, re.MULTILINE)
+ all_interfaces = re.findall("^\d+: (\S+?)[@:].*$", result, re.MULTILINE)
+
+ if div_phy_virt:
+ phy_interfaces = []
+ vir_interfaces = []
+ for d in all_interfaces:
+ path = os.path.join(SYSFS_NET_PATH, d)
+ if not os.path.isdir(path):
+ continue
+ if not os.path.exists(os.path.join(path, "device")):
+ vir_interfaces.append(d)
+ else:
+ phy_interfaces.append(d)
+ return (phy_interfaces, vir_interfaces)
+ else:
+ return all_interfaces
def get_net_if_addrs(if_name, runner=None):
@@ -590,6 +890,7 @@ def add_to_bridge(ifname, brname, ovs=None):
ovs.add_port(brname, ifname)
+
@__init_openvswitch
def del_from_bridge(ifname, brname, ovs=None):
"""
@@ -1477,7 +1778,6 @@ def get_ip_address_by_interface(ifname):
by using socket.inet_ntoa().
"""
- SIOCGIFADDR = 0x8915 # Get interface address <bits/ioctls.h>
mysocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
return socket.inet_ntoa(fcntl.ioctl(
diff --git a/virttest/virt_vm.py b/virttest/virt_vm.py
index b84e9e8..87d2ee7 100644
--- a/virttest/virt_vm.py
+++ b/virttest/virt_vm.py
@@ -538,7 +538,7 @@ class BaseVM(object):
"""
nic = self.virtnet[index]
# TODO: Determine port redirection in use w/o checking nettype
- if nic.nettype != 'bridge':
+ if not (nic.nettype == 'bridge' or nic.nettype == 'macvtap'):
return "localhost"
if not nic.has_key('mac') and self.params.get('vm_type') == 'libvirt':
# Look it up from xml
@@ -603,7 +603,8 @@ class BaseVM(object):
@raise VMPortNotRedirectedError: If an unredirected port is requested
in user mode
"""
- if self.virtnet[nic_index].nettype == "bridge":
+ nic_nettype = self.virtnet[nic_index].nettype
+ if nic_nettype == "bridge" or nic_nettype =="macvtap":
return port
else:
try:
--
1.7.11.7
_______________________________________________
Virt-test-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/virt-test-devel