From 51429f9f74ba55ef244c68679ba81d2d7f2c6e78 Mon Sep 17 00:00:00 2001 From: Roger Luethi Date: Fri, 31 Jul 2026 15:25:25 +0200 Subject: [PATCH 1/4] tests/e2e: add synthetic base fixtures The SONiC E2E golden test previously depended on osism/testbed's example seed data, seeded through netbox-manager. Replace it with frozen, in-repo synthetic NetBox fixtures under tests/e2e/scenario/resources/ (100-base.yml, 150-context.yml, 200-fabric.yml, 250-oob.yml, 260-metalbox.yml) plus the minimal edgecore-7726-32x-e2e device type, and commit the first four golden files this scenario produces (e2e-spine-1, e2e-leaf-1, e2e-leaf-2, e2e-oob-1). The goldens are now reproducible without any reference to osism/testbed. Fixture topology: one spine (e2e-spine-1) cabled to two leaves (e2e-leaf-1, e2e-leaf-2) on numbered /31 point-to-point links inside a prefix with the Transfer IPAM role, a standalone OOB switch (e2e-oob-1), and a metalbox (e2e-metalbox-1) that is not itself a SONiC device. e2e-leaf-1 also carries an access port (untagged VLAN 100) and a trunk port (tagged VLAN 200) plus a VLAN200 SVI, and e2e-leaf-2 carries a table_id-only VRF (vrf99) on a data port. Device filter contract, reverse-engineered from osism/tasks/conductor/sonic: a device is generated only when all three hold -- status=active, tagged managed-by-metalbox, and role.slug is one of DEFAULT_SONIC_ROLES (spine/leaf/switch here). Any one missing silently skips the device: no golden, no error. e2e-metalbox-1 deliberately fails this filter (role metalbox, no managed-by-metalbox tag) so it is never generated. Each switch's ASN is derived from its Loopback0 address (4200 + zero-padded 3rd/4th octet), so the fixture's IP addresses are load-bearing, not free choices: e2e-spine-1 172.16.10.1/20 192.168.20.1/32 ASN 4200020001 e2e-leaf-1 172.16.10.11/20 192.168.20.11/32 ASN 4200020011 e2e-leaf-2 172.16.10.12/20 192.168.20.12/32 ASN 4200020012 e2e-oob-1 172.16.10.21/20 192.168.20.21/32 ASN 4200020021 The metalbox unlocks DNS_NAMESERVER/NTP_SERVER by holding 172.16.10.254/20 -- an address inside the switches' shared /20 OOB subnet (172.16.0.0/20) -- on a non-mgmt_only interface. _get_metalbox_ip_for_device() matches purely by that subnet membership and explicitly skips mgmt_only interfaces; it never follows a cable, so no cabling to the metalbox is required. Two generator behaviours were not obvious from the code and cost iterations to find, so are called out in comments in 200-fabric.yml: a newly created interface not present in the device type requires an explicit `type`, and a point-to-point link's address must fall inside a prefix with the Transfer IPAM role or the link is treated as IP-unnumbered and BGP_NEIGHBOR/BGP_NEIGHBOR_AF stay empty despite being cabled and addressed. This covers 30 of the 38 config_db tables; ACL_TABLE/ACL_RULE and the rest come from the base scaffold regardless of fixtures. PORTCHANNEL*, the breakout paths and the EVPN/VXLAN/multi-VRF tables are out of scope here and land with their own scenario files and goldens in later PRs, which must not modify these five base files since everything device-wide is intentionally concentrated here. Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Roger Luethi --- .../golden/osism_e2e-leaf-1_config_db.json | 698 ++++++++++++++++++ .../golden/osism_e2e-leaf-2_config_db.json | 683 +++++++++++++++++ .../e2e/golden/osism_e2e-oob-1_config_db.json | 650 ++++++++++++++++ .../golden/osism_e2e-spine-1_config_db.json | 679 +++++++++++++++++ .../devicetypes/Edgecore/7726-32X-E2E.yaml | 27 + tests/e2e/scenario/resources/100-base.yml | 118 +++ tests/e2e/scenario/resources/150-context.yml | 28 + tests/e2e/scenario/resources/200-fabric.yml | 258 +++++++ tests/e2e/scenario/resources/250-oob.yml | 47 ++ tests/e2e/scenario/resources/260-metalbox.yml | 40 + 10 files changed, 3228 insertions(+) create mode 100644 tests/e2e/golden/osism_e2e-leaf-1_config_db.json create mode 100644 tests/e2e/golden/osism_e2e-leaf-2_config_db.json create mode 100644 tests/e2e/golden/osism_e2e-oob-1_config_db.json create mode 100644 tests/e2e/golden/osism_e2e-spine-1_config_db.json create mode 100644 tests/e2e/scenario/devicetypes/Edgecore/7726-32X-E2E.yaml create mode 100644 tests/e2e/scenario/resources/100-base.yml create mode 100644 tests/e2e/scenario/resources/150-context.yml create mode 100644 tests/e2e/scenario/resources/200-fabric.yml create mode 100644 tests/e2e/scenario/resources/250-oob.yml create mode 100644 tests/e2e/scenario/resources/260-metalbox.yml diff --git a/tests/e2e/golden/osism_e2e-leaf-1_config_db.json b/tests/e2e/golden/osism_e2e-leaf-1_config_db.json new file mode 100644 index 00000000..b34178d6 --- /dev/null +++ b/tests/e2e/golden/osism_e2e-leaf-1_config_db.json @@ -0,0 +1,698 @@ +{ + "ACL_RULE": { + "GNMI_ONLY|RULE_1": { + "IP_TYPE": "IP", + "L4_DST_PORT": "8080", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + }, + "SNMP_ONLY|RULE_1": { + "IP_TYPE": "IP", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + }, + "SSH_ONLY|RULE_1": { + "IP_TYPE": "IP", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + } + }, + "ACL_TABLE": { + "GNMI_ONLY": { + "policy_desc": "GNMI_ONLY", + "services": [ + "EXTERNAL_CLIENT" + ], + "type": "CTRLPLANE" + }, + "SNMP_ONLY": { + "policy_desc": "SNMP_ONLY", + "services": [ + "SNMP" + ], + "type": "CTRLPLANE" + }, + "SSH_ONLY": { + "policy_desc": "SSH_ONLY", + "services": [ + "SSH" + ], + "type": "CTRLPLANE" + } + }, + "BGP_GLOBALS": { + "default": { + "always_compare_med": "true", + "ebgp_requires_policy": "false", + "external_compare_router_id": "false", + "fast_external_failover": "true", + "holdtime": "180", + "ignore_as_path_length": "false", + "keepalive": "60", + "load_balance_mp_relax": "false", + "local_asn": "4200020011", + "log_nbr_state_changes": "true", + "network_import_check": "true", + "router_id": "192.168.20.11" + } + }, + "BGP_GLOBALS_AF": { + "default|ipv4_unicast": { + "ibgp_equal_cluster_length": "false", + "max_ebgp_paths": "2", + "max_ibgp_paths": "2", + "route_flap_dampen": "false" + }, + "default|ipv6_unicast": { + "ibgp_equal_cluster_length": "false", + "max_ebgp_paths": "2", + "max_ibgp_paths": "2" + }, + "default|l2vpn_evpn": { + "advertise-all-vni": "true", + "advertise-svi-ip": "true", + "dad-enabled": "true" + } + }, + "BGP_GLOBALS_AF_NETWORK": { + "default|ipv4_unicast|192.168.20.11/32": {} + }, + "BGP_GLOBALS_ROUTE_ADVERTISE": { + "default|L2VPN_EVPN|IPV4_UNICAST": {}, + "default|L2VPN_EVPN|IPV6_UNICAST": {} + }, + "BGP_NEIGHBOR": { + "default|192.168.30.0": { + "local_addr": "192.168.30.1", + "peer_type": "external", + "v6only": "false" + } + }, + "BGP_NEIGHBOR_AF": { + "default|Ethernet0|ipv4_unicast": { + "admin_status": "true" + }, + "default|Ethernet0|l2vpn_evpn": { + "admin_status": "true" + } + }, + "BREAKOUT_CFG": {}, + "BREAKOUT_PORTS": {}, + "DEVICE_METADATA": { + "localhost": { + "hostname": "e2e-leaf-1", + "hwsku": "Accton-AS7726-32X", + "mac": "00:00:00:00:00:00", + "platform": "x86_64-accton_as7726_32x-r0" + } + }, + "DNS_NAMESERVER": { + "172.16.10.254": {} + }, + "INTERFACE": { + "Ethernet0": {}, + "Ethernet0|192.168.30.1/31": {} + }, + "LOOPBACK": { + "Loopback0": { + "admin_status": "up" + } + }, + "LOOPBACK_INTERFACE": { + "Loopback0": {}, + "Loopback0|192.168.20.11/32": {} + }, + "MGMT_INTERFACE": { + "eth0": { + "admin_status": "up" + }, + "eth0|172.16.10.11/20": {} + }, + "NTP_SERVER": { + "172.16.10.254": { + "maxpoll": "10", + "minpoll": "6", + "prefer": "false" + } + }, + "PORT": { + "Ethernet0": { + "admin_status": "up", + "adv_speeds": "all", + "alias": "Eth1/1", + "autoneg": "off", + "index": "1", + "lanes": "1,2,3,4", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet100": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/26", + "autoneg": "off", + "index": "26", + "lanes": "101,102,103,104", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet104": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/27", + "autoneg": "off", + "index": "27", + "lanes": "105,106,107,108", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet108": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/28", + "autoneg": "off", + "index": "28", + "lanes": "109,110,111,112", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet112": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/29", + "autoneg": "off", + "index": "29", + "lanes": "113,114,115,116", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet116": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/30", + "autoneg": "off", + "index": "30", + "lanes": "117,118,119,120", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet12": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/4", + "autoneg": "off", + "index": "4", + "lanes": "13,14,15,16", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "tagged_vlans": [ + "200" + ], + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet120": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/31", + "autoneg": "off", + "index": "31", + "lanes": "121,122,123,124", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet124": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/32", + "autoneg": "off", + "index": "32", + "lanes": "125,126,127,128", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet125": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/33", + "autoneg": "off", + "index": "33", + "lanes": "129", + "link_training": "off", + "mtu": "9100", + "speed": "10000", + "unreliable_los": "auto", + "valid_speeds": "10000,1000" + }, + "Ethernet126": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/34", + "autoneg": "off", + "index": "34", + "lanes": "128", + "link_training": "off", + "mtu": "9100", + "speed": "10000", + "unreliable_los": "auto", + "valid_speeds": "10000,1000" + }, + "Ethernet16": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/5", + "autoneg": "off", + "index": "5", + "lanes": "17,18,19,20", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet20": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/6", + "autoneg": "off", + "index": "6", + "lanes": "21,22,23,24", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet24": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/7", + "autoneg": "off", + "index": "7", + "lanes": "25,26,27,28", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet28": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/8", + "autoneg": "off", + "index": "8", + "lanes": "29,30,31,32", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet32": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/9", + "autoneg": "off", + "index": "9", + "lanes": "33,34,35,36", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet36": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/10", + "autoneg": "off", + "index": "10", + "lanes": "37,38,39,40", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet4": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/2", + "autoneg": "off", + "index": "2", + "lanes": "5,6,7,8", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet40": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/11", + "autoneg": "off", + "index": "11", + "lanes": "41,42,43,44", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet44": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/12", + "autoneg": "off", + "index": "12", + "lanes": "45,46,47,48", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet48": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/13", + "autoneg": "off", + "index": "13", + "lanes": "49,50,51,52", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet52": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/14", + "autoneg": "off", + "index": "14", + "lanes": "53,54,55,56", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet56": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/15", + "autoneg": "off", + "index": "15", + "lanes": "57,58,59,60", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet60": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/16", + "autoneg": "off", + "index": "16", + "lanes": "61,62,63,64", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet64": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/17", + "autoneg": "off", + "index": "17", + "lanes": "65,66,67,68", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet68": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/18", + "autoneg": "off", + "index": "18", + "lanes": "69,70,71,72", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet72": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/19", + "autoneg": "off", + "index": "19", + "lanes": "73,74,75,76", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet76": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/20", + "autoneg": "off", + "index": "20", + "lanes": "77,78,79,80", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet8": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/3", + "autoneg": "off", + "index": "3", + "lanes": "9,10,11,12", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet80": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/21", + "autoneg": "off", + "index": "21", + "lanes": "81,82,83,84", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet84": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/22", + "autoneg": "off", + "index": "22", + "lanes": "85,86,87,88", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet88": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/23", + "autoneg": "off", + "index": "23", + "lanes": "89,90,91,92", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet92": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/24", + "autoneg": "off", + "index": "24", + "lanes": "93,94,95,96", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet96": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/25", + "autoneg": "off", + "index": "25", + "lanes": "97,98,99,100", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + } + }, + "PORTCHANNEL": {}, + "PORTCHANNEL_INTERFACE": {}, + "PORTCHANNEL_MEMBER": {}, + "ROUTE_REDISTRIBUTE": { + "default|connected|bgp|ipv4": {}, + "default|connected|bgp|ipv6": {} + }, + "SNMP_AGENT_ADDRESS_CONFIG": { + "172.16.10.11|161|mgmt": { + "name": "agentEntry1" + } + }, + "SNMP_SERVER": { + "SYSTEM": { + "sysContact": "e2e@example.com", + "sysLocation": "E2E Lab", + "traps": "enable" + } + }, + "SNMP_SERVER_GROUP_MEMBER": { + "monitoring|e2e-monitor": { + "securityModel": [ + "usm" + ] + } + }, + "SNMP_SERVER_PARAMS": { + "targetEntry1": { + "security-level": "auth-priv", + "user": "e2e-monitor" + } + }, + "SNMP_SERVER_TARGET": { + "targetEntry1": { + "ip": "172.16.10.254", + "port": "162", + "retries": "3", + "tag": [ + "trapNotify", + "mgmt" + ], + "targetParams": "targetEntry1", + "timeout": "1500" + } + }, + "SNMP_SERVER_USER": { + "e2e-monitor": { + "aesKey": "OBFUSCATEDPRIVSECRET", + "shaKey": "OBFUSCATEDAUTHSECRET" + } + }, + "STATIC_ROUTE": { + "mgmt|0.0.0.0/0": { + "nexthop": "172.16.10.254" + } + }, + "SYSLOG_SERVER": { + "172.16.10.254": { + "message-type": "log", + "protocol": "UDP", + "remote-port": "514", + "severity": "info", + "vrf_name": "mgmt" + } + }, + "VERSIONS": { + "DATABASE": { + "VERSION": "version_4_0_1" + } + }, + "VLAN": { + "Vlan100": { + "admin_status": "up", + "autostate": "enable", + "members": [ + "Ethernet8" + ], + "vlanid": "100" + }, + "Vlan200": { + "admin_status": "up", + "autostate": "enable", + "members": [ + "Ethernet12" + ], + "vlanid": "200" + } + }, + "VLAN_INTERFACE": { + "Vlan200": { + "admin_status": "up" + }, + "Vlan200|192.168.40.1/24": {} + }, + "VLAN_MEMBER": { + "Vlan100|Ethernet8": { + "tagging_mode": "untagged" + }, + "Vlan200|Ethernet12": { + "tagging_mode": "tagged" + } + }, + "VRF": { + "default": { + "enabled": "true" + } + }, + "VXLAN_EVPN_NVO": {}, + "VXLAN_TUNNEL": {}, + "VXLAN_TUNNEL_MAP": {} +} diff --git a/tests/e2e/golden/osism_e2e-leaf-2_config_db.json b/tests/e2e/golden/osism_e2e-leaf-2_config_db.json new file mode 100644 index 00000000..1d52120d --- /dev/null +++ b/tests/e2e/golden/osism_e2e-leaf-2_config_db.json @@ -0,0 +1,683 @@ +{ + "ACL_RULE": { + "GNMI_ONLY|RULE_1": { + "IP_TYPE": "IP", + "L4_DST_PORT": "8080", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + }, + "SNMP_ONLY|RULE_1": { + "IP_TYPE": "IP", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + }, + "SSH_ONLY|RULE_1": { + "IP_TYPE": "IP", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + } + }, + "ACL_TABLE": { + "GNMI_ONLY": { + "policy_desc": "GNMI_ONLY", + "services": [ + "EXTERNAL_CLIENT" + ], + "type": "CTRLPLANE" + }, + "SNMP_ONLY": { + "policy_desc": "SNMP_ONLY", + "services": [ + "SNMP" + ], + "type": "CTRLPLANE" + }, + "SSH_ONLY": { + "policy_desc": "SSH_ONLY", + "services": [ + "SSH" + ], + "type": "CTRLPLANE" + } + }, + "BGP_GLOBALS": { + "Vrf99": { + "always_compare_med": "true", + "ebgp_requires_policy": "false", + "external_compare_router_id": "false", + "fast_external_failover": "true", + "holdtime": "180", + "ignore_as_path_length": "false", + "keepalive": "60", + "load_balance_mp_relax": "false", + "local_asn": "4200020012", + "log_nbr_state_changes": "true", + "network_import_check": "true", + "router_id": "192.168.20.12" + }, + "default": { + "always_compare_med": "true", + "ebgp_requires_policy": "false", + "external_compare_router_id": "false", + "fast_external_failover": "true", + "holdtime": "180", + "ignore_as_path_length": "false", + "keepalive": "60", + "load_balance_mp_relax": "false", + "local_asn": "4200020012", + "log_nbr_state_changes": "true", + "network_import_check": "true", + "router_id": "192.168.20.12" + } + }, + "BGP_GLOBALS_AF": { + "default|ipv4_unicast": { + "ibgp_equal_cluster_length": "false", + "max_ebgp_paths": "2", + "max_ibgp_paths": "2", + "route_flap_dampen": "false" + }, + "default|ipv6_unicast": { + "ibgp_equal_cluster_length": "false", + "max_ebgp_paths": "2", + "max_ibgp_paths": "2" + }, + "default|l2vpn_evpn": { + "advertise-all-vni": "true", + "advertise-svi-ip": "true", + "dad-enabled": "true" + } + }, + "BGP_GLOBALS_AF_NETWORK": { + "default|ipv4_unicast|192.168.20.12/32": {} + }, + "BGP_GLOBALS_ROUTE_ADVERTISE": { + "default|L2VPN_EVPN|IPV4_UNICAST": {}, + "default|L2VPN_EVPN|IPV6_UNICAST": {} + }, + "BGP_NEIGHBOR": { + "default|192.168.30.2": { + "local_addr": "192.168.30.3", + "peer_type": "external", + "v6only": "false" + } + }, + "BGP_NEIGHBOR_AF": { + "default|Ethernet0|ipv4_unicast": { + "admin_status": "true" + }, + "default|Ethernet0|l2vpn_evpn": { + "admin_status": "true" + } + }, + "BREAKOUT_CFG": {}, + "BREAKOUT_PORTS": {}, + "DEVICE_METADATA": { + "localhost": { + "hostname": "e2e-leaf-2", + "hwsku": "Accton-AS7726-32X", + "mac": "00:00:00:00:00:00", + "platform": "x86_64-accton_as7726_32x-r0" + } + }, + "DNS_NAMESERVER": { + "172.16.10.254": {} + }, + "INTERFACE": { + "Ethernet0": {}, + "Ethernet0|192.168.30.3/31": {} + }, + "LOOPBACK": { + "Loopback0": { + "admin_status": "up" + } + }, + "LOOPBACK_INTERFACE": { + "Loopback0": {}, + "Loopback0|192.168.20.12/32": {} + }, + "MGMT_INTERFACE": { + "eth0": { + "admin_status": "up" + }, + "eth0|172.16.10.12/20": {} + }, + "NTP_SERVER": { + "172.16.10.254": { + "maxpoll": "10", + "minpoll": "6", + "prefer": "false" + } + }, + "PORT": { + "Ethernet0": { + "admin_status": "up", + "adv_speeds": "all", + "alias": "Eth1/1", + "autoneg": "off", + "index": "1", + "lanes": "1,2,3,4", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet100": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/26", + "autoneg": "off", + "index": "26", + "lanes": "101,102,103,104", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet104": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/27", + "autoneg": "off", + "index": "27", + "lanes": "105,106,107,108", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet108": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/28", + "autoneg": "off", + "index": "28", + "lanes": "109,110,111,112", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet112": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/29", + "autoneg": "off", + "index": "29", + "lanes": "113,114,115,116", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet116": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/30", + "autoneg": "off", + "index": "30", + "lanes": "117,118,119,120", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet12": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/4", + "autoneg": "off", + "index": "4", + "lanes": "13,14,15,16", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet120": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/31", + "autoneg": "off", + "index": "31", + "lanes": "121,122,123,124", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet124": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/32", + "autoneg": "off", + "index": "32", + "lanes": "125,126,127,128", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet125": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/33", + "autoneg": "off", + "index": "33", + "lanes": "129", + "link_training": "off", + "mtu": "9100", + "speed": "10000", + "unreliable_los": "auto", + "valid_speeds": "10000,1000" + }, + "Ethernet126": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/34", + "autoneg": "off", + "index": "34", + "lanes": "128", + "link_training": "off", + "mtu": "9100", + "speed": "10000", + "unreliable_los": "auto", + "valid_speeds": "10000,1000" + }, + "Ethernet16": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/5", + "autoneg": "off", + "index": "5", + "lanes": "17,18,19,20", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet20": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/6", + "autoneg": "off", + "index": "6", + "lanes": "21,22,23,24", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet24": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/7", + "autoneg": "off", + "index": "7", + "lanes": "25,26,27,28", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet28": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/8", + "autoneg": "off", + "index": "8", + "lanes": "29,30,31,32", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet32": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/9", + "autoneg": "off", + "index": "9", + "lanes": "33,34,35,36", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet36": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/10", + "autoneg": "off", + "index": "10", + "lanes": "37,38,39,40", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet4": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/2", + "autoneg": "off", + "index": "2", + "lanes": "5,6,7,8", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet40": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/11", + "autoneg": "off", + "index": "11", + "lanes": "41,42,43,44", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet44": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/12", + "autoneg": "off", + "index": "12", + "lanes": "45,46,47,48", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet48": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/13", + "autoneg": "off", + "index": "13", + "lanes": "49,50,51,52", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet52": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/14", + "autoneg": "off", + "index": "14", + "lanes": "53,54,55,56", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet56": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/15", + "autoneg": "off", + "index": "15", + "lanes": "57,58,59,60", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet60": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/16", + "autoneg": "off", + "index": "16", + "lanes": "61,62,63,64", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet64": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/17", + "autoneg": "off", + "index": "17", + "lanes": "65,66,67,68", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet68": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/18", + "autoneg": "off", + "index": "18", + "lanes": "69,70,71,72", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet72": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/19", + "autoneg": "off", + "index": "19", + "lanes": "73,74,75,76", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet76": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/20", + "autoneg": "off", + "index": "20", + "lanes": "77,78,79,80", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet8": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/3", + "autoneg": "off", + "index": "3", + "lanes": "9,10,11,12", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet80": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/21", + "autoneg": "off", + "index": "21", + "lanes": "81,82,83,84", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet84": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/22", + "autoneg": "off", + "index": "22", + "lanes": "85,86,87,88", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet88": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/23", + "autoneg": "off", + "index": "23", + "lanes": "89,90,91,92", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet92": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/24", + "autoneg": "off", + "index": "24", + "lanes": "93,94,95,96", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet96": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/25", + "autoneg": "off", + "index": "25", + "lanes": "97,98,99,100", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + } + }, + "PORTCHANNEL": {}, + "PORTCHANNEL_INTERFACE": {}, + "PORTCHANNEL_MEMBER": {}, + "ROUTE_REDISTRIBUTE": { + "default|connected|bgp|ipv4": {}, + "default|connected|bgp|ipv6": {} + }, + "SNMP_AGENT_ADDRESS_CONFIG": { + "172.16.10.12|161|mgmt": { + "name": "agentEntry1" + } + }, + "SNMP_SERVER": { + "SYSTEM": { + "sysContact": "e2e@example.com", + "sysLocation": "E2E Lab", + "traps": "enable" + } + }, + "SNMP_SERVER_GROUP_MEMBER": { + "monitoring|e2e-monitor": { + "securityModel": [ + "usm" + ] + } + }, + "SNMP_SERVER_PARAMS": { + "targetEntry1": { + "security-level": "auth-priv", + "user": "e2e-monitor" + } + }, + "SNMP_SERVER_TARGET": { + "targetEntry1": { + "ip": "172.16.10.254", + "port": "162", + "retries": "3", + "tag": [ + "trapNotify", + "mgmt" + ], + "targetParams": "targetEntry1", + "timeout": "1500" + } + }, + "SNMP_SERVER_USER": { + "e2e-monitor": { + "aesKey": "OBFUSCATEDPRIVSECRET", + "shaKey": "OBFUSCATEDAUTHSECRET" + } + }, + "STATIC_ROUTE": { + "mgmt|0.0.0.0/0": { + "nexthop": "172.16.10.254" + } + }, + "SYSLOG_SERVER": { + "172.16.10.254": { + "message-type": "log", + "protocol": "UDP", + "remote-port": "514", + "severity": "info", + "vrf_name": "mgmt" + } + }, + "VERSIONS": { + "DATABASE": { + "VERSION": "version_4_0_1" + } + }, + "VLAN": {}, + "VLAN_INTERFACE": {}, + "VLAN_MEMBER": {}, + "VRF": { + "Vrf99": { + "vrf_table_id": 99 + }, + "default": { + "enabled": "true" + } + }, + "VXLAN_EVPN_NVO": {}, + "VXLAN_TUNNEL": {}, + "VXLAN_TUNNEL_MAP": {} +} diff --git a/tests/e2e/golden/osism_e2e-oob-1_config_db.json b/tests/e2e/golden/osism_e2e-oob-1_config_db.json new file mode 100644 index 00000000..e48a309f --- /dev/null +++ b/tests/e2e/golden/osism_e2e-oob-1_config_db.json @@ -0,0 +1,650 @@ +{ + "ACL_RULE": { + "GNMI_ONLY|RULE_1": { + "IP_TYPE": "IP", + "L4_DST_PORT": "8080", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + }, + "SNMP_ONLY|RULE_1": { + "IP_TYPE": "IP", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + }, + "SSH_ONLY|RULE_1": { + "IP_TYPE": "IP", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + } + }, + "ACL_TABLE": { + "GNMI_ONLY": { + "policy_desc": "GNMI_ONLY", + "services": [ + "EXTERNAL_CLIENT" + ], + "type": "CTRLPLANE" + }, + "SNMP_ONLY": { + "policy_desc": "SNMP_ONLY", + "services": [ + "SNMP" + ], + "type": "CTRLPLANE" + }, + "SSH_ONLY": { + "policy_desc": "SSH_ONLY", + "services": [ + "SSH" + ], + "type": "CTRLPLANE" + } + }, + "BGP_GLOBALS": { + "default": { + "always_compare_med": "true", + "ebgp_requires_policy": "false", + "external_compare_router_id": "false", + "fast_external_failover": "true", + "holdtime": "180", + "ignore_as_path_length": "false", + "keepalive": "60", + "load_balance_mp_relax": "false", + "local_asn": "4200020021", + "log_nbr_state_changes": "true", + "network_import_check": "true", + "router_id": "192.168.20.21" + } + }, + "BGP_GLOBALS_AF": { + "default|ipv4_unicast": { + "ibgp_equal_cluster_length": "false", + "max_ebgp_paths": "2", + "max_ibgp_paths": "2", + "route_flap_dampen": "false" + }, + "default|ipv6_unicast": { + "ibgp_equal_cluster_length": "false", + "max_ebgp_paths": "2", + "max_ibgp_paths": "2" + }, + "default|l2vpn_evpn": { + "advertise-all-vni": "true", + "advertise-svi-ip": "true", + "dad-enabled": "true" + } + }, + "BGP_GLOBALS_AF_NETWORK": { + "default|ipv4_unicast|192.168.20.21/32": {} + }, + "BGP_GLOBALS_ROUTE_ADVERTISE": { + "default|L2VPN_EVPN|IPV4_UNICAST": {}, + "default|L2VPN_EVPN|IPV6_UNICAST": {} + }, + "BGP_NEIGHBOR": {}, + "BGP_NEIGHBOR_AF": {}, + "BREAKOUT_CFG": {}, + "BREAKOUT_PORTS": {}, + "DEVICE_METADATA": { + "localhost": { + "hostname": "e2e-oob-1", + "hwsku": "Accton-AS7726-32X", + "mac": "00:00:00:00:00:00", + "platform": "x86_64-accton_as7726_32x-r0" + } + }, + "DNS_NAMESERVER": { + "172.16.10.254": {} + }, + "INTERFACE": {}, + "LOOPBACK": { + "Loopback0": { + "admin_status": "up" + } + }, + "LOOPBACK_INTERFACE": { + "Loopback0": {}, + "Loopback0|192.168.20.21/32": {} + }, + "MGMT_INTERFACE": { + "eth0": { + "admin_status": "up" + }, + "eth0|172.16.10.21/20": {} + }, + "NTP_SERVER": { + "172.16.10.254": { + "maxpoll": "10", + "minpoll": "6", + "prefer": "false" + } + }, + "PORT": { + "Ethernet0": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/1", + "autoneg": "off", + "index": "1", + "lanes": "1,2,3,4", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet100": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/26", + "autoneg": "off", + "index": "26", + "lanes": "101,102,103,104", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet104": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/27", + "autoneg": "off", + "index": "27", + "lanes": "105,106,107,108", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet108": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/28", + "autoneg": "off", + "index": "28", + "lanes": "109,110,111,112", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet112": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/29", + "autoneg": "off", + "index": "29", + "lanes": "113,114,115,116", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet116": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/30", + "autoneg": "off", + "index": "30", + "lanes": "117,118,119,120", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet12": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/4", + "autoneg": "off", + "index": "4", + "lanes": "13,14,15,16", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet120": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/31", + "autoneg": "off", + "index": "31", + "lanes": "121,122,123,124", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet124": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/32", + "autoneg": "off", + "index": "32", + "lanes": "125,126,127,128", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet125": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/33", + "autoneg": "off", + "index": "33", + "lanes": "129", + "link_training": "off", + "mtu": "9100", + "speed": "10000", + "unreliable_los": "auto", + "valid_speeds": "10000,1000" + }, + "Ethernet126": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/34", + "autoneg": "off", + "index": "34", + "lanes": "128", + "link_training": "off", + "mtu": "9100", + "speed": "10000", + "unreliable_los": "auto", + "valid_speeds": "10000,1000" + }, + "Ethernet16": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/5", + "autoneg": "off", + "index": "5", + "lanes": "17,18,19,20", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet20": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/6", + "autoneg": "off", + "index": "6", + "lanes": "21,22,23,24", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet24": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/7", + "autoneg": "off", + "index": "7", + "lanes": "25,26,27,28", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet28": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/8", + "autoneg": "off", + "index": "8", + "lanes": "29,30,31,32", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet32": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/9", + "autoneg": "off", + "index": "9", + "lanes": "33,34,35,36", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet36": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/10", + "autoneg": "off", + "index": "10", + "lanes": "37,38,39,40", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet4": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/2", + "autoneg": "off", + "index": "2", + "lanes": "5,6,7,8", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet40": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/11", + "autoneg": "off", + "index": "11", + "lanes": "41,42,43,44", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet44": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/12", + "autoneg": "off", + "index": "12", + "lanes": "45,46,47,48", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet48": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/13", + "autoneg": "off", + "index": "13", + "lanes": "49,50,51,52", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet52": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/14", + "autoneg": "off", + "index": "14", + "lanes": "53,54,55,56", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet56": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/15", + "autoneg": "off", + "index": "15", + "lanes": "57,58,59,60", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet60": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/16", + "autoneg": "off", + "index": "16", + "lanes": "61,62,63,64", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet64": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/17", + "autoneg": "off", + "index": "17", + "lanes": "65,66,67,68", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet68": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/18", + "autoneg": "off", + "index": "18", + "lanes": "69,70,71,72", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet72": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/19", + "autoneg": "off", + "index": "19", + "lanes": "73,74,75,76", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet76": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/20", + "autoneg": "off", + "index": "20", + "lanes": "77,78,79,80", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet8": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/3", + "autoneg": "off", + "index": "3", + "lanes": "9,10,11,12", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet80": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/21", + "autoneg": "off", + "index": "21", + "lanes": "81,82,83,84", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet84": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/22", + "autoneg": "off", + "index": "22", + "lanes": "85,86,87,88", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet88": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/23", + "autoneg": "off", + "index": "23", + "lanes": "89,90,91,92", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet92": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/24", + "autoneg": "off", + "index": "24", + "lanes": "93,94,95,96", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet96": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/25", + "autoneg": "off", + "index": "25", + "lanes": "97,98,99,100", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + } + }, + "PORTCHANNEL": {}, + "PORTCHANNEL_INTERFACE": {}, + "PORTCHANNEL_MEMBER": {}, + "ROUTE_REDISTRIBUTE": { + "default|connected|bgp|ipv4": {}, + "default|connected|bgp|ipv6": {} + }, + "SNMP_AGENT_ADDRESS_CONFIG": { + "172.16.10.21|161|mgmt": { + "name": "agentEntry1" + } + }, + "SNMP_SERVER": { + "SYSTEM": { + "sysContact": "e2e@example.com", + "sysLocation": "E2E Lab", + "traps": "enable" + } + }, + "SNMP_SERVER_GROUP_MEMBER": { + "monitoring|e2e-monitor": { + "securityModel": [ + "usm" + ] + } + }, + "SNMP_SERVER_PARAMS": { + "targetEntry1": { + "security-level": "auth-priv", + "user": "e2e-monitor" + } + }, + "SNMP_SERVER_TARGET": { + "targetEntry1": { + "ip": "172.16.10.254", + "port": "162", + "retries": "3", + "tag": [ + "trapNotify", + "mgmt" + ], + "targetParams": "targetEntry1", + "timeout": "1500" + } + }, + "SNMP_SERVER_USER": { + "e2e-monitor": { + "aesKey": "OBFUSCATEDPRIVSECRET", + "shaKey": "OBFUSCATEDAUTHSECRET" + } + }, + "STATIC_ROUTE": { + "mgmt|0.0.0.0/0": { + "nexthop": "172.16.10.254" + } + }, + "SYSLOG_SERVER": { + "172.16.10.254": { + "message-type": "log", + "protocol": "UDP", + "remote-port": "514", + "severity": "info", + "vrf_name": "mgmt" + } + }, + "VERSIONS": { + "DATABASE": { + "VERSION": "version_4_0_1" + } + }, + "VLAN": {}, + "VLAN_INTERFACE": {}, + "VLAN_MEMBER": {}, + "VRF": { + "default": { + "enabled": "true" + } + }, + "VXLAN_EVPN_NVO": {}, + "VXLAN_TUNNEL": {}, + "VXLAN_TUNNEL_MAP": {} +} diff --git a/tests/e2e/golden/osism_e2e-spine-1_config_db.json b/tests/e2e/golden/osism_e2e-spine-1_config_db.json new file mode 100644 index 00000000..ced04a8a --- /dev/null +++ b/tests/e2e/golden/osism_e2e-spine-1_config_db.json @@ -0,0 +1,679 @@ +{ + "ACL_RULE": { + "GNMI_ONLY|RULE_1": { + "IP_TYPE": "IP", + "L4_DST_PORT": "8080", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + }, + "SNMP_ONLY|RULE_1": { + "IP_TYPE": "IP", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + }, + "SSH_ONLY|RULE_1": { + "IP_TYPE": "IP", + "PACKET_ACTION": "ACCEPT", + "PRIORITY": "9999", + "SRC_IP": "172.16.0.0/20" + } + }, + "ACL_TABLE": { + "GNMI_ONLY": { + "policy_desc": "GNMI_ONLY", + "services": [ + "EXTERNAL_CLIENT" + ], + "type": "CTRLPLANE" + }, + "SNMP_ONLY": { + "policy_desc": "SNMP_ONLY", + "services": [ + "SNMP" + ], + "type": "CTRLPLANE" + }, + "SSH_ONLY": { + "policy_desc": "SSH_ONLY", + "services": [ + "SSH" + ], + "type": "CTRLPLANE" + } + }, + "BGP_GLOBALS": { + "default": { + "always_compare_med": "true", + "ebgp_requires_policy": "false", + "external_compare_router_id": "false", + "fast_external_failover": "true", + "holdtime": "180", + "ignore_as_path_length": "false", + "keepalive": "60", + "load_balance_mp_relax": "false", + "local_asn": "4200020001", + "log_nbr_state_changes": "true", + "network_import_check": "true", + "router_id": "192.168.20.1" + } + }, + "BGP_GLOBALS_AF": { + "default|ipv4_unicast": { + "ibgp_equal_cluster_length": "false", + "max_ebgp_paths": "2", + "max_ibgp_paths": "2", + "route_flap_dampen": "false" + }, + "default|ipv6_unicast": { + "ibgp_equal_cluster_length": "false", + "max_ebgp_paths": "2", + "max_ibgp_paths": "2" + }, + "default|l2vpn_evpn": { + "advertise-all-vni": "true", + "advertise-svi-ip": "true", + "dad-enabled": "true" + } + }, + "BGP_GLOBALS_AF_NETWORK": { + "default|ipv4_unicast|192.168.20.1/32": {} + }, + "BGP_GLOBALS_ROUTE_ADVERTISE": { + "default|L2VPN_EVPN|IPV4_UNICAST": {}, + "default|L2VPN_EVPN|IPV6_UNICAST": {} + }, + "BGP_NEIGHBOR": { + "default|192.168.30.1": { + "local_addr": "192.168.30.0", + "peer_type": "external", + "v6only": "false" + }, + "default|192.168.30.3": { + "local_addr": "192.168.30.2", + "peer_type": "external", + "v6only": "false" + } + }, + "BGP_NEIGHBOR_AF": { + "default|Ethernet0|ipv4_unicast": { + "admin_status": "true" + }, + "default|Ethernet0|l2vpn_evpn": { + "admin_status": "true" + }, + "default|Ethernet4|ipv4_unicast": { + "admin_status": "true" + }, + "default|Ethernet4|l2vpn_evpn": { + "admin_status": "true" + } + }, + "BREAKOUT_CFG": {}, + "BREAKOUT_PORTS": {}, + "DEVICE_METADATA": { + "localhost": { + "hostname": "e2e-spine-1", + "hwsku": "Accton-AS7726-32X", + "mac": "00:00:00:00:00:00", + "platform": "x86_64-accton_as7726_32x-r0" + } + }, + "DNS_NAMESERVER": { + "172.16.10.254": {} + }, + "INTERFACE": { + "Ethernet0": {}, + "Ethernet0|192.168.30.0/31": {}, + "Ethernet4": {}, + "Ethernet4|192.168.30.2/31": {} + }, + "LOOPBACK": { + "Loopback0": { + "admin_status": "up" + } + }, + "LOOPBACK_INTERFACE": { + "Loopback0": {}, + "Loopback0|192.168.20.1/32": {} + }, + "MGMT_INTERFACE": { + "eth0": { + "admin_status": "up" + }, + "eth0|172.16.10.1/20": {} + }, + "NTP_SERVER": { + "172.16.10.254": { + "maxpoll": "10", + "minpoll": "6", + "prefer": "false" + } + }, + "PORT": { + "Ethernet0": { + "admin_status": "up", + "adv_speeds": "all", + "alias": "Eth1/1", + "autoneg": "off", + "index": "1", + "lanes": "1,2,3,4", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet100": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/26", + "autoneg": "off", + "index": "26", + "lanes": "101,102,103,104", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet104": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/27", + "autoneg": "off", + "index": "27", + "lanes": "105,106,107,108", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet108": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/28", + "autoneg": "off", + "index": "28", + "lanes": "109,110,111,112", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet112": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/29", + "autoneg": "off", + "index": "29", + "lanes": "113,114,115,116", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet116": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/30", + "autoneg": "off", + "index": "30", + "lanes": "117,118,119,120", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet12": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/4", + "autoneg": "off", + "index": "4", + "lanes": "13,14,15,16", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet120": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/31", + "autoneg": "off", + "index": "31", + "lanes": "121,122,123,124", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet124": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/32", + "autoneg": "off", + "index": "32", + "lanes": "125,126,127,128", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet125": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/33", + "autoneg": "off", + "index": "33", + "lanes": "129", + "link_training": "off", + "mtu": "9100", + "speed": "10000", + "unreliable_los": "auto", + "valid_speeds": "10000,1000" + }, + "Ethernet126": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/34", + "autoneg": "off", + "index": "34", + "lanes": "128", + "link_training": "off", + "mtu": "9100", + "speed": "10000", + "unreliable_los": "auto", + "valid_speeds": "10000,1000" + }, + "Ethernet16": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/5", + "autoneg": "off", + "index": "5", + "lanes": "17,18,19,20", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet20": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/6", + "autoneg": "off", + "index": "6", + "lanes": "21,22,23,24", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet24": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/7", + "autoneg": "off", + "index": "7", + "lanes": "25,26,27,28", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet28": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/8", + "autoneg": "off", + "index": "8", + "lanes": "29,30,31,32", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet32": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/9", + "autoneg": "off", + "index": "9", + "lanes": "33,34,35,36", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet36": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/10", + "autoneg": "off", + "index": "10", + "lanes": "37,38,39,40", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet4": { + "admin_status": "up", + "adv_speeds": "all", + "alias": "Eth1/2", + "autoneg": "off", + "index": "2", + "lanes": "5,6,7,8", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet40": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/11", + "autoneg": "off", + "index": "11", + "lanes": "41,42,43,44", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet44": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/12", + "autoneg": "off", + "index": "12", + "lanes": "45,46,47,48", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet48": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/13", + "autoneg": "off", + "index": "13", + "lanes": "49,50,51,52", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet52": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/14", + "autoneg": "off", + "index": "14", + "lanes": "53,54,55,56", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet56": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/15", + "autoneg": "off", + "index": "15", + "lanes": "57,58,59,60", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet60": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/16", + "autoneg": "off", + "index": "16", + "lanes": "61,62,63,64", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet64": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/17", + "autoneg": "off", + "index": "17", + "lanes": "65,66,67,68", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet68": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/18", + "autoneg": "off", + "index": "18", + "lanes": "69,70,71,72", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet72": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/19", + "autoneg": "off", + "index": "19", + "lanes": "73,74,75,76", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet76": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/20", + "autoneg": "off", + "index": "20", + "lanes": "77,78,79,80", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet8": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/3", + "autoneg": "off", + "index": "3", + "lanes": "9,10,11,12", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet80": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/21", + "autoneg": "off", + "index": "21", + "lanes": "81,82,83,84", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet84": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/22", + "autoneg": "off", + "index": "22", + "lanes": "85,86,87,88", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet88": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/23", + "autoneg": "off", + "index": "23", + "lanes": "89,90,91,92", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet92": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/24", + "autoneg": "off", + "index": "24", + "lanes": "93,94,95,96", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + }, + "Ethernet96": { + "admin_status": "down", + "adv_speeds": "all", + "alias": "Eth1/25", + "autoneg": "off", + "index": "25", + "lanes": "97,98,99,100", + "link_training": "off", + "mtu": "9100", + "speed": "100000", + "unreliable_los": "auto", + "valid_speeds": "100000,40000" + } + }, + "PORTCHANNEL": {}, + "PORTCHANNEL_INTERFACE": {}, + "PORTCHANNEL_MEMBER": {}, + "ROUTE_REDISTRIBUTE": { + "default|connected|bgp|ipv4": {}, + "default|connected|bgp|ipv6": {} + }, + "SNMP_AGENT_ADDRESS_CONFIG": { + "172.16.10.1|161|mgmt": { + "name": "agentEntry1" + } + }, + "SNMP_SERVER": { + "SYSTEM": { + "sysContact": "e2e@example.com", + "sysLocation": "E2E Lab", + "traps": "enable" + } + }, + "SNMP_SERVER_GROUP_MEMBER": { + "monitoring|e2e-monitor": { + "securityModel": [ + "usm" + ] + } + }, + "SNMP_SERVER_PARAMS": { + "targetEntry1": { + "security-level": "auth-priv", + "user": "e2e-monitor" + } + }, + "SNMP_SERVER_TARGET": { + "targetEntry1": { + "ip": "172.16.10.254", + "port": "162", + "retries": "3", + "tag": [ + "trapNotify", + "mgmt" + ], + "targetParams": "targetEntry1", + "timeout": "1500" + } + }, + "SNMP_SERVER_USER": { + "e2e-monitor": { + "aesKey": "OBFUSCATEDPRIVSECRET", + "shaKey": "OBFUSCATEDAUTHSECRET" + } + }, + "STATIC_ROUTE": { + "mgmt|0.0.0.0/0": { + "nexthop": "172.16.10.254" + } + }, + "SYSLOG_SERVER": { + "172.16.10.254": { + "message-type": "log", + "protocol": "UDP", + "remote-port": "514", + "severity": "info", + "vrf_name": "mgmt" + } + }, + "VERSIONS": { + "DATABASE": { + "VERSION": "version_4_0_1" + } + }, + "VLAN": {}, + "VLAN_INTERFACE": {}, + "VLAN_MEMBER": {}, + "VRF": { + "default": { + "enabled": "true" + } + }, + "VXLAN_EVPN_NVO": {}, + "VXLAN_TUNNEL": {}, + "VXLAN_TUNNEL_MAP": {} +} diff --git a/tests/e2e/scenario/devicetypes/Edgecore/7726-32X-E2E.yaml b/tests/e2e/scenario/devicetypes/Edgecore/7726-32X-E2E.yaml new file mode 100644 index 00000000..8e16b9f4 --- /dev/null +++ b/tests/e2e/scenario/devicetypes/Edgecore/7726-32X-E2E.yaml @@ -0,0 +1,27 @@ +--- +# Minimal device type shared by two SONiC E2E scenarios: the base +# spine/leaf/OOB fabric (100-base.yml/200-fabric.yml/250-oob.yml/ +# 260-metalbox.yml) and the port-channel scenario (600-portchannel.yml). +# +# Not a faithful model of the real 7726-32X; it defines only a management +# port and two data ports. The fabric scenario uses the two data ports as +# plain point-to-point uplinks (any access/trunk/VLAN ports it needs are +# created separately, not from this device type); the port-channel scenario +# instead bonds them into a LAG. Config generation is driven by the hwsku +# custom field (Accton-AS7726-32X on the scenario devices), not by this +# device type -- the device type only controls which NetBox interfaces +# exist. The LAG interface itself is created in the port-channel scenario's +# resources overlay (type: lag) with these two ports as members. +manufacturer: Edgecore +model: 7726-32X-E2E +slug: edgecore-7726-32x-e2e +u_height: 1.0 +is_full_depth: true +interfaces: + - name: eth0 + type: 1000base-t + mgmt_only: true + - name: Ethernet0 + type: 100gbase-x-qsfp28 + - name: Ethernet4 + type: 100gbase-x-qsfp28 diff --git a/tests/e2e/scenario/resources/100-base.yml b/tests/e2e/scenario/resources/100-base.yml new file mode 100644 index 00000000..ac70066a --- /dev/null +++ b/tests/e2e/scenario/resources/100-base.yml @@ -0,0 +1,118 @@ +--- +# Base objects for the SONiC E2E synthetic fixtures. +# +# Everything here is referenced (by name/slug) from the later numbered +# files in this directory, so it must be seeded first. `site`/`location`/ +# `tenant` are kept from testbed's own conventions (Discworld / +# Ankh-Morpork / Testbed) purely so the fixtures stay recognisable to +# anyone used to the retired testbed-derived seed data -- there is no +# functional dependency on osism/testbed itself. + +- vars: + site: Discworld + location: Ankh-Morpork + tenant: Testbed + +- tenant: + name: "{{ tenant }}" + slug: testbed + +- site: + name: "{{ site }}" + slug: discworld + +- location: + name: "{{ location }}" + slug: ankh-morpork + site: "{{ site }}" + +# Device roles. spine/leaf/switch are in DEFAULT_SONIC_ROLES, so a device +# tagged managed-by-metalbox with one of these roles gets a golden; metalbox +# is deliberately excluded from DEFAULT_SONIC_ROLES since the metalbox is +# not a SONiC device and must never be generated. +- device_role: + name: Spine + slug: spine + +- device_role: + name: Leaf + slug: leaf + +- device_role: + name: Switch + slug: switch + +- device_role: + name: Metalbox + slug: metalbox + +# Required by the device filter (status=active + this tag + role in +# DEFAULT_SONIC_ROLES). A device missing this tag is skipped silently, with +# no golden and no error. +- tag: + name: Managed by Metalbox + slug: managed-by-metalbox + +# The generator reads sonic_parameters.hwsku (and .version) from this field +# to decide whether -- and how -- to generate a device's SONiC config; PR 3 +# also stores its breakout_mode declaration under the same field. +- custom_field: + name: sonic_parameters + type: json + label: sonic parameters + description: SONiC parameters + required: false + object_types: + - dcim.device + +- ipam_role: + name: OOB + +# Referenced by the fabric's point-to-point link prefix in 200-fabric.yml. +# _get_transfer_role_ipv4_addresses() only treats a link as numbered +# (rather than IP-unnumbered) when its address falls inside a prefix with +# this role -- without it, BGP_NEIGHBOR/BGP_NEIGHBOR_AF stay empty even +# though the link is cabled and addressed. +- ipam_role: + name: Transfer + slug: transfer + +# VLAN 100 carries vlan_role: OOB because it is the fixture's real-world +# out-of-band management VLAN (matching how these are modelled in NetBox in +# practice); the generator does not currently read vlan_role itself. VLAN +# 200 below is deliberately role-less: it exists only to exercise the plain +# tagged-VLAN path (untagged/tagged port coverage in 200-fabric.yml) and has +# no OOB-like real-world counterpart, so giving it a role would be +# unmotivated decoration. This is intentional, not an oversight -- don't +# "fix" the asymmetry by adding one, since regenerating after that edit +# would move goldens for no functional reason. +- vlan: + name: oob + vid: 100 + site: "{{ site }}" + tenant: "{{ tenant }}" + vlan_role: OOB + +- vlan: + name: data + vid: 200 + site: "{{ site }}" + tenant: "{{ tenant }}" + +- prefix: + family: 4 + prefix: 172.16.0.0/20 + prefix_role: OOB + tenant: "{{ tenant }}" + +- prefix: + family: 4 + prefix: 192.168.20.0/24 + tenant: "{{ tenant }}" + +- rack: + name: E2E + site: "{{ site }}" + location: "{{ location }}" + tenant: "{{ tenant }}" + u_height: 47 diff --git a/tests/e2e/scenario/resources/150-context.yml b/tests/e2e/scenario/resources/150-context.yml new file mode 100644 index 00000000..5a8937cd --- /dev/null +++ b/tests/e2e/scenario/resources/150-context.yml @@ -0,0 +1,28 @@ +--- +# Config context supplying the segment-level log-server and SNMP settings +# every fixture device inherits through the Testbed tenant (every device in +# this scenario belongs to it). +# +# _segment_log_server_hosts unlocks SYSLOG_SERVER. _segment_snmp_server_username +# unlocks SNMP_SERVER_USER and SNMP_SERVER_GROUP_MEMBER; +# _segment_snmp_server_hosts additionally unlocks SNMP_SERVER_PARAMS and +# SNMP_SERVER_TARGET. No `secrets` custom field is set anywhere in this +# scenario, so get_vault() is never called and the SNMP auth/priv passwords +# fall back to the literals OBFUSCATEDAUTHSECRET / OBFUSCATEDPRIVSECRET -- +# that is the evidence the no-vault path was exercised. + +- config_context: + name: e2e-segment + tenants: + - Testbed + data: + _segment_log_server_hosts: + - 172.16.10.254 + _segment_log_server_proto: udp + _segment_log_server_severity: info + _segment_log_server_vrf: mgmt + _segment_snmp_server_username: e2e-monitor + _segment_snmp_server_hosts: + - 172.16.10.254 + _segment_snmp_server_location: E2E Lab + _segment_snmp_server_contact: e2e@example.com diff --git a/tests/e2e/scenario/resources/200-fabric.yml b/tests/e2e/scenario/resources/200-fabric.yml new file mode 100644 index 00000000..b101825a --- /dev/null +++ b/tests/e2e/scenario/resources/200-fabric.yml @@ -0,0 +1,258 @@ +--- +# Spine/leaf fabric for the SONiC E2E base scenario: one spine and two +# leaves, cabled spine<->leaf, one leaf carrying an access and a trunk +# port, and one VRF assigned to a data port. +# +# oob_ip / Loopback0 values are fixed by the fixture value contract in the +# plan (docs/superpowers/plans/2026-07-30-sonic-e2e-synthetic-fixtures.md): +# the ASN is derived from Loopback0 (4200 + zero-padded 3rd/4th octet), so +# these addresses are not free choices. +# +# Interface names come from the Accton-AS7726-32X hwsku's port_config +# (files/sonic/port_config/Accton-AS7726-32X.ini), not from the minimal +# edgecore-7726-32x-e2e device type -- that device type only defines eth0, +# Ethernet0 and Ethernet4, so every other interface used here is created +# explicitly via device_interface stanzas. + +# ---------------------------------------------------------------- spine -- +- device: + name: e2e-spine-1 + tenant: Testbed + site: Discworld + location: Ankh-Morpork + rack: E2E + device_type: edgecore-7726-32x-e2e + device_role: spine + status: active + face: front + position: 1 + tags: + - managed-by-metalbox + custom_fields: + sonic_parameters: + hwsku: Accton-AS7726-32X + version: 4.5.0 + +- device_interface: + device: e2e-spine-1 + name: Loopback0 + type: virtual + enabled: true + +- ip_address: + tenant: Testbed + address: 172.16.10.1/20 + assigned_object: + name: eth0 + device: e2e-spine-1 + +- ip_address: + tenant: Testbed + address: 192.168.20.1/32 + assigned_object: + name: Loopback0 + device: e2e-spine-1 + +- device: + name: e2e-spine-1 + oob_ip: 172.16.10.1/20 + primary_ip4: 192.168.20.1/32 + +# --------------------------------------------------------------- leaf-1 -- +- device: + name: e2e-leaf-1 + tenant: Testbed + site: Discworld + location: Ankh-Morpork + rack: E2E + device_type: edgecore-7726-32x-e2e + device_role: leaf + status: active + face: front + position: 2 + tags: + - managed-by-metalbox + custom_fields: + sonic_parameters: + hwsku: Accton-AS7726-32X + version: 4.5.0 + +- device_interface: + device: e2e-leaf-1 + name: Loopback0 + type: virtual + enabled: true + +- ip_address: + tenant: Testbed + address: 172.16.10.11/20 + assigned_object: + name: eth0 + device: e2e-leaf-1 + +- ip_address: + tenant: Testbed + address: 192.168.20.11/32 + assigned_object: + name: Loopback0 + device: e2e-leaf-1 + +- device: + name: e2e-leaf-1 + oob_ip: 172.16.10.11/20 + primary_ip4: 192.168.20.11/32 + +# --------------------------------------------------------------- leaf-2 -- +- device: + name: e2e-leaf-2 + tenant: Testbed + site: Discworld + location: Ankh-Morpork + rack: E2E + device_type: edgecore-7726-32x-e2e + device_role: leaf + status: active + face: front + position: 3 + tags: + - managed-by-metalbox + custom_fields: + sonic_parameters: + hwsku: Accton-AS7726-32X + version: 4.5.0 + +- device_interface: + device: e2e-leaf-2 + name: Loopback0 + type: virtual + enabled: true + +- ip_address: + tenant: Testbed + address: 172.16.10.12/20 + assigned_object: + name: eth0 + device: e2e-leaf-2 + +- ip_address: + tenant: Testbed + address: 192.168.20.12/32 + assigned_object: + name: Loopback0 + device: e2e-leaf-2 + +- device: + name: e2e-leaf-2 + oob_ip: 172.16.10.12/20 + primary_ip4: 192.168.20.12/32 + +# ---------------------------------------------------------- fabric links -- +# Point-to-point link addresses must fall inside a prefix with the +# Transfer role, or _get_transfer_role_ipv4_addresses() treats the link as +# IP-unnumbered and BGP_NEIGHBOR/BGP_NEIGHBOR_AF stay empty even though the +# link is cabled and addressed. +- prefix: + family: 4 + prefix: 192.168.30.0/24 + prefix_role: Transfer + tenant: Testbed + +# e2e-spine-1 Ethernet0 <-> e2e-leaf-1 Ethernet0, on a /31. +- cable: + termination_a_type: dcim.interface + termination_a: + device: e2e-spine-1 + name: Ethernet0 + termination_b_type: dcim.interface + termination_b: + device: e2e-leaf-1 + name: Ethernet0 + +- ip_address: + tenant: Testbed + address: 192.168.30.0/31 + assigned_object: + name: Ethernet0 + device: e2e-spine-1 + +- ip_address: + tenant: Testbed + address: 192.168.30.1/31 + assigned_object: + name: Ethernet0 + device: e2e-leaf-1 + +# e2e-spine-1 Ethernet4 <-> e2e-leaf-2 Ethernet0, on a /31. +- cable: + termination_a_type: dcim.interface + termination_a: + device: e2e-spine-1 + name: Ethernet4 + termination_b_type: dcim.interface + termination_b: + device: e2e-leaf-2 + name: Ethernet0 + +- ip_address: + tenant: Testbed + address: 192.168.30.2/31 + assigned_object: + name: Ethernet4 + device: e2e-spine-1 + +- ip_address: + tenant: Testbed + address: 192.168.30.3/31 + assigned_object: + name: Ethernet0 + device: e2e-leaf-2 + +# ------------------------------------------------------ leaf-1 VLAN ports -- +# Access port with untagged VLAN 100 (oob) and a trunk port with tagged +# VLAN 200 (data), so VLAN, VLAN_INTERFACE and VLAN_MEMBER populate. +- device_interface: + device: e2e-leaf-1 + name: Ethernet8 + type: 100gbase-x-qsfp28 + mode: access + untagged_vlan: + name: oob + site: Discworld + +- device_interface: + device: e2e-leaf-1 + name: Ethernet12 + type: 100gbase-x-qsfp28 + mode: tagged + tagged_vlans: + - name: data + site: Discworld + +# VLAN_INTERFACE (SVI) needs an actual NetBox virtual interface named +# Vlan carrying an IP address -- VLAN_MEMBER from the ports above +# does not by itself create one. +- device_interface: + device: e2e-leaf-1 + name: Vlan200 + type: virtual + enabled: true + +- ip_address: + tenant: Testbed + address: 192.168.40.1/24 + assigned_object: + name: Vlan200 + device: e2e-leaf-1 + +# ------------------------------------------------------------------ VRF -- +# vrf99 has no RD, so it takes the table_id-only branch (VRF["Vrf99"] = +# {vrf_table_id: 99}), which is enough to populate VRF, the BGP_GLOBALS* +# family and ROUTE_REDISTRIBUTE without pulling in EVPN/VXLAN. +- vrf: + name: vrf99 + tenant: Testbed + +- device_interface: + device: e2e-leaf-2 + name: Ethernet4 + vrf: vrf99 diff --git a/tests/e2e/scenario/resources/250-oob.yml b/tests/e2e/scenario/resources/250-oob.yml new file mode 100644 index 00000000..bdb24bba --- /dev/null +++ b/tests/e2e/scenario/resources/250-oob.yml @@ -0,0 +1,47 @@ +--- +# Standalone OOB switch. Role "switch" is in DEFAULT_SONIC_ROLES, so this +# device also gets a golden -- it exercises the filter with a role other +# than spine/leaf. + +- device: + name: e2e-oob-1 + tenant: Testbed + site: Discworld + location: Ankh-Morpork + rack: E2E + device_type: edgecore-7726-32x-e2e + device_role: switch + status: active + face: front + position: 4 + tags: + - managed-by-metalbox + custom_fields: + sonic_parameters: + hwsku: Accton-AS7726-32X + version: 4.5.0 + +- device_interface: + device: e2e-oob-1 + name: Loopback0 + type: virtual + enabled: true + +- ip_address: + tenant: Testbed + address: 172.16.10.21/20 + assigned_object: + name: eth0 + device: e2e-oob-1 + +- ip_address: + tenant: Testbed + address: 192.168.20.21/32 + assigned_object: + name: Loopback0 + device: e2e-oob-1 + +- device: + name: e2e-oob-1 + oob_ip: 172.16.10.21/20 + primary_ip4: 192.168.20.21/32 diff --git a/tests/e2e/scenario/resources/260-metalbox.yml b/tests/e2e/scenario/resources/260-metalbox.yml new file mode 100644 index 00000000..fbffcc01 --- /dev/null +++ b/tests/e2e/scenario/resources/260-metalbox.yml @@ -0,0 +1,40 @@ +--- +# The metalbox. Role "metalbox" is NOT in DEFAULT_SONIC_ROLES and this +# device is deliberately NOT tagged managed-by-metalbox, so it must never +# get a golden of its own -- if one appears, this device wrongly acquired a +# SONiC role or the tag. +# +# _get_metalbox_ip_for_device() walks every role=metalbox device's +# interfaces and returns the first IPv4 that falls inside the SONiC +# device's OOB network; it does not follow a cable. All four switches use +# /20 oob_ips, so that network is 172.16.0.0/20, and 172.16.10.254 sits +# inside it -- that subnet membership, not any cabling, is what unlocks +# DNS_NAMESERVER and NTP_SERVER. +# +# The address must NOT sit on a mgmt_only interface: +# _load_metalbox_devices_cache() explicitly skips those, so it cannot be on +# eth0 (mgmt_only: true in the device type). Ethernet0 is a plain data port +# on this device type and is otherwise unused here. + +- device: + name: e2e-metalbox-1 + tenant: Testbed + site: Discworld + location: Ankh-Morpork + rack: E2E + device_type: edgecore-7726-32x-e2e + device_role: metalbox + status: active + face: front + position: 5 + +- ip_address: + tenant: Testbed + address: 172.16.10.254/20 + assigned_object: + name: Ethernet0 + device: e2e-metalbox-1 + +- device: + name: e2e-metalbox-1 + primary_ip4: 172.16.10.254/20 From 61fa8dbceb4a8489f7138432a1261bef4dd4adb5 Mon Sep 17 00:00:00 2001 From: Roger Luethi Date: Wed, 5 Aug 2026 08:55:52 +0200 Subject: [PATCH 2/4] tests/e2e: report config_db table coverage The "N of M config_db tables covered" claim made whenever the golden set grows had only ever been established by an ad-hoc script run once during development, so nobody could re-derive or check it afterwards. Add tests/e2e/coverage.py, run by `make sonic-e2e-coverage`. It works in two independent steps: derive the tables the generator can emit from config["TABLE"]/cfg["TABLE"] assignments under osism/tasks/conductor/sonic/ (excluding the generated schema package, which is data rather than emission logic), then collect the tables that are non-empty in at least one file under tests/e2e/golden/. Both sides are derived on every run, so this needs no updating as scenarios are added -- each one simply makes the reported number go up. It is a reporting tool, not a gate: it is not wired into sonic_golden_test.sh or the Zuul job, and the golden comparison stays the only check that can fail a run. It does exit non-zero while any emitted table has no golden, which against the base fixtures alone is the honest answer -- 30 of 38, naming the eight tables the breakout, port-channel and EVPN scenarios go on to cover. Both halves are pure functions over a directory tree, so they are unit tested in tests/unit/e2e/, next to the comparator and the generation driver. The emission regex is where the risk sits: it produces the reported denominator and decides the exit code, and because a table that is non-empty in the goldens but missed by the regex does not fail the run, a silent narrowing would surface only as a quietly shrinking number. It lands here, with the first goldens, rather than with the last scenario, so the number is available and meaningful while the golden set is still being built up. Assisted-by: Claude:claude-opus-5 Signed-off-by: Roger Luethi --- Makefile | 8 +- tests/e2e/coverage.py | 89 ++++++++++++++++++ tests/unit/e2e/test_coverage.py | 162 ++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/coverage.py create mode 100644 tests/unit/e2e/test_coverage.py diff --git a/Makefile b/Makefile index 05a6a103..0ac8b869 100644 --- a/Makefile +++ b/Makefile @@ -21,4 +21,10 @@ sonic-e2e-up: sonic-e2e-down: docker compose -f tests/e2e/compose.yaml down --volumes --remove-orphans -.PHONY: sonic-e2e sonic-e2e-regen sonic-e2e-up sonic-e2e-down +# Report config_db table coverage of the golden set (tests/e2e/coverage.py). +# A reporting tool only -- not part of the gating check, which stays the +# golden comparison run by sonic-e2e above. +sonic-e2e-coverage: + pipenv run python -m tests.e2e.coverage + +.PHONY: sonic-e2e sonic-e2e-regen sonic-e2e-up sonic-e2e-down sonic-e2e-coverage diff --git a/tests/e2e/coverage.py b/tests/e2e/coverage.py new file mode 100644 index 00000000..a9aded4e --- /dev/null +++ b/tests/e2e/coverage.py @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: Apache-2.0 + +"""config_db table coverage report for the SONiC E2E golden set. + +This is a reporting tool, not a gate: it is not invoked from +sonic_golden_test.sh, and the golden comparison (tests/e2e/compare.py) +remains the only check that actually fails a run. It exists so the "N of M +config_db tables covered" claim made when the golden set is extended can be +re-derived by anyone, at any time, instead of only having existed as an +ad-hoc one-off script run once during development. + +It works in two independent steps: + +1. Derive the set of tables the generator can emit by grepping + osism/tasks/conductor/sonic/ (excluding the generated schema package, + _generated/, which is data rather than emission logic) for direct + ``config["TABLE"]``/``cfg["TABLE"]`` assignments -- including a nested + item assignment such as ``config["ACL_TABLE"]["SSH_ONLY"] = ...`` or a + ``.update(...)`` call, but not a mere read such as + ``"x" in config["VERSIONS"]``. This is a static, syntactic approximation + of "tables the generator can populate", not a guarantee every branch + that reaches it is exercised. +2. Collect the set of tables that are non-empty in at least one file under + tests/e2e/golden/*.json. + +The report prints both counts and, if any emitted table is never non-empty +across the golden set, lists them and exits 1. +""" + +import re +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[2] +GENERATOR_DIR = REPO_ROOT / "osism" / "tasks" / "conductor" / "sonic" +GOLDEN_DIR = REPO_ROOT / "tests" / "e2e" / "golden" + +# Matches config["TABLE"] or cfg["TABLE"], optionally followed by one or +# more ["key"]/[expr] accessors, then either an assignment ("=" but not +# "==") or a .update(...) call -- i.e. the table is a write target, not +# merely read. +_TABLE_ASSIGNMENT_RE = re.compile( + r'(?:config|cfg)\["([A-Z][A-Z0-9_]*)"\](?:\[[^\]]*\])*\s*(?:=(?!=)|\.update\()' +) + + +def emitted_tables(generator_dir=GENERATOR_DIR): + """Tables the generator can emit, derived from source assignments.""" + tables = set() + for path in sorted(Path(generator_dir).rglob("*.py")): + if "_generated" in path.parts: + continue + for line in path.read_text().splitlines(): + if line.lstrip().startswith("#"): + continue + for match in _TABLE_ASSIGNMENT_RE.finditer(line): + tables.add(match.group(1)) + return tables + + +def covered_tables(golden_dir=GOLDEN_DIR): + """Tables that are non-empty in at least one golden file.""" + import json + + tables = set() + for path in sorted(Path(golden_dir).glob("*.json")): + config = json.loads(path.read_text()) + for table, value in config.items(): + if value: + tables.add(table) + return tables + + +def main(argv=None): + emitted = emitted_tables() + covered = covered_tables() + missing = sorted(emitted - covered) + + print(f"generator emits {len(emitted)} tables; {len(covered)} non-empty") + if missing: + print("not covered by any golden file:") + for table in missing: + print(f" {table}") + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/unit/e2e/test_coverage.py b/tests/unit/e2e/test_coverage.py new file mode 100644 index 00000000..b165cc0f --- /dev/null +++ b/tests/unit/e2e/test_coverage.py @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: Apache-2.0 + +"""Unit tests for the E2E config_db coverage report. + +Both halves of tests/e2e/coverage.py are pure functions over a directory +tree, so they are tested here as part of the regular unit suite. The +emitted-table regex carries most of the risk: it produces the denominator +of the "N of M config_db tables covered" claim and decides the exit code, +and a silent widening or narrowing of it would otherwise go unnoticed. +""" + +import json + +from tests.e2e import coverage +from tests.e2e.coverage import covered_tables, emitted_tables, main + + +class TestEmittedTables: + @staticmethod + def _write(directory, name, source): + path = directory / name + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(source) + + def test_direct_assignment_is_an_emission(self, tmp_path): + self._write(tmp_path, "port.py", 'config["PORT"] = {}\n') + + assert emitted_tables(tmp_path) == {"PORT"} + + def test_cfg_alias_is_recognised(self, tmp_path): + self._write(tmp_path, "vlan.py", 'cfg["VLAN"] = {}\n') + + assert emitted_tables(tmp_path) == {"VLAN"} + + def test_nested_item_assignment_is_an_emission(self, tmp_path): + self._write(tmp_path, "acl.py", 'config["ACL_TABLE"]["SSH_ONLY"] = {}\n') + + assert emitted_tables(tmp_path) == {"ACL_TABLE"} + + def test_update_call_is_an_emission(self, tmp_path): + self._write(tmp_path, "meta.py", 'config["DEVICE_METADATA"].update(other)\n') + + assert emitted_tables(tmp_path) == {"DEVICE_METADATA"} + + def test_membership_read_is_not_an_emission(self, tmp_path): + self._write(tmp_path, "versions.py", 'if "DATABASE" in config["VERSIONS"]:\n') + + assert emitted_tables(tmp_path) == set() + + def test_equality_comparison_is_not_an_emission(self, tmp_path): + self._write(tmp_path, "guard.py", 'if config["PORT"] == {}:\n') + + assert emitted_tables(tmp_path) == set() + + def test_read_on_the_right_hand_side_is_not_an_emission(self, tmp_path): + self._write( + tmp_path, "copy.py", 'config["INTERFACE"]["x"] = config["PORT"]["y"]\n' + ) + + assert emitted_tables(tmp_path) == {"INTERFACE"} + + def test_commented_out_assignment_is_ignored(self, tmp_path): + self._write(tmp_path, "todo.py", ' # config["VXLAN_TUNNEL"] = {}\n') + + assert emitted_tables(tmp_path) == set() + + def test_generated_schema_package_is_excluded(self, tmp_path): + self._write(tmp_path, "_generated/schema.py", 'config["SFLOW"] = {}\n') + + assert emitted_tables(tmp_path) == set() + + def test_subdirectories_are_searched(self, tmp_path): + self._write(tmp_path, "sub/bgp.py", 'config["BGP_NEIGHBOR"] = {}\n') + + assert emitted_tables(tmp_path) == {"BGP_NEIGHBOR"} + + def test_non_python_files_are_ignored(self, tmp_path): + self._write(tmp_path, "notes.txt", 'config["MIRROR_SESSION"] = {}\n') + + assert emitted_tables(tmp_path) == set() + + def test_lowercase_keys_are_not_table_names(self, tmp_path): + self._write(tmp_path, "local.py", 'config["scratch"] = {}\n') + + assert emitted_tables(tmp_path) == set() + + def test_tables_accumulate_across_files(self, tmp_path): + self._write(tmp_path, "a.py", 'config["PORT"] = {}\n') + self._write(tmp_path, "b.py", 'config["VLAN"] = {}\n') + + assert emitted_tables(tmp_path) == {"PORT", "VLAN"} + + +class TestCoveredTables: + @staticmethod + def _write(directory, name, config): + (directory / name).write_text(json.dumps(config)) + + def test_populated_table_is_covered(self, tmp_path): + self._write(tmp_path, "sw1.json", {"PORT": {"Ethernet0": {"speed": "100000"}}}) + + assert covered_tables(tmp_path) == {"PORT"} + + def test_empty_table_is_not_covered(self, tmp_path): + self._write(tmp_path, "sw1.json", {"PORT": {"Ethernet0": {}}, "VLAN": {}}) + + assert covered_tables(tmp_path) == {"PORT"} + + def test_table_empty_in_every_file_is_not_covered(self, tmp_path): + self._write(tmp_path, "sw1.json", {"VXLAN_TUNNEL": {}}) + self._write(tmp_path, "sw2.json", {"VXLAN_TUNNEL": {}}) + + assert covered_tables(tmp_path) == set() + + def test_coverage_accumulates_across_files(self, tmp_path): + # A table populated on one device and empty on another is covered: + # this is how VLAN and BGP_NEIGHBOR reach the count in the real set. + self._write(tmp_path, "sw1.json", {"VLAN": {}, "PORT": {"Ethernet0": {}}}) + self._write(tmp_path, "sw2.json", {"VLAN": {"Vlan100": {}}, "PORT": {}}) + + assert covered_tables(tmp_path) == {"VLAN", "PORT"} + + def test_non_json_files_are_ignored(self, tmp_path): + self._write(tmp_path, "sw1.json", {"PORT": {"Ethernet0": {}}}) + (tmp_path / "notes.txt").write_text('{"VLAN": {"Vlan100": {}}}') + + assert covered_tables(tmp_path) == {"PORT"} + + +class TestMain: + @staticmethod + def _stub(monkeypatch, emitted, covered): + monkeypatch.setattr(coverage, "emitted_tables", lambda: set(emitted)) + monkeypatch.setattr(coverage, "covered_tables", lambda: set(covered)) + + def test_full_coverage_returns_zero_and_prints_counts(self, monkeypatch, capsys): + self._stub(monkeypatch, {"PORT", "VLAN"}, {"PORT", "VLAN"}) + + rc = main() + + assert rc == 0 + assert "generator emits 2 tables; 2 non-empty" in capsys.readouterr().out + + def test_uncovered_tables_fail_and_are_listed_sorted(self, monkeypatch, capsys): + self._stub(monkeypatch, {"PORT", "VXLAN_TUNNEL", "BREAKOUT_CFG"}, {"PORT"}) + + rc = main() + out = capsys.readouterr().out + + assert rc == 1 + assert "not covered by any golden file:" in out + assert out.splitlines()[-2:] == [" BREAKOUT_CFG", " VXLAN_TUNNEL"] + + def test_covered_table_the_generator_cannot_emit_is_not_a_failure( + self, monkeypatch, capsys + ): + self._stub(monkeypatch, {"PORT"}, {"PORT", "HAND_WRITTEN_TABLE"}) + + rc = main() + + assert rc == 0 + assert "generator emits 1 tables; 2 non-empty" in capsys.readouterr().out From ffe790377fd410043b32f66adf65b1e90394faab Mon Sep 17 00:00:00 2001 From: Roger Luethi Date: Wed, 5 Aug 2026 07:39:51 +0200 Subject: [PATCH 3/4] README: document the SONiC E2E golden test The README explains how to run the unit and integration suites, but the E2E golden test added in this series had no entry point outside the Makefile and the harness script's own header comment, so there was nothing pointing a newcomer at "make sonic-e2e". Add a third section in the same shape as the existing two: what the test does, the prerequisites beyond the development dependencies (docker with the compose plugin, openssl, and a netbox-manager checkout found as a sibling directory or via NETBOX_MANAGER_DIR), and the Makefile targets for running, iterating against a reused stack, and regenerating. The coverage report gets its own mention because it is the one part of this suite nothing else surfaces: it is not wired into the harness or the Zuul job, so `make sonic-e2e-coverage` is the only way anyone sees which config_db tables the golden set reaches. Two behaviours are called out because neither is guessable from the error it produces. Regeneration refuses a stack left over from an earlier run, since applying the fixtures over a populated database can yield goldens that a fresh stack -- which CI always uses -- would not reproduce. And seeding applies every file under tests/e2e/scenario/resources/ regardless of git status, so a stray file there joins the fixture set; the harness header records that this has broken a run twice. The remaining environment overrides are left to tests/e2e/sonic_golden_test.sh, which already documents them, rather than duplicated here where they would drift. Assisted-by: Claude:claude-opus-5 Signed-off-by: Roger Luethi --- README.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/README.md b/README.md index 1c9cea93..916a16f0 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,60 @@ REDIS_HOST=localhost REDIS_DB=15 pipenv run pytest tests/integration > above, or `OSISM_ALLOW_DEFAULT_REDIS_DB=1` if the Redis itself is disposable. > `REDIS_DB` moves the direct client, the Celery broker and the result backend > together. + +## Running the SONiC E2E golden test + +The end-to-end test in `tests/e2e/` provisions NetBox with a docker compose +stack, seeds it from the fixtures in `tests/e2e/scenario/`, generates the SONiC +`config_db.json` files and compares them against the goldens in +`tests/e2e/golden/`. Besides the development dependencies it needs docker with +the compose plugin, `openssl`, and a `netbox-manager` checkout for the seeding +CLI — a sibling directory by default, `NETBOX_MANAGER_DIR` otherwise. + +``` +pipenv install --dev +make sonic-e2e +``` + +A cold run takes roughly ten minutes, most of it starting NetBox. To iterate +without paying that each time, bring the stack up separately and leave it +running: + +``` +make sonic-e2e-up # start NetBox and leave it up; sonic-e2e reuses it +make sonic-e2e-down # stop it again and remove its volumes +``` + +After an intentional generator change, rewrite the goldens and review the diff +before committing it. Regeneration deliberately refuses to run against a stack +left over from an earlier run, because applying the fixtures over a populated +database can produce goldens that CI — which always starts fresh — would not +reproduce: + +``` +make sonic-e2e-down +make sonic-e2e-regen +``` + +How much of the generated config the golden set actually covers is reported +separately, because nothing in CI reports it: + +``` +make sonic-e2e-coverage +``` + +That compares the `config_db` tables the generator can emit against the tables +that are non-empty in at least one golden, and names any that no golden covers. +It exits non-zero while that list is non-empty, so it is worth running after +adding a scenario to confirm the new tables landed. It gates nothing on its own +— the golden comparison above is the only check that fails a run. + +`tests/e2e/sonic_golden_test.sh` documents the remaining environment overrides +(`NETBOX_PORT`, `KEEP_STACK`, `SEED_PARALLEL` and the regeneration escape +hatch). + +> **Warning:** Seeding applies *every* file under +> `tests/e2e/scenario/resources/`, tracked or not, so a stray file there joins +> the fixture set — which either breaks the run or silently changes the +> goldens. Check that directory with `git status --ignored` before regenerating +> or debugging a mismatch. From 67c10a0cf83f0ac3e5fe00b4cd2f7c447e364ff2 Mon Sep 17 00:00:00 2001 From: Roger Luethi Date: Thu, 30 Jul 2026 21:37:05 +0200 Subject: [PATCH 4/4] zuul: add the SONiC E2E golden test Add python-osism-sonic-e2e as a Zuul job, wired via playbooks/pre-sonic-e2e.yml (pre-run) and playbooks/test-sonic-e2e.yml (run). The job uses nodeset: ubuntu-noble and timeout: 2400, since bringing up the compose stack, installing netbox-manager into its own venv, seeding NetBox and generating/comparing SONiC configs for every supported HWSKU takes longer than the default job timeout. A files matcher restricts when it runs in the check pipeline to changes that can affect the generated output or the harness itself (settings, conductor/sonic code, the E2E tests, the playbooks, Pipfile.lock, files/sonic/, requirements.txt and requirements.ansible.txt (the sonic_golden_test.sh harness installs the [ansible] extra that setup.cfg maps to the latter), setup.cfg itself, and .zuul.yaml/Makefile); it also runs unconditionally on periodic-daily. netbox-manager is still pulled at tip-of-main via required-projects because it remains the seeding tool, so a Depends-On is honored for its code -- but its example/ seed data is no longer used, so this job no longer detects drift in that data. pre-sonic-e2e.yml retains the accept_ra=2 sysctl because the Zuul node is IPv6-only and learns its default route via SLAAC; without it, router advertisements are not accepted on interfaces where forwarding is enabled and the node loses its route to the outside network. Assisted-by: Claude:claude-sonnet-5 Signed-off-by: Roger Luethi --- .zuul.yaml | 33 ++++++++++++++++ playbooks/pre-sonic-e2e.yml | 74 ++++++++++++++++++++++++++++++++++++ playbooks/test-sonic-e2e.yml | 40 +++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 playbooks/pre-sonic-e2e.yml create mode 100644 playbooks/test-sonic-e2e.yml diff --git a/.zuul.yaml b/.zuul.yaml index 6e3fcd70..145287f0 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -140,6 +140,37 @@ pre-run: playbooks/pre.yml run: playbooks/test-integration.yml +# End-to-end golden test for the SONiC config generator: provisions NetBox +# with a docker compose stack, seeds it from the in-repo fixtures under +# tests/e2e/scenario/, runs sync_sonic() and compares the exported config_db +# files against tests/e2e/golden/. In check the job only runs when files +# that can change the generated output (or the harness itself) are touched; +# periodic-daily runs it unconditionally. netbox-manager is still pulled at +# tip-of-main via required-projects because it remains the seeding tool, so +# a Depends-On is honored for its code -- but its example/ seed data is no +# longer used, so this job no longer detects drift in that data. +- job: + name: python-osism-sonic-e2e + nodeset: ubuntu-noble + pre-run: playbooks/pre-sonic-e2e.yml + run: playbooks/test-sonic-e2e.yml + required-projects: + - osism/netbox-manager + timeout: 2400 + files: + - ^\.zuul\.yaml$ + - ^Makefile$ + - ^Pipfile\.lock$ + - ^files/sonic/.* + - ^osism/settings\.py$ + - ^osism/tasks/conductor/.* + - ^osism/utils/.* + - ^playbooks/(pre-|test-)sonic-e2e\.yml$ + - ^requirements\.ansible\.txt$ + - ^requirements\.txt$ + - ^setup\.cfg$ + - ^tests/e2e/.* + - project: merge-mode: squash-merge default-branch: main @@ -154,6 +185,7 @@ - python-osism-test-setup - python-osism-unit-tests - python-osism-integration-tests + - python-osism-sonic-e2e periodic-daily: jobs: - flake8 @@ -163,6 +195,7 @@ - python-osism-test-setup - python-osism-unit-tests - python-osism-integration-tests + - python-osism-sonic-e2e periodic-midnight: jobs: - container-image-python-osism-push diff --git a/playbooks/pre-sonic-e2e.yml b/playbooks/pre-sonic-e2e.yml new file mode 100644 index 00000000..5aa7e7a4 --- /dev/null +++ b/playbooks/pre-sonic-e2e.yml @@ -0,0 +1,74 @@ +--- +# Node preparation for the SONiC config-generation E2E golden test. +# +# The test's NetBox fixture is a docker compose stack (tests/e2e/compose.yaml), +# so the node only needs Docker: ensure-docker installs docker-compose-plugin +# along with docker-ce. No kind, kubectl or helm. netbox-manager's own E2E job +# still provisions NetBox on kind, so there is nothing to keep in sync here. +- name: Prepare the SONiC E2E node + hosts: all + + pre_tasks: + # This CI node is IPv6-only and learns its address and default route via + # SLAAC / Router Advertisements. If Docker enables + # net.ipv6.conf.all.forwarding=1, the kernel stops honouring RAs at the + # default accept_ra=1, so the SLAAC default route expires and the node + # drops off the network a few minutes into the run. accept_ra=2 keeps RAs + # honoured even while forwarding is on, preserving the default route. + # Compose's default bridge network is IPv4-only, so Docker probably will + # not turn on IPv6 forwarding here -- but this guard is cheap insurance + # against a severe failure mode (an unreachable CI node) and is kept + # deliberately; removing it should be its own separate experiment, not a + # side effect of some other change. Set it here -- before Docker enables + # forwarding -- so the route never lapses. See + # https://docs.docker.com/engine/daemon/ipv6/ and + # https://forums.docker.com/t/docker-removes-host-ipv6-default-route/83238 + - name: Keep accepting IPv6 RAs after Docker enables forwarding (preserve default route) + become: true + ansible.builtin.copy: + dest: /etc/sysctl.d/99-sonic-e2e-accept-ra.conf + owner: root + group: root + mode: "0644" + content: | + net.ipv6.conf.all.accept_ra = 2 + net.ipv6.conf.default.accept_ra = 2 + {% if ansible_default_ipv6.interface is defined %} + net.ipv6.conf.{{ ansible_default_ipv6.interface }}.accept_ra = 2 + {% endif %} + + - name: Apply the accept_ra sysctl settings now + become: true + ansible.builtin.command: + cmd: sysctl -p /etc/sysctl.d/99-sonic-e2e-accept-ra.conf + changed_when: true + # A node without an ansible_default_ipv6.interface fact (or otherwise + # missing these IPv6 keys) fails sysctl -p; this is best-effort + # insurance for the common IPv6-only case, not a hard requirement, so + # do not fail pre-run over it. + failed_when: false + + roles: + - ensure-pip + - ensure-pipenv + - ensure-docker + + tasks: + - name: Ensure the Docker service is running + become: true + ansible.builtin.service: + name: docker + state: started + enabled: true + + # openssl is used by tests/e2e/sonic_golden_test.sh directly. curl is used + # by the NetBox container's healthcheck rather than by the script itself, + # but ensure-docker needs it anyway. python3-venv provides the venv module + # the script uses for the seeding venv. + - name: Install required packages + become: true + ansible.builtin.apt: + name: + - curl + - openssl + - python3-venv diff --git a/playbooks/test-sonic-e2e.yml b/playbooks/test-sonic-e2e.yml new file mode 100644 index 00000000..ab8ebfc0 --- /dev/null +++ b/playbooks/test-sonic-e2e.yml @@ -0,0 +1,40 @@ +--- +- name: Run the SONiC config-generation E2E golden test + hosts: all + + vars: + python_venv_dir: /tmp/venv + + tasks: + - name: Install dependencies + ansible.builtin.shell: + executable: /bin/bash + chdir: "{{ zuul.project.src_dir }}" + cmd: | + set -e + set -o pipefail + set -x + + {{ python_venv_dir }}/bin/pipenv install --dev --deploy + {{ python_venv_dir }}/bin/pipenv run pip install . + + - name: Run the E2E golden test + ansible.builtin.shell: + executable: /bin/bash + chdir: "{{ zuul.project.src_dir }}" + cmd: | + set -e + set -o pipefail + set -x + + # The script invokes bare `pipenv`; the netbox-manager checkout comes + # from Zuul's required-projects, so a Depends-On change to it is + # tested against the changed code and seed data. + export PATH="{{ python_venv_dir }}/bin:${PATH}" + export NETBOX_MANAGER_DIR="{{ ansible_user_dir }}/{{ zuul.projects['github.com/osism/netbox-manager'].src_dir }}" + + # Serial by default: concurrent seeding deadlocks on dcim_device FK + # row locks (see tests/e2e/sonic_golden_test.sh, Phase 2). Set + # `sonic_e2e_seed_parallel: 4` in the job vars to opt back in. + export SEED_PARALLEL="{{ sonic_e2e_seed_parallel | default(1) }}" + tests/e2e/sonic_golden_test.sh