136 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # SPDX-License-Identifier: GPL-2.0
 | |
| 
 | |
| import errno
 | |
| import json
 | |
| import os
 | |
| import random
 | |
| import re
 | |
| import time
 | |
| from .utils import cmd, ip
 | |
| 
 | |
| 
 | |
| class NetdevSim:
 | |
|     """
 | |
|     Class for netdevsim netdevice and its attributes.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, nsimdev, port_index, ifname, ns=None):
 | |
|         # In case udev renamed the netdev to according to new schema,
 | |
|         # check if the name matches the port_index.
 | |
|         nsimnamere = re.compile(r"eni\d+np(\d+)")
 | |
|         match = nsimnamere.match(ifname)
 | |
|         if match and int(match.groups()[0]) != port_index + 1:
 | |
|             raise Exception("netdevice name mismatches the expected one")
 | |
| 
 | |
|         self.ifname = ifname
 | |
|         self.nsimdev = nsimdev
 | |
|         self.port_index = port_index
 | |
|         self.ns = ns
 | |
|         self.dfs_dir = "%s/ports/%u/" % (nsimdev.dfs_dir, port_index)
 | |
|         ret = ip("-j link show dev %s" % ifname, ns=ns)
 | |
|         self.dev = json.loads(ret.stdout)[0]
 | |
|         self.ifindex = self.dev["ifindex"]
 | |
| 
 | |
|     def dfs_write(self, path, val):
 | |
|         self.nsimdev.dfs_write(f'ports/{self.port_index}/' + path, val)
 | |
| 
 | |
| 
 | |
| class NetdevSimDev:
 | |
|     """
 | |
|     Class for netdevsim bus device and its attributes.
 | |
|     """
 | |
|     @staticmethod
 | |
|     def ctrl_write(path, val):
 | |
|         fullpath = os.path.join("/sys/bus/netdevsim/", path)
 | |
|         with open(fullpath, "w") as f:
 | |
|             f.write(val)
 | |
| 
 | |
|     def dfs_write(self, path, val):
 | |
|         fullpath = os.path.join(f"/sys/kernel/debug/netdevsim/netdevsim{self.addr}/", path)
 | |
|         with open(fullpath, "w") as f:
 | |
|             f.write(val)
 | |
| 
 | |
|     def __init__(self, port_count=1, queue_count=1, ns=None):
 | |
|         # nsim will spawn in init_net, we'll set to actual ns once we switch it there
 | |
|         self.ns = None
 | |
| 
 | |
|         if not os.path.exists("/sys/bus/netdevsim"):
 | |
|             cmd("modprobe netdevsim")
 | |
| 
 | |
|         addr = random.randrange(1 << 15)
 | |
|         while True:
 | |
|             try:
 | |
|                 self.ctrl_write("new_device", "%u %u %u" % (addr, port_count, queue_count))
 | |
|             except OSError as e:
 | |
|                 if e.errno == errno.ENOSPC:
 | |
|                     addr = random.randrange(1 << 15)
 | |
|                     continue
 | |
|                 raise e
 | |
|             break
 | |
|         self.addr = addr
 | |
| 
 | |
|         # As probe of netdevsim device might happen from a workqueue,
 | |
|         # so wait here until all netdevs appear.
 | |
|         self.wait_for_netdevs(port_count)
 | |
| 
 | |
|         if ns:
 | |
|             cmd(f"devlink dev reload netdevsim/netdevsim{addr} netns {ns.name}")
 | |
|             self.ns = ns
 | |
| 
 | |
|         cmd("udevadm settle", ns=self.ns)
 | |
|         ifnames = self.get_ifnames()
 | |
| 
 | |
|         self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr
 | |
| 
 | |
|         self.nsims = []
 | |
|         for port_index in range(port_count):
 | |
|             self.nsims.append(self._make_port(port_index, ifnames[port_index]))
 | |
| 
 | |
|         self.removed = False
 | |
| 
 | |
|     def __enter__(self):
 | |
|         return self
 | |
| 
 | |
|     def __exit__(self, ex_type, ex_value, ex_tb):
 | |
|         """
 | |
|         __exit__ gets called at the end of a "with" block.
 | |
|         """
 | |
|         self.remove()
 | |
| 
 | |
|     def _make_port(self, port_index, ifname):
 | |
|         return NetdevSim(self, port_index, ifname, self.ns)
 | |
| 
 | |
|     def get_ifnames(self):
 | |
|         ifnames = []
 | |
|         listdir = cmd(f"ls /sys/bus/netdevsim/devices/netdevsim{self.addr}/net/",
 | |
|                       ns=self.ns).stdout.split()
 | |
|         for ifname in listdir:
 | |
|             ifnames.append(ifname)
 | |
|         ifnames.sort()
 | |
|         return ifnames
 | |
| 
 | |
|     def wait_for_netdevs(self, port_count):
 | |
|         timeout = 5
 | |
|         timeout_start = time.time()
 | |
| 
 | |
|         while True:
 | |
|             try:
 | |
|                 ifnames = self.get_ifnames()
 | |
|             except FileNotFoundError as e:
 | |
|                 ifnames = []
 | |
|             if len(ifnames) == port_count:
 | |
|                 break
 | |
|             if time.time() < timeout_start + timeout:
 | |
|                 continue
 | |
|             raise Exception("netdevices did not appear within timeout")
 | |
| 
 | |
|     def remove(self):
 | |
|         if not self.removed:
 | |
|             self.ctrl_write("del_device", "%u" % (self.addr, ))
 | |
|             self.removed = True
 | |
| 
 | |
|     def remove_nsim(self, nsim):
 | |
|         self.nsims.remove(nsim)
 | |
|         self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ),
 | |
|                         "%u" % (nsim.port_index, ))
 |