3434 get_connected_interface_ipv4_address ,
3535)
3636from .cache import get_cached_device_interfaces
37- from .constants import BGP_AF_L2VPN_EVPN_TAG , DEFAULT_SONIC_ROLES
37+ from .constants import BGP_AF_L2VPN_EVPN_TAG , DEFAULT_SONIC_ROLES , DEFAULT_EVPN_SYSTEM_MAC , DEFAULT_SAG_MAC
3838
3939# Global cache for NTP servers to avoid multiple queries
4040_ntp_servers_cache = None
@@ -73,16 +73,7 @@ def generate_sonic_config(device, hwsku, device_as_mapping=None, config_version=
7373 # Get port channel configuration from NetBox first (needed by get_connected_interfaces)
7474 portchannel_info = detect_port_channels (device )
7575
76- # Resolve evpn_system_mac early so it is validated once and passed explicitly later
77- _raw_evpn_mac = device .config_context .get ("_evpn_system_mac" )
78- evpn_system_mac = (
79- _raw_evpn_mac if isinstance (_raw_evpn_mac , str ) and _raw_evpn_mac else None
80- )
81- if _raw_evpn_mac and not evpn_system_mac :
82- logger .warning (
83- f"Device { device .name } : '_evpn_system_mac' in config_context is not a valid string"
84- f" (got { type (_raw_evpn_mac ).__name__ !r} ), ignoring"
85- )
76+ evpn_system_mac = DEFAULT_EVPN_SYSTEM_MAC
8677
8778 # Get connected interfaces to determine admin_status
8879 connected_interfaces , connected_portchannels = get_connected_interfaces (
@@ -270,7 +261,8 @@ def generate_sonic_config(device, hwsku, device_as_mapping=None, config_version=
270261 config ["MGMT_INTERFACE" ]["eth0" ] = {"admin_status" : "up" }
271262 config ["MGMT_INTERFACE" ][f"eth0|{ oob_ip } /{ prefix_len } " ] = {}
272263 metalbox_ip = _get_metalbox_ip_for_device (device )
273- config ["STATIC_ROUTE" ] = {}
264+ if "STATIC_ROUTE" not in config :
265+ config ["STATIC_ROUTE" ] = {}
274266 config ["STATIC_ROUTE" ]["mgmt|0.0.0.0/0" ] = {"nexthop" : metalbox_ip }
275267 else :
276268 oob_ip = None
@@ -1756,7 +1748,11 @@ def _add_vlan_configuration(config, vlan_info, netbox_interfaces, device):
17561748
17571749 if addresses or anycast_addresses :
17581750 # Add the VLAN interface base entry
1759- config ["VLAN_INTERFACE" ][vlan_name ] = {"admin_status" : "up" }
1751+ vlan_iface_entry = {"admin_status" : "up" }
1752+ vrf_name = interface_data .get ("vrf_name" )
1753+ if vrf_name :
1754+ vlan_iface_entry ["vrf_name" ] = vrf_name
1755+ config ["VLAN_INTERFACE" ][vlan_name ] = vlan_iface_entry
17601756
17611757 # Add regular IP configuration for each address (IPv4 and IPv6)
17621758 for address in addresses :
@@ -1786,20 +1782,40 @@ def _add_vlan_configuration(config, vlan_info, netbox_interfaces, device):
17861782 config ["SAG" ][f"{ vlan_name } |IPv6" ] = {"gwip" : ipv6_anycast }
17871783
17881784 if sag_enabled :
1789- gwmac = device .config_context .get ("_sag_gwmac" )
1790- if not gwmac :
1791- raise ValueError (
1792- f"Device { device .name } has SAG anycast addresses but no '_sag_gwmac' "
1793- "defined in its config context"
1794- )
17951785 if "SAG_GLOBAL" not in config :
17961786 config ["SAG_GLOBAL" ] = {}
17971787 config ["SAG_GLOBAL" ]["IP" ] = {
17981788 "IPv4" : "enable" ,
17991789 "IPv6" : "enable" ,
1800- "gwmac" : gwmac ,
1790+ "gwmac" : DEFAULT_SAG_MAC ,
18011791 }
18021792
1793+ # Add static default routes per VRF from sonic_parameters on VLAN interfaces
1794+ for vid , interface_data in vlan_info ["vlan_interfaces" ].items ():
1795+ vrf_name = interface_data .get ("vrf_name" )
1796+ if not vrf_name :
1797+ continue
1798+ logger .debug (f"Adding static default routes for VRF { vrf_name } (Vlan{ vid } )" )
1799+
1800+ default_route_ipv4 = interface_data .get ("default_route_ipv4" )
1801+ default_route_ipv6 = interface_data .get ("default_route_ipv6" )
1802+ if not default_route_ipv4 and not default_route_ipv6 :
1803+ continue
1804+ if "STATIC_ROUTE" not in config :
1805+ config ["STATIC_ROUTE" ] = {}
1806+ if default_route_ipv4 :
1807+ config ["STATIC_ROUTE" ][f"{ vrf_name } |0.0.0.0/0" ] = {
1808+ "nexthop" : default_route_ipv4
1809+ }
1810+ logger .debug (
1811+ f"Added static IPv4 default route for VRF { vrf_name } via { default_route_ipv4 } (Vlan{ vid } )"
1812+ )
1813+ if default_route_ipv6 :
1814+ config ["STATIC_ROUTE" ][f"{ vrf_name } |::/0" ] = {"nexthop" : default_route_ipv6 }
1815+ logger .debug (
1816+ f"Added static IPv6 default route for VRF { vrf_name } via { default_route_ipv6 } (Vlan{ vid } )"
1817+ )
1818+
18031819
18041820def _add_loopback_configuration (config , loopback_info ):
18051821 """Add Loopback configuration from NetBox."""
@@ -2139,7 +2155,7 @@ def _add_vrf_configuration(config, vrf_info, vlan_info, netbox_interfaces):
21392155 )
21402156
21412157
2142- def _add_portchannel_configuration (config , portchannel_info , evpn_system_mac = None ):
2158+ def _add_portchannel_configuration (config , portchannel_info , evpn_system_mac ):
21432159 """Add port channel configuration from NetBox."""
21442160 if portchannel_info ["portchannels" ]:
21452161 for pc_name , pc_data in portchannel_info ["portchannels" ].items ():
@@ -2154,6 +2170,18 @@ def _add_portchannel_configuration(config, portchannel_info, evpn_system_mac=Non
21542170 pc_config ["system_mac" ] = evpn_system_mac
21552171 config ["PORTCHANNEL" ][pc_name ] = pc_config
21562172
2173+ # Add EVPN_ETHERNET_SEGMENT configuration for EVPN multihoming LAGs
2174+ if pc_data .get ("evpn_lag" ):
2175+ if "EVPN_ETHERNET_SEGMENT" not in config :
2176+ config ["EVPN_ETHERNET_SEGMENT" ] = {}
2177+ config ["EVPN_ETHERNET_SEGMENT" ][pc_name ] = {
2178+ "esi" : "AUTO" ,
2179+ "esi_type" : "TYPE_3_MAC_BASED" ,
2180+ "ifname" : pc_name ,
2181+ }
2182+ if "EVPN_MH_GLOBAL" not in config :
2183+ config ["EVPN_MH_GLOBAL" ] = {"default" : {"startup_delay" : "300" }}
2184+
21572185 # Add PORTCHANNEL_INTERFACE configuration to enable IPv6 link-local
21582186 config ["PORTCHANNEL_INTERFACE" ][pc_name ] = {
21592187 "ipv6_use_link_local_only" : "enable"
0 commit comments