From 6efb61471d67891aa529c954b7ac716cb95441d6 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Wed, 19 Jun 2019 03:46:34 -0700 Subject: [PATCH 1/7] FRR config templates support VRF 1. BGP_NEIGHBOR key extend to vrf_name|ip_address 2. BGP_PEER_RANGE add an attribute vrf_name 3. Add table STATIC_ROUTE: a. key is prefix or vrf_name|prefix b. fields are nexthop, distance, nexthop-vrf c. nexthop is mandatory, its value can be ip_address or if_alias --- dockers/docker-fpm-frr/bgpd.conf.j2 | 98 ++++++++++++++---- dockers/docker-fpm-frr/frr.conf.j2 | 149 +++++++++++++++++++++++---- dockers/docker-fpm-frr/zebra.conf.j2 | 53 ++++++++++ 3 files changed, 261 insertions(+), 39 deletions(-) diff --git a/dockers/docker-fpm-frr/bgpd.conf.j2 b/dockers/docker-fpm-frr/bgpd.conf.j2 index 9153a2455010..76fd2c1c52dc 100644 --- a/dockers/docker-fpm-frr/bgpd.conf.j2 +++ b/dockers/docker-fpm-frr/bgpd.conf.j2 @@ -15,8 +15,9 @@ agentx ! enable password {# {{ en_passwd }} TODO: param needed #} {% endblock system_init %} ! +{% block bgp %} {% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} -{% block bgp_init %} +{# block bgp_init #} ! ! bgp multiple-instance ! @@ -24,7 +25,52 @@ route-map FROM_BGP_SPEAKER_V4 permit 10 ! route-map TO_BGP_SPEAKER_V4 deny 10 ! +{% set vrf_lo_intf = {} %} +{% for intf_key in LOOPBACK_INTERFACE %} +{% if intf_key is string() %} +{% set intf_vrf = 'global' %} +{% if LOOPBACK_INTERFACE[intf_key].has_key('vrf_name') %} +{% set intf_vrf = LOOPBACK_INTERFACE[intf_key]['vrf_name'] %} +{% endif %} +{% if vrf_lo_intf.has_key(intf_vrf) or vrf_lo_intf.update({intf_vrf:[]}) %} +{% endif %} +{% if vrf_lo_intf[intf_vrf].append(intf_key) %} +{% endif %} +{% endif %} +{% endfor %} +{% set vrf_bgp_nbr = {} %} +{% for nbr_key in BGP_NEIGHBOR %} +{% if nbr_key | length == 2 %} +{% set nbr_vrf = nbr_key[0] %} +{% set nbr_addr = nbr_key[1] %} +{% else %} +{% set nbr_vrf = 'global' %} +{% set nbr_addr = nbr_key %} +{% endif %} +{% if vrf_bgp_nbr.has_key(nbr_vrf) or vrf_bgp_nbr.update({nbr_vrf:{}}) %} +{% endif %} +{% if vrf_bgp_nbr[nbr_vrf].update({nbr_addr:BGP_NEIGHBOR[nbr_key]}) %} +{% endif %} +{% endfor %} +{% set vrf_bgp_pr = {} %} +{% for pr_key in BGP_PEER_RANGE %} +{% set pr_vrf = 'global' %} +{% if BGP_PEER_RANGE[pr_key].has_key('vrf_name') %} +{% set pr_vrf = BGP_PEER_RANGE[pr_key]['vrf_name'] %} +{% endif %} +{% if vrf_bgp_nbr.has_key(pr_vrf) or vrf_bgp_nbr.update({pr_vrf:{}}) %} +{% endif %} +{% if vrf_bgp_pr.has_key(pr_vrf) or vrf_bgp_pr.update({pr_vrf:{}}) %} +{% endif %} +{% if vrf_bgp_pr[pr_vrf].update({pr_key:BGP_PEER_RANGE[pr_key]}) %} +{% endif %} +{% endfor %} +{% for vrf in vrf_bgp_nbr %} +{% if vrf == 'global' %} router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} +{% else %} +router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} vrf {{ vrf }} +{% endif %} bgp log-neighbor-changes bgp bestpath as-path multipath-relax no bgp default ipv4-unicast @@ -33,25 +79,33 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} bgp graceful-restart preserve-fw-state {% endif %} +{% set vrf_lo0 = "" %} +{% if vrf_lo_intf[vrf] | length > 0 %} +{% set vrf_lo0 = vrf_lo_intf[vrf][0] %} +{% endif %} {% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if prefix | ipv4 and name == 'Loopback0' %} +{% if prefix | ipv4 and name == vrf_lo0 %} bgp router-id {{ prefix | ip }} {% endif %} {% endfor %} {# advertise loopback #} {% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if prefix | ipv4 and name == 'Loopback0' %} +{% if prefix | ipv4 and name == vrf_lo0 %} network {{ prefix | ip }}/32 -{% elif prefix | ipv6 and name == 'Loopback0' %} +{% elif prefix | ipv6 and name == vrf_lo0 %} address-family ipv6 - network {{ prefix | ip }}/64 + network {{ prefix | ip }}/128 exit-address-family {% endif %} {% endfor %} -{% endblock bgp_init %} -{% endif %} -{% block vlan_advertisement %} +{# endblock bgp_init #} +{# block vlan_advertisement #} {% for (name, prefix) in VLAN_INTERFACE|pfx_filter %} +{% set vlan_intf_vrf = 'global' %} +{% if VLAN_INTERFACE[name].has_key('vrf_name') %} +{% set vlan_intf_vrf = VLAN_INTERFACE[name]['vrf_name'] %} +{% endif %} +{% if vlan_intf_vrf == vrf %} {% if prefix | ipv4 %} network {{ prefix }} {% elif prefix | ipv6 %} @@ -59,10 +113,11 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} network {{ prefix }} exit-address-family {% endif %} +{% endif %} {% endfor %} -{% endblock vlan_advertisement %} -{% block bgp_sessions %} -{% for neighbor_addr, bgp_session in BGP_NEIGHBOR.iteritems() %} +{# endblock vlan_advertisement #} +{# block bgp_sessions #} +{% for neighbor_addr, bgp_session in vrf_bgp_nbr[vrf].iteritems() %} {% if bgp_session['asn'] | int != 0 %} neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} @@ -111,10 +166,10 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% endif %} {% endif %} {% endfor %} -{% endblock bgp_sessions %} -{% block bgp_peers_with_range %} -{% if BGP_PEER_RANGE %} -{% for bgp_peer in BGP_PEER_RANGE.values() %} +{# endblock bgp_sessions #} +{# block bgp_peers_with_range #} +{% if vrf_bgp_pr.has_key(vrf) %} +{% for bgp_peer in vrf_bgp_pr[vrf].values() %} neighbor {{ bgp_peer['name'] }} peer-group neighbor {{ bgp_peer['name'] }} passive {% if bgp_peer['peer_asn'] is defined %} @@ -127,8 +182,12 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% if bgp_peer['src_address'] is defined %} neighbor {{ bgp_peer['name'] }} update-source {{ bgp_peer['src_address'] | ip }} {% else %} +{% set vrf_lo1 = "" %} +{% if vrf_lo_intf[vrf] | length > 1 %} +{% set vrf_lo1 = vrf_lo_intf[vrf][1] %} +{% endif %} {% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if name == 'Loopback1' %} +{% if name == vrf_lo1 %} neighbor {{ bgp_peer['name'] }} update-source {{ prefix | ip }} {% endif %} {% endfor %} @@ -148,14 +207,13 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} exit-address-family {% endfor %} {% endif %} -{% endblock bgp_peers_with_range %} -! -{% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} -maximum-paths 64 +{# endblock bgp_peers_with_range #} ! +{% endfor %} route-map ISOLATE permit 10 set as-path prepend {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% endif %} +{% endblock bgp %} ! route-map set-next-hop-global-v6 permit 10 set ipv6 next-hop prefer-global diff --git a/dockers/docker-fpm-frr/frr.conf.j2 b/dockers/docker-fpm-frr/frr.conf.j2 index 297f79b2d302..60da9285e03d 100644 --- a/dockers/docker-fpm-frr/frr.conf.j2 +++ b/dockers/docker-fpm-frr/frr.conf.j2 @@ -30,14 +30,67 @@ link-detect {% endblock interfaces %} ! {% block default_route %} +{% if MGMT_INTERFACE %} ! set static default route to mgmt gateway as a backup to learned default {% for (name, prefix) in MGMT_INTERFACE|pfx_filter %} {% if prefix | ipv4 %} ip route 0.0.0.0/0 {{ MGMT_INTERFACE[(name, prefix)]['gwaddr'] }} 200 {% endif %} {% endfor %} +{% endif %} {% endblock default_route %} +{% block static_route %} +{% if STATIC_ROUTE %} +{% set glb_rt = {} %} +{% set vrf_rt = {} %} +{% for rt_key in STATIC_ROUTE %} +{% if rt_key is not string() %} +{% if vrf_rt.has_key(rt_key[0]) or vrf_rt.update({rt_key[0]:{}}) %} +{% endif %} +{% if vrf_rt[rt_key[0]].update({rt_key[1]:STATIC_ROUTE[rt_key]}) %} +{% endif %} +{% else %} +{% if glb_rt.update({rt_key:STATIC_ROUTE[rt_key]}) %} +{% endif %} +{% endif %} +{% endfor %} +{% for rt_key, rt_attr in glb_rt.iteritems() %} +{% if rt_attr.has_key('distance') and rt_attr['distance'] | int != 1 %} +{% if rt_attr.has_key('nexthop-vrf') %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} +{% endif %} +{% else %} +{% if rt_attr.has_key('nexthop-vrf') %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} +{% endif %} +{% endif %} +{% endfor %} +! +{% for vrf, rt in vrf_rt.iteritems() %} +vrf {{ vrf }} +{% for rt_key, rt_attr in rt.iteritems() %} +{% if rt_attr.has_key('distance') and rt_attr['distance'] | int != 1 %} +{% if rt_attr.has_key('nexthop-vrf') %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} +{% endif %} +{% else %} +{% if rt_attr.has_key('nexthop-vrf') %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} +{% endif %} +{% endif %} +{% endfor %} ! +{% endfor %} +{% endif %} +{% endblock static_route %} {% block source_loopback %} {% set lo_ipv4_addrs = [] %} {% set lo_ipv6_addrs = [] %} @@ -71,8 +124,9 @@ ipv6 protocol bgp route-map RM_SET_SRC6 {% endif %} {% endblock source_loopback %} ! +{% block bgp %} {% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} -{% block bgp_init %} +{# block bgp_init #} ! ! bgp multiple-instance ! @@ -80,7 +134,52 @@ route-map FROM_BGP_SPEAKER_V4 permit 10 ! route-map TO_BGP_SPEAKER_V4 deny 10 ! +{% set vrf_lo_intf = {} %} +{% for intf_key in LOOPBACK_INTERFACE %} +{% if intf_key is string() %} +{% set intf_vrf = 'global' %} +{% if LOOPBACK_INTERFACE[intf_key].has_key('vrf_name') %} +{% set intf_vrf = LOOPBACK_INTERFACE[intf_key]['vrf_name'] %} +{% endif %} +{% if vrf_lo_intf.has_key(intf_vrf) or vrf_lo_intf.update({intf_vrf:[]}) %} +{% endif %} +{% if vrf_lo_intf[intf_vrf].append(intf_key) %} +{% endif %} +{% endif %} +{% endfor %} +{% set vrf_bgp_nbr = {} %} +{% for nbr_key in BGP_NEIGHBOR %} +{% if nbr_key | length == 2 %} +{% set nbr_vrf = nbr_key[0] %} +{% set nbr_addr = nbr_key[1] %} +{% else %} +{% set nbr_vrf = 'global' %} +{% set nbr_addr = nbr_key %} +{% endif %} +{% if vrf_bgp_nbr.has_key(nbr_vrf) or vrf_bgp_nbr.update({nbr_vrf:{}}) %} +{% endif %} +{% if vrf_bgp_nbr[nbr_vrf].update({nbr_addr:BGP_NEIGHBOR[nbr_key]}) %} +{% endif %} +{% endfor %} +{% set vrf_bgp_pr = {} %} +{% for pr_key in BGP_PEER_RANGE %} +{% set pr_vrf = 'global' %} +{% if BGP_PEER_RANGE[pr_key].has_key('vrf_name') %} +{% set pr_vrf = BGP_PEER_RANGE[pr_key]['vrf_name'] %} +{% endif %} +{% if vrf_bgp_nbr.has_key(pr_vrf) or vrf_bgp_nbr.update({pr_vrf:{}}) %} +{% endif %} +{% if vrf_bgp_pr.has_key(pr_vrf) or vrf_bgp_pr.update({pr_vrf:{}}) %} +{% endif %} +{% if vrf_bgp_pr[pr_vrf].update({pr_key:BGP_PEER_RANGE[pr_key]}) %} +{% endif %} +{% endfor %} +{% for vrf in vrf_bgp_nbr %} +{% if vrf == 'global' %} router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} +{% else %} +router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} vrf {{ vrf }} +{% endif %} bgp log-neighbor-changes bgp bestpath as-path multipath-relax no bgp default ipv4-unicast @@ -88,25 +187,33 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} bgp graceful-restart {% endif %} +{% set vrf_lo0 = "" %} +{% if vrf_lo_intf[vrf] | length > 0 %} +{% set vrf_lo0 = vrf_lo_intf[vrf][0] %} +{% endif %} {% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if prefix | ipv4 and name == 'Loopback0' %} +{% if prefix | ipv4 and name == vrf_lo0 %} bgp router-id {{ prefix | ip }} {% endif %} {% endfor %} {# advertise loopback #} {% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if prefix | ipv4 and name == 'Loopback0' %} +{% if prefix | ipv4 and name == vrf_lo0 %} network {{ prefix | ip }}/32 -{% elif prefix | ipv6 and name == 'Loopback0' %} +{% elif prefix | ipv6 and name == vrf_lo0 %} address-family ipv6 network {{ prefix | ip }}/128 exit-address-family {% endif %} {% endfor %} -{% endblock bgp_init %} -{% endif %} -{% block vlan_advertisement %} +{# endblock bgp_init #} +{# block vlan_advertisement #} {% for (name, prefix) in VLAN_INTERFACE|pfx_filter %} +{% set vlan_intf_vrf = 'global' %} +{% if VLAN_INTERFACE[name].has_key('vrf_name') %} +{% set vlan_intf_vrf = VLAN_INTERFACE[name]['vrf_name'] %} +{% endif %} +{% if vlan_intf_vrf == vrf %} {% if prefix | ipv4 %} network {{ prefix }} {% elif prefix | ipv6 %} @@ -114,10 +221,11 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} network {{ prefix }} exit-address-family {% endif %} +{% endif %} {% endfor %} -{% endblock vlan_advertisement %} -{% block bgp_sessions %} -{% for neighbor_addr, bgp_session in BGP_NEIGHBOR.iteritems() %} +{# endblock vlan_advertisement #} +{# block bgp_sessions #} +{% for neighbor_addr, bgp_session in vrf_bgp_nbr[vrf].iteritems() %} {% if bgp_session['asn'] | int != 0 %} neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} @@ -166,16 +274,20 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% endif %} {% endif %} {% endfor %} -{% endblock bgp_sessions %} -{% block bgp_peers_with_range %} -{% if BGP_PEER_RANGE %} -{% for bgp_peer in BGP_PEER_RANGE.values() %} +{# endblock bgp_sessions #} +{# block bgp_peers_with_range #} +{% if vrf_bgp_pr.has_key(vrf) %} +{% for bgp_peer in vrf_bgp_pr[vrf].values() %} neighbor {{ bgp_peer['name'] }} peer-group neighbor {{ bgp_peer['name'] }} passive neighbor {{ bgp_peer['name'] }} remote-as {{ deployment_id_asn_map[DEVICE_METADATA['localhost']['deployment_id']] }} neighbor {{ bgp_peer['name'] }} ebgp-multihop 255 +{% set vrf_lo1 = "" %} +{% if vrf_lo_intf[vrf] | length > 1 %} +{% set vrf_lo1 = vrf_lo_intf[vrf][1] %} +{% endif %} {% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if name == 'Loopback1' %} +{% if name == vrf_lo1 %} neighbor {{ bgp_peer['name'] }} update-source {{ prefix | ip }} {% endif %} {% endfor %} @@ -196,14 +308,13 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} exit-address-family {% endfor %} {% endif %} -{% endblock bgp_peers_with_range %} -! -{% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} -maximum-paths 64 +{# endblock bgp_peers_with_range #} ! +{% endfor %} route-map ISOLATE permit 10 set as-path prepend {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% endif %} +{% endblock bgp %} ! route-map set-next-hop-global-v6 permit 10 set ipv6 next-hop prefer-global diff --git a/dockers/docker-fpm-frr/zebra.conf.j2 b/dockers/docker-fpm-frr/zebra.conf.j2 index c0357eaed86a..d0183bd6a2a5 100644 --- a/dockers/docker-fpm-frr/zebra.conf.j2 +++ b/dockers/docker-fpm-frr/zebra.conf.j2 @@ -27,14 +27,67 @@ link-detect {% endblock interfaces %} ! {% block default_route %} +{% if MGMT_INTERFACE %} ! set static default route to mgmt gateway as a backup to learned default {% for (name, prefix) in MGMT_INTERFACE|pfx_filter %} {% if prefix | ipv4 %} ip route 0.0.0.0/0 {{ MGMT_INTERFACE[(name, prefix)]['gwaddr'] }} 200 {% endif %} {% endfor %} +{% endif %} {% endblock default_route %} +{% block static_route %} +{% if STATIC_ROUTE %} +{% set glb_rt = {} %} +{% set vrf_rt = {} %} +{% for rt_key in STATIC_ROUTE %} +{% if rt_key is not string() %} +{% if vrf_rt.has_key(rt_key[0]) or vrf_rt.update({rt_key[0]:{}}) %} +{% endif %} +{% if vrf_rt[rt_key[0]].update({rt_key[1]:STATIC_ROUTE[rt_key]}) %} +{% endif %} +{% else %} +{% if glb_rt.update({rt_key:STATIC_ROUTE[rt_key]}) %} +{% endif %} +{% endif %} +{% endfor %} +{% for rt_key, rt_attr in glb_rt.iteritems() %} +{% if rt_attr.has_key('distance') and rt_attr['distance'] | int != 1 %} +{% if rt_attr.has_key('nexthop-vrf') %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} +{% endif %} +{% else %} +{% if rt_attr.has_key('nexthop-vrf') %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} +{% endif %} +{% endif %} +{% endfor %} +! +{% for vrf, rt in vrf_rt.iteritems() %} +vrf {{ vrf }} +{% for rt_key, rt_attr in rt.iteritems() %} +{% if rt_attr.has_key('distance') and rt_attr['distance'] | int != 1 %} +{% if rt_attr.has_key('nexthop-vrf') %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} +{% endif %} +{% else %} +{% if rt_attr.has_key('nexthop-vrf') %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} +{% endif %} +{% endif %} +{% endfor %} ! +{% endfor %} +{% endif %} +{% endblock static_route %} {% block source_loopback %} {% set lo_ipv4_addrs = [] %} {% set lo_ipv6_addrs = [] %} From 8b460151a3f2993bcabf4b484aca170c36c4be6e Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Mon, 22 Jul 2019 18:47:16 -0700 Subject: [PATCH 2/7] Revise FRR FRR config templates support VRF 1. Compatible with old interface table, which has no name keys, to deal with config in .xml file 2. Sort all config and then output .conf file 3. Rollback advertise 64 prefix for lo ipv6 addresses 4. Add seperator between default route and static route --- dockers/docker-fpm-frr/bgpd.conf.j2 | 35 ++++++++-------- dockers/docker-fpm-frr/frr.conf.j2 | 40 ++++++++++--------- dockers/docker-fpm-frr/zebra.conf.j2 | 7 ++-- .../tests/sample_output/frr.conf | 38 +++++++++--------- 4 files changed, 63 insertions(+), 57 deletions(-) diff --git a/dockers/docker-fpm-frr/bgpd.conf.j2 b/dockers/docker-fpm-frr/bgpd.conf.j2 index 76fd2c1c52dc..6539d7ebfd23 100644 --- a/dockers/docker-fpm-frr/bgpd.conf.j2 +++ b/dockers/docker-fpm-frr/bgpd.conf.j2 @@ -26,18 +26,17 @@ route-map FROM_BGP_SPEAKER_V4 permit 10 route-map TO_BGP_SPEAKER_V4 deny 10 ! {% set vrf_lo_intf = {} %} -{% for intf_key in LOOPBACK_INTERFACE %} -{% if intf_key is string() %} -{% set intf_vrf = 'global' %} -{% if LOOPBACK_INTERFACE[intf_key].has_key('vrf_name') %} -{% set intf_vrf = LOOPBACK_INTERFACE[intf_key]['vrf_name'] %} -{% endif %} -{% if vrf_lo_intf.has_key(intf_vrf) or vrf_lo_intf.update({intf_vrf:[]}) %} -{% endif %} -{% if vrf_lo_intf[intf_vrf].append(intf_key) %} -{% endif %} +{% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} +{% set intf_vrf = 'global' %} +{% if LOOPBACK_INTERFACE.has_key(name) and LOOPBACK_INTERFACE[name].has_key('vrf_name') %} +{% set intf_vrf = LOOPBACK_INTERFACE[name]['vrf_name'] %} +{% endif %} +{% if vrf_lo_intf.has_key(intf_vrf) or vrf_lo_intf.update({intf_vrf:[]}) %} +{% endif %} +{% if name not in vrf_lo_intf[intf_vrf] and vrf_lo_intf[intf_vrf].append(name) %} {% endif %} {% endfor %} +{% set vrf_routers = [] %} {% set vrf_bgp_nbr = {} %} {% for nbr_key in BGP_NEIGHBOR %} {% if nbr_key | length == 2 %} @@ -47,6 +46,8 @@ route-map TO_BGP_SPEAKER_V4 deny 10 {% set nbr_vrf = 'global' %} {% set nbr_addr = nbr_key %} {% endif %} +{% if nbr_vrf in vrf_routers or vrf_routers.append(nbr_vrf) %} +{% endif %} {% if vrf_bgp_nbr.has_key(nbr_vrf) or vrf_bgp_nbr.update({nbr_vrf:{}}) %} {% endif %} {% if vrf_bgp_nbr[nbr_vrf].update({nbr_addr:BGP_NEIGHBOR[nbr_key]}) %} @@ -58,14 +59,14 @@ route-map TO_BGP_SPEAKER_V4 deny 10 {% if BGP_PEER_RANGE[pr_key].has_key('vrf_name') %} {% set pr_vrf = BGP_PEER_RANGE[pr_key]['vrf_name'] %} {% endif %} -{% if vrf_bgp_nbr.has_key(pr_vrf) or vrf_bgp_nbr.update({pr_vrf:{}}) %} +{% if pr_vrf not in vrf_routers and vrf_routers.append(pr_vrf) %} {% endif %} {% if vrf_bgp_pr.has_key(pr_vrf) or vrf_bgp_pr.update({pr_vrf:{}}) %} {% endif %} {% if vrf_bgp_pr[pr_vrf].update({pr_key:BGP_PEER_RANGE[pr_key]}) %} {% endif %} {% endfor %} -{% for vrf in vrf_bgp_nbr %} +{% for vrf in vrf_routers|sort %} {% if vrf == 'global' %} router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% else %} @@ -94,7 +95,7 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} vrf {{ vrf }} network {{ prefix | ip }}/32 {% elif prefix | ipv6 and name == vrf_lo0 %} address-family ipv6 - network {{ prefix | ip }}/128 + network {{ prefix | ip }}/64 exit-address-family {% endif %} {% endfor %} @@ -102,7 +103,7 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} vrf {{ vrf }} {# block vlan_advertisement #} {% for (name, prefix) in VLAN_INTERFACE|pfx_filter %} {% set vlan_intf_vrf = 'global' %} -{% if VLAN_INTERFACE[name].has_key('vrf_name') %} +{% if VLAN_INTERFACE.has_key(name) and VLAN_INTERFACE[name].has_key('vrf_name') %} {% set vlan_intf_vrf = VLAN_INTERFACE[name]['vrf_name'] %} {% endif %} {% if vlan_intf_vrf == vrf %} @@ -117,7 +118,8 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} vrf {{ vrf }} {% endfor %} {# endblock vlan_advertisement #} {# block bgp_sessions #} -{% for neighbor_addr, bgp_session in vrf_bgp_nbr[vrf].iteritems() %} +{% if vrf_bgp_nbr.has_key(vrf) %} +{% for neighbor_addr, bgp_session in vrf_bgp_nbr[vrf]|dictsort %} {% if bgp_session['asn'] | int != 0 %} neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} @@ -166,10 +168,11 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} vrf {{ vrf }} {% endif %} {% endif %} {% endfor %} +{% endif %} {# endblock bgp_sessions #} {# block bgp_peers_with_range #} {% if vrf_bgp_pr.has_key(vrf) %} -{% for bgp_peer in vrf_bgp_pr[vrf].values() %} +{% for peer_key, bgp_peer in vrf_bgp_pr[vrf]|dictsort %} neighbor {{ bgp_peer['name'] }} peer-group neighbor {{ bgp_peer['name'] }} passive {% if bgp_peer['peer_asn'] is defined %} diff --git a/dockers/docker-fpm-frr/frr.conf.j2 b/dockers/docker-fpm-frr/frr.conf.j2 index 60da9285e03d..6609f4de4a06 100644 --- a/dockers/docker-fpm-frr/frr.conf.j2 +++ b/dockers/docker-fpm-frr/frr.conf.j2 @@ -39,6 +39,7 @@ ip route 0.0.0.0/0 {{ MGMT_INTERFACE[(name, prefix)]['gwaddr'] }} 200 {% endfor %} {% endif %} {% endblock default_route %} +! {% block static_route %} {% if STATIC_ROUTE %} {% set glb_rt = {} %} @@ -54,7 +55,7 @@ ip route 0.0.0.0/0 {{ MGMT_INTERFACE[(name, prefix)]['gwaddr'] }} 200 {% endif %} {% endif %} {% endfor %} -{% for rt_key, rt_attr in glb_rt.iteritems() %} +{% for rt_key, rt_attr in glb_rt|dictsort %} {% if rt_attr.has_key('distance') and rt_attr['distance'] | int != 1 %} {% if rt_attr.has_key('nexthop-vrf') %} ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} @@ -70,9 +71,9 @@ ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {% endif %} {% endfor %} ! -{% for vrf, rt in vrf_rt.iteritems() %} +{% for vrf, rt in vrf_rt|dictsort %} vrf {{ vrf }} -{% for rt_key, rt_attr in rt.iteritems() %} +{% for rt_key, rt_attr in rt|dictsort %} {% if rt_attr.has_key('distance') and rt_attr['distance'] | int != 1 %} {% if rt_attr.has_key('nexthop-vrf') %} ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} @@ -135,18 +136,17 @@ route-map FROM_BGP_SPEAKER_V4 permit 10 route-map TO_BGP_SPEAKER_V4 deny 10 ! {% set vrf_lo_intf = {} %} -{% for intf_key in LOOPBACK_INTERFACE %} -{% if intf_key is string() %} -{% set intf_vrf = 'global' %} -{% if LOOPBACK_INTERFACE[intf_key].has_key('vrf_name') %} -{% set intf_vrf = LOOPBACK_INTERFACE[intf_key]['vrf_name'] %} -{% endif %} -{% if vrf_lo_intf.has_key(intf_vrf) or vrf_lo_intf.update({intf_vrf:[]}) %} -{% endif %} -{% if vrf_lo_intf[intf_vrf].append(intf_key) %} -{% endif %} +{% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} +{% set intf_vrf = 'global' %} +{% if LOOPBACK_INTERFACE.has_key(name) and LOOPBACK_INTERFACE[name].has_key('vrf_name') %} +{% set intf_vrf = LOOPBACK_INTERFACE[name]['vrf_name'] %} +{% endif %} +{% if vrf_lo_intf.has_key(intf_vrf) or vrf_lo_intf.update({intf_vrf:[]}) %} +{% endif %} +{% if name not in vrf_lo_intf[intf_vrf] and vrf_lo_intf[intf_vrf].append(name) %} {% endif %} {% endfor %} +{% set vrf_routers = [] %} {% set vrf_bgp_nbr = {} %} {% for nbr_key in BGP_NEIGHBOR %} {% if nbr_key | length == 2 %} @@ -156,6 +156,8 @@ route-map TO_BGP_SPEAKER_V4 deny 10 {% set nbr_vrf = 'global' %} {% set nbr_addr = nbr_key %} {% endif %} +{% if nbr_vrf in vrf_routers or vrf_routers.append(nbr_vrf) %} +{% endif %} {% if vrf_bgp_nbr.has_key(nbr_vrf) or vrf_bgp_nbr.update({nbr_vrf:{}}) %} {% endif %} {% if vrf_bgp_nbr[nbr_vrf].update({nbr_addr:BGP_NEIGHBOR[nbr_key]}) %} @@ -167,14 +169,14 @@ route-map TO_BGP_SPEAKER_V4 deny 10 {% if BGP_PEER_RANGE[pr_key].has_key('vrf_name') %} {% set pr_vrf = BGP_PEER_RANGE[pr_key]['vrf_name'] %} {% endif %} -{% if vrf_bgp_nbr.has_key(pr_vrf) or vrf_bgp_nbr.update({pr_vrf:{}}) %} +{% if pr_vrf not in vrf_routers and vrf_routers.append(pr_vrf) %} {% endif %} {% if vrf_bgp_pr.has_key(pr_vrf) or vrf_bgp_pr.update({pr_vrf:{}}) %} {% endif %} {% if vrf_bgp_pr[pr_vrf].update({pr_key:BGP_PEER_RANGE[pr_key]}) %} {% endif %} {% endfor %} -{% for vrf in vrf_bgp_nbr %} +{% for vrf in vrf_routers|sort %} {% if vrf == 'global' %} router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% else %} @@ -210,7 +212,7 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} vrf {{ vrf }} {# block vlan_advertisement #} {% for (name, prefix) in VLAN_INTERFACE|pfx_filter %} {% set vlan_intf_vrf = 'global' %} -{% if VLAN_INTERFACE[name].has_key('vrf_name') %} +{% if VLAN_INTERFACE.has_key(name) and VLAN_INTERFACE[name].has_key('vrf_name') %} {% set vlan_intf_vrf = VLAN_INTERFACE[name]['vrf_name'] %} {% endif %} {% if vlan_intf_vrf == vrf %} @@ -225,7 +227,8 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} vrf {{ vrf }} {% endfor %} {# endblock vlan_advertisement #} {# block bgp_sessions #} -{% for neighbor_addr, bgp_session in vrf_bgp_nbr[vrf].iteritems() %} +{% if vrf_bgp_nbr.has_key(vrf) %} +{% for neighbor_addr, bgp_session in vrf_bgp_nbr[vrf]|dictsort %} {% if bgp_session['asn'] | int != 0 %} neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} @@ -274,10 +277,11 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} vrf {{ vrf }} {% endif %} {% endif %} {% endfor %} +{% endif %} {# endblock bgp_sessions #} {# block bgp_peers_with_range #} {% if vrf_bgp_pr.has_key(vrf) %} -{% for bgp_peer in vrf_bgp_pr[vrf].values() %} +{% for peer_key, bgp_peer in vrf_bgp_pr[vrf]|dictsort %} neighbor {{ bgp_peer['name'] }} peer-group neighbor {{ bgp_peer['name'] }} passive neighbor {{ bgp_peer['name'] }} remote-as {{ deployment_id_asn_map[DEVICE_METADATA['localhost']['deployment_id']] }} diff --git a/dockers/docker-fpm-frr/zebra.conf.j2 b/dockers/docker-fpm-frr/zebra.conf.j2 index d0183bd6a2a5..d6fe67470e7c 100644 --- a/dockers/docker-fpm-frr/zebra.conf.j2 +++ b/dockers/docker-fpm-frr/zebra.conf.j2 @@ -36,6 +36,7 @@ ip route 0.0.0.0/0 {{ MGMT_INTERFACE[(name, prefix)]['gwaddr'] }} 200 {% endfor %} {% endif %} {% endblock default_route %} +! {% block static_route %} {% if STATIC_ROUTE %} {% set glb_rt = {} %} @@ -51,7 +52,7 @@ ip route 0.0.0.0/0 {{ MGMT_INTERFACE[(name, prefix)]['gwaddr'] }} 200 {% endif %} {% endif %} {% endfor %} -{% for rt_key, rt_attr in glb_rt.iteritems() %} +{% for rt_key, rt_attr in glb_rt|dictsort %} {% if rt_attr.has_key('distance') and rt_attr['distance'] | int != 1 %} {% if rt_attr.has_key('nexthop-vrf') %} ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} @@ -67,9 +68,9 @@ ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {% endif %} {% endfor %} ! -{% for vrf, rt in vrf_rt.iteritems() %} +{% for vrf, rt in vrf_rt|dictsort %} vrf {{ vrf }} -{% for rt_key, rt_attr in rt.iteritems() %} +{% for rt_key, rt_attr in rt|dictsort %} {% if rt_attr.has_key('distance') and rt_attr['distance'] | int != 1 %} {% if rt_attr.has_key('nexthop-vrf') %} ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} diff --git a/src/sonic-config-engine/tests/sample_output/frr.conf b/src/sonic-config-engine/tests/sample_output/frr.conf index 56d7122186b2..d95db019a99f 100644 --- a/src/sonic-config-engine/tests/sample_output/frr.conf +++ b/src/sonic-config-engine/tests/sample_output/frr.conf @@ -90,24 +90,6 @@ router bgp 65100 neighbor 10.0.0.63 soft-reconfiguration inbound maximum-paths 64 exit-address-family - neighbor fc00::7a remote-as 64600 - neighbor fc00::7a description ARISTA03T1 - address-family ipv6 - neighbor fc00::7a allowas-in 1 - neighbor fc00::7a activate - neighbor fc00::7a soft-reconfiguration inbound - neighbor fc00::7a route-map set-next-hop-global-v6 in - maximum-paths 64 - exit-address-family - neighbor fc00::7e remote-as 64600 - neighbor fc00::7e description ARISTA04T1 - address-family ipv6 - neighbor fc00::7e allowas-in 1 - neighbor fc00::7e activate - neighbor fc00::7e soft-reconfiguration inbound - neighbor fc00::7e route-map set-next-hop-global-v6 in - maximum-paths 64 - exit-address-family neighbor fc00::72 remote-as 64600 neighbor fc00::72 description ARISTA01T1 address-family ipv6 @@ -126,8 +108,24 @@ router bgp 65100 neighbor fc00::76 route-map set-next-hop-global-v6 in maximum-paths 64 exit-address-family -! -maximum-paths 64 + neighbor fc00::7a remote-as 64600 + neighbor fc00::7a description ARISTA03T1 + address-family ipv6 + neighbor fc00::7a allowas-in 1 + neighbor fc00::7a activate + neighbor fc00::7a soft-reconfiguration inbound + neighbor fc00::7a route-map set-next-hop-global-v6 in + maximum-paths 64 + exit-address-family + neighbor fc00::7e remote-as 64600 + neighbor fc00::7e description ARISTA04T1 + address-family ipv6 + neighbor fc00::7e allowas-in 1 + neighbor fc00::7e activate + neighbor fc00::7e soft-reconfiguration inbound + neighbor fc00::7e route-map set-next-hop-global-v6 in + maximum-paths 64 + exit-address-family ! route-map ISOLATE permit 10 set as-path prepend 65100 From f64b9e69e1daafd06aa55ce504f785308ddaae19 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Tue, 23 Jul 2019 00:28:21 -0700 Subject: [PATCH 3/7] fix t2-chassis-fe test failure --- .../tests/sample_output/t2-chassis-fe-bgpd.conf | 2 -- .../tests/sample_output/t2-chassis-fe-pc-zebra.conf | 1 - .../tests/sample_output/t2-chassis-fe-vni-zebra.conf | 1 - .../tests/sample_output/t2-chassis-fe-zebra.conf | 1 - src/sonic-config-engine/tests/test_j2files_t2_chassis_fe.py | 2 +- 5 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-bgpd.conf b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-bgpd.conf index 515e0aba8df2..726305f30f4d 100644 --- a/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-bgpd.conf +++ b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-bgpd.conf @@ -72,8 +72,6 @@ router bgp 4000 maximum-paths 64 exit-address-family ! -maximum-paths 64 -! route-map ISOLATE permit 10 set as-path prepend 4000 ! diff --git a/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-pc-zebra.conf b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-pc-zebra.conf index 8861e6d3012f..9fe27c51b9e0 100644 --- a/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-pc-zebra.conf +++ b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-pc-zebra.conf @@ -23,7 +23,6 @@ interface PortChannel8 link-detect ! ! -! set static default route to mgmt gateway as a backup to learned default ! ! Set ip source to loopback for bgp learned routes route-map RM_SET_SRC permit 10 diff --git a/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-vni-zebra.conf b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-vni-zebra.conf index 1f9dce8812bd..ff51f1ed62be 100644 --- a/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-vni-zebra.conf +++ b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-vni-zebra.conf @@ -23,7 +23,6 @@ interface Ethernet8 link-detect ! ! -! set static default route to mgmt gateway as a backup to learned default ! ! Set ip source to loopback for bgp learned routes route-map RM_SET_SRC permit 10 diff --git a/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-zebra.conf b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-zebra.conf index b89aeb4a382f..285167167160 100644 --- a/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-zebra.conf +++ b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-zebra.conf @@ -23,7 +23,6 @@ interface Ethernet8 link-detect ! ! -! set static default route to mgmt gateway as a backup to learned default ! ! Set ip source to loopback for bgp learned routes route-map RM_SET_SRC permit 10 diff --git a/src/sonic-config-engine/tests/test_j2files_t2_chassis_fe.py b/src/sonic-config-engine/tests/test_j2files_t2_chassis_fe.py index 388f1b2885a6..531a9d477a66 100644 --- a/src/sonic-config-engine/tests/test_j2files_t2_chassis_fe.py +++ b/src/sonic-config-engine/tests/test_j2files_t2_chassis_fe.py @@ -35,7 +35,7 @@ def test_t2_chassis_fe_pc_zebra_frr(self): self.assertTrue(filecmp.cmp(os.path.join(self.test_dir, 'sample_output', 't2-chassis-fe-pc-zebra.conf'), self.output_file)) # Test zebra.conf in FRR docker for a T2 chassis frontend (fe) switch with specified VNI - def test_t2_chassis_fe_pc_zebra_frr(self): + def test_t2_chassis_fe_vni_zebra_frr(self): conf_template = os.path.join(self.test_dir, '..', '..', '..', 'dockers', 'docker-fpm-frr', 'zebra.conf.j2') argument = '-m ' + self.t2_chassis_fe_vni_minigraph + ' -p ' + self.t2_chassis_fe_port_config + ' -t ' + conf_template + ' > ' + self.output_file self.run_script(argument) From ab5daf8c9b95cef92891d3783d33b0cf82d44833 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Thu, 31 Oct 2019 03:44:11 -0700 Subject: [PATCH 4/7] FRR config templates support VRF --- dockers/docker-fpm-frr/bgpcfgd | 62 +++++++---- dockers/docker-fpm-frr/bgpd.conf.default.j2 | 103 ++++++++++++++---- dockers/docker-fpm-frr/frr.conf.j2 | 1 + dockers/docker-fpm-frr/staticd.conf.j2 | 1 + .../staticd.static_route.conf.j2 | 52 +++++++++ 5 files changed, 179 insertions(+), 40 deletions(-) create mode 100644 dockers/docker-fpm-frr/staticd.static_route.conf.j2 diff --git a/dockers/docker-fpm-frr/bgpcfgd b/dockers/docker-fpm-frr/bgpcfgd index aad3c0c6d627..26792b4f1eb9 100755 --- a/dockers/docker-fpm-frr/bgpcfgd +++ b/dockers/docker-fpm-frr/bgpcfgd @@ -91,12 +91,22 @@ class BGPConfigManager(object): daemon.add_manager(swsscommon.CONFIG_DB, swsscommon.CFG_BGP_NEIGHBOR_TABLE_NAME, self.__bgp_handler) def load_peers(self): - peers = set() - command = ["vtysh", "-c", "show bgp neighbors json"] + vrfs = set() + command = ["vtysh", "-c", "show bgp vrfs json"] rc, out, err = run_command(command) if rc == 0: - js_bgp = json.loads(out) - peers = set(js_bgp.keys()) + js_vrf = json.loads(out) + vrfs = set(js_vrf['vrfs'].keys()) + + peers = set() + for vrf in vrfs: + command = ["vtysh", "-c", 'show bgp vrf {} neighbors json'.format(vrf)] + rc, out, err = run_command(command) + if rc == 0: + js_bgp = json.loads(out) + for nbr in js_bgp.keys(): + peers.add((vrf, nbr)) + return peers def __metadata_handler(self, key, op, data): @@ -114,37 +124,48 @@ class BGPConfigManager(object): def __update_bgp(self): cmds = [] for key, op, data in self.bgp_messages: + if '|' in key: + vrf, nbr = key.split('|', 1) + cmds.append('router bgp {} vrf {}'.format(self.bgp_asn, vrf)) + syslog.syslog(syslog.LOG_INFO, 'Enter router bgp {} vrf {}'.format(self.bgp_asn, vrf)) + else: + vrf = 'default' + nbr = key + cmds.append('router bgp {}'.format(self.bgp_asn)) + syslog.syslog(syslog.LOG_INFO, 'Enter router bgp {}'.format(self.bgp_asn)) if op == swsscommon.SET_COMMAND: - if key not in self.peers: + if (vrf, nbr) not in self.peers: try: - txt = self.bgp_peer_add_template.render(DEVICE_METADATA=self.meta, neighbor_addr=key, bgp_session=data) + txt = self.bgp_peer_add_template.render(DEVICE_METADATA=self.meta, neighbor_addr=nbr, bgp_session=data) cmds.append(txt) except: - syslog.syslog(syslog.LOG_ERR, 'Peer {}. Error in rendering the template for "SET" command {}'.format(key, data)) + syslog.syslog(syslog.LOG_ERR, 'Peer {}. Error in rendering the template for "SET" command {}'.format(nbr, data)) else: - syslog.syslog(syslog.LOG_INFO, 'Peer {} added with attributes {}'.format(key, data)) - self.peers.add(key) + syslog.syslog(syslog.LOG_INFO, 'Peer {} added with attributes {}'.format(nbr, data)) + self.peers.add((vrf, nbr)) else: # when the peer is already configured we support "shutdown/no shutdown" # commands for the peers only if "admin_status" in data: if data['admin_status'] == 'up': - cmds.append(self.bgp_peer_no_shutdown.render(neighbor_addr=key)) - syslog.syslog(syslog.LOG_INFO, 'Peer {} admin state is set to "up"'.format(key)) + cmds.append(self.bgp_peer_no_shutdown.render(neighbor_addr=nbr)) + syslog.syslog(syslog.LOG_INFO, 'Peer {} admin state is set to "up"'.format(nbr)) elif data['admin_status'] == 'down': - cmds.append(self.bgp_peer_shutdown.render(neighbor_addr=key)) - syslog.syslog(syslog.LOG_INFO, 'Peer {} admin state is set to "down"'.format(key)) + cmds.append(self.bgp_peer_shutdown.render(neighbor_addr=nbr)) + syslog.syslog(syslog.LOG_INFO, 'Peer {} admin state is set to "down"'.format(nbr)) else: - syslog.syslog(syslog.LOG_ERR, "Peer {}: Can't update the peer. has wrong attribute value attr['admin_status'] = '{}'".format(key, data['admin_status'])) + syslog.syslog(syslog.LOG_ERR, "Peer {}: Can't update the peer. has wrong attribute value attr['admin_status'] = '{}'".format(nbr, data['admin_status'])) else: - syslog.syslog(syslog.LOG_INFO, "Peer {}: Can't update the peer. No 'admin_status' attribute in the request".format(key)) + syslog.syslog(syslog.LOG_INFO, "Peer {}: Can't update the peer. No 'admin_status' attribute in the request".format(nbr)) elif op == swsscommon.DEL_COMMAND: - if key in self.peers: - cmds.append(self.bgp_peer_del_template.render(neighbor_addr=key)) - syslog.syslog(syslog.LOG_INFO, 'Peer {} has been removed'.format(key)) - self.peers.remove(key) + if (vrf, nbr) in self.peers: + cmds.append(self.bgp_peer_del_template.render(neighbor_addr=nbr)) + syslog.syslog(syslog.LOG_INFO, 'Peer {} has been removed'.format(nbr)) + self.peers.remove((vrf, nbr)) else: - syslog.syslog(syslog.LOG_WARNING, 'Peer {} is not found'.format(key)) + syslog.syslog(syslog.LOG_WARNING, 'Peer {} is not found'.format(nbr)) + cmds.append('!') + syslog.syslog(syslog.LOG_INFO, 'Exit router bgp mode') self.bgp_messages = [] if len(cmds) == 0: @@ -153,7 +174,6 @@ class BGPConfigManager(object): fd, tmp_filename = tempfile.mkstemp(dir='/tmp') os.close(fd) with open (tmp_filename, 'w') as fp: - fp.write('router bgp %s\n' % self.bgp_asn) for cmd in cmds: fp.write("%s\n" % cmd) diff --git a/dockers/docker-fpm-frr/bgpd.conf.default.j2 b/dockers/docker-fpm-frr/bgpd.conf.default.j2 index 957c2792435f..18e263fd6436 100644 --- a/dockers/docker-fpm-frr/bgpd.conf.default.j2 +++ b/dockers/docker-fpm-frr/bgpd.conf.default.j2 @@ -1,6 +1,7 @@ ! +{% block bgp %} {% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} -{% block bgp_init %} +{# block bgp_init #} ! ! bgp multiple-instance ! @@ -36,7 +37,53 @@ route-map ISOLATE permit 10 route-map set-next-hop-global-v6 permit 10 set ipv6 next-hop prefer-global ! +{% set vrf_lo_intf = {} %} +{% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} +{% set intf_vrf = 'default' %} +{% if LOOPBACK_INTERFACE.has_key(name) and LOOPBACK_INTERFACE[name].has_key('vrf_name') %} +{% set intf_vrf = LOOPBACK_INTERFACE[name]['vrf_name'] %} +{% endif %} +{% if vrf_lo_intf.has_key(intf_vrf) or vrf_lo_intf.update({intf_vrf:[]}) %} +{% endif %} +{% if name not in vrf_lo_intf[intf_vrf] and vrf_lo_intf[intf_vrf].append(name) %} +{% endif %} +{% endfor %} +{% set vrf_routers = [] %} +{% set vrf_bgp_nbr = {} %} +{% for nbr_key in BGP_NEIGHBOR %} +{% if nbr_key | length == 2 %} +{% set nbr_vrf = nbr_key[0] %} +{% set nbr_addr = nbr_key[1] %} +{% else %} +{% set nbr_vrf = 'default' %} +{% set nbr_addr = nbr_key %} +{% endif %} +{% if nbr_vrf in vrf_routers or vrf_routers.append(nbr_vrf) %} +{% endif %} +{% if vrf_bgp_nbr.has_key(nbr_vrf) or vrf_bgp_nbr.update({nbr_vrf:{}}) %} +{% endif %} +{% if vrf_bgp_nbr[nbr_vrf].update({nbr_addr:BGP_NEIGHBOR[nbr_key]}) %} +{% endif %} +{% endfor %} +{% set vrf_bgp_pr = {} %} +{% for pr_key in BGP_PEER_RANGE %} +{% set pr_vrf = 'default' %} +{% if BGP_PEER_RANGE[pr_key].has_key('vrf_name') %} +{% set pr_vrf = BGP_PEER_RANGE[pr_key]['vrf_name'] %} +{% endif %} +{% if pr_vrf not in vrf_routers and vrf_routers.append(pr_vrf) %} +{% endif %} +{% if vrf_bgp_pr.has_key(pr_vrf) or vrf_bgp_pr.update({pr_vrf:{}}) %} +{% endif %} +{% if vrf_bgp_pr[pr_vrf].update({pr_key:BGP_PEER_RANGE[pr_key]}) %} +{% endif %} +{% endfor %} +{% for vrf in vrf_routers|sort %} +{% if vrf == 'default' %} router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} +{% else %} +router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} vrf {{ vrf }} +{% endif %} bgp log-neighbor-changes bgp bestpath as-path multipath-relax no bgp default ipv4-unicast @@ -45,25 +92,33 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} bgp graceful-restart preserve-fw-state {% endif %} +{% set vrf_lo0 = "" %} +{% if vrf_lo_intf[vrf] | length > 0 %} +{% set vrf_lo0 = vrf_lo_intf[vrf][0] %} +{% endif %} {% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if prefix | ipv4 and name == 'Loopback0' %} +{% if prefix | ipv4 and name == vrf_lo0 %} bgp router-id {{ prefix | ip }} {% endif %} {% endfor %} {# advertise loopback #} {% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if prefix | ipv4 and name == 'Loopback0' %} +{% if prefix | ipv4 and name == vrf_lo0 %} network {{ prefix | ip }}/32 -{% elif prefix | ipv6 and name == 'Loopback0' %} +{% elif prefix | ipv6 and name == vrf_lo0 %} address-family ipv6 network {{ prefix | ip }}/64 exit-address-family {% endif %} {% endfor %} -{% endblock bgp_init %} -{% endif %} -{% block vlan_advertisement %} +{# endblock bgp_init #} +{# block vlan_advertisement #} {% for (name, prefix) in VLAN_INTERFACE|pfx_filter %} +{% set vlan_intf_vrf = 'default' %} +{% if VLAN_INTERFACE.has_key(name) and VLAN_INTERFACE[name].has_key('vrf_name') %} +{% set vlan_intf_vrf = VLAN_INTERFACE[name]['vrf_name'] %} +{% endif %} +{% if vlan_intf_vrf == vrf %} {% if prefix | ipv4 %} network {{ prefix }} {% elif prefix | ipv6 %} @@ -71,17 +126,18 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} network {{ prefix }} exit-address-family {% endif %} +{% endif %} {% endfor %} -{% endblock vlan_advertisement %} -{% block maximum_paths %} +{# endblock vlan_advertisement #} +{# block maximum_paths #} address-family ipv4 maximum-paths 64 exit-address-family address-family ipv6 maximum-paths 64 exit-address-family -{% endblock maximum_paths %} -{% block peers_peer_group %} +{# endblock maximum_paths #} +{# block peers_peer_group #} neighbor PEER_V4 peer-group neighbor PEER_V6 peer-group address-family ipv4 @@ -98,10 +154,10 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} neighbor PEER_V6 soft-reconfiguration inbound neighbor PEER_V6 route-map TO_BGP_PEER_V6 out exit-address-family -{% endblock peers_peer_group %} -{% block bgp_peers_with_range %} -{% if BGP_PEER_RANGE %} -{% for bgp_peer in BGP_PEER_RANGE.values() %} +{# endblock peers_peer_group #} +{# block bgp_peers_with_range #} +{% if vrf_bgp_pr.has_key(vrf) %} +{% for peer_key, bgp_peer in vrf_bgp_pr[vrf]|dictsort %} neighbor {{ bgp_peer['name'] }} peer-group neighbor {{ bgp_peer['name'] }} passive {% if bgp_peer['peer_asn'] is defined %} @@ -114,8 +170,12 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% if bgp_peer['src_address'] is defined %} neighbor {{ bgp_peer['name'] }} update-source {{ bgp_peer['src_address'] | ip }} {% else %} +{% set vrf_lo1 = "" %} +{% if vrf_lo_intf[vrf] | length > 1 %} +{% set vrf_lo1 = vrf_lo_intf[vrf][1] %} +{% endif %} {% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if name == 'Loopback1' %} +{% if name == vrf_lo1 %} neighbor {{ bgp_peer['name'] }} update-source {{ prefix | ip }} {% endif %} {% endfor %} @@ -133,8 +193,9 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} exit-address-family {% endfor %} {% endif %} -{% endblock bgp_peers_with_range %} -{% block bgp_monitors %} +{# endblock bgp_peers_with_range #} +{# block bgp_monitors #} +{% if vrf == 'default' %} {% if BGP_MONITORS is defined and BGP_MONITORS|length > 0 %} neighbor BGPMON_V4 peer-group {% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} @@ -153,5 +214,9 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} neighbor {{ neighbor_addr }} activate {% endfor %} {% endif %} -{% endblock bgp_monitors %} +{% endif %} +{# endblock bgp_monitors #} ! +{% endfor %} +{% endif %} +{% endblock bgp %} diff --git a/dockers/docker-fpm-frr/frr.conf.j2 b/dockers/docker-fpm-frr/frr.conf.j2 index afa40ad8ba75..1d5a1b787856 100644 --- a/dockers/docker-fpm-frr/frr.conf.j2 +++ b/dockers/docker-fpm-frr/frr.conf.j2 @@ -13,6 +13,7 @@ agentx {% include "zebra.interfaces.conf.j2" %} ! {% include "staticd.default_route.conf.j2" %} +{% include "staticd.static_route.conf.j2" %} ! {% include "bgpd.conf.default.j2" %} ! diff --git a/dockers/docker-fpm-frr/staticd.conf.j2 b/dockers/docker-fpm-frr/staticd.conf.j2 index 4e39e17d7dbf..89628d4a7b97 100644 --- a/dockers/docker-fpm-frr/staticd.conf.j2 +++ b/dockers/docker-fpm-frr/staticd.conf.j2 @@ -9,4 +9,5 @@ {% include "daemons.common.conf.j2" %} ! {% include "staticd.default_route.conf.j2" %} +{% include "staticd.static_route.conf.j2" %} ! diff --git a/dockers/docker-fpm-frr/staticd.static_route.conf.j2 b/dockers/docker-fpm-frr/staticd.static_route.conf.j2 new file mode 100644 index 000000000000..7fc52eca5330 --- /dev/null +++ b/dockers/docker-fpm-frr/staticd.static_route.conf.j2 @@ -0,0 +1,52 @@ +{% block static_route %} +{% if STATIC_ROUTE %} +{% set glb_rt = {} %} +{% set vrf_rt = {} %} +{% for rt_key in STATIC_ROUTE %} +{% if rt_key is not string() %} +{% if vrf_rt.has_key(rt_key[0]) or vrf_rt.update({rt_key[0]:{}}) %} +{% endif %} +{% if vrf_rt[rt_key[0]].update({rt_key[1]:STATIC_ROUTE[rt_key]}) %} +{% endif %} +{% else %} +{% if glb_rt.update({rt_key:STATIC_ROUTE[rt_key]}) %} +{% endif %} +{% endif %} +{% endfor %} +{% for rt_key, rt_attr in glb_rt|dictsort %} +{% if rt_attr.has_key('distance') and rt_attr['distance'] | int != 1 %} +{% if rt_attr.has_key('nexthop-vrf') %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} +{% endif %} +{% else %} +{% if rt_attr.has_key('nexthop-vrf') %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} +ip route {{ rt_key }} {{ rt_attr['nexthop'] }} +{% endif %} +{% endif %} +{% endfor %} +! +{% for vrf, rt in vrf_rt|dictsort %} +vrf {{ vrf }} +{% for rt_key, rt_attr in rt|dictsort %} +{% if rt_attr.has_key('distance') and rt_attr['distance'] | int != 1 %} +{% if rt_attr.has_key('nexthop-vrf') %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} {{ rt_attr['distance'] }} +{% endif %} +{% else %} +{% if rt_attr.has_key('nexthop-vrf') %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} nexthop-vrf {{ rt_attr['nexthop-vrf'] }} +{% else %} + ip route {{ rt_key }} {{ rt_attr['nexthop'] }} +{% endif %} +{% endif %} +{% endfor %} +! +{% endfor %} +{% endif %} +{% endblock static_route %} From c94031fc4c748bba299f85fa65ac090b8eead311 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Thu, 31 Oct 2019 18:29:14 -0700 Subject: [PATCH 5/7] sample_output changes because of '!' at file tail --- src/sonic-config-engine/tests/sample_output/bgpd_frr.conf | 3 ++- src/sonic-config-engine/tests/sample_output/frr.conf | 3 ++- .../tests/sample_output/t2-chassis-fe-bgpd.conf | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sonic-config-engine/tests/sample_output/bgpd_frr.conf b/src/sonic-config-engine/tests/sample_output/bgpd_frr.conf index 3a8abf050b45..e73bd13fef20 100644 --- a/src/sonic-config-engine/tests/sample_output/bgpd_frr.conf +++ b/src/sonic-config-engine/tests/sample_output/bgpd_frr.conf @@ -83,4 +83,5 @@ router bgp 65100 neighbor 10.20.30.40 peer-group BGPMON_V4 neighbor 10.20.30.40 description BGPMonitor neighbor 10.20.30.40 activate -!! +! +! diff --git a/src/sonic-config-engine/tests/sample_output/frr.conf b/src/sonic-config-engine/tests/sample_output/frr.conf index edcf0939a03f..47734e9870fe 100644 --- a/src/sonic-config-engine/tests/sample_output/frr.conf +++ b/src/sonic-config-engine/tests/sample_output/frr.conf @@ -115,4 +115,5 @@ router bgp 65100 neighbor 10.20.30.40 peer-group BGPMON_V4 neighbor 10.20.30.40 description BGPMonitor neighbor 10.20.30.40 activate -!! +! +! diff --git a/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-bgpd.conf b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-bgpd.conf index b0b5e2cb1192..8e5274e80b9c 100644 --- a/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-bgpd.conf +++ b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-bgpd.conf @@ -82,4 +82,5 @@ router bgp 4000 neighbor PEER_V6 soft-reconfiguration inbound neighbor PEER_V6 route-map TO_BGP_PEER_V6 out exit-address-family -!! +! +! From d5a9fe3f0738f18173fb5e2ffb252b21351019b5 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Thu, 7 Nov 2019 19:13:31 -0800 Subject: [PATCH 6/7] address review comments --- dockers/docker-fpm-frr/bgpcfgd | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/dockers/docker-fpm-frr/bgpcfgd b/dockers/docker-fpm-frr/bgpcfgd index 721f4430e0d3..7d61956873a2 100755 --- a/dockers/docker-fpm-frr/bgpcfgd +++ b/dockers/docker-fpm-frr/bgpcfgd @@ -93,12 +93,12 @@ class BGPConfigManager(object): daemon.add_manager(swsscommon.CONFIG_DB, swsscommon.CFG_BGP_NEIGHBOR_TABLE_NAME, self.__bgp_handler) def load_peers(self): - vrfs = set() + vrfs = [] command = ["vtysh", "-c", "show bgp vrfs json"] rc, out, err = run_command(command) if rc == 0: js_vrf = json.loads(out) - vrfs = set(js_vrf['vrfs'].keys()) + vrfs = js_vrf['vrfs'].keys() peers = set() for vrf in vrfs: @@ -142,12 +142,12 @@ class BGPConfigManager(object): if '|' in key: vrf, nbr = key.split('|', 1) cmds.append('router bgp {} vrf {}'.format(self.bgp_asn, vrf)) - syslog.syslog(syslog.LOG_INFO, 'Enter router bgp {} vrf {}'.format(self.bgp_asn, vrf)) + syslog.syslog(syslog.LOG_DEBUG, 'Enter router bgp {} vrf {}'.format(self.bgp_asn, vrf)) else: vrf = 'default' nbr = key cmds.append('router bgp {}'.format(self.bgp_asn)) - syslog.syslog(syslog.LOG_INFO, 'Enter router bgp {}'.format(self.bgp_asn)) + syslog.syslog(syslog.LOG_DEBUG, 'Enter router bgp {}'.format(self.bgp_asn)) if op == swsscommon.SET_COMMAND: if (vrf, nbr) not in self.peers: if 'name' in data and data['name'] not in self.neig_meta: @@ -158,9 +158,9 @@ class BGPConfigManager(object): txt = self.bgp_peer_add_template.render(DEVICE_METADATA=self.meta, DEVICE_NEIGHBOR_METADATA=self.neig_meta, neighbor_addr=nbr, bgp_session=data) cmds.append(txt) except: - syslog.syslog(syslog.LOG_ERR, 'Peer {}. Error in rendering the template for "SET" command {}'.format(nbr, data)) + syslog.syslog(syslog.LOG_ERR, 'Peer {}. Error in rendering the template for "SET" command {}'.format(key, data)) else: - syslog.syslog(syslog.LOG_INFO, 'Peer {} added with attributes {}'.format(nbr, data)) + syslog.syslog(syslog.LOG_INFO, 'Peer {} added with attributes {}'.format(key, data)) self.peers.add((vrf, nbr)) else: # when the peer is already configured we support "shutdown/no shutdown" @@ -168,23 +168,23 @@ class BGPConfigManager(object): if "admin_status" in data: if data['admin_status'] == 'up': cmds.append(self.bgp_peer_no_shutdown.render(neighbor_addr=nbr)) - syslog.syslog(syslog.LOG_INFO, 'Peer {} admin state is set to "up"'.format(nbr)) + syslog.syslog(syslog.LOG_INFO, 'Peer {} admin state is set to "up"'.format(key)) elif data['admin_status'] == 'down': cmds.append(self.bgp_peer_shutdown.render(neighbor_addr=nbr)) - syslog.syslog(syslog.LOG_INFO, 'Peer {} admin state is set to "down"'.format(nbr)) + syslog.syslog(syslog.LOG_INFO, 'Peer {} admin state is set to "down"'.format(key)) else: - syslog.syslog(syslog.LOG_ERR, "Peer {}: Can't update the peer. has wrong attribute value attr['admin_status'] = '{}'".format(nbr, data['admin_status'])) + syslog.syslog(syslog.LOG_ERR, "Peer {}: Can't update the peer. has wrong attribute value attr['admin_status'] = '{}'".format(key, data['admin_status'])) else: - syslog.syslog(syslog.LOG_INFO, "Peer {}: Can't update the peer. No 'admin_status' attribute in the request".format(nbr)) + syslog.syslog(syslog.LOG_INFO, "Peer {}: Can't update the peer. No 'admin_status' attribute in the request".format(key)) elif op == swsscommon.DEL_COMMAND: if (vrf, nbr) in self.peers: cmds.append(self.bgp_peer_del_template.render(neighbor_addr=nbr)) - syslog.syslog(syslog.LOG_INFO, 'Peer {} has been removed'.format(nbr)) + syslog.syslog(syslog.LOG_INFO, 'Peer {} has been removed'.format(key)) self.peers.remove((vrf, nbr)) else: - syslog.syslog(syslog.LOG_WARNING, 'Peer {} is not found'.format(nbr)) + syslog.syslog(syslog.LOG_WARNING, 'Peer {} is not found'.format(key)) cmds.append('!') - syslog.syslog(syslog.LOG_INFO, 'Exit router bgp mode') + syslog.syslog(syslog.LOG_DEBUG, 'Exit router bgp mode') self.bgp_messages = new_bgp_messages if len(cmds) == 0: From 74d32037876ed3c81cc8f8d257e0a4b3b61cc269 Mon Sep 17 00:00:00 2001 From: Tyler Li Date: Fri, 6 Dec 2019 02:10:44 -0800 Subject: [PATCH 7/7] remove extra spaces --- dockers/docker-fpm-frr/bgpcfgd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dockers/docker-fpm-frr/bgpcfgd b/dockers/docker-fpm-frr/bgpcfgd index 821cad004f19..b7a3a4cc6f43 100755 --- a/dockers/docker-fpm-frr/bgpcfgd +++ b/dockers/docker-fpm-frr/bgpcfgd @@ -371,7 +371,7 @@ class BGPPeerMgr(Manager): @staticmethod def load_peers(): vrfs = [] - command = ["vtysh", "-c", "show bgp vrfs json"] + command = ["vtysh", "-c", "show bgp vrfs json"] rc, out, err = run_command(command) if rc == 0: js_vrf = json.loads(out) @@ -379,7 +379,7 @@ class BGPPeerMgr(Manager): peers = set() for vrf in vrfs: - command = ["vtysh", "-c", 'show bgp vrf {} neighbors json'.format(vrf)] + command = ["vtysh", "-c", 'show bgp vrf {} neighbors json'.format(vrf)] rc, out, err = run_command(command) if rc == 0: js_bgp = json.loads(out)