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

Reply via email to