[minigraph.py]: Parse mux cable table (#5876)

Parse device IPv6 loopback addresses when available
Add MUX_CABLE table to config DB:

'MUX_CABLE': {
	'Ethernet4': {
    	'state': 'auto',
        'server_ipv4': '10.10.10.1/32',
        'server_ipv6': 'fe80::0001/128'
	}
}
This commit is contained in:
Lawrence Lee 2020-11-16 13:14:34 -08:00 committed by GitHub
parent 67dbbb33bf
commit 261a81d379
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 134 additions and 24 deletions

View File

@ -79,6 +79,7 @@ def get_peer_switch_info(link_metadata, devices):
def parse_device(device): def parse_device(device):
lo_prefix = None lo_prefix = None
lo_prefix_v6 = None
mgmt_prefix = None mgmt_prefix = None
d_type = None # don't shadow type() d_type = None # don't shadow type()
hwsku = None hwsku = None
@ -90,6 +91,8 @@ def parse_device(device):
for node in device: for node in device:
if node.tag == str(QName(ns, "Address")): if node.tag == str(QName(ns, "Address")):
lo_prefix = node.find(str(QName(ns2, "IPPrefix"))).text lo_prefix = node.find(str(QName(ns2, "IPPrefix"))).text
elif node.tag == str(QName(ns, "AddressV6")):
lo_prefix_v6 = node.find(str(QName(ns2, "IPPrefix"))).text
elif node.tag == str(QName(ns, "ManagementAddress")): elif node.tag == str(QName(ns, "ManagementAddress")):
mgmt_prefix = node.find(str(QName(ns2, "IPPrefix"))).text mgmt_prefix = node.find(str(QName(ns2, "IPPrefix"))).text
elif node.tag == str(QName(ns, "Hostname")): elif node.tag == str(QName(ns, "Hostname")):
@ -98,7 +101,7 @@ def parse_device(device):
hwsku = node.text hwsku = node.text
elif node.tag == str(QName(ns, "DeploymentId")): elif node.tag == str(QName(ns, "DeploymentId")):
deployment_id = node.text deployment_id = node.text
return (lo_prefix, mgmt_prefix, name, hwsku, d_type, deployment_id) return (lo_prefix, lo_prefix_v6, mgmt_prefix, name, hwsku, d_type, deployment_id)
def parse_png(png, hname): def parse_png(png, hname):
neighbors = {} neighbors = {}
@ -160,10 +163,12 @@ def parse_png(png, hname):
if child.tag == str(QName(ns, "Devices")): if child.tag == str(QName(ns, "Devices")):
for device in child.findall(str(QName(ns, "Device"))): for device in child.findall(str(QName(ns, "Device"))):
(lo_prefix, mgmt_prefix, name, hwsku, d_type, deployment_id) = parse_device(device) (lo_prefix, lo_prefix_v6, mgmt_prefix, name, hwsku, d_type, deployment_id) = parse_device(device)
device_data = {'lo_addr': lo_prefix, 'type': d_type, 'mgmt_addr': mgmt_prefix, 'hwsku': hwsku } device_data = {'lo_addr': lo_prefix, 'type': d_type, 'mgmt_addr': mgmt_prefix, 'hwsku': hwsku }
if deployment_id: if deployment_id:
device_data['deployment_id'] = deployment_id device_data['deployment_id'] = deployment_id
if lo_prefix_v6:
device_data['lo_addr_v6'] = lo_prefix_v6
devices[name] = device_data devices[name] = device_data
if name == hname: if name == hname:
@ -281,10 +286,12 @@ def parse_asic_png(png, asic_name, hostname):
if child.tag == str(QName(ns, "Devices")): if child.tag == str(QName(ns, "Devices")):
for device in child.findall(str(QName(ns, "Device"))): for device in child.findall(str(QName(ns, "Device"))):
(lo_prefix, mgmt_prefix, name, hwsku, d_type, deployment_id) = parse_device(device) (lo_prefix, lo_prefix_v6, mgmt_prefix, name, hwsku, d_type, deployment_id) = parse_device(device)
device_data = {'lo_addr': lo_prefix, 'type': d_type, 'mgmt_addr': mgmt_prefix, 'hwsku': hwsku } device_data = {'lo_addr': lo_prefix, 'type': d_type, 'mgmt_addr': mgmt_prefix, 'hwsku': hwsku }
if deployment_id: if deployment_id:
device_data['deployment_id'] = deployment_id device_data['deployment_id'] = deployment_id
if lo_prefix_v6:
device_data['lo_addr_v6']= lo_prefix_v6
devices[name] = device_data devices[name] = device_data
return (neighbors, devices, port_speeds) return (neighbors, devices, port_speeds)
@ -1234,6 +1241,8 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None, hw
results['TUNNEL'] = get_tunnel_entries(tunnel_intfs, lo_intfs, hostname) results['TUNNEL'] = get_tunnel_entries(tunnel_intfs, lo_intfs, hostname)
results['MUX_CABLE'] = get_mux_cable_entries(mux_cable_ports, neighbors, devices)
for nghbr in list(neighbors.keys()): for nghbr in list(neighbors.keys()):
# remove port not in port_config.ini # remove port not in port_config.ini
if nghbr not in ports: if nghbr not in ports:
@ -1317,10 +1326,24 @@ def get_tunnel_entries(tunnel_intfs, lo_intfs, hostname):
tunnels[tunnel_key] = tunnel_attr tunnels[tunnel_key] = tunnel_attr
return tunnels return tunnels
def get_mux_cable_entries(mux_cable_ports, neighbors, devices):
mux_cable_table = {}
for intf in mux_cable_ports:
if intf in neighbors:
entry = {}
neighbor = neighbors[intf]['name']
entry['state'] = 'auto'
entry['server_ipv4'] = devices[neighbor]['lo_addr']
if 'lo_addr_v6' in devices[neighbor]:
entry['server_ipv6'] = devices[neighbor]['lo_addr_v6']
mux_cable_table[intf] = entry
return mux_cable_table
def parse_device_desc_xml(filename): def parse_device_desc_xml(filename):
root = ET.parse(filename).getroot() root = ET.parse(filename).getroot()
(lo_prefix, mgmt_prefix, hostname, hwsku, d_type, _) = parse_device(root) (lo_prefix, lo_prefix_v6, mgmt_prefix, hostname, hwsku, d_type, _) = parse_device(root)
results = {} results = {}
results['DEVICE_METADATA'] = {'localhost': { results['DEVICE_METADATA'] = {'localhost': {
@ -1329,6 +1352,8 @@ def parse_device_desc_xml(filename):
}} }}
results['LOOPBACK_INTERFACE'] = {('lo', lo_prefix): {}} results['LOOPBACK_INTERFACE'] = {('lo', lo_prefix): {}}
if lo_prefix_v6:
results['LOOPBACK_INTERFACE'] = {('lo_v6', lo_prefix_v6): {}}
mgmt_intf = {} mgmt_intf = {}
mgmtipn = ipaddress.ip_network(UNICODE_TYPE(mgmt_prefix), False) mgmtipn = ipaddress.ip_network(UNICODE_TYPE(mgmt_prefix), False)

View File

@ -213,6 +213,24 @@
<StartDevice>switch-02t1</StartDevice> <StartDevice>switch-02t1</StartDevice>
<StartPort>port1</StartPort> <StartPort>port1</StartPort>
</DeviceLinkBase> </DeviceLinkBase>
<DeviceLinkBase i:type="DeviceInterfaceLink">
<ElementType>DeviceInterfaceLink</ElementType>
<AutoNegotiation>true</AutoNegotiation>
<Bandwidth>25000</Bandwidth>
<EndDevice>switch-t0</EndDevice>
<EndPort>fortyGigE0/4</EndPort>
<StartDevice>server1</StartDevice>
<StartPort>port1</StartPort>
</DeviceLinkBase>
<DeviceLinkBase i:type="DeviceInterfaceLink">
<ElementType>DeviceInterfaceLink</ElementType>
<AutoNegotiation>true</AutoNegotiation>
<Bandwidth>40000</Bandwidth>
<EndDevice>switch-t0</EndDevice>
<EndPort>fortyGigE0/8</EndPort>
<StartDevice>server2</StartDevice>
<StartPort>port1</StartPort>
</DeviceLinkBase>
<DeviceLinkBase i:type="LogicalLink"> <DeviceLinkBase i:type="LogicalLink">
<ElementType>LogicalLink</ElementType> <ElementType>LogicalLink</ElementType>
<Bandwidth>10000</Bandwidth> <Bandwidth>10000</Bandwidth>
@ -278,6 +296,34 @@
</ManagementAddress> </ManagementAddress>
<HwSku>Force10-S6000</HwSku> <HwSku>Force10-S6000</HwSku>
</Device> </Device>
<Device i:type="Server">
<ElementType>Server</ElementType>
<Address xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>10.10.10.1/32</d5p1:IPPrefix>
</Address>
<AddressV6 xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>fe80::0001/128</d5p1:IPPrefix>
</AddressV6>
<ManagementAddress xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>10.0.0.1/32</d5p1:IPPrefix>
</ManagementAddress>
<Hostname>server1</Hostname>
<HwSku>server-sku</HwSku>
</Device>
<Device i:type="Server">
<ElementType>Server</ElementType>
<Address xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>10.10.10.2/32</d5p1:IPPrefix>
</Address>
<AddressV6 xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>fe80::0002/128</d5p1:IPPrefix>
</AddressV6>
<ManagementAddress xmlns:d5p1="Microsoft.Search.Autopilot.NetMux">
<d5p1:IPPrefix>10.0.0.2/32</d5p1:IPPrefix>
</ManagementAddress>
<Hostname>server2</Hostname>
<HwSku>server-sku</HwSku>
</Device>
</Devices> </Devices>
</PngDec> </PngDec>
<LinkMetadataDeclaration> <LinkMetadataDeclaration>

View File

@ -348,7 +348,7 @@ class TestCfgGen(TestCase):
utils.to_dict( utils.to_dict(
"{'Ethernet8': {'lanes': '37,38,39,40', 'description': 'Interface description', 'pfc_asym': 'off', 'mtu': '9100', 'alias': 'fortyGigE0/8', 'admin_status': 'up', 'speed': '40000', 'mux_cable': 'true'}, " "{'Ethernet8': {'lanes': '37,38,39,40', 'description': 'Interface description', 'pfc_asym': 'off', 'mtu': '9100', 'alias': 'fortyGigE0/8', 'admin_status': 'up', 'speed': '40000', 'mux_cable': 'true'}, "
"'Ethernet0': {'lanes': '29,30,31,32', 'description': 'switch-01t1:port1', 'pfc_asym': 'off', 'mtu': '9100', 'alias': 'fortyGigE0/0', 'admin_status': 'up', 'speed': '10000'}, " "'Ethernet0': {'lanes': '29,30,31,32', 'description': 'switch-01t1:port1', 'pfc_asym': 'off', 'mtu': '9100', 'alias': 'fortyGigE0/0', 'admin_status': 'up', 'speed': '10000'}, "
"'Ethernet4': {'lanes': '25,26,27,28', 'description': 'fortyGigE0/4', 'pfc_asym': 'off', 'mtu': '9100', 'alias': 'fortyGigE0/4', 'admin_status': 'up', 'speed': '25000', 'mux_cable': 'true'}, " "'Ethernet4': {'lanes': '25,26,27,28', 'description': 'server1:port1', 'pfc_asym': 'off', 'mtu': '9100', 'alias': 'fortyGigE0/4', 'admin_status': 'up', 'speed': '25000', 'mux_cable': 'true'}, "
"'Ethernet108': {'alias': 'fortyGigE0/108', 'pfc_asym': 'off', 'lanes': '81,82,83,84', 'description': 'fortyGigE0/108', 'mtu': '9100'}, " "'Ethernet108': {'alias': 'fortyGigE0/108', 'pfc_asym': 'off', 'lanes': '81,82,83,84', 'description': 'fortyGigE0/108', 'mtu': '9100'}, "
"'Ethernet100': {'alias': 'fortyGigE0/100', 'pfc_asym': 'off', 'lanes': '125,126,127,128', 'description': 'fortyGigE0/100', 'mtu': '9100'}, " "'Ethernet100': {'alias': 'fortyGigE0/100', 'pfc_asym': 'off', 'lanes': '125,126,127,128', 'description': 'fortyGigE0/100', 'mtu': '9100'}, "
"'Ethernet104': {'alias': 'fortyGigE0/104', 'pfc_asym': 'off', 'lanes': '85,86,87,88', 'description': 'fortyGigE0/104', 'mtu': '9100'}, " "'Ethernet104': {'alias': 'fortyGigE0/104', 'pfc_asym': 'off', 'lanes': '85,86,87,88', 'description': 'fortyGigE0/104', 'mtu': '9100'}, "

View File

@ -123,24 +123,41 @@ class TestCfgGenCaseInsensitive(TestCase):
def test_minigraph_neighbor_metadata(self): def test_minigraph_neighbor_metadata(self):
argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v "DEVICE_NEIGHBOR_METADATA"' argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v "DEVICE_NEIGHBOR_METADATA"'
expected_table = {
'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'
},
'server1': {
'hwsku': 'server-sku',
'lo_addr': '10.10.10.1/32',
'lo_addr_v6': 'fe80::0001/128',
'mgmt_addr': '10.0.0.1/32',
'type': 'Server'
},
'server2': {
'hwsku': 'server-sku',
'lo_addr': '10.10.10.2/32',
'lo_addr_v6': 'fe80::0002/128',
'mgmt_addr': '10.0.0.2/32',
'type': 'Server'
}
}
output = self.run_script(argument) output = self.run_script(argument)
self.maxDiff = None
self.assertEqual( self.assertEqual(
utils.to_dict(output.strip()), utils.to_dict(output.strip()),
utils.to_dict("{" \ expected_table
"'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 # everflow portion is not used
@ -227,3 +244,25 @@ class TestCfgGenCaseInsensitive(TestCase):
utils.to_dict(output.strip()), utils.to_dict(output.strip()),
expected_tunnel expected_tunnel
) )
def test_minigraph_mux_cable_table(self):
argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v "MUX_CABLE"'
expected_table = {
'Ethernet4': {
'state': 'auto',
'server_ipv4': '10.10.10.1/32',
'server_ipv6': 'fe80::0001/128'
},
'Ethernet8': {
'state': 'auto',
'server_ipv4': '10.10.10.2/32',
'server_ipv6': 'fe80::0002/128'
}
}
output = self.run_script(argument)
self.assertEqual(
utils.to_dict(output.strip()),
expected_table
)

View File

@ -202,8 +202,8 @@ class TestMultiNpuCfgGen(TestCase):
output = json.loads(self.run_script(argument)) output = json.loads(self.run_script(argument))
self.assertDictEqual(output, \ self.assertDictEqual(output, \
{'01T2': {'lo_addr': None, 'mgmt_addr': '89.139.132.40', 'hwsku': 'VM', 'type': 'SpineRouter'}, {'01T2': {'lo_addr': None, 'mgmt_addr': '89.139.132.40', 'hwsku': 'VM', 'type': 'SpineRouter'},
'ASIC3': {'lo_addr': '0.0.0.0/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'}, 'ASIC3': {'lo_addr': '0.0.0.0/0', 'lo_addr_v6': '::/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'},
'ASIC2': {'lo_addr': '0.0.0.0/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'}}) 'ASIC2': {'lo_addr': '0.0.0.0/0', 'lo_addr_v6': '::/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'}})
def test_backend_asic_device_neigh(self): def test_backend_asic_device_neigh(self):
argument = "-m {} -p {} -n asic3 --var-json \"DEVICE_NEIGHBOR\"".format(self.sample_graph, self.port_config[3]) argument = "-m {} -p {} -n asic3 --var-json \"DEVICE_NEIGHBOR\"".format(self.sample_graph, self.port_config[3])
@ -218,8 +218,8 @@ class TestMultiNpuCfgGen(TestCase):
argument = "-m {} -p {} -n asic3 --var-json \"DEVICE_NEIGHBOR_METADATA\"".format(self.sample_graph, self.port_config[3]) argument = "-m {} -p {} -n asic3 --var-json \"DEVICE_NEIGHBOR_METADATA\"".format(self.sample_graph, self.port_config[3])
output = json.loads(self.run_script(argument)) output = json.loads(self.run_script(argument))
self.assertDictEqual(output, \ self.assertDictEqual(output, \
{'ASIC1': {'lo_addr': '0.0.0.0/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'}, {'ASIC1': {'lo_addr': '0.0.0.0/0', 'lo_addr_v6': '::/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'},
'ASIC0': {'lo_addr': '0.0.0.0/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'}}) 'ASIC0': {'lo_addr': '0.0.0.0/0', 'lo_addr_v6': '::/0', 'mgmt_addr': '0.0.0.0/0', 'hwsku': 'multi-npu-asic', 'type': 'Asic'}})
def test_frontend_bgp_neighbor(self): def test_frontend_bgp_neighbor(self):
argument = "-m {} -p {} -n asic0 --var-json \"BGP_NEIGHBOR\"".format(self.sample_graph, self.port_config[0]) argument = "-m {} -p {} -n asic0 --var-json \"BGP_NEIGHBOR\"".format(self.sample_graph, self.port_config[0])