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] Support parse IPv6 in device_desc.xml #11095

Merged
merged 9 commits into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from 8 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
25 changes: 17 additions & 8 deletions src/sonic-config-engine/minigraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def parse_device(device):
lo_prefix = None
lo_prefix_v6 = None
mgmt_prefix = None
mgmt_prefix_v6 = None
d_type = None # don't shadow type()
hwsku = None
name = None
Expand All @@ -101,6 +102,8 @@ def parse_device(device):
lo_prefix_v6 = node.find(str(QName(ns2, "IPPrefix"))).text
elif node.tag == str(QName(ns, "ManagementAddress")):
mgmt_prefix = node.find(str(QName(ns2, "IPPrefix"))).text
elif node.tag == str(QName(ns, "ManagementAddressV6")):
mgmt_prefix_v6 = node.find(str(QName(ns2, "IPPrefix"))).text
elif node.tag == str(QName(ns, "Hostname")):
name = node.text
elif node.tag == str(QName(ns, "HwSku")):
Expand All @@ -117,7 +120,7 @@ def parse_device(device):
if d_type is None and str(QName(ns3, "type")) in device.attrib:
d_type = device.attrib[str(QName(ns3, "type"))]

return (lo_prefix, lo_prefix_v6, mgmt_prefix, name, hwsku, d_type, deployment_id, cluster, d_subtype)
return (lo_prefix, lo_prefix_v6, mgmt_prefix, mgmt_prefix_v6, name, hwsku, d_type, deployment_id, cluster, d_subtype)


def calculate_lcm_for_ecmp (nhdevices_bank_map, nhip_bank_map):
Expand Down Expand Up @@ -254,7 +257,7 @@ def parse_png(png, hname, dpg_ecmp_content = None):

if child.tag == str(QName(ns, "Devices")):
for device in child.findall(str(QName(ns, "Device"))):
(lo_prefix, lo_prefix_v6, mgmt_prefix, name, hwsku, d_type, deployment_id, cluster, d_subtype) = parse_device(device)
(lo_prefix, lo_prefix_v6, mgmt_prefix, mgmt_prefix_v6, name, hwsku, d_type, deployment_id, cluster, d_subtype) = parse_device(device)
device_data = {'lo_addr': lo_prefix, 'type': d_type, 'mgmt_addr': mgmt_prefix, 'hwsku': hwsku}
if cluster:
device_data['cluster'] = cluster
Expand Down Expand Up @@ -387,7 +390,7 @@ def parse_asic_png(png, asic_name, hostname):

if child.tag == str(QName(ns, "Devices")):
for device in child.findall(str(QName(ns, "Device"))):
(lo_prefix, lo_prefix_v6, mgmt_prefix, name, hwsku, d_type, deployment_id, cluster, _) = parse_device(device)
(lo_prefix, lo_prefix_v6, mgmt_prefix, mgmt_prefix_v6, name, hwsku, d_type, deployment_id, cluster, _) = parse_device(device)
device_data = {'lo_addr': lo_prefix, 'type': d_type, 'mgmt_addr': mgmt_prefix, 'hwsku': hwsku }
if cluster:
device_data['cluster'] = cluster
Expand Down Expand Up @@ -1836,7 +1839,7 @@ def get_mux_cable_entries(mux_cable_ports, neighbors, devices):

def parse_device_desc_xml(filename):
root = ET.parse(filename).getroot()
(lo_prefix, lo_prefix_v6, mgmt_prefix, hostname, hwsku, d_type, _, _, _) = parse_device(root)
(lo_prefix, lo_prefix_v6, mgmt_prefix, mgmt_prefix_v6, hostname, hwsku, d_type, _, _, _) = parse_device(root)

results = {}
results['DEVICE_METADATA'] = {'localhost': {
Expand All @@ -1848,10 +1851,16 @@ def parse_device_desc_xml(filename):
if lo_prefix_v6:
results['LOOPBACK_INTERFACE'] = {('lo_v6', lo_prefix_v6): {}}

mgmt_intf = {}
mgmtipn = ipaddress.ip_network(UNICODE_TYPE(mgmt_prefix), False)
gwaddr = ipaddress.ip_address((next(mgmtipn.hosts())))
results['MGMT_INTERFACE'] = {('eth0', mgmt_prefix): {'gwaddr': gwaddr}}
results['MGMT_INTERFACE'] = {}
if mgmt_prefix and mgmt_prefix != '0.0.0.0/0':
mgmtipn = ipaddress.ip_network(UNICODE_TYPE(mgmt_prefix), False)
gwaddr = ipaddress.ip_address((next(mgmtipn.hosts())))
results['MGMT_INTERFACE'].update({('eth0', mgmt_prefix): {'gwaddr': gwaddr}})

if mgmt_prefix_v6 and mgmt_prefix_v6 != '::/0':
mgmtipn_v6 = ipaddress.ip_network(UNICODE_TYPE(mgmt_prefix_v6), False)
gwaddr_v6 = ipaddress.ip_address((next(mgmtipn_v6.hosts())))
results['MGMT_INTERFACE'].update({('eth0', mgmt_prefix_v6): {'gwaddr': gwaddr_v6}})

return results

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Device i:type="ToRRouter" xmlns="Microsoft.Search.Autopilot.Evolution" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Hostname>switch-t0</Hostname>
<HwSku>Force10-S6000</HwSku>
<ClusterName>AAA00PrdStr00</ClusterName>
<ManagementAddress xmlns:a="Microsoft.Search.Autopilot.NetMux">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ManagementAddress

Could you create another test data with no ManagementAddress node at all?

<a:IPPrefix>0.0.0.0/0</a:IPPrefix>
</ManagementAddress>
<ManagementAddressV6 xmlns:a="Microsoft.Search.Autopilot.NetMux">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ManagementAddressV6

Could you reformat this XML file?

<a:IPPrefix>FC00:1::32/64</a:IPPrefix>
</ManagementAddressV6>
</Device>
11 changes: 11 additions & 0 deletions src/sonic-config-engine/tests/simple-sample-device-desc.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Device i:type="ToRRouter" xmlns="Microsoft.Search.Autopilot.Evolution" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Hostname>switch-t0</Hostname>
<HwSku>Force10-S6000</HwSku>
<ClusterName>AAA00PrdStr00</ClusterName>
<ManagementAddress xmlns:a="Microsoft.Search.Autopilot.NetMux">
<a:IPPrefix>10.0.0.100/24</a:IPPrefix>
</ManagementAddress>
<ManagementAddressV6 xmlns:a="Microsoft.Search.Autopilot.NetMux">
<a:IPPrefix>FC00:1::32/64</a:IPPrefix>
</ManagementAddressV6>
</Device>
21 changes: 19 additions & 2 deletions src/sonic-config-engine/tests/test_minigraph_case.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import os
import subprocess
import ipaddress

import tests.common_utils as utils
import minigraph
Expand All @@ -21,6 +22,8 @@ def setUp(self):
self.sample_simple_graph = os.path.join(self.test_dir, 'simple-sample-graph.xml')
self.sample_resource_graph = os.path.join(self.test_dir, 'sample-graph-resource-type.xml')
self.sample_subintf_graph = os.path.join(self.test_dir, 'sample-graph-subintf.xml')
self.sample_simple_device_desc = os.path.join(self.test_dir, 'simple-sample-device-desc.xml')
self.sample_simple_device_desc_ipv6_only = os.path.join(self.test_dir, 'simple-sample-device-desc-ipv6-only.xml')
self.port_config = os.path.join(self.test_dir, 't0-sample-port-config.ini')

def run_script(self, argument, check_stderr=False):
Expand Down Expand Up @@ -463,5 +466,19 @@ def test_minigraph_mirror_dscp(self):
expected_ports.sort()
)



def test_parse_device_desc_xml_mgmt_interface(self):
# Regular device_desc.xml with both IPv4 and IPv6 mgmt address
result = minigraph.parse_device_desc_xml(self.sample_simple_device_desc)
mgmt_intf = result['MGMT_INTERFACE']
self.assertEqual(len(mgmt_intf.keys()), 2)
self.assertTrue(('eth0', '10.0.0.100/24') in mgmt_intf.keys())
self.assertTrue(('eth0', 'FC00:1::32/64') in mgmt_intf.keys())
self.assertTrue(ipaddress.ip_address(u'10.0.0.1') == mgmt_intf[('eth0', '10.0.0.100/24')]['gwaddr'])
self.assertTrue(ipaddress.ip_address(u'fc00:1::1') == mgmt_intf[('eth0', 'FC00:1::32/64')]['gwaddr'])

# Special device_desc.xml with IPv6 mgmt address only
result = minigraph.parse_device_desc_xml(self.sample_simple_device_desc_ipv6_only)
mgmt_intf = result['MGMT_INTERFACE']
self.assertEqual(len(mgmt_intf.keys()), 1)
self.assertTrue(('eth0', 'FC00:1::32/64') in mgmt_intf.keys())
self.assertTrue(ipaddress.ip_address(u'fc00:1::1') == mgmt_intf[('eth0', 'FC00:1::32/64')]['gwaddr'])