Skip to content

Commit

Permalink
[minigraph.py]: Parse IP-in-IP tunnels from minigraph (#5742)
Browse files Browse the repository at this point in the history
Take tunnel info from `<TunnelInterface>` tag in minigraph, and create tables in config_DB:

```
"TUNNEL": {
    "MUX_TUNNEL_0": {
        "tunnel_type": "IPINIP",
        "dst_ip": "26.1.1.10",
        "dscp_mode": "uniform",
        "encap_ecn_mode": "standard",
        "ecn_mode": "copy_from_outer",
        "ttl_mode": "pipe"
    }
}
```

Signed-off-by: Lawrence Lee <[email protected]>
  • Loading branch information
theasianpianist authored Nov 7, 2020
1 parent 6156cb2 commit 23b0e07
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 5 deletions.
45 changes: 42 additions & 3 deletions src/sonic-config-engine/minigraph.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import print_function

import calendar
from ipaddress import IPv4Address, IPv4Network, ip_address, ip_network
import math
import os
import sys
Expand Down Expand Up @@ -280,6 +281,7 @@ def parse_loopback_intf(child):
def parse_dpg(dpg, hname):
aclintfs = None
mgmtintfs = None
tunnelintfs = defaultdict(dict)
for child in dpg:
"""
In Multi-NPU platforms the acl intfs are defined only for the host not for individual asic.
Expand Down Expand Up @@ -473,7 +475,25 @@ def parse_dpg(dpg, hname):
except:
print("Warning: Ignoring Control Plane ACL %s without type" % aclname, file=sys.stderr)

return intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni
mg_tunnels = child.find(str(QName(ns, "TunnelInterfaces")))
if mg_tunnels is not None:
table_key_to_mg_key_map = {"encap_ecn_mode": "EcnEncapsulationMode",
"ecn_mode": "EcnDecapsulationMode",
"dscp_mode": "DifferentiatedServicesCodePointMode",
"ttl_mode": "TtlMode"}
for mg_tunnel in mg_tunnels.findall(str(QName(ns, "TunnelInterface"))):
tunnel_type = mg_tunnel.attrib["Type"]
tunnel_name = mg_tunnel.attrib["Name"]
tunnelintfs[tunnel_type][tunnel_name] = {
"tunnel_type": mg_tunnel.attrib["Type"].upper(),
}

for table_key, mg_key in table_key_to_mg_key_map.items():
# If the minigraph has the key, add the corresponding config DB key to the table
if mg_key in mg_tunnel.attrib:
tunnelintfs[tunnel_type][tunnel_name][table_key] = mg_tunnel.attrib[mg_key]

return intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni, tunnelintfs
return None, None, None, None, None, None, None, None, None, None

def parse_host_loopback(dpg, hname):
Expand Down Expand Up @@ -866,6 +886,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
intfs = None
vlan_intfs = None
pc_intfs = None
tunnel_intfs = None
vlans = None
vlan_members = None
pcs = None
Expand Down Expand Up @@ -923,7 +944,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
for child in root:
if asic_name is None:
if child.tag == str(QName(ns, "DpgDec")):
(intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni) = parse_dpg(child, hostname)
(intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni, tunnel_intfs) = parse_dpg(child, hostname)
elif child.tag == str(QName(ns, "CpgDec")):
(bgp_sessions, bgp_internal_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, hostname)
elif child.tag == str(QName(ns, "PngDec")):
Expand All @@ -938,7 +959,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
(port_speeds_default, port_descriptions) = parse_deviceinfo(child, hwsku)
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)
(intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni, tunnel_intfs) = parse_dpg(child, asic_name)
host_lo_intfs = parse_host_loopback(child, hostname)
elif child.tag == str(QName(ns, "CpgDec")):
(bgp_sessions, bgp_internal_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, asic_name, local_devices)
Expand Down Expand Up @@ -1167,6 +1188,8 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
results['VLAN'] = vlans
results['VLAN_MEMBER'] = vlan_members

results['TUNNEL'] = get_tunnel_entries(tunnel_intfs, lo_intfs, hostname)

for nghbr in list(neighbors.keys()):
# remove port not in port_config.ini
if nghbr not in ports:
Expand Down Expand Up @@ -1234,6 +1257,22 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw

return results

def get_tunnel_entries(tunnel_intfs, lo_intfs, hostname):
lo_addr = ''
# Use the first IPv4 loopback as the tunnel destination IP
for addr in lo_intfs.keys():
ip_addr = ip_network(UNICODE_TYPE(addr[1]))
if isinstance(ip_addr, IPv4Network):
lo_addr = str(ip_addr.network_address)
break

tunnels = {}
for type, tunnel_dict in tunnel_intfs.items():
for tunnel_key, tunnel_attr in tunnel_dict.items():
tunnel_attr['dst_ip'] = lo_addr
tunnels[tunnel_key] = tunnel_attr
return tunnels


def parse_device_desc_xml(filename):
root = ET.parse(filename).getroot()
Expand Down
54 changes: 53 additions & 1 deletion src/sonic-config-engine/tests/simple-sample-graph-case.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@
<SubInterface/>
</PortChannel>
</PortChannelInterfaces>
<TunnelInterfaces>
<TunnelInterface Name="MuxTunnel0" Type="IPInIP" AttachTo="Loopback0" DifferentiatedServicesCodePointMode="uniform" EcnEncapsulationMode="standard" EcnDecapsulationMode="copy_from_outer" TtlMode="pipe" />
</TunnelInterfaces>
<VlanInterfaces>
<VlanInterface>
<Name>ab1</Name>
Expand Down Expand Up @@ -232,12 +235,36 @@
<StartPort>U</StartPort>
<Validate>true</Validate>
</DeviceLinkBase>
<DeviceLinkBase i:type="LogicalLink">
<ElementType>LogicalLink</ElementType>
<Bandwidth>0</Bandwidth>
<ChassisInternal>false</ChassisInternal>
<EndDevice>switch-t0</EndDevice>
<EndPort>MuxTunnel0</EndPort>
<FlowControl>false</FlowControl>
<StartDevice>switch2-t0</StartDevice>
<StartPort>MuxTunnel0</StartPort>
<Validate>true</Validate>
</DeviceLinkBase>
</DeviceInterfaceLinks>
<Devices>
<Device i:type="ToRRouter">
<Address xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>26.1.1.10</d5p1:IPPrefix>
</Address>
<Hostname>switch-t0</Hostname>
<HwSku>Force10-S6000</HwSku>
</Device>
<Device i:type="ToRRouter">
<Address xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>25.1.1.10</d5p1:IPPrefix>
</Address>
<ManagementAddress xmlns:a="Microsoft.Search.Autopilot.NetMux">
<a:IPPrefix>10.7.0.196/26</a:IPPrefix>
</ManagementAddress>
<Hostname>switch2-t0</Hostname>
<HwSku>Force10-S6000</HwSku>
</Device>
<Device i:type="LeafRouter">
<Hostname>switch-01t1</Hostname>
<Address xmlns:a="Microsoft.Search.Autopilot.NetMux">
Expand All @@ -252,6 +279,31 @@
</Device>
</Devices>
</PngDec>
<LinkMetadataDeclaration>
<Link xmlns:d3p1="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution">
<d3p1:LinkMetadata>
<d3p1:Name i:nil="true" />
<d3p1:Properties>
<d3p1:DeviceProperty>
<d3p1:Name>DevicePeeringLink</d3p1:Name>
<d3p1:Reference i:nil="true" />
<d3p1:Value>True</d3p1:Value>
</d3p1:DeviceProperty>
<d3p1:DeviceProperty>
<d3p1:Name>UpperTOR</d3p1:Name>
<d3p1:Reference i:nil="true" />
<d3p1:Value>switch-t0</d3p1:Value>
</d3p1:DeviceProperty>
<d3p1:DeviceProperty>
<d3p1:Name>LowerTOR</d3p1:Name>
<d3p1:Reference i:nil="true" />
<d3p1:Value>switch2-t0</d3p1:Value>
</d3p1:DeviceProperty>
</d3p1:Properties>
<d3p1:Key>switch2-t0:MuxTunnel0;switch-t0:MuxTunnel0</d3p1:Key>
</d3p1:LinkMetadata>
</Link>
</LinkMetadataDeclaration>
<MetadataDeclaration>
<Devices xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution">
<a:DeviceMetadata>
Expand Down Expand Up @@ -372,6 +424,6 @@
</ManagementInterfaces>
</DeviceInfo>
</DeviceInfos>
<Hostname>switch-T0</Hostname>
<Hostname>switch-t0</Hostname>
<HwSku>Force10-S6000</HwSku>
</DeviceMiniGraph>
35 changes: 34 additions & 1 deletion src/sonic-config-engine/tests/test_minigraph_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,21 @@ def test_minigraph_neighbor_metadata(self):
output = self.run_script(argument)
self.assertEqual(
utils.to_dict(output.strip()),
utils.to_dict("{'switch-01t1': {'lo_addr': '10.1.0.186/32', 'mgmt_addr': '10.7.0.196/26', 'hwsku': 'Force10-S6000', 'type': 'LeafRouter', 'deployment_id': '2'}}")
utils.to_dict("{" \
"'switch-01t1': {" \
"'lo_addr': '10.1.0.186/32'," \
"'mgmt_addr': '10.7.0.196/26'," \
"'hwsku': 'Force10-S6000'," \
"'type': 'LeafRouter'," \
"'deployment_id': '2'" \
"}," \
"'switch2-t0': {" \
"'hwsku': 'Force10-S6000'," \
"'lo_addr': '25.1.1.10'," \
"'mgmt_addr': '10.7.0.196/26'," \
"'type': 'ToRRouter'" \
"}" \
"}")
)

# everflow portion is not used
Expand Down Expand Up @@ -170,3 +184,22 @@ def test_mux_cable_parsing(self):
self.assertTrue(port["mux_cable"])
else:
self.assertTrue("mux_cable" not in port)

def test_minigraph_tunnel_table(self):
argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v "TUNNEL"'
expected_tunnel = {
"MuxTunnel0": {
"tunnel_type": "IPINIP",
"dst_ip": "10.1.0.32",
"dscp_mode": "uniform",
"encap_ecn_mode": "standard",
"ecn_mode": "copy_from_outer",
"ttl_mode": "pipe"
}
}

output = self.run_script(argument)
self.assertEqual(
utils.to_dict(output.strip()),
expected_tunnel
)

0 comments on commit 23b0e07

Please sign in to comment.