[minigraph.py]: Parse IP-in-IP tunnels from minigraph (#5742)
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 <lawlee@microsoft.com>
This commit is contained in:
parent
6156cb2805
commit
23b0e07d48
@ -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
|
||||
@ -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.
|
||||
@ -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):
|
||||
@ -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
|
||||
@ -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")):
|
||||
@ -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)
|
||||
@ -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:
|
||||
@ -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()
|
||||
|
@ -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>
|
||||
@ -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">
|
||||
@ -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>
|
||||
@ -372,6 +424,6 @@
|
||||
</ManagementInterfaces>
|
||||
</DeviceInfo>
|
||||
</DeviceInfos>
|
||||
<Hostname>switch-T0</Hostname>
|
||||
<Hostname>switch-t0</Hostname>
|
||||
<HwSku>Force10-S6000</HwSku>
|
||||
</DeviceMiniGraph>
|
||||
|
@ -121,7 +121,21 @@ class TestCfgGenCaseInsensitive(TestCase):
|
||||
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
|
||||
@ -170,3 +184,22 @@ class TestCfgGenCaseInsensitive(TestCase):
|
||||
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
|
||||
)
|
||||
|
Reference in New Issue
Block a user