Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'ynl-tests'

Jakub Kicinski says:

====================
selftests: net: groundwork for YNL-based tests

Currently the options for writing networking tests are C, bash or
some mix of the two. YAML/Netlink gives us the ability to easily
interface with Netlink in higher level laguages. In particular,
there is a Python library already available in tree, under tools/net.
Add the scaffolding which allows writing tests using this library.

The "scaffolding" is needed because the library lives under
tools/net and uses YAML files from under Documentation/.
So we need a small amount of glue code to find those things
and add them to TEST_FILES.

This series adds both a basic SW sanity test and driver
test which can be run against netdevsim or a real device.
When I develop core code I usually test with netdevsim,
then a real device, and then a backport to Meta's kernel.
Because of the lack of integration, until now I had
to throw away the (YNL-based) test script and netdevsim code.

Running tests in tree directly:

$ ./tools/testing/selftests/net/nl_netdev.py
KTAP version 1
1..2
ok 1 nl_netdev.empty_check
ok 2 nl_netdev.lo_check
# Totals: pass:2 fail:0 xfail:0 xpass:0 skip:0 error:0

in tree via make:

$ make -C tools/testing/selftests/ TARGETS=net \
TEST_PROGS=nl_netdev.py TEST_GEN_PROGS="" run_tests
[ ... ]

and installed externally, all seem to work:

$ make -C tools/testing/selftests/ TARGETS=net \
install INSTALL_PATH=/tmp/ksft-net
$ /tmp/ksft-net/run_kselftest.sh -t net:nl_netdev.py
[ ... ]

For driver tests I followed the lead of net/forwarding and
get the device name from env and/or a config file.

v3:
- fix up netdevsim C
- various small nits in other patches (see changelog in patches)
v2: https://lore.kernel.org/all/20240403023426.1762996-1-kuba@kernel.org/
- don't add to TARGETS, create a deperate variable with deps
- support and use with
- support and use passing arguments to tests
v1: https://lore.kernel.org/all/20240402010520.1209517-1-kuba@kernel.org/
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+617 -1
+11
drivers/net/netdevsim/ethtool.c
··· 140 140 return 0; 141 141 } 142 142 143 + static void 144 + nsim_get_fec_stats(struct net_device *dev, struct ethtool_fec_stats *fec_stats) 145 + { 146 + fec_stats->corrected_blocks.total = 123; 147 + fec_stats->uncorrectable_blocks.total = 4; 148 + } 149 + 143 150 static int nsim_get_ts_info(struct net_device *dev, 144 151 struct ethtool_ts_info *info) 145 152 { ··· 170 163 .set_channels = nsim_set_channels, 171 164 .get_fecparam = nsim_get_fecparam, 172 165 .set_fecparam = nsim_set_fecparam, 166 + .get_fec_stats = nsim_get_fec_stats, 173 167 .get_ts_info = nsim_get_ts_info, 174 168 }; 175 169 ··· 189 181 ns->netdev->ethtool_ops = &nsim_ethtool_ops; 190 182 191 183 nsim_ethtool_ring_init(ns); 184 + 185 + ns->ethtool.pauseparam.report_stats_rx = true; 186 + ns->ethtool.pauseparam.report_stats_tx = true; 192 187 193 188 ns->ethtool.fec.fec = ETHTOOL_FEC_NONE; 194 189 ns->ethtool.fec.active_fec = ETHTOOL_FEC_NONE;
+49
drivers/net/netdevsim/netdev.c
··· 19 19 #include <linux/module.h> 20 20 #include <linux/netdevice.h> 21 21 #include <linux/slab.h> 22 + #include <net/netdev_queues.h> 22 23 #include <net/netlink.h> 23 24 #include <net/pkt_cls.h> 24 25 #include <net/rtnetlink.h> ··· 331 330 .ndo_set_features = nsim_set_features, 332 331 }; 333 332 333 + /* We don't have true per-queue stats, yet, so do some random fakery here. 334 + * Only report stuff for queue 0. 335 + */ 336 + static void nsim_get_queue_stats_rx(struct net_device *dev, int idx, 337 + struct netdev_queue_stats_rx *stats) 338 + { 339 + struct rtnl_link_stats64 rtstats = {}; 340 + 341 + if (!idx) 342 + nsim_get_stats64(dev, &rtstats); 343 + 344 + stats->packets = rtstats.rx_packets - !!rtstats.rx_packets; 345 + stats->bytes = rtstats.rx_bytes; 346 + } 347 + 348 + static void nsim_get_queue_stats_tx(struct net_device *dev, int idx, 349 + struct netdev_queue_stats_tx *stats) 350 + { 351 + struct rtnl_link_stats64 rtstats = {}; 352 + 353 + if (!idx) 354 + nsim_get_stats64(dev, &rtstats); 355 + 356 + stats->packets = rtstats.tx_packets - !!rtstats.tx_packets; 357 + stats->bytes = rtstats.tx_bytes; 358 + } 359 + 360 + static void nsim_get_base_stats(struct net_device *dev, 361 + struct netdev_queue_stats_rx *rx, 362 + struct netdev_queue_stats_tx *tx) 363 + { 364 + struct rtnl_link_stats64 rtstats = {}; 365 + 366 + nsim_get_stats64(dev, &rtstats); 367 + 368 + rx->packets = !!rtstats.rx_packets; 369 + rx->bytes = 0; 370 + tx->packets = !!rtstats.tx_packets; 371 + tx->bytes = 0; 372 + } 373 + 374 + static const struct netdev_stat_ops nsim_stat_ops = { 375 + .get_queue_stats_tx = nsim_get_queue_stats_tx, 376 + .get_queue_stats_rx = nsim_get_queue_stats_rx, 377 + .get_base_stats = nsim_get_base_stats, 378 + }; 379 + 334 380 static void nsim_setup(struct net_device *dev) 335 381 { 336 382 ether_setup(dev); ··· 408 360 409 361 ns->phc = phc; 410 362 ns->netdev->netdev_ops = &nsim_netdev_ops; 363 + ns->netdev->stat_ops = &nsim_stat_ops; 411 364 412 365 err = nsim_udp_tunnels_info_create(ns->nsim_dev, ns->netdev); 413 366 if (err)
+9 -1
tools/testing/selftests/Makefile
··· 17 17 TARGETS += dmabuf-heaps 18 18 TARGETS += drivers/dma-buf 19 19 TARGETS += drivers/s390x/uvdevice 20 + TARGETS += drivers/net 20 21 TARGETS += drivers/net/bonding 21 22 TARGETS += drivers/net/team 22 23 TARGETS += dt ··· 116 115 117 116 TARGETS_HOTPLUG = cpu-hotplug 118 117 TARGETS_HOTPLUG += memory-hotplug 118 + 119 + # Networking tests want the net/lib target, include it automatically 120 + ifneq ($(filter net drivers/net,$(TARGETS)),) 121 + ifeq ($(filter net/lib,$(TARGETS)),) 122 + INSTALL_DEP_TARGETS := net/lib 123 + endif 124 + endif 119 125 120 126 # User can optionally provide a TARGETS skiplist. By default we skip 121 127 # BPF since it has cutting edge build time dependencies which require ··· 253 245 install -m 744 run_kselftest.sh $(INSTALL_PATH)/ 254 246 rm -f $(TEST_LIST) 255 247 @ret=1; \ 256 - for TARGET in $(TARGETS); do \ 248 + for TARGET in $(TARGETS) $(INSTALL_DEP_TARGETS); do \ 257 249 BUILD_TARGET=$$BUILD/$$TARGET; \ 258 250 $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install \ 259 251 INSTALL_PATH=$(INSTALL_PATH)/$$TARGET \
+7
tools/testing/selftests/drivers/net/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + TEST_INCLUDES := $(wildcard lib/py/*.py) 4 + 5 + TEST_PROGS := stats.py 6 + 7 + include ../../lib.mk
+30
tools/testing/selftests/drivers/net/README.rst
··· 1 + Running tests 2 + ============= 3 + 4 + Tests are executed within kselftest framework like any other tests. 5 + By default tests execute against software drivers such as netdevsim. 6 + All tests must support running against a real device (SW-only tests 7 + should instead be placed in net/ or drivers/net/netdevsim, HW-only 8 + tests in drivers/net/hw). 9 + 10 + Set appropriate variables to point the tests at a real device. 11 + 12 + Variables 13 + ========= 14 + 15 + Variables can be set in the environment or by creating a net.config 16 + file in the same directory as this README file. Example:: 17 + 18 + $ NETIF=eth0 ./some_test.sh 19 + 20 + or:: 21 + 22 + $ cat tools/testing/selftests/drivers/net/net.config 23 + # Variable set in a file 24 + NETIF=eth0 25 + 26 + NETIF 27 + ~~~~~ 28 + 29 + Name of the netdevice against which the test should be executed. 30 + When empty or not set software devices will be used.
+17
tools/testing/selftests/drivers/net/lib/py/__init__.py
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + import sys 4 + from pathlib import Path 5 + 6 + KSFT_DIR = (Path(__file__).parent / "../../../..").resolve() 7 + 8 + try: 9 + sys.path.append(KSFT_DIR.as_posix()) 10 + from net.lib.py import * 11 + except ModuleNotFoundError as e: 12 + ksft_pr("Failed importing `net` library from kernel sources") 13 + ksft_pr(str(e)) 14 + ktap_result(True, comment="SKIP") 15 + sys.exit(4) 16 + 17 + from .env import *
+52
tools/testing/selftests/drivers/net/lib/py/env.py
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + import os 4 + import shlex 5 + from pathlib import Path 6 + from lib.py import ip 7 + from lib.py import NetdevSimDev 8 + 9 + class NetDrvEnv: 10 + def __init__(self, src_path): 11 + self._ns = None 12 + 13 + self.env = os.environ.copy() 14 + self._load_env_file(src_path) 15 + 16 + if 'NETIF' in self.env: 17 + self.dev = ip("link show dev " + self.env['NETIF'], json=True)[0] 18 + else: 19 + self._ns = NetdevSimDev() 20 + self.dev = self._ns.nsims[0].dev 21 + self.ifindex = self.dev['ifindex'] 22 + 23 + def __enter__(self): 24 + return self 25 + 26 + def __exit__(self, ex_type, ex_value, ex_tb): 27 + """ 28 + __exit__ gets called at the end of a "with" block. 29 + """ 30 + self.__del__() 31 + 32 + def __del__(self): 33 + if self._ns: 34 + self._ns.remove() 35 + self._ns = None 36 + 37 + def _load_env_file(self, src_path): 38 + src_dir = Path(src_path).parent.resolve() 39 + if not (src_dir / "net.config").exists(): 40 + return 41 + 42 + lexer = shlex.shlex(open((src_dir / "net.config").as_posix(), 'r').read()) 43 + k = None 44 + for token in lexer: 45 + if k is None: 46 + k = token 47 + self.env[k] = "" 48 + elif token == "=": 49 + pass 50 + else: 51 + self.env[k] = token 52 + k = None
+86
tools/testing/selftests/drivers/net/stats.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + from lib.py import ksft_run, ksft_in, ksft_true, KsftSkipEx, KsftXfailEx 5 + from lib.py import EthtoolFamily, NetdevFamily, RtnlFamily, NlError 6 + from lib.py import NetDrvEnv 7 + 8 + ethnl = EthtoolFamily() 9 + netfam = NetdevFamily() 10 + rtnl = RtnlFamily() 11 + 12 + 13 + def check_pause(cfg) -> None: 14 + global ethnl 15 + 16 + try: 17 + ethnl.pause_get({"header": {"dev-index": cfg.ifindex}}) 18 + except NlError as e: 19 + if e.error == 95: 20 + raise KsftXfailEx("pause not supported by the device") 21 + raise 22 + 23 + data = ethnl.pause_get({"header": {"dev-index": cfg.ifindex, 24 + "flags": {'stats'}}}) 25 + ksft_true(data['stats'], "driver does not report stats") 26 + 27 + 28 + def check_fec(cfg) -> None: 29 + global ethnl 30 + 31 + try: 32 + ethnl.fec_get({"header": {"dev-index": cfg.ifindex}}) 33 + except NlError as e: 34 + if e.error == 95: 35 + raise KsftXfailEx("FEC not supported by the device") 36 + raise 37 + 38 + data = ethnl.fec_get({"header": {"dev-index": cfg.ifindex, 39 + "flags": {'stats'}}}) 40 + ksft_true(data['stats'], "driver does not report stats") 41 + 42 + 43 + def pkt_byte_sum(cfg) -> None: 44 + global netfam, rtnl 45 + 46 + def get_qstat(test): 47 + global netfam 48 + stats = netfam.qstats_get({}, dump=True) 49 + if stats: 50 + for qs in stats: 51 + if qs["ifindex"]== test.ifindex: 52 + return qs 53 + 54 + qstat = get_qstat(cfg) 55 + if qstat is None: 56 + raise KsftSkipEx("qstats not supported by the device") 57 + 58 + for key in ['tx-packets', 'tx-bytes', 'rx-packets', 'rx-bytes']: 59 + ksft_in(key, qstat, "Drivers should always report basic keys") 60 + 61 + # Compare stats, rtnl stats and qstats must match, 62 + # but the interface may be up, so do a series of dumps 63 + # each time the more "recent" stats must be higher or same. 64 + def stat_cmp(rstat, qstat): 65 + for key in ['tx-packets', 'tx-bytes', 'rx-packets', 'rx-bytes']: 66 + if rstat[key] != qstat[key]: 67 + return rstat[key] - qstat[key] 68 + return 0 69 + 70 + for _ in range(10): 71 + rtstat = rtnl.getlink({"ifi-index": cfg.ifindex})['stats'] 72 + if stat_cmp(rtstat, qstat) < 0: 73 + raise Exception("RTNL stats are lower, fetched later") 74 + qstat = get_qstat(cfg) 75 + if stat_cmp(rtstat, qstat) > 0: 76 + raise Exception("Qstats are lower, fetched later") 77 + 78 + 79 + def main() -> None: 80 + with NetDrvEnv(__file__) as cfg: 81 + ksft_run([check_pause, check_fec, pkt_byte_sum], 82 + args=(cfg, )) 83 + 84 + 85 + if __name__ == "__main__": 86 + main()
+1
tools/testing/selftests/net/Makefile
··· 34 34 TEST_PROGS += cmsg_so_mark.sh 35 35 TEST_PROGS += cmsg_time.sh cmsg_ipv6.sh 36 36 TEST_PROGS += netns-name.sh 37 + TEST_PROGS += nl_netdev.py 37 38 TEST_PROGS += srv6_end_dt46_l3vpn_test.sh 38 39 TEST_PROGS += srv6_end_dt4_l3vpn_test.sh 39 40 TEST_PROGS += srv6_end_dt6_l3vpn_test.sh
+8
tools/testing/selftests/net/lib/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + TEST_FILES := ../../../../../Documentation/netlink/specs 4 + TEST_FILES += ../../../../net/ynl 5 + 6 + TEST_INCLUDES := $(wildcard py/*.py) 7 + 8 + include ../../lib.mk
+7
tools/testing/selftests/net/lib/py/__init__.py
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + from .consts import KSRC 4 + from .ksft import * 5 + from .nsim import * 6 + from .utils import * 7 + from .ynl import NlError, YnlFamily, EthtoolFamily, NetdevFamily, RtnlFamily
+9
tools/testing/selftests/net/lib/py/consts.py
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + import sys 4 + from pathlib import Path 5 + 6 + KSFT_DIR = (Path(__file__).parent / "../../..").resolve() 7 + KSRC = (Path(__file__).parent / "../../../../../..").resolve() 8 + 9 + KSFT_MAIN_NAME = Path(sys.argv[0]).with_suffix("").name
+96
tools/testing/selftests/net/lib/py/ksft.py
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + import builtins 4 + from .consts import KSFT_MAIN_NAME 5 + 6 + KSFT_RESULT = None 7 + 8 + 9 + class KsftSkipEx(Exception): 10 + pass 11 + 12 + 13 + class KsftXfailEx(Exception): 14 + pass 15 + 16 + 17 + def ksft_pr(*objs, **kwargs): 18 + print("#", *objs, **kwargs) 19 + 20 + 21 + def ksft_eq(a, b, comment=""): 22 + global KSFT_RESULT 23 + if a != b: 24 + KSFT_RESULT = False 25 + ksft_pr("Check failed", a, "!=", b, comment) 26 + 27 + 28 + def ksft_true(a, comment=""): 29 + global KSFT_RESULT 30 + if not a: 31 + KSFT_RESULT = False 32 + ksft_pr("Check failed", a, "does not eval to True", comment) 33 + 34 + 35 + def ksft_in(a, b, comment=""): 36 + global KSFT_RESULT 37 + if a not in b: 38 + KSFT_RESULT = False 39 + ksft_pr("Check failed", a, "not in", b, comment) 40 + 41 + 42 + def ksft_ge(a, b, comment=""): 43 + global KSFT_RESULT 44 + if a < b: 45 + KSFT_RESULT = False 46 + ksft_pr("Check failed", a, "<", b, comment) 47 + 48 + 49 + def ktap_result(ok, cnt=1, case="", comment=""): 50 + res = "" 51 + if not ok: 52 + res += "not " 53 + res += "ok " 54 + res += str(cnt) + " " 55 + res += KSFT_MAIN_NAME 56 + if case: 57 + res += "." + str(case.__name__) 58 + if comment: 59 + res += " # " + comment 60 + print(res) 61 + 62 + 63 + def ksft_run(cases, args=()): 64 + totals = {"pass": 0, "fail": 0, "skip": 0, "xfail": 0} 65 + 66 + print("KTAP version 1") 67 + print("1.." + str(len(cases))) 68 + 69 + global KSFT_RESULT 70 + cnt = 0 71 + for case in cases: 72 + KSFT_RESULT = True 73 + cnt += 1 74 + try: 75 + case(*args) 76 + except KsftSkipEx as e: 77 + ktap_result(True, cnt, case, comment="SKIP " + str(e)) 78 + totals['skip'] += 1 79 + continue 80 + except KsftXfailEx as e: 81 + ktap_result(True, cnt, case, comment="XFAIL " + str(e)) 82 + totals['xfail'] += 1 83 + continue 84 + except Exception as e: 85 + for line in str(e).split('\n'): 86 + ksft_pr("Exception|", line) 87 + ktap_result(False, cnt, case) 88 + totals['fail'] += 1 89 + continue 90 + 91 + ktap_result(KSFT_RESULT, cnt, case) 92 + totals['pass'] += 1 93 + 94 + print( 95 + f"# Totals: pass:{totals['pass']} fail:{totals['fail']} xfail:{totals['xfail']} xpass:0 skip:{totals['skip']} error:0" 96 + )
+115
tools/testing/selftests/net/lib/py/nsim.py
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + import json 4 + import os 5 + import random 6 + import re 7 + import time 8 + from .utils import cmd, ip 9 + 10 + 11 + class NetdevSim: 12 + """ 13 + Class for netdevsim netdevice and its attributes. 14 + """ 15 + 16 + def __init__(self, nsimdev, port_index, ifname, ns=None): 17 + # In case udev renamed the netdev to according to new schema, 18 + # check if the name matches the port_index. 19 + nsimnamere = re.compile(r"eni\d+np(\d+)") 20 + match = nsimnamere.match(ifname) 21 + if match and int(match.groups()[0]) != port_index + 1: 22 + raise Exception("netdevice name mismatches the expected one") 23 + 24 + self.nsimdev = nsimdev 25 + self.port_index = port_index 26 + ret = ip("-j link show dev %s" % ifname, ns=ns) 27 + self.dev = json.loads(ret.stdout)[0] 28 + 29 + def dfs_write(self, path, val): 30 + self.nsimdev.dfs_write(f'ports/{self.port_index}/' + path, val) 31 + 32 + 33 + class NetdevSimDev: 34 + """ 35 + Class for netdevsim bus device and its attributes. 36 + """ 37 + @staticmethod 38 + def ctrl_write(path, val): 39 + fullpath = os.path.join("/sys/bus/netdevsim/", path) 40 + with open(fullpath, "w") as f: 41 + f.write(val) 42 + 43 + def dfs_write(self, path, val): 44 + fullpath = os.path.join(f"/sys/kernel/debug/netdevsim/netdevsim{self.addr}/", path) 45 + with open(fullpath, "w") as f: 46 + f.write(val) 47 + 48 + def __init__(self, port_count=1, ns=None): 49 + # nsim will spawn in init_net, we'll set to actual ns once we switch it there 50 + self.ns = None 51 + 52 + if not os.path.exists("/sys/bus/netdevsim"): 53 + cmd("modprobe netdevsim") 54 + 55 + addr = random.randrange(1 << 15) 56 + while True: 57 + try: 58 + self.ctrl_write("new_device", "%u %u" % (addr, port_count)) 59 + except OSError as e: 60 + if e.errno == errno.ENOSPC: 61 + addr = random.randrange(1 << 15) 62 + continue 63 + raise e 64 + break 65 + self.addr = addr 66 + 67 + # As probe of netdevsim device might happen from a workqueue, 68 + # so wait here until all netdevs appear. 69 + self.wait_for_netdevs(port_count) 70 + 71 + if ns: 72 + cmd(f"devlink dev reload netdevsim/netdevsim{addr} netns {ns.name}") 73 + self.ns = ns 74 + 75 + cmd("udevadm settle", ns=self.ns) 76 + ifnames = self.get_ifnames() 77 + 78 + self.dfs_dir = "/sys/kernel/debug/netdevsim/netdevsim%u/" % addr 79 + 80 + self.nsims = [] 81 + for port_index in range(port_count): 82 + self.nsims.append(NetdevSim(self, port_index, ifnames[port_index], 83 + ns=ns)) 84 + 85 + def get_ifnames(self): 86 + ifnames = [] 87 + listdir = cmd(f"ls /sys/bus/netdevsim/devices/netdevsim{self.addr}/net/", 88 + ns=self.ns).stdout.split() 89 + for ifname in listdir: 90 + ifnames.append(ifname) 91 + ifnames.sort() 92 + return ifnames 93 + 94 + def wait_for_netdevs(self, port_count): 95 + timeout = 5 96 + timeout_start = time.time() 97 + 98 + while True: 99 + try: 100 + ifnames = self.get_ifnames() 101 + except FileNotFoundError as e: 102 + ifnames = [] 103 + if len(ifnames) == port_count: 104 + break 105 + if time.time() < timeout_start + timeout: 106 + continue 107 + raise Exception("netdevices did not appear within timeout") 108 + 109 + def remove(self): 110 + self.ctrl_write("del_device", "%u" % (self.addr, )) 111 + 112 + def remove_nsim(self, nsim): 113 + self.nsims.remove(nsim) 114 + self.ctrl_write("devices/netdevsim%u/del_port" % (self.addr, ), 115 + "%u" % (nsim.port_index, ))
+47
tools/testing/selftests/net/lib/py/utils.py
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + import json as _json 4 + import subprocess 5 + 6 + class cmd: 7 + def __init__(self, comm, shell=True, fail=True, ns=None, background=False): 8 + if ns: 9 + if isinstance(ns, NetNS): 10 + ns = ns.name 11 + comm = f'ip netns exec {ns} ' + comm 12 + 13 + self.stdout = None 14 + self.stderr = None 15 + self.ret = None 16 + 17 + self.comm = comm 18 + self.proc = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, 19 + stderr=subprocess.PIPE) 20 + if not background: 21 + self.process(terminate=False, fail=fail) 22 + 23 + def process(self, terminate=True, fail=None): 24 + if terminate: 25 + self.proc.terminate() 26 + stdout, stderr = self.proc.communicate() 27 + self.stdout = stdout.decode("utf-8") 28 + self.stderr = stderr.decode("utf-8") 29 + self.proc.stdout.close() 30 + self.proc.stderr.close() 31 + self.ret = self.proc.returncode 32 + 33 + if self.proc.returncode != 0 and fail: 34 + if len(stderr) > 0 and stderr[-1] == "\n": 35 + stderr = stderr[:-1] 36 + raise Exception("Command failed: %s\n%s" % (self.proc.args, stderr)) 37 + 38 + 39 + def ip(args, json=None, ns=None): 40 + cmd_str = "ip " 41 + if json: 42 + cmd_str += '-j ' 43 + cmd_str += args 44 + cmd_obj = cmd(cmd_str, ns=ns) 45 + if json: 46 + return _json.loads(cmd_obj.stdout) 47 + return cmd_obj
+49
tools/testing/selftests/net/lib/py/ynl.py
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + import sys 4 + from pathlib import Path 5 + from .consts import KSRC, KSFT_DIR 6 + from .ksft import ksft_pr, ktap_result 7 + 8 + # Resolve paths 9 + try: 10 + if (KSFT_DIR / "kselftest-list.txt").exists(): 11 + # Running in "installed" selftests 12 + tools_full_path = KSFT_DIR 13 + SPEC_PATH = KSFT_DIR / "net/lib/specs" 14 + 15 + sys.path.append(tools_full_path.as_posix()) 16 + from net.lib.ynl.lib import YnlFamily, NlError 17 + else: 18 + # Running in tree 19 + tools_full_path = KSRC / "tools" 20 + SPEC_PATH = KSRC / "Documentation/netlink/specs" 21 + 22 + sys.path.append(tools_full_path.as_posix()) 23 + from net.ynl.lib import YnlFamily, NlError 24 + except ModuleNotFoundError as e: 25 + ksft_pr("Failed importing `ynl` library from kernel sources") 26 + ksft_pr(str(e)) 27 + ktap_result(True, comment="SKIP") 28 + sys.exit(4) 29 + 30 + # 31 + # Wrapper classes, loading the right specs 32 + # Set schema='' to avoid jsonschema validation, it's slow 33 + # 34 + class EthtoolFamily(YnlFamily): 35 + def __init__(self): 36 + super().__init__((SPEC_PATH / Path('ethtool.yaml')).as_posix(), 37 + schema='') 38 + 39 + 40 + class RtnlFamily(YnlFamily): 41 + def __init__(self): 42 + super().__init__((SPEC_PATH / Path('rt_link.yaml')).as_posix(), 43 + schema='') 44 + 45 + 46 + class NetdevFamily(YnlFamily): 47 + def __init__(self): 48 + super().__init__((SPEC_PATH / Path('netdev.yaml')).as_posix(), 49 + schema='')
+24
tools/testing/selftests/net/nl_netdev.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + from lib.py import ksft_run, ksft_pr, ksft_eq, ksft_ge, NetdevFamily 5 + 6 + 7 + def empty_check(nf) -> None: 8 + devs = nf.dev_get({}, dump=True) 9 + ksft_ge(len(devs), 1) 10 + 11 + 12 + def lo_check(nf) -> None: 13 + lo_info = nf.dev_get({"ifindex": 1}) 14 + ksft_eq(len(lo_info['xdp-features']), 0) 15 + ksft_eq(len(lo_info['xdp-rx-metadata-features']), 0) 16 + 17 + 18 + def main() -> None: 19 + nf = NetdevFamily() 20 + ksft_run([empty_check, lo_check], args=(nf, )) 21 + 22 + 23 + if __name__ == "__main__": 24 + main()