Skip to content

Unit tests for osism/tasks/conductor/sonic/config_generator.py — BGP/VLAN/Loopback/VRF #2223

@berendt

Description

@berendt

Background

Follow-up to #2192 (foundation) and PR #2193 (pytest + Zuul infrastructure). Part of Tier 3 (#2199). Companion issue to the config_generator.py orchestrator and ports issues. Covers BGP, VLAN, Loopback, and VRF helpers in osism/tasks/conductor/sonic/config_generator.py.

Scope

Add tests/unit/tasks/conductor/sonic/test_config_generator_bgp_vlan_vrf.py covering the helpers below in osism/tasks/conductor/sonic/config_generator.py.

Test targets

_add_bgp_configurations(config, connected_interfaces, connected_portchannels, portchannel_info, device, device_as_mapping=None, interface_ips=None, netbox_interfaces=None, transfer_ips=None, netbox=None, vlan_info=None, vrf_info=None)config_generator.py:914

Patch osism.tasks.conductor.sonic.config_generator.get_connected_device_for_sonic_interface and ...get_connected_interface_ipv4_address.

BGP_NEIGHBOR_AF for connected interfaces (lines 951–1039)

  • Untagged VLAN member → excluded (the VLAN-SVI branch handles it instead)
  • Direct IPv4 (no transfer role) → excluded from BGP, log message recorded
  • No direct IPv4 → ipv4_unicast + ipv6_unicast entries under default|<port>|...
  • Transfer-role IPv4 → ipv4_unicast only (v6only=false semantics deferred to BGP_NEIGHBOR test below)
  • Switch-to-switch (connected_device.role.slugDEFAULT_SONIC_ROLES) → l2vpn_evpn entry added
  • Non-switch connected device but interface has the BGP_AF_L2VPN_EVPN_TAG tag → l2vpn_evpn entry added
  • Non-switch + no tag → l2vpn_evpn skipped
  • Non-default VRF (via vrf_info["interface_vrf_mapping"]) → l2vpn_evpn never added
  • Port channel member (portchannel_info["member_mapping"]) → skipped from interface BGP loop

BGP_NEIGHBOR_AF for port channels (lines 1040–1079)

  • Each connected_portchannel → ipv4_unicast + ipv6_unicast entries
  • Switch connection → l2vpn_evpn added
  • Non-switch → l2vpn_evpn skipped
  • Non-default VRF → l2vpn_evpn never added

BGP_NEIGHBOR for connected interfaces (lines 1081–1179)

  • Direct IPv4 only → not added (excluded from BGP detection log)
  • Untagged VLAN member → skipped
  • No direct IPv4, no peer IP → key is default|<port>, peer_type="external", v6only="true" (default VRF only)
  • No direct IPv4, peer IP returned → key is default|<peer_ip>, local_addr set from interface_ips or transfer_ips
  • Transfer-role IPv4 → v6only="false"
  • Non-default VRF → v6only key not present
  • _determine_peer_type returns "internal" (matching AS) → propagated as peer_type

BGP_NEIGHBOR for port channels (lines 1181–1229)

  • Default VRF, no peer IP → key default|<pc_name>, peer_type="external", v6only="true"
  • Default VRF, peer IP → key uses peer IP; local_addr not set (port channels don't have NetBox interface entries)
  • Non-default VRF → v6only not present
  • Switch connection → _determine_peer_type evaluated

VLAN-interface BGP (lines 1234–1339)

  • VLAN with one untagged member and a peer IPv4 → BGP_NEIGHBOR[default|<peer_ip>] and BGP_NEIGHBOR_AF[default|<peer_ip>|ipv4_unicast] added
  • Same peer IP appearing on two untagged members → only one entry (deduped via peer_ips_found)
  • VLAN without addresses / no untagged members → skipped
  • No untagged member produces a peer IPv4 → warning logged, no entries added
  • VLAN interface name (f"Vlan{vid}") used to look up VRF → non-default VRF works
  • Member NetBox name not in netbox_interfaces → skipped, debug log

_get_connected_device_for_interface(device, interface_name)config_generator.py:1342

One delegation test verifying it returns the value from get_connected_device_for_sonic_interface.

_determine_peer_type(local_device, connected_device, device_as_mapping=None)config_generator.py:1355

Patch calculate_local_asn_from_ipv4.

  • Both devices in device_as_mapping with same AS → "internal"
  • Both devices with different AS → "external"
  • local_device not in mapping but has primary_ip4 → AS computed via patched function
  • connected_device not in mapping but has primary_ip4 → AS computed
  • Either device has no primary_ip4 and not in mapping → AS None, returns "external"
  • calculate_local_asn_from_ipv4 raises → caught, returns "external" (debug log)

_add_vlan_configuration(config, vlan_info, netbox_interfaces, device)config_generator.py:1700

  • VLAN with two members (one tagged, one untagged) → VLAN[Vlan100] populated with members list (sorted by SONiC name); VLAN_MEMBER entries created with the right tagging_mode
  • Default vlanid is str(vid); name falls back to f"Vlan{vid}" if NetBox name is empty
  • NetBox interface name without a SONiC mapping → warning logged, member skipped
  • VLAN interface (SVI) with addresses → VLAN_INTERFACE[Vlan<vid>] = {"admin_status": "up"} and one entry per address (VLAN_INTERFACE[Vlan<vid>|<address>] = {})
  • VLAN interface without addresses → no SVI entry

_add_loopback_configuration(config, loopback_info)config_generator.py:1760

  • One Loopback with one IPv4 address → LOOPBACK[name]={admin_status:"up"}, LOOPBACK_INTERFACE[name]={}, LOOPBACK_INTERFACE[name|addr]={}
  • Loopback0 with IPv4 → BGP_GLOBALS_AF_NETWORK[default|ipv4_unicast|<addr>]={}
  • Loopback0 with IPv6 → BGP_GLOBALS_AF_NETWORK[default|ipv6_unicast|<addr>]={}
  • Non-Loopback0 → BGP_GLOBALS_AF_NETWORK not touched
  • Invalid IP string → warning logged, skipped

_get_vrf_info(device)config_generator.py:1793

Patch get_cached_device_interfaces and convert_netbox_interface_to_sonic.

  • Interface without VRF → skipped
  • Interface VRF name vrf42 (no RD) → Vrf42 definition with table_id=42
  • VRF name vrfStorage + numeric RD 2001vrfStorage definition with vni=2001
  • VRF name vrf2001 + textual RD vrfStorage → SONiC name vrfStorage, vni=2001
  • VRF with no name match and a textual RD → uses RD as SONiC name
  • VRF with no name match and no RD → warning logged, skipped
  • Multiple interfaces in the same VRF → VRF defined once, both interfaces in interface_vrf_mapping
  • Per-interface exception → warning logged, processing continues
  • Top-level exception (cached interfaces raise) → warning logged, returns {"vrfs": {}, "interface_vrf_mapping": {}}

_add_vrf_configuration(config, vrf_info, netbox_interfaces)config_generator.py:1937

  • VRF with vniVRF (with fallback="false"), VLAN[Vlan<vni>], VLAN_INTERFACE[Vlan<vni>]={vrf_name:...}, BGP_GLOBALS_AF[<vrf>|ipv4_unicast], BGP_GLOBALS_AF[<vrf>|l2vpn_evpn] with import/export RTs and route-distinguisher, BGP_GLOBALS_ROUTE_ADVERTISE[<vrf>|L2VPN_EVPN|IPV4_UNICAST], BGP_GLOBALS_ROUTE_ADVERTISE[<vrf>|L2VPN_EVPN|IPV6_UNICAST], ROUTE_REDISTRIBUTE[<vrf>|connected|bgp|ipv4]
  • VRF with table_id only → VRF[<vrf>]={vrf_table_id: <id>}
  • VRF with neither → VRF[<vrf>]={}
  • BGP_GLOBALS["default"] exists → deep-copied to BGP_GLOBALS[<vrf>]
  • Multiple VRFs with VNI → VXLAN_TUNNEL/VXLAN_EVPN_NVO/VXLAN_TUNNEL_MAP entries created (one map per VRF)
  • No VRF with VNI → VXLAN sections not created
  • Interface in vrf_info["interface_vrf_mapping"] and in config["INTERFACE"]vrf_name set on the interface
  • Interface in vrf_info["interface_vrf_mapping"] and in config["PORTCHANNEL_INTERFACE"]vrf_name set on the port channel
  • Interface in mapping but in neither section → debug log, no error

Mocking hints

  • Build vrf_info, vlan_info, netbox_interfaces, interface_ips, transfer_ips inline as plain dicts.
  • Initialize config with all keys the helpers mutate (BGP_NEIGHBOR, BGP_NEIGHBOR_AF, VLAN, VLAN_MEMBER, VLAN_INTERFACE, LOOPBACK, LOOPBACK_INTERFACE, BGP_GLOBALS_AF_NETWORK, VRF, VXLAN_TUNNEL, VXLAN_EVPN_NVO, VXLAN_TUNNEL_MAP, BGP_GLOBALS_AF, BGP_GLOBALS_ROUTE_ADVERTISE, INTERFACE, PORTCHANNEL_INTERFACE).
  • device.config_context.get(...) is consulted in _add_log_server_configuration and _add_snmp_configuration (covered in the orchestrator issue), not here.
  • Construct interfaces with SimpleNamespace and a nested vrf=SimpleNamespace(name="...", rd="...").
  • BGP_AF_L2VPN_EVPN_TAG comes from osism.tasks.conductor.sonic.constants — mock interfaces with tags=[SimpleNamespace(slug=BGP_AF_L2VPN_EVPN_TAG)].

Definition of Done

  • tests/unit/tasks/conductor/sonic/test_config_generator_bgp_vlan_vrf.py created
  • All listed cases covered
  • pytest --cov=osism.tasks.conductor.sonic.config_generator for the targeted functions ≥ 85 %
  • pipenv run pytest tests/unit/tasks/conductor/sonic/test_config_generator_bgp_vlan_vrf.py passes locally
  • flake8, mypy, python-black remain green
  • Zuul job python-osism-unit-tests passes

Dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions