From 6d29ab896de31f2707b0f140f6339f7a30f52f54 Mon Sep 17 00:00:00 2001 From: Jan Tluka Date: Tue, 6 Aug 2024 15:46:30 +0200 Subject: [PATCH 1/7] Recipes/ENRT: split SRIOVDevices to standalone module Signed-off-by: Jan Tluka --- lnst/Recipes/ENRT/BaseSRIOVNetnsTcRecipe.py | 58 +-------------------- lnst/Recipes/ENRT/SRIOVDevices.py | 58 +++++++++++++++++++++ 2 files changed, 59 insertions(+), 57 deletions(-) create mode 100644 lnst/Recipes/ENRT/SRIOVDevices.py diff --git a/lnst/Recipes/ENRT/BaseSRIOVNetnsTcRecipe.py b/lnst/Recipes/ENRT/BaseSRIOVNetnsTcRecipe.py index ecb643cea..45465cee9 100644 --- a/lnst/Recipes/ENRT/BaseSRIOVNetnsTcRecipe.py +++ b/lnst/Recipes/ENRT/BaseSRIOVNetnsTcRecipe.py @@ -1,7 +1,4 @@ from collections.abc import Collection -from dataclasses import dataclass -from itertools import zip_longest -from typing import Optional from lnst.Common.Parameters import ( Param, @@ -11,11 +8,11 @@ from lnst.Common.IpAddress import interface_addresses from lnst.Controller import HostReq, DeviceReq, RecipeParam from lnst.Controller.NetNamespace import NetNamespace -from lnst.Devices import RemoteDevice from lnst.RecipeCommon.endpoints import EndpointPair, IPEndpoint from lnst.RecipeCommon.Perf.Recipe import RecipeConf from lnst.Recipes.ENRT.helpers import ip_endpoint_pairs from lnst.Recipes.ENRT.BaremetalEnrtRecipe import BaremetalEnrtRecipe +from lnst.Recipes.ENRT.SRIOVDevices import SRIOVDevices from lnst.RecipeCommon.Ping.PingEndpoints import PingEndpoints from lnst.Recipes.ENRT.BaseEnrtRecipe import EnrtConfiguration from lnst.Recipes.ENRT.ConfigMixins.OffloadSubConfigMixin import ( @@ -26,59 +23,6 @@ ) -@dataclass -class SRIOVDevices(): - """ - The class takes care of - 1. creating the vfs - 2. accessing the vfs by index or using next() - 3. accessing the vf representors by index or using next() - 3. accessing vf/vf representor pairs by index or using next() - - For example: - ``` - host.eth0.eswitch_mode = "switchdev" - sriov_devices = SRIOVDevices(host.eth0, 2) - - vfs = sriov_devices.vfs # all vf devices - vf_representors = sriov_devices.vf_reps # all vf representors - - vf0, vf_rep0 = sriov_devices[0] # vf/vf_rep of first virtual function - vf1, vf_rep1 = sriov_devices[1] # vf/vf_rep of second virtual function - - for vf, vf_rep in sriov_devices: # iteration over vf/vf_representor pairs - vf.up() - vf_rep.up() - ``` - """ - phys_dev: RemoteDevice - vfs: list[RemoteDevice] - vf_reps: Optional[list[RemoteDevice]] = None - - def __init__(self, phys_dev: RemoteDevice, number_of_vfs: int = 1): - self.phys_dev = phys_dev - phys_dev.up_and_wait() - self.vfs, self.vf_reps = phys_dev.create_vfs(number_of_vfs) - - for vf_index, vf in enumerate(self.vfs): - phys_dev.host.map_device(f"{phys_dev._id}_vf{vf_index}", {"ifname": vf.name}) - - for vf_rep_index, vf_rep in enumerate(self.vf_reps): - phys_dev.host.map_device(f"{phys_dev._id}_vf_rep{vf_index}", {"ifname": vf_rep.name}) - - def __iter__(self): - if self.vf_reps: - return zip(self.vfs, self.vf_reps, strict=True) - - return zip_longest(self.vfs, [None]) - - def __getitem__(self, key): - if self.vf_reps: - return self.vfs[key], self.vf_reps[key] - - return self.vfs[key], None - - class BaseSRIOVNetnsTcRecipe( CommonHWSubConfigMixin, OffloadSubConfigMixin, BaremetalEnrtRecipe ): diff --git a/lnst/Recipes/ENRT/SRIOVDevices.py b/lnst/Recipes/ENRT/SRIOVDevices.py new file mode 100644 index 000000000..41a86ea73 --- /dev/null +++ b/lnst/Recipes/ENRT/SRIOVDevices.py @@ -0,0 +1,58 @@ +from dataclasses import dataclass +from itertools import zip_longest +from typing import Optional + +from lnst.Devices import RemoteDevice + + +@dataclass +class SRIOVDevices(): + """ + The class takes care of + 1. creating the vfs + 2. accessing the vfs by index or using next() + 3. accessing the vf representors by index or using next() + 3. accessing vf/vf representor pairs by index or using next() + + For example: + ``` + host.eth0.eswitch_mode = "switchdev" + sriov_devices = SRIOVDevices(host.eth0, 2) + + vfs = sriov_devices.vfs # all vf devices + vf_representors = sriov_devices.vf_reps # all vf representors + + vf0, vf_rep0 = sriov_devices[0] # vf/vf_rep of first virtual function + vf1, vf_rep1 = sriov_devices[1] # vf/vf_rep of second virtual function + + for vf, vf_rep in sriov_devices: # iteration over vf/vf_representor pairs + vf.up() + vf_rep.up() + ``` + """ + phys_dev: RemoteDevice + vfs: list[RemoteDevice] + vf_reps: Optional[list[RemoteDevice]] = None + + def __init__(self, phys_dev: RemoteDevice, number_of_vfs: int = 1): + self.phys_dev = phys_dev + phys_dev.up_and_wait() + self.vfs, self.vf_reps = phys_dev.create_vfs(number_of_vfs) + + for vf_index, vf in enumerate(self.vfs): + phys_dev.host.map_device(f"{phys_dev._id}_vf{vf_index}", {"ifname": vf.name}) + + for vf_rep_index, vf_rep in enumerate(self.vf_reps): + phys_dev.host.map_device(f"{phys_dev._id}_vf_rep{vf_index}", {"ifname": vf_rep.name}) + + def __iter__(self): + if self.vf_reps: + return zip(self.vfs, self.vf_reps, strict=True) + + return zip_longest(self.vfs, [None]) + + def __getitem__(self, key): + if self.vf_reps: + return self.vfs[key], self.vf_reps[key] + + return self.vfs[key], None From b872c563929ea34c56730401e98bf6076071c2e0 Mon Sep 17 00:00:00 2001 From: Jan Tluka Date: Tue, 6 Aug 2024 15:48:39 +0200 Subject: [PATCH 2/7] Recipes/ENRT/SRIOVDevices.py: fix variable typo Signed-off-by: Jan Tluka --- lnst/Recipes/ENRT/SRIOVDevices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lnst/Recipes/ENRT/SRIOVDevices.py b/lnst/Recipes/ENRT/SRIOVDevices.py index 41a86ea73..ca0266f2b 100644 --- a/lnst/Recipes/ENRT/SRIOVDevices.py +++ b/lnst/Recipes/ENRT/SRIOVDevices.py @@ -43,7 +43,7 @@ def __init__(self, phys_dev: RemoteDevice, number_of_vfs: int = 1): phys_dev.host.map_device(f"{phys_dev._id}_vf{vf_index}", {"ifname": vf.name}) for vf_rep_index, vf_rep in enumerate(self.vf_reps): - phys_dev.host.map_device(f"{phys_dev._id}_vf_rep{vf_index}", {"ifname": vf_rep.name}) + phys_dev.host.map_device(f"{phys_dev._id}_vf_rep{vf_rep_index}", {"ifname": vf_rep.name}) def __iter__(self): if self.vf_reps: From fd720a94256acaa50552cdeb2849cacf3794a9bf Mon Sep 17 00:00:00 2001 From: Jan Tluka Date: Tue, 6 Aug 2024 16:10:38 +0200 Subject: [PATCH 3/7] Recipes/ENRT/SRIOVDevices.py: fix for unavailable vf_reps Signed-off-by: Jan Tluka --- lnst/Recipes/ENRT/SRIOVDevices.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lnst/Recipes/ENRT/SRIOVDevices.py b/lnst/Recipes/ENRT/SRIOVDevices.py index ca0266f2b..6630fa736 100644 --- a/lnst/Recipes/ENRT/SRIOVDevices.py +++ b/lnst/Recipes/ENRT/SRIOVDevices.py @@ -42,8 +42,9 @@ def __init__(self, phys_dev: RemoteDevice, number_of_vfs: int = 1): for vf_index, vf in enumerate(self.vfs): phys_dev.host.map_device(f"{phys_dev._id}_vf{vf_index}", {"ifname": vf.name}) - for vf_rep_index, vf_rep in enumerate(self.vf_reps): - phys_dev.host.map_device(f"{phys_dev._id}_vf_rep{vf_rep_index}", {"ifname": vf_rep.name}) + if self.vf_reps is not None: + for vf_rep_index, vf_rep in enumerate(self.vf_reps): + phys_dev.host.map_device(f"{phys_dev._id}_vf_rep{vf_rep_index}", {"ifname": vf_rep.name}) def __iter__(self): if self.vf_reps: From f5611edcaf9637b10ca0373e661a9eda44031e82 Mon Sep 17 00:00:00 2001 From: Jan Tluka Date: Tue, 6 Aug 2024 15:24:37 +0200 Subject: [PATCH 4/7] Recipes/ENRT: add UseVfsMixin Enables use of VFs in ENRT recipes without need of code modification. Signed-off-by: Jan Tluka --- lnst/Recipes/ENRT/UseVfsMixin.py | 52 ++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 lnst/Recipes/ENRT/UseVfsMixin.py diff --git a/lnst/Recipes/ENRT/UseVfsMixin.py b/lnst/Recipes/ENRT/UseVfsMixin.py new file mode 100644 index 000000000..f0c426715 --- /dev/null +++ b/lnst/Recipes/ENRT/UseVfsMixin.py @@ -0,0 +1,52 @@ +from lnst.Common.Parameters import BoolParam +from lnst.Controller.Requirements import DeviceReq +from lnst.Recipes.ENRT.SRIOVDevices import SRIOVDevices + + +class UseVfsMixin: + """ + Mixin allows any ENRT recipe to use virtual function (VF) interfaces + instead of physical interfaces defined by DeviceReq requirements. + + VF interfaces are created automatically and DeviceReq handles are replaced + with VF Device instances. This allows user to interact with the network + interfaces without additional changes to the code of recipe. + + There are some limitations, for example pause frames cannot be configured + since the VF do not support these. + """ + + use_vfs = BoolParam(default=False) + + def test_wide_configuration(self): + config = super().test_wide_configuration() + + if not self.params.use_vfs: + return config + + config.vf_config = {} + for host_key, host_req in self.req: + dev_names = [key for key, value in host_req if isinstance(value, DeviceReq)] + host = getattr(self.matched, host_key) + + # remap_pfs_to_vfs + for dev_name in dev_names: + dev = getattr(host, dev_name) + sriov_devices = SRIOVDevices(dev, 1) + vf_dev = sriov_devices.vfs[0] + host.map_device(dev_name, {"ifname": vf_dev.name}) + + host_config = config.vf_config.setdefault(host, []) + host_config.append(sriov_devices) + + return config + + def test_wide_deconfiguration(self, config): + if self.params.use_vfs: + for host, sriov_devices_list in config.vf_config.items(): + for sriov_devices in sriov_devices_list: + vf_dev = sriov_devices.vfs[0] + host.map_device(vf_dev._id, {"ifname": sriov_devices.phys_dev.name}) + sriov_devices.phys_dev.delete_vfs() + + super().test_wide_deconfiguration(config) From 6c349e80124c227c6f19edbce69b3df9809779eb Mon Sep 17 00:00:00 2001 From: Jan Tluka Date: Fri, 2 Feb 2024 13:41:37 +0100 Subject: [PATCH 5/7] Recipes/ENRT/BaremetalEnrtRecipe.py: inherit UseVfsMixin Signed-off-by: Jan Tluka --- lnst/Recipes/ENRT/BaremetalEnrtRecipe.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lnst/Recipes/ENRT/BaremetalEnrtRecipe.py b/lnst/Recipes/ENRT/BaremetalEnrtRecipe.py index e1bc89804..764d9c0dd 100644 --- a/lnst/Recipes/ENRT/BaremetalEnrtRecipe.py +++ b/lnst/Recipes/ENRT/BaremetalEnrtRecipe.py @@ -1,4 +1,5 @@ from lnst.Recipes.ENRT.BaseEnrtRecipe import BaseEnrtRecipe +from lnst.Recipes.ENRT.UseVfsMixin import UseVfsMixin from lnst.Recipes.ENRT.PerfTestMixins import CommonPerfTestTweakMixin from lnst.Recipes.ENRT.ConfigMixins.DisableTurboboostMixin import ( DisableTurboboostMixin, @@ -55,6 +56,7 @@ def disable_turboboost_host_list(self): class BaremetalEnrtRecipe( + UseVfsMixin, BaremetalEnrtCommonMixins, BaremetalEnrtMeasurementGenerators, BaseEnrtRecipe, From a87cb8a38cdfdbc0e0a50d8665059a9609f957fd Mon Sep 17 00:00:00 2001 From: Jan Tluka Date: Wed, 14 Aug 2024 11:59:45 +0200 Subject: [PATCH 6/7] Recipes/ENRT/UseVfsMixin: add generate_test_wide_description Signed-off-by: Jan Tluka --- lnst/Recipes/ENRT/UseVfsMixin.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lnst/Recipes/ENRT/UseVfsMixin.py b/lnst/Recipes/ENRT/UseVfsMixin.py index f0c426715..5fd5ea450 100644 --- a/lnst/Recipes/ENRT/UseVfsMixin.py +++ b/lnst/Recipes/ENRT/UseVfsMixin.py @@ -50,3 +50,16 @@ def test_wide_deconfiguration(self, config): sriov_devices.phys_dev.delete_vfs() super().test_wide_deconfiguration(config) + + def generate_test_wide_description(self, config): + description = super().generate_test_wide_description(config) + + if self.params.use_vfs: + description += [ + f"Using vf device {vf_dev.name} of pf {sriov_devices.phys_dev.name} for DeviceReq {host.hostid}.{vf_dev._id}" + for host, sriov_devices_list in config.vf_config.items() + for sriov_devices in sriov_devices_list + for vf_dev in sriov_devices.vfs + ] + + return description From 798c83cf7fcea85a29c97f855e0fbcd838a0bc91 Mon Sep 17 00:00:00 2001 From: Jan Tluka Date: Thu, 15 Aug 2024 10:36:14 +0200 Subject: [PATCH 7/7] Recipes/ENRT: call test_wide_configuration before creating devices To properly use the UseVfsMixin, the test_wide_configuration must be called before creating any devices, otherwise the PFs could be used as parent device for e.g. Bond or VlanDevice instead of VFs. Signed-off-by: Jan Tluka --- lnst/Recipes/ENRT/BondRecipe.py | 3 ++- lnst/Recipes/ENRT/TeamRecipe.py | 3 +-- lnst/Recipes/ENRT/TeamVsBondRecipe.py | 3 +-- lnst/Recipes/ENRT/VlansOverBondRecipe.py | 2 +- lnst/Recipes/ENRT/VlansOverTeamRecipe.py | 2 +- lnst/Recipes/ENRT/VlansRecipe.py | 3 +-- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lnst/Recipes/ENRT/BondRecipe.py b/lnst/Recipes/ENRT/BondRecipe.py index b6e12fc27..5e120d4b6 100644 --- a/lnst/Recipes/ENRT/BondRecipe.py +++ b/lnst/Recipes/ENRT/BondRecipe.py @@ -89,9 +89,10 @@ def test_wide_configuration(self): """ host1, host2 = self.matched.host1, self.matched.host2 + config = super().test_wide_configuration() + host1.bond0 = BondDevice(mode=self.params.bonding_mode, miimon=self.params.miimon_value) - config = super().test_wide_configuration() for dev in [host1.eth0, host1.eth1]: dev.down() diff --git a/lnst/Recipes/ENRT/TeamRecipe.py b/lnst/Recipes/ENRT/TeamRecipe.py index 6669cdcce..95717884f 100644 --- a/lnst/Recipes/ENRT/TeamRecipe.py +++ b/lnst/Recipes/ENRT/TeamRecipe.py @@ -85,12 +85,11 @@ def test_wide_configuration(self): | host2.eth0 = 192.168.10.2/24 and fc00:0:0:1::2/64 """ host1, host2 = self.matched.host1, self.matched.host2 + config = super().test_wide_configuration() teamd_config = {'runner': {'name': self.params.runner_name}} host1.team0 = TeamDevice(config=teamd_config) - config = super().test_wide_configuration() - for dev in [host1.eth0, host1.eth1]: dev.down() host1.team0.slave_add(dev) diff --git a/lnst/Recipes/ENRT/TeamVsBondRecipe.py b/lnst/Recipes/ENRT/TeamVsBondRecipe.py index 99d816041..fc2bfb8c4 100644 --- a/lnst/Recipes/ENRT/TeamVsBondRecipe.py +++ b/lnst/Recipes/ENRT/TeamVsBondRecipe.py @@ -47,13 +47,12 @@ class TeamVsBondRecipe(PerfReversibleFlowMixin, CommonHWSubConfigMixin, def test_wide_configuration(self): host1, host2 = self.matched.host1, self.matched.host2 + config = super().test_wide_configuration() host1.team0 = TeamDevice(config={'runner': {'name': self.params.runner_name}}) - host2.bond0 = BondDevice(mode=self.params.bonding_mode, miimon=self.params.miimon_value) - config = super().test_wide_configuration() ipv4_addr = interface_addresses(self.params.net_ipv4) ipv6_addr = interface_addresses(self.params.net_ipv6) diff --git a/lnst/Recipes/ENRT/VlansOverBondRecipe.py b/lnst/Recipes/ENRT/VlansOverBondRecipe.py index 8711fea6f..3ae680a6f 100644 --- a/lnst/Recipes/ENRT/VlansOverBondRecipe.py +++ b/lnst/Recipes/ENRT/VlansOverBondRecipe.py @@ -115,6 +115,7 @@ def test_wide_configuration(self): | host2.vlan2 = 192.168.30.2/24 and fc00:0:0:3::2/64 """ host1, host2 = self.matched.host1, self.matched.host2 + config = super().test_wide_configuration() host1.bond0 = BondDevice(mode=self.params.bonding_mode, miimon=self.params.miimon_value) @@ -129,7 +130,6 @@ def test_wide_configuration(self): host2.vlan1 = VlanDevice(realdev=host2.eth0, vlan_id=self.params.vlan1_id) host2.vlan2 = VlanDevice(realdev=host2.eth0, vlan_id=self.params.vlan2_id) - config = super().test_wide_configuration() config.track_device(host1.bond0) vlan0_ipv4_addr = interface_addresses(self.params.vlan0_ipv4) diff --git a/lnst/Recipes/ENRT/VlansOverTeamRecipe.py b/lnst/Recipes/ENRT/VlansOverTeamRecipe.py index 4cda08491..46478e9e1 100644 --- a/lnst/Recipes/ENRT/VlansOverTeamRecipe.py +++ b/lnst/Recipes/ENRT/VlansOverTeamRecipe.py @@ -58,6 +58,7 @@ class VlansOverTeamRecipe(PerfReversibleFlowMixin, VlanPingEvaluatorMixin, def test_wide_configuration(self): host1, host2 = self.matched.host1, self.matched.host2 + config = super().test_wide_configuration() host1.team0 = TeamDevice(config={'runner': {'name': self.params.runner_name}}) for dev in [host1.eth0, host1.eth1]: @@ -71,7 +72,6 @@ def test_wide_configuration(self): host2.vlan1 = VlanDevice(realdev=host2.eth0, vlan_id=self.params.vlan1_id) host2.vlan2 = VlanDevice(realdev=host2.eth0, vlan_id=self.params.vlan2_id) - config = super().test_wide_configuration() config.track_device(host1.team0) vlan0_ipv4_addr = interface_addresses(self.params.vlan0_ipv4) diff --git a/lnst/Recipes/ENRT/VlansRecipe.py b/lnst/Recipes/ENRT/VlansRecipe.py index 3017fa16a..814374e6a 100644 --- a/lnst/Recipes/ENRT/VlansRecipe.py +++ b/lnst/Recipes/ENRT/VlansRecipe.py @@ -85,6 +85,7 @@ def test_wide_configuration(self): """ host1, host2 = self.matched.host1, self.matched.host2 + config = super().test_wide_configuration() host1.eth0.down() host2.eth0.down() @@ -96,8 +97,6 @@ def test_wide_configuration(self): host2.vlan1 = VlanDevice(realdev=host2.eth0, vlan_id=self.params.vlan1_id) host2.vlan2 = VlanDevice(realdev=host2.eth0, vlan_id=self.params.vlan2_id) - config = super().test_wide_configuration() - vlan0_ipv4_addr = interface_addresses(self.params.vlan0_ipv4) vlan0_ipv6_addr = interface_addresses(self.params.vlan0_ipv6) vlan1_ipv4_addr = interface_addresses(self.params.vlan1_ipv4)