10b5ee16dSJuraj Linkeš# SPDX-License-Identifier: BSD-3-Clause 20b5ee16dSJuraj Linkeš# Copyright(c) 2024 PANTHEON.tech s.r.o. 30b5ee16dSJuraj Linkeš 40b5ee16dSJuraj Linkeš"""Testbed topology representation. 50b5ee16dSJuraj Linkeš 60b5ee16dSJuraj LinkešA topology of a testbed captures what links are available between the testbed's nodes. 70b5ee16dSJuraj LinkešThe link information then implies what type of topology is available. 80b5ee16dSJuraj Linkeš""" 90b5ee16dSJuraj Linkeš 100b5ee16dSJuraj Linkešfrom dataclasses import dataclass 11*949d1488SLuca Vizzarrofrom os import environ 12039256daSJuraj Linkešfrom typing import TYPE_CHECKING, Iterable 13039256daSJuraj Linkeš 14*949d1488SLuca Vizzarroif TYPE_CHECKING or environ.get("DTS_DOC_BUILD"): 15039256daSJuraj Linkeš from enum import Enum as NoAliasEnum 16039256daSJuraj Linkešelse: 17039256daSJuraj Linkeš from aenum import NoAliasEnum 180b5ee16dSJuraj Linkeš 190b5ee16dSJuraj Linkešfrom framework.config import PortConfig 20039256daSJuraj Linkešfrom framework.exception import ConfigurationError 210b5ee16dSJuraj Linkeš 220b5ee16dSJuraj Linkešfrom .port import Port 230b5ee16dSJuraj Linkeš 240b5ee16dSJuraj Linkeš 25039256daSJuraj Linkešclass TopologyType(int, NoAliasEnum): 260b5ee16dSJuraj Linkeš """Supported topology types.""" 270b5ee16dSJuraj Linkeš 280b5ee16dSJuraj Linkeš #: A topology with no Traffic Generator. 290b5ee16dSJuraj Linkeš no_link = 0 300b5ee16dSJuraj Linkeš #: A topology with one physical link between the SUT node and the TG node. 310b5ee16dSJuraj Linkeš one_link = 1 320b5ee16dSJuraj Linkeš #: A topology with two physical links between the Sut node and the TG node. 330b5ee16dSJuraj Linkeš two_links = 2 34039256daSJuraj Linkeš #: The default topology required by test cases if not specified otherwise. 35039256daSJuraj Linkeš default = 2 36039256daSJuraj Linkeš 37039256daSJuraj Linkeš @classmethod 38039256daSJuraj Linkeš def get_from_value(cls, value: int) -> "TopologyType": 39039256daSJuraj Linkeš r"""Get the corresponding instance from value. 40039256daSJuraj Linkeš 41039256daSJuraj Linkeš :class:`~enum.Enum`\s that don't allow aliases don't know which instance should be returned 42039256daSJuraj Linkeš as there could be multiple valid instances. Except for the :attr:`default` value, 43039256daSJuraj Linkeš :class:`TopologyType` is a regular :class:`~enum.Enum`. 44039256daSJuraj Linkeš When getting an instance from value, we're not interested in the default, 45039256daSJuraj Linkeš since we already know the value, allowing us to remove the ambiguity. 46039256daSJuraj Linkeš """ 47039256daSJuraj Linkeš match value: 48039256daSJuraj Linkeš case 0: 49039256daSJuraj Linkeš return TopologyType.no_link 50039256daSJuraj Linkeš case 1: 51039256daSJuraj Linkeš return TopologyType.one_link 52039256daSJuraj Linkeš case 2: 53039256daSJuraj Linkeš return TopologyType.two_links 54039256daSJuraj Linkeš case _: 55039256daSJuraj Linkeš raise ConfigurationError("More than two links in a topology are not supported.") 560b5ee16dSJuraj Linkeš 570b5ee16dSJuraj Linkeš 580b5ee16dSJuraj Linkešclass Topology: 590b5ee16dSJuraj Linkeš """Testbed topology. 600b5ee16dSJuraj Linkeš 610b5ee16dSJuraj Linkeš The topology contains ports processed into ingress and egress ports. 620b5ee16dSJuraj Linkeš If there are no ports on a node, dummy ports (ports with no actual values) are stored. 630b5ee16dSJuraj Linkeš If there is only one link available, the ports of this link are stored 640b5ee16dSJuraj Linkeš as both ingress and egress ports. 650b5ee16dSJuraj Linkeš 660b5ee16dSJuraj Linkeš The dummy ports shouldn't be used. It's up to :class:`~framework.runner.DTSRunner` 670b5ee16dSJuraj Linkeš to ensure no test case or suite requiring actual links is executed 680b5ee16dSJuraj Linkeš when the topology prohibits it and up to the developers to make sure that test cases 690b5ee16dSJuraj Linkeš not requiring any links don't use any ports. Otherwise, the underlying methods 700b5ee16dSJuraj Linkeš using the ports will fail. 710b5ee16dSJuraj Linkeš 720b5ee16dSJuraj Linkeš Attributes: 730b5ee16dSJuraj Linkeš type: The type of the topology. 740b5ee16dSJuraj Linkeš tg_port_egress: The egress port of the TG node. 750b5ee16dSJuraj Linkeš sut_port_ingress: The ingress port of the SUT node. 760b5ee16dSJuraj Linkeš sut_port_egress: The egress port of the SUT node. 770b5ee16dSJuraj Linkeš tg_port_ingress: The ingress port of the TG node. 780b5ee16dSJuraj Linkeš """ 790b5ee16dSJuraj Linkeš 800b5ee16dSJuraj Linkeš type: TopologyType 810b5ee16dSJuraj Linkeš tg_port_egress: Port 820b5ee16dSJuraj Linkeš sut_port_ingress: Port 830b5ee16dSJuraj Linkeš sut_port_egress: Port 840b5ee16dSJuraj Linkeš tg_port_ingress: Port 850b5ee16dSJuraj Linkeš 860b5ee16dSJuraj Linkeš def __init__(self, sut_ports: Iterable[Port], tg_ports: Iterable[Port]): 870b5ee16dSJuraj Linkeš """Create the topology from `sut_ports` and `tg_ports`. 880b5ee16dSJuraj Linkeš 890b5ee16dSJuraj Linkeš Args: 900b5ee16dSJuraj Linkeš sut_ports: The SUT node's ports. 910b5ee16dSJuraj Linkeš tg_ports: The TG node's ports. 920b5ee16dSJuraj Linkeš """ 930b5ee16dSJuraj Linkeš port_links = [] 940b5ee16dSJuraj Linkeš for sut_port in sut_ports: 950b5ee16dSJuraj Linkeš for tg_port in tg_ports: 960b5ee16dSJuraj Linkeš if (sut_port.identifier, sut_port.peer) == ( 970b5ee16dSJuraj Linkeš tg_port.peer, 980b5ee16dSJuraj Linkeš tg_port.identifier, 990b5ee16dSJuraj Linkeš ): 1000b5ee16dSJuraj Linkeš port_links.append(PortLink(sut_port=sut_port, tg_port=tg_port)) 1010b5ee16dSJuraj Linkeš 102039256daSJuraj Linkeš self.type = TopologyType.get_from_value(len(port_links)) 103b935bdc3SLuca Vizzarro dummy_port = Port( 104b935bdc3SLuca Vizzarro "", 105b935bdc3SLuca Vizzarro PortConfig( 106b935bdc3SLuca Vizzarro pci="0000:00:00.0", 107b935bdc3SLuca Vizzarro os_driver_for_dpdk="", 108b935bdc3SLuca Vizzarro os_driver="", 109b935bdc3SLuca Vizzarro peer_node="", 110b935bdc3SLuca Vizzarro peer_pci="0000:00:00.0", 111b935bdc3SLuca Vizzarro ), 112b935bdc3SLuca Vizzarro ) 1130b5ee16dSJuraj Linkeš self.tg_port_egress = dummy_port 1140b5ee16dSJuraj Linkeš self.sut_port_ingress = dummy_port 1150b5ee16dSJuraj Linkeš self.sut_port_egress = dummy_port 1160b5ee16dSJuraj Linkeš self.tg_port_ingress = dummy_port 1170b5ee16dSJuraj Linkeš if self.type > TopologyType.no_link: 1180b5ee16dSJuraj Linkeš self.tg_port_egress = port_links[0].tg_port 1190b5ee16dSJuraj Linkeš self.sut_port_ingress = port_links[0].sut_port 1200b5ee16dSJuraj Linkeš self.sut_port_egress = self.sut_port_ingress 1210b5ee16dSJuraj Linkeš self.tg_port_ingress = self.tg_port_egress 1220b5ee16dSJuraj Linkeš if self.type > TopologyType.one_link: 1230b5ee16dSJuraj Linkeš self.sut_port_egress = port_links[1].sut_port 1240b5ee16dSJuraj Linkeš self.tg_port_ingress = port_links[1].tg_port 1250b5ee16dSJuraj Linkeš 1260b5ee16dSJuraj Linkeš 1270b5ee16dSJuraj Linkeš@dataclass(slots=True, frozen=True) 1280b5ee16dSJuraj Linkešclass PortLink: 1290b5ee16dSJuraj Linkeš """The physical, cabled connection between the ports. 1300b5ee16dSJuraj Linkeš 1310b5ee16dSJuraj Linkeš Attributes: 1320b5ee16dSJuraj Linkeš sut_port: The port on the SUT node connected to `tg_port`. 1330b5ee16dSJuraj Linkeš tg_port: The port on the TG node connected to `sut_port`. 1340b5ee16dSJuraj Linkeš """ 1350b5ee16dSJuraj Linkeš 1360b5ee16dSJuraj Linkeš sut_port: Port 1370b5ee16dSJuraj Linkeš tg_port: Port 138