From 002335a3d5f0adceeeccebcd513f5e345e833789 Mon Sep 17 00:00:00 2001 From: arlakshm <55814491+arlakshm@users.noreply.github.com> Date: Thu, 2 Jul 2020 11:37:40 -0700 Subject: [PATCH] [config]: Loopback Interface changes for multi ASIC devices (#4825) * Loopback IP changes for multi ASIC devices multi ASIC will have 2 Loopback Interfaces - Loopback0 has globally unique IP address, which is advertised by the multi ASIC device to its peers. This way all the external devices will see this device as a single device. - Loopback4096 is assigned an IP address which has a scope is within the device. Each ASIC has a different ip address for Loopback4096. This ip address will be used as Router-Id by the bgp instance on multi ASIC devices. This PR implements this change for multi ASIC devices Signed-off-by: Arvindsrinivasan Lakshmi Narasimhan --- .../docker-fpm-frr/frr/bgpd/bgpd.main.conf.j2 | 16 +++++++- src/sonic-config-engine/minigraph.py | 32 +++++++++++---- src/sonic-config-engine/sonic-cfggen | 3 ++ src/sonic-config-engine/sonic_device_util.py | 16 ++++++-- .../tests/multi_npu_data/sample-minigraph.xml | 40 +++++++++++++++++-- .../tests/test_multinpu_cfggen.py | 29 ++++++++++++++ 6 files changed, 120 insertions(+), 16 deletions(-) diff --git a/dockers/docker-fpm-frr/frr/bgpd/bgpd.main.conf.j2 b/dockers/docker-fpm-frr/frr/bgpd/bgpd.main.conf.j2 index ef98dcf9f16a..0199b254d149 100644 --- a/dockers/docker-fpm-frr/frr/bgpd/bgpd.main.conf.j2 +++ b/dockers/docker-fpm-frr/frr/bgpd/bgpd.main.conf.j2 @@ -15,7 +15,7 @@ ipv6 prefix-list PL_LoopbackV6 permit {{ get_ipv6_loopback_address(LOOPBACK_INTE {% endif %} ! ! -{% if DEVICE_METADATA['localhost']['sub_role'] == 'FrontEnd' %} +{% if multi_asic() %} route-map HIDE_INTERNAL permit 10 set community local-AS ! @@ -38,16 +38,30 @@ router bgp {{ DEVICE_METADATA['localhost']['bgp_asn'] }} {% endif %} ! {# set router-id #} +{% if multi_asic() %} + bgp router-id {{ get_ipv4_loopback_address(LOOPBACK_INTERFACE, "Loopback4096") | ip }} +{% else %} bgp router-id {{ get_ipv4_loopback_address(LOOPBACK_INTERFACE, "Loopback0") | ip }} +{% endif %} ! {# advertise loopback #} network {{ get_ipv4_loopback_address(LOOPBACK_INTERFACE, "Loopback0") | ip }}/32 +{% if multi_asic() %} + network {{ get_ipv4_loopback_address(LOOPBACK_INTERFACE, "Loopback4096") | ip }}/32 route-map HIDE_INTERNAL +{% endif %} ! {% if get_ipv6_loopback_address(LOOPBACK_INTERFACE, "Loopback0") != 'None' %} address-family ipv6 network {{ get_ipv6_loopback_address(LOOPBACK_INTERFACE, "Loopback0") | ip }}/64 exit-address-family {% endif %} +{% if multi_asic() %} +{% if get_ipv6_loopback_address(LOOPBACK_INTERFACE, "Loopback4096") != 'None' %} + address-family ipv6 + network {{ get_ipv6_loopback_address(LOOPBACK_INTERFACE, "Loopback4096") | ip }}/64 route-map HIDE_INTERNAL + exit-address-family +{% endif %} +{% endif %} {% endblock bgp_init %} ! {% block vlan_advertisement %} diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index 79e553e0191d..2fe699987279 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -251,6 +251,15 @@ def parse_asic_png(png, asic_name, hostname): devices[name] = device_data return (neighbors, devices, port_speeds) +def parse_loopback_intf(child): + lointfs = child.find(str(QName(ns, "LoopbackIPInterfaces"))) + lo_intfs = {} + for lointf in lointfs.findall(str(QName(ns1, "LoopbackIPInterface"))): + intfname = lointf.find(str(QName(ns, "AttachTo"))).text + ipprefix = lointf.find(str(QName(ns1, "PrefixStr"))).text + lo_intfs[(intfname, ipprefix)] = {} + return lo_intfs + def parse_dpg(dpg, hname): aclintfs = None mgmtintfs = None @@ -269,7 +278,6 @@ def parse_dpg(dpg, hname): """ if mgmtintfs is None and child.find(str(QName(ns, "ManagementIPInterfaces"))) is not None: mgmtintfs = child.find(str(QName(ns, "ManagementIPInterfaces"))) - hostname = child.find(str(QName(ns, "Hostname"))) if hostname.text.lower() != hname.lower(): continue @@ -290,12 +298,7 @@ def parse_dpg(dpg, hname): ipprefix = ipintf.find(str(QName(ns, "Prefix"))).text intfs[(intfname, ipprefix)] = {} - lointfs = child.find(str(QName(ns, "LoopbackIPInterfaces"))) - lo_intfs = {} - for lointf in lointfs.findall(str(QName(ns1, "LoopbackIPInterface"))): - intfname = lointf.find(str(QName(ns, "AttachTo"))).text - ipprefix = lointf.find(str(QName(ns1, "PrefixStr"))).text - lo_intfs[(intfname, ipprefix)] = {} + lo_intfs = parse_loopback_intf(child) mvrfConfigs = child.find(str(QName(ns, "MgmtVrfConfigs"))) mvrf = {} @@ -452,6 +455,13 @@ def parse_dpg(dpg, hname): return intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni return None, None, None, None, None, None, None, None, None, None +def parse_host_loopback(dpg, hname): + for child in dpg: + hostname = child.find(str(QName(ns, "Hostname"))) + if hostname.text.lower() != hname.lower(): + continue + lo_intfs = parse_loopback_intf(child) + return lo_intfs def parse_cpg(cpg, hname): bgp_sessions = {} @@ -826,6 +836,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw cloudtype = None hostname = None linkmetas = {} + host_lo_intfs = None # hostname is the asic_name, get the asic_id from the asic_name if asic_name is not None: @@ -867,6 +878,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw else: if child.tag == str(QName(ns, "DpgDec")): (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni) = parse_dpg(child, asic_name) + host_lo_intfs = parse_host_loopback(child, hostname) elif child.tag == str(QName(ns, "CpgDec")): (bgp_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, asic_name) enable_internal_bgp_session(bgp_sessions, filename, asic_name) @@ -930,6 +942,12 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw for lo_intf in lo_intfs: results['LOOPBACK_INTERFACE'][lo_intf] = lo_intfs[lo_intf] results['LOOPBACK_INTERFACE'][lo_intf[0]] = {} + + if host_lo_intfs is not None: + for host_lo_intf in host_lo_intfs: + results['LOOPBACK_INTERFACE'][host_lo_intf] = host_lo_intfs[host_lo_intf] + results['LOOPBACK_INTERFACE'][host_lo_intf[0]] = {} + results['MGMT_VRF_CONFIG'] = mvrf phyport_intfs = {} diff --git a/src/sonic-config-engine/sonic-cfggen b/src/sonic-config-engine/sonic-cfggen index 1118703458db..9bda72071937 100755 --- a/src/sonic-config-engine/sonic-cfggen +++ b/src/sonic-config-engine/sonic-cfggen @@ -45,6 +45,7 @@ from sonic_device_util import get_machine_info from sonic_device_util import get_platform_info from sonic_device_util import get_system_mac from sonic_device_util import get_npu_id_from_name +from sonic_device_util import is_multi_npu from config_samples import generate_sample_config from config_samples import get_available_config from swsssdk import SonicV2Connector, ConfigDBConnector, SonicDBConfig @@ -344,6 +345,8 @@ def main(): env.filters['ip_network'] = ip_network for attr in ['ip', 'network', 'prefixlen', 'netmask', 'broadcast']: env.filters[attr] = partial(prefix_attr, attr) + # Pass the is_multi_npu function as global + env.globals['multi_asic'] = is_multi_npu template = env.get_template(template_file) print(template.render(sort_data(data))) diff --git a/src/sonic-config-engine/sonic_device_util.py b/src/sonic-config-engine/sonic_device_util.py index 5984a7047fe7..ddfa751250c5 100644 --- a/src/sonic-config-engine/sonic_device_util.py +++ b/src/sonic-config-engine/sonic_device_util.py @@ -44,12 +44,20 @@ def get_npu_id_from_name(npu_name): else: return None +def get_asic_conf_file_path(platform): + asic_conf_path_candidates = [] + asic_conf_path_candidates.append(os.path.join('/usr/share/sonic/platform', ASIC_CONF_FILENAME)) + if platform is not None: + asic_conf_path_candidates.append(os.path.join(SONIC_DEVICE_PATH, platform, ASIC_CONF_FILENAME)) + for asic_conf_file_path in asic_conf_path_candidates: + if os.path.isfile(asic_conf_file_path): + return asic_conf_file_path + return None + def get_num_npus(): platform = get_platform_info(get_machine_info()) - if not platform: - return 1 - asic_conf_file_path = os.path.join(SONIC_DEVICE_PATH, platform, ASIC_CONF_FILENAME) - if not os.path.isfile(asic_conf_file_path): + asic_conf_file_path = get_asic_conf_file_path(platform) + if asic_conf_file_path is None: return 1 with open(asic_conf_file_path) as asic_conf_file: for line in asic_conf_file: diff --git a/src/sonic-config-engine/tests/multi_npu_data/sample-minigraph.xml b/src/sonic-config-engine/tests/multi_npu_data/sample-minigraph.xml index 8ca17925c6ec..412262315b4e 100644 --- a/src/sonic-config-engine/tests/multi_npu_data/sample-minigraph.xml +++ b/src/sonic-config-engine/tests/multi_npu_data/sample-minigraph.xml @@ -387,12 +387,20 @@ LoopbackInterface HostIP - Loopback0 + Loopback4096 8.0.0.0/32 8.0.0.0/32 + + HostIP1 + Loopback4096 + + FD00:1::32/128 + + FD00:1::32/128 + @@ -457,12 +465,20 @@ LoopbackInterface HostIP - Loopback0 + Loopback4096 8.0.0.1/32 8.0.0.1/32 + + HostIP1 + Loopback4096 + + FD00:2::32/128 + + FD00:2::32/128 + @@ -526,12 +542,20 @@ LoopbackInterface HostIP - Loopback0 + Loopback4096 8.0.0.4/32 8.0.0.4/32 + + HostIP1 + Loopback4096 + + FD00:3::32/128 + + FD00:3::32/128 + @@ -580,12 +604,20 @@ LoopbackInterface HostIP - Loopback0 + Loopback4096 8.0.0.5/32 8.0.0.5/32 + + HostIP1 + Loopback4096 + + FD00:4::32/128 + + FD00:4::32/128 + diff --git a/src/sonic-config-engine/tests/test_multinpu_cfggen.py b/src/sonic-config-engine/tests/test_multinpu_cfggen.py index c3307b482b79..7511ce9bcdd1 100644 --- a/src/sonic-config-engine/tests/test_multinpu_cfggen.py +++ b/src/sonic-config-engine/tests/test_multinpu_cfggen.py @@ -245,3 +245,32 @@ def test_back_end_asic_acl(self): argument = "-m {} -p {} -n asic3 --var-json \"ACL_TABLE\"".format(self.sample_graph, self.port_config[3]) output = json.loads(self.run_script(argument)) self.assertDictEqual(output, {}) + + def test_loopback_intfs(self): + argument = "-m {} --var-json \"LOOPBACK_INTERFACE\"".format(self.sample_graph) + output = json.loads(self.run_script(argument)) + self.assertDictEqual(output, {\ + "Loopback0": {}, + "Loopback0|10.1.0.32/32": {}, + "Loopback0|FC00:1::32/128": {}}) + + # The asic configuration should have 2 loopback interfaces + argument = "-m {} -n asic0 --var-json \"LOOPBACK_INTERFACE\"".format(self.sample_graph) + output = json.loads(self.run_script(argument)) + self.assertDictEqual(output, { \ + "Loopback0": {}, + "Loopback4096": {}, + "Loopback0|10.1.0.32/32": {}, + "Loopback0|FC00:1::32/128": {}, + "Loopback4096|8.0.0.0/32": {}, + "Loopback4096|FD00:1::32/128": {}}) + + argument = "-m {} -n asic3 --var-json \"LOOPBACK_INTERFACE\"".format(self.sample_graph) + output = json.loads(self.run_script(argument)) + self.assertDictEqual(output, {\ + "Loopback0": {}, + "Loopback4096": {}, + "Loopback0|10.1.0.32/32": {}, + "Loopback0|FC00:1::32/128": {}, + "Loopback4096|8.0.0.5/32": {}, + "Loopback4096|FD00:4::32/128": {}})