[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:
parent
ebe4a84eee
commit
b9a7665662
@ -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
|
||||
@ -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:
|
||||
@ -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,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):
|
||||
mux_cable_table = {}
|
||||
|
||||
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)
|
||||
|
||||
if cable_name in devices:
|
||||
cable_type = devices[cable_name].get('subtype')
|
||||
if cable_type is None:
|
||||
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
|
||||
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)
|
||||
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[port]['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)
|
||||
|
||||
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)
|
||||
|
@ -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>
|
||||
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user