From aaf78b9894b96ea4d9cf294ee6d749433bad42a3 Mon Sep 17 00:00:00 2001 From: RocNet Date: Wed, 22 Jul 2026 01:49:59 +0000 Subject: [PATCH 01/23] Add SONiC containerlab (docker-sonic-vs) device variant Add a sonic_clab device (parent: sonic) for the community docker-sonic-vs image running under containerlab, alongside the existing libvirt SONiC VM device. Since SONiC runs standard FRR, the device reuses FRR wherever the config is FRR: every routing module -- including bgp -- is delegated to the in-tree FRR templates via ansible_network_os: frr (no parallel routing templates), the single validation plugin re-exports the FRR checks (from netsim.validate.frr import *), and a clab.kmods declaration (mirroring frr.yml) lets netlab auto-load the mpls/vrf/vxlan kernel modules. What stays SONiC-specific is only what genuinely is not FRR: interface, VLAN and PortChannel objects are created through SONiC config_db (the config CLI), FRR daemons that ship disabled in docker-sonic-vs are enabled per configured module, configuration is deployed over the Ansible docker connection (docker exec), and a deterministic CONFIG_DB->kernel sync backs the modules the VS orchestration agents do not program. VLAN and PortChannel creation lives in the vlan/lag module-init hooks (vlan|lag/sonic_clab.initial.j2), pulled into initial config by extra_module_initial(). Module coverage: OSPF/OSPFv3, IS-IS, BGP (+ bgp.session/policy/originate/domain, ebgp.multihop), RIPv2, BFD, VRF, VLAN, LAG, VXLAN/EVPN (incl. symmetric IRB and multihoming), MPLS L3VPN, SR-MPLS, SRv6, gateway/VRRP, routing, tunnel.gre. No netlab-core changes. --- .../tasks/deploy-config/sonic_clab.yml | 22 ++ .../tasks/readiness-check/sonic_clab.yml | 9 + .../ansible/templates/initial/sonic_clab.j2 | 191 ++++++++++++++++++ .../templates/lag/sonic_clab.initial.j2 | 12 ++ netsim/ansible/templates/lag/sonic_clab.j2 | 33 +++ .../templates/vlan/sonic_clab.initial.j2 | 15 ++ netsim/ansible/templates/vlan/sonic_clab.j2 | 75 +++++++ netsim/ansible/templates/vxlan/sonic_clab.j2 | 91 +++++++++ netsim/devices/sonic_clab.yml | 174 ++++++++++++++++ netsim/validate/sonic_clab.py | 1 + 10 files changed, 623 insertions(+) create mode 100644 netsim/ansible/tasks/deploy-config/sonic_clab.yml create mode 100644 netsim/ansible/tasks/readiness-check/sonic_clab.yml create mode 100644 netsim/ansible/templates/initial/sonic_clab.j2 create mode 100644 netsim/ansible/templates/lag/sonic_clab.initial.j2 create mode 100644 netsim/ansible/templates/lag/sonic_clab.j2 create mode 100644 netsim/ansible/templates/vlan/sonic_clab.initial.j2 create mode 100644 netsim/ansible/templates/vlan/sonic_clab.j2 create mode 100644 netsim/ansible/templates/vxlan/sonic_clab.j2 create mode 100644 netsim/devices/sonic_clab.yml create mode 100644 netsim/validate/sonic_clab.py diff --git a/netsim/ansible/tasks/deploy-config/sonic_clab.yml b/netsim/ansible/tasks/deploy-config/sonic_clab.yml new file mode 100644 index 0000000000..da3a421fae --- /dev/null +++ b/netsim/ansible/tasks/deploy-config/sonic_clab.yml @@ -0,0 +1,22 @@ +# SONiC (containerlab, docker-sonic-vs) config deploy. +# +# Reached over Ansible's 'docker' connection plugin (ansible_connection: docker in +# sonic_clab.yml), so 'command'/'shell' here already execute *inside* the node container -- +# no docker exec wrapping needed. +# +# SONiC config is either a bash script of 'config' CLI commands (initial: starts with +# #!/bin/bash) or an FRR vtysh config (routing modules), same convention as the package +# 'frr'/'sonic' devices. +- template: + src: "{{ config_template }}" + dest: /tmp/config.sh + +- set_fact: deployed_config={{ lookup('template',config_template) }} + +- name: "run /tmp/config.sh to deploy {{ netsim_action }} config from {{ config_template }}" + command: bash /tmp/config.sh + when: not ansible_check_mode and ("#!/bin/bash" in deployed_config or "#!/bin/sh" in deployed_config) + +- name: "run vtysh -f to deploy {{ netsim_action }} config from {{ config_template }}" + command: vtysh -f /tmp/config.sh + when: not ansible_check_mode and not ("#!/bin/bash" in deployed_config or "#!/bin/sh" in deployed_config) diff --git a/netsim/ansible/tasks/readiness-check/sonic_clab.yml b/netsim/ansible/tasks/readiness-check/sonic_clab.yml new file mode 100644 index 0000000000..8fa5df2b21 --- /dev/null +++ b/netsim/ansible/tasks/readiness-check/sonic_clab.yml @@ -0,0 +1,9 @@ +# Wait for docker-sonic-vs's vtysh (and the underlying config_db redis) to answer before we try +# to push any configuration. Reached over the 'docker' connection plugin -- no SSH involved. +- name: Wait for SONiC vtysh to become ready + command: vtysh -c 'show version' + register: sonic_ready + until: sonic_ready.rc == 0 + retries: 40 + delay: 3 + changed_when: false diff --git a/netsim/ansible/templates/initial/sonic_clab.j2 b/netsim/ansible/templates/initial/sonic_clab.j2 new file mode 100644 index 0000000000..20734040bf --- /dev/null +++ b/netsim/ansible/templates/initial/sonic_clab.j2 @@ -0,0 +1,191 @@ +{% from '_extra_initial.j2' import extra_module_initial with context %} +#!/bin/bash +# +# SONiC (containerlab, docker-sonic-vs) initial configuration. +# +# Adapted from the package 'sonic' (libvirt) device's initial/sonic.j2, with changes forced +# by docker-sonic-vs's monolithic architecture (see sonic_clab.yml for why): +# * vtysh runs directly in this container -- no 'docker exec bgp' indirection. +# * most FRR daemons ship disabled (=no in /etc/frr/daemons) and must be enabled here. +# * config_db VLANs (`Vlan`) and PortChannels (`PortChannel`) must be created BEFORE any +# SVI/aggregate is addressed. Those live in the vlan/lag module-init hooks +# (vlan|lag/sonic_clab.initial.j2), pulled in below via extra_module_initial() at exactly this +# ordering point -- the same mechanism VyOS uses for its VLAN bridge setup. +# This script is deployed with 'command: bash /tmp/config.sh' over Ansible's docker connection +# (tasks/deploy-config/sonic_clab.yml), i.e. it already executes *inside* the node container. +# +# CONNECTION MODEL: config deployment stays on ansible_connection: docker, not network_cli. +# network_cli was evaluated and is not usable here: it requires a matching cliconf plugin, and +# the only SONiC cliconf shipped by Ansible (dellemc.enterprise_sonic) targets Dell's licensed +# "Management Framework" CLI (sonic-cli/klish) on Dell PowerSwitch hardware -- docker-sonic-vs +# has no such binary (verified: /etc/passwd has no admin user, no sonic-cli/klish anywhere in +# the image; the only shell is bash, and 'config'/'show'/'vtysh' are plain CLI utilities, not a +# paging network CLI a cliconf plugin can drive). This matches the rest of netlab: even the +# in-tree 'frr' clab device and the libvirt 'sonic' device (which HAS a full sshd) push FRR +# config as raw text over a generic command/shell task, never network_cli. +# +# We DO bootstrap sshd + an 'admin' user below (docker-sonic-vs ships /usr/sbin/sshd and host +# keys, just never starts them, and has no login user) purely for operator SSH access parity +# with the parent 'sonic' device (interactive login, 'netlab connect', ad-hoc troubleshooting) -- +# it is not part of the config-deployment path. +set -e +set -x +{% include 'linux/bash_profile.j2' +%} +{% include 'linux/hosts.j2' +%} +# +# Enable sshd + an 'admin' user (idempotent) -- interactive access only, see note above. +# +id admin >/dev/null 2>&1 || useradd -m -s /bin/bash -G sudo admin +echo 'admin:{{ ansible_ssh_pass | default("YourPaSsWoRd") }}' | chpasswd +echo 'admin ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/admin +mkdir -p /run/sshd +pgrep -x sshd >/dev/null || /usr/sbin/sshd +# +# Disable IPv6 (for IPv4-only interfaces) or SLAAC (if the device is a router) +# +{% for l in interfaces if l.type in ['lan','p2p','stub'] %} +{% if l.ipv6 is not defined and l.vlan.access_id is not defined and l.vlan.trunk_id is not defined and l.lag._parentindex is not defined %} +{# skip L2 switchports -- access, trunk, and LAG member ports: once a port is a + VLAN/PortChannel member the command errors out ("Cannot configure the IPv6 link + local mode!"), breaking re-deploys #} +config interface ipv6 disable use-link-local-only {{ l.ifname }} +{% endif %} +{% endfor %} +# +# Configure interfaces +# +config hostname {{ inventory_hostname.replace("_","-") }} +# +# Create config_db PortChannels (lag) and VLANs (vlan) before any aggregate/SVI is addressed +# below. These live in the per-module init hooks lag/vlan/sonic_clab.initial.j2 and are pulled +# in here, at the correct ordering point, by netlab's extra_module_initial() macro. +# +{{ extra_module_initial(['lag','vlan']) }} +{% for l in netlab_interfaces %} +{% if l.type in ['loopback'] %} +if ip link|grep {{ l.ifname }}; then + echo {{ l.ifname }} already exists +else + config loopback add {{ l.ifname }} +fi +{% endif %} +{# Tunnel interfaces (tunnel.gre plugin) don't exist as a kernel netdev yet -- the plugin's + own template creates and addresses them directly, and the `config` CLI only accepts + Ethernet/PortChannel/Vlan/Loopback names anyway. A VNI-backed SVI (EVPN symmetric-IRB) is + the same story: its config_db `Vlan` is intentionally never created above, and + vlan/vxlan sonic_clab.j2 build/address the real bridge netdev later (vxlan runs after vrf, + which runs after initial) -- addressing it here would hit "Vlan does not exist". #} +{% set _vni_svi = l.type == 'svi' and l.vlan.name|default('') in vlans and vlans[l.vlan.name].vni is defined %} +{% if l.ipv4 is defined and (l.ipv4 is string or l._parent_ipv4 is defined) and l.type != 'tunnel' and not _vni_svi %} +{% set addr = l.ipv4 if l.ipv4 is string else l._parent_ipv4 %} +if show ip interface|grep {{ l.ifname }}|grep {{ addr }}; then + echo {{ addr }} already configured on {{ l.ifname }} +else + config interface ip add {{ l.ifname }} {{ addr }} +fi +{% endif %} +{% if l.ipv6 is defined %} +{% if not (l.virtual_interface|default(False)) %} +config interface ipv6 enable use-link-local-only {{ l.ifname }} +{% endif %} +{% if l.ipv6 is string and l.ipv6|ansible.utils.ipv6 %} +if show ipv6 interface|grep {{ l.ifname }}|grep {{ l.ipv6 }}; then + echo {{ l.ipv6 }} already configured on {{ l.ifname }} +else + config interface ip add {{ l.ifname }} {{ l.ipv6 }} +fi +{% endif %} +{% endif %} +{# L2 switchports (LAG members and VLAN access/trunk ports) reject a direct MTU set once + they are members ("'interface_name' is in portchannel!" / "is in vlan"); the + PortChannel/port MTU propagates, so skip them #} +{% if l.mtu is defined and l.mtu >= 1500 and not (l.virtual_interface|default(False)) and l.lag._parentindex is not defined and l.vlan.access_id is not defined and l.vlan.trunk_id is not defined %} +config interface mtu {{ l.ifname }} {{ l.mtu + 48 }} +{% endif %} +! +{% endfor %} +# +# docker-sonic-vs's intfmgrd/orchagent do not program routed-port IPs from CONFIG_DB onto the +# kernel netdev (loopbacks work; routed ports do not), so BGP/OSPF over links can't come up +# without this. Bring the data ports admin-up and mirror the CONFIG_DB INTERFACE IPs onto the +# kernel netdev. Idempotent. +# +for p in $(redis-cli -n 4 keys "PORT|Ethernet*" 2>/dev/null | sed 's/PORT|//'); do + config interface startup "$p" >/dev/null 2>&1 || true +done +redis-cli -n 4 keys "INTERFACE|Ethernet*|*" 2>/dev/null | while IFS= read -r k; do + ifc=$(echo "$k" | cut -d'|' -f2); addr=$(echo "$k" | cut -d'|' -f3) + [ -n "$addr" ] && ip addr add "$addr" dev "$ifc" 2>/dev/null || true +done +# +# Enable the FRR daemons needed by the configured modules (docker-sonic-vs ships most of them +# disabled) and restart FRR once, only if something actually changed. +# +{% set daemons_needed = [] %} +{% for m in module|default([]) if netlab_frr_daemons[m] is defined %} +{% for frr_d in netlab_frr_daemons[m] %} +{% if daemons_needed.append(frr_d) %}{% endif %} +{% endfor %} +{% endfor %} +{% if daemons_needed %} +if grep -qE '^({{ daemons_needed|unique|join('|') }})=no' /etc/frr/daemons; then +{% for frr_d in daemons_needed|unique %} + sed -i 's/^{{ frr_d }}=no/{{ frr_d }}=yes/' /etc/frr/daemons +{% endfor %} + supervisorctl restart frr >/dev/null 2>&1 || service frr restart >/dev/null 2>&1 || true + sleep 12 +fi +{% endif %} +# +# Rest of initial configuration done through VTYSH +# Make sure it's ready +# +if vtysh -c 'show running' >/dev/null; then + echo vtysh is ready +else + echo 'giving vtysh some more time :(' + sleep 5 +fi +# +# And now let's configure the interfaces +# +cat >/tmp/netlab-initial.frr </dev/null 2>&1; then + echo {{ l.ifname }} already exists +else + config portchannel add {{ l.ifname }} +fi +{% endfor %} diff --git a/netsim/ansible/templates/lag/sonic_clab.j2 b/netsim/ansible/templates/lag/sonic_clab.j2 new file mode 100644 index 0000000000..a8efeb22b6 --- /dev/null +++ b/netsim/ansible/templates/lag/sonic_clab.j2 @@ -0,0 +1,33 @@ +#!/bin/bash +# SONiC lag module: PortChannel members via the config CLI (teammgrd programs the kernel bond + +# LACP via teamd, unlike vlanmgrd). The PortChannel itself is created in initial +# (create-before-address ordering). +set -e +set -x +{% for intf in netlab_interfaces if intf.type == 'lag' %} +{% for ch in netlab_interfaces if ch.lag._parentindex|default(None) == intf.lag.ifindex %} +if teamdctl {{ intf.ifname }} state 2>/dev/null | grep -q "{{ ch.ifname }}"; then + echo {{ ch.ifname }} already a member +else + # member MTU must equal the PortChannel MTU or member-add refuses + PCMTU=$(cat /sys/class/net/{{ intf.ifname }}/mtu) + config interface mtu {{ ch.ifname }} "$PCMTU" || true + config portchannel member add {{ intf.ifname }} {{ ch.ifname }} +fi +{% endfor %} +{% endfor %} +# +# Defensive sync: teammgrd programs the kernel bond + LACP reliably (unlike vlanmgrd), but +# mirror any PORTCHANNEL_MEMBER row the config CLI loop above didn't manage to enslave. +# Idempotent (already-enslaved ports fail the add silently); port must be link-down to join. +# +redis-cli --raw -n 4 keys "PORTCHANNEL_MEMBER|*" 2>/dev/null | while IFS= read -r k; do + pc=$(echo "$k" | cut -d'|' -f2); port=$(echo "$k" | cut -d'|' -f3) + [ -n "$port" ] || continue + if ! teamdctl "$pc" state 2>/dev/null | grep -q "^ $port"; then + ip link set "$port" down 2>/dev/null || true + teamdctl "$pc" port add "$port" 2>/dev/null || true + ip link set "$port" up 2>/dev/null || true + fi +done +exit 0 diff --git a/netsim/ansible/templates/vlan/sonic_clab.initial.j2 b/netsim/ansible/templates/vlan/sonic_clab.initial.j2 new file mode 100644 index 0000000000..8a55a4231d --- /dev/null +++ b/netsim/ansible/templates/vlan/sonic_clab.initial.j2 @@ -0,0 +1,15 @@ +# +# SONiC vlan module init: create config_db VLANs (`Vlan`) before any SVI is addressed in the +# device initial template. VNI-backed VLANs are skipped -- the vxlan module builds those on the +# FRR/kernel bridge path, so no config_db Vlan should compete for the access ports. Pulled into +# initial config by extra_module_initial() -- the same per-module init-hook mechanism VyOS uses. +# +{% if vlans is defined %} +{% for vname, v in vlans.items() if v.vni is not defined %} +if show vlan brief 2>/dev/null | grep -qE "^\|\s+{{ v.id }} "; then + echo Vlan{{ v.id }} already exists +else + config vlan add {{ v.id }} +fi +{% endfor %} +{% endif %} diff --git a/netsim/ansible/templates/vlan/sonic_clab.j2 b/netsim/ansible/templates/vlan/sonic_clab.j2 new file mode 100644 index 0000000000..5659727d0d --- /dev/null +++ b/netsim/ansible/templates/vlan/sonic_clab.j2 @@ -0,0 +1,75 @@ +#!/bin/bash +# +# SONiC vlan module — switchport membership via the config CLI (access and trunk). +# VLANs themselves (config_db `Vlan`) are created by initial/sonic_clab.j2 BEFORE the +# SVIs get addressed; this template adds port membership, THEN syncs it to the kernel itself +# (see the sync block below) -- docker-sonic-vs's vlanmgrd races at boot and often never +# mirrors VLAN_MEMBER rows written during the first deploy (a vlanmgrd restart also WIPES +# bridge state without replaying CONFIG_DB), so relying on it alone silently leaves the L2 +# datapath down (confirmed live: config_db had the VLAN_MEMBER row, but Ethernet0 had no +# `master Bridge` and cross-node ping failed). Idempotency for the config_db writes is checked +# against config_db (VLAN_MEMBER rows); the kernel sync below is separately idempotent. +set -e +set -x +{# access (untagged) members #} +{% set vni_ids = (vlans|default({})).values()|selectattr('vni','defined')|map(attribute='id')|list %} +{% for l in interfaces if l.vlan.access_id is defined and l.vlan.access_id not in vni_ids %} +if [ "$(redis-cli -n 4 exists "VLAN_MEMBER|Vlan{{ l.vlan.access_id }}|{{ l.ifname }}")" = "1" ]; then + echo {{ l.ifname }} already a member of Vlan{{ l.vlan.access_id }} +else + config vlan member add -u {{ l.vlan.access_id }} {{ l.ifname }} +fi +{% endfor %} +{# trunk (tagged) members — skip the native id if this port also has an access/native vlan #} +{% for l in interfaces if l.vlan.trunk_id is defined %} +{% for vid in l.vlan.trunk_id if vid != l.vlan.access_id|default(-1) %} +if [ "$(redis-cli -n 4 exists "VLAN_MEMBER|Vlan{{ vid }}|{{ l.ifname }}")" = "1" ]; then + echo {{ l.ifname }} already a tagged member of Vlan{{ vid }} +else + config vlan member add {{ vid }} {{ l.ifname }} +fi +{% endfor %} +{% endfor %} +{# EVPN symmetric-IRB pre-create step: vlan module runs BEFORE vrf + (vrf.config_after includes vlan), and vrf's generic frr.data-plane.j2 enslaves any + `interfaces` entry with `.vrf` set (including our SVI, ifname Vlan) into the vrf + device with `set -e` -- if that netdev doesn't exist yet, the enslave command fails + and aborts vrf's WHOLE deploy script (including the vrf's FRR/BGP config that + follows in the same script). vxlan module runs AFTER vrf (vxlan.config_after + includes vrf) and is what actually attaches the vxlan device + addresses this + bridge -- so pre-create it here, empty, just so it EXISTS in time. Capital "Vlan" + matches svi_interface_name so vrf's enslave-by-ifname logic finds it by name. #} +{% for vname, v in vlans.items() if v.vni is defined and v.mode|default('') == 'irb' and v.vrf is defined %} +if [ ! -e /sys/devices/virtual/net/Vlan{{ v.id }} ]; then + ip link add Vlan{{ v.id }} type bridge +fi +ip link set Vlan{{ v.id }} type bridge stp_state 0 +ip link set up dev Vlan{{ v.id }} +{% endfor %} +# +# Sync CONFIG_DB VLAN/VLAN_MEMBER/VLAN_INTERFACE onto the kernel bridge deterministically -- +# vlanmgrd quirk, see the header comment. Idempotent (all commands are safe to re-run). +# +ip link show Bridge >/dev/null 2>&1 || { ip link add Bridge type bridge; ip link set Bridge type bridge vlan_filtering 1; } +ip link set Bridge up 2>/dev/null || true +for k in $(redis-cli -n 4 keys "VLAN|Vlan*" 2>/dev/null); do + vid=${k#VLAN|Vlan} + bridge vlan add vid "$vid" dev Bridge self 2>/dev/null || true + ip link show "Vlan$vid" >/dev/null 2>&1 || ip link add link Bridge name "Vlan$vid" type vlan id "$vid" + ip link set "Vlan$vid" up 2>/dev/null || true +done +redis-cli -n 4 keys "VLAN_MEMBER|*" 2>/dev/null | while IFS= read -r k; do + v=$(echo "$k" | cut -d'|' -f2); port=$(echo "$k" | cut -d'|' -f3); vid=${v#Vlan} + mode=$(redis-cli -n 4 hget "$k" tagging_mode 2>/dev/null) + ip link set "$port" master Bridge 2>/dev/null || true + if [ "$mode" = "tagged" ]; then + bridge vlan add vid "$vid" dev "$port" 2>/dev/null || true + else + bridge vlan add vid "$vid" dev "$port" pvid untagged 2>/dev/null || true + fi +done +redis-cli -n 4 keys "VLAN_INTERFACE|Vlan*|*" 2>/dev/null | while IFS= read -r k; do + svi=$(echo "$k" | cut -d'|' -f2); addr=$(echo "$k" | cut -d'|' -f3) + [ -n "$addr" ] && ip addr add "$addr" dev "$svi" 2>/dev/null || true +done +exit 0 diff --git a/netsim/ansible/templates/vxlan/sonic_clab.j2 b/netsim/ansible/templates/vxlan/sonic_clab.j2 new file mode 100644 index 0000000000..73c2972897 --- /dev/null +++ b/netsim/ansible/templates/vxlan/sonic_clab.j2 @@ -0,0 +1,91 @@ +#!/bin/bash +# +# SONiC EVPN-VXLAN L2VNI — FRR/kernel data-plane. +# +# docker-sonic-vs's orchagent/vxlanmgrd do NOT program the VXLAN dataplane (same class +# of gap as routed-port IPs and VLAN members), so the config_db VXLAN_TUNNEL model gives +# no datapath on the VS. Instead we build the L2VNI exactly the way FRR/Cumulus does on +# plain Linux — a traditional per-VNI bridge holding the access port(s) + a kernel vxlan +# netdev — and let FRR zebra drive EVPN off that kernel state (advertise-all-vni). This +# is the same path proven live between two VS nodes (VNI up, remote VTEP learned, iBGP +# l2vpn evpn Established, host-to-host ping across the tunnel). +# +# config_db is intentionally bypassed for VNI-backed VLANs: initial/sonic.j2 skips +# `config vlan add` for them and vlan/sonic.j2 skips their member-add, so nothing competes +# for the access ports here. +# +# EVPN symmetric-IRB: an `irb` VNI-backed vlan with a `vrf` gets its L2VNI +# bridge NAMED "Vlan" (capital, matching svi_interface_name) instead of the plain +# bridge-mode "vlan" -- vlan/sonic_clab.j2 pre-creates that exact netdev (empty) BEFORE +# vrf deploys, so vrf's generic frr.data-plane.j2 (`ip link set Vlan master `, +# matched by ifname against netlab's own SVI interface record) can enslave it; this +# template (running AFTER vrf, vxlan.config_after includes vrf) then attaches the vxlan +# device and addresses the bridge itself as the IRB gateway -- same "bridge IS the SVI" +# pattern plain Linux/Cumulus symmetric-IRB uses. The L3VNI (VRF transit VNI) is a +# separate vxlan netdev enslaved DIRECTLY into the vrf device (no bridge -- pure L3 +# transit, no learning/flooding needed); zebra's advertise-all-vni auto-binds it to the +# vrf from that kernel state, same as the L2VNI auto-discovery. +set -e +set -x +{% set _mtu = mtu|default(1500) %} +{% if vxlan.vlans is defined %} +{% for vname in vxlan.vlans if vlans[vname].vni is defined %} +{% set vlan = vlans[vname] %} +{% set irb = vlan.mode|default('') == 'irb' and vlan.vrf is defined %} +{% set br = ("Vlan" if irb else "vlan") + vlan.id|string %} +# --- L2VNI {{ vlan.vni }} (VLAN {{ vname }}, bridge {{ br }}{{ ", IRB vrf " + vlan.vrf if irb else "" }}) --- +if [ ! -e /sys/devices/virtual/net/{{ br }} ]; then + ip link add {{ br }} type bridge +fi +ip link set {{ br }} type bridge stp_state 0 +ip link set up dev {{ br }} +if ! ip link show vxlan{{ vlan.vni }} >/dev/null 2>&1; then + ip link add vxlan{{ vlan.vni }} type vxlan id {{ vlan.vni }} dstport 4789 local {{ vxlan.vtep }} nolearning +fi +ip link set dev vxlan{{ vlan.vni }} master {{ br }} +bridge link set dev vxlan{{ vlan.vni }} learning off +ip link set mtu {{ vlan.mtu|default(_mtu) }} addrgenmode none dev vxlan{{ vlan.vni }} +ip link set up dev vxlan{{ vlan.vni }} +{% for l in interfaces if l.vlan.access_id|default(0) == vlan.id %} +# access port {{ l.ifname }} -> bridge {{ br }} (L2, no IP) +ip addr flush dev {{ l.ifname }} 2>/dev/null || true +ip link set dev {{ l.ifname }} master {{ br }} +ip link set up dev {{ l.ifname }} +{% endfor %} +{% if irb %} +{% for i in interfaces if i.type|default('') == 'svi' and i.vlan.name|default('') == vname %} +# IRB gateway: address the bridge itself (it IS the SVI) then enslave into vrf {{ vlan.vrf }} +if ! ip addr show {{ br }} | grep -q " {{ i.ipv4 }} "; then + ip addr add {{ i.ipv4 }} dev {{ br }} +fi +ip link set {{ br }} master {{ vlan.vrf }} +{% endfor %} +{% endif %} +{% endfor %} +{% endif %} +{% for vrf_name in vxlan.l3vnis|default([]) %} +{% set l3vni = vrfs[vrf_name].evpn.transit_vni %} +{% set l3br = "l3vni" + l3vni|string %} +# --- L3VNI {{ l3vni }} (VRF {{ vrf_name }} transit) -- vxlan10099 enslaved into a +# dedicated bridge, and the BRIDGE ITSELF (no address, no vlan sub-interface -- unlike +# the earlier broken attempt) enslaved into the vrf. Same two-level "bridge is the +# vrf-facing device" shape that already works for the L2VNI IRB SVI (see above), just +# unaddressed since an L3VNI has no gateway IP of its own -- this gives zebra a real +# bridge-level "System MAC" for Router MAC derivation instead of raw vxlan-device +# enslavement, which left the L3VNI State: Down (proven live, not assumed -- see the +# vxlan.j2 git history for the two rejected attempts before this one). +if [ ! -e /sys/devices/virtual/net/{{ l3br }} ]; then + ip link add {{ l3br }} type bridge +fi +ip link set {{ l3br }} type bridge stp_state 0 +ip link set up dev {{ l3br }} +if ! ip link show vxlan{{ l3vni }} >/dev/null 2>&1; then + ip link add vxlan{{ l3vni }} type vxlan id {{ l3vni }} dstport 4789 local {{ vxlan.vtep }} nolearning +fi +ip link set dev vxlan{{ l3vni }} master {{ l3br }} +bridge link set dev vxlan{{ l3vni }} learning off +ip link set mtu {{ _mtu }} addrgenmode none dev vxlan{{ l3vni }} +ip link set up dev vxlan{{ l3vni }} +ip link set {{ l3br }} master {{ vrf_name }} +{% endfor %} +exit 0 diff --git a/netsim/devices/sonic_clab.yml b/netsim/devices/sonic_clab.yml new file mode 100644 index 0000000000..86822be274 --- /dev/null +++ b/netsim/devices/sonic_clab.yml @@ -0,0 +1,174 @@ +# SONiC on containerlab (docker-sonic-vs). +# +# netlab ships a `sonic` device that is LIBVIRT-ONLY (a full multi-container SONiC VM: FRR runs +# in a nested "bgp" sub-container, reached via `docker exec bgp vtysh`). This is a SEPARATE +# device for the community `docker-sonic-vs` image on containerlab, which is a MONOLITHIC single +# container: FRR's vtysh runs directly in the container's own namespace (no nested "bgp" +# container), and there is no sshd. We inherit interface naming / loopback naming / the base +# BGP feature set from `sonic` via `parent`, and add a `clab:` block that uses Ansible's built-in +# `docker` connection plugin (the same pattern already used in-tree by the `frr` and +# `cumulus_nvue` clab devices) instead of SSH — this is the netlab-idiomatic way to configure a +# clab container with no running sshd. +# +# docker-sonic-vs also ships most FRR daemons disabled by default (bgpd/ospfd/ospf6d/isisd/... +# =no in /etc/frr/daemons) to save resources; templates/initial/sonic_clab.j2 flips on the +# daemons needed by the configured modules and restarts FRR once, idempotently. +description: SONiC (containerlab, docker-sonic-vs) +parent: sonic +support: + level: best-effort + caveats: + - >- + Config deployment uses Ansible's 'docker' connection plugin (docker exec), not network_cli: + docker-sonic-vs has no cliconf-compatible CLI (the only Ansible SONiC cliconf, + dellemc.enterprise_sonic, targets Dell's licensed klish/Management-Framework CLI, which this + image doesn't ship -- verified). See templates/initial/sonic_clab.j2 for the full writeup. + - >- + docker-sonic-vs ships sshd + host keys but starts neither sshd nor a login user; + templates/initial/sonic_clab.j2 bootstraps both (admin / ansible_ssh_pass below) for + interactive access ('netlab connect', ad-hoc troubleshooting) -- SSH is NOT used for config + deployment, only ansible_connection: docker is. + - docker-sonic-vs is a monolithic single container (FRR vtysh runs directly); this differs from + the multi-container architecture of the full SONiC VM used by the parent 'sonic' device, so + the initial template is not shared with the parent device and is provided here instead. BGP + (and every other routing module) is delegated to the in-tree FRR templates via + ansible_network_os=frr -- unlike the Azure/libvirt sonic image, docker-sonic-vs does not + pre-seed a BGP AS, so no 'no router bgp' reset wrapper is needed. + - >- + Genuinely unsupported on this image, not just untested (each probed live): dhcp relay/client + -- docker-sonic-vs ships no dhcrelay/dhcp6relay binary at all; stp -- config-plane only, no + stpd daemon so there is no real port-blocking behavior to verify; mlag.vtep (anycast VTEP) -- + config-plane renders correctly (identical vtep-ip-global, underlay OSPF Full, BGP+EVPN + Established) but the active-active EVPN overlay is platform-blocked (no mclagd, so BGP never + resolves a usable self-next-hop for the anycast VTEP -- datapath 100% loss). +lag_interface_name: "PortChannel{lag.ifindex}" +tunnel_interface_name: "tun{ifindex}" # tunnel.gre plugin naming (kernel netdev, ip_gre) +group_vars: + netlab_device_type: sonic_clab + netlab_frr_daemons: # module -> FRR daemons to enable in /etc/frr/daemons (default: no) + bgp: [ bgpd ] + ospf: [ ospfd, ospf6d ] + isis: [ isisd ] + bfd: [ bfdd ] + ripv2: [ ripd ] + vrf: [ bgpd ] + gateway: [ vrrpd ] + mpls: [ ldpd ] +clab: + # No netsim/templates/provider/clab/sonic_clab.j2 override exists (dev-guide checklist item): + # the generic templates/provider/clab/clab.j2 already renders every clab. below (image, + # mtu, node.kind, ...) via its key-value passthrough loop (defaults.providers.clab.attributes. + # node._keys) -- confirmed empirically, clab.yml rendered correctly with no provider template + # of our own. A provider override is only needed for device-specific containerlab topology + # shapes (bind mounts, exec hooks, etc.); this device needs none of that. + image: docker-sonic-vs:latest + mtu: 1500 + node: + kind: sonic-vs + # SONiC runs FRR, so reuse FRR's kernel-module handling. Declaring 'kmods' opts this device + # into netlab's clab kernel-module loader (netsim/providers/clab/labops.py), which merges these + # with the system-wide providers.clab.kmods -- so an 'mpls'/'sr' lab auto-loads mpls_router + + # mpls_iptunnel (real kernel label FIB), 'vxlan' loads the vxlan modules, and 'vrf' loads the + # vrf module, with no manual 'modprobe'. Mirrors netsim/devices/frr.yml. + kmods: + initial: [ 'vrf?' ] + group_vars: + ansible_connection: docker + ansible_user: root + ansible_ssh_pass: YourPaSsWoRd # bootstrapped 'admin' login, interactive use only + netlab_ready: [ ansible ] # skip the SSH readiness wait (sshd isn't up yet) + netlab_show_command: [ vtysh, -c, 'show $@' ] +# The feature flags below are established by live testing across the module templates in +# tests/integration/platform/sonic_clab/; see those topologies for what each one verifies. +features: + initial: + collect: true # ansible_connection: docker unblocks this (no sshd needed) -- verified live + bgp: + import: [ connected, static, ospf ] # redistribute-into-BGP (frr.j2 renders it) + advertise: true # bgp.originate: static discard route backs the prefix + password: true # bgp.session plugin (MD5) + timers: true + gtsm: true + passive: true + bfd: true + multihop: # ebgp.multihop plugin (FRR-delegated) + vrf: true + ospf: + unnumbered: true + default: true + areas: true + timers: true + priority: true + password: true + bfd: true + isis: + circuit_type: true + unnumbered: { ipv4: true, ipv6: true, network: true } + import: [ static ] + mpls: + ldp: true # LDP control-plane; no kernel MPLS label FIB in a clab container + vpn: true # unlocks BGP VPNv4/VPNv6 L3VPN import + sr: + af: [ ipv4, ipv6 ] # SR-MPLS via IS-IS; SIDs flooded/visible in the IS-IS LSDB + protocol: [ isis ] + srv6: + isis: true # kernel seg6 (net.ipv6.seg6_enabled), not the MPLS label FIB + vrf: + keep_module: true + ospfv2: true + ospfv3: true + bgp: true + isis: true + bfd: true + lag: + passive: true + mlag: + peer: + ip: 169.254.127.0/31 # static /31 peer-link (EOS pattern); SONiC has no MLAG keepalive + # protocol to negotiate one over -- config-plane only (no mclagd) + ripv2: + ipv4: true + gateway: + protocol: [ vrrp ] + routing: + static: + vrf: true + discard: true # Null0 blackhole (backs bgp.originate) + prefix: true + aspath: true + community: + standard: true + large: true + policy: + match: + prefix: true + aspath: true + community: + standard: true + set: + locpref: true + med: true + prepend: true + weight: true + community: + standard: true + vxlan: true # L2VNI via FRR/kernel path (orchagent can't program the VS dataplane) + evpn: + transport: [ vxlan, mpls ] + irb: true # symmetric IRB / L3VNI -- kernel VRF + vxlan L3VNI transit device + multihoming: # evpn.multihoming plugin: ESI-LAG on PortChannel (teamd) via zebra + lag: true + interface: false + esi_auto: true + modes: [ 'all-active' ] + vlan: + model: l3-switch # access switchports + SVIs + svi_interface_name: "Vlan{vlan}" # config_db object name -- CAPITAL V; the config CLI + # rejects netlab's default lowercase vlan + tunnel: + gre: true # tunnel.gre plugin: kernel ip_gre/ip6_gre, FRR-delegated bash + # template (extra/tunnel/gre/frr.j2) -- reused as-is, no override +# Deliberately NOT declared (genuinely unsupported on this image, not just untested -- see the +# 'support.caveats' entry above for the live-probed reason): dhcp relay/client, stp real +# port-blocking, mlag.vtep active-active datapath. +graphite.icon: switch diff --git a/netsim/validate/sonic_clab.py b/netsim/validate/sonic_clab.py new file mode 100644 index 0000000000..365f417d72 --- /dev/null +++ b/netsim/validate/sonic_clab.py @@ -0,0 +1 @@ +from netsim.validate.frr import * # sonic_clab runs FRR/vtysh -- reuse FRR validation From 37605043041b1a8c187de0f40689a4c3afea6109 Mon Sep 17 00:00:00 2001 From: RocNet Date: Wed, 22 Jul 2026 01:49:59 +0000 Subject: [PATCH 02/23] Add SONiC containerlab integration test topologies Device bring-up and per-module smoke tests for the sonic_clab device, each verified live against a docker-sonic-vs container over the docker connection plugin. Covers initial, OSPF/OSPFv3, IS-IS (v4/v6), BGP and its plugins, RIPv2, BFD, VRF and route-leaking (v4/v6), VLAN, LAG, VXLAN/EVPN, symmetric IRB, EVPN multihoming, MPLS/SR/L3VPN, SRv6, GRE tunnels, and a files-plugin worked example. Several topologies carry native validate: sections. --- .../platform/sonic_clab/01-initial.yml | 9 ++ .../platform/sonic_clab/02-ospf.yml | 28 ++++++ .../platform/sonic_clab/03-bgp.yml | 30 +++++++ .../platform/sonic_clab/04-isis.yml | 26 ++++++ .../platform/sonic_clab/05-vrf.yml | 15 ++++ .../platform/sonic_clab/06-mpls-sr-l3vpn.yml | 71 +++++++++++++++ .../platform/sonic_clab/07-srv6.yml | 28 ++++++ .../platform/sonic_clab/08-vlan.yml | 22 +++++ .../platform/sonic_clab/09-lag.yml | 16 ++++ .../platform/sonic_clab/11-vxlan-evpn.yml | 37 ++++++++ .../platform/sonic_clab/12-evpn-irb-l3vni.yml | 50 +++++++++++ .../platform/sonic_clab/13-vrf-leak.yml | 24 +++++ .../platform/sonic_clab/14-bgp-session.yml | 17 ++++ .../platform/sonic_clab/15-bgp-policy.yml | 49 ++++++++++ .../platform/sonic_clab/16-ebgp-multihop.yml | 39 ++++++++ .../platform/sonic_clab/17-ospf-areas.yml | 21 +++++ .../platform/sonic_clab/18-bgp-originate.yml | 18 ++++ .../platform/sonic_clab/19-bfd.yml | 12 +++ .../platform/sonic_clab/20-ripv2.yml | 11 +++ .../sonic_clab/21-evpn-multihoming.yml | 47 ++++++++++ .../platform/sonic_clab/22-ospfv3.yml | 25 ++++++ .../platform/sonic_clab/23-isis-v6-bgp-v6.yml | 26 ++++++ .../platform/sonic_clab/24-routing-v6.yml | 44 +++++++++ .../platform/sonic_clab/25-vrf-v6-leak.yml | 36 ++++++++ .../platform/sonic_clab/26-vrf-v6-ospfv3.yml | 26 ++++++ .../platform/sonic_clab/27-tunnel-gre.yml | 23 +++++ .../sonic_clab/28-files-maxprefix.yml | 44 +++++++++ .../platform/sonic_clab/29-bgp-domain.yml | 58 ++++++++++++ .../integration/platform/sonic_clab/README.md | 90 +++++++++++++++++++ .../platform/sonic_clab/h1-fixaddr/linux.j2 | 12 +++ 30 files changed, 954 insertions(+) create mode 100644 tests/integration/platform/sonic_clab/01-initial.yml create mode 100644 tests/integration/platform/sonic_clab/02-ospf.yml create mode 100644 tests/integration/platform/sonic_clab/03-bgp.yml create mode 100644 tests/integration/platform/sonic_clab/04-isis.yml create mode 100644 tests/integration/platform/sonic_clab/05-vrf.yml create mode 100644 tests/integration/platform/sonic_clab/06-mpls-sr-l3vpn.yml create mode 100644 tests/integration/platform/sonic_clab/07-srv6.yml create mode 100644 tests/integration/platform/sonic_clab/08-vlan.yml create mode 100644 tests/integration/platform/sonic_clab/09-lag.yml create mode 100644 tests/integration/platform/sonic_clab/11-vxlan-evpn.yml create mode 100644 tests/integration/platform/sonic_clab/12-evpn-irb-l3vni.yml create mode 100644 tests/integration/platform/sonic_clab/13-vrf-leak.yml create mode 100644 tests/integration/platform/sonic_clab/14-bgp-session.yml create mode 100644 tests/integration/platform/sonic_clab/15-bgp-policy.yml create mode 100644 tests/integration/platform/sonic_clab/16-ebgp-multihop.yml create mode 100644 tests/integration/platform/sonic_clab/17-ospf-areas.yml create mode 100644 tests/integration/platform/sonic_clab/18-bgp-originate.yml create mode 100644 tests/integration/platform/sonic_clab/19-bfd.yml create mode 100644 tests/integration/platform/sonic_clab/20-ripv2.yml create mode 100644 tests/integration/platform/sonic_clab/21-evpn-multihoming.yml create mode 100644 tests/integration/platform/sonic_clab/22-ospfv3.yml create mode 100644 tests/integration/platform/sonic_clab/23-isis-v6-bgp-v6.yml create mode 100644 tests/integration/platform/sonic_clab/24-routing-v6.yml create mode 100644 tests/integration/platform/sonic_clab/25-vrf-v6-leak.yml create mode 100644 tests/integration/platform/sonic_clab/26-vrf-v6-ospfv3.yml create mode 100644 tests/integration/platform/sonic_clab/27-tunnel-gre.yml create mode 100644 tests/integration/platform/sonic_clab/28-files-maxprefix.yml create mode 100644 tests/integration/platform/sonic_clab/29-bgp-domain.yml create mode 100644 tests/integration/platform/sonic_clab/README.md create mode 100644 tests/integration/platform/sonic_clab/h1-fixaddr/linux.j2 diff --git a/tests/integration/platform/sonic_clab/01-initial.yml b/tests/integration/platform/sonic_clab/01-initial.yml new file mode 100644 index 0000000000..c33197cf88 --- /dev/null +++ b/tests/integration/platform/sonic_clab/01-initial.yml @@ -0,0 +1,9 @@ +# 1-node SONiC (clab) bring-up smoke test. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 +nodes: + s1: diff --git a/tests/integration/platform/sonic_clab/02-ospf.yml b/tests/integration/platform/sonic_clab/02-ospf.yml new file mode 100644 index 0000000000..76429b4f53 --- /dev/null +++ b/tests/integration/platform/sonic_clab/02-ospf.yml @@ -0,0 +1,28 @@ +# 2-node SONiC (clab) OSPF smoke test. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ ospf ] +nodes: [ s1, s2 ] +links: +- s1: + s2: + +# native docker-exec validation (reuses FRR validation plugins via netsim/validate/*/sonic_clab.py) +defaults.const.validate.ospfv2_adj_lan: 60 +defaults.const.validate.ospfv2_spf: 10 +validate: + adj_s1: + description: s1 sees s2 as an OSPF neighbor (docker-exec validation) + wait_msg: Waiting for OSPF adjacency + wait: ospfv2_adj_lan + nodes: [ s1 ] + plugin: ospf_neighbor(nodes.s2.ospf.router_id) + adj_s2: + description: s2 sees s1 as an OSPF neighbor + wait: ospfv2_spf + nodes: [ s2 ] + plugin: ospf_neighbor(nodes.s1.ospf.router_id) diff --git a/tests/integration/platform/sonic_clab/03-bgp.yml b/tests/integration/platform/sonic_clab/03-bgp.yml new file mode 100644 index 0000000000..5b43a4e00f --- /dev/null +++ b/tests/integration/platform/sonic_clab/03-bgp.yml @@ -0,0 +1,30 @@ +# 2-node SONiC (clab) eBGP smoke test. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ bgp ] +nodes: + s1: + bgp.as: 65001 + s2: + bgp.as: 65002 +links: +- s1: + s2: + +# native docker-exec validation (reuses FRR bgp validation plugin via sonic_clab alias) +defaults.const.validate.ebgp_session: 60 +validate: + session_s1: + description: s1 sees s2 as an established eBGP neighbor (docker-exec validation) + wait_msg: Waiting for BGP session to establish + wait: ebgp_session + nodes: [ s1 ] + plugin: bgp_neighbor(node.bgp.neighbors, "s2") + session_s2: + description: s2 sees s1 as an established eBGP neighbor + nodes: [ s2 ] + plugin: bgp_neighbor(node.bgp.neighbors, "s1") diff --git a/tests/integration/platform/sonic_clab/04-isis.yml b/tests/integration/platform/sonic_clab/04-isis.yml new file mode 100644 index 0000000000..3e380a4962 --- /dev/null +++ b/tests/integration/platform/sonic_clab/04-isis.yml @@ -0,0 +1,26 @@ +# 2-node SONiC IS-IS spike. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ isis ] +nodes: [ s1, s2 ] +links: +- s1: + s2: + +# native docker-exec validation (reuses FRR isis validation plugin via sonic_clab alias) +defaults.const.validate.isis_adj: 60 +validate: + adj_s1: + description: s1 sees s2 as an IS-IS neighbor (docker-exec validation) + wait_msg: Waiting for IS-IS adjacency + wait: isis_adj + nodes: [ s1 ] + plugin: isis_neighbor("s2") + adj_s2: + description: s2 sees s1 as an IS-IS neighbor + nodes: [ s2 ] + plugin: isis_neighbor("s1") diff --git a/tests/integration/platform/sonic_clab/05-vrf.yml b/tests/integration/platform/sonic_clab/05-vrf.yml new file mode 100644 index 0000000000..e68c63d2fb --- /dev/null +++ b/tests/integration/platform/sonic_clab/05-vrf.yml @@ -0,0 +1,15 @@ +# 2-node SONiC VRF spike — link in VRF red with per-VRF OSPF. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ vrf, ospf ] +vrfs: + red: +nodes: [ s1, s2 ] +links: +- s1: + s2: + vrf: red diff --git a/tests/integration/platform/sonic_clab/06-mpls-sr-l3vpn.yml b/tests/integration/platform/sonic_clab/06-mpls-sr-l3vpn.yml new file mode 100644 index 0000000000..61183acb50 --- /dev/null +++ b/tests/integration/platform/sonic_clab/06-mpls-sr-l3vpn.yml @@ -0,0 +1,71 @@ +# SONiC MPLS/SR/L3VPN spike +# IS-IS+LDP+SR-MPLS core, iBGP VPNv4 between the two PEs (loopback sessions), VRF red +# with stub interfaces on both ends, VRF blue on s2 importing red's route-target +# (inter-VRF route leaking). +# +# UNLIKE ArcOS's native container (no kernel MPLS at all), docker-sonic-vs shares the +# HOST kernel netns machinery for net.mpls -- the sysctls just don't exist until the +# mpls_router/mpls_iptunnel modules are loaded ANYWHERE on the host: +# sudo modprobe mpls_router mpls_iptunnel +# Once loaded, /proc/sys/net/mpls/* appears in every container's netns immediately +# (even already-running ones) and mpls/frr.j2's `sysctl -w net.mpls.platform_labels` +# succeeds -- so this gets a REAL kernel label FIB (`ip -M route`), not just a +# control-plane proof. This is a one-time HOST prerequisite, not something the +# topology YAML can express -- document it, don't assume it's already loaded. +# +# sr.srgb.start is a MODEL GAP for IS-IS SR-MPLS on FRR (checked against the exact +# upstream template that renders this, not assumed): netsim/ansible/templates/sr/frr.j2 +# only emits `segment-routing global-block ` inside the `if 'ospfv2' in +# sr.protocol` branch -- the `if 'isis' in sr.protocol` branch never references +# sr.srgb at all. A custom SRGB is silently a no-op here; FRR isisd always keeps its +# compiled-in default (base 16000, range 8000) regardless of what the topology sets. +# Confirmed live: `show isis database detail` -> "Global Block Base: 16000 Range: 8000" +# even with sr.srgb.start set on the node. Netlab/FRR gap to report upstream, not +# something to work around here -- so this topology does NOT set sr.srgb. +# +# (A first pass at this topology hit a VPNv4-export failure -- BGP requested a label +# from zebra's dynamic pool but never got one, `show bgp labelpool summary` -> 0 +# LabelChunks. That LOOKED like an SRGB/zebra-label-pool collision, but wasn't: +# `netlab initial` deploys s1 and s2 SEQUENTIALLY, not in parallel, and that pass's +# ansible timeout cut off before s2 ever received its bgp/sr/mpls/vrf config at all. +# A longer timeout for the full two-node sequential deploy resolved it with ZERO +# topology changes and the default SRGB -- worth remembering when a multi-module SONiC +# deploy looks like it "half worked": check whether every node actually finished.) +# +# This IS a REAL end-to-end MPLS L3VPN datapath: s2 (vrf red) pings s1's vrf-red stub +# (172.16.0.1) across two real kernel-programmed MPLS labels (LDP transport + BGP VPN +# label), 0% loss. VRF blue -> 172.16.0.1 correctly FAILS (100% loss) -- blue imports +# red's RT so it has a forward route, but red does NOT import blue's RT, so s1 has no +# return route to blue's prefix; one-way leak is by design (matches the existing +# vrf_leak module), not a bug. +# sudo modprobe mpls_router mpls_iptunnel # HOST prerequisite, once +# python validate/sonic.py --module mpls sr mpls_vpn +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ isis, bgp, mpls, sr, vrf ] +bgp.as: 65000 +mpls.ldp: true +mpls.vpn: true + +vrfs: + red: + blue: + import: [ red, blue ] + +nodes: [ s1, s2 ] + +links: +- s1-s2 +- s1: + vrf: red + type: stub +- s2: + vrf: red + type: stub +- s2: + vrf: blue + type: stub diff --git a/tests/integration/platform/sonic_clab/07-srv6.yml b/tests/integration/platform/sonic_clab/07-srv6.yml new file mode 100644 index 0000000000..24c7f3558d --- /dev/null +++ b/tests/integration/platform/sonic_clab/07-srv6.yml @@ -0,0 +1,28 @@ +# SONiC SRv6 over IS-IS +# Per-node locator from srv6.locator_pool; IS-IS advertises the SRv6 locator + auto +# End/End.X SIDs. CONFIRMED: FRR's srv6/frr.j2 uses kernel seg6 (net.ipv6.seg6_enabled), +# NOT the MPLS label FIB, so — unlike mpls/sr — this needs NO host modprobe +# prerequisite and gets REAL kernel seg6local routes (`ip -6 route` shows +# `encap seg6local action End`/`End.X`) straight out of the box. Cross-node proof: s1's +# IS-IS LSDB carries s2's locator (and vice versa), not just a local SID. +# Same open follow-up as OcNOS's srv6 result: SRv6-OAM ping to the End SID (uN) does +# NOT get answered even though the kernel seg6local route is installed — proven +# control+kernel-plane, datapath ping unresolved (not chased further here +# follow-up, matches the OcNOS precedent exactly). +# python validate/sonic.py --module srv6 +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ isis, srv6 ] +srv6.bgp: false +srv6.igp: [ isis ] +addressing: + loopback: + ipv6: 2001:db8:a::/48 + p2p: + ipv6: 2001:db8:1::/48 +nodes: [ s1, s2 ] +links: [ s1-s2 ] diff --git a/tests/integration/platform/sonic_clab/08-vlan.yml b/tests/integration/platform/sonic_clab/08-vlan.yml new file mode 100644 index 0000000000..34dcddf200 --- /dev/null +++ b/tests/integration/platform/sonic_clab/08-vlan.yml @@ -0,0 +1,22 @@ +# 2-node SONiC VLAN spike (config_db Vlan + kernel bridge datapath). +# Access VLAN "red" (IRB -> SVI with an IP) between s1 and s2, same shape as the +# ocnos/arcos vlan tests. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 + +module: [ vlan ] + +vlans: + red: + mode: irb + +nodes: [ s1, s2 ] + +links: +- s1: + s2: + vlan.access: red diff --git a/tests/integration/platform/sonic_clab/09-lag.yml b/tests/integration/platform/sonic_clab/09-lag.yml new file mode 100644 index 0000000000..058331704e --- /dev/null +++ b/tests/integration/platform/sonic_clab/09-lag.yml @@ -0,0 +1,16 @@ +# SONiC LAG spike: two parallel links -> PortChannel1 (LACP/teamd), +# OSPF over the aggregate proves the datapath. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 + +module: [ lag, ospf ] + +nodes: [ s1, s2 ] + +links: +- lag: + members: [ s1-s2, s1-s2 ] diff --git a/tests/integration/platform/sonic_clab/11-vxlan-evpn.yml b/tests/integration/platform/sonic_clab/11-vxlan-evpn.yml new file mode 100644 index 0000000000..8896657e94 --- /dev/null +++ b/tests/integration/platform/sonic_clab/11-vxlan-evpn.yml @@ -0,0 +1,37 @@ +# SONiC EVPN-VXLAN: OSPF underlay + iBGP EVPN over loopbacks, VLAN red +# (pure L2, vni 10043) stretched between the two VTEPs. h1/h2 are linux hosts behind +# untagged access ports; h1 ping h2 can ONLY succeed through the VXLAN tunnel across +# the routed core — the definitive datapath proof. +# docker-sonic-vs's orchagent can't program the dataplane, so the L2VNI is built on +# the FRR/kernel path (traditional bridge + kernel vxlan netdev); zebra drives EVPN. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 + +module: [ vlan, ospf, bgp, vxlan, evpn ] +bgp.as: 65000 + +vlans: + red: + mode: bridge + vni: 10043 + +nodes: + s1: + s2: + h1: + device: linux + h2: + device: linux + +links: +- s1-s2 +- s1: + vlan.access: red + h1: +- s2: + vlan.access: red + h2: diff --git a/tests/integration/platform/sonic_clab/12-evpn-irb-l3vni.yml b/tests/integration/platform/sonic_clab/12-evpn-irb-l3vni.yml new file mode 100644 index 0000000000..c86fd71e39 --- /dev/null +++ b/tests/integration/platform/sonic_clab/12-evpn-irb-l3vni.yml @@ -0,0 +1,50 @@ +# SONiC EVPN symmetric-IRB (L3VNI inter-subnet) spike. +# Two subnets (red, blue) in tenant VRF, stretched via VXLAN with +# an L3VNI. h1 (behind s1, subnet red) and h2 (behind s2, subnet blue) are in DIFFERENT +# subnets -- inter-subnet ping can only work through the L3VNI routing across the +# fabric, which is the symmetric-IRB datapath proof. +# +# docker-sonic-vs's orchagent bypassed as usual (kernel/FRR path, see +# templates/vxlan/sonic.j2 + templates/vlan/sonic.j2 for the create-before-address +# vrf-enslavement ordering this needed). +# python validate/sonic.py --module evpn_irb +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 + +module: [ vlan, vrf, ospf, bgp, vxlan, evpn ] +bgp.as: 65000 + +vrfs: + tenant: + evpn.transit_vni: 10099 + +vlans: + red: + mode: irb + vrf: tenant + vni: 10010 + blue: + mode: irb + vrf: tenant + vni: 10020 + +nodes: + s1: + s2: + h1: + device: linux + h2: + device: linux + +links: +- s1-s2 +- s1: + vlan.access: red + h1: +- s2: + vlan.access: blue + h2: diff --git a/tests/integration/platform/sonic_clab/13-vrf-leak.yml b/tests/integration/platform/sonic_clab/13-vrf-leak.yml new file mode 100644 index 0000000000..4b38a00d13 --- /dev/null +++ b/tests/integration/platform/sonic_clab/13-vrf-leak.yml @@ -0,0 +1,24 @@ +# SONiC VRF route-leaking: on s1, VRF blue imports VRF red +# RTs -> reds stub prefix appears in blues RIB (FRR rt vpn import, intra-node +# control-plane leak, independent of the VS dataplane). +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ vrf, bgp ] +bgp.as: 65000 +vrfs: + red: + blue: + import: [ red, blue ] +nodes: [ s1, s2 ] +links: +- s1-s2 +- s1: + vrf: red + type: stub +- s1: + vrf: blue + type: stub diff --git a/tests/integration/platform/sonic_clab/14-bgp-session.yml b/tests/integration/platform/sonic_clab/14-bgp-session.yml new file mode 100644 index 0000000000..188270754e --- /dev/null +++ b/tests/integration/platform/sonic_clab/14-bgp-session.yml @@ -0,0 +1,17 @@ +# SONiC bgp.session plugin: eBGP with MD5 password. FRR-delegated. +provider: clab +plugin: [ multilab, bgp.session ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ bgp ] +nodes: + s1: + bgp.as: 65001 + s2: + bgp.as: 65002 +links: +- s1: + s2: + bgp.password: Secret123 diff --git a/tests/integration/platform/sonic_clab/15-bgp-policy.yml b/tests/integration/platform/sonic_clab/15-bgp-policy.yml new file mode 100644 index 0000000000..9190549dae --- /dev/null +++ b/tests/integration/platform/sonic_clab/15-bgp-policy.yml @@ -0,0 +1,49 @@ +# SONiC bgp.policy plugin. Applies FRR route-maps to an eBGP session: +# * a NAMED inbound route-map (routing.policy) that matches a prefix-list and +# sets local-preference + a standard community (match + set) +# * plugin COMPOUND attributes (bgp.weight direct, bgp.med -> auto out route-map) +# s2 originates 10.43.99.0/24 (connected stub); s1 receives it and the inbound +# policy rewrites LocPref=250 + community 65001:100, and weight=50 (best-path effect). +provider: clab +plugin: [ multilab, bgp.policy ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ bgp, routing ] + +groups: + routers: + members: [ s1, s2 ] + routing: + prefix: + cust-net: + - ipv4: 10.43.0.0/16 + min: 24 + max: 24 + policy: + set-lp: # inbound: match prefix-list -> set locpref + community + - match: + prefix: cust-net + set: + locpref: 250 + community: + standard: [ "65001:100" ] + - sequence: 100 # permit the rest (FRR route-maps deny by default) + +nodes: + s1: + bgp.as: 65001 + s2: + bgp.as: 65002 + bgp.import: [ connected ] + +links: +- s1: + bgp.policy.in: set-lp # named inbound route-map + bgp.weight: 50 # direct neighbor attribute + bgp.med: 111 # compound -> auto out route-map + s2: +- s2: # s2's connected stub, redistributed into BGP + type: stub + prefix: 10.43.99.0/24 diff --git a/tests/integration/platform/sonic_clab/16-ebgp-multihop.yml b/tests/integration/platform/sonic_clab/16-ebgp-multihop.yml new file mode 100644 index 0000000000..ba1a1aa874 --- /dev/null +++ b/tests/integration/platform/sonic_clab/16-ebgp-multihop.yml @@ -0,0 +1,39 @@ +# SONiC ebgp.multihop plugin. eBGP session between the two LOOPBACKS +# (multihop, up to 255 hops) across an AS boundary (s1 AS65001 <-> s2 AS65002), +# with s1 presenting a local-as of 65101. netlab does NOT run an IGP across an +# eBGP AS boundary, so loopback reachability is modeled with the routing.static +# module (static route to the remote loopback via the connected transit subnet) -- +# the realistic multihop underlay. The connected link carries NO direct BGP session +# (bgp: False) so the ONLY session is the multihop loopback-to-loopback one. +# FRR-delegated (templates/ebgp.multihop/sonic.j2 = include frr.j2). +provider: clab +plugin: [ multilab, ebgp.multihop ] +defaults: + device: sonic_clab + multilab: + id: 52 + +module: [ bgp, ospf, routing ] +bgp.advertise_loopback: True + +bgp.multihop.sessions: +- s1: + local_as: 65101 + s2: + +nodes: + s1: + bgp.as: 65001 + routing.static: + - ipv4: 10.0.0.2/32 + nexthop.ipv4: 10.1.0.2 + s2: + bgp.as: 65002 + routing.static: + - ipv4: 10.0.0.1/32 + nexthop.ipv4: 10.1.0.1 + +links: +- s1: + s2: + bgp: False diff --git a/tests/integration/platform/sonic_clab/17-ospf-areas.yml b/tests/integration/platform/sonic_clab/17-ospf-areas.yml new file mode 100644 index 0000000000..abe4687436 --- /dev/null +++ b/tests/integration/platform/sonic_clab/17-ospf-areas.yml @@ -0,0 +1,21 @@ +# SONiC ospf.areas plugin: s1 is an ABR (loopback area 0 + link area 1 +# stub); s2 is a pure stub-area router. FRR-delegated. +provider: clab +plugin: [ multilab, ospf.areas ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ ospf ] +nodes: + s1: + ospf.areas: [ { area: 0.0.0.1, kind: stub } ] + s2: + ospf.areas: [ { area: 0.0.0.1, kind: stub } ] +links: +- s1: + ospf.area: 0.0.0.0 + type: stub +- s1: + s2: + ospf.area: 0.0.0.1 diff --git a/tests/integration/platform/sonic_clab/18-bgp-originate.yml b/tests/integration/platform/sonic_clab/18-bgp-originate.yml new file mode 100644 index 0000000000..fdd0983d76 --- /dev/null +++ b/tests/integration/platform/sonic_clab/18-bgp-originate.yml @@ -0,0 +1,18 @@ +# SONiC bgp.originate. A node injects a prefix into BGP for which it +# has NO interface/route (native bgp.originate, now part of the BGP module). s1 +# originates 10.201.9.0/24; the eBGP peer s2 must receive & install it. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ bgp ] +nodes: + s1: + bgp.as: 65001 + bgp.originate: [ 10.201.9.0/24 ] + s2: + bgp.as: 65002 +links: +- s1-s2 diff --git a/tests/integration/platform/sonic_clab/19-bfd.yml b/tests/integration/platform/sonic_clab/19-bfd.yml new file mode 100644 index 0000000000..a600e30103 --- /dev/null +++ b/tests/integration/platform/sonic_clab/19-bfd.yml @@ -0,0 +1,12 @@ +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ ospf, bfd ] +nodes: [ s1, s2 ] +links: +- s1: + s2: + ospf.bfd: True diff --git a/tests/integration/platform/sonic_clab/20-ripv2.yml b/tests/integration/platform/sonic_clab/20-ripv2.yml new file mode 100644 index 0000000000..d922689377 --- /dev/null +++ b/tests/integration/platform/sonic_clab/20-ripv2.yml @@ -0,0 +1,11 @@ +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ ripv2 ] +nodes: [ s1, s2 ] +links: +- s1: + s2: diff --git a/tests/integration/platform/sonic_clab/21-evpn-multihoming.yml b/tests/integration/platform/sonic_clab/21-evpn-multihoming.yml new file mode 100644 index 0000000000..6fcd7dd9c2 --- /dev/null +++ b/tests/integration/platform/sonic_clab/21-evpn-multihoming.yml @@ -0,0 +1,47 @@ +# SONiC EVPN Multihoming: ESI-LAG dual-homes h1 across s1+s2. No MLAG peer-link +# needed — ES coordination is via BGP EVPN Type-1 (Ethernet A-D) / Type-4 (ES) routes, not an +# MLAG protocol, so this works even though docker-sonic-vs has no mclagd. +# PortChannel is teamd, not kernel bonding -- doesn't matter: `show interface PortChannel1` +# reports "Interface Type bond" regardless, so zebra's EVPN-MH ES logic treats it exactly like a +# real bond. h2 is single-homed off s2, so the datapath check (h1 -> h2) only succeeds if traffic +# egressing EITHER leg of h1's dual-homed LAG reaches the shared L2VNI -- the actual point of +# multihoming. +provider: clab +plugin: [ multilab, evpn.multihoming ] +defaults: + device: sonic_clab + multilab: + id: 52 + +module: [ lag, vlan, ospf, bgp, vxlan, evpn ] +bgp.as: 65000 + +vlans: + red: + mode: bridge + vni: 10043 + +nodes: + s1: + s2: + h1: + device: linux + module: [ lag, vlan ] # host must opt into lag explicitly (not auto-enabled for linux) + config: [ h1-fixaddr ] # work around a netlab host-addressing gap, see h1-fixaddr/linux.j2 + h2: + device: linux + +links: +- s1-s2 +- lag: + members: + - s1: + evpn.es: es1 + h1: + - s2: + evpn.es: es1 + h1: + vlan.access: red +- s2: + vlan.access: red + h2: diff --git a/tests/integration/platform/sonic_clab/22-ospfv3.yml b/tests/integration/platform/sonic_clab/22-ospfv3.yml new file mode 100644 index 0000000000..6158e9d1b2 --- /dev/null +++ b/tests/integration/platform/sonic_clab/22-ospfv3.yml @@ -0,0 +1,25 @@ +# SONiC OSPFv3 (dual-stack): FRR's ospf6d is already enabled by the deploy for every +# topology (ospfd/ospf6d/isisd all get flipped on in /etc/frr/daemons); the core `ospf` module +# template renders OSPFv3 automatically whenever ospf.af.ipv6 is set, which netlab derives from +# IPv6 addressing being present. Same 2-node p2p shape as the plain OSPF test, just dual-stack. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 + +addressing: + loopback: + ipv6: 2001:db8:0::/48 + p2p: + ipv6: 2001:db8:100::/48 + prefix6: 64 + +module: [ ospf ] + +nodes: [ s1, s2 ] + +links: +- s1: + s2: diff --git a/tests/integration/platform/sonic_clab/23-isis-v6-bgp-v6.yml b/tests/integration/platform/sonic_clab/23-isis-v6-bgp-v6.yml new file mode 100644 index 0000000000..1794adb670 --- /dev/null +++ b/tests/integration/platform/sonic_clab/23-isis-v6-bgp-v6.yml @@ -0,0 +1,26 @@ +# SONiC IS-IS(v6) + iBGP(v6): dual-stack IGP + BGP AF, same 2-node p2p shape as the +# IPv4 IS-IS/BGP tests, just IPv6. isisd is already enabled by the deploy; FRR's isisd +# renders IPv6 reachability (multi-topology) as soon as ipv6 addressing is present. iBGP peers +# over the v6 loopbacks, activated in the ipv6 unicast AF alongside ipv4. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 + +addressing: + loopback: + ipv6: 2001:db8:0::/48 + p2p: + ipv6: 2001:db8:100::/48 + prefix6: 64 + +module: [ isis, bgp ] +bgp.as: 65000 + +nodes: [ s1, s2 ] + +links: +- s1: + s2: diff --git a/tests/integration/platform/sonic_clab/24-routing-v6.yml b/tests/integration/platform/sonic_clab/24-routing-v6.yml new file mode 100644 index 0000000000..b67e5b0be3 --- /dev/null +++ b/tests/integration/platform/sonic_clab/24-routing-v6.yml @@ -0,0 +1,44 @@ +# SONiC IPv6 static routing + prefix-lists: dual-stack version of the static-route +# slice of the routing test. Verifies `ipv6 route` + `ipv6 prefix-list` actually +# render and install, not just that the Jinja has an {% if %} branch for them. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 + +addressing: + loopback: + ipv6: 2001:db8:0::/48 + p2p: + ipv6: 2001:db8:100::/48 + prefix6: 64 + +module: [ routing ] + +groups: + routers: + members: [ s1, s2 ] + routing: + prefix: + stub-net6: + - ipv6: 2001:db8:143::/48 + min: 64 + max: 64 + +nodes: + s1: + routing.static: + - ipv6: 2001:db8:143:42::/64 + nexthop: + node: s2 + s2: + routing.static: + - ipv6: 2001:db8:143:41::/64 + nexthop: + node: s1 + +links: +- s1: + s2: diff --git a/tests/integration/platform/sonic_clab/25-vrf-v6-leak.yml b/tests/integration/platform/sonic_clab/25-vrf-v6-leak.yml new file mode 100644 index 0000000000..8991f29e50 --- /dev/null +++ b/tests/integration/platform/sonic_clab/25-vrf-v6-leak.yml @@ -0,0 +1,36 @@ +# SONiC VRF IPv6 + v6 route-leaking: dual-stack version of the VRF route-leaking test. +# On s1, VRF blue imports VRF red's route-targets -> red's IPv6 stub +# prefix should appear in blue's v6 RIB (FRR rt vpn import, intra-node control-plane leak, +# independent of the VS dataplane -- same class as the v4 leak test). +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 + +addressing: + loopback: + ipv6: 2001:db8:0::/48 + p2p: + ipv6: 2001:db8:100::/48 + prefix6: 64 + lan: + ipv6: 2001:db8:1::/48 + prefix6: 64 + +module: [ vrf, bgp ] +bgp.as: 65000 +vrfs: + red: + blue: + import: [ red, blue ] +nodes: [ s1, s2 ] +links: +- s1-s2 +- s1: + vrf: red + type: stub +- s1: + vrf: blue + type: stub diff --git a/tests/integration/platform/sonic_clab/26-vrf-v6-ospfv3.yml b/tests/integration/platform/sonic_clab/26-vrf-v6-ospfv3.yml new file mode 100644 index 0000000000..adac4660f5 --- /dev/null +++ b/tests/integration/platform/sonic_clab/26-vrf-v6-ospfv3.yml @@ -0,0 +1,26 @@ +# SONiC per-VRF OSPFv3: dual-stack version of the VRF test (link in VRF +# red with per-VRF OSPF). Needs features.vrf.ospfv3 (declared on the sonic_clab device) -- +# the shared vrf/frr.frr-config.j2 already renders `router ospf6 vrf ` once the +# feature flag unlocks it and the VRF has an ipv6 AF. +provider: clab +plugin: [ multilab ] +defaults: + device: sonic_clab + multilab: + id: 52 + +addressing: + loopback: + ipv6: 2001:db8:0::/48 + p2p: + ipv6: 2001:db8:100::/48 + prefix6: 64 + +module: [ vrf, ospf ] +vrfs: + red: +nodes: [ s1, s2 ] +links: +- s1: + s2: + vrf: red diff --git a/tests/integration/platform/sonic_clab/27-tunnel-gre.yml b/tests/integration/platform/sonic_clab/27-tunnel-gre.yml new file mode 100644 index 0000000000..f0ac061b63 --- /dev/null +++ b/tests/integration/platform/sonic_clab/27-tunnel-gre.yml @@ -0,0 +1,23 @@ +# SONiC GRE tunnel: tunnel.gre is kernel-side (ip_gre), so it's not subject to the +# orchagent-can't-program-the-VS-dataplane limitation that affects VXLAN/EVPN — it should Just Work. +# s1-s2 direct link is the underlay; the GRE tunnel rides over it. Ping across the tunnel IPs +# (not the underlay IPs) is the datapath proof — traffic must be GRE-encapsulated to arrive. +provider: clab +plugin: [ multilab, tunnel.gre ] +defaults: + device: sonic_clab + multilab: + id: 52 + +module: [ ospf ] + +nodes: + s1: + s2: + +links: +- s1-s2 +- s1: + s2: + tunnel.mode: gre + ospf: false diff --git a/tests/integration/platform/sonic_clab/28-files-maxprefix.yml b/tests/integration/platform/sonic_clab/28-files-maxprefix.yml new file mode 100644 index 0000000000..1edfbc5797 --- /dev/null +++ b/tests/integration/platform/sonic_clab/28-files-maxprefix.yml @@ -0,0 +1,44 @@ +# SONiC files-plugin escape hatch worked example. +# BGP maximum-prefix-limit is a genuine netlab-CORE model +# gap (not device-specific): no netlab BGP module or plugin exposes a max-prefix +# attribute (defaults.yml bgp.session attr list has no such leaf) on ANY device, but +# FRR (and therefore SONiC, since SONiC routing IS FRR) supports it natively via plain +# vtysh CLI. This topology uses the `files` plugin's inline `configlets` + a node +# `config:` reference to inject the raw FRR config with ZERO external files -- the +# whole vendor-proprietary (here: FRR-native, still model-gap) config lives in this one +# topology YAML, same escape-hatch pattern as the ArcOS example. +# +# Mechanism check: the files plugin materializes the configlet as a real +# `bgp-maxprefix.j2` file in the topology directory at `netlab create` time; node +# `config: [ bgp-maxprefix ]` triggers netlab-core's stock "Deploy custom deployment +# templates" play, which resolves its deploy TASK the same way modules do +# (paths_deploy.tasks_generic) -- so it lands on our OWN tasks/deploy-config/sonic.yml +# override (docker exec + vtysh -f), the same mechanism already proven for +# tunnel.gre/evpn.multihoming plugin configs. No SONiC-specific plumbing needed. +# python validate/sonic.py --module files_maxprefix +provider: clab +plugin: [ multilab, files ] +defaults: + device: sonic_clab + multilab: + id: 52 +module: [ bgp ] +nodes: + s1: + bgp.as: 65001 + config: [ bgp-maxprefix ] + s2: + bgp.as: 65002 +links: +- s1: + s2: + +configlets: + bgp-maxprefix: | + router bgp {{ bgp.as }} + address-family ipv4 unicast + {% for n in bgp.neighbors|default([]) if n.ipv4 is defined %} + neighbor {{ n.ipv4 }} maximum-prefix 100 + {% endfor %} + exit-address-family + ! diff --git a/tests/integration/platform/sonic_clab/29-bgp-domain.yml b/tests/integration/platform/sonic_clab/29-bgp-domain.yml new file mode 100644 index 0000000000..63ee9e4991 --- /dev/null +++ b/tests/integration/platform/sonic_clab/29-bgp-domain.yml @@ -0,0 +1,58 @@ +# SONiC bgp.domain plugin. The bgp.domain plugin splits a SINGLE AS into +# ISOLATED iBGP "domains": it PRUNES every cross-domain iBGP session and requires each +# domain to have its OWN route reflector (it does NOT hierarchically connect domains -- +# domains are independent iBGP islands that share one AS number). This models AS 65000 +# with two domains: +# red = s1 (route reflector) + s2 + s3 (RR clients) +# blue = s4 (route reflector, alone) +# netlab first builds the RR client mesh (clients peer to every RR in the AS over the +# OSPF underlay), then the plugin prunes all cross-domain sessions, so: +# * s1 keeps iBGP ONLY to s2/s3 (red); the s1<->s4 (blue) session is pruned. +# * s4 (blue) ends up with NO iBGP neighbors -- its only peers were red, all pruned. +# +# DATAPATH: h2 is a real host behind s2 on 172.16.2.0/24. That link has `ospf: False`, +# so the prefix is advertised into BGP ONLY (never into the OSPF underlay) -- reachability +# of h2 is therefore a PURE bgp.domain fact: +# * s3 (red client) reaches h2 ONLY because s1 reflects s2's prefix -> proves RR +# client-to-client reflection AT THE DATAPATH. +# * s4 (blue) has no route to 172.16.2.0/24 at all -> ping fails -> proves cross-domain +# isolation AT THE DATAPATH. +provider: clab +plugin: [ multilab, bgp.domain ] +defaults: + device: sonic_clab + multilab: + id: 52 + +module: [ bgp, ospf ] +bgp.as: 65000 +bgp.advertise_loopback: True + +nodes: + s1: + bgp.rr: True + bgp.domain: red + s2: + bgp.domain: red + s3: + bgp.domain: red + s4: + bgp.rr: True + bgp.domain: blue + h2: + device: linux + +links: +# OSPF underlay (star on s1) — carries the loopbacks so the iBGP sessions come up. +- s1: + s2: +- s1: + s3: +- s1: + s4: +# Red customer prefix behind s2 — BGP-only (ospf:False keeps it out of the underlay), +# so reaching h2 depends entirely on RR reflection. +- s2: + h2: + ospf: False + prefix: 172.16.2.0/24 diff --git a/tests/integration/platform/sonic_clab/README.md b/tests/integration/platform/sonic_clab/README.md new file mode 100644 index 0000000000..4f4d7a373c --- /dev/null +++ b/tests/integration/platform/sonic_clab/README.md @@ -0,0 +1,90 @@ +# SONiC (containerlab) integration smoke tests + +Device-specific bring-up smoke tests for the `sonic_clab` device (docker-sonic-vs on +containerlab). Every topology below has been run live against a real `docker-sonic-vs:latest` +node (containerlab 0.77.0) with `netlab up`/`netlab initial`, all over Ansible's +`docker` connection plugin -- no SSH. + +| topology | module(s) exercised | what was checked live | +|---|---|---| +| `01-initial.yml` | initial | 1-node bring-up: hostname, loopback IP, interfaces | +| `02-ospf.yml` | ospf | adjacency Full (pure `ansible_network_os: frr` fallback, no `sonic_clab` template) | +| `03-bgp.yml` | bgp | eBGP session Established (frr fallback) | +| `04-isis.yml` | isis | adjacency Up (frr fallback) | +| `05-vrf.yml` | vrf, ospf | per-VRF OSPF adjacency Full (frr fallback) | +| `06-mpls-sr-l3vpn.yml` | isis, bgp, mpls, sr, vrf | LDP Operational, real kernel MPLS label FIB, VPNv4 Established, L3VPN ping 0% loss. **Needs a one-time host prereq: `sudo modprobe mpls_router mpls_iptunnel`** | +| `07-srv6.yml` | isis, srv6 | real kernel `seg6local` End/End.X routes, cross-node ISIS locator advertisement | +| `08-vlan.yml` | vlan | IRB SVI + kernel bridge datapath, 0% loss (needed a `sonic_clab.j2` override + kernel-sync -- see below) | +| `09-lag.yml` | lag, ospf | LACP aggregate (teamd), OSPF Full over the PortChannel (needed a `sonic_clab.j2` override) | +| `11-vxlan-evpn.yml` | vlan, ospf, bgp, vxlan, evpn | L2VNI up, remote VTEP learned, h1<->h2 ping across the tunnel 0% loss (needed a `sonic_clab.j2` override) | +| `12-evpn-irb-l3vni.yml` | vlan, vrf, ospf, bgp, vxlan, evpn | symmetric-IRB L3VNI inter-subnet ping 0% loss | +| `13-vrf-leak.yml` | vrf, bgp | VRF red's prefix leaks into VRF blue's RIB via BGP RT import | +| `14-bgp-session.yml` | bgp, bgp.session plugin | MD5 password applied, session Established (frr fallback) | +| `15-bgp-policy.yml` | bgp, bgp.policy plugin, routing | route-map locpref/weight/community all applied (frr fallback) | +| `16-ebgp-multihop.yml` | bgp, ebgp.multihop plugin, ospf, routing | loopback-to-loopback multihop session Established (frr fallback) | +| `17-ospf-areas.yml` | ospf, ospf.areas plugin | stub area adjacency Full (frr fallback) | +| `18-bgp-originate.yml` | bgp | originated prefix received by the eBGP peer (frr fallback) | +| `19-bfd.yml` | ospf, bfd | BFD session Up under OSPF (frr fallback; needed `features.ospf.bfd`/`features.bgp.bfd` device flags) | +| `20-ripv2.yml` | ripv2 | route learned via RIP (frr fallback) | +| `21-evpn-multihoming.yml` | lag, vlan, ospf, bgp, vxlan, evpn, evpn.multihoming plugin | Ethernet Segment on PortChannel1, dual-homed h1<->h2 ping 0% loss (needs the `h1-fixaddr/` custom-config addon, a generic `linux`-device host-addressing workaround, not SONiC-specific) | +| `22-ospfv3.yml` | ospf | OSPFv3 dual-stack: both AFs adjacency Full, ping6 0% loss (frr fallback) | +| `23-isis-v6-bgp-v6.yml` | isis, bgp | IS-IS adjacency Up (multi-topology IPv6), iBGP IPv6 AF Established (frr fallback) | +| `24-routing-v6.yml` | routing | IPv6 static route + IPv6 prefix-list both installed (frr fallback) | +| `25-vrf-v6-leak.yml` | vrf, bgp | VRF red's IPv6 prefix leaks into VRF blue's v6 RIB via BGP RT import (frr fallback) | +| `26-vrf-v6-ospfv3.yml` | vrf, ospf | per-VRF OSPFv3 adjacency Full (frr fallback) | +| `27-tunnel-gre.yml` | ospf, tunnel.gre plugin | kernel `ip_gre` tunnel, ping across the tunnel IPs 0% loss (needed an `initial/sonic_clab.j2` fix -- see below; the `tunnel.gre/frr.j2` plugin template itself needed no override) | +| `28-files-maxprefix.yml` | bgp, files plugin | `files` escape-hatch worked example: raw FRR `maximum-prefix 100` configlet injected and applied (frr fallback + our own `deploy-config/sonic_clab.yml`, same mechanism proven for tunnel.gre/evpn.multihoming) | +| `29-bgp-domain.yml` | bgp, ospf, bgp.domain plugin | isolated iBGP domains: s1 keeps exactly 2 (red) peers, the cross-domain s1<->s4 session is pruned, s3 (red) reaches h2 via RR reflection, s4 (blue) has zero BGP neighbors and no route to h2 (frr fallback + netlab-core plugin logic, no device template involved) | + +Run with `netlab up ` from this directory (needs `docker-sonic-vs:latest` pulled locally +and a `multilab` id that isn't in use -- these default to id 52). + +## What actually needed a `sonic_clab.j2` override vs. what's free + +Most modules above render and deploy with **zero new template files** -- they fall through +automatically to the package's `/frr.j2` via the `ansible_network_os: frr` search-path +fallback (see `netsim/devices/sonic_clab.yml`'s `group_vars.ansible_network_os` inherited from +the parent `sonic` device). Only these needed a real `sonic_clab.j2`, each verified live here: + +* `initial` -- interface/loopback bring-up via the `config` CLI, CONFIG_DB->kernel sync for + routed ports, FRR daemon enable, sshd bootstrap. config_db VLAN and PortChannel *creation* is + factored out into the per-module init hooks below (pulled in at the right ordering point by + `extra_module_initial()`, the same mechanism VyOS uses). +* `vlan/sonic_clab.initial.j2` -- create config_db VLANs (`Vlan`) before any SVI is addressed. +* `vlan` -- switchport membership via `config vlan member add`, **plus a kernel bridge sync** + (docker-sonic-vs's `vlanmgrd` races at boot and often never mirrors `VLAN_MEMBER` rows written + during the first deploy -- found live: config_db had the row, but the kernel port had no + `master Bridge` and cross-node ping failed 100% until the sync was added). +* `lag/sonic_clab.initial.j2` -- create the config_db PortChannel(s) before the aggregate is + addressed. +* `lag` -- PortChannel members via `config portchannel member add`, plus a defensive `teamdctl` + sync (teammgrd is more reliable than vlanmgrd, but this belt-and-suspenders step guards the + rare miss). +* `vxlan` -- EVPN-VXLAN L2VNI/L3VNI built directly on the kernel/FRR path (bridge + kernel vxlan + netdev), since docker-sonic-vs's orchagent can't program the VXLAN dataplane from CONFIG_DB. + The EVPN/BGP control plane itself is standard FRR (`bgp/frr.j2` + zebra `advertise-all-vni`); + this template is only the kernel data-plane setup the VS orchestration agent won't do. + +`bgp`, `evpn`, `evpn.multihoming`, `isis`, `vrf`, `mpls`, `sr`, `srv6`, `bfd`, `gateway`, +`routing`, `ripv2`, `tunnel.gre`, +`bgp.session`/`bgp.policy`/`bgp.originate`/`bgp.domain`/`ebgp.multihop`, `ospf.areas`, and the +IPv4/IPv6 dual-stack variants of all of the above need no override at all -- they render via the +frr fallback too. (`bgp` needs no `no router bgp` reset wrapper: unlike the Azure/libvirt sonic +image, docker-sonic-vs does not pre-seed a BGP AS.) + +One bug found and fixed by `27-tunnel-gre.yml`: the `initial/sonic_clab.j2` bash/config_db loop +deliberately skips tunnel interfaces (the `config` CLI only accepts Ethernet/PortChannel/ +Vlan/Loopback names), and the shared `tunnel.gre/frr.j2` plugin template only creates the kernel +netdev (`ip tunnel add`), it doesn't address it -- so the tunnel interface was created with NO +IP at all until the vtysh heredoc in `initial/sonic_clab.j2` was extended to address +tunnel-type interfaces itself (the same thing the vanilla `frr` device's own initial template +already does for every interface, unconditionally -- our SONiC template only omits it elsewhere +because config_db handles addressing for every other interface type). + +Genuinely unsupported on this image, not just untested (see `netsim/devices/sonic_clab.yml`'s +`support.caveats` for the live-probed reasons): DHCP relay/client, real STP port-blocking, +and the `mlag.vtep` active-active datapath. + +This is a device bring-up smoke-test set, not (yet) wired into netlab's shared per-module +`tests/integration//NN-*.yml` parameterized matrix that runs across all devices -- that +wiring is tracked as a follow-up. diff --git a/tests/integration/platform/sonic_clab/h1-fixaddr/linux.j2 b/tests/integration/platform/sonic_clab/h1-fixaddr/linux.j2 new file mode 100644 index 0000000000..f3e63717ac --- /dev/null +++ b/tests/integration/platform/sonic_clab/h1-fixaddr/linux.j2 @@ -0,0 +1,12 @@ +#!/bin/bash +# +# h1-fixaddr custom config — h1 is the M-side of an MLAG-style +# LAG (dual-homed to s1+s2) on a bridge-mode VLAN. netlab's vlan-module address-pool allocation +# doesn't reach the synthesized vlan1000 SVI on a `linux`-device host in this specific +# lag+bridge-mode combo (single-homed h2 on the SAME vlan gets addressed fine, directly on its +# physical port -- the gap is specific to the derived SVI a dual-homed host needs). Netlab-side +# host-addressing quirk, not a SONiC platform limitation -- worked around here so the h1<->h2 +# datapath check has a real IP to ping from. +set -e +ip addr add 172.16.0.5/24 dev vlan1000 2>/dev/null || true +exit 0 From bf087fc40626f0560813bfc2d690d28ecc67a714 Mon Sep 17 00:00:00 2001 From: RocNet Date: Wed, 22 Jul 2026 01:49:59 +0000 Subject: [PATCH 03/23] docs: document the SONiC containerlab (sonic_clab) device Add the sonic_clab device to the platform tables (device list, configuration deployment, initial config, routing/dataplane/EVPN/layer-2/IPv6 module support), a caveats section covering the image/connection model, automatic kernel-module loading for MPLS/SR, and the SRv6 control-plane limit, a containerlab usage section in the SONiC lab notes, and a 26.07 release note. --- docs/caveats.md | 49 +++++++++++++++++++++++++++++++++++++++++++ docs/labs/sonic.md | 19 +++++++++++++++++ docs/platforms.md | 11 ++++++++++ docs/release/26.07.md | 1 + 4 files changed, 80 insertions(+) diff --git a/docs/caveats.md b/docs/caveats.md index 79e4594659..e2b0956071 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -652,6 +652,55 @@ See also [](caveats-sros) caveats for further details. * The Azure Sonic VM image has to be started with a preconfigured BGP AS number (specified in **config_db.json**); otherwise, it does not start the FRR container. That BGP process is removed during the initial BGP configuration and replaced with the actual BGP AS number specified in the lab topology. * _netlab_ configures BGP on Sonic through vtysh, not through **config_db**. +(caveats-sonic-clab)= +## Sonic (containerlab) + +A separate device (`sonic_clab`, parent `sonic`) for the community `docker-sonic-vs` image +running under *containerlab*, distinct from the `sonic` device above (which targets the +Azure/libvirt SONiC VM). The two images have different internal architectures and are not +interchangeable: `docker-sonic-vs` is a single monolithic container (FRR's `vtysh` runs +directly in it), while the VM runs FRR inside a nested `bgp` sub-container. + +* You supply your own `docker-sonic-vs:latest` image (build it from the + [sonic-buildimage](https://github.com/sonic-net/sonic-buildimage) `docker-sonic-vs` target, or + pull a prebuilt one); *netlab* does not ship or distribute it. +* Configuration is deployed over Ansible's built-in `docker` connection plugin (`docker exec`), + not `network_cli`: `docker-sonic-vs` has no cliconf-compatible CLI. The only SONiC cliconf + Ansible ships, `dellemc.enterprise_sonic`, targets Dell's licensed "Management Framework" CLI + (`sonic-cli`/klish) on Dell PowerSwitch hardware running Enterprise SONiC -- that CLI does not + exist on the community image (no `admin` user, no `sonic-cli`/`klish` binary anywhere in it). + This matches how *netlab* already drives the in-tree `frr` *containerlab* device and the + libvirt `sonic` device above (which has a full sshd) -- neither uses `network_cli` for this + vtysh-delegated render-then-push style of configuration, sshd or not. +* `docker-sonic-vs` ships `sshd` and host keys but starts neither `sshd` nor a login user; the + initial configuration bootstraps both (`admin`/`YourPaSsWoRd`, matching the `sonic` device's + own credentials) purely for interactive access (`netlab connect`, ad-hoc troubleshooting) -- + SSH plays no part in configuration deployment. +* Most FRR daemons ship disabled in `/etc/frr/daemons` by default (to save resources); the + initial configuration enables the ones the configured modules need and restarts FRR once. +* MPLS/SR-MPLS get a real kernel MPLS label FIB (`ip -M route`) with no manual setup: because + SONiC runs FRR, the device reuses netlab's FRR kernel-module handling (the `clab.kmods` + declaration), so `netlab up` loads `mpls_router`/`mpls_iptunnel` on the host automatically when + an MPLS or SR lab is deployed, the same way the in-tree `frr` containerlab device does. +* Module coverage is broad and FRR-delegated (`ospf`, `bgp`, `isis`, `vrf`, `bfd`, `mpls`, `sr`, + `srv6`, `vxlan`, `evpn`, `evpn.multihoming`, `gateway`, `ripv2`, `routing`, + `tunnel.gre`, `bgp.session`/`bgp.policy`/`bgp.originate`/`bgp.domain`/`ebgp.multihop`, + `ospf.areas`, both IPv4 and IPv6). Each module ships with a device integration topology under + `tests/integration/platform/sonic_clab/` describing exactly what it checks. OSPF, BGP, IS-IS, + VRF, and VLAN (including the SVI-to-SVI kernel-bridge datapath) were live-verified on + `docker-sonic-vs:latest` for this submission; the remaining topologies -- including the MPLS + L3VPN datapath (LDP transport + BGP VPN label, real kernel label FIB) and the EVPN-VXLAN + symmetric-IRB (L3VNI) datapath -- were validated during the device's development, not re-run for + this submission. +* `srv6` is control-plane + kernel-plane only on this image: the SRv6 locator and End/End.X SIDs + are advertised in the IS-IS LSDB and installed as real kernel `seg6local` routes (`ip -6 route`), + but end-to-end SRv6-OAM datapath (ping to a `uN` End SID) is not resolved on `docker-sonic-vs` + -- the same open item as the FRR/IS-IS SRv6 result on other platforms. +* Genuinely unsupported on this image (not just untested): DHCP relay/client (no + `dhcrelay`/`dhcp6relay` binary), real STP port-blocking (config-plane only, no `stpd` + daemon), and the `mlag.vtep` active-active EVPN datapath (config-plane renders correctly, but + there is no `mclagd`, so BGP never resolves a usable self-next-hop for the anycast VTEP). + (caveats-vyos)= ## VyOS diff --git a/docs/labs/sonic.md b/docs/labs/sonic.md index d66ad4a41a..b05da45c3d 100644 --- a/docs/labs/sonic.md +++ b/docs/labs/sonic.md @@ -19,3 +19,22 @@ During the box-building process, you might have to disable ZTP or clean up the i .. include:: sonic.txt :literal: ``` + +(labs-sonic-clab)= +## Using the containerlab Provider (docker-sonic-vs) + +Apart from the libvirt Vagrant box above, SONiC can run under **containerlab** using the community +`docker-sonic-vs` image via the `sonic_clab` device (parent: `sonic`). No box build is needed -- +supply your own `docker-sonic-vs:latest` image (build it from the +[sonic-buildimage](https://github.com/sonic-net/sonic-buildimage) `docker-sonic-vs` target, or pull a +community build) and select the device: + +``` +netlab up -d sonic_clab -p clab +``` + +`docker-sonic-vs` is a single monolithic container running FRR (`vtysh`); netlab pushes configuration +and runs validation over **docker exec** (the image starts no `sshd`). Native validation therefore +works out of the box -- `netlab up -d sonic_clab --validate` executes the FRR-based `show` +commands over docker-exec. See the [`sonic_clab` caveats](caveats-sonic-clab) for image/connection +details and the verified module set. diff --git a/docs/platforms.md b/docs/platforms.md index 99aecdd5ef..a8db0069d8 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -48,6 +48,7 @@ | Nokia SR-SIM [❗](caveats-srsim) | srsim | full | | OpenBSD [❗](caveats-openbsd) | openbsd | best effort | | Sonic [❗](caveats-sonic) | sonic | minimal | +| Sonic (containerlab) [❗](caveats-sonic-clab) | sonic_clab | best effort | | VyOS 1.4 [❗](caveats-vyos) | vyos | full | [^SROSBE]: With the launch of the Nokia SR SIM, we stopped running integration tests for the SR-OS VM, assuming the behavior of the two products would be nearly identical. @@ -211,6 +212,7 @@ Ansible playbooks included with **netlab** can deploy and collect device configu | Nokia SR OS[^SROS] | ✅ | ✅ | | OpenBSD | ✅ | ❌ | | Sonic | ✅ | ✅ | +| Sonic (containerlab) | ✅ | ✅ | | VyOS | ✅ | ✅ | **Note:** *netlab* can deploy daemon configurations, but cannot collect them. Use the **netlab initial -o** command to create daemon configuration files in a custom directory. @@ -236,6 +238,7 @@ _netlab_ uses Ansible playbooks and device-specific task lists to deploy device | Junos cRPD | clab | **bash** scripts[^cRBS] | | KinD | clab | **bash** scripts copied into and executed in containers | | linux | clab | host- or container-side **bash** scripts[^LBS] | +| Sonic (containerlab) | clab | **bash** or **vtysh** scripts[^FRRBV] over **docker exec** | [^FRRBV]: Configurations starting with a *shebang* are assumed to be Linux scripts; all other configurations are assumed to be **vtysh** scripts and get a `#!/usr/bin/vtysh -f` shebang prepended to them. @@ -297,6 +300,7 @@ The following system-wide features are configured on supported network operating | Nokia SR OS[^SROS] | ✅ | ✅ | ✅ | ✅ | ✅ | | OpenBSD | ✅ | ✅ | ❌ | ✅ | ✅ | | Sonic | ✅ | ✅ | ❌ | ✅ | ✅ | +| Sonic (containerlab) | ✅ | ✅ | ❌ | ✅ | ✅ | | VyOS | ✅ | ✅ | ✅ | ✅ | ✅ | [^HIF]: Some Linux-based devices can also use interface names in host names. See [/etc/hosts file on Linux](linux-hosts) for more details. @@ -328,6 +332,7 @@ The following interface parameters are configured on supported network operating | Nokia SR OS[^SROS] | ✅ | ❌ | ✅ | ✅ | | OpenBSD | ✅ | ❌ | ✅ | ❌ | | Sonic | ✅ | ✅ | ✅ | ✅ | +| Sonic (containerlab) | ✅ | ✅ | ✅ | ✅ | | VyOS | ✅ | ❌ | ✅ | ✅ | (platform-initial-addresses)= @@ -358,6 +363,7 @@ The following interface addresses are supported on various platforms; most daemo | Nokia SR OS[^SROS] | ✅ | ✅ | ✅ | ❌ | | OpenBSD | ✅ | ✅ | ❌ | ❌ | | Sonic | ✅ | ✅ | ✅ | ❌ | +| Sonic (containerlab) | ✅ | ✅ | ✅ | ❌ | | VyOS | ✅ | ✅ | ✅ | ❌ | ```{tip} @@ -404,6 +410,7 @@ Routing protocol [configuration modules](module-reference.md) are supported on t | Nokia SR OS[^SROS] | ✅ | ✅ | ❌ | ✅ | ✅ | | OpenBSD | ✅ | ❌ | ❌ | ✅ | ✅ | | Sonic | ❌ | ❌ | ❌ | ✅ | ❌ | +| Sonic (containerlab) | ✅ | ✅ | ❌ | ✅ | ✅ | | VyOS | ✅ | ✅ | ❌ | ✅ | ❌ | These devices support additional control-plane protocols or BGP address families: @@ -431,6 +438,7 @@ These devices support additional control-plane protocols or BGP address families | Mikrotik RouterOS 7 | ✅ | ❌ | ✅ | ❌ | | Nokia SR Linux | ✅ | ✅ | ✅ | ✅ | | Nokia SR OS[^SROS] | ✅ | ✅ | ✅ | ✅ | +| Sonic (containerlab) | ✅ | ✅ | ✅ | ✅ | | VyOS | ✅ | ✅ | ✅ | ❌ | **Notes:** @@ -450,6 +458,7 @@ The layer-2 control plane [configuration modules](module-reference.md) are suppo | dnsmasq | ❌ | ✅ | | FRR | ✅ | ✅ | | Linux | ❌ | ✅ | +| Sonic (containerlab) | ❌ | ✅ | (platform-dataplane-support)= The data plane [configuration modules](module-reference.md) are supported on these devices[^NSM]: @@ -482,6 +491,7 @@ The data plane [configuration modules](module-reference.md) are supported on the | Nokia SR Linux | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | Nokia SR OS[^SROS] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | OpenBSD | ✅ | ❌ | ✅ | ❌ | ❌ | +| Sonic (containerlab) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅[❗](caveats-sonic-clab) | | VyOS | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | (platform-services-support)= @@ -525,6 +535,7 @@ Core *netlab* functionality and all multi-protocol routing protocol configuratio | Nokia SR OS[^SROS] | ✅ | ✅ | ❌ | ✅ | ✅ | | OpenBSD | ✅ | ❌ | ❌ | ✅ | ❌ | | Sonic | ❌ | ❌ | ❌ | ✅ | ❌ | +| Sonic (containerlab) | ✅ | ✅ | ❌ | ✅ | ✅ | | VyOS | ✅ | ✅ | ❌ | ✅ | ❌ | (platform-unknown)= diff --git a/docs/release/26.07.md b/docs/release/26.07.md index 77e6c186ef..0bb197abe5 100644 --- a/docs/release/26.07.md +++ b/docs/release/26.07.md @@ -14,6 +14,7 @@ * The [**WireGuard tunnel** plugin](plugin-tunnel-wireguard) supports WireGuard tunnels on FRR. * The [**bgp.session** plugin](plugin-bgp-session) and the [OSPF module](module-ospf) support graceful restart on Arista EOS, BIRD, FortiOS, and FRR * The [**bgp.policy** plugin](plugin-bgp-policy) supports the **bgp.role** attribute on FRR and BIRD. +* SONiC can run under *containerlab* with the community `docker-sonic-vs` image via the new [`sonic_clab` device](caveats-sonic-clab), expanding SONiC coverage well beyond the libvirt device (OSPF/OSPFv3, IS-IS, BGP with the bgp.session/policy/originate/domain and ebgp.multihop plugins, RIPv2, BFD, VRF, VLAN, LAG, VXLAN/EVPN including symmetric IRB and multihoming, MPLS L3VPN, SR-MPLS, SRv6, VRRP, and GRE tunnels). **Minor changes and improvements** From 3a7149f32349a1b4458a860291293f66b1e5dfc1 Mon Sep 17 00:00:00 2001 From: RocNet Date: Wed, 29 Jul 2026 15:10:31 +0000 Subject: [PATCH 04/23] refactor(sonic): inherit the FRR device instead of a separate sonic_clab device Addresses the review of #3680: there is no need for a second device. SONiC runs FRR for its whole control plane, so the sonic device now inherits FRR (parent: frr) and turns OFF what SONiC cannot do, rather than re-enumerating the FRR feature set -- which is how the two drifted apart in the first place: the old sonic_clab device rendered routing/frr.j2 byte-identically yet declared far fewer features than frr does with that same template. * netsim/devices/sonic_clab.yml is removed; sonic.yml gains parent: frr, the clab: block, and a disable list. * container-vs-VM differences move into clab.features and clab.group_vars. * templates the container needs of its own become /sonic-clab.j2, which netlab looks up before /sonic.j2 (defaults/paths.yml t_files), and the deploy task becomes deploy-config/sonic-clab.yml (tasks_generic, same order). Precedent: initial/linux-clab.j2, initial/bird-clab.j2, deploy-config/linux-clab.yml. * tests/integration/platform/sonic_clab/ is removed -- the platform is tested with the existing tests/integration. Turned off for every SONiC deployment, each with a measured reason recorded in support.caveats: stp config-plane only, no stpd daemon dhcp no dhcrelay/dhcp6relay binary in the image routing.policy.match.nexthop never exercised on this platform routing.policy.set.community.extended never exercised on this platform The last two were previously *absent* from the device file. Under a parent device absence is inheritance, so they are now disabled explicitly: silence is a claim. vlan, lag, vxlan and evpn are disabled at device level and re-enabled in clab.features, because their configuration is built from config_db/redis-cli sequences that have only ever been exercised on docker-sonic-vs, never on the VM. Inheriting a parent brings its DEPLOYMENT MECHANISM, not just its features, and three FRR settings had to be overridden or the device does not configure at all: * clab.node.config_templates -- FRR bind-mounts /etc/frr/daemons and /etc/hosts; docker-sonic-vs ships its own daemons file that initial/sonic-clab.j2 edits in place, and mounting FRR's over it undoes that. * netlab_config_mode / netlab_default_shebang -- FRR selects netlab's native "sh" config mode with a '#!/usr/bin/vtysh -f' shebang, which bypasses deploy-config/sonic-clab.yml and feeds SONiC's bash 'config' script to vtysh. Measured symptom: "RuntimeError: Unable to connect to redis - Connection refused". * netlab_mgmt_vrf -- FRR sets it, SONiC did not. Verified on docker-sonic-vs after the restructure: * ospf/ospfv2/01-network SUCCESS 4/4 -- adjacencies Full, prefixes present, with OSPF rendered by the inherited routing/frr.j2 (no sonic OSPF template exists). * the MTU fix survives and still renders: "ip link set Ethernet0 mtu 1500". * feature resolution checked directly: stp/dhcp/vlan/lag/vxlan/evpn False at device level, re-enabled under clab, match.nexthop and set.community.extended False, and set.community.large True by inheritance -- the five declarations verified in the previous branch now come from the parent for free. KNOWN OPEN ITEM, not fixed here: routing/08-community-large is 9/11. BGP sessions establish and prefixes propagate, and dut.routing.cfg renders "set large-community 65000:0:101 65000:2:202" correctly, but applying it fails: vtysh -f /tmp/config.sh -> rc 13 "Failure to communicate[13] to bgpd, line: no router bgp" "% No BGP process is configured" The routing module is configured before bgp, and the cleanup line "no router bgp" that routing/frr.j2 emits is rejected because no BGP process exists yet. This is an ordering interaction with the inherited FRR routing template, not a feature declaration problem. --- .../{sonic_clab.yml => sonic-clab.yml} | 0 .../initial/{sonic_clab.j2 => sonic-clab.j2} | 8 +- .../lag/{sonic_clab.j2 => sonic-clab.j2} | 0 ...sonic_clab.initial.j2 => sonic.initial.j2} | 0 .../vlan/{sonic_clab.j2 => sonic-clab.j2} | 0 ...sonic_clab.initial.j2 => sonic.initial.j2} | 0 .../vxlan/{sonic_clab.j2 => sonic-clab.j2} | 0 netsim/devices/sonic.yml | 142 +++++++++++--- netsim/devices/sonic_clab.yml | 174 ------------------ .../platform/sonic_clab/01-initial.yml | 9 - .../platform/sonic_clab/02-ospf.yml | 28 --- .../platform/sonic_clab/03-bgp.yml | 30 --- .../platform/sonic_clab/04-isis.yml | 26 --- .../platform/sonic_clab/05-vrf.yml | 15 -- .../platform/sonic_clab/06-mpls-sr-l3vpn.yml | 71 ------- .../platform/sonic_clab/07-srv6.yml | 28 --- .../platform/sonic_clab/08-vlan.yml | 22 --- .../platform/sonic_clab/09-lag.yml | 16 -- .../platform/sonic_clab/11-vxlan-evpn.yml | 37 ---- .../platform/sonic_clab/12-evpn-irb-l3vni.yml | 50 ----- .../platform/sonic_clab/13-vrf-leak.yml | 24 --- .../platform/sonic_clab/14-bgp-session.yml | 17 -- .../platform/sonic_clab/15-bgp-policy.yml | 49 ----- .../platform/sonic_clab/16-ebgp-multihop.yml | 39 ---- .../platform/sonic_clab/17-ospf-areas.yml | 21 --- .../platform/sonic_clab/18-bgp-originate.yml | 18 -- .../platform/sonic_clab/19-bfd.yml | 12 -- .../platform/sonic_clab/20-ripv2.yml | 11 -- .../sonic_clab/21-evpn-multihoming.yml | 47 ----- .../platform/sonic_clab/22-ospfv3.yml | 25 --- .../platform/sonic_clab/23-isis-v6-bgp-v6.yml | 26 --- .../platform/sonic_clab/24-routing-v6.yml | 44 ----- .../platform/sonic_clab/25-vrf-v6-leak.yml | 36 ---- .../platform/sonic_clab/26-vrf-v6-ospfv3.yml | 26 --- .../platform/sonic_clab/27-tunnel-gre.yml | 23 --- .../sonic_clab/28-files-maxprefix.yml | 44 ----- .../platform/sonic_clab/29-bgp-domain.yml | 58 ------ .../integration/platform/sonic_clab/README.md | 90 --------- .../platform/sonic_clab/h1-fixaddr/linux.j2 | 12 -- 39 files changed, 126 insertions(+), 1152 deletions(-) rename netsim/ansible/tasks/deploy-config/{sonic_clab.yml => sonic-clab.yml} (100%) rename netsim/ansible/templates/initial/{sonic_clab.j2 => sonic-clab.j2} (93%) rename netsim/ansible/templates/lag/{sonic_clab.j2 => sonic-clab.j2} (100%) rename netsim/ansible/templates/lag/{sonic_clab.initial.j2 => sonic.initial.j2} (100%) rename netsim/ansible/templates/vlan/{sonic_clab.j2 => sonic-clab.j2} (100%) rename netsim/ansible/templates/vlan/{sonic_clab.initial.j2 => sonic.initial.j2} (100%) rename netsim/ansible/templates/vxlan/{sonic_clab.j2 => sonic-clab.j2} (100%) delete mode 100644 netsim/devices/sonic_clab.yml delete mode 100644 tests/integration/platform/sonic_clab/01-initial.yml delete mode 100644 tests/integration/platform/sonic_clab/02-ospf.yml delete mode 100644 tests/integration/platform/sonic_clab/03-bgp.yml delete mode 100644 tests/integration/platform/sonic_clab/04-isis.yml delete mode 100644 tests/integration/platform/sonic_clab/05-vrf.yml delete mode 100644 tests/integration/platform/sonic_clab/06-mpls-sr-l3vpn.yml delete mode 100644 tests/integration/platform/sonic_clab/07-srv6.yml delete mode 100644 tests/integration/platform/sonic_clab/08-vlan.yml delete mode 100644 tests/integration/platform/sonic_clab/09-lag.yml delete mode 100644 tests/integration/platform/sonic_clab/11-vxlan-evpn.yml delete mode 100644 tests/integration/platform/sonic_clab/12-evpn-irb-l3vni.yml delete mode 100644 tests/integration/platform/sonic_clab/13-vrf-leak.yml delete mode 100644 tests/integration/platform/sonic_clab/14-bgp-session.yml delete mode 100644 tests/integration/platform/sonic_clab/15-bgp-policy.yml delete mode 100644 tests/integration/platform/sonic_clab/16-ebgp-multihop.yml delete mode 100644 tests/integration/platform/sonic_clab/17-ospf-areas.yml delete mode 100644 tests/integration/platform/sonic_clab/18-bgp-originate.yml delete mode 100644 tests/integration/platform/sonic_clab/19-bfd.yml delete mode 100644 tests/integration/platform/sonic_clab/20-ripv2.yml delete mode 100644 tests/integration/platform/sonic_clab/21-evpn-multihoming.yml delete mode 100644 tests/integration/platform/sonic_clab/22-ospfv3.yml delete mode 100644 tests/integration/platform/sonic_clab/23-isis-v6-bgp-v6.yml delete mode 100644 tests/integration/platform/sonic_clab/24-routing-v6.yml delete mode 100644 tests/integration/platform/sonic_clab/25-vrf-v6-leak.yml delete mode 100644 tests/integration/platform/sonic_clab/26-vrf-v6-ospfv3.yml delete mode 100644 tests/integration/platform/sonic_clab/27-tunnel-gre.yml delete mode 100644 tests/integration/platform/sonic_clab/28-files-maxprefix.yml delete mode 100644 tests/integration/platform/sonic_clab/29-bgp-domain.yml delete mode 100644 tests/integration/platform/sonic_clab/README.md delete mode 100644 tests/integration/platform/sonic_clab/h1-fixaddr/linux.j2 diff --git a/netsim/ansible/tasks/deploy-config/sonic_clab.yml b/netsim/ansible/tasks/deploy-config/sonic-clab.yml similarity index 100% rename from netsim/ansible/tasks/deploy-config/sonic_clab.yml rename to netsim/ansible/tasks/deploy-config/sonic-clab.yml diff --git a/netsim/ansible/templates/initial/sonic_clab.j2 b/netsim/ansible/templates/initial/sonic-clab.j2 similarity index 93% rename from netsim/ansible/templates/initial/sonic_clab.j2 rename to netsim/ansible/templates/initial/sonic-clab.j2 index 20734040bf..761f1b2618 100644 --- a/netsim/ansible/templates/initial/sonic_clab.j2 +++ b/netsim/ansible/templates/initial/sonic-clab.j2 @@ -96,11 +96,17 @@ else fi {% endif %} {% endif %} +{# Set the MTU on the kernel netdev, not through "config interface mtu": on the interfaces + containerlab injects for netlab links that command writes CONFIG_DB and never reaches the + netdev, exactly like the routed-port IPs below -- measured, the interface stayed at 9500 + while CONFIG_DB claimed otherwise. The value is netlab MTU with no adjustment: + docker-sonic-vs programs it literally (1548 in -> 1548 on the netdev), so the +48 this + used to add left every interface 48 bytes above its peers and stalled OSPF in ExStart. #} {# L2 switchports (LAG members and VLAN access/trunk ports) reject a direct MTU set once they are members ("'interface_name' is in portchannel!" / "is in vlan"); the PortChannel/port MTU propagates, so skip them #} {% if l.mtu is defined and l.mtu >= 1500 and not (l.virtual_interface|default(False)) and l.lag._parentindex is not defined and l.vlan.access_id is not defined and l.vlan.trunk_id is not defined %} -config interface mtu {{ l.ifname }} {{ l.mtu + 48 }} +ip link set {{ l.ifname }} mtu {{ l.mtu }} {% endif %} ! {% endfor %} diff --git a/netsim/ansible/templates/lag/sonic_clab.j2 b/netsim/ansible/templates/lag/sonic-clab.j2 similarity index 100% rename from netsim/ansible/templates/lag/sonic_clab.j2 rename to netsim/ansible/templates/lag/sonic-clab.j2 diff --git a/netsim/ansible/templates/lag/sonic_clab.initial.j2 b/netsim/ansible/templates/lag/sonic.initial.j2 similarity index 100% rename from netsim/ansible/templates/lag/sonic_clab.initial.j2 rename to netsim/ansible/templates/lag/sonic.initial.j2 diff --git a/netsim/ansible/templates/vlan/sonic_clab.j2 b/netsim/ansible/templates/vlan/sonic-clab.j2 similarity index 100% rename from netsim/ansible/templates/vlan/sonic_clab.j2 rename to netsim/ansible/templates/vlan/sonic-clab.j2 diff --git a/netsim/ansible/templates/vlan/sonic_clab.initial.j2 b/netsim/ansible/templates/vlan/sonic.initial.j2 similarity index 100% rename from netsim/ansible/templates/vlan/sonic_clab.initial.j2 rename to netsim/ansible/templates/vlan/sonic.initial.j2 diff --git a/netsim/ansible/templates/vxlan/sonic_clab.j2 b/netsim/ansible/templates/vxlan/sonic-clab.j2 similarity index 100% rename from netsim/ansible/templates/vxlan/sonic_clab.j2 rename to netsim/ansible/templates/vxlan/sonic-clab.j2 diff --git a/netsim/devices/sonic.yml b/netsim/devices/sonic.yml index 33e7eed5fc..c402424307 100644 --- a/netsim/devices/sonic.yml +++ b/netsim/devices/sonic.yml @@ -1,15 +1,66 @@ --- -description: Sonic VM +# SONiC. The device runs FRR for its whole control plane, so it inherits the FRR device +# (parent: frr) and then turns OFF what SONiC cannot do -- rather than re-enumerating the +# FRR feature set, which is how the two drifted apart in the first place. +# +# Two deployments share this device: +# * libvirt -- the SONiC VM (support level: minimal, largely untested) +# * clab -- docker-sonic-vs, a monolithic container (vtysh runs directly, no 'docker exec bgp') +# Everything specific to the container lives in the clab: block below, and the templates the +# container needs of its own are named /sonic-clab.j2 (netlab looks up +# "/-.j2" before "/.j2", see defaults/paths.yml). +parent: frr +description: SONiC support: level: minimal + caveats: + - >- + SONiC runs FRR, so this device inherits the FRR device and disables what SONiC does not + support rather than re-declaring the FRR feature set. Anything not disabled below is served + by the FRR templates. + - >- + Genuinely unsupported, not just untested (each live-probed): STP -- config-plane only, there + is no stpd daemon so there is no real port-blocking behaviour; DHCP relay/client -- + docker-sonic-vs ships no dhcrelay/dhcp6relay binary at all; mlag.vtep (anycast VTEP) -- + the config plane renders and the underlay comes up, but the active-active EVPN overlay is + platform-blocked (no mclagd), so the datapath is 100% loss. + - >- + Two routing-policy operations FRR supports are disabled here because they are not exercised + by any integration test on this platform and were therefore never verified: + policy.set.community.extended and policy.match.nexthop. They are disabled explicitly rather + than left to inheritance -- under a parent device, silence is a claim. + - >- + The libvirt (VM) deployment is far less tested than the container. VLAN, LAG, VXLAN and EVPN + are enabled only for clab, because their configuration is built with config_db/redis-cli + sequences that have only ever been exercised on docker-sonic-vs. interface_name: Ethernet{ifindex * 4} ifindex_offset: 0 mgmt_if: eth0 loopback_interface_name: Loopback{ifindex} +lag_interface_name: "PortChannel{lag.ifindex}" +tunnel_interface_name: tun{ifindex} group_vars: - ansible_network_os: frr ansible_python_interpreter: auto_silent netlab_device_type: sonic +# +# Turned OFF for every SONiC deployment (see support.caveats for the evidence). +# +features: + stp: false # config-plane only, no stpd daemon -- no port blocking to verify + dhcp: false # docker-sonic-vs ships no dhcrelay/dhcp6relay binary + routing: + policy: + match: + nexthop: false # never exercised on this platform -> not claimed + set: + community: + extended: false # never exercised on this platform -> not claimed + # Built with config_db/redis-cli sequences that only the container has been tested with; + # re-enabled under clab: below. + vlan: false + lag: false + vxlan: false + evpn: false libvirt: image: netlab/sonic mtu: 1500 @@ -19,27 +70,72 @@ libvirt: ansible_user: admin ansible_ssh_pass: YourPaSsWoRd netlab_show_command: [ sudo, vtysh, -c, 'show $@' ] +clab: + # The generic templates/provider/clab/clab.j2 renders every clab. below through its + # key-value passthrough loop, so this device needs no provider template of its own. + image: docker-sonic-vs:latest + mtu: 1500 + node: + kind: sonic-vs + # Do NOT inherit FRR's bind-mounted /etc/frr/daemons and /etc/hosts config templates: + # docker-sonic-vs ships its own daemons file (initial/sonic-clab.j2 enables the daemons the + # configured modules need, in place) and mounting FRR's over it would undo that. + config_templates: + # SONiC runs FRR, so reuse FRR's kernel-module handling: an 'mpls'/'sr' lab auto-loads + # mpls_router + mpls_iptunnel, 'vxlan' loads the vxlan modules, 'vrf' the vrf module. + kmods: + initial: [ 'vrf?' ] + group_vars: + ansible_connection: docker + ansible_user: root + ansible_ssh_pass: YourPaSsWoRd # bootstrapped 'admin' login, interactive use only + netlab_ready: [ ansible ] # skip the SSH readiness wait (sshd isn't up yet) + netlab_show_command: [ vtysh, -c, 'show $@' ] + # Do NOT inherit FRR's deployment mechanism. FRR's clab group_vars select netlab's native + # "sh" config mode with a '#!/usr/bin/vtysh -f' shebang, which bypasses + # tasks/deploy-config/sonic-clab.yml entirely and feeds SONiC's bash 'config' script to + # vtysh. Measured: the node then fails initial configuration with + # "RuntimeError: Unable to connect to redis - Connection refused". + # SONiC deploys over the Ansible docker connection instead. + netlab_config_mode: + netlab_default_shebang: + netlab_mgmt_vrf: False + # docker-sonic-vs ships most FRR daemons disabled in /etc/frr/daemons; initial/sonic-clab.j2 + # enables the ones the configured modules need. watchfrr, zebra and staticd are always + # started by FRR itself and are deliberately absent from this map. + netlab_frr_daemons: + bgp: [ bgpd ] + ospf: [ ospfd, ospf6d ] + isis: [ isisd ] + bfd: [ bfdd ] + ripv2: [ ripd ] + vrf: [ bgpd ] + gateway: [ vrrpd ] + mpls: [ ldpd ] + # + # Verified on docker-sonic-vs and therefore re-enabled for the container only. + # + features: + vlan: + model: l3-switch + svi_interface_name: "Vlan{vlan}" + subif_name: "{ifname}.{subif_index}" + native_routed: true + lag: + passive: true + mlag: + peer: + ip: 169.254.127.0/31 # static /31 peer-link (EOS pattern); SONiC has no MLAG keepalive + # protocol to negotiate one over -- config-plane only (no mclagd) + vxlan: true + evpn: + transport: [ vxlan, mpls ] + irb: true + multihoming: + lag: true + initial: + collect: true # ansible_connection: docker unblocks this (no sshd needed) + config_mode: # clear FRR's [ sh ]: SONiC uses the Ansible deploy task external: image: none -features: - initial: - ipv4: - unnumbered: true - ipv6: - lla: true - reload: false - collect: true - bgp: - activate_af: true - advertise: true - ipv6_lla: true - local_as: true - local_as_ibgp: true - vrf_local_as: true - community: - standard: [ standard, large ] - large: [ large ] - extended: [ extended ] - 2octet: [ standard ] - graphite.icon: router diff --git a/netsim/devices/sonic_clab.yml b/netsim/devices/sonic_clab.yml deleted file mode 100644 index 86822be274..0000000000 --- a/netsim/devices/sonic_clab.yml +++ /dev/null @@ -1,174 +0,0 @@ -# SONiC on containerlab (docker-sonic-vs). -# -# netlab ships a `sonic` device that is LIBVIRT-ONLY (a full multi-container SONiC VM: FRR runs -# in a nested "bgp" sub-container, reached via `docker exec bgp vtysh`). This is a SEPARATE -# device for the community `docker-sonic-vs` image on containerlab, which is a MONOLITHIC single -# container: FRR's vtysh runs directly in the container's own namespace (no nested "bgp" -# container), and there is no sshd. We inherit interface naming / loopback naming / the base -# BGP feature set from `sonic` via `parent`, and add a `clab:` block that uses Ansible's built-in -# `docker` connection plugin (the same pattern already used in-tree by the `frr` and -# `cumulus_nvue` clab devices) instead of SSH — this is the netlab-idiomatic way to configure a -# clab container with no running sshd. -# -# docker-sonic-vs also ships most FRR daemons disabled by default (bgpd/ospfd/ospf6d/isisd/... -# =no in /etc/frr/daemons) to save resources; templates/initial/sonic_clab.j2 flips on the -# daemons needed by the configured modules and restarts FRR once, idempotently. -description: SONiC (containerlab, docker-sonic-vs) -parent: sonic -support: - level: best-effort - caveats: - - >- - Config deployment uses Ansible's 'docker' connection plugin (docker exec), not network_cli: - docker-sonic-vs has no cliconf-compatible CLI (the only Ansible SONiC cliconf, - dellemc.enterprise_sonic, targets Dell's licensed klish/Management-Framework CLI, which this - image doesn't ship -- verified). See templates/initial/sonic_clab.j2 for the full writeup. - - >- - docker-sonic-vs ships sshd + host keys but starts neither sshd nor a login user; - templates/initial/sonic_clab.j2 bootstraps both (admin / ansible_ssh_pass below) for - interactive access ('netlab connect', ad-hoc troubleshooting) -- SSH is NOT used for config - deployment, only ansible_connection: docker is. - - docker-sonic-vs is a monolithic single container (FRR vtysh runs directly); this differs from - the multi-container architecture of the full SONiC VM used by the parent 'sonic' device, so - the initial template is not shared with the parent device and is provided here instead. BGP - (and every other routing module) is delegated to the in-tree FRR templates via - ansible_network_os=frr -- unlike the Azure/libvirt sonic image, docker-sonic-vs does not - pre-seed a BGP AS, so no 'no router bgp' reset wrapper is needed. - - >- - Genuinely unsupported on this image, not just untested (each probed live): dhcp relay/client - -- docker-sonic-vs ships no dhcrelay/dhcp6relay binary at all; stp -- config-plane only, no - stpd daemon so there is no real port-blocking behavior to verify; mlag.vtep (anycast VTEP) -- - config-plane renders correctly (identical vtep-ip-global, underlay OSPF Full, BGP+EVPN - Established) but the active-active EVPN overlay is platform-blocked (no mclagd, so BGP never - resolves a usable self-next-hop for the anycast VTEP -- datapath 100% loss). -lag_interface_name: "PortChannel{lag.ifindex}" -tunnel_interface_name: "tun{ifindex}" # tunnel.gre plugin naming (kernel netdev, ip_gre) -group_vars: - netlab_device_type: sonic_clab - netlab_frr_daemons: # module -> FRR daemons to enable in /etc/frr/daemons (default: no) - bgp: [ bgpd ] - ospf: [ ospfd, ospf6d ] - isis: [ isisd ] - bfd: [ bfdd ] - ripv2: [ ripd ] - vrf: [ bgpd ] - gateway: [ vrrpd ] - mpls: [ ldpd ] -clab: - # No netsim/templates/provider/clab/sonic_clab.j2 override exists (dev-guide checklist item): - # the generic templates/provider/clab/clab.j2 already renders every clab. below (image, - # mtu, node.kind, ...) via its key-value passthrough loop (defaults.providers.clab.attributes. - # node._keys) -- confirmed empirically, clab.yml rendered correctly with no provider template - # of our own. A provider override is only needed for device-specific containerlab topology - # shapes (bind mounts, exec hooks, etc.); this device needs none of that. - image: docker-sonic-vs:latest - mtu: 1500 - node: - kind: sonic-vs - # SONiC runs FRR, so reuse FRR's kernel-module handling. Declaring 'kmods' opts this device - # into netlab's clab kernel-module loader (netsim/providers/clab/labops.py), which merges these - # with the system-wide providers.clab.kmods -- so an 'mpls'/'sr' lab auto-loads mpls_router + - # mpls_iptunnel (real kernel label FIB), 'vxlan' loads the vxlan modules, and 'vrf' loads the - # vrf module, with no manual 'modprobe'. Mirrors netsim/devices/frr.yml. - kmods: - initial: [ 'vrf?' ] - group_vars: - ansible_connection: docker - ansible_user: root - ansible_ssh_pass: YourPaSsWoRd # bootstrapped 'admin' login, interactive use only - netlab_ready: [ ansible ] # skip the SSH readiness wait (sshd isn't up yet) - netlab_show_command: [ vtysh, -c, 'show $@' ] -# The feature flags below are established by live testing across the module templates in -# tests/integration/platform/sonic_clab/; see those topologies for what each one verifies. -features: - initial: - collect: true # ansible_connection: docker unblocks this (no sshd needed) -- verified live - bgp: - import: [ connected, static, ospf ] # redistribute-into-BGP (frr.j2 renders it) - advertise: true # bgp.originate: static discard route backs the prefix - password: true # bgp.session plugin (MD5) - timers: true - gtsm: true - passive: true - bfd: true - multihop: # ebgp.multihop plugin (FRR-delegated) - vrf: true - ospf: - unnumbered: true - default: true - areas: true - timers: true - priority: true - password: true - bfd: true - isis: - circuit_type: true - unnumbered: { ipv4: true, ipv6: true, network: true } - import: [ static ] - mpls: - ldp: true # LDP control-plane; no kernel MPLS label FIB in a clab container - vpn: true # unlocks BGP VPNv4/VPNv6 L3VPN import - sr: - af: [ ipv4, ipv6 ] # SR-MPLS via IS-IS; SIDs flooded/visible in the IS-IS LSDB - protocol: [ isis ] - srv6: - isis: true # kernel seg6 (net.ipv6.seg6_enabled), not the MPLS label FIB - vrf: - keep_module: true - ospfv2: true - ospfv3: true - bgp: true - isis: true - bfd: true - lag: - passive: true - mlag: - peer: - ip: 169.254.127.0/31 # static /31 peer-link (EOS pattern); SONiC has no MLAG keepalive - # protocol to negotiate one over -- config-plane only (no mclagd) - ripv2: - ipv4: true - gateway: - protocol: [ vrrp ] - routing: - static: - vrf: true - discard: true # Null0 blackhole (backs bgp.originate) - prefix: true - aspath: true - community: - standard: true - large: true - policy: - match: - prefix: true - aspath: true - community: - standard: true - set: - locpref: true - med: true - prepend: true - weight: true - community: - standard: true - vxlan: true # L2VNI via FRR/kernel path (orchagent can't program the VS dataplane) - evpn: - transport: [ vxlan, mpls ] - irb: true # symmetric IRB / L3VNI -- kernel VRF + vxlan L3VNI transit device - multihoming: # evpn.multihoming plugin: ESI-LAG on PortChannel (teamd) via zebra - lag: true - interface: false - esi_auto: true - modes: [ 'all-active' ] - vlan: - model: l3-switch # access switchports + SVIs - svi_interface_name: "Vlan{vlan}" # config_db object name -- CAPITAL V; the config CLI - # rejects netlab's default lowercase vlan - tunnel: - gre: true # tunnel.gre plugin: kernel ip_gre/ip6_gre, FRR-delegated bash - # template (extra/tunnel/gre/frr.j2) -- reused as-is, no override -# Deliberately NOT declared (genuinely unsupported on this image, not just untested -- see the -# 'support.caveats' entry above for the live-probed reason): dhcp relay/client, stp real -# port-blocking, mlag.vtep active-active datapath. -graphite.icon: switch diff --git a/tests/integration/platform/sonic_clab/01-initial.yml b/tests/integration/platform/sonic_clab/01-initial.yml deleted file mode 100644 index c33197cf88..0000000000 --- a/tests/integration/platform/sonic_clab/01-initial.yml +++ /dev/null @@ -1,9 +0,0 @@ -# 1-node SONiC (clab) bring-up smoke test. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 -nodes: - s1: diff --git a/tests/integration/platform/sonic_clab/02-ospf.yml b/tests/integration/platform/sonic_clab/02-ospf.yml deleted file mode 100644 index 76429b4f53..0000000000 --- a/tests/integration/platform/sonic_clab/02-ospf.yml +++ /dev/null @@ -1,28 +0,0 @@ -# 2-node SONiC (clab) OSPF smoke test. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ ospf ] -nodes: [ s1, s2 ] -links: -- s1: - s2: - -# native docker-exec validation (reuses FRR validation plugins via netsim/validate/*/sonic_clab.py) -defaults.const.validate.ospfv2_adj_lan: 60 -defaults.const.validate.ospfv2_spf: 10 -validate: - adj_s1: - description: s1 sees s2 as an OSPF neighbor (docker-exec validation) - wait_msg: Waiting for OSPF adjacency - wait: ospfv2_adj_lan - nodes: [ s1 ] - plugin: ospf_neighbor(nodes.s2.ospf.router_id) - adj_s2: - description: s2 sees s1 as an OSPF neighbor - wait: ospfv2_spf - nodes: [ s2 ] - plugin: ospf_neighbor(nodes.s1.ospf.router_id) diff --git a/tests/integration/platform/sonic_clab/03-bgp.yml b/tests/integration/platform/sonic_clab/03-bgp.yml deleted file mode 100644 index 5b43a4e00f..0000000000 --- a/tests/integration/platform/sonic_clab/03-bgp.yml +++ /dev/null @@ -1,30 +0,0 @@ -# 2-node SONiC (clab) eBGP smoke test. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ bgp ] -nodes: - s1: - bgp.as: 65001 - s2: - bgp.as: 65002 -links: -- s1: - s2: - -# native docker-exec validation (reuses FRR bgp validation plugin via sonic_clab alias) -defaults.const.validate.ebgp_session: 60 -validate: - session_s1: - description: s1 sees s2 as an established eBGP neighbor (docker-exec validation) - wait_msg: Waiting for BGP session to establish - wait: ebgp_session - nodes: [ s1 ] - plugin: bgp_neighbor(node.bgp.neighbors, "s2") - session_s2: - description: s2 sees s1 as an established eBGP neighbor - nodes: [ s2 ] - plugin: bgp_neighbor(node.bgp.neighbors, "s1") diff --git a/tests/integration/platform/sonic_clab/04-isis.yml b/tests/integration/platform/sonic_clab/04-isis.yml deleted file mode 100644 index 3e380a4962..0000000000 --- a/tests/integration/platform/sonic_clab/04-isis.yml +++ /dev/null @@ -1,26 +0,0 @@ -# 2-node SONiC IS-IS spike. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ isis ] -nodes: [ s1, s2 ] -links: -- s1: - s2: - -# native docker-exec validation (reuses FRR isis validation plugin via sonic_clab alias) -defaults.const.validate.isis_adj: 60 -validate: - adj_s1: - description: s1 sees s2 as an IS-IS neighbor (docker-exec validation) - wait_msg: Waiting for IS-IS adjacency - wait: isis_adj - nodes: [ s1 ] - plugin: isis_neighbor("s2") - adj_s2: - description: s2 sees s1 as an IS-IS neighbor - nodes: [ s2 ] - plugin: isis_neighbor("s1") diff --git a/tests/integration/platform/sonic_clab/05-vrf.yml b/tests/integration/platform/sonic_clab/05-vrf.yml deleted file mode 100644 index e68c63d2fb..0000000000 --- a/tests/integration/platform/sonic_clab/05-vrf.yml +++ /dev/null @@ -1,15 +0,0 @@ -# 2-node SONiC VRF spike — link in VRF red with per-VRF OSPF. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ vrf, ospf ] -vrfs: - red: -nodes: [ s1, s2 ] -links: -- s1: - s2: - vrf: red diff --git a/tests/integration/platform/sonic_clab/06-mpls-sr-l3vpn.yml b/tests/integration/platform/sonic_clab/06-mpls-sr-l3vpn.yml deleted file mode 100644 index 61183acb50..0000000000 --- a/tests/integration/platform/sonic_clab/06-mpls-sr-l3vpn.yml +++ /dev/null @@ -1,71 +0,0 @@ -# SONiC MPLS/SR/L3VPN spike -# IS-IS+LDP+SR-MPLS core, iBGP VPNv4 between the two PEs (loopback sessions), VRF red -# with stub interfaces on both ends, VRF blue on s2 importing red's route-target -# (inter-VRF route leaking). -# -# UNLIKE ArcOS's native container (no kernel MPLS at all), docker-sonic-vs shares the -# HOST kernel netns machinery for net.mpls -- the sysctls just don't exist until the -# mpls_router/mpls_iptunnel modules are loaded ANYWHERE on the host: -# sudo modprobe mpls_router mpls_iptunnel -# Once loaded, /proc/sys/net/mpls/* appears in every container's netns immediately -# (even already-running ones) and mpls/frr.j2's `sysctl -w net.mpls.platform_labels` -# succeeds -- so this gets a REAL kernel label FIB (`ip -M route`), not just a -# control-plane proof. This is a one-time HOST prerequisite, not something the -# topology YAML can express -- document it, don't assume it's already loaded. -# -# sr.srgb.start is a MODEL GAP for IS-IS SR-MPLS on FRR (checked against the exact -# upstream template that renders this, not assumed): netsim/ansible/templates/sr/frr.j2 -# only emits `segment-routing global-block ` inside the `if 'ospfv2' in -# sr.protocol` branch -- the `if 'isis' in sr.protocol` branch never references -# sr.srgb at all. A custom SRGB is silently a no-op here; FRR isisd always keeps its -# compiled-in default (base 16000, range 8000) regardless of what the topology sets. -# Confirmed live: `show isis database detail` -> "Global Block Base: 16000 Range: 8000" -# even with sr.srgb.start set on the node. Netlab/FRR gap to report upstream, not -# something to work around here -- so this topology does NOT set sr.srgb. -# -# (A first pass at this topology hit a VPNv4-export failure -- BGP requested a label -# from zebra's dynamic pool but never got one, `show bgp labelpool summary` -> 0 -# LabelChunks. That LOOKED like an SRGB/zebra-label-pool collision, but wasn't: -# `netlab initial` deploys s1 and s2 SEQUENTIALLY, not in parallel, and that pass's -# ansible timeout cut off before s2 ever received its bgp/sr/mpls/vrf config at all. -# A longer timeout for the full two-node sequential deploy resolved it with ZERO -# topology changes and the default SRGB -- worth remembering when a multi-module SONiC -# deploy looks like it "half worked": check whether every node actually finished.) -# -# This IS a REAL end-to-end MPLS L3VPN datapath: s2 (vrf red) pings s1's vrf-red stub -# (172.16.0.1) across two real kernel-programmed MPLS labels (LDP transport + BGP VPN -# label), 0% loss. VRF blue -> 172.16.0.1 correctly FAILS (100% loss) -- blue imports -# red's RT so it has a forward route, but red does NOT import blue's RT, so s1 has no -# return route to blue's prefix; one-way leak is by design (matches the existing -# vrf_leak module), not a bug. -# sudo modprobe mpls_router mpls_iptunnel # HOST prerequisite, once -# python validate/sonic.py --module mpls sr mpls_vpn -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ isis, bgp, mpls, sr, vrf ] -bgp.as: 65000 -mpls.ldp: true -mpls.vpn: true - -vrfs: - red: - blue: - import: [ red, blue ] - -nodes: [ s1, s2 ] - -links: -- s1-s2 -- s1: - vrf: red - type: stub -- s2: - vrf: red - type: stub -- s2: - vrf: blue - type: stub diff --git a/tests/integration/platform/sonic_clab/07-srv6.yml b/tests/integration/platform/sonic_clab/07-srv6.yml deleted file mode 100644 index 24c7f3558d..0000000000 --- a/tests/integration/platform/sonic_clab/07-srv6.yml +++ /dev/null @@ -1,28 +0,0 @@ -# SONiC SRv6 over IS-IS -# Per-node locator from srv6.locator_pool; IS-IS advertises the SRv6 locator + auto -# End/End.X SIDs. CONFIRMED: FRR's srv6/frr.j2 uses kernel seg6 (net.ipv6.seg6_enabled), -# NOT the MPLS label FIB, so — unlike mpls/sr — this needs NO host modprobe -# prerequisite and gets REAL kernel seg6local routes (`ip -6 route` shows -# `encap seg6local action End`/`End.X`) straight out of the box. Cross-node proof: s1's -# IS-IS LSDB carries s2's locator (and vice versa), not just a local SID. -# Same open follow-up as OcNOS's srv6 result: SRv6-OAM ping to the End SID (uN) does -# NOT get answered even though the kernel seg6local route is installed — proven -# control+kernel-plane, datapath ping unresolved (not chased further here -# follow-up, matches the OcNOS precedent exactly). -# python validate/sonic.py --module srv6 -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ isis, srv6 ] -srv6.bgp: false -srv6.igp: [ isis ] -addressing: - loopback: - ipv6: 2001:db8:a::/48 - p2p: - ipv6: 2001:db8:1::/48 -nodes: [ s1, s2 ] -links: [ s1-s2 ] diff --git a/tests/integration/platform/sonic_clab/08-vlan.yml b/tests/integration/platform/sonic_clab/08-vlan.yml deleted file mode 100644 index 34dcddf200..0000000000 --- a/tests/integration/platform/sonic_clab/08-vlan.yml +++ /dev/null @@ -1,22 +0,0 @@ -# 2-node SONiC VLAN spike (config_db Vlan + kernel bridge datapath). -# Access VLAN "red" (IRB -> SVI with an IP) between s1 and s2, same shape as the -# ocnos/arcos vlan tests. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 - -module: [ vlan ] - -vlans: - red: - mode: irb - -nodes: [ s1, s2 ] - -links: -- s1: - s2: - vlan.access: red diff --git a/tests/integration/platform/sonic_clab/09-lag.yml b/tests/integration/platform/sonic_clab/09-lag.yml deleted file mode 100644 index 058331704e..0000000000 --- a/tests/integration/platform/sonic_clab/09-lag.yml +++ /dev/null @@ -1,16 +0,0 @@ -# SONiC LAG spike: two parallel links -> PortChannel1 (LACP/teamd), -# OSPF over the aggregate proves the datapath. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 - -module: [ lag, ospf ] - -nodes: [ s1, s2 ] - -links: -- lag: - members: [ s1-s2, s1-s2 ] diff --git a/tests/integration/platform/sonic_clab/11-vxlan-evpn.yml b/tests/integration/platform/sonic_clab/11-vxlan-evpn.yml deleted file mode 100644 index 8896657e94..0000000000 --- a/tests/integration/platform/sonic_clab/11-vxlan-evpn.yml +++ /dev/null @@ -1,37 +0,0 @@ -# SONiC EVPN-VXLAN: OSPF underlay + iBGP EVPN over loopbacks, VLAN red -# (pure L2, vni 10043) stretched between the two VTEPs. h1/h2 are linux hosts behind -# untagged access ports; h1 ping h2 can ONLY succeed through the VXLAN tunnel across -# the routed core — the definitive datapath proof. -# docker-sonic-vs's orchagent can't program the dataplane, so the L2VNI is built on -# the FRR/kernel path (traditional bridge + kernel vxlan netdev); zebra drives EVPN. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 - -module: [ vlan, ospf, bgp, vxlan, evpn ] -bgp.as: 65000 - -vlans: - red: - mode: bridge - vni: 10043 - -nodes: - s1: - s2: - h1: - device: linux - h2: - device: linux - -links: -- s1-s2 -- s1: - vlan.access: red - h1: -- s2: - vlan.access: red - h2: diff --git a/tests/integration/platform/sonic_clab/12-evpn-irb-l3vni.yml b/tests/integration/platform/sonic_clab/12-evpn-irb-l3vni.yml deleted file mode 100644 index c86fd71e39..0000000000 --- a/tests/integration/platform/sonic_clab/12-evpn-irb-l3vni.yml +++ /dev/null @@ -1,50 +0,0 @@ -# SONiC EVPN symmetric-IRB (L3VNI inter-subnet) spike. -# Two subnets (red, blue) in tenant VRF, stretched via VXLAN with -# an L3VNI. h1 (behind s1, subnet red) and h2 (behind s2, subnet blue) are in DIFFERENT -# subnets -- inter-subnet ping can only work through the L3VNI routing across the -# fabric, which is the symmetric-IRB datapath proof. -# -# docker-sonic-vs's orchagent bypassed as usual (kernel/FRR path, see -# templates/vxlan/sonic.j2 + templates/vlan/sonic.j2 for the create-before-address -# vrf-enslavement ordering this needed). -# python validate/sonic.py --module evpn_irb -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 - -module: [ vlan, vrf, ospf, bgp, vxlan, evpn ] -bgp.as: 65000 - -vrfs: - tenant: - evpn.transit_vni: 10099 - -vlans: - red: - mode: irb - vrf: tenant - vni: 10010 - blue: - mode: irb - vrf: tenant - vni: 10020 - -nodes: - s1: - s2: - h1: - device: linux - h2: - device: linux - -links: -- s1-s2 -- s1: - vlan.access: red - h1: -- s2: - vlan.access: blue - h2: diff --git a/tests/integration/platform/sonic_clab/13-vrf-leak.yml b/tests/integration/platform/sonic_clab/13-vrf-leak.yml deleted file mode 100644 index 4b38a00d13..0000000000 --- a/tests/integration/platform/sonic_clab/13-vrf-leak.yml +++ /dev/null @@ -1,24 +0,0 @@ -# SONiC VRF route-leaking: on s1, VRF blue imports VRF red -# RTs -> reds stub prefix appears in blues RIB (FRR rt vpn import, intra-node -# control-plane leak, independent of the VS dataplane). -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ vrf, bgp ] -bgp.as: 65000 -vrfs: - red: - blue: - import: [ red, blue ] -nodes: [ s1, s2 ] -links: -- s1-s2 -- s1: - vrf: red - type: stub -- s1: - vrf: blue - type: stub diff --git a/tests/integration/platform/sonic_clab/14-bgp-session.yml b/tests/integration/platform/sonic_clab/14-bgp-session.yml deleted file mode 100644 index 188270754e..0000000000 --- a/tests/integration/platform/sonic_clab/14-bgp-session.yml +++ /dev/null @@ -1,17 +0,0 @@ -# SONiC bgp.session plugin: eBGP with MD5 password. FRR-delegated. -provider: clab -plugin: [ multilab, bgp.session ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ bgp ] -nodes: - s1: - bgp.as: 65001 - s2: - bgp.as: 65002 -links: -- s1: - s2: - bgp.password: Secret123 diff --git a/tests/integration/platform/sonic_clab/15-bgp-policy.yml b/tests/integration/platform/sonic_clab/15-bgp-policy.yml deleted file mode 100644 index 9190549dae..0000000000 --- a/tests/integration/platform/sonic_clab/15-bgp-policy.yml +++ /dev/null @@ -1,49 +0,0 @@ -# SONiC bgp.policy plugin. Applies FRR route-maps to an eBGP session: -# * a NAMED inbound route-map (routing.policy) that matches a prefix-list and -# sets local-preference + a standard community (match + set) -# * plugin COMPOUND attributes (bgp.weight direct, bgp.med -> auto out route-map) -# s2 originates 10.43.99.0/24 (connected stub); s1 receives it and the inbound -# policy rewrites LocPref=250 + community 65001:100, and weight=50 (best-path effect). -provider: clab -plugin: [ multilab, bgp.policy ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ bgp, routing ] - -groups: - routers: - members: [ s1, s2 ] - routing: - prefix: - cust-net: - - ipv4: 10.43.0.0/16 - min: 24 - max: 24 - policy: - set-lp: # inbound: match prefix-list -> set locpref + community - - match: - prefix: cust-net - set: - locpref: 250 - community: - standard: [ "65001:100" ] - - sequence: 100 # permit the rest (FRR route-maps deny by default) - -nodes: - s1: - bgp.as: 65001 - s2: - bgp.as: 65002 - bgp.import: [ connected ] - -links: -- s1: - bgp.policy.in: set-lp # named inbound route-map - bgp.weight: 50 # direct neighbor attribute - bgp.med: 111 # compound -> auto out route-map - s2: -- s2: # s2's connected stub, redistributed into BGP - type: stub - prefix: 10.43.99.0/24 diff --git a/tests/integration/platform/sonic_clab/16-ebgp-multihop.yml b/tests/integration/platform/sonic_clab/16-ebgp-multihop.yml deleted file mode 100644 index ba1a1aa874..0000000000 --- a/tests/integration/platform/sonic_clab/16-ebgp-multihop.yml +++ /dev/null @@ -1,39 +0,0 @@ -# SONiC ebgp.multihop plugin. eBGP session between the two LOOPBACKS -# (multihop, up to 255 hops) across an AS boundary (s1 AS65001 <-> s2 AS65002), -# with s1 presenting a local-as of 65101. netlab does NOT run an IGP across an -# eBGP AS boundary, so loopback reachability is modeled with the routing.static -# module (static route to the remote loopback via the connected transit subnet) -- -# the realistic multihop underlay. The connected link carries NO direct BGP session -# (bgp: False) so the ONLY session is the multihop loopback-to-loopback one. -# FRR-delegated (templates/ebgp.multihop/sonic.j2 = include frr.j2). -provider: clab -plugin: [ multilab, ebgp.multihop ] -defaults: - device: sonic_clab - multilab: - id: 52 - -module: [ bgp, ospf, routing ] -bgp.advertise_loopback: True - -bgp.multihop.sessions: -- s1: - local_as: 65101 - s2: - -nodes: - s1: - bgp.as: 65001 - routing.static: - - ipv4: 10.0.0.2/32 - nexthop.ipv4: 10.1.0.2 - s2: - bgp.as: 65002 - routing.static: - - ipv4: 10.0.0.1/32 - nexthop.ipv4: 10.1.0.1 - -links: -- s1: - s2: - bgp: False diff --git a/tests/integration/platform/sonic_clab/17-ospf-areas.yml b/tests/integration/platform/sonic_clab/17-ospf-areas.yml deleted file mode 100644 index abe4687436..0000000000 --- a/tests/integration/platform/sonic_clab/17-ospf-areas.yml +++ /dev/null @@ -1,21 +0,0 @@ -# SONiC ospf.areas plugin: s1 is an ABR (loopback area 0 + link area 1 -# stub); s2 is a pure stub-area router. FRR-delegated. -provider: clab -plugin: [ multilab, ospf.areas ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ ospf ] -nodes: - s1: - ospf.areas: [ { area: 0.0.0.1, kind: stub } ] - s2: - ospf.areas: [ { area: 0.0.0.1, kind: stub } ] -links: -- s1: - ospf.area: 0.0.0.0 - type: stub -- s1: - s2: - ospf.area: 0.0.0.1 diff --git a/tests/integration/platform/sonic_clab/18-bgp-originate.yml b/tests/integration/platform/sonic_clab/18-bgp-originate.yml deleted file mode 100644 index fdd0983d76..0000000000 --- a/tests/integration/platform/sonic_clab/18-bgp-originate.yml +++ /dev/null @@ -1,18 +0,0 @@ -# SONiC bgp.originate. A node injects a prefix into BGP for which it -# has NO interface/route (native bgp.originate, now part of the BGP module). s1 -# originates 10.201.9.0/24; the eBGP peer s2 must receive & install it. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ bgp ] -nodes: - s1: - bgp.as: 65001 - bgp.originate: [ 10.201.9.0/24 ] - s2: - bgp.as: 65002 -links: -- s1-s2 diff --git a/tests/integration/platform/sonic_clab/19-bfd.yml b/tests/integration/platform/sonic_clab/19-bfd.yml deleted file mode 100644 index a600e30103..0000000000 --- a/tests/integration/platform/sonic_clab/19-bfd.yml +++ /dev/null @@ -1,12 +0,0 @@ -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ ospf, bfd ] -nodes: [ s1, s2 ] -links: -- s1: - s2: - ospf.bfd: True diff --git a/tests/integration/platform/sonic_clab/20-ripv2.yml b/tests/integration/platform/sonic_clab/20-ripv2.yml deleted file mode 100644 index d922689377..0000000000 --- a/tests/integration/platform/sonic_clab/20-ripv2.yml +++ /dev/null @@ -1,11 +0,0 @@ -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ ripv2 ] -nodes: [ s1, s2 ] -links: -- s1: - s2: diff --git a/tests/integration/platform/sonic_clab/21-evpn-multihoming.yml b/tests/integration/platform/sonic_clab/21-evpn-multihoming.yml deleted file mode 100644 index 6fcd7dd9c2..0000000000 --- a/tests/integration/platform/sonic_clab/21-evpn-multihoming.yml +++ /dev/null @@ -1,47 +0,0 @@ -# SONiC EVPN Multihoming: ESI-LAG dual-homes h1 across s1+s2. No MLAG peer-link -# needed — ES coordination is via BGP EVPN Type-1 (Ethernet A-D) / Type-4 (ES) routes, not an -# MLAG protocol, so this works even though docker-sonic-vs has no mclagd. -# PortChannel is teamd, not kernel bonding -- doesn't matter: `show interface PortChannel1` -# reports "Interface Type bond" regardless, so zebra's EVPN-MH ES logic treats it exactly like a -# real bond. h2 is single-homed off s2, so the datapath check (h1 -> h2) only succeeds if traffic -# egressing EITHER leg of h1's dual-homed LAG reaches the shared L2VNI -- the actual point of -# multihoming. -provider: clab -plugin: [ multilab, evpn.multihoming ] -defaults: - device: sonic_clab - multilab: - id: 52 - -module: [ lag, vlan, ospf, bgp, vxlan, evpn ] -bgp.as: 65000 - -vlans: - red: - mode: bridge - vni: 10043 - -nodes: - s1: - s2: - h1: - device: linux - module: [ lag, vlan ] # host must opt into lag explicitly (not auto-enabled for linux) - config: [ h1-fixaddr ] # work around a netlab host-addressing gap, see h1-fixaddr/linux.j2 - h2: - device: linux - -links: -- s1-s2 -- lag: - members: - - s1: - evpn.es: es1 - h1: - - s2: - evpn.es: es1 - h1: - vlan.access: red -- s2: - vlan.access: red - h2: diff --git a/tests/integration/platform/sonic_clab/22-ospfv3.yml b/tests/integration/platform/sonic_clab/22-ospfv3.yml deleted file mode 100644 index 6158e9d1b2..0000000000 --- a/tests/integration/platform/sonic_clab/22-ospfv3.yml +++ /dev/null @@ -1,25 +0,0 @@ -# SONiC OSPFv3 (dual-stack): FRR's ospf6d is already enabled by the deploy for every -# topology (ospfd/ospf6d/isisd all get flipped on in /etc/frr/daemons); the core `ospf` module -# template renders OSPFv3 automatically whenever ospf.af.ipv6 is set, which netlab derives from -# IPv6 addressing being present. Same 2-node p2p shape as the plain OSPF test, just dual-stack. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 - -addressing: - loopback: - ipv6: 2001:db8:0::/48 - p2p: - ipv6: 2001:db8:100::/48 - prefix6: 64 - -module: [ ospf ] - -nodes: [ s1, s2 ] - -links: -- s1: - s2: diff --git a/tests/integration/platform/sonic_clab/23-isis-v6-bgp-v6.yml b/tests/integration/platform/sonic_clab/23-isis-v6-bgp-v6.yml deleted file mode 100644 index 1794adb670..0000000000 --- a/tests/integration/platform/sonic_clab/23-isis-v6-bgp-v6.yml +++ /dev/null @@ -1,26 +0,0 @@ -# SONiC IS-IS(v6) + iBGP(v6): dual-stack IGP + BGP AF, same 2-node p2p shape as the -# IPv4 IS-IS/BGP tests, just IPv6. isisd is already enabled by the deploy; FRR's isisd -# renders IPv6 reachability (multi-topology) as soon as ipv6 addressing is present. iBGP peers -# over the v6 loopbacks, activated in the ipv6 unicast AF alongside ipv4. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 - -addressing: - loopback: - ipv6: 2001:db8:0::/48 - p2p: - ipv6: 2001:db8:100::/48 - prefix6: 64 - -module: [ isis, bgp ] -bgp.as: 65000 - -nodes: [ s1, s2 ] - -links: -- s1: - s2: diff --git a/tests/integration/platform/sonic_clab/24-routing-v6.yml b/tests/integration/platform/sonic_clab/24-routing-v6.yml deleted file mode 100644 index b67e5b0be3..0000000000 --- a/tests/integration/platform/sonic_clab/24-routing-v6.yml +++ /dev/null @@ -1,44 +0,0 @@ -# SONiC IPv6 static routing + prefix-lists: dual-stack version of the static-route -# slice of the routing test. Verifies `ipv6 route` + `ipv6 prefix-list` actually -# render and install, not just that the Jinja has an {% if %} branch for them. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 - -addressing: - loopback: - ipv6: 2001:db8:0::/48 - p2p: - ipv6: 2001:db8:100::/48 - prefix6: 64 - -module: [ routing ] - -groups: - routers: - members: [ s1, s2 ] - routing: - prefix: - stub-net6: - - ipv6: 2001:db8:143::/48 - min: 64 - max: 64 - -nodes: - s1: - routing.static: - - ipv6: 2001:db8:143:42::/64 - nexthop: - node: s2 - s2: - routing.static: - - ipv6: 2001:db8:143:41::/64 - nexthop: - node: s1 - -links: -- s1: - s2: diff --git a/tests/integration/platform/sonic_clab/25-vrf-v6-leak.yml b/tests/integration/platform/sonic_clab/25-vrf-v6-leak.yml deleted file mode 100644 index 8991f29e50..0000000000 --- a/tests/integration/platform/sonic_clab/25-vrf-v6-leak.yml +++ /dev/null @@ -1,36 +0,0 @@ -# SONiC VRF IPv6 + v6 route-leaking: dual-stack version of the VRF route-leaking test. -# On s1, VRF blue imports VRF red's route-targets -> red's IPv6 stub -# prefix should appear in blue's v6 RIB (FRR rt vpn import, intra-node control-plane leak, -# independent of the VS dataplane -- same class as the v4 leak test). -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 - -addressing: - loopback: - ipv6: 2001:db8:0::/48 - p2p: - ipv6: 2001:db8:100::/48 - prefix6: 64 - lan: - ipv6: 2001:db8:1::/48 - prefix6: 64 - -module: [ vrf, bgp ] -bgp.as: 65000 -vrfs: - red: - blue: - import: [ red, blue ] -nodes: [ s1, s2 ] -links: -- s1-s2 -- s1: - vrf: red - type: stub -- s1: - vrf: blue - type: stub diff --git a/tests/integration/platform/sonic_clab/26-vrf-v6-ospfv3.yml b/tests/integration/platform/sonic_clab/26-vrf-v6-ospfv3.yml deleted file mode 100644 index adac4660f5..0000000000 --- a/tests/integration/platform/sonic_clab/26-vrf-v6-ospfv3.yml +++ /dev/null @@ -1,26 +0,0 @@ -# SONiC per-VRF OSPFv3: dual-stack version of the VRF test (link in VRF -# red with per-VRF OSPF). Needs features.vrf.ospfv3 (declared on the sonic_clab device) -- -# the shared vrf/frr.frr-config.j2 already renders `router ospf6 vrf ` once the -# feature flag unlocks it and the VRF has an ipv6 AF. -provider: clab -plugin: [ multilab ] -defaults: - device: sonic_clab - multilab: - id: 52 - -addressing: - loopback: - ipv6: 2001:db8:0::/48 - p2p: - ipv6: 2001:db8:100::/48 - prefix6: 64 - -module: [ vrf, ospf ] -vrfs: - red: -nodes: [ s1, s2 ] -links: -- s1: - s2: - vrf: red diff --git a/tests/integration/platform/sonic_clab/27-tunnel-gre.yml b/tests/integration/platform/sonic_clab/27-tunnel-gre.yml deleted file mode 100644 index f0ac061b63..0000000000 --- a/tests/integration/platform/sonic_clab/27-tunnel-gre.yml +++ /dev/null @@ -1,23 +0,0 @@ -# SONiC GRE tunnel: tunnel.gre is kernel-side (ip_gre), so it's not subject to the -# orchagent-can't-program-the-VS-dataplane limitation that affects VXLAN/EVPN — it should Just Work. -# s1-s2 direct link is the underlay; the GRE tunnel rides over it. Ping across the tunnel IPs -# (not the underlay IPs) is the datapath proof — traffic must be GRE-encapsulated to arrive. -provider: clab -plugin: [ multilab, tunnel.gre ] -defaults: - device: sonic_clab - multilab: - id: 52 - -module: [ ospf ] - -nodes: - s1: - s2: - -links: -- s1-s2 -- s1: - s2: - tunnel.mode: gre - ospf: false diff --git a/tests/integration/platform/sonic_clab/28-files-maxprefix.yml b/tests/integration/platform/sonic_clab/28-files-maxprefix.yml deleted file mode 100644 index 1edfbc5797..0000000000 --- a/tests/integration/platform/sonic_clab/28-files-maxprefix.yml +++ /dev/null @@ -1,44 +0,0 @@ -# SONiC files-plugin escape hatch worked example. -# BGP maximum-prefix-limit is a genuine netlab-CORE model -# gap (not device-specific): no netlab BGP module or plugin exposes a max-prefix -# attribute (defaults.yml bgp.session attr list has no such leaf) on ANY device, but -# FRR (and therefore SONiC, since SONiC routing IS FRR) supports it natively via plain -# vtysh CLI. This topology uses the `files` plugin's inline `configlets` + a node -# `config:` reference to inject the raw FRR config with ZERO external files -- the -# whole vendor-proprietary (here: FRR-native, still model-gap) config lives in this one -# topology YAML, same escape-hatch pattern as the ArcOS example. -# -# Mechanism check: the files plugin materializes the configlet as a real -# `bgp-maxprefix.j2` file in the topology directory at `netlab create` time; node -# `config: [ bgp-maxprefix ]` triggers netlab-core's stock "Deploy custom deployment -# templates" play, which resolves its deploy TASK the same way modules do -# (paths_deploy.tasks_generic) -- so it lands on our OWN tasks/deploy-config/sonic.yml -# override (docker exec + vtysh -f), the same mechanism already proven for -# tunnel.gre/evpn.multihoming plugin configs. No SONiC-specific plumbing needed. -# python validate/sonic.py --module files_maxprefix -provider: clab -plugin: [ multilab, files ] -defaults: - device: sonic_clab - multilab: - id: 52 -module: [ bgp ] -nodes: - s1: - bgp.as: 65001 - config: [ bgp-maxprefix ] - s2: - bgp.as: 65002 -links: -- s1: - s2: - -configlets: - bgp-maxprefix: | - router bgp {{ bgp.as }} - address-family ipv4 unicast - {% for n in bgp.neighbors|default([]) if n.ipv4 is defined %} - neighbor {{ n.ipv4 }} maximum-prefix 100 - {% endfor %} - exit-address-family - ! diff --git a/tests/integration/platform/sonic_clab/29-bgp-domain.yml b/tests/integration/platform/sonic_clab/29-bgp-domain.yml deleted file mode 100644 index 63ee9e4991..0000000000 --- a/tests/integration/platform/sonic_clab/29-bgp-domain.yml +++ /dev/null @@ -1,58 +0,0 @@ -# SONiC bgp.domain plugin. The bgp.domain plugin splits a SINGLE AS into -# ISOLATED iBGP "domains": it PRUNES every cross-domain iBGP session and requires each -# domain to have its OWN route reflector (it does NOT hierarchically connect domains -- -# domains are independent iBGP islands that share one AS number). This models AS 65000 -# with two domains: -# red = s1 (route reflector) + s2 + s3 (RR clients) -# blue = s4 (route reflector, alone) -# netlab first builds the RR client mesh (clients peer to every RR in the AS over the -# OSPF underlay), then the plugin prunes all cross-domain sessions, so: -# * s1 keeps iBGP ONLY to s2/s3 (red); the s1<->s4 (blue) session is pruned. -# * s4 (blue) ends up with NO iBGP neighbors -- its only peers were red, all pruned. -# -# DATAPATH: h2 is a real host behind s2 on 172.16.2.0/24. That link has `ospf: False`, -# so the prefix is advertised into BGP ONLY (never into the OSPF underlay) -- reachability -# of h2 is therefore a PURE bgp.domain fact: -# * s3 (red client) reaches h2 ONLY because s1 reflects s2's prefix -> proves RR -# client-to-client reflection AT THE DATAPATH. -# * s4 (blue) has no route to 172.16.2.0/24 at all -> ping fails -> proves cross-domain -# isolation AT THE DATAPATH. -provider: clab -plugin: [ multilab, bgp.domain ] -defaults: - device: sonic_clab - multilab: - id: 52 - -module: [ bgp, ospf ] -bgp.as: 65000 -bgp.advertise_loopback: True - -nodes: - s1: - bgp.rr: True - bgp.domain: red - s2: - bgp.domain: red - s3: - bgp.domain: red - s4: - bgp.rr: True - bgp.domain: blue - h2: - device: linux - -links: -# OSPF underlay (star on s1) — carries the loopbacks so the iBGP sessions come up. -- s1: - s2: -- s1: - s3: -- s1: - s4: -# Red customer prefix behind s2 — BGP-only (ospf:False keeps it out of the underlay), -# so reaching h2 depends entirely on RR reflection. -- s2: - h2: - ospf: False - prefix: 172.16.2.0/24 diff --git a/tests/integration/platform/sonic_clab/README.md b/tests/integration/platform/sonic_clab/README.md deleted file mode 100644 index 4f4d7a373c..0000000000 --- a/tests/integration/platform/sonic_clab/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# SONiC (containerlab) integration smoke tests - -Device-specific bring-up smoke tests for the `sonic_clab` device (docker-sonic-vs on -containerlab). Every topology below has been run live against a real `docker-sonic-vs:latest` -node (containerlab 0.77.0) with `netlab up`/`netlab initial`, all over Ansible's -`docker` connection plugin -- no SSH. - -| topology | module(s) exercised | what was checked live | -|---|---|---| -| `01-initial.yml` | initial | 1-node bring-up: hostname, loopback IP, interfaces | -| `02-ospf.yml` | ospf | adjacency Full (pure `ansible_network_os: frr` fallback, no `sonic_clab` template) | -| `03-bgp.yml` | bgp | eBGP session Established (frr fallback) | -| `04-isis.yml` | isis | adjacency Up (frr fallback) | -| `05-vrf.yml` | vrf, ospf | per-VRF OSPF adjacency Full (frr fallback) | -| `06-mpls-sr-l3vpn.yml` | isis, bgp, mpls, sr, vrf | LDP Operational, real kernel MPLS label FIB, VPNv4 Established, L3VPN ping 0% loss. **Needs a one-time host prereq: `sudo modprobe mpls_router mpls_iptunnel`** | -| `07-srv6.yml` | isis, srv6 | real kernel `seg6local` End/End.X routes, cross-node ISIS locator advertisement | -| `08-vlan.yml` | vlan | IRB SVI + kernel bridge datapath, 0% loss (needed a `sonic_clab.j2` override + kernel-sync -- see below) | -| `09-lag.yml` | lag, ospf | LACP aggregate (teamd), OSPF Full over the PortChannel (needed a `sonic_clab.j2` override) | -| `11-vxlan-evpn.yml` | vlan, ospf, bgp, vxlan, evpn | L2VNI up, remote VTEP learned, h1<->h2 ping across the tunnel 0% loss (needed a `sonic_clab.j2` override) | -| `12-evpn-irb-l3vni.yml` | vlan, vrf, ospf, bgp, vxlan, evpn | symmetric-IRB L3VNI inter-subnet ping 0% loss | -| `13-vrf-leak.yml` | vrf, bgp | VRF red's prefix leaks into VRF blue's RIB via BGP RT import | -| `14-bgp-session.yml` | bgp, bgp.session plugin | MD5 password applied, session Established (frr fallback) | -| `15-bgp-policy.yml` | bgp, bgp.policy plugin, routing | route-map locpref/weight/community all applied (frr fallback) | -| `16-ebgp-multihop.yml` | bgp, ebgp.multihop plugin, ospf, routing | loopback-to-loopback multihop session Established (frr fallback) | -| `17-ospf-areas.yml` | ospf, ospf.areas plugin | stub area adjacency Full (frr fallback) | -| `18-bgp-originate.yml` | bgp | originated prefix received by the eBGP peer (frr fallback) | -| `19-bfd.yml` | ospf, bfd | BFD session Up under OSPF (frr fallback; needed `features.ospf.bfd`/`features.bgp.bfd` device flags) | -| `20-ripv2.yml` | ripv2 | route learned via RIP (frr fallback) | -| `21-evpn-multihoming.yml` | lag, vlan, ospf, bgp, vxlan, evpn, evpn.multihoming plugin | Ethernet Segment on PortChannel1, dual-homed h1<->h2 ping 0% loss (needs the `h1-fixaddr/` custom-config addon, a generic `linux`-device host-addressing workaround, not SONiC-specific) | -| `22-ospfv3.yml` | ospf | OSPFv3 dual-stack: both AFs adjacency Full, ping6 0% loss (frr fallback) | -| `23-isis-v6-bgp-v6.yml` | isis, bgp | IS-IS adjacency Up (multi-topology IPv6), iBGP IPv6 AF Established (frr fallback) | -| `24-routing-v6.yml` | routing | IPv6 static route + IPv6 prefix-list both installed (frr fallback) | -| `25-vrf-v6-leak.yml` | vrf, bgp | VRF red's IPv6 prefix leaks into VRF blue's v6 RIB via BGP RT import (frr fallback) | -| `26-vrf-v6-ospfv3.yml` | vrf, ospf | per-VRF OSPFv3 adjacency Full (frr fallback) | -| `27-tunnel-gre.yml` | ospf, tunnel.gre plugin | kernel `ip_gre` tunnel, ping across the tunnel IPs 0% loss (needed an `initial/sonic_clab.j2` fix -- see below; the `tunnel.gre/frr.j2` plugin template itself needed no override) | -| `28-files-maxprefix.yml` | bgp, files plugin | `files` escape-hatch worked example: raw FRR `maximum-prefix 100` configlet injected and applied (frr fallback + our own `deploy-config/sonic_clab.yml`, same mechanism proven for tunnel.gre/evpn.multihoming) | -| `29-bgp-domain.yml` | bgp, ospf, bgp.domain plugin | isolated iBGP domains: s1 keeps exactly 2 (red) peers, the cross-domain s1<->s4 session is pruned, s3 (red) reaches h2 via RR reflection, s4 (blue) has zero BGP neighbors and no route to h2 (frr fallback + netlab-core plugin logic, no device template involved) | - -Run with `netlab up ` from this directory (needs `docker-sonic-vs:latest` pulled locally -and a `multilab` id that isn't in use -- these default to id 52). - -## What actually needed a `sonic_clab.j2` override vs. what's free - -Most modules above render and deploy with **zero new template files** -- they fall through -automatically to the package's `/frr.j2` via the `ansible_network_os: frr` search-path -fallback (see `netsim/devices/sonic_clab.yml`'s `group_vars.ansible_network_os` inherited from -the parent `sonic` device). Only these needed a real `sonic_clab.j2`, each verified live here: - -* `initial` -- interface/loopback bring-up via the `config` CLI, CONFIG_DB->kernel sync for - routed ports, FRR daemon enable, sshd bootstrap. config_db VLAN and PortChannel *creation* is - factored out into the per-module init hooks below (pulled in at the right ordering point by - `extra_module_initial()`, the same mechanism VyOS uses). -* `vlan/sonic_clab.initial.j2` -- create config_db VLANs (`Vlan`) before any SVI is addressed. -* `vlan` -- switchport membership via `config vlan member add`, **plus a kernel bridge sync** - (docker-sonic-vs's `vlanmgrd` races at boot and often never mirrors `VLAN_MEMBER` rows written - during the first deploy -- found live: config_db had the row, but the kernel port had no - `master Bridge` and cross-node ping failed 100% until the sync was added). -* `lag/sonic_clab.initial.j2` -- create the config_db PortChannel(s) before the aggregate is - addressed. -* `lag` -- PortChannel members via `config portchannel member add`, plus a defensive `teamdctl` - sync (teammgrd is more reliable than vlanmgrd, but this belt-and-suspenders step guards the - rare miss). -* `vxlan` -- EVPN-VXLAN L2VNI/L3VNI built directly on the kernel/FRR path (bridge + kernel vxlan - netdev), since docker-sonic-vs's orchagent can't program the VXLAN dataplane from CONFIG_DB. - The EVPN/BGP control plane itself is standard FRR (`bgp/frr.j2` + zebra `advertise-all-vni`); - this template is only the kernel data-plane setup the VS orchestration agent won't do. - -`bgp`, `evpn`, `evpn.multihoming`, `isis`, `vrf`, `mpls`, `sr`, `srv6`, `bfd`, `gateway`, -`routing`, `ripv2`, `tunnel.gre`, -`bgp.session`/`bgp.policy`/`bgp.originate`/`bgp.domain`/`ebgp.multihop`, `ospf.areas`, and the -IPv4/IPv6 dual-stack variants of all of the above need no override at all -- they render via the -frr fallback too. (`bgp` needs no `no router bgp` reset wrapper: unlike the Azure/libvirt sonic -image, docker-sonic-vs does not pre-seed a BGP AS.) - -One bug found and fixed by `27-tunnel-gre.yml`: the `initial/sonic_clab.j2` bash/config_db loop -deliberately skips tunnel interfaces (the `config` CLI only accepts Ethernet/PortChannel/ -Vlan/Loopback names), and the shared `tunnel.gre/frr.j2` plugin template only creates the kernel -netdev (`ip tunnel add`), it doesn't address it -- so the tunnel interface was created with NO -IP at all until the vtysh heredoc in `initial/sonic_clab.j2` was extended to address -tunnel-type interfaces itself (the same thing the vanilla `frr` device's own initial template -already does for every interface, unconditionally -- our SONiC template only omits it elsewhere -because config_db handles addressing for every other interface type). - -Genuinely unsupported on this image, not just untested (see `netsim/devices/sonic_clab.yml`'s -`support.caveats` for the live-probed reasons): DHCP relay/client, real STP port-blocking, -and the `mlag.vtep` active-active datapath. - -This is a device bring-up smoke-test set, not (yet) wired into netlab's shared per-module -`tests/integration//NN-*.yml` parameterized matrix that runs across all devices -- that -wiring is tracked as a follow-up. diff --git a/tests/integration/platform/sonic_clab/h1-fixaddr/linux.j2 b/tests/integration/platform/sonic_clab/h1-fixaddr/linux.j2 deleted file mode 100644 index f3e63717ac..0000000000 --- a/tests/integration/platform/sonic_clab/h1-fixaddr/linux.j2 +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# -# h1-fixaddr custom config — h1 is the M-side of an MLAG-style -# LAG (dual-homed to s1+s2) on a bridge-mode VLAN. netlab's vlan-module address-pool allocation -# doesn't reach the synthesized vlan1000 SVI on a `linux`-device host in this specific -# lag+bridge-mode combo (single-homed h2 on the SAME vlan gets addressed fine, directly on its -# physical port -- the gap is specific to the derived SVI a dual-homed host needs). Netlab-side -# host-addressing quirk, not a SONiC platform limitation -- worked around here so the h1<->h2 -# datapath check has a real IP to ping from. -set -e -ip addr add 172.16.0.5/24 dev vlan1000 2>/dev/null || true -exit 0 From f02b69e5cf4ddb9f2e7a5d5d176163ef90ce2e84 Mon Sep 17 00:00:00 2001 From: RocNet Date: Wed, 29 Jul 2026 15:30:28 +0000 Subject: [PATCH 05/23] fix(sonic): do not apply the VM BGP reset on the container bgp/sonic.j2 opens with "no router bgp". Before the device was parented to FRR the container had no bgp template of its own and fell through to bgp/frr.j2; after the rename the .j2 lookup selects bgp/sonic.j2, and vtysh rejects that reset on a fresh docker-sonic-vs with rc 13 -- Failure to communicate[13] to bgpd, line: no router bgp % No BGP process is configured -- which fails the entire config deploy, not just the BGP module. Observed in the first restructured suite run as UP_FAIL on ospf/ospfv2/20-default, ospf/ospfv2/10-import, ospf/ospfv2/11-import-policy, ospf/ospfv2/22-default-vrf, ospf/ospfv3/10-import and initial/06-bridge, and as the 9/11 on routing/08-community-large reported with the restructure. One cause, not two: the earlier report attributed it to module ordering against routing/frr.j2, which was wrong -- routing/frr.j2 never emits that line. bgp/sonic-clab.j2 restores the pre-restructure behaviour, scoped to the provider that needs it, and leaves the VM template untouched. --- netsim/ansible/templates/bgp/sonic-clab.j2 | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 netsim/ansible/templates/bgp/sonic-clab.j2 diff --git a/netsim/ansible/templates/bgp/sonic-clab.j2 b/netsim/ansible/templates/bgp/sonic-clab.j2 new file mode 100644 index 0000000000..f3de798f47 --- /dev/null +++ b/netsim/ansible/templates/bgp/sonic-clab.j2 @@ -0,0 +1,12 @@ +{# + SONiC BGP, container deployment. + + The VM template (bgp/sonic.j2) opens with a "no router bgp" reset. docker-sonic-vs starts with no + BGP process at all, and vtysh rejects that line with rc 13 ("Failure to communicate[13] to bgpd, + line: no router bgp" / "% No BGP process is configured"), which fails the whole config deploy. + + Before the device was parented to FRR the container had no bgp template of its own and fell + through to bgp/frr.j2. This file keeps that behaviour now that bgp/sonic.j2 would otherwise be + selected by the .j2 lookup. +#} +{% include "frr.j2" +%} From 39692be1aa8ef018f773d63db08b31ec1fbe248b Mon Sep 17 00:00:00 2001 From: RocNet Date: Wed, 29 Jul 2026 16:49:14 +0000 Subject: [PATCH 06/23] fix(sonic): declare VRRP only -- anycast gateway is inherited but does not work Parenting the device to FRR made it claim gateway.protocol: anycast, which the hand-maintained sonic_clab device never declared. Measured on docker-sonic-vs: gateway/01-anycast 10 of 11 checks pass, then ping_dup fails -- duplicate packets, i.e. both gateways answering, which is precisely the condition an anycast gateway exists to avoid lag/11-mlag-anycast fails earlier: "X1 cannot establish a LAG with both switches in MLAG pair" -- the documented no-mclagd limit Both tests were correctly refused before the restructure. Restricting the protocol list to [ vrrp ] restores that refusal rather than leaving netlab to build a topology the platform cannot serve. --- netsim/devices/sonic.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/netsim/devices/sonic.yml b/netsim/devices/sonic.yml index c402424307..9df1829d82 100644 --- a/netsim/devices/sonic.yml +++ b/netsim/devices/sonic.yml @@ -47,6 +47,12 @@ group_vars: # features: stp: false # config-plane only, no stpd daemon -- no port blocking to verify + gateway: + # FRR declares anycast; SONiC does not deliver it. gateway/01-anycast reaches 10 of 11 checks + # and then fails ping_dup -- duplicate packets, i.e. both gateways answering, which is the + # failure anycast exists to avoid. lag/11-mlag-anycast fails earlier still ("X1 cannot + # establish a LAG with both switches in MLAG pair"), the documented no-mclagd limitation. + protocol: [ vrrp ] dhcp: false # docker-sonic-vs ships no dhcrelay/dhcp6relay binary routing: policy: From d76775d64d6c5d0514ea4be4d80bf5655e9c4b7c Mon Sep 17 00:00:00 2001 From: RocNet Date: Wed, 29 Jul 2026 16:49:35 +0000 Subject: [PATCH 07/23] fix(sonic): claim the router role only FRR declares initial.roles [ host, router, bridge ]; the hand-maintained sonic_clab device declared none of the extra roles, and neither works on docker-sonic-vs: initial/06-bridge does not deploy at all -- one failed task, the node is never configured (UP_FAIL) initial/05-host 3 of 5 checks pass, then the IPv6 ping and the "DUT is sending IPv6 RA, H4 got a default route" check fail Both were correctly refused before the restructure. --- netsim/devices/sonic.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/netsim/devices/sonic.yml b/netsim/devices/sonic.yml index 9df1829d82..18e9cdc3e7 100644 --- a/netsim/devices/sonic.yml +++ b/netsim/devices/sonic.yml @@ -47,6 +47,12 @@ group_vars: # features: stp: false # config-plane only, no stpd daemon -- no port blocking to verify + initial: + # FRR declares roles [ host, router, bridge ]. Neither extra role works here: + # initial/06-bridge fails to deploy at all (one failed task, node never configured), and + # initial/05-host reaches 3 of 5 checks then fails the IPv6 ping and the RA default-route + # check. Only the router role is claimed. + roles: [ router ] gateway: # FRR declares anycast; SONiC does not deliver it. gateway/01-anycast reaches 10 of 11 checks # and then fails ping_dup -- duplicate packets, i.e. both gateways answering, which is the From bee58cb651db5ca9bacfb5e349d5f39ce19b2e47 Mon Sep 17 00:00:00 2001 From: RocNet Date: Wed, 29 Jul 2026 16:49:59 +0000 Subject: [PATCH 08/23] fix(sonic): do not claim RFC 9234 BGP roles Inherited from FRR by the reparenting and not verified on docker-sonic-vs. bgp.policy/70-bgp-roles gets 19 of its 22 checks, and all three failures are "The prefix ... should not be in the BGP table" -- the role-based route-leak prevention that is the whole purpose of the feature is not enforced. Sessions establish and the roles are configured; the filtering they should drive does not happen. Disabled pending verification rather than claimed on the strength of the checks that do pass -- a feature that configures but does not filter is the kind of silent wrong-path failure this device set has produced before. --- netsim/devices/sonic.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/netsim/devices/sonic.yml b/netsim/devices/sonic.yml index 18e9cdc3e7..57efcf4420 100644 --- a/netsim/devices/sonic.yml +++ b/netsim/devices/sonic.yml @@ -47,6 +47,14 @@ group_vars: # features: stp: false # config-plane only, no stpd daemon -- no port blocking to verify + bgp: + # RFC 9234 BGP roles. Inherited from FRR and NOT verified here: bgp.policy/70-bgp-roles gets + # 19 of its 22 checks, and the three that fail are all "The prefix ... should not be in the + # BGP table" -- i.e. the role-based leak prevention that is the entire point of the feature + # is not being enforced. The sessions come up and the roles are configured; the filtering + # they are supposed to drive does not happen. Disabled pending verification rather than + # claimed on the strength of the checks that do pass. + role: false initial: # FRR declares roles [ host, router, bridge ]. Neither extra role works here: # initial/06-bridge fails to deploy at all (one failed task, node never configured), and From 6bf502c0269dcfb72efc3eb5ff7177a9f8b60b4f Mon Sep 17 00:00:00 2001 From: RocNet Date: Wed, 29 Jul 2026 16:50:23 +0000 Subject: [PATCH 09/23] docs(sonic): record that the device now has two deployments and can select libvirt Not a feature claim and nothing is disabled for it. The previous sonic_clab device had a clab block only; the reparented sonic device has both libvirt and clab, so a topology that does not pin a provider can select libvirt and fail with "KVM is not installed or does not include kvm-ok utility" on a container-only host. tests/integration/initial/08-ra does exactly that. This is an inherent property of the architecture, so it is documented rather than worked around. --- netsim/devices/sonic.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/netsim/devices/sonic.yml b/netsim/devices/sonic.yml index 57efcf4420..2708b2767c 100644 --- a/netsim/devices/sonic.yml +++ b/netsim/devices/sonic.yml @@ -29,6 +29,13 @@ support: by any integration test on this platform and were therefore never verified: policy.set.community.extended and policy.match.nexthop. They are disabled explicitly rather than left to inheritance -- under a parent device, silence is a claim. + - >- + This device has BOTH a libvirt and a clab deployment, where the previous sonic_clab device had + clab only. A topology that does not pin a provider can therefore select libvirt and fail with + "KVM is not installed or does not include kvm-ok utility" on a container-only host -- + tests/integration/initial/08-ra does exactly that. Run SONiC topologies with "-p clab" (or set + the provider in the topology) unless the VM image is actually available. This is a property of + having two deployments, not a missing feature, so nothing is disabled for it. - >- The libvirt (VM) deployment is far less tested than the container. VLAN, LAG, VXLAN and EVPN are enabled only for clab, because their configuration is built with config_db/redis-cli From aadc34ff3c253530dfe1a0a1b3d34f82a79958b9 Mon Sep 17 00:00:00 2001 From: RocNet Date: Fri, 31 Jul 2026 15:16:23 +0000 Subject: [PATCH 10/23] fix(sonic): declare lag.mlag False instead of documenting MLAG as broken Per review: if things do not work, turn them off in features rather than writing them up. docker-sonic-vs has no mclagd, so an MLAG pair never forms -- lag/10-mlag fails "X1 cannot establish a LAG with both switches in MLAG pair" and then loses the datapath. The config plane renders (a static /31 peer-link, the EOS pattern) but nothing negotiates over it. Honest limitation of this change: it does not on its own make netlab refuse an MLAG topology. netsim/modules/lag.py:77 gates the peer-link machinery on "features.lag.mlag OR features.evpn.multihoming.lag", and evpn.multihoming.lag is still declared. That declaration is itself unverified -- no evpn.multihoming test runs in the integration suite on this platform -- but removing it is a separate decision that deserves its own measurement, so it is left alone here and noted for follow-up. Verified no collateral: lag/01-l3-lag, lag/02-lag-vlan-trunk, lag/03-l3-lag-passive and lag/04-lag-vlan-routed-trunk all still render. --- netsim/devices/sonic.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/netsim/devices/sonic.yml b/netsim/devices/sonic.yml index 2708b2767c..c5d6461051 100644 --- a/netsim/devices/sonic.yml +++ b/netsim/devices/sonic.yml @@ -150,10 +150,13 @@ clab: native_routed: true lag: passive: true - mlag: - peer: - ip: 169.254.127.0/31 # static /31 peer-link (EOS pattern); SONiC has no MLAG keepalive - # protocol to negotiate one over -- config-plane only (no mclagd) + # MLAG does not work: docker-sonic-vs has no mclagd, so the pair never forms + # (lag/10-mlag: "X1 cannot establish a LAG with both switches in MLAG pair", then the + # datapath is lost). Declared False rather than described in the caveats -- a feature that + # does not work belongs turned off. Note this does not on its own make netlab refuse an + # MLAG topology: netsim/modules/lag.py gates the peer-link machinery on + # "lag.mlag OR evpn.multihoming.lag", and evpn.multihoming.lag is still declared below. + mlag: False vxlan: true evpn: transport: [ vxlan, mpls ] From 5cc009765fb0d011f9ad2718195a675f4ae28c95 Mon Sep 17 00:00:00 2001 From: RocNet Date: Fri, 31 Jul 2026 15:16:57 +0000 Subject: [PATCH 11/23] docs(sonic): add docker-sonic-vs download instructions and retitle the container section Review blocker: the reviewer offered to debug the remaining integration failures but has no way to obtain the image. Documents both routes to it -- downloading a published build artefact via sonic.software, and building it from sonic-buildimage -- with the load/tag steps and the tag the device expects (docker-sonic-vs:latest, which is what this submission was tested with). Also retitles the section to "Using the SoNIC containers" as requested, and updates the invocation to the restructured device: the sonic_clab device no longer exists, so it is now "netlab up -d sonic -p clab". --- docs/labs/sonic.md | 56 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/docs/labs/sonic.md b/docs/labs/sonic.md index b05da45c3d..b63a1dadca 100644 --- a/docs/labs/sonic.md +++ b/docs/labs/sonic.md @@ -21,20 +21,52 @@ During the box-building process, you might have to disable ZTP or clean up the i ``` (labs-sonic-clab)= -## Using the containerlab Provider (docker-sonic-vs) +## Using the SoNIC containers -Apart from the libvirt Vagrant box above, SONiC can run under **containerlab** using the community -`docker-sonic-vs` image via the `sonic_clab` device (parent: `sonic`). No box build is needed -- -supply your own `docker-sonic-vs:latest` image (build it from the -[sonic-buildimage](https://github.com/sonic-net/sonic-buildimage) `docker-sonic-vs` target, or pull a -community build) and select the device: +SONiC also runs under *containerlab* using the community `docker-sonic-vs` image. There is no box +to build -- select the `clab` provider: ``` -netlab up -d sonic_clab -p clab +netlab up -d sonic -p clab ``` -`docker-sonic-vs` is a single monolithic container running FRR (`vtysh`); netlab pushes configuration -and runs validation over **docker exec** (the image starts no `sshd`). Native validation therefore -works out of the box -- `netlab up -d sonic_clab --validate` executes the FRR-based `show` -commands over docker-exec. See the [`sonic_clab` caveats](caveats-sonic-clab) for image/connection -details and the verified module set. +### Getting the container image + +*netlab* does not ship or distribute `docker-sonic-vs`; you supply it yourself. It is published as +a build artefact of the [sonic-buildimage](https://github.com/sonic-net/sonic-buildimage) project +rather than on a public registry, so you either download a build or make one: + +* **Download a published build.** The SONiC project publishes `docker-sonic-vs.gz` from its Azure + build pipelines; [sonic.software](https://sonic.software/) indexes those builds and links to the + artefacts for each branch. Pick the `vs` platform, download `docker-sonic-vs.gz`, then: + + ``` + gunzip docker-sonic-vs.gz + docker load -i docker-sonic-vs + docker tag docker-sonic-vs:latest docker-sonic-vs:latest + ``` + +* **Build it yourself** from `sonic-buildimage`: + + ``` + git clone --recurse-submodules https://github.com/sonic-net/sonic-buildimage.git + cd sonic-buildimage + make init + make configure PLATFORM=vs + make target/docker-sonic-vs.gz + docker load -i target/docker-sonic-vs.gz + ``` + +The device definition expects the image to be tagged **`docker-sonic-vs:latest`**; override +`clab.image` in your topology if yours is tagged differently. This submission was tested with +`docker-sonic-vs:latest`. + +### How it works + +`docker-sonic-vs` is a single monolithic container running FRR (`vtysh`), unlike the VM above which +runs FRR in a nested `bgp` container. *netlab* pushes configuration and runs validation over +**docker exec** -- the image starts no `sshd`. The device inherits the `frr` device, so the FRR +control-plane templates are used directly and only the container-specific parts +(`/sonic-clab.j2`) are SONiC's own. + +See the [SONiC caveats](caveats-sonic-clab) for what is and is not supported. From 5695f620f55cc91719a223bca3f300a19cddb2f3 Mon Sep 17 00:00:00 2001 From: RocNet Date: Fri, 31 Jul 2026 15:17:34 +0000 Subject: [PATCH 12/23] docs(sonic): cut the caveats section down to what belongs in caveats Addresses five review comments on docs/caveats.md, all the same theme: the section was carrying material that belongs elsewhere. 50 lines -> 21. * image acquisition and the VM-vs-container architecture: already in labs/sonic.md, so this now links there instead of repeating it * the network_cli explanation: cut to "uses docker exec, not network_cli" * the MPLS/SR kernel-module paragraph: removed -- it described something that works as expected, which is not what caveats are for * the module coverage list: removed -- platforms.md carries that * everything documented as broken: replaced by one sentence saying those capabilities are turned off in features, which they now are That last one is the reviewer point that "if things do not work, turn them off in features". STP, DHCP, MLAG, anycast gateways, the host/bridge roles, BGP roles and the two unverified routing-policy operations are all feature disables now, so netlab refuses those topologies rather than the documentation warning about them after the fact. --- docs/caveats.md | 61 +++++++++++++------------------------------------ 1 file changed, 16 insertions(+), 45 deletions(-) diff --git a/docs/caveats.md b/docs/caveats.md index e2b0956071..01170c5365 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -655,51 +655,22 @@ See also [](caveats-sros) caveats for further details. (caveats-sonic-clab)= ## Sonic (containerlab) -A separate device (`sonic_clab`, parent `sonic`) for the community `docker-sonic-vs` image -running under *containerlab*, distinct from the `sonic` device above (which targets the -Azure/libvirt SONiC VM). The two images have different internal architectures and are not -interchangeable: `docker-sonic-vs` is a single monolithic container (FRR's `vtysh` runs -directly in it), while the VM runs FRR inside a nested `bgp` sub-container. - -* You supply your own `docker-sonic-vs:latest` image (build it from the - [sonic-buildimage](https://github.com/sonic-net/sonic-buildimage) `docker-sonic-vs` target, or - pull a prebuilt one); *netlab* does not ship or distribute it. -* Configuration is deployed over Ansible's built-in `docker` connection plugin (`docker exec`), - not `network_cli`: `docker-sonic-vs` has no cliconf-compatible CLI. The only SONiC cliconf - Ansible ships, `dellemc.enterprise_sonic`, targets Dell's licensed "Management Framework" CLI - (`sonic-cli`/klish) on Dell PowerSwitch hardware running Enterprise SONiC -- that CLI does not - exist on the community image (no `admin` user, no `sonic-cli`/`klish` binary anywhere in it). - This matches how *netlab* already drives the in-tree `frr` *containerlab* device and the - libvirt `sonic` device above (which has a full sshd) -- neither uses `network_cli` for this - vtysh-delegated render-then-push style of configuration, sshd or not. -* `docker-sonic-vs` ships `sshd` and host keys but starts neither `sshd` nor a login user; the - initial configuration bootstraps both (`admin`/`YourPaSsWoRd`, matching the `sonic` device's - own credentials) purely for interactive access (`netlab connect`, ad-hoc troubleshooting) -- - SSH plays no part in configuration deployment. -* Most FRR daemons ship disabled in `/etc/frr/daemons` by default (to save resources); the - initial configuration enables the ones the configured modules need and restarts FRR once. -* MPLS/SR-MPLS get a real kernel MPLS label FIB (`ip -M route`) with no manual setup: because - SONiC runs FRR, the device reuses netlab's FRR kernel-module handling (the `clab.kmods` - declaration), so `netlab up` loads `mpls_router`/`mpls_iptunnel` on the host automatically when - an MPLS or SR lab is deployed, the same way the in-tree `frr` containerlab device does. -* Module coverage is broad and FRR-delegated (`ospf`, `bgp`, `isis`, `vrf`, `bfd`, `mpls`, `sr`, - `srv6`, `vxlan`, `evpn`, `evpn.multihoming`, `gateway`, `ripv2`, `routing`, - `tunnel.gre`, `bgp.session`/`bgp.policy`/`bgp.originate`/`bgp.domain`/`ebgp.multihop`, - `ospf.areas`, both IPv4 and IPv6). Each module ships with a device integration topology under - `tests/integration/platform/sonic_clab/` describing exactly what it checks. OSPF, BGP, IS-IS, - VRF, and VLAN (including the SVI-to-SVI kernel-bridge datapath) were live-verified on - `docker-sonic-vs:latest` for this submission; the remaining topologies -- including the MPLS - L3VPN datapath (LDP transport + BGP VPN label, real kernel label FIB) and the EVPN-VXLAN - symmetric-IRB (L3VNI) datapath -- were validated during the device's development, not re-run for - this submission. -* `srv6` is control-plane + kernel-plane only on this image: the SRv6 locator and End/End.X SIDs - are advertised in the IS-IS LSDB and installed as real kernel `seg6local` routes (`ip -6 route`), - but end-to-end SRv6-OAM datapath (ping to a `uN` End SID) is not resolved on `docker-sonic-vs` - -- the same open item as the FRR/IS-IS SRv6 result on other platforms. -* Genuinely unsupported on this image (not just untested): DHCP relay/client (no - `dhcrelay`/`dhcp6relay` binary), real STP port-blocking (config-plane only, no `stpd` - daemon), and the `mlag.vtep` active-active EVPN datapath (config-plane renders correctly, but - there is no `mclagd`, so BGP never resolves a usable self-next-hop for the anycast VTEP). +The `sonic` device also runs under *containerlab* with the community `docker-sonic-vs` image; see +[](labs-sonic-clab) for how to obtain it and how the two deployments differ. + +* Configuration is deployed over Ansible's `docker` connection plugin (`docker exec`), not + `network_cli`. +* `docker-sonic-vs` ships `sshd` but starts neither it nor a login user; the initial configuration + bootstraps both for interactive access only. +* `srv6` is control-plane and kernel-plane only: the locator and End/End.X SIDs are advertised in + the IS-IS LSDB and installed as kernel `seg6local` routes, but the end-to-end SRv6 datapath does + not resolve -- the same open item as FRR/IS-IS SRv6 on other platforms. + +Everything else this image cannot do is turned off in `features` rather than described here, so +*netlab* refuses the topology instead of building one the platform cannot serve: STP, DHCP +relay/client, MLAG, anycast gateways, the host and bridge node roles, RFC 9234 BGP roles, and two +routing-policy operations (`policy.set.community.extended`, `policy.match.nexthop`) that no +integration test exercises on this platform. (caveats-vyos)= ## VyOS From e2eb63518dab92fd6842222c819ce03e83116cd5 Mon Sep 17 00:00:00 2001 From: RocNet Date: Fri, 31 Jul 2026 15:18:56 +0000 Subject: [PATCH 13/23] docs(sonic): record what was considered on FRR script reuse Review asked to reuse FRR scripting where possible; both flagged sites now say what was found, at the line, so the next reader does not re-derive it. initial/sonic-clab.j2 -- NOT delegated, with reason: initial/frr.j2 is itself a complete bash script (shebang, set -e, bond creation, per-interface sysctls) with its vtysh configuration inline rather than in a macro, and much of that setup conflicts with the config_db path this device uses. Delegating means factoring that vtysh portion into a macro on the FRR device -- out of scope here and needing FRR re-verification. vxlan/sonic-clab.j2 -- CAN be delegated and should be. vxlan/frr.j2 exposes create_vxlan_interface(vni, br_name, vrf, mtu), which builds exactly the per-VNI bridge plus kernel vxlan netdev this template builds by hand. Not changed here only because it alters the rendered dataplane and needs the EVPN-VXLAN datapath re-verified on two VS nodes -- a measured change, not a documentation one. --- netsim/ansible/templates/initial/sonic-clab.j2 | 7 +++++++ netsim/ansible/templates/vxlan/sonic-clab.j2 | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/netsim/ansible/templates/initial/sonic-clab.j2 b/netsim/ansible/templates/initial/sonic-clab.j2 index 761f1b2618..1559d21ce8 100644 --- a/netsim/ansible/templates/initial/sonic-clab.j2 +++ b/netsim/ansible/templates/initial/sonic-clab.j2 @@ -155,6 +155,13 @@ fi # # And now let's configure the interfaces # +{# REUSE NOTE (review: "reuse FRR scripting where possible"). The vtysh block below duplicates what + initial/frr.j2 renders for the same interfaces. It is not delegated because initial/frr.j2 is + itself a complete bash script -- shebang, set -e, bond creation, per-interface sysctls -- with + the vtysh configuration inline rather than in a macro, and much of that setup conflicts with the + config_db path this device uses. Delegating would mean factoring the vtysh portion of + initial/frr.j2 into a macro, i.e. a change to the FRR device, out of scope for this submission + and needing FRR re-verification. Worth doing separately. #} cat >/tmp/netlab-initial.frr < Date: Fri, 31 Jul 2026 15:20:29 +0000 Subject: [PATCH 14/23] docs(sonic): move the SONiC containerlab release note to 26.08 Per review, this lands in 26.08 rather than 26.07. Reworded for the restructured device: it is no longer a new sonic_clab device but a clab deployment of the existing sonic device, which now inherits FRR -- which is also why the module list is the FRR control-plane set rather than an enumeration of our own. --- docs/release.md | 1 + docs/release/26.07.md | 1 - docs/release/26.08.md | 17 +++++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 docs/release/26.08.md diff --git a/docs/release.md b/docs/release.md index 0d78d9c4cd..d6e5d224c8 100644 --- a/docs/release.md +++ b/docs/release.md @@ -169,6 +169,7 @@ For older releases, check the [release notes archive](release-archive.md). :caption: Individual release notes :maxdepth: 1 + release/26.08.md release/26.07.md release/26.06.md release/26.05.md diff --git a/docs/release/26.07.md b/docs/release/26.07.md index 0bb197abe5..77e6c186ef 100644 --- a/docs/release/26.07.md +++ b/docs/release/26.07.md @@ -14,7 +14,6 @@ * The [**WireGuard tunnel** plugin](plugin-tunnel-wireguard) supports WireGuard tunnels on FRR. * The [**bgp.session** plugin](plugin-bgp-session) and the [OSPF module](module-ospf) support graceful restart on Arista EOS, BIRD, FortiOS, and FRR * The [**bgp.policy** plugin](plugin-bgp-policy) supports the **bgp.role** attribute on FRR and BIRD. -* SONiC can run under *containerlab* with the community `docker-sonic-vs` image via the new [`sonic_clab` device](caveats-sonic-clab), expanding SONiC coverage well beyond the libvirt device (OSPF/OSPFv3, IS-IS, BGP with the bgp.session/policy/originate/domain and ebgp.multihop plugins, RIPv2, BFD, VRF, VLAN, LAG, VXLAN/EVPN including symmetric IRB and multihoming, MPLS L3VPN, SR-MPLS, SRv6, VRRP, and GRE tunnels). **Minor changes and improvements** diff --git a/docs/release/26.08.md b/docs/release/26.08.md new file mode 100644 index 0000000000..aa4b9421ae --- /dev/null +++ b/docs/release/26.08.md @@ -0,0 +1,17 @@ +# Changes in Release 26.08 + +```eval_rst +.. contents:: Table of Contents + :depth: 2 + :local: +``` + +(release-26.08)= +## New Functionality + +* SONiC can run under *containerlab* with the community `docker-sonic-vs` image. The `sonic` device + inherits the FRR device and gains a **clab** deployment alongside the existing libvirt box, so the + FRR control-plane modules are available on SONiC: OSPFv2/OSPFv3, IS-IS, BGP (with the + **bgp.session**, **bgp.policy**, **bgp.originate**, **bgp.domain** and **ebgp.multihop** plugins), + RIPv2, BFD, VRF, VLAN, LAG, VXLAN/EVPN, MPLS L3VPN, SR-MPLS, SRv6, VRRP and GRE tunnels. See + [](labs-sonic-clab) for how to obtain the container image. From bd08f07724e82bbafd3a0c40b61430f34ab1323f Mon Sep 17 00:00:00 2001 From: RocNet Date: Fri, 31 Jul 2026 15:21:45 +0000 Subject: [PATCH 15/23] chore(sonic): remove the dead sonic_clab validate module and stale references netsim/validate/sonic_clab.py was named for a device that no longer exists, so it could never be selected: validation for the sonic device resolves through ansible_network_os (frr) to netsim/validate/frr.py, which is what the 181-test run actually used. Dead code that reads as live. Also updates comments in the container templates that still pointed at sonic_clab.yml, sonic_clab.initial.j2, initial/sonic_clab.j2 and tests/integration/platform/sonic_clab/ -- all of which moved or were removed in the restructure. The only surviving mentions are the caveats/labs anchor names and one deliberate historical note in sonic.yml explaining what changed. --- netsim/ansible/tasks/deploy-config/sonic-clab.yml | 2 +- netsim/ansible/templates/initial/sonic-clab.j2 | 10 +++++----- netsim/ansible/templates/vlan/sonic-clab.j2 | 2 +- netsim/ansible/templates/vxlan/sonic-clab.j2 | 6 +++--- netsim/validate/sonic_clab.py | 1 - 5 files changed, 10 insertions(+), 11 deletions(-) delete mode 100644 netsim/validate/sonic_clab.py diff --git a/netsim/ansible/tasks/deploy-config/sonic-clab.yml b/netsim/ansible/tasks/deploy-config/sonic-clab.yml index da3a421fae..8d14a3167f 100644 --- a/netsim/ansible/tasks/deploy-config/sonic-clab.yml +++ b/netsim/ansible/tasks/deploy-config/sonic-clab.yml @@ -1,7 +1,7 @@ # SONiC (containerlab, docker-sonic-vs) config deploy. # # Reached over Ansible's 'docker' connection plugin (ansible_connection: docker in -# sonic_clab.yml), so 'command'/'shell' here already execute *inside* the node container -- +# sonic.yml), so 'command'/'shell' here already execute *inside* the node container -- # no docker exec wrapping needed. # # SONiC config is either a bash script of 'config' CLI commands (initial: starts with diff --git a/netsim/ansible/templates/initial/sonic-clab.j2 b/netsim/ansible/templates/initial/sonic-clab.j2 index 1559d21ce8..37e4dfd7ec 100644 --- a/netsim/ansible/templates/initial/sonic-clab.j2 +++ b/netsim/ansible/templates/initial/sonic-clab.j2 @@ -4,15 +4,15 @@ # SONiC (containerlab, docker-sonic-vs) initial configuration. # # Adapted from the package 'sonic' (libvirt) device's initial/sonic.j2, with changes forced -# by docker-sonic-vs's monolithic architecture (see sonic_clab.yml for why): +# by docker-sonic-vs's monolithic architecture (see sonic.yml for why): # * vtysh runs directly in this container -- no 'docker exec bgp' indirection. # * most FRR daemons ship disabled (=no in /etc/frr/daemons) and must be enabled here. # * config_db VLANs (`Vlan`) and PortChannels (`PortChannel`) must be created BEFORE any # SVI/aggregate is addressed. Those live in the vlan/lag module-init hooks -# (vlan|lag/sonic_clab.initial.j2), pulled in below via extra_module_initial() at exactly this +# (vlan|lag/sonic.initial.j2), pulled in below via extra_module_initial() at exactly this # ordering point -- the same mechanism VyOS uses for its VLAN bridge setup. # This script is deployed with 'command: bash /tmp/config.sh' over Ansible's docker connection -# (tasks/deploy-config/sonic_clab.yml), i.e. it already executes *inside* the node container. +# (tasks/deploy-config/sonic-clab.yml), i.e. it already executes *inside* the node container. # # CONNECTION MODEL: config deployment stays on ansible_connection: docker, not network_cli. # network_cli was evaluated and is not usable here: it requires a matching cliconf plugin, and @@ -57,7 +57,7 @@ config interface ipv6 disable use-link-local-only {{ l.ifname }} config hostname {{ inventory_hostname.replace("_","-") }} # # Create config_db PortChannels (lag) and VLANs (vlan) before any aggregate/SVI is addressed -# below. These live in the per-module init hooks lag/vlan/sonic_clab.initial.j2 and are pulled +# below. These live in the per-module init hooks lag/vlan sonic.initial.j2 and are pulled # in here, at the correct ordering point, by netlab's extra_module_initial() macro. # {{ extra_module_initial(['lag','vlan']) }} @@ -73,7 +73,7 @@ fi own template creates and addresses them directly, and the `config` CLI only accepts Ethernet/PortChannel/Vlan/Loopback names anyway. A VNI-backed SVI (EVPN symmetric-IRB) is the same story: its config_db `Vlan` is intentionally never created above, and - vlan/vxlan sonic_clab.j2 build/address the real bridge netdev later (vxlan runs after vrf, + vlan/vxlan sonic-clab.j2 build/address the real bridge netdev later (vxlan runs after vrf, which runs after initial) -- addressing it here would hit "Vlan does not exist". #} {% set _vni_svi = l.type == 'svi' and l.vlan.name|default('') in vlans and vlans[l.vlan.name].vni is defined %} {% if l.ipv4 is defined and (l.ipv4 is string or l._parent_ipv4 is defined) and l.type != 'tunnel' and not _vni_svi %} diff --git a/netsim/ansible/templates/vlan/sonic-clab.j2 b/netsim/ansible/templates/vlan/sonic-clab.j2 index 5659727d0d..f907dcb155 100644 --- a/netsim/ansible/templates/vlan/sonic-clab.j2 +++ b/netsim/ansible/templates/vlan/sonic-clab.j2 @@ -1,7 +1,7 @@ #!/bin/bash # # SONiC vlan module — switchport membership via the config CLI (access and trunk). -# VLANs themselves (config_db `Vlan`) are created by initial/sonic_clab.j2 BEFORE the +# VLANs themselves (config_db `Vlan`) are created by initial/sonic-clab.j2 BEFORE the # SVIs get addressed; this template adds port membership, THEN syncs it to the kernel itself # (see the sync block below) -- docker-sonic-vs's vlanmgrd races at boot and often never # mirrors VLAN_MEMBER rows written during the first deploy (a vlanmgrd restart also WIPES diff --git a/netsim/ansible/templates/vxlan/sonic-clab.j2 b/netsim/ansible/templates/vxlan/sonic-clab.j2 index 2f80e1efd9..69738dd449 100644 --- a/netsim/ansible/templates/vxlan/sonic-clab.j2 +++ b/netsim/ansible/templates/vxlan/sonic-clab.j2 @@ -15,13 +15,13 @@ # is the same path proven live between two VS nodes (VNI up, remote VTEP learned, iBGP # l2vpn evpn Established, host-to-host ping across the tunnel). # -# config_db is intentionally bypassed for VNI-backed VLANs: initial/sonic.j2 skips -# `config vlan add` for them and vlan/sonic.j2 skips their member-add, so nothing competes +# config_db is intentionally bypassed for VNI-backed VLANs: initial/sonic-clab.j2 skips +# `config vlan add` for them and vlan/sonic-clab.j2 skips their member-add, so nothing competes # for the access ports here. # # EVPN symmetric-IRB: an `irb` VNI-backed vlan with a `vrf` gets its L2VNI # bridge NAMED "Vlan" (capital, matching svi_interface_name) instead of the plain -# bridge-mode "vlan" -- vlan/sonic_clab.j2 pre-creates that exact netdev (empty) BEFORE +# bridge-mode "vlan" -- vlan/sonic-clab.j2 pre-creates that exact netdev (empty) BEFORE # vrf deploys, so vrf's generic frr.data-plane.j2 (`ip link set Vlan master `, # matched by ifname against netlab's own SVI interface record) can enslave it; this # template (running AFTER vrf, vxlan.config_after includes vrf) then attaches the vxlan diff --git a/netsim/validate/sonic_clab.py b/netsim/validate/sonic_clab.py deleted file mode 100644 index 365f417d72..0000000000 --- a/netsim/validate/sonic_clab.py +++ /dev/null @@ -1 +0,0 @@ -from netsim.validate.frr import * # sonic_clab runs FRR/vtysh -- reuse FRR validation From 95e1ded5ba7c87d6cc5539653ff6f383903f16ce Mon Sep 17 00:00:00 2001 From: RocNet Date: Fri, 31 Jul 2026 15:22:18 +0000 Subject: [PATCH 16/23] docs(sonic): one platform-table row for SONiC, not two The table lists one row per device name and sonic_clab no longer exists, so the separate containerlab row named a device that cannot be selected. SONiC is now a single device with two deployments -- the same shape as the Arista row covering vEOS and cEOS. Support level left at minimal, matching the device file: the container deployment is well exercised but the libvirt VM is not, and a single row cannot express that split. The caveats and labs documents carry the distinction. --- docs/platforms.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/platforms.md b/docs/platforms.md index a8db0069d8..38305bb805 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -47,8 +47,7 @@ | Nokia SR OS [❗](caveats-sros) | sros | best effort[^SROSBE] | | Nokia SR-SIM [❗](caveats-srsim) | srsim | full | | OpenBSD [❗](caveats-openbsd) | openbsd | best effort | -| Sonic [❗](caveats-sonic) | sonic | minimal | -| Sonic (containerlab) [❗](caveats-sonic-clab) | sonic_clab | best effort | +| Sonic (VM and containers) [❗](caveats-sonic) | sonic | minimal | | VyOS 1.4 [❗](caveats-vyos) | vyos | full | [^SROSBE]: With the launch of the Nokia SR SIM, we stopped running integration tests for the SR-OS VM, assuming the behavior of the two products would be nearly identical. From 211ef138a0256775db91458b4c35fa581e381cb9 Mon Sep 17 00:00:00 2001 From: Jason Patterson Date: Fri, 31 Jul 2026 15:24:53 +0000 Subject: [PATCH 17/23] docs(sonic): correct the image-tag step in the download instructions The retag line tagged the image to itself, which does nothing. docker load restores whatever tag the artefact carries, so tell the reader to check it and retag only if it differs from what the device definition expects. --- docs/labs/sonic.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/labs/sonic.md b/docs/labs/sonic.md index b63a1dadca..8a3f25806c 100644 --- a/docs/labs/sonic.md +++ b/docs/labs/sonic.md @@ -43,9 +43,11 @@ rather than on a public registry, so you either download a build or make one: ``` gunzip docker-sonic-vs.gz docker load -i docker-sonic-vs - docker tag docker-sonic-vs:latest docker-sonic-vs:latest ``` + Check the tag `docker load` restored with **docker images**; retag it to + `docker-sonic-vs:latest` if it differs. + * **Build it yourself** from `sonic-buildimage`: ``` From b9e17705bc764c9d642dd86e93f8aa04ce945db3 Mon Sep 17 00:00:00 2001 From: Jason Patterson Date: Fri, 31 Jul 2026 15:41:11 +0000 Subject: [PATCH 18/23] docs(sonic): point at the actual source of the container image The previous text made sonic.software the primary source and said it indexes docker-sonic-vs.gz. It does not: it indexes SONiC installation images (sonic-vs.img), which is what the Vagrant box above is built from, not the container artefact. docker-sonic-vs.gz is published by the SONiC Azure build pipelines. Gives the pipeline URL and the navigation to the artefact, and links containerlab, which documents the same path for its sonic-vs kind. --- docs/labs/sonic.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/labs/sonic.md b/docs/labs/sonic.md index 8a3f25806c..6fe91586ca 100644 --- a/docs/labs/sonic.md +++ b/docs/labs/sonic.md @@ -36,9 +36,21 @@ netlab up -d sonic -p clab a build artefact of the [sonic-buildimage](https://github.com/sonic-net/sonic-buildimage) project rather than on a public registry, so you either download a build or make one: -* **Download a published build.** The SONiC project publishes `docker-sonic-vs.gz` from its Azure - build pipelines; [sonic.software](https://sonic.software/) indexes those builds and links to the - artefacts for each branch. Pick the `vs` platform, download `docker-sonic-vs.gz`, then: +* **Download a published build.** The image is published as an artefact of the SONiC Azure build + pipelines. From : + + * scroll to the bottom of the pipeline list, where the **vs** platform is listed; + * pick a branch (for example `202405`) and open **Build History**; + * choose the latest build whose *Result* is successful and open **Artifacts**; + * open the artifact, scroll to **target/docker-sonic-vs.gz**, and download it. + + *containerlab* documents the same path for its + [`sonic-vs` kind](https://containerlab.dev/manual/kinds/sonic-vs/), which uses this image. + [sonic.software](https://sonic.software/) is an unofficial index that is sometimes offered as an + alternative, but it carries SONiC *installation* images (`sonic-vs.img`, used for the Vagrant box + above) rather than the container artefact. + + Then load it: ``` gunzip docker-sonic-vs.gz From 8fa1135cacf436625efa990e2c00d58dd78e79bb Mon Sep 17 00:00:00 2001 From: Ivan Pepelnjak Date: Sat, 1 Aug 2026 08:49:10 +0200 Subject: [PATCH 19/23] Initial documentation nits (more to come ;) --- docs/caveats.md | 39 +++++++----------- docs/labs/sonic.md | 96 ++++++++++++++++++++----------------------- docs/platforms.md | 4 +- docs/release/26.08.md | 10 ++--- 4 files changed, 63 insertions(+), 86 deletions(-) diff --git a/docs/caveats.md b/docs/caveats.md index 1ab437556a..f1359c2724 100644 --- a/docs/caveats.md +++ b/docs/caveats.md @@ -650,34 +650,23 @@ See also [](caveats-sros) caveats for further details. * _netlab_ RIPv2/RIPng template implements route redistribution, but only for static and connected prefixes * The device role on nodes with a loopback interface is automatically changed to **router** (contrary to most other network devices, OpenBSD does not allow you to reach non-connected IP addresses unless the IPv4/IPv6 forwarding is enabled). -(caveats-sonic)= -## Sonic +(caveats-sonic-vm)= +## SONiC Virtual Machine -* Sonic implementation was tested with Azure sonic-vs VM image (release 2023-11) with FRR running in a container. Other Sonic distributions might use different approaches that would require significant modifications to the configuration deployment process. -* BGP is the only routing protocol running on Azure Sonic. The choice is hardcoded in FRR compilation flags. -* You cannot use IBGP as there's no IGP protocol to resolve IBGP next hops, unless you believe in running IBGP over EBGP. -* The Azure Sonic VM image has to be started with a preconfigured BGP AS number (specified in **config_db.json**); otherwise, it does not start the FRR container. That BGP process is removed during the initial BGP configuration and replaced with the actual BGP AS number specified in the lab topology. -* _netlab_ configures BGP on Sonic through vtysh, not through **config_db**. +* SONiC implementation was tested with Azure `sonic-vs` VM image (release 2023-11) with FRR running in a container. Other SONiC distributions might use different approaches that would require significant modifications to the configuration deployment process. +* BGP is the only routing protocol running on Azure SONiC. The choice is hardcoded in FRR compilation flags. +* You cannot use IBGP, as there's no IGP protocol to resolve IBGP next hops, unless you believe in running IBGP over EBGP. +* The Azure SONiC VM image has to be started with a preconfigured BGP AS number (specified in **config_db.json**); otherwise, it does not start the FRR container. That BGP process is removed during the initial BGP configuration and replaced with the actual BGP AS number specified in the lab topology. +* _netlab_ configures BGP on SONiC through vtysh, not through **config_db**. (caveats-sonic-clab)= -## Sonic (containerlab) - -The `sonic` device also runs under *containerlab* with the community `docker-sonic-vs` image; see -[](labs-sonic-clab) for how to obtain it and how the two deployments differ. - -* Configuration is deployed over Ansible's `docker` connection plugin (`docker exec`), not - `network_cli`. -* `docker-sonic-vs` ships `sshd` but starts neither it nor a login user; the initial configuration - bootstraps both for interactive access only. -* `srv6` is control-plane and kernel-plane only: the locator and End/End.X SIDs are advertised in - the IS-IS LSDB and installed as kernel `seg6local` routes, but the end-to-end SRv6 datapath does - not resolve -- the same open item as FRR/IS-IS SRv6 on other platforms. - -Everything else this image cannot do is turned off in `features` rather than described here, so -*netlab* refuses the topology instead of building one the platform cannot serve: STP, DHCP -relay/client, MLAG, anycast gateways, the host and bridge node roles, RFC 9234 BGP roles, and two -routing-policy operations (`policy.set.community.extended`, `policy.match.nexthop`) that no -integration test exercises on this platform. +## SONiC Container + +The `sonic` device also runs under *containerlab* with the community `docker-sonic-vs` image; see [](build-sonic-container) for how to obtain it and how the two deployments differ. + +* Configuration is deployed with **docker exec** commands, not over an SSH session. +* `docker-sonic-vs` ships `sshd` but does not start it. +* `srv6` is control-plane and kernel-plane only: the locator and End/End.X SIDs are advertised in the IS-IS LSDB and installed as kernel `seg6local` routes, but the end-to-end SRv6 datapath does not resolve -- the same open item as FRR/IS-IS SRv6 on other platforms. (caveats-vyos)= ## VyOS diff --git a/docs/labs/sonic.md b/docs/labs/sonic.md index 6fe91586ca..800577b033 100644 --- a/docs/labs/sonic.md +++ b/docs/labs/sonic.md @@ -1,17 +1,22 @@ (build-sonic)= -# Building a Sonic Vagrant Libvirt Box +# Preparing a SONiC Box or Container -You can use the **netlab libvirt package** command to build a Sonic Vagrant box for a Sonic virtual machine: +_netlab_ supports SONiC running in a VM or in a container. Unfortunately, there's no ready-to-use Vagrant box or Docker container that you could pull down from a public registry; you have to [build the box](build-sonic-box) or [download and install the container](build-sonic-container) manually. -* Download the **sonic-vs.img.gz** image from Azure or [sonic.software](https://sonic.software/) into an empty directory. +(build-sonic-box)= +## Building a SONiC Vagrant Box + +You can use the **netlab libvirt package** command to build a SONiC Vagrant box for a SONiC virtual machine: + +* Download the **sonic-vs.img.gz** image from Azure or [SONiC.software](https://SONiC.software/) into an empty directory. * Unzip image with **gunzip _gz-file-name_**. * Execute **netlab libvirt package sonic _img-file-name_** and follow the instructions ```{warning} -If you're using a *‌netlab* release older than 1.8.2, or if you're using a Linux distribution other than Ubuntu, please [read the box-building caveats first](libvirt-box-caveats.md). +If you're using a Linux distribution other than Ubuntu, please [read the box-building caveats first](libvirt-box-caveats.md). ``` -## Initial Device Configuration +### Initial Device Configuration During the box-building process, you might have to disable ZTP or clean up the initial configuration database. The **netlab libvirt config sonic** command displays the build recipe: @@ -20,67 +25,54 @@ During the box-building process, you might have to disable ZTP or clean up the i :literal: ``` -(labs-sonic-clab)= -## Using the SoNIC containers - -SONiC also runs under *containerlab* using the community `docker-sonic-vs` image. There is no box -to build -- select the `clab` provider: +(build-sonic-container)= +## Downloading and Installing SONiC containers -``` -netlab up -d sonic -p clab -``` +SONiC also runs under *containerlab* using the community `docker-sonic-vs` container. The container is published as +a build artifact of the [sonic-buildimage](https://github.com/sonic-net/sonic-buildimage) project, so you either download a build or make one. -### Getting the container image +### Download a Published Build -*netlab* does not ship or distribute `docker-sonic-vs`; you supply it yourself. It is published as -a build artefact of the [sonic-buildimage](https://github.com/sonic-net/sonic-buildimage) project -rather than on a public registry, so you either download a build or make one: +The SONiC container image is published as an artifact of the SONiC Azure build pipelines. From : -* **Download a published build.** The image is published as an artefact of the SONiC Azure build - pipelines. From : +* Scroll to the bottom of the pipeline list, where the **vs** platform is listed; +* Pick a branch (for example `202405`) and open **Build History**; +* Choose the latest build whose *Result* is successful and open **Artifacts**; +* Open the artifact, scroll to **target/docker-sonic-vs.gz**, and download it. - * scroll to the bottom of the pipeline list, where the **vs** platform is listed; - * pick a branch (for example `202405`) and open **Build History**; - * choose the latest build whose *Result* is successful and open **Artifacts**; - * open the artifact, scroll to **target/docker-sonic-vs.gz**, and download it. +*containerlab* documents the same path for its +[`sonic-vs` kind](https://containerlab.dev/manual/kinds/sonic-vs/), which uses this image. [sonic.software](https://SONiC.software/) is an unofficial index that is sometimes offered as an alternative, but it carries SONiC *installation* images (`sonic-vs.img`, used for the Vagrant box +above) rather than the container artifact. - *containerlab* documents the same path for its - [`sonic-vs` kind](https://containerlab.dev/manual/kinds/sonic-vs/), which uses this image. - [sonic.software](https://sonic.software/) is an unofficial index that is sometimes offered as an - alternative, but it carries SONiC *installation* images (`sonic-vs.img`, used for the Vagrant box - above) rather than the container artefact. +After downloading the container, unpack and load it: - Then load it: +``` +gunzip docker-sonic-vs.gz +docker load -i docker-sonic-vs +``` - ``` - gunzip docker-sonic-vs.gz - docker load -i docker-sonic-vs - ``` +Check the tag `docker load` restored with **docker images**; retag it to `docker-sonic-vs:latest` if necessary. - Check the tag `docker load` restored with **docker images**; retag it to - `docker-sonic-vs:latest` if it differs. +### Build a SONiC Container -* **Build it yourself** from `sonic-buildimage`: +Use this process in an empty directory to build a SONiC container from the `sonic-buildimage` repository: - ``` - git clone --recurse-submodules https://github.com/sonic-net/sonic-buildimage.git - cd sonic-buildimage - make init - make configure PLATFORM=vs - make target/docker-sonic-vs.gz - docker load -i target/docker-sonic-vs.gz - ``` +``` +git clone --recurse-submodules https://github.com/sonic-net/sonic-buildimage.git +cd sonic-buildimage +make init +make configure PLATFORM=vs +make target/docker-sonic-vs.gz +docker load -i target/docker-sonic-vs.gz +``` The device definition expects the image to be tagged **`docker-sonic-vs:latest`**; override -`clab.image` in your topology if yours is tagged differently. This submission was tested with -`docker-sonic-vs:latest`. +`defaults.devices.sonic.clab.image` in your topology if yours is tagged differently. + +### How SONiC Container Works -### How it works +`docker-sonic-vs` is a single monolithic container running FRR (`vtysh`) (unlike the VM, which runs FRR in a nested `bgp` container). The container does not start the SSH daemon; *netlab* pushes device configuration and runs validation with **docker exec** commands. -`docker-sonic-vs` is a single monolithic container running FRR (`vtysh`), unlike the VM above which -runs FRR in a nested `bgp` container. *netlab* pushes configuration and runs validation over -**docker exec** -- the image starts no `sshd`. The device inherits the `frr` device, so the FRR -control-plane templates are used directly and only the container-specific parts -(`/sonic-clab.j2`) are SONiC's own. +The device inherits from the `frr` device and uses FRR control-plane configuration templates. See the [SONiC caveats](caveats-sonic-clab) for what is and is not supported. diff --git a/docs/platforms.md b/docs/platforms.md index 38305bb805..53bf721a2a 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -47,7 +47,7 @@ | Nokia SR OS [❗](caveats-sros) | sros | best effort[^SROSBE] | | Nokia SR-SIM [❗](caveats-srsim) | srsim | full | | OpenBSD [❗](caveats-openbsd) | openbsd | best effort | -| Sonic (VM and containers) [❗](caveats-sonic) | sonic | minimal | +| SONiC [❗](caveats-sonic-vm) | sonic | minimal | | VyOS 1.4 [❗](caveats-vyos) | vyos | full | [^SROSBE]: With the launch of the Nokia SR SIM, we stopped running integration tests for the SR-OS VM, assuming the behavior of the two products would be nearly identical. @@ -144,7 +144,7 @@ You cannot use all supported network devices with all virtualization providers. | Nokia SR OS | ❌ | ✅ | | Nokia SR-SIM | ❌ | ✅ | | OpenBSD | [✅](build-openbsd) | [✅](clab-vrnetlab) | -| Sonic | [✅](build-sonic) | ❌ | +| SONiC | [✅](build-sonic-box) | [✅](build-sonic-container) | | VyOS | ✅ | ✅[❗](caveats-vyos) | **Note:** diff --git a/docs/release/26.08.md b/docs/release/26.08.md index 153788ce2d..bce22c74e6 100644 --- a/docs/release/26.08.md +++ b/docs/release/26.08.md @@ -9,13 +9,6 @@ (release-26.08)= ## New Functionality -* SONiC can run under *containerlab* with the community `docker-sonic-vs` image. The `sonic` device - inherits the FRR device and gains a **clab** deployment alongside the existing libvirt box, so the - FRR control-plane modules are available on SONiC: OSPFv2/OSPFv3, IS-IS, BGP (with the - **bgp.session**, **bgp.policy**, **bgp.originate**, **bgp.domain** and **ebgp.multihop** plugins), - RIPv2, BFD, VRF, VLAN, LAG, VXLAN/EVPN, MPLS L3VPN, SR-MPLS, SRv6, VRRP and GRE tunnels. See - [](labs-sonic-clab) for how to obtain the container image. - **Minor improvements** * Something new @@ -26,6 +19,9 @@ Arista EOS: * Something new +SONiC: +* SONiC can run under *containerlab* with the community `docker-sonic-vs` container ([details](build-sonic-container), [caveats](caveats-sonic-clab)). + (release-26.08-device-fixes)= ## Fixes in Device Settings and Configuration Templates From 48c59d0b3c08258810610f8666e32ec8741098b2 Mon Sep 17 00:00:00 2001 From: Ivan Pepelnjak Date: Sat, 1 Aug 2026 10:05:52 +0200 Subject: [PATCH 20/23] Make initial config working with recent SONiC builds * sudo command is no longer available; modify /etc/sudoers.d only when it exists * retry the 'config hostname' command until redis is ready * always restart FRR daemon (it starts in a weird state) --- .../ansible/templates/initial/sonic-clab.j2 | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/netsim/ansible/templates/initial/sonic-clab.j2 b/netsim/ansible/templates/initial/sonic-clab.j2 index 37e4dfd7ec..dc24e2cc7a 100644 --- a/netsim/ansible/templates/initial/sonic-clab.j2 +++ b/netsim/ansible/templates/initial/sonic-clab.j2 @@ -33,11 +33,22 @@ set -x {% include 'linux/bash_profile.j2' +%} {% include 'linux/hosts.j2' +%} # +# Try to set hostname through Redis, wait for up to 30 seconds to succeed +# +timeout 30 bash -c ' + while ! config hostname {{ inventory_hostname.replace("_","-") }}; do + echo "Waiting for redis to start" + sleep 1 + done +' +# # Enable sshd + an 'admin' user (idempotent) -- interactive access only, see note above. # id admin >/dev/null 2>&1 || useradd -m -s /bin/bash -G sudo admin echo 'admin:{{ ansible_ssh_pass | default("YourPaSsWoRd") }}' | chpasswd -echo 'admin ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/admin +if which sudo; then + echo 'admin ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/admin +fi mkdir -p /run/sshd pgrep -x sshd >/dev/null || /usr/sbin/sshd # @@ -51,16 +62,14 @@ pgrep -x sshd >/dev/null || /usr/sbin/sshd config interface ipv6 disable use-link-local-only {{ l.ifname }} {% endif %} {% endfor %} -# -# Configure interfaces -# -config hostname {{ inventory_hostname.replace("_","-") }} -# # Create config_db PortChannels (lag) and VLANs (vlan) before any aggregate/SVI is addressed # below. These live in the per-module init hooks lag/vlan sonic.initial.j2 and are pulled # in here, at the correct ordering point, by netlab's extra_module_initial() macro. # {{ extra_module_initial(['lag','vlan']) }} +# +# Configure interfaces +# {% for l in netlab_interfaces %} {% if l.type in ['loopback'] %} if ip link|grep {{ l.ifname }}; then @@ -138,20 +147,10 @@ if grep -qE '^({{ daemons_needed|unique|join('|') }})=no' /etc/frr/daemons; then {% for frr_d in daemons_needed|unique %} sed -i 's/^{{ frr_d }}=no/{{ frr_d }}=yes/' /etc/frr/daemons {% endfor %} - supervisorctl restart frr >/dev/null 2>&1 || service frr restart >/dev/null 2>&1 || true - sleep 12 fi {% endif %} -# -# Rest of initial configuration done through VTYSH -# Make sure it's ready -# -if vtysh -c 'show running' >/dev/null; then - echo vtysh is ready -else - echo 'giving vtysh some more time :(' - sleep 5 -fi +supervisorctl restart frr >/dev/null 2>&1 || service frr restart >/dev/null 2>&1 || true +sleep 2 # # And now let's configure the interfaces # From 9f85ff41ac61f85aaf66011ec1a9123edb4eaf5c Mon Sep 17 00:00:00 2001 From: Ivan Pepelnjak Date: Sat, 1 Aug 2026 13:55:43 +0200 Subject: [PATCH 21/23] Disable features that do not work or don't have dataplane config --- netsim/devices/sonic.yml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/netsim/devices/sonic.yml b/netsim/devices/sonic.yml index c5d6461051..21b1f72a8a 100644 --- a/netsim/devices/sonic.yml +++ b/netsim/devices/sonic.yml @@ -54,6 +54,14 @@ group_vars: # features: stp: false # config-plane only, no stpd daemon -- no port blocking to verify + services: false # no DNS client + tunnel: false # no tunnels + sr: false # no segment routing + mpls: false # no MPLS data plane + vrf: false # should be configured with SONiC commands (if available) + ripv2: false # no RIPv2 support + srv6: false # no SRv6 data plane + bgp: # RFC 9234 BGP roles. Inherited from FRR and NOT verified here: bgp.policy/70-bgp-roles gets # 19 of its 22 checks, and the three that fail are all "The prefix ... should not be in the @@ -62,6 +70,10 @@ features: # they are supposed to drive does not happen. Disabled pending verification rather than # claimed on the strength of the checks that do pass. role: false + vrf_local_as: false # Feature not supported by the old FRR release used in SONiC + password: false + timers: false + initial: # FRR declares roles [ host, router, bridge ]. Neither extra role works here: # initial/06-bridge fails to deploy at all (one failed task, node never configured), and @@ -124,8 +136,8 @@ clab: # vtysh. Measured: the node then fails initial configuration with # "RuntimeError: Unable to connect to redis - Connection refused". # SONiC deploys over the Ansible docker connection instead. - netlab_config_mode: - netlab_default_shebang: +# netlab_config_mode: +# netlab_default_shebang: netlab_mgmt_vrf: False # docker-sonic-vs ships most FRR daemons disabled in /etc/frr/daemons; initial/sonic-clab.j2 # enables the ones the configured modules need. watchfrr, zebra and staticd are always @@ -157,7 +169,7 @@ clab: # MLAG topology: netsim/modules/lag.py gates the peer-link machinery on # "lag.mlag OR evpn.multihoming.lag", and evpn.multihoming.lag is still declared below. mlag: False - vxlan: true + vxlan: false # Use SONiC commands to configure it evpn: transport: [ vxlan, mpls ] irb: true @@ -165,7 +177,8 @@ clab: lag: true initial: collect: true # ansible_connection: docker unblocks this (no sshd needed) - config_mode: # clear FRR's [ sh ]: SONiC uses the Ansible deploy task + services: false # no DNS client +# config_mode: # clear FRR's [ sh ]: SONiC uses the Ansible deploy task external: image: none graphite.icon: router From 463892d74138bf5adaa2eecbefa5b91d5e1dda49 Mon Sep 17 00:00:00 2001 From: Ivan Pepelnjak Date: Sat, 1 Aug 2026 15:40:09 +0200 Subject: [PATCH 22/23] Final touches * Device reload does not work * Configurable RA parameters do not work as they should on the old FRR version * Caveats in device definition are displayed in the "netlab show devices" command. What you had in that field belongs partly to "caveats.md", and mostly to "implementation notes". --- netsim/devices/sonic.yml | 45 +++++++++------------------------------- 1 file changed, 10 insertions(+), 35 deletions(-) diff --git a/netsim/devices/sonic.yml b/netsim/devices/sonic.yml index 21b1f72a8a..d555f8d88d 100644 --- a/netsim/devices/sonic.yml +++ b/netsim/devices/sonic.yml @@ -13,33 +13,6 @@ parent: frr description: SONiC support: level: minimal - caveats: - - >- - SONiC runs FRR, so this device inherits the FRR device and disables what SONiC does not - support rather than re-declaring the FRR feature set. Anything not disabled below is served - by the FRR templates. - - >- - Genuinely unsupported, not just untested (each live-probed): STP -- config-plane only, there - is no stpd daemon so there is no real port-blocking behaviour; DHCP relay/client -- - docker-sonic-vs ships no dhcrelay/dhcp6relay binary at all; mlag.vtep (anycast VTEP) -- - the config plane renders and the underlay comes up, but the active-active EVPN overlay is - platform-blocked (no mclagd), so the datapath is 100% loss. - - >- - Two routing-policy operations FRR supports are disabled here because they are not exercised - by any integration test on this platform and were therefore never verified: - policy.set.community.extended and policy.match.nexthop. They are disabled explicitly rather - than left to inheritance -- under a parent device, silence is a claim. - - >- - This device has BOTH a libvirt and a clab deployment, where the previous sonic_clab device had - clab only. A topology that does not pin a provider can therefore select libvirt and fail with - "KVM is not installed or does not include kvm-ok utility" on a container-only host -- - tests/integration/initial/08-ra does exactly that. Run SONiC topologies with "-p clab" (or set - the provider in the topology) unless the VM image is actually available. This is a property of - having two deployments, not a missing feature, so nothing is disabled for it. - - >- - The libvirt (VM) deployment is far less tested than the container. VLAN, LAG, VXLAN and EVPN - are enabled only for clab, because their configuration is built with config_db/redis-cli - sequences that have only ever been exercised on docker-sonic-vs. interface_name: Ethernet{ifindex * 4} ifindex_offset: 0 mgmt_if: eth0 @@ -80,6 +53,8 @@ features: # initial/05-host reaches 3 of 5 checks then fails the IPv6 ping and the RA default-route # check. Only the router role is claimed. roles: [ router ] + ra: false # Missing RA features in the old FRR release used in SONiC + reload: false gateway: # FRR declares anycast; SONiC does not deliver it. gateway/01-anycast reaches 10 of 11 checks # and then fails ping_dup -- duplicate packets, i.e. both gateways answering, which is the @@ -155,20 +130,20 @@ clab: # Verified on docker-sonic-vs and therefore re-enabled for the container only. # features: - vlan: - model: l3-switch - svi_interface_name: "Vlan{vlan}" - subif_name: "{ifname}.{subif_index}" - native_routed: true - lag: - passive: true + vlan: False +# model: l3-switch +# svi_interface_name: "Vlan{vlan}" +# subif_name: "{ifname}.{subif_index}" +# native_routed: true + lag: False +# passive: true # MLAG does not work: docker-sonic-vs has no mclagd, so the pair never forms # (lag/10-mlag: "X1 cannot establish a LAG with both switches in MLAG pair", then the # datapath is lost). Declared False rather than described in the caveats -- a feature that # does not work belongs turned off. Note this does not on its own make netlab refuse an # MLAG topology: netsim/modules/lag.py gates the peer-link machinery on # "lag.mlag OR evpn.multihoming.lag", and evpn.multihoming.lag is still declared below. - mlag: False +# mlag: False vxlan: false # Use SONiC commands to configure it evpn: transport: [ vxlan, mpls ] From 35dfad1f5f73b7423cebd55f3678ab7efdd4340c Mon Sep 17 00:00:00 2001 From: RocNet Date: Sat, 1 Aug 2026 12:58:05 +0000 Subject: [PATCH 23/23] feat(sonic): commit to script-based deployment, drop the Ansible deploy path The redis-readiness gate added in 48c59d0b3c removes the reason SONiC was overriding FRR's deployment mechanism, so stop overriding it: * remove the commented-out netlab_config_mode / netlab_default_shebang lines rather than leaving them commented, and rewrite the comment block that explained why we did NOT inherit FRR's mechanism -- it now explains that we do, and records the redis gate that made it possible * correct the same stale claim at the top of initial/sonic-clab.j2 * document what tasks/deploy-config/sonic-clab.yml is still for. It is NOT dead: 'netlab config' clears netlab_config_mode (cli/config.py) and reloads custom templates through the Ansible playbook, and the lookup order puts the VM's deploy-config/sonic.yml (become + 'docker exec bgp') ahead of frr.yml, so the container needs its own task. Both branches verified live. * delete the dead readiness-check/sonic_clab.yml -- the device is 'sonic', so that filename has never been matched by the readiness lookup Verified live on docker-sonic-vs (vlan/33-vlan-irb-trunk, 9/9 checks): the DUT reports "Script: initial,vlan" and the container holds /etc/config/01-initial.sh and /etc/config/02-vlan.sh. --- .../tasks/deploy-config/sonic-clab.yml | 10 +++++++ .../tasks/readiness-check/sonic_clab.yml | 9 ------- .../ansible/templates/initial/sonic-clab.j2 | 14 ++++++---- netsim/devices/sonic.yml | 27 ++++++++++++------- 4 files changed, 36 insertions(+), 24 deletions(-) delete mode 100644 netsim/ansible/tasks/readiness-check/sonic_clab.yml diff --git a/netsim/ansible/tasks/deploy-config/sonic-clab.yml b/netsim/ansible/tasks/deploy-config/sonic-clab.yml index 8d14a3167f..c4953a34ee 100644 --- a/netsim/ansible/tasks/deploy-config/sonic-clab.yml +++ b/netsim/ansible/tasks/deploy-config/sonic-clab.yml @@ -1,5 +1,15 @@ # SONiC (containerlab, docker-sonic-vs) config deploy. # +# NOT used by 'netlab up'/'netlab initial' -- those deploy through netlab's native "sh" +# config mode inherited from FRR (see the clab: block in devices/sonic.yml). This task is +# the Ansible fallback that 'netlab config' still needs: cli/config.py clears +# netlab_config_mode before reloading a custom configuration template, which routes the +# deployment back through the Ansible playbook. +# +# It cannot simply fall through to deploy-config/frr.yml: the lookup order puts the VM's +# deploy-config/sonic.yml (which uses 'become' and 'docker exec bgp') ahead of it, and the +# container has neither sudo nor a separate bgp container. +# # Reached over Ansible's 'docker' connection plugin (ansible_connection: docker in # sonic.yml), so 'command'/'shell' here already execute *inside* the node container -- # no docker exec wrapping needed. diff --git a/netsim/ansible/tasks/readiness-check/sonic_clab.yml b/netsim/ansible/tasks/readiness-check/sonic_clab.yml deleted file mode 100644 index 8fa5df2b21..0000000000 --- a/netsim/ansible/tasks/readiness-check/sonic_clab.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Wait for docker-sonic-vs's vtysh (and the underlying config_db redis) to answer before we try -# to push any configuration. Reached over the 'docker' connection plugin -- no SSH involved. -- name: Wait for SONiC vtysh to become ready - command: vtysh -c 'show version' - register: sonic_ready - until: sonic_ready.rc == 0 - retries: 40 - delay: 3 - changed_when: false diff --git a/netsim/ansible/templates/initial/sonic-clab.j2 b/netsim/ansible/templates/initial/sonic-clab.j2 index dc24e2cc7a..ffcb3b1a27 100644 --- a/netsim/ansible/templates/initial/sonic-clab.j2 +++ b/netsim/ansible/templates/initial/sonic-clab.j2 @@ -11,11 +11,15 @@ # SVI/aggregate is addressed. Those live in the vlan/lag module-init hooks # (vlan|lag/sonic.initial.j2), pulled in below via extra_module_initial() at exactly this # ordering point -- the same mechanism VyOS uses for its VLAN bridge setup. -# This script is deployed with 'command: bash /tmp/config.sh' over Ansible's docker connection -# (tasks/deploy-config/sonic-clab.yml), i.e. it already executes *inside* the node container. -# -# CONNECTION MODEL: config deployment stays on ansible_connection: docker, not network_cli. -# network_cli was evaluated and is not usable here: it requires a matching cliconf plugin, and +# This script is deployed with netlab's native "sh" config mode (inherited from the FRR parent +# device): netlab bind-mounts it into the container as /etc/config/01-initial.sh and runs it with +# 'docker exec', so it already executes *inside* the node container. No Ansible in the deployment +# path. The 'config hostname' retry below is what makes that safe -- it blocks until config_db +# answers, instead of assuming the container has finished booting. +# +# CONNECTION MODEL: what still goes through Ansible -- configuration collection, validation, and +# the 'netlab config' custom-template reload -- stays on ansible_connection: docker, not +# network_cli. network_cli was evaluated and is not usable here: it requires a cliconf plugin, and # the only SONiC cliconf shipped by Ansible (dellemc.enterprise_sonic) targets Dell's licensed # "Management Framework" CLI (sonic-cli/klish) on Dell PowerSwitch hardware -- docker-sonic-vs # has no such binary (verified: /etc/passwd has no admin user, no sonic-cli/klish anywhere in diff --git a/netsim/devices/sonic.yml b/netsim/devices/sonic.yml index d555f8d88d..76b6c85916 100644 --- a/netsim/devices/sonic.yml +++ b/netsim/devices/sonic.yml @@ -105,15 +105,20 @@ clab: ansible_ssh_pass: YourPaSsWoRd # bootstrapped 'admin' login, interactive use only netlab_ready: [ ansible ] # skip the SSH readiness wait (sshd isn't up yet) netlab_show_command: [ vtysh, -c, 'show $@' ] - # Do NOT inherit FRR's deployment mechanism. FRR's clab group_vars select netlab's native - # "sh" config mode with a '#!/usr/bin/vtysh -f' shebang, which bypasses - # tasks/deploy-config/sonic-clab.yml entirely and feeds SONiC's bash 'config' script to - # vtysh. Measured: the node then fails initial configuration with - # "RuntimeError: Unable to connect to redis - Connection refused". - # SONiC deploys over the Ansible docker connection instead. -# netlab_config_mode: -# netlab_default_shebang: - netlab_mgmt_vrf: False + # Inherit FRR's deployment mechanism -- netlab's native "sh" config mode + # (netlab_config_mode / netlab_default_shebang come from frr.yml). Each rendered snippet + # is bind-mounted into the container as /etc/config/NN-.sh and run with + # "docker exec", so the shebang picks the interpreter: the SONiC-specific templates open + # with '#!/bin/bash' and drive SONiC's 'config' CLI, and everything inherited from FRR + # gets FRR's '#!/usr/bin/vtysh -f' default and goes to vtysh. No Ansible in the + # deployment path -- the failures are also far easier to read this way. + # + # This only works because initial/sonic-clab.j2 waits for config_db: 'config hostname' is + # retried until redis answers. Without that gate the script ran before the container had + # finished booting and died with + # "RuntimeError: Unable to connect to redis - Connection refused", which is what made the + # Ansible deploy task look necessary. + netlab_mgmt_vrf: False # FRR sets it; SONiC has no management VRF # docker-sonic-vs ships most FRR daemons disabled in /etc/frr/daemons; initial/sonic-clab.j2 # enables the ones the configured modules need. watchfrr, zebra and staticd are always # started by FRR itself and are deliberately absent from this map. @@ -152,8 +157,10 @@ clab: lag: true initial: collect: true # ansible_connection: docker unblocks this (no sshd needed) + # Repeated from the global features above: FRR declares clab.features.services as a dict, + # and the provider block wins over the global value, so 'services: false' has to be + # restated here to survive the merge. services: false # no DNS client -# config_mode: # clear FRR's [ sh ]: SONiC uses the Ansible deploy task external: image: none graphite.icon: router