From 0ed8c8146411d5a1dd05e93cd8da0845a403acf1 Mon Sep 17 00:00:00 2001 From: Wei Bai Date: Tue, 9 Jul 2019 08:32:20 +0800 Subject: [PATCH] [sonic-cfggen][FRR] Support T2 chassis frontend (#3100) --- dockers/docker-fpm-frr/bgpd.conf.default.j2 | 135 ++++++++ dockers/docker-fpm-frr/bgpd.conf.j2 | 137 +------- ...bgpd.conf.spine_chassis_frontend_router.j2 | 128 +++++++ dockers/docker-fpm-frr/zebra.conf.j2 | 10 + src/sonic-config-engine/minigraph.py | 107 +++++- .../sample_output/t2-chassis-fe-bgpd.conf | 82 +++++ .../sample_output/t2-chassis-fe-pc-zebra.conf | 38 ++ .../t2-chassis-fe-vni-zebra.conf | 38 ++ .../sample_output/t2-chassis-fe-zebra.conf | 38 ++ .../tests/t2-chassis-fe-graph-pc.xml | 324 ++++++++++++++++++ .../tests/t2-chassis-fe-graph-vni.xml | 249 ++++++++++++++ .../tests/t2-chassis-fe-graph.xml | 248 ++++++++++++++ .../tests/t2-chassis-fe-port-config.ini | 33 ++ src/sonic-config-engine/tests/test_cfggen.py | 11 +- .../tests/test_cfggen_t2_chassis_fe.py | 72 ++++ src/sonic-config-engine/tests/test_j2files.py | 2 +- .../tests/test_j2files_t2_chassis_fe.py | 57 +++ .../tests/test_minigraph_case.py | 12 +- 18 files changed, 1582 insertions(+), 139 deletions(-) create mode 100644 dockers/docker-fpm-frr/bgpd.conf.default.j2 create mode 100644 dockers/docker-fpm-frr/bgpd.conf.spine_chassis_frontend_router.j2 create mode 100644 src/sonic-config-engine/tests/sample_output/t2-chassis-fe-bgpd.conf create mode 100644 src/sonic-config-engine/tests/sample_output/t2-chassis-fe-pc-zebra.conf create mode 100644 src/sonic-config-engine/tests/sample_output/t2-chassis-fe-vni-zebra.conf create mode 100644 src/sonic-config-engine/tests/sample_output/t2-chassis-fe-zebra.conf create mode 100644 src/sonic-config-engine/tests/t2-chassis-fe-graph-pc.xml create mode 100644 src/sonic-config-engine/tests/t2-chassis-fe-graph-vni.xml create mode 100644 src/sonic-config-engine/tests/t2-chassis-fe-graph.xml create mode 100644 src/sonic-config-engine/tests/t2-chassis-fe-port-config.ini create mode 100644 src/sonic-config-engine/tests/test_cfggen_t2_chassis_fe.py create mode 100644 src/sonic-config-engine/tests/test_j2files_t2_chassis_fe.py diff --git a/dockers/docker-fpm-frr/bgpd.conf.default.j2 b/dockers/docker-fpm-frr/bgpd.conf.default.j2 new file mode 100644 index 0000000000..735cb3264a --- /dev/null +++ b/dockers/docker-fpm-frr/bgpd.conf.default.j2 @@ -0,0 +1,135 @@ +{% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} +{% block bgp_init %} +! +! bgp multiple-instance +! +route-map FROM_BGP_SPEAKER_V4 permit 10 +! +route-map TO_BGP_SPEAKER_V4 deny 10 +! +router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} + bgp log-neighbor-changes + bgp bestpath as-path multipath-relax + no bgp default ipv4-unicast + bgp graceful-restart restart-time 240 + bgp graceful-restart +{% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} + bgp graceful-restart preserve-fw-state +{% endif %} +{% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} +{% if prefix | ipv4 and name == 'Loopback0' %} + bgp router-id {{ prefix | ip }} +{% endif %} +{% endfor %} +{# advertise loopback #} +{% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} +{% if prefix | ipv4 and name == 'Loopback0' %} + network {{ prefix | ip }}/32 +{% elif prefix | ipv6 and name == 'Loopback0' %} + address-family ipv6 + network {{ prefix | ip }}/64 + exit-address-family +{% endif %} +{% endfor %} +{% endblock bgp_init %} +{% endif %} +{% block vlan_advertisement %} +{% for (name, prefix) in VLAN_INTERFACE|pfx_filter %} +{% if prefix | ipv4 %} + network {{ prefix }} +{% elif prefix | ipv6 %} + address-family ipv6 + network {{ prefix }} + exit-address-family +{% endif %} +{% endfor %} +{% endblock vlan_advertisement %} +{% block bgp_sessions %} +{% for neighbor_addr, bgp_session in BGP_NEIGHBOR.iteritems() %} +{% if bgp_session['asn'] | int != 0 %} + neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} + neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} +{# set the bgp neighbor timers if they have not default values #} +{% if (bgp_session['keepalive'] is defined and bgp_session['keepalive'] | int != 60) + or (bgp_session['holdtime'] is defined and bgp_session['holdtime'] | int != 180) %} + neighbor {{ neighbor_addr }} timers {{ bgp_session['keepalive'] }} {{ bgp_session['holdtime'] }} +{% endif %} +{% if bgp_session.has_key('admin_status') and bgp_session['admin_status'] == 'down' or not bgp_session.has_key('admin_status') and DEVICE_METADATA['localhost'].has_key('default_bgp_status') and DEVICE_METADATA['localhost']['default_bgp_status'] == 'down' %} + neighbor {{ neighbor_addr }} shutdown +{% endif %} +{% if neighbor_addr | ipv4 %} + address-family ipv4 +{% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} + neighbor {{ neighbor_addr }} allowas-in 1 +{% endif %} + neighbor {{ neighbor_addr }} activate + neighbor {{ neighbor_addr }} soft-reconfiguration inbound +{% if bgp_session['rrclient'] | int != 0 %} + neighbor {{ neighbor_addr }} route-reflector-client +{% endif %} +{% if bgp_session['nhopself'] | int != 0 %} + neighbor {{ neighbor_addr }} next-hop-self +{% endif %} + maximum-paths 64 + exit-address-family +{% endif %} +{% if neighbor_addr | ipv6 %} + address-family ipv6 +{% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} + neighbor {{ neighbor_addr }} allowas-in 1 +{% endif %} + neighbor {{ neighbor_addr }} activate + neighbor {{ neighbor_addr }} soft-reconfiguration inbound +{% if bgp_session['rrclient'] | int != 0 %} + neighbor {{ neighbor_addr }} route-reflector-client +{% endif %} +{% if bgp_session['nhopself'] | int != 0 %} + neighbor {{ neighbor_addr }} next-hop-self +{% endif %} +{% if bgp_session['asn'] != DEVICE_METADATA['localhost']['bgp_asn'] %} + neighbor {{ neighbor_addr }} route-map set-next-hop-global-v6 in +{% endif %} + maximum-paths 64 + exit-address-family +{% endif %} +{% endif %} +{% endfor %} +{% endblock bgp_sessions %} +{% block bgp_peers_with_range %} +{% if BGP_PEER_RANGE %} +{% for bgp_peer in BGP_PEER_RANGE.values() %} + neighbor {{ bgp_peer['name'] }} peer-group + neighbor {{ bgp_peer['name'] }} passive +{% if bgp_peer['peer_asn'] is defined %} + neighbor {{ bgp_peer['name'] }} remote-as {{ bgp_peer['peer_asn'] }} +{% else %} + neighbor {{ bgp_peer['name'] }} remote-as {{ deployment_id_asn_map[DEVICE_METADATA['localhost']['deployment_id']] }} +{% endif %} + neighbor {{ bgp_peer['name'] }} ebgp-multihop 255 + neighbor {{ bgp_peer['name'] }} soft-reconfiguration inbound +{% if bgp_peer['src_address'] is defined %} + neighbor {{ bgp_peer['name'] }} update-source {{ bgp_peer['src_address'] | ip }} +{% else %} +{% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} +{% if name == 'Loopback1' %} + neighbor {{ bgp_peer['name'] }} update-source {{ prefix | ip }} +{% endif %} +{% endfor %} +{% endif %} + neighbor {{ bgp_peer['name'] }} route-map FROM_BGP_SPEAKER_V4 in + neighbor {{ bgp_peer['name'] }} route-map TO_BGP_SPEAKER_V4 out +{% for ip_range in bgp_peer['ip_range'] %} + bgp listen range {{ip_range}} peer-group {{ bgp_peer['name'] }} +{% endfor %} + address-family ipv4 + neighbor {{ bgp_peer['name'] }} activate + maximum-paths 64 + exit-address-family + address-family ipv6 + neighbor {{ bgp_peer['name'] }} activate + maximum-paths 64 + exit-address-family +{% endfor %} +{% endif %} +{% endblock bgp_peers_with_range %} +! diff --git a/dockers/docker-fpm-frr/bgpd.conf.j2 b/dockers/docker-fpm-frr/bgpd.conf.j2 index 9153a24550..e9554806b6 100644 --- a/dockers/docker-fpm-frr/bgpd.conf.j2 +++ b/dockers/docker-fpm-frr/bgpd.conf.j2 @@ -15,140 +15,11 @@ agentx ! enable password {# {{ en_passwd }} TODO: param needed #} {% endblock system_init %} ! -{% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} -{% block bgp_init %} -! -! bgp multiple-instance -! -route-map FROM_BGP_SPEAKER_V4 permit 10 -! -route-map TO_BGP_SPEAKER_V4 deny 10 -! -router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} - bgp log-neighbor-changes - bgp bestpath as-path multipath-relax - no bgp default ipv4-unicast - bgp graceful-restart restart-time 240 - bgp graceful-restart -{% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} - bgp graceful-restart preserve-fw-state +{% if DEVICE_METADATA['localhost']['type'] == "SpineChassisFrontendRouter" %} +{% include "bgpd.conf.spine_chassis_frontend_router.j2" %} +{% else%} +{% include "bgpd.conf.default.j2" %} {% endif %} -{% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if prefix | ipv4 and name == 'Loopback0' %} - bgp router-id {{ prefix | ip }} -{% endif %} -{% endfor %} -{# advertise loopback #} -{% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if prefix | ipv4 and name == 'Loopback0' %} - network {{ prefix | ip }}/32 -{% elif prefix | ipv6 and name == 'Loopback0' %} - address-family ipv6 - network {{ prefix | ip }}/64 - exit-address-family -{% endif %} -{% endfor %} -{% endblock bgp_init %} -{% endif %} -{% block vlan_advertisement %} -{% for (name, prefix) in VLAN_INTERFACE|pfx_filter %} -{% if prefix | ipv4 %} - network {{ prefix }} -{% elif prefix | ipv6 %} - address-family ipv6 - network {{ prefix }} - exit-address-family -{% endif %} -{% endfor %} -{% endblock vlan_advertisement %} -{% block bgp_sessions %} -{% for neighbor_addr, bgp_session in BGP_NEIGHBOR.iteritems() %} -{% if bgp_session['asn'] | int != 0 %} - neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} - neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} -{# set the bgp neighbor timers if they have not default values #} -{% if (bgp_session['keepalive'] is defined and bgp_session['keepalive'] | int != 60) - or (bgp_session['holdtime'] is defined and bgp_session['holdtime'] | int != 180) %} - neighbor {{ neighbor_addr }} timers {{ bgp_session['keepalive'] }} {{ bgp_session['holdtime'] }} -{% endif %} -{% if bgp_session.has_key('admin_status') and bgp_session['admin_status'] == 'down' or not bgp_session.has_key('admin_status') and DEVICE_METADATA['localhost'].has_key('default_bgp_status') and DEVICE_METADATA['localhost']['default_bgp_status'] == 'down' %} - neighbor {{ neighbor_addr }} shutdown -{% endif %} -{% if neighbor_addr | ipv4 %} - address-family ipv4 -{% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} - neighbor {{ neighbor_addr }} allowas-in 1 -{% endif %} - neighbor {{ neighbor_addr }} activate - neighbor {{ neighbor_addr }} soft-reconfiguration inbound -{% if bgp_session['rrclient'] | int != 0 %} - neighbor {{ neighbor_addr }} route-reflector-client -{% endif %} -{% if bgp_session['nhopself'] | int != 0 %} - neighbor {{ neighbor_addr }} next-hop-self -{% endif %} - maximum-paths 64 - exit-address-family -{% endif %} -{% if neighbor_addr | ipv6 %} - address-family ipv6 -{% if DEVICE_METADATA['localhost']['type'] == 'ToRRouter' %} - neighbor {{ neighbor_addr }} allowas-in 1 -{% endif %} - neighbor {{ neighbor_addr }} activate - neighbor {{ neighbor_addr }} soft-reconfiguration inbound -{% if bgp_session['rrclient'] | int != 0 %} - neighbor {{ neighbor_addr }} route-reflector-client -{% endif %} -{% if bgp_session['nhopself'] | int != 0 %} - neighbor {{ neighbor_addr }} next-hop-self -{% endif %} -{% if bgp_session['asn'] != DEVICE_METADATA['localhost']['bgp_asn'] %} - neighbor {{ neighbor_addr }} route-map set-next-hop-global-v6 in -{% endif %} - maximum-paths 64 - exit-address-family -{% endif %} -{% endif %} -{% endfor %} -{% endblock bgp_sessions %} -{% block bgp_peers_with_range %} -{% if BGP_PEER_RANGE %} -{% for bgp_peer in BGP_PEER_RANGE.values() %} - neighbor {{ bgp_peer['name'] }} peer-group - neighbor {{ bgp_peer['name'] }} passive -{% if bgp_peer['peer_asn'] is defined %} - neighbor {{ bgp_peer['name'] }} remote-as {{ bgp_peer['peer_asn'] }} -{% else %} - neighbor {{ bgp_peer['name'] }} remote-as {{ deployment_id_asn_map[DEVICE_METADATA['localhost']['deployment_id']] }} -{% endif %} - neighbor {{ bgp_peer['name'] }} ebgp-multihop 255 - neighbor {{ bgp_peer['name'] }} soft-reconfiguration inbound -{% if bgp_peer['src_address'] is defined %} - neighbor {{ bgp_peer['name'] }} update-source {{ bgp_peer['src_address'] | ip }} -{% else %} -{% for (name, prefix) in LOOPBACK_INTERFACE|pfx_filter %} -{% if name == 'Loopback1' %} - neighbor {{ bgp_peer['name'] }} update-source {{ prefix | ip }} -{% endif %} -{% endfor %} -{% endif %} - neighbor {{ bgp_peer['name'] }} route-map FROM_BGP_SPEAKER_V4 in - neighbor {{ bgp_peer['name'] }} route-map TO_BGP_SPEAKER_V4 out -{% for ip_range in bgp_peer['ip_range'] %} - bgp listen range {{ip_range}} peer-group {{ bgp_peer['name'] }} -{% endfor %} - address-family ipv4 - neighbor {{ bgp_peer['name'] }} activate - maximum-paths 64 - exit-address-family - address-family ipv6 - neighbor {{ bgp_peer['name'] }} activate - maximum-paths 64 - exit-address-family -{% endfor %} -{% endif %} -{% endblock bgp_peers_with_range %} ! {% if DEVICE_METADATA['localhost'].has_key('bgp_asn') %} maximum-paths 64 diff --git a/dockers/docker-fpm-frr/bgpd.conf.spine_chassis_frontend_router.j2 b/dockers/docker-fpm-frr/bgpd.conf.spine_chassis_frontend_router.j2 new file mode 100644 index 0000000000..9bd5ef1947 --- /dev/null +++ b/dockers/docker-fpm-frr/bgpd.conf.spine_chassis_frontend_router.j2 @@ -0,0 +1,128 @@ +{# VNET BGP Instance #} +! Vnet BGP instance +{% set interfaces_in_vnets = [] %} +{% block vnet_bgp_instance %} +{% for vnet_name, vnet_metadata in VNET.iteritems() %} +router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} vrf {{ vnet_name }} + no bgp default ipv4-unicast + bgp log-neighbor-changes + bgp bestpath as-path multipath-relax + no bgp default ipv4-unicast + bgp graceful-restart restart-time 240 + bgp graceful-restart +{# Router ID #} +{% for (name, prefix) in LOOPBACK_INTERFACE | pfx_filter %} +{% if prefix | ipv4 and name == 'Loopback0' %} + bgp router-id {{ prefix | ip }} +{% endif %} +{% endfor %} +{# Got interfaces that belong this vnet #} +{% set interfaces_in_vnet = [] %} +{% for (key, metadata) in INTERFACE.iteritems() %} +{% if metadata.has_key("vnet_name") and metadata["vnet_name"] == vnet_name %} +{% for (name_prefix_pair, metadata) in INTERFACE.iteritems() %} +{% if key == name_prefix_pair[0] %} +{% if interfaces_in_vnet.append( name_prefix_pair[1] | ip ) %} +{% endif %} +{% if interfaces_in_vnets.append( name_prefix_pair[1] | ip ) %} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} +{# Each bgp neighbors #} +{% for neighbor_addr, bgp_session in BGP_NEIGHBOR.iteritems() %} +{% if bgp_session.has_key("local_addr") and bgp_session["local_addr"] in interfaces_in_vnet %} +{% if bgp_session['asn'] | int != 0 %} + neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} + neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} +{# set the bgp neighbor timers if they have not default values #} +{% if (bgp_session['keepalive'] is defined and bgp_session['keepalive'] | int != 60) + or (bgp_session['holdtime'] is defined and bgp_session['holdtime'] | int != 180) %} + neighbor {{ neighbor_addr }} timers {{ bgp_session['keepalive'] }} {{ bgp_session['holdtime'] }} +{% endif %} +{% if bgp_session.has_key('admin_status') and bgp_session['admin_status'] == 'down' or not bgp_session.has_key('admin_status') and DEVICE_METADATA['localhost'].has_key('default_bgp_status') and DEVICE_METADATA['localhost']['default_bgp_status'] == 'down' %} + neighbor {{ neighbor_addr }} shutdown +{% endif %} +{% if neighbor_addr | ipv4 %} + address-family ipv4 unicast + neighbor {{ neighbor_addr }} activate + neighbor {{ neighbor_addr }} soft-reconfiguration inbound + maximum-paths 64 + exit-address-family +{% endif %} + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family +{% endif %} +{% endif %} +{% endfor %} +{% endfor %} +{% endblock vnet_bgp_instance %} + +{# default bgp #} +{% block default_bgp_instance %} +{% block bgp_init %} +! +! bgp multiple-instance +! +route-map FROM_BGP_SPEAKER_V4 permit 10 +! +route-map TO_BGP_SPEAKER_V4 deny 10 +! +router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} + bgp log-neighbor-changes + bgp bestpath as-path multipath-relax + no bgp default ipv4-unicast + bgp graceful-restart restart-time 240 + bgp graceful-restart +{% for (name, prefix) in LOOPBACK_INTERFACE | pfx_filter %} +{% if prefix | ipv4 and name == 'Loopback0' %} + bgp router-id {{ prefix | ip }} +{% endif %} +{% endfor %} +{# advertise loopback #} +{% for (name, prefix) in LOOPBACK_INTERFACE | pfx_filter %} +{% if prefix | ipv4 and name == 'Loopback0' %} + network {{ prefix | ip }}/32 +{% elif prefix | ipv6 and name == 'Loopback0' %} + address-family ipv6 + network {{ prefix | ip }}/64 + exit-address-family +{% endif %} +{% endfor %} +{% endblock bgp_init %} +{% block bgp_sessions %} +{% for neighbor_addr, bgp_session in BGP_NEIGHBOR.iteritems() %} +{% if not bgp_session.has_key("local_addr") or bgp_session["local_addr"] not in interfaces_in_vnets %} +{% if bgp_session['asn'] | int != 0 %} + neighbor {{ neighbor_addr }} remote-as {{ bgp_session['asn'] }} + neighbor {{ neighbor_addr }} description {{ bgp_session['name'] }} +{# set the bgp neighbor timers if they have not default values #} +{% if (bgp_session['keepalive'] is defined and bgp_session['keepalive'] | int != 60) + or (bgp_session['holdtime'] is defined and bgp_session['holdtime'] | int != 180) %} + neighbor {{ neighbor_addr }} timers {{ bgp_session['keepalive'] }} {{ bgp_session['holdtime'] }} +{% endif %} +{% if bgp_session.has_key('admin_status') and bgp_session['admin_status'] == 'down' or not bgp_session.has_key('admin_status') and DEVICE_METADATA['localhost'].has_key('default_bgp_status') and DEVICE_METADATA['localhost']['default_bgp_status'] == 'down' %} + neighbor {{ neighbor_addr }} shutdown +{% endif %} +{% if bgp_session["asn"] != DEVICE_METADATA['localhost']['bgp_asn'] %} +{% if neighbor_addr | ipv4 %} + address-family ipv4 unicast + neighbor {{ neighbor_addr }} allowas-in 1 + neighbor {{ neighbor_addr }} activate + neighbor {{ neighbor_addr }} soft-reconfiguration inbound + maximum-paths 64 + exit-address-family +{% endif %} +{% else %} + address-family l2vpn evpn + neighbor {{ neighbor_addr }} activate + advertise-all-vni + exit-address-family +{% endif %} +{% endif %} +{% endif %} +{% endfor %} +{% endblock bgp_sessions %} +{% endblock default_bgp_instance %} diff --git a/dockers/docker-fpm-frr/zebra.conf.j2 b/dockers/docker-fpm-frr/zebra.conf.j2 index c0357eaed8..7200d81fda 100644 --- a/dockers/docker-fpm-frr/zebra.conf.j2 +++ b/dockers/docker-fpm-frr/zebra.conf.j2 @@ -12,6 +12,16 @@ password zebra enable password zebra {% endblock sys_init %} ! +{% block vrf %} +{% if VNET is defined %} +{% for vnet_name, vnet_metadata in VNET.iteritems() %} +vrf {{ vnet_name }} +vni {{ vnet_metadata['vni'] }} +! +{% endfor %} +{% endif %} +{% endblock vrf %} +! {% block interfaces %} ! Enable link-detect (default disabled) {% for (name, prefix) in INTERFACE|pfx_filter %} diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index 8d08b399b4..4c1238a215 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -26,6 +26,13 @@ ns1 = "http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolut ns2 = "Microsoft.Search.Autopilot.NetMux" ns3 = "http://www.w3.org/2001/XMLSchema-instance" +# Device types +spine_chassis_frontend_role = 'SpineChassisFrontendRouter' +chassis_backend_role = 'ChassisBackendRouter' + +# Default Virtual Network Index (VNI) +vni_default = 8000 + class minigraph_encoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, ( @@ -149,6 +156,14 @@ def parse_dpg(dpg, hname): if hostname.text.lower() != hname.lower(): continue + vni = vni_default + vni_element = child.find(str(QName(ns, "VNI"))) + if vni_element != None: + if vni_element.text.isdigit(): + vni = int(vni_element.text) + else: + print >> sys.stderr, "VNI must be an integer (use default VNI %d instead)" % vni_default + ipintfs = child.find(str(QName(ns, "IPInterfaces"))) intfs = {} for ipintf in ipintfs.findall(str(QName(ns, "IPInterface"))): @@ -290,8 +305,8 @@ def parse_dpg(dpg, hname): except: print >> sys.stderr, "Warning: Ignoring Control Plane ACL %s without type" % aclname - return intfs, lo_intfs, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls - return None, None, None, None, None, None, None + return intfs, lo_intfs, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni + return None, None, None, None, None, None, None, None, None def parse_cpg(cpg, hname): @@ -413,6 +428,88 @@ def parse_deviceinfo(meta, hwsku): port_speeds[port_alias_map.get(alias, alias)] = speed return port_speeds, port_descriptions +# Function to check if IP address is present in the key. +# If it is present, then the key would be a tuple. +def is_ip_prefix_in_key(key): + return (isinstance(key, tuple)) + +# Special parsing for spine chassis frontend +def parse_spine_chassis_fe(results, vni, lo_intfs, phyport_intfs, pc_intfs, pc_members, devices): + chassis_vnet ='VnetFE' + chassis_vxlan_tunnel = 'TunnelInt' + chassis_vni = vni + + # Vxlan tunnel information + lo_addr = '0.0.0.0' + for lo in lo_intfs: + lo_network = ipaddress.IPNetwork(lo[1]) + if lo_network.version == 4: + lo_addr = str(lo_network.ip) + break + + results['VXLAN_TUNNEL'] = {chassis_vxlan_tunnel: { + 'source_ip': lo_addr + }} + + # Vnet information + results['VNET'] = {chassis_vnet: { + 'vxlan_tunnel': chassis_vxlan_tunnel, + 'vni': chassis_vni + }} + + # Find L3 physical interfaces that should be enslaved to Vnet + for intf in phyport_intfs: + # We only care about L3 physical interfaces + if is_ip_prefix_in_key(intf) == False: + continue + + # intf = (intf name, IP prefix) + intf_name = intf[0] + neighbor_router = results['DEVICE_NEIGHBOR'][intf_name]['name'] + + # If the neighbor router is an external router + if devices[neighbor_router]['type'] != chassis_backend_role: + + # Enslave the interface to a Vnet + if intf_name in phyport_intfs: + phyport_intfs[intf_name] = {'vnet_name': chassis_vnet} + else: + print >> sys.stderr, 'Warning: cannot find the key %s' % (intf_name) + + # Find L3 port chennel interfaces that should be enslaved to Vnet + for pc_intf in pc_intfs: + # We only care about L3 port channel interfaces + if is_ip_prefix_in_key(pc_intf) == False: + continue + + # Get port channel interface name + # pc intf = (pc intf name, IP prefix) + pc_intf_name = pc_intf[0] + + intf_name = None + # Get a physical interface that belongs to this port channel + for pc_member in pc_members: + if pc_member[0] == pc_intf_name: + intf_name = pc_member[1] + break + + if intf_name == None: + print >> sys.stderr, 'Warning: cannot find any interfaces that belong to %s' % (pc_intf_name) + continue + + # Get the neighbor router of this port channel interface + neighbor_router = results['DEVICE_NEIGHBOR'][intf_name]['name'] + + # If the neighbor router is an external router + if devices[neighbor_router]['type'] != chassis_backend_role: + + # Enslave the port channel interface to a Vnet + if pc_intf_name in pc_intfs: + pc_intfs[pc_intf_name] = {'vnet_name': chassis_vnet} + else: + print >> sys.stderr, 'Warning: cannot find the key %s' % (pc_intf_name) + + def parse_xml(filename, platform=None, port_config_file=None): root = ET.parse(filename).getroot() mini_graph_path = filename @@ -462,7 +559,7 @@ def parse_xml(filename, platform=None, port_config_file=None): port_alias_map.update(alias_map) for child in root: if child.tag == str(QName(ns, "DpgDec")): - (intfs, lo_intfs, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls) = parse_dpg(child, hostname) + (intfs, lo_intfs, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni) = parse_dpg(child, hostname) elif child.tag == str(QName(ns, "CpgDec")): (bgp_sessions, bgp_asn, bgp_peers_with_range) = parse_cpg(child, hostname) elif child.tag == str(QName(ns, "PngDec")): @@ -643,6 +740,10 @@ def parse_xml(filename, platform=None, port_config_file=None): count += 1 results['MIRROR_SESSION'] = mirror_sessions + # Special parsing for spine chassis frontend routers + if current_device['type'] == spine_chassis_frontend_role: + parse_spine_chassis_fe(results, vni, lo_intfs, phyport_intfs, pc_intfs, pc_members, devices) + return results 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 new file mode 100644 index 0000000000..515e0aba8d --- /dev/null +++ b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-bgpd.conf @@ -0,0 +1,82 @@ +! +! =========== Managed by sonic-cfggen DO NOT edit manually! ==================== +! generated by templates/quagga/bgpd.conf.j2 with config DB data +! file: bgpd.conf +! +! +hostname SpineFront01 +password zebra +log syslog informational +log facility local4 +agentx +! enable password ! +! Vnet BGP instance +router bgp 4000 vrf VnetFE + no bgp default ipv4-unicast + bgp log-neighbor-changes + bgp bestpath as-path multipath-relax + no bgp default ipv4-unicast + bgp graceful-restart restart-time 240 + bgp graceful-restart + bgp router-id 4.0.0.0 + neighbor 192.168.0.1 remote-as 3000 + neighbor 192.168.0.1 description Leaf01 + neighbor 192.168.0.1 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.0.1 activate + neighbor 192.168.0.1 soft-reconfiguration inbound + maximum-paths 64 + exit-address-family + address-family l2vpn evpn + advertise ipv4 unicast + exit-address-family + +! +! bgp multiple-instance +! +route-map FROM_BGP_SPEAKER_V4 permit 10 +! +route-map TO_BGP_SPEAKER_V4 deny 10 +! +router bgp 4000 + bgp log-neighbor-changes + bgp bestpath as-path multipath-relax + no bgp default ipv4-unicast + bgp graceful-restart restart-time 240 + bgp graceful-restart + bgp router-id 4.0.0.0 + network 4.0.0.0/32 + neighbor 4.0.0.1 remote-as 4000 + neighbor 4.0.0.1 description SpineFront02 + neighbor 4.0.0.1 timers 3 10 + address-family l2vpn evpn + neighbor 4.0.0.1 activate + advertise-all-vni + exit-address-family + neighbor 172.16.0.2 remote-as 5000 + neighbor 172.16.0.2 description SpineBack01 + neighbor 172.16.0.2 timers 3 10 + address-family ipv4 unicast + neighbor 172.16.0.2 allowas-in 1 + neighbor 172.16.0.2 activate + neighbor 172.16.0.2 soft-reconfiguration inbound + maximum-paths 64 + exit-address-family + neighbor 172.16.0.10 remote-as 5000 + neighbor 172.16.0.10 description SpineBack02 + neighbor 172.16.0.10 timers 3 10 + address-family ipv4 unicast + neighbor 172.16.0.10 allowas-in 1 + neighbor 172.16.0.10 activate + neighbor 172.16.0.10 soft-reconfiguration inbound + maximum-paths 64 + exit-address-family +! +maximum-paths 64 +! +route-map ISOLATE permit 10 +set as-path prepend 4000 +! +route-map set-next-hop-global-v6 permit 10 +set ipv6 next-hop prefer-global +! 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 new file mode 100644 index 0000000000..8861e6d301 --- /dev/null +++ b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-pc-zebra.conf @@ -0,0 +1,38 @@ +! +! =========== Managed by sonic-cfggen DO NOT edit manually! ==================== +! generated by templates/quagga/zebra.conf.j2 using config DB data +! file: zebra.conf +! +! +hostname SpineFront01 +password zebra +enable password zebra +! +vrf VnetFE +vni 8000 +! +! +! Enable link-detect (default disabled) +interface PortChannel0 +link-detect +! +interface PortChannel4 +link-detect +! +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 + set src 4.0.0.0 +! +ip protocol bgp route-map RM_SET_SRC +! +! +log syslog informational +log facility local4 +! + 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 new file mode 100644 index 0000000000..1f9dce8812 --- /dev/null +++ b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-vni-zebra.conf @@ -0,0 +1,38 @@ +! +! =========== Managed by sonic-cfggen DO NOT edit manually! ==================== +! generated by templates/quagga/zebra.conf.j2 using config DB data +! file: zebra.conf +! +! +hostname SpineFront01 +password zebra +enable password zebra +! +vrf VnetFE +vni 9000 +! +! +! Enable link-detect (default disabled) +interface Ethernet0 +link-detect +! +interface Ethernet4 +link-detect +! +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 + set src 4.0.0.0 +! +ip protocol bgp route-map RM_SET_SRC +! +! +log syslog informational +log facility local4 +! + 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 new file mode 100644 index 0000000000..b89aeb4a38 --- /dev/null +++ b/src/sonic-config-engine/tests/sample_output/t2-chassis-fe-zebra.conf @@ -0,0 +1,38 @@ +! +! =========== Managed by sonic-cfggen DO NOT edit manually! ==================== +! generated by templates/quagga/zebra.conf.j2 using config DB data +! file: zebra.conf +! +! +hostname SpineFront01 +password zebra +enable password zebra +! +vrf VnetFE +vni 8000 +! +! +! Enable link-detect (default disabled) +interface Ethernet0 +link-detect +! +interface Ethernet4 +link-detect +! +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 + set src 4.0.0.0 +! +ip protocol bgp route-map RM_SET_SRC +! +! +log syslog informational +log facility local4 +! + diff --git a/src/sonic-config-engine/tests/t2-chassis-fe-graph-pc.xml b/src/sonic-config-engine/tests/t2-chassis-fe-graph-pc.xml new file mode 100644 index 0000000000..3fb744ce91 --- /dev/null +++ b/src/sonic-config-engine/tests/t2-chassis-fe-graph-pc.xml @@ -0,0 +1,324 @@ + + + + + + SpineFront01 + 192.168.0.2 + Leaf01 + 192.168.0.1 + 1 + 10 + 3 + + + SpineFront01 + 172.16.0.1 + SpineBack01 + 172.16.0.2 + 1 + 10 + 3 + + + SpineFront01 + 172.16.0.9 + SpineBack02 + 172.16.0.10 + 1 + 10 + 3 + + + SpineFront01 + 4.0.0.0 + SpineFront02 + 4.0.0.1 + 10 + 10 + 3 + + + + + 4000 + SpineFront01 + + +
192.168.0.1
+ + + +
+ +
172.16.0.2
+ + + +
+ +
172.16.0.10
+ + + +
+ +
4.0.0.1
+ + + +
+
+ +
+ + 3000 + Leaf01 + + + + 5000 + SpineBack01 + + + + 5000 + SpineBack02 + + + + 4000 + SpineFront02 + + +
+
+ + + + + + HostIP + Loopback0 + + 4.0.0.0/32 + + 4.0.0.0/32 + + + + + + + + SpineFront01 + + + PortChannel0 + Ethernet0;Ethernet4 + + + + PortChannel4 + Ethernet8;Ethernet12 + + + + PortChannel8 + Ethernet16;Ethernet20 + + + + + + + + PortChannel0 + 192.168.0.2/30 + + + + PortChannel4 + 172.16.0.1/30 + + + + PortChannel8 + 172.16.0.9/30 + + + + + + + + + + + + DeviceInterfaceLink + Leaf01 + Ethernet0 + SpineFront01 + Ethernet0 + + + DeviceInterfaceLink + Leaf01 + Ethernet4 + SpineFront01 + Ethernet4 + + + DeviceInterfaceLink + SpineBack01 + Ethernet8 + SpineFront01 + Ethernet8 + + + DeviceInterfaceLink + SpineBack01 + Ethernet12 + SpineFront01 + Ethernet12 + + + DeviceInterfaceLink + SpineBack02 + Ethernet16 + SpineFront01 + Ethernet16 + + + DeviceInterfaceLink + SpineBack02 + Ethernet20 + SpineFront01 + Ethernet20 + + + + + SpineFront01 + Force10-S6000 + + + SpineFront02 + Force10-S6000 + + + Leaf01 + Force10-S6000 + + + SpineBack01 + Force10-S6000 + + + SpineBack02 + Force10-S6000 + + + + + + true + + + DeviceInterface + + true + false + 1 + Ethernet0 + + false + 0 + 0 + 25000 + + + DeviceInterface + + true + false + 1 + Ethernet4 + + false + 0 + 0 + 25000 + + + DeviceInterface + + true + false + 1 + Ethernet8 + + false + 0 + 0 + 25000 + + + DeviceInterface + + true + false + 1 + Ethernet12 + + false + 0 + 0 + 25000 + + + DeviceInterface + + true + false + 1 + Ethernet16 + + false + 0 + 0 + 25000 + + + DeviceInterface + + true + false + 1 + Ethernet20 + + false + 0 + 0 + 25000 + + + false + 0 + Force10-S6000 + + + + + + + SpineFront01 + + + + + + SpineFront01 + Force10-S6000 +
diff --git a/src/sonic-config-engine/tests/t2-chassis-fe-graph-vni.xml b/src/sonic-config-engine/tests/t2-chassis-fe-graph-vni.xml new file mode 100644 index 0000000000..f820b180f7 --- /dev/null +++ b/src/sonic-config-engine/tests/t2-chassis-fe-graph-vni.xml @@ -0,0 +1,249 @@ + + + + + + SpineFront01 + 192.168.0.2 + Leaf01 + 192.168.0.1 + 1 + 10 + 3 + + + SpineFront01 + 172.16.0.1 + SpineBack01 + 172.16.0.2 + 1 + 10 + 3 + + + SpineFront01 + 172.16.0.9 + SpineBack02 + 172.16.0.10 + 1 + 10 + 3 + + + SpineFront01 + 4.0.0.0 + SpineFront02 + 4.0.0.1 + 10 + 10 + 3 + + + + + 4000 + SpineFront01 + + +
192.168.0.1
+ + + +
+ +
172.16.0.2
+ + + +
+ +
172.16.0.10
+ + + +
+ +
4.0.0.1
+ + + +
+
+ +
+ + 3000 + Leaf01 + + + + 5000 + SpineBack01 + + + + 5000 + SpineBack02 + + + + 4000 + SpineFront02 + + +
+
+ + + 9000 + + + + HostIP + Loopback0 + + 4.0.0.0/32 + + 4.0.0.0/32 + + + + + + + + SpineFront01 + + + + + + Ethernet0 + 192.168.0.2/30 + + + + Ethernet4 + 172.16.0.1/30 + + + + Ethernet8 + 172.16.0.9/30 + + + + + + + + + + + + DeviceInterfaceLink + Leaf01 + Ethernet4 + SpineFront01 + Ethernet0 + + + DeviceInterfaceLink + SpineBack01 + Ethernet0 + SpineFront01 + Ethernet4 + + + DeviceInterfaceLink + SpineBack02 + Ethernet4 + SpineFront01 + Ethernet8 + + + + + SpineFront01 + Force10-S6000 + + + SpineFront02 + Force10-S6000 + + + Leaf01 + Force10-S6000 + + + SpineBack01 + Force10-S6000 + + + SpineBack02 + Force10-S6000 + + + + + + true + + + DeviceInterface + + true + false + 1 + Ethernet0 + + false + 0 + 0 + 40000 + + + DeviceInterface + + true + false + 1 + Ethernet4 + + false + 0 + 0 + 40000 + + + DeviceInterface + + true + false + 1 + Ethernet8 + + false + 0 + 0 + 40000 + + + false + 0 + Force10-S6000 + + + + + + + SpineFront01 + + + + + + SpineFront01 + Force10-S6000 +
diff --git a/src/sonic-config-engine/tests/t2-chassis-fe-graph.xml b/src/sonic-config-engine/tests/t2-chassis-fe-graph.xml new file mode 100644 index 0000000000..76320b766b --- /dev/null +++ b/src/sonic-config-engine/tests/t2-chassis-fe-graph.xml @@ -0,0 +1,248 @@ + + + + + + SpineFront01 + 192.168.0.2 + Leaf01 + 192.168.0.1 + 1 + 10 + 3 + + + SpineFront01 + 172.16.0.1 + SpineBack01 + 172.16.0.2 + 1 + 10 + 3 + + + SpineFront01 + 172.16.0.9 + SpineBack02 + 172.16.0.10 + 1 + 10 + 3 + + + SpineFront01 + 4.0.0.0 + SpineFront02 + 4.0.0.1 + 10 + 10 + 3 + + + + + 4000 + SpineFront01 + + +
192.168.0.1
+ + + +
+ +
172.16.0.2
+ + + +
+ +
172.16.0.10
+ + + +
+ +
4.0.0.1
+ + + +
+
+ +
+ + 3000 + Leaf01 + + + + 5000 + SpineBack01 + + + + 5000 + SpineBack02 + + + + 4000 + SpineFront02 + + +
+
+ + + + + + HostIP + Loopback0 + + 4.0.0.0/32 + + 4.0.0.0/32 + + + + + + + + SpineFront01 + + + + + + Ethernet0 + 192.168.0.2/30 + + + + Ethernet4 + 172.16.0.1/30 + + + + Ethernet8 + 172.16.0.9/30 + + + + + + + + + + + + DeviceInterfaceLink + Leaf01 + Ethernet4 + SpineFront01 + Ethernet0 + + + DeviceInterfaceLink + SpineBack01 + Ethernet0 + SpineFront01 + Ethernet4 + + + DeviceInterfaceLink + SpineBack02 + Ethernet4 + SpineFront01 + Ethernet8 + + + + + SpineFront01 + Force10-S6000 + + + SpineFront02 + Force10-S6000 + + + Leaf01 + Force10-S6000 + + + SpineBack01 + Force10-S6000 + + + SpineBack02 + Force10-S6000 + + + + + + true + + + DeviceInterface + + true + false + 1 + Ethernet0 + + false + 0 + 0 + 40000 + + + DeviceInterface + + true + false + 1 + Ethernet4 + + false + 0 + 0 + 40000 + + + DeviceInterface + + true + false + 1 + Ethernet8 + + false + 0 + 0 + 40000 + + + false + 0 + Force10-S6000 + + + + + + + SpineFront01 + + + + + + SpineFront01 + Force10-S6000 +
diff --git a/src/sonic-config-engine/tests/t2-chassis-fe-port-config.ini b/src/sonic-config-engine/tests/t2-chassis-fe-port-config.ini new file mode 100644 index 0000000000..1a9786c590 --- /dev/null +++ b/src/sonic-config-engine/tests/t2-chassis-fe-port-config.ini @@ -0,0 +1,33 @@ +# name lanes +Ethernet0 0,1,2,3 +Ethernet4 4,5,6,7 +Ethernet8 8,9,10,11 +Ethernet12 12,13,14,15 +Ethernet16 16,17,18,19 +Ethernet20 20,21,22,23 +Ethernet24 24,25,26,27 +Ethernet28 28,29,30,31 +Ethernet32 32,33,34,35 +Ethernet36 36,37,38,39 +Ethernet40 40,41,42,43 +Ethernet44 44,45,46,47 +Ethernet48 48,49,50,51 +Ethernet52 52,53,54,55 +Ethernet56 56,57,58,59 +Ethernet60 60,61,62,63 +Ethernet64 64,65,66,67 +Ethernet68 68,69,70,71 +Ethernet72 72,73,74,75 +Ethernet76 76,77,78,79 +Ethernet80 80,81,82,83 +Ethernet84 84,85,86,87 +Ethernet88 88,89,90,91 +Ethernet92 92,93,94,95 +Ethernet96 96,97,98,99 +Ethernet100 100,101,102,103 +Ethernet104 104,105,106,107 +Ethernet108 108,109,110,111 +Ethernet112 112,113,114,115 +Ethernet116 116,117,118,119 +Ethernet120 120,121,122,123 +Ethernet124 124,125,126,127 \ No newline at end of file diff --git a/src/sonic-config-engine/tests/test_cfggen.py b/src/sonic-config-engine/tests/test_cfggen.py index f267651ff6..eedaf67827 100644 --- a/src/sonic-config-engine/tests/test_cfggen.py +++ b/src/sonic-config-engine/tests/test_cfggen.py @@ -15,7 +15,7 @@ class TestCfgGen(TestCase): self.sample_graph_bgp_speaker = os.path.join(self.test_dir, 't0-sample-bgp-speaker.xml') self.sample_device_desc = os.path.join(self.test_dir, 'device.xml') self.port_config = os.path.join(self.test_dir, 't0-sample-port-config.ini') - + def run_script(self, argument, check_stderr=False): print '\n Running sonic-cfggen ' + argument if check_stderr: @@ -239,3 +239,12 @@ class TestCfgGen(TestCase): output = self.run_script(argument) self.assertEqual(output.strip(), "{'10.0.10.1': {}, '10.0.10.2': {}}") + def test_minigraph_vnet(self): + argument = '-m "' + self.sample_graph_simple + '" -p "' + self.port_config + '" -v "VNET"' + output = self.run_script(argument) + self.assertEqual(output.strip(), "") + + def test_minigraph_vxlan(self): + argument = '-m "' + self.sample_graph_simple + '" -p "' + self.port_config + '" -v "VXLAN_TUNNEL"' + output = self.run_script(argument) + self.assertEqual(output.strip(), "") \ No newline at end of file diff --git a/src/sonic-config-engine/tests/test_cfggen_t2_chassis_fe.py b/src/sonic-config-engine/tests/test_cfggen_t2_chassis_fe.py new file mode 100644 index 0000000000..cc438896a0 --- /dev/null +++ b/src/sonic-config-engine/tests/test_cfggen_t2_chassis_fe.py @@ -0,0 +1,72 @@ +from unittest import TestCase +import subprocess +import os + +class TestCfgGenT2ChassisFe(TestCase): + + def setUp(self): + self.test_dir = os.path.dirname(os.path.realpath(__file__)) + self.script_file = os.path.join(self.test_dir, '..', 'sonic-cfggen') + self.sample_graph_t2_chassis_fe = os.path.join(self.test_dir, 't2-chassis-fe-graph.xml') + self.sample_graph_t2_chassis_fe_vni = os.path.join(self.test_dir, 't2-chassis-fe-graph-vni.xml') + self.sample_graph_t2_chassis_fe_pc = os.path.join(self.test_dir, 't2-chassis-fe-graph-pc.xml') + self.t2_chassis_fe_port_config = os.path.join(self.test_dir, 't2-chassis-fe-port-config.ini') + + def run_script(self, argument, check_stderr=False): + print '\n Running sonic-cfggen ' + argument + if check_stderr: + output = subprocess.check_output(self.script_file + ' ' + argument, stderr=subprocess.STDOUT, shell=True) + else: + output = subprocess.check_output(self.script_file + ' ' + argument, shell=True) + + linecount = output.strip().count('\n') + if linecount <= 0: + print ' Output: ' + output.strip() + else: + print ' Output: ({0} lines, {1} bytes)'.format(linecount + 1, len(output)) + return output + + def test_minigraph_t2_chassis_fe_type(self): + argument = '-m "' + self.sample_graph_t2_chassis_fe + '" -p "' + self.t2_chassis_fe_port_config + '" -v "DEVICE_METADATA[\'localhost\'][\'type\']"' + output = self.run_script(argument) + self.assertEqual(output.strip(), 'SpineChassisFrontendRouter') + + def test_minigraph_t2_chassis_fe_interfaces(self): + argument = '-m "' + self.sample_graph_t2_chassis_fe + '" -p "' + self.t2_chassis_fe_port_config + '" -v "INTERFACE"' + output = self.run_script(argument) + self.assertEqual(output.strip(), + "{'Ethernet8': {}, " + "('Ethernet8', '172.16.0.9/30'): {}, " + "'Ethernet0': {'vnet_name': 'VnetFE'}, " + "('Ethernet4', '172.16.0.1/30'): {}, " + "('Ethernet0', '192.168.0.2/30'): {}, " + "'Ethernet4': {}}") + + def test_minigraph_t2_chassis_fe_pc_interfaces(self): + argument = '-m "' + self.sample_graph_t2_chassis_fe_pc + '" -p "' + self.t2_chassis_fe_port_config + '" -v "PORTCHANNEL_INTERFACE"' + output = self.run_script(argument) + self.assertEqual(output.strip(), + "{'PortChannel8': {}, " + "('PortChannel0', '192.168.0.2/30'): {}, " + "('PortChannel4', '172.16.0.1/30'): {}, " + "'PortChannel4': {}, " + "('PortChannel8', '172.16.0.9/30'): {}, " + "'PortChannel0': {'vnet_name': 'VnetFE'}}") + + # Test a minigraph file where VNI is not specified + # Default VNI is 8000 + def test_minigraph_t2_chassis_fe_vnet_default(self): + argument = '-m "' + self.sample_graph_t2_chassis_fe + '" -p "' + self.t2_chassis_fe_port_config + '" -v "VNET"' + output = self.run_script(argument) + self.assertEqual(output.strip(), "{'VnetFE': {'vxlan_tunnel': 'TunnelInt', 'vni': 8000}}") + + # Test a minigraph file where VNI is specified + def test_minigraph_t2_chassis_fe_vnet(self): + argument = '-m "' + self.sample_graph_t2_chassis_fe_vni + '" -p "' + self.t2_chassis_fe_port_config + '" -v "VNET"' + output = self.run_script(argument) + self.assertEqual(output.strip(), "{'VnetFE': {'vxlan_tunnel': 'TunnelInt', 'vni': 9000}}") + + def test_minigraph_t2_chassis_fe_vxlan(self): + argument = '-m "' + self.sample_graph_t2_chassis_fe + '" -p "' + self.t2_chassis_fe_port_config + '" -v "VXLAN_TUNNEL"' + output = self.run_script(argument) + self.assertEqual(output.strip(), "{'TunnelInt': {'source_ip': '4.0.0.0'}}") \ No newline at end of file diff --git a/src/sonic-config-engine/tests/test_j2files.py b/src/sonic-config-engine/tests/test_j2files.py index ce7f56eccf..525b5e79aa 100644 --- a/src/sonic-config-engine/tests/test_j2files.py +++ b/src/sonic-config-engine/tests/test_j2files.py @@ -136,4 +136,4 @@ class TestJ2Files(TestCase): try: os.remove(self.output_file) except OSError: - pass + pass \ No newline at end of file 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 new file mode 100644 index 0000000000..2215d4e8ca --- /dev/null +++ b/src/sonic-config-engine/tests/test_j2files_t2_chassis_fe.py @@ -0,0 +1,57 @@ +import filecmp +import os +import subprocess +import json +import shutil + +from unittest import TestCase + +class TestJ2FilesT2ChassisFe(TestCase): + def setUp(self): + self.test_dir = os.path.dirname(os.path.realpath(__file__)) + self.script_file = os.path.join(self.test_dir, '..', 'sonic-cfggen') + self.t2_chassis_fe_minigraph = os.path.join(self.test_dir, 't2-chassis-fe-graph.xml') + self.t2_chassis_fe_vni_minigraph = os.path.join(self.test_dir, 't2-chassis-fe-graph-vni.xml') + self.t2_chassis_fe_pc_minigraph = os.path.join(self.test_dir, 't2-chassis-fe-graph-pc.xml') + self.t2_chassis_fe_port_config = os.path.join(self.test_dir, 't2-chassis-fe-port-config.ini') + self.output_file = os.path.join(self.test_dir, 'output') + + def run_script(self, argument): + print 'CMD: sonic-cfggen ' + argument + return subprocess.check_output(self.script_file + ' ' + argument, shell=True) + + # Test zebra.conf in FRR docker for a T2 chassis frontend (fe) + def test_t2_chassis_fe_zebra_frr(self): + conf_template = os.path.join(self.test_dir, '..', '..', '..', 'dockers', 'docker-fpm-frr', 'zebra.conf.j2') + argument = '-m ' + self.t2_chassis_fe_minigraph + ' -p ' + self.t2_chassis_fe_port_config + ' -t ' + conf_template + ' > ' + self.output_file + self.run_script(argument) + self.assertTrue(filecmp.cmp(os.path.join(self.test_dir, 'sample_output', 't2-chassis-fe-zebra.conf'), self.output_file)) + + # Test zebra.conf in FRR docker for a T2 chassis frontend (fe) switch with port channel interfaces + def test_t2_chassis_fe_pc_zebra_frr(self): + conf_template = os.path.join(self.test_dir, '..', '..', '..', 'dockers', 'docker-fpm-frr', 'zebra.conf.j2') + argument = '-m ' + self.t2_chassis_fe_pc_minigraph + ' -p ' + self.t2_chassis_fe_port_config + ' -t ' + conf_template + ' > ' + self.output_file + self.run_script(argument) + 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): + 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) + self.assertTrue(filecmp.cmp(os.path.join(self.test_dir, 'sample_output', 't2-chassis-fe-vni-zebra.conf'), self.output_file)) + + # Test bgpd.conf in FRR docker for a T2 chassis frontend (fe) + def test_t2_chassis_frontend_bgpd_frr(self): + conf_template = os.path.join(self.test_dir, '..', '..', '..', 'dockers', 'docker-fpm-frr', 'bgpd.conf.j2') + argument = '-m ' + self.t2_chassis_fe_minigraph + ' -p ' + self.t2_chassis_fe_port_config + ' -t ' + conf_template + ' > ' + self.output_file + self.run_script(argument) + self.assertTrue(filecmp.cmp(os.path.join(self.test_dir, 'sample_output', 't2-chassis-fe-bgpd.conf'), self.output_file)) + + def tearDown(self): + try: + os.remove(self.output_file) + except OSError: + pass + + diff --git a/src/sonic-config-engine/tests/test_minigraph_case.py b/src/sonic-config-engine/tests/test_minigraph_case.py index d363acdca0..093aea05b5 100644 --- a/src/sonic-config-engine/tests/test_minigraph_case.py +++ b/src/sonic-config-engine/tests/test_minigraph_case.py @@ -9,7 +9,7 @@ class TestCfgGenCaseInsensitive(TestCase): self.script_file = os.path.join(self.test_dir, '..', 'sonic-cfggen') self.sample_graph = os.path.join(self.test_dir, 'simple-sample-graph-case.xml') self.port_config = os.path.join(self.test_dir, 't0-sample-port-config.ini') - + def run_script(self, argument, check_stderr=False): print '\n Running sonic-cfggen ' + argument if check_stderr: @@ -123,3 +123,13 @@ class TestCfgGenCaseInsensitive(TestCase): argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v "NTP_SERVER"' output = self.run_script(argument) self.assertEqual(output.strip(), "{'10.0.10.1': {}, '10.0.10.2': {}}") + + def test_minigraph_vnet(self): + argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v "VNET"' + output = self.run_script(argument) + self.assertEqual(output.strip(), "") + + def test_minigraph_vxlan(self): + argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v "VXLAN_TUNNEL"' + output = self.run_script(argument) + self.assertEqual(output.strip(), "") \ No newline at end of file