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

Why I did it
To further support parse out soc_ipv4 and soc_ipv6 out of Dpg:

<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>
</DeviceDataPlaneInfo>
Signed-off-by: Longxiang Lyu lolv@microsoft.com

How I did it
For servers loopback definitions in Dpg, if they contain LoopbackIPInterface with tags AttachTo, which has value of format like <server_name>SOC, the address will be regarded as a SoC IP, and sonic-cfggen now will treat the port connected to the server as active-active if the redundancy_type is either Libra or Mixed.

How to verify it
Pass the unittest.

Signed-off-by: Longxiang Lyu <lolv@microsoft.com>
This commit is contained in:
Longxiang Lyu 2022-07-06 06:50:05 +08:00 committed by GitHub
parent ebe4a84eee
commit b9a7665662
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 144 additions and 83 deletions

View File

@ -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 = {}
@ -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 = {}
@ -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
@ -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")))
@ -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 = {}
@ -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():
@ -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):
@ -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")
@ -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")):
@ -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
@ -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
@ -1826,13 +1833,52 @@ 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):
mux_cable_table = {}
for intf, cable_name in mux_cable_ports.items():
if intf in neighbors:
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
entry = {}
neighbor = neighbors[intf]['name']
neighbor = neighbors[port]['name']
entry['state'] = 'auto'
if devices[neighbor]['lo_addr'] is not None:
@ -1845,27 +1891,18 @@ def get_mux_cable_entries(mux_cable_ports, neighbors, devices):
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
if is_active_active:
entry['cable_type'] = 'active-active'
entry.update(active_active_ports[port])
mux_cable_table[port] = entry
else:
print("Warning: no server IPv4 loopback found for {}, skipping mux cable table entry".format(neighbor), file=sys.stderr)
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)
else:
print("Warning: skip parsing device %s for mux cable entry, device definition not found" % cable_name, 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)

View File

@ -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>
@ -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>
@ -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">
@ -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>

View File

@ -236,14 +236,6 @@ class TestCfgGenCaseInsensitive(TestCase):
'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)
@ -421,6 +413,7 @@ class TestCfgGenCaseInsensitive(TestCase):
'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'
}
}