From 9f9f3855c1033165db1d703d9eaad8c37db022c9 Mon Sep 17 00:00:00 2001 From: Dashuai Zhang Date: Sat, 24 May 2025 23:59:57 +1000 Subject: [PATCH] cherr-pick generate_topo change only in #18159 --- ansible/generate_topo.py | 151 ++++++++++++++++++++++++++++++++------- 1 file changed, 124 insertions(+), 27 deletions(-) diff --git a/ansible/generate_topo.py b/ansible/generate_topo.py index 88f4014ce8..7b02451c82 100755 --- a/ansible/generate_topo.py +++ b/ansible/generate_topo.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 +from collections import defaultdict, namedtuple import copy -from typing import Any, Dict, List, Tuple +from typing import Any, Dict, List, Tuple, Union from ipaddress import IPv4Network, IPv6Network import click import jinja2 @@ -11,6 +12,24 @@ PTF_BACKPLANE_IPV6 = "fc0a::ff" backplane_additional_offset_ipv6 = 0 + +class LagPort(set): + def __init__(self, *ports): + super().__init__(ports) + + +class PortList(list): + def __init__(self, *lag_ports: Union[LagPort, int]): + super().__init__(lag_ports) + + def __contains__(self, key): + if super().__contains__(key): + return True + return any([key in lag_port for lag_port in self if isinstance(lag_port, LagPort)]) + + +Breakout = namedtuple('Breakout', ['port', 'index']) + # Define the roles for the devices in the topology roles_cfg = { "t0": { @@ -27,6 +46,13 @@ "uplink": {"role": "t2", "asn": 65200, "asn_v6": 4200200000}, "peer": None, }, + "lt2": { + "asn": 4200100000, + "asn_v6": 4200100000, + "downlink": {"role": "t1", "asn": 4200000000, "asn_v6": 4200000000, "asn_increment": 0, "num_lags": 1}, + "uplink": {"role": "ut2", "asn": 4200200000, "asn_v6": 4200200000, "asn_increment": 0}, + "peer": None, + }, } hw_port_cfg = { @@ -84,12 +110,20 @@ 'peer_ports': [], 'skip_ports': [16, 17, 44, 45, 48, 49], "panel_port_step": 1}, + 'o128lt2': {"ds_breakout": 2, "us_breakout": 2, "ds_link_step": 1, "us_link_step": 1, + 'uplink_ports': PortList(LagPort(45), 46, 47, 48, LagPort(49), 50, 51, 52), + 'peer_ports': [], + 'skip_ports': PortList(63), + "panel_port_step": 1}, } vlan_group_cfgs = [ - {"name": "one_vlan_a", "vlan_count": 1, "v4_prefix": "192.168.0.0/21", "v6_prefix": "fc02:1000::0/64"}, - {"name": "two_vlan_a", "vlan_count": 2, "v4_prefix": "192.168.0.0/22", "v6_prefix": "fc02:100::0/64"}, - {"name": "four_vlan_a", "vlan_count": 4, "v4_prefix": "192.168.0.0/22", "v6_prefix": "fc02:100::0/64"}, + {"name": "one_vlan_a", "vlan_count": 1, + "v4_prefix": "192.168.0.0/21", "v6_prefix": "fc02:1000::0/64"}, + {"name": "two_vlan_a", "vlan_count": 2, + "v4_prefix": "192.168.0.0/22", "v6_prefix": "fc02:100::0/64"}, + {"name": "four_vlan_a", "vlan_count": 4, + "v4_prefix": "192.168.0.0/22", "v6_prefix": "fc02:100::0/64"}, ] @@ -124,7 +158,8 @@ def __init__(self, dut_asn: int, dut_asn_v6: int, role_cfg: Dict[str, Any], - ip_offset: int = None): + ip_offset: int = None, + num_lags=0): self.role = role_cfg["role"] @@ -136,7 +171,8 @@ def __init__(self, self.tornum = tornum # VLAN configuration - self.vlans = [link_id] + self.vlans = [link_id] if not isinstance( + link_id, range) else [*link_id] # BGP configuration self.asn = role_cfg["asn"] @@ -145,22 +181,32 @@ def __init__(self, self.peer_asn_v6 = dut_asn_v6 # IP addresses - self.dut_intf_ipv4, self.pc_intf_ipv4 = calc_ipv4_pair("10.0.0.0", self.ip_offset) - self.dut_intf_ipv6, self.pc_intf_ipv6 = calc_ipv6_pair("FC00::", self.ip_offset) + self.dut_intf_ipv4, self.pc_intf_ipv4 = calc_ipv4_pair( + "10.0.0.0", self.ip_offset) + self.dut_intf_ipv6, self.pc_intf_ipv6 = calc_ipv6_pair( + "FC00::", self.ip_offset) self.loopback_ipv4 = calc_ipv4("100.1.0.0", self.ip_offset+1) - self.loopback_ipv6 = calc_ipv6("2064:100::", (self.ip_offset+1) * 2**64) + self.loopback_ipv6 = calc_ipv6( + "2064:100::", (self.ip_offset+1) * 2**64) + + # Set lags + self.num_lags = num_lags # Backplane IPs global backplane_additional_offset_ipv4 - self.bp_ipv4 = calc_ipv4("10.10.246.1", self.ip_offset+1+backplane_additional_offset_ipv4) + self.bp_ipv4 = calc_ipv4( + "10.10.246.1", self.ip_offset+1+backplane_additional_offset_ipv4) if self.bp_ipv4 == PTF_BACKPLANE_IPV4: backplane_additional_offset_ipv4 = 1 - self.bp_ipv4 = calc_ipv4("10.10.246.1", self.ip_offset+1+backplane_additional_offset_ipv4) + self.bp_ipv4 = calc_ipv4( + "10.10.246.1", self.ip_offset+1+backplane_additional_offset_ipv4) global backplane_additional_offset_ipv6 - self.bp_ipv6 = calc_ipv6("fc0a::1", (self.ip_offset+1+backplane_additional_offset_ipv6)) + self.bp_ipv6 = calc_ipv6( + "fc0a::1", (self.ip_offset+1+backplane_additional_offset_ipv6)) if self.bp_ipv6 == PTF_BACKPLANE_IPV6: backplane_additional_offset_ipv6 = 1 - self.bp_ipv6 = calc_ipv6("fc0a::1", self.ip_offset+1+backplane_additional_offset_ipv6) + self.bp_ipv6 = calc_ipv6( + "fc0a::1", self.ip_offset+1+backplane_additional_offset_ipv6) class HostInterface: @@ -197,12 +243,14 @@ def __init__(self, name: str, vlan_count: int, hostifs: List[HostInterface], v4_ # Split host if into the number of VLANs hostif_count_per_vlan = len(hostifs) // vlan_count - hostif_groups = [hostifs[i*hostif_count_per_vlan:(i+1)*hostif_count_per_vlan] for i in range(vlan_count)] + hostif_groups = [ + hostifs[i*hostif_count_per_vlan:(i+1)*hostif_count_per_vlan] for i in range(vlan_count)] v4_prefix = IPv4Network(v4_prefix) v6_prefix = IPv6Network(v6_prefix) for vlan_index in range(len(hostif_groups)): - vlan = Vlan(1000 + vlan_index * 100, hostif_groups[vlan_index], v4_prefix, v6_prefix) + vlan = Vlan(1000 + vlan_index * 100, + hostif_groups[vlan_index], v4_prefix, v6_prefix) self.vlans.append(vlan) # Move to next subnet based on the prefix length @@ -218,31 +266,46 @@ def generate_topo(role: str, port_cfg_type: str = "default", ) -> Tuple[List[VM], List[HostInterface]]: + def _find_lag_port(port_id: int) -> bool: + nonlocal port_cfg + if not isinstance(port_cfg["uplink_ports"], PortList): + return False, + + lag_port = next( + (lp for lp in port_cfg["uplink_ports"] if isinstance(lp, LagPort) and port_id in lp), None) + return (lag_port is not None, lag_port) + dut_role_cfg = roles_cfg[role] port_cfg = hw_port_cfg[port_cfg_type] vm_list = [] downlinkif_list = [] uplinkif_list = [] - per_role_vm_count = {} + per_role_vm_count = defaultdict(lambda: 0) + lag_port_assigned = set() tornum = 1 link_id_start = 0 + for panel_port_id in list(range(0, panel_port_count, port_cfg['panel_port_step'])) + peer_ports: vm_role_cfg = None link_step = 1 link_type = None + if panel_port_id in uplink_ports: if dut_role_cfg["uplink"] is None: - raise ValueError("Uplink port specified for a role that doesn't have an uplink") + raise ValueError( + "Uplink port specified for a role that doesn't have an uplink") vm_role_cfg = dut_role_cfg["uplink"] link_id_end = link_id_start + port_cfg['us_breakout'] + num_breakout = port_cfg['us_breakout'] link_step = port_cfg['us_link_step'] link_type = 'up' elif panel_port_id in peer_ports: if dut_role_cfg["peer"] is None: - raise ValueError("Peer port specified for a role that doesn't have a peer") + raise ValueError( + "Peer port specified for a role that doesn't have a peer") vm_role_cfg = dut_role_cfg["peer"] @@ -255,23 +318,54 @@ def generate_topo(role: str, vm_role_cfg = dut_role_cfg["downlink"] link_id_end = link_id_start + port_cfg['ds_breakout'] + num_breakout = port_cfg['ds_breakout'] link_step = port_cfg['ds_link_step'] link_type = 'down' + is_lag_port, lag_port = _find_lag_port(panel_port_id) + + if panel_port_id in lag_port_assigned: + continue + + if is_lag_port: + per_role_vm_count[vm_role_cfg["role"]] += 1 + end_vlan_range = link_id_start + len(lag_port) * num_breakout + + vm_role_cfg["asn"] += vm_role_cfg.get("asn_increment", 1) + vm = VM(range(link_id_start, end_vlan_range), len(vm_list), per_role_vm_count[vm_role_cfg["role"]], tornum, + dut_role_cfg["asn"], dut_role_cfg["asn_v6"], vm_role_cfg, link_id_start, + num_lags=len(lag_port) * num_breakout) + + vm_list.append(vm) + + if link_type == 'up': + uplinkif_list.append(link_id_start) + elif link_type == 'down': + tornum += 1 + downlinkif_list.append(link_id_start) + + lag_port_assigned.update(lag_port) + + link_id_start = end_vlan_range + continue + for link_id in range(link_id_start, link_id_end): vm = None hostif = None # Create the VM or host interface based on the configuration if vm_role_cfg is not None: - if vm_role_cfg["role"] not in per_role_vm_count: - per_role_vm_count[vm_role_cfg["role"]] = 0 per_role_vm_count[vm_role_cfg["role"]] += 1 if (link_id - link_id_start) % link_step == 0 and panel_port_id not in skip_ports: - vm_role_cfg["asn"] += 1 + # Skip breakout if defined + if (panel_port_id, link_id - link_id_start) in skip_ports: + continue + + vm_role_cfg["asn"] += vm_role_cfg.get("asn_increment", 1) vm = VM(link_id, len(vm_list), per_role_vm_count[vm_role_cfg["role"]], tornum, - dut_role_cfg["asn"], dut_role_cfg["asn_v6"], vm_role_cfg, link_id) + dut_role_cfg["asn"], dut_role_cfg["asn_v6"], vm_role_cfg, link_id, + num_lags=vm_role_cfg.get('num_lags', 0)) vm_list.append(vm) if link_type == 'up': uplinkif_list.append(link_id) @@ -282,7 +376,6 @@ def generate_topo(role: str, if (link_id - link_id_start) % link_step == 0 and panel_port_id not in skip_ports: hostif = HostInterface(link_id) downlinkif_list.append(hostif) - print(panel_port_id, link_id_start, link_id_end, link_step, vm_role_cfg) link_id_start = link_id_end return vm_list, downlinkif_list, uplinkif_list @@ -339,7 +432,7 @@ def write_topo_file(role: str, @click.command() -@click.option("--role", "-r", required=True, type=click.Choice(['t0', 't1']), help="Role of the device") +@click.option("--role", "-r", required=True, type=click.Choice(['t0', 't1', 'lt2']), help="Role of the device") @click.option("--keyword", "-k", required=True, type=str, help="Keyword for the topology file") @click.option("--template", "-t", required=True, type=str, help="Path to the Jinja template file") @click.option("--port-count", "-c", required=True, type=int, help="Number of physical ports used on the device") @@ -374,19 +467,23 @@ def main(role: str, keyword: str, template: str, port_count: int, uplinks: str, - ./generate_topo.py -r t0 -k isolated-v6 -t t0-isolated-v6 -c 64 -l 'c512s2-sparse' - ./generate_topo.py -r t1 -k isolated-v6 -t t1-isolated-v6 -c 64 -l 'c448o16' - ./generate_topo.py -r t1 -k isolated-v6 -t t1-isolated-v6 -c 64 -l 'c448o16-sparse' + - ./generate_topo.py -r lt2 -k o128 -t lt2_128 -c 64 -l 'o128lt2' """ uplink_ports = [int(port) for port in uplinks.split(",")] if uplinks != "" else \ hw_port_cfg[link_cfg]['uplink_ports'] - peer_ports = [int(port) for port in peers.split(",")] if peers != "" else hw_port_cfg[link_cfg]['peer_ports'] - skip_ports = [int(port) for port in skips.split(",")] if skips != "" else hw_port_cfg[link_cfg]['skip_ports'] + peer_ports = [int(port) for port in peers.split( + ",")] if peers != "" else hw_port_cfg[link_cfg]['peer_ports'] + skip_ports = [int(port) for port in skips.split( + ",")] if skips != "" else hw_port_cfg[link_cfg]['skip_ports'] vm_list, downlinkif_list, uplinkif_list = generate_topo(role, port_count, uplink_ports, peer_ports, skip_ports, link_cfg) vlan_group_list = [] if role == "t0": vlan_group_list = generate_vlan_groups(downlinkif_list) - file_content = generate_topo_file(role, f"templates/topo_{template}.j2", vm_list, downlinkif_list, vlan_group_list) + file_content = generate_topo_file( + role, f"templates/topo_{template}.j2", vm_list, downlinkif_list, vlan_group_list) write_topo_file(role, keyword, len(downlinkif_list), len(uplinkif_list), len(peer_ports), file_content)