Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[minigraph][dualtor] Support parsing soc_ip out of dpg #11207

Merged
merged 6 commits into from
Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 81 additions & 44 deletions src/sonic-config-engine/minigraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ def formulate_fine_grained_ecmp(version, dpg_ecmp_content, port_device_map, port
fine_grained_content = {"FG_NHG_MEMBER": FG_NHG_MEMBER, "FG_NHG": FG_NHG, "NEIGH": NEIGH}
return fine_grained_content


def parse_png(png, hname, dpg_ecmp_content = None):
neighbors = {}
devices = {}
Expand Down Expand Up @@ -400,9 +401,9 @@ def parse_asic_png(png, asic_name, hostname):
device_data['lo_addr_v6']= lo_prefix_v6
devices[name] = device_data


return (neighbors, devices, port_speeds)


def parse_loopback_intf(child):
lointfs = child.find(str(QName(ns, "LoopbackIPInterfaces")))
lo_intfs = {}
Expand All @@ -412,6 +413,7 @@ def parse_loopback_intf(child):
lo_intfs[(intfname, ipprefix)] = {}
return lo_intfs


def parse_dpg(dpg, hname):
aclintfs = None
mgmtintfs = None
Expand Down Expand Up @@ -455,7 +457,7 @@ def parse_dpg(dpg, hname):
ipprefix = ipintf.find(str(QName(ns, "Prefix"))).text
intfs[(intfname, ipprefix)] = {}
ip_intfs_map[ipprefix] = intfalias
lo_intfs = parse_loopback_intf(child)
lo_intfs = parse_loopback_intf(child)

subintfs = child.find(str(QName(ns, "SubInterfaces")))
if subintfs is not None:
Expand Down Expand Up @@ -757,7 +759,6 @@ def parse_dpg(dpg, hname):
return None, None, None, None, None, None, 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")))
Expand All @@ -766,6 +767,7 @@ def parse_host_loopback(dpg, hname):
lo_intfs = parse_loopback_intf(child)
return lo_intfs


def parse_cpg(cpg, hname, local_devices=[]):
bgp_sessions = {}
bgp_internal_sessions = {}
Expand Down Expand Up @@ -891,6 +893,7 @@ def parse_meta(meta, hname):
max_cores = None
kube_data = {}
macsec_profile = {}
redundancy_type = None
device_metas = meta.find(str(QName(ns, "Devices")))
for device in device_metas.findall(str(QName(ns1, "DeviceMetadata"))):
if device.find(str(QName(ns1, "Name"))).text.lower() == hname.lower():
Expand Down Expand Up @@ -933,7 +936,9 @@ def parse_meta(meta, hname):
kube_data["ip"] = value
elif name == 'MacSecProfile':
macsec_profile = parse_macsec_profile(value)
return syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data, macsec_profile
elif name == "RedundancyType":
redundancy_type = value
return syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data, macsec_profile, redundancy_type


def parse_system_defaults(meta):
Expand Down Expand Up @@ -1313,6 +1318,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
static_routes = {}
system_defaults = {}
macsec_profile = {}
redundancy_type = None

hwsku_qn = QName(ns, "HwSku")
hostname_qn = QName(ns, "Hostname")
Expand Down Expand Up @@ -1343,7 +1349,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
elif child.tag == str(QName(ns, "UngDec")):
(u_neighbors, u_devices, _, _, _, _, _, _) = parse_png(child, hostname, None)
elif child.tag == str(QName(ns, "MetadataDeclaration")):
(syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data, macsec_profile) = parse_meta(child, hostname)
(syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, resource_type, downstream_subrole, switch_id, switch_type, max_cores, kube_data, macsec_profile, redundancy_type) = parse_meta(child, hostname)
elif child.tag == str(QName(ns, "LinkMetadataDeclaration")):
linkmetas = parse_linkmeta(child, hostname)
elif child.tag == str(QName(ns, "DeviceInfos")):
Expand Down Expand Up @@ -1567,11 +1573,6 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
if macsec_enabled and 'PrimaryKey' in macsec_profile:
port['macsec'] = macsec_profile['PrimaryKey']

# If connected to a smart cable, get the connection position
for port_name, port in ports.items():
if port_name in mux_cable_ports:
port['mux_cable'] = "true"

# set port description if parsed from deviceinfo
for port_name in port_descriptions:
# ignore port not in port_config.ini
Expand Down Expand Up @@ -1713,7 +1714,13 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
# Add src_ip and qos remapping config into TUNNEL table if tunnel_qos_remap is enabled
results['TUNNEL'] = get_tunnel_entries(tunnel_intfs, tunnel_intfs_qos_remap_config, lo_intfs, system_defaults.get('tunnel_qos_remap', {}), mux_tunnel_name, peer_switch_ip)

results['MUX_CABLE'] = get_mux_cable_entries(mux_cable_ports, neighbors, devices)
active_active_ports = get_ports_in_active_active(root, devices, neighbors)
results['MUX_CABLE'] = get_mux_cable_entries(ports, mux_cable_ports, active_active_ports, neighbors, devices, redundancy_type)

# If connected to a smart cable, get the connection position
for port_name, port in results['PORT'].items():
if port_name in results['MUX_CABLE']:
port['mux_cable'] = "true"

if static_routes:
results['STATIC_ROUTE'] = static_routes
Expand Down Expand Up @@ -1826,46 +1833,76 @@ def get_tunnel_entries(tunnel_intfs, tunnel_intfs_qos_remap_config, lo_intfs, tu

return tunnels

def get_mux_cable_entries(mux_cable_ports, neighbors, devices):

def get_ports_in_active_active(root, devices, neighbors):
"""Parse out ports in active-active cable type."""
servers = {hostname.lower(): device_data for hostname, device_data in devices.items() if device_data["type"] == "Server"}
ports_in_active_active = {}
dpg_section = root.find(str(QName(ns, "DpgDec")))
neighbor_to_port_mapping = {neighbor["name"].lower(): port for port, neighbor in neighbors.items()}
if dpg_section is not None:
for child in dpg_section:
hostname = child.find(str(QName(ns, "Hostname")))
if hostname is None:
continue
hostname = hostname.text.lower()
if hostname not in servers:
continue
lo_intfs = parse_loopback_intf(child)
soc_intfs = {}
for intfname, ipprefix in lo_intfs.keys():
intfname_lower = intfname.lower()
if hostname + "soc" == intfname_lower:
ipprefix = str(ipaddress.ip_network(UNICODE_TYPE(ipprefix.split("/")[0])))
if "." in ipprefix:
soc_intfs["soc_ipv4"] = ipprefix
elif ":" in ipprefix:
soc_intfs["soc_ipv6"] = ipprefix
if hostname in neighbor_to_port_mapping and soc_intfs:
ports_in_active_active[neighbor_to_port_mapping[hostname]] = soc_intfs
return ports_in_active_active


def get_mux_cable_entries(ports, mux_cable_ports, active_active_ports, neighbors, devices, redundancy_type):
mux_cable_table = {}
if redundancy_type:
redundancy_type = redundancy_type.lower()

for port in ports:
is_active_active = redundancy_type in ("libra", "mixed") and port in active_active_ports
is_active_standby = port in mux_cable_ports
if is_active_active and is_active_standby:
print("Warning: skip %s as it is defined as active-standby and actie-active" % port, file=sys.stderr)
continue
if not (is_active_active or is_active_standby):
continue

for intf, cable_name in mux_cable_ports.items():
if intf in neighbors:
entry = {}
neighbor = neighbors[intf]['name']
entry['state'] = 'auto'

if devices[neighbor]['lo_addr'] is not None:
# Always force a /32 prefix for server IPv4 loopbacks
server_ipv4_lo_addr = devices[neighbor]['lo_addr'].split("/")[0]
server_ipv4_lo_prefix = ipaddress.ip_network(UNICODE_TYPE(server_ipv4_lo_addr))
entry['server_ipv4'] = str(server_ipv4_lo_prefix)

if 'lo_addr_v6' in devices[neighbor] and devices[neighbor]['lo_addr_v6'] is not None:
server_ipv6_lo_addr = devices[neighbor]['lo_addr_v6'].split('/')[0]
server_ipv6_lo_prefix = ipaddress.ip_network(UNICODE_TYPE(server_ipv6_lo_addr))
entry['server_ipv6'] = str(server_ipv6_lo_prefix)
mux_cable_table[intf] = entry
else:
print("Warning: no server IPv4 loopback found for {}, skipping mux cable table entry".format(neighbor), file=sys.stderr)
entry = {}
neighbor = neighbors[port]['name']
entry['state'] = 'auto'

if cable_name in devices:
cable_type = devices[cable_name].get('subtype')
if cable_type is None:
continue
if cable_type in dualtor_cable_types:
mux_cable_table[intf]['cable_type'] = cable_type
if cable_type == 'active-active':
soc_ipv4 = devices[cable_name]['lo_addr'].split('/')[0]
soc_ipv4_prefix = ipaddress.ip_network(UNICODE_TYPE(soc_ipv4))
mux_cable_table[intf]['soc_ipv4'] = str(soc_ipv4_prefix)
else:
print("Warning: skip parsing device %s for mux cable entry, cable type %s not supported" % (cable_name, cable_type), file=sys.stderr)
if devices[neighbor]['lo_addr'] is not None:
# Always force a /32 prefix for server IPv4 loopbacks
server_ipv4_lo_addr = devices[neighbor]['lo_addr'].split("/")[0]
server_ipv4_lo_prefix = ipaddress.ip_network(UNICODE_TYPE(server_ipv4_lo_addr))
entry['server_ipv4'] = str(server_ipv4_lo_prefix)

if 'lo_addr_v6' in devices[neighbor] and devices[neighbor]['lo_addr_v6'] is not None:
server_ipv6_lo_addr = devices[neighbor]['lo_addr_v6'].split('/')[0]
server_ipv6_lo_prefix = ipaddress.ip_network(UNICODE_TYPE(server_ipv6_lo_addr))
entry['server_ipv6'] = str(server_ipv6_lo_prefix)

if is_active_active:
entry['cable_type'] = 'active-active'
entry.update(active_active_ports[port])

mux_cable_table[port] = entry
else:
print("Warning: skip parsing device %s for mux cable entry, device definition not found" % cable_name, file=sys.stderr)
print("Warning: no server IPv4 loopback found for {}, skipping mux cable table entry".format(neighbor), file=sys.stderr)

return mux_cable_table


def parse_device_desc_xml(filename):
root = ET.parse(filename).getroot()
(lo_prefix, lo_prefix_v6, mgmt_prefix, mgmt_prefix_v6, hostname, hwsku, d_type, _, _, _) = parse_device(root)
Expand Down
91 changes: 61 additions & 30 deletions src/sonic-config-engine/tests/simple-sample-graph-case.xml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,62 @@
<DownstreamSummaries/>
<DownstreamSummarySet xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution"/>
</DeviceDataPlaneInfo>
<DeviceDataPlaneInfo>
<IPSecTunnels />
<LoopbackIPInterfaces xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution">
<a:LoopbackIPInterface>
<ElementType>LoopbackInterface</ElementType>
<Name>HostIP</Name>
<AttachTo>Loopback0</AttachTo>
<a:Prefix xmlns:b="Microsoft.Search.Autopilot.NetMux">
<b:IPPrefix>10.10.10.2/32</b:IPPrefix>
</a:Prefix>
<a:PrefixStr>10.10.10.2/32</a:PrefixStr>
</a:LoopbackIPInterface>
<a:LoopbackIPInterface>
<ElementType>LoopbackInterface</ElementType>
<Name>HostIP1</Name>
<AttachTo>Loopback0</AttachTo>
<a:Prefix xmlns:b="Microsoft.Search.Autopilot.NetMux">
<b:IPPrefix>fe80::0002/128</b:IPPrefix>
</a:Prefix>
<a:PrefixStr>fe80::0002/128</a:PrefixStr>
</a:LoopbackIPInterface>
<a:LoopbackIPInterface>
<ElementType>LoopbackInterface</ElementType>
<Name>SoCHostIP0</Name>
<AttachTo>server2SOC</AttachTo>
<a:Prefix xmlns:b="Microsoft.Search.Autopilot.NetMux">
<b:IPPrefix>10.10.10.3/32</b:IPPrefix>
</a:Prefix>
<a:PrefixStr>10.10.10.3/32</a:PrefixStr>
</a:LoopbackIPInterface>
<a:LoopbackIPInterface>
<ElementType>LoopbackInterface</ElementType>
<Name>SoCHostIP1</Name>
<AttachTo>server2SOC</AttachTo>
<a:Prefix xmlns:b="Microsoft.Search.Autopilot.NetMux">
<b:IPPrefix>fe80::0003/128</b:IPPrefix>
</a:Prefix>
<a:PrefixStr>fe80::0003/128</a:PrefixStr>
</a:LoopbackIPInterface>
</LoopbackIPInterfaces>
<ManagementIPInterfaces xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution" />
<ManagementVIPInterfaces xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution" />
<MplsInterfaces />
<MplsTeInterfaces />
<RsvpInterfaces />
<Hostname>server2</Hostname>
<PortChannelInterfaces />
<SubInterfaces />
<VlanInterfaces />
<IPInterfaces />
<DataAcls />
<AclInterfaces />
<NatInterfaces xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution" />
<DownstreamSummaries />
<DownstreamSummarySet xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution" />
</DeviceDataPlaneInfo>
</DpgDec>
<PngDec>
<DeviceInterfaceLinks>
Expand Down Expand Up @@ -262,17 +318,6 @@
<StartPort>L</StartPort>
<Validate>true</Validate>
</DeviceLinkBase>
<DeviceLinkBase i:type="LogicalLink">
<ElementType>LogicalLink</ElementType>
<Bandwidth>10000</Bandwidth>
<ChassisInternal>false</ChassisInternal>
<EndDevice>switch-t0</EndDevice>
<EndPort>fortyGigE0/8</EndPort>
<FlowControl>true</FlowControl>
<StartDevice>server2-SC</StartDevice>
<StartPort>U</StartPort>
<Validate>true</Validate>
</DeviceLinkBase>
<DeviceLinkBase i:type="LogicalLink">
<ElementType>LogicalLink</ElementType>
<Bandwidth>0</Bandwidth>
Expand Down Expand Up @@ -349,25 +394,6 @@
<Hostname>server1</Hostname>
<HwSku>server-sku</HwSku>
</Device>
<Device i:type="SmartCable">
<ElementType>SmartCable</ElementType>
<SubType>active-active</SubType>
<Address xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>10.10.10.3/32</d5p1:IPPrefix>
</Address>
<AddressV6 xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>::/0</d5p1:IPPrefix>
</AddressV6>
<ManagementAddress xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>0.0.0.0/0</d5p1:IPPrefix>
</ManagementAddress>
<ManagementAddressV6 xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>::/0</d5p1:IPPrefix>
</ManagementAddressV6>
<SerialNumber i:nil="true" />
<Hostname>server2-SC</Hostname>
<HwSku>smartcable-sku</HwSku>
</Device>
<Device i:type="Server">
<ElementType>Server</ElementType>
<Address xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
Expand Down Expand Up @@ -506,6 +532,11 @@
<a:Reference i:nil="true"/>
<a:Value>Storage</a:Value>
</a:DeviceProperty>
<a:DeviceProperty>
<a:Name>RedundancyType</a:Name>
<a:Reference i:nil="true"/>
<a:Value>Mixed</a:Value>
</a:DeviceProperty>
</a:Properties>
</a:DeviceMetadata>
</Devices>
Expand Down
9 changes: 1 addition & 8 deletions src/sonic-config-engine/tests/test_minigraph_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,14 +236,6 @@ def test_minigraph_neighbor_metadata(self):
'lo_addr_v6': '::/0',
'mgmt_addr': '0.0.0.0/0',
'type': 'SmartCable'
},
'server2-SC': {
'hwsku': 'smartcable-sku',
'lo_addr': '10.10.10.3/32',
'lo_addr_v6': '::/0',
'mgmt_addr': '0.0.0.0/0',
'type': 'SmartCable',
'subtype': 'active-active'
}
}
output = self.run_script(argument)
Expand Down Expand Up @@ -421,6 +413,7 @@ def test_minigraph_mux_cable_table(self):
'server_ipv4': '10.10.10.2/32',
'server_ipv6': 'fe80::2/128',
'soc_ipv4': '10.10.10.3/32',
'soc_ipv6': 'fe80::3/128',
'cable_type': 'active-active'
}
}
Expand Down