@@ -13,6 +13,7 @@ executions:
test_suites:
- hello_world
system_under_test: "SUT 1"
+ traffic_generator_system: "TG 1"
nodes:
- name: "SUT 1"
hostname: sut1.change.me.localhost
@@ -25,3 +26,27 @@ nodes:
hugepages: # optional; if removed, will use system hugepage configuration
amount: 256
force_first_numa: false
+ ports:
+ - pci: "0000:00:08.0"
+ dpdk_os_driver: vfio-pci
+ os_driver: i40e
+ peer_node: "TG 1"
+ peer_pci: "0000:00:08.0"
+ - name: "TG 1"
+ hostname: tg1.change.me.localhost
+ user: root
+ arch: x86_64
+ os: linux
+ lcores: ""
+ use_first_core: false
+ hugepages: # optional; if removed, will use system hugepage configuration
+ amount: 256
+ force_first_numa: false
+ ports:
+ - pci: "0000:00:08.0"
+ dpdk_os_driver: rdma
+ os_driver: rdma
+ peer_node: "SUT 1"
+ peer_pci: "0000:00:08.0"
+ traffic_generator:
+ type: SCAPY
@@ -12,7 +12,7 @@
import pathlib
from dataclasses import dataclass
from enum import Enum, auto, unique
-from typing import Any, TypedDict
+from typing import Any, TypedDict, Union
import warlock # type: ignore
import yaml
@@ -61,6 +61,18 @@ class Compiler(StrEnum):
msvc = auto()
+@unique
+class NodeType(StrEnum):
+ physical = auto()
+ virtual = auto()
+
+
+@unique
+class TrafficGeneratorType(StrEnum):
+ NONE = auto()
+ SCAPY = auto()
+
+
# Slots enables some optimizations, by pre-allocating space for the defined
# attributes in the underlying data structure.
#
@@ -72,6 +84,41 @@ class HugepageConfiguration:
force_first_numa: bool
+@dataclass(slots=True, frozen=True)
+class PortConfig:
+ id: int
+ node: str
+ pci: str
+ dpdk_os_driver: str
+ os_driver: str
+ peer_node: str
+ peer_pci: str
+
+ @staticmethod
+ def from_dict(id: int, node: str, d: dict) -> "PortConfig":
+ return PortConfig(id=id, node=node, **d)
+
+
+@dataclass(slots=True, frozen=True)
+class TrafficGeneratorConfig:
+ traffic_generator_type: TrafficGeneratorType
+
+ @staticmethod
+ def from_dict(d: dict):
+ # This looks useless now, but is designed to allow expansion to traffic
+ # generators that require more configuration later.
+ match TrafficGeneratorType(d["type"]):
+ case TrafficGeneratorType.SCAPY:
+ return ScapyTrafficGeneratorConfig(
+ traffic_generator_type=TrafficGeneratorType.SCAPY
+ )
+
+
+@dataclass(slots=True, frozen=True)
+class ScapyTrafficGeneratorConfig(TrafficGeneratorConfig):
+ pass
+
+
@dataclass(slots=True, frozen=True)
class NodeConfiguration:
name: str
@@ -82,29 +129,52 @@ class NodeConfiguration:
os: OS
lcores: str
use_first_core: bool
- memory_channels: int
hugepages: HugepageConfiguration | None
+ ports: list[PortConfig]
@staticmethod
- def from_dict(d: dict) -> "NodeConfiguration":
+ def from_dict(d: dict) -> Union["SUTConfiguration", "TGConfiguration"]:
hugepage_config = d.get("hugepages")
if hugepage_config:
if "force_first_numa" not in hugepage_config:
hugepage_config["force_first_numa"] = False
hugepage_config = HugepageConfiguration(**hugepage_config)
- return NodeConfiguration(
- name=d["name"],
- hostname=d["hostname"],
- user=d["user"],
- password=d.get("password"),
- arch=Architecture(d["arch"]),
- os=OS(d["os"]),
- lcores=d.get("lcores", "1"),
- use_first_core=d.get("use_first_core", False),
- memory_channels=d.get("memory_channels", 1),
- hugepages=hugepage_config,
- )
+ common_config = {"name": d["name"],
+ "hostname": d["hostname"],
+ "user": d["user"],
+ "password": d.get("password"),
+ "arch": Architecture(d["arch"]),
+ "os": OS(d["os"]),
+ "lcores": d.get("lcores", "1"),
+ "use_first_core": d.get("use_first_core", False),
+ "hugepages": hugepage_config,
+ "ports": [
+ PortConfig.from_dict(i, d["name"], port)
+ for i, port in enumerate(d["ports"])
+ ]}
+
+ if "traffic_generator" in d:
+ return TGConfiguration(
+ traffic_generator=TrafficGeneratorConfig.from_dict(
+ d["traffic_generator"]),
+ **common_config
+ )
+ else:
+ return SUTConfiguration(
+ memory_channels=d.get("memory_channels", 1),
+ **common_config
+ )
+
+
+@dataclass(slots=True, frozen=True)
+class SUTConfiguration(NodeConfiguration):
+ memory_channels: int
+
+
+@dataclass(slots=True, frozen=True)
+class TGConfiguration(NodeConfiguration):
+ traffic_generator: TrafficGeneratorConfig
@dataclass(slots=True, frozen=True)
@@ -156,7 +226,8 @@ class ExecutionConfiguration:
perf: bool
func: bool
test_suites: list[TestSuiteConfig]
- system_under_test: NodeConfiguration
+ system_under_test: SUTConfiguration
+ traffic_generator_system: TGConfiguration
@staticmethod
def from_dict(d: dict, node_map: dict) -> "ExecutionConfiguration":
@@ -169,12 +240,16 @@ def from_dict(d: dict, node_map: dict) -> "ExecutionConfiguration":
sut_name = d["system_under_test"]
assert sut_name in node_map, f"Unknown SUT {sut_name} in execution {d}"
+ tg_name = d["traffic_generator_system"]
+ assert tg_name in node_map, f"Unknown TG {tg_name} in execution {d}"
+
return ExecutionConfiguration(
build_targets=build_targets,
perf=d["perf"],
func=d["func"],
test_suites=test_suites,
system_under_test=node_map[sut_name],
+ traffic_generator_system=node_map[tg_name],
)
@@ -6,6 +6,76 @@
"type": "string",
"description": "A unique identifier for a node"
},
+ "NIC": {
+ "type": "string",
+ "enum": [
+ "ALL",
+ "ConnectX3_MT4103",
+ "ConnectX4_LX_MT4117",
+ "ConnectX4_MT4115",
+ "ConnectX5_MT4119",
+ "ConnectX5_MT4121",
+ "I40E_10G-10G_BASE_T_BC",
+ "I40E_10G-10G_BASE_T_X722",
+ "I40E_10G-SFP_X722",
+ "I40E_10G-SFP_XL710",
+ "I40E_10G-X722_A0",
+ "I40E_1G-1G_BASE_T_X722",
+ "I40E_25G-25G_SFP28",
+ "I40E_40G-QSFP_A",
+ "I40E_40G-QSFP_B",
+ "IAVF-ADAPTIVE_VF",
+ "IAVF-VF",
+ "IAVF_10G-X722_VF",
+ "ICE_100G-E810C_QSFP",
+ "ICE_25G-E810C_SFP",
+ "ICE_25G-E810_XXV_SFP",
+ "IGB-I350_VF",
+ "IGB_1G-82540EM",
+ "IGB_1G-82545EM_COPPER",
+ "IGB_1G-82571EB_COPPER",
+ "IGB_1G-82574L",
+ "IGB_1G-82576",
+ "IGB_1G-82576_QUAD_COPPER",
+ "IGB_1G-82576_QUAD_COPPER_ET2",
+ "IGB_1G-82580_COPPER",
+ "IGB_1G-I210_COPPER",
+ "IGB_1G-I350_COPPER",
+ "IGB_1G-I354_SGMII",
+ "IGB_1G-PCH_LPTLP_I218_LM",
+ "IGB_1G-PCH_LPTLP_I218_V",
+ "IGB_1G-PCH_LPT_I217_LM",
+ "IGB_1G-PCH_LPT_I217_V",
+ "IGB_2.5G-I354_BACKPLANE_2_5GBPS",
+ "IGC-I225_LM",
+ "IGC-I226_LM",
+ "IXGBE_10G-82599_SFP",
+ "IXGBE_10G-82599_SFP_SF_QP",
+ "IXGBE_10G-82599_T3_LOM",
+ "IXGBE_10G-82599_VF",
+ "IXGBE_10G-X540T",
+ "IXGBE_10G-X540_VF",
+ "IXGBE_10G-X550EM_A_SFP",
+ "IXGBE_10G-X550EM_X_10G_T",
+ "IXGBE_10G-X550EM_X_SFP",
+ "IXGBE_10G-X550EM_X_VF",
+ "IXGBE_10G-X550T",
+ "IXGBE_10G-X550_VF",
+ "brcm_57414",
+ "brcm_P2100G",
+ "cavium_0011",
+ "cavium_a034",
+ "cavium_a063",
+ "cavium_a064",
+ "fastlinq_ql41000",
+ "fastlinq_ql41000_vf",
+ "fastlinq_ql45000",
+ "fastlinq_ql45000_vf",
+ "hi1822",
+ "virtio"
+ ]
+ },
+
"ARCH": {
"type": "string",
"enum": [
@@ -20,6 +90,20 @@
"linux"
]
},
+ "OS_WITH_OPTIONS": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/OS"
+ },
+ {
+ "type": "string",
+ "enum": [
+ "ALL",
+ "OTHER"
+ ]
+ }
+ ]
+ },
"cpu": {
"type": "string",
"description": "Native should be the default on x86",
@@ -94,6 +178,34 @@
"amount"
]
},
+ "mac_address": {
+ "type": "string",
+ "description": "A MAC address",
+ "pattern": "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"
+ },
+ "pktgen_type": {
+ "type": "string",
+ "enum": [
+ "IXIA",
+ "IXIA_NETWORK",
+ "TREX",
+ "SCAPY",
+ "NONE"
+ ]
+ },
+ "pci_address": {
+ "type": "string",
+ "pattern": "^[\\da-fA-F]{4}:[\\da-fA-F]{2}:[\\da-fA-F]{2}.\\d:?\\w*$"
+ },
+ "port_peer_address": {
+ "description": "Peer is a TRex port, and IXIA port or a PCI address",
+ "oneOf": [
+ {
+ "description": "PCI peer port",
+ "$ref": "#/definitions/pci_address"
+ }
+ ]
+ },
"test_suite": {
"type": "string",
"enum": [
@@ -165,6 +277,60 @@
},
"hugepages": {
"$ref": "#/definitions/hugepages"
+ },
+ "ports": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Each port should be described on both sides of the connection. This makes configuration slightly more verbose but greatly simplifies implementation. If there are an inconsistencies, then DTS will not run until that issue is fixed. An example inconsistency would be port 1, node 1 says it is connected to port 1, node 2, but port 1, node 2 says it is connected to port 2, node 1.",
+ "properties": {
+ "pci": {
+ "$ref": "#/definitions/pci_address",
+ "description": "The local PCI address of the port"
+ },
+ "dpdk_os_driver": {
+ "type": "string",
+ "description": "The driver that the kernel should bind this device to for DPDK to use it. (ex: vfio-pci)"
+ },
+ "os_driver": {
+ "type": "string",
+ "description": "The driver normally used by this port (ex: i40e)"
+ },
+ "peer_node": {
+ "type": "string",
+ "description": "The name of the node the peer port is on"
+ },
+ "peer_pci": {
+ "$ref": "#/definitions/pci_address",
+ "description": "The PCI address of the peer port"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "pci",
+ "dpdk_os_driver",
+ "os_driver",
+ "peer_node",
+ "peer_pci"
+ ]
+ },
+ "minimum": 1
+ },
+ "traffic_generator": {
+ "oneOf": [
+ {
+ "type": "object",
+ "description": "Scapy traffic generator",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "SCAPY"
+ ]
+ }
+ }
+ }
+ ]
}
},
"additionalProperties": false,
@@ -213,6 +379,9 @@
},
"system_under_test": {
"$ref": "#/definitions/node_name"
+ },
+ "traffic_generator_system": {
+ "$ref": "#/definitions/node_name"
}
},
"additionalProperties": false,
@@ -221,7 +390,8 @@
"perf",
"func",
"test_suites",
- "system_under_test"
+ "system_under_test",
+ "traffic_generator_system"
]
},
"minimum": 1