diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index 8d3e82ea123..0344ad0f04e 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -42,11 +42,33 @@ class minigraph_encoder(json.JSONEncoder): def default(self, obj): - if isinstance(obj, - (ipaddress.IPv4Network, ipaddress.IPv6Network, ipaddress.IPv4Address, ipaddress.IPv6Address)): + if isinstance(obj, ( + ipaddress.IPv4Network, ipaddress.IPv6Network, + ipaddress.IPv4Address, ipaddress.IPv6Address + )): return str(obj) return json.JSONEncoder.default(self, obj) +def parse_device(device): + lo_prefix = None + mgmt_prefix = None + d_type = None # don't shadow type() + hwsku = None + name = None + if str(QName(ns3, "type")) in device.attrib: + d_type = device.attrib[str(QName(ns3, "type"))] + + for node in device: + if node.tag == str(QName(ns, "Address")): + lo_prefix = 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, "Hostname")): + name = node.text + elif node.tag == str(QName(ns, "HwSku")): + hwsku = node.text + return (lo_prefix, mgmt_prefix, name, hwsku, d_type) + def parse_png(png, hname): neighbors = {} devices = {} @@ -77,24 +99,9 @@ def parse_png(png, hname): if child.tag == str(QName(ns, "Devices")): for device in child.findall(str(QName(ns, "Device"))): - lo_addr = None - # don't shadow type() - d_type = None - mgmt_addr = None - hwsku = None - if str(QName(ns3, "type")) in device.attrib: - d_type = device.attrib[str(QName(ns3, "type"))] - - for node in device: - if node.tag == str(QName(ns, "Address")): - lo_addr = node.find(str(QName(ns2, "IPPrefix"))).text.split('/')[0] - elif node.tag == str(QName(ns, "ManagementAddress")): - mgmt_addr = node.find(str(QName(ns2, "IPPrefix"))).text.split('/')[0] - elif node.tag == str(QName(ns, "Hostname")): - name = node.text - elif node.tag == str(QName(ns, "HwSku")): - hwsku = node.text - + (lo_prefix, mgmt_prefix, name, hwsku, d_type) = parse_device(device) + lo_addr = None if not lo_prefix else lo_prefix.split('/')[0] + mgmt_addr = None if not mgmt_prefix else mgmt_prefix.split('/')[0] devices[name] = {'lo_addr': lo_addr, 'type': d_type, 'mgmt_addr': mgmt_addr, 'hwsku': hwsku} if child.tag == str(QName(ns, "DeviceInterfaceLinks")): @@ -270,7 +277,7 @@ def parse_cpg(cpg, hname): myasn = int(asn) peers = router.find(str(QName(ns1, "Peers"))) for bgpPeer in peers.findall(str(QName(ns, "BGPPeer"))): - addr = bgpPeer.find(str(QName(ns, "Address"))).text + addr = bgpPeer.find(str(QName(ns, "Address"))).text if bgpPeer.find(str(QName(ns1, "PeersRange"))) is not None: name = bgpPeer.find(str(QName(ns1, "Name"))).text ip_range = bgpPeer.find(str(QName(ns1, "PeersRange"))).text @@ -382,7 +389,6 @@ def parse_port_config(hwsku, platform=None, port_config_file=None): port_alias_map[alias] = name return ports - def parse_xml(filename, platform=None, port_config_file=None): root = ET.parse(filename).getroot() mini_graph_path = filename @@ -432,9 +438,7 @@ def parse_xml(filename, platform=None, port_config_file=None): elif child.tag == str(QName(ns, "MetadataDeclaration")): (syslog_servers, dhcp_servers, ntp_servers, mgmt_routes, erspan_dst, deployment_id) = parse_meta(child, hostname) - Tree = lambda: defaultdict(Tree) - - results = Tree() + results = {} results['minigraph_hwsku'] = hwsku # sorting by lambdas are not easily done without custom filters. # TODO: add jinja2 filter to accept a lambda to sort a list of dictionaries by attribute. @@ -484,6 +488,38 @@ def parse_xml(filename, platform=None, port_config_file=None): return results +def parse_device_desc_xml(filename): + root = ET.parse(filename).getroot() + (lo_prefix, mgmt_prefix, hostname, hwsku, d_type) = parse_device(root) + + results = {} + results['minigraph_hwsku'] = hwsku + results['minigraph_hostname'] = hostname + results['inventory_hostname'] = hostname + + lo_intfs = [] + ipn = ipaddress.IPNetwork(lo_prefix) + ipaddr = ipn.ip + prefix_len = ipn.prefixlen + ipmask = ipn.netmask + lo_intf = {'name': None, 'addr': ipaddr, 'prefixlen': prefix_len} + if isinstance(ipn, ipaddress.IPv4Network): + lo_intf['mask'] = ipmask + else: + lo_intf['mask'] = str(prefix_len) + lo_intfs.append(lo_intf) + results['minigraph_lo_interfaces'] = lo_intfs + + mgmt_intf = None + mgmt_ipn = ipaddress.IPNetwork(mgmt_prefix) + ipaddr = mgmt_ipn.ip + prefix_len = str(mgmt_ipn.prefixlen) + ipmask = mgmt_ipn.netmask + gwaddr = ipaddress.IPAddress(int(mgmt_ipn.network) + 1) + mgmt_intf = {'addr': ipaddr, 'prefixlen': prefix_len, 'mask': ipmask, 'gwaddr': gwaddr} + results['minigraph_mgmt_interface'] = mgmt_intf + return results + port_alias_map = {} def print_parse_xml(filename): diff --git a/src/sonic-config-engine/sonic-cfggen b/src/sonic-config-engine/sonic-cfggen index c0ce51d4bf8..8f5b78ac66c 100755 --- a/src/sonic-config-engine/sonic-cfggen +++ b/src/sonic-config-engine/sonic-cfggen @@ -9,6 +9,7 @@ import netaddr import json from minigraph import minigraph_encoder from minigraph import parse_xml +from minigraph import parse_device_desc_xml from sonic_platform import get_machine_info from sonic_platform import get_platform_info @@ -47,7 +48,9 @@ def unique_name(l): def main(): parser=argparse.ArgumentParser(description="Render configuration file from minigraph data and jinja2 template.") - parser.add_argument("-m", "--minigraph", help="minigraph xml file") + group = parser.add_mutually_exclusive_group() + group.add_argument("-m", "--minigraph", help="minigraph xml file") + group.add_argument("-M", "--device-description", help="device description xml file") parser.add_argument("-p", "--port-config", help="port config file, used with -m") parser.add_argument("-y", "--yaml", help="yaml file that contains addtional variables", action='append', default=[]) parser.add_argument("-a", "--additional-data", help="addition data, in json string") @@ -79,6 +82,9 @@ def main(): else: data.update(parse_xml(minigraph)) + if args.device_description != None: + data.update(parse_device_desc_xml(args.device_description)) + for yaml_file in args.yaml: with open(yaml_file, 'r') as stream: additional_data = yaml.load(stream) diff --git a/src/sonic-config-engine/tests/device.xml b/src/sonic-config-engine/tests/device.xml new file mode 100644 index 00000000000..2995c4dde13 --- /dev/null +++ b/src/sonic-config-engine/tests/device.xml @@ -0,0 +1,17 @@ + + ToRRouter +
+ 10.10.0.12/32 +
+ + ::/0 + + + 10.0.1.5/28 + + + ::/0 + + switch1 + ACS-MSN2700 +
diff --git a/src/sonic-config-engine/tests/test_cfggen.py b/src/sonic-config-engine/tests/test_cfggen.py index 9a305fd72aa..eae4cb74fa4 100644 --- a/src/sonic-config-engine/tests/test_cfggen.py +++ b/src/sonic-config-engine/tests/test_cfggen.py @@ -12,6 +12,7 @@ def setUp(self): self.sample_graph_simple = os.path.join(self.test_dir, 'simple-sample-graph.xml') self.sample_graph_pc_test = os.path.join(self.test_dir, 'pc-test-graph.xml') self.sample_graph_bgp_speaker = os.path.join(self.test_dir, 't0-sample-bgp-speaker.xml') + self.sample_device_desc = os.path.join(self.test_dir, 'device.xml') self.port_config = os.path.join(self.test_dir, 't0-sample-port-config.ini') def run_script(self, argument): @@ -29,6 +30,16 @@ def test_dummy_run(self): output = self.run_script(argument) self.assertEqual(output, '') + def test_device_desc(self): + argument = '-v minigraph_hwsku -M "' + self.sample_device_desc + '"' + output = self.run_script(argument) + self.assertEqual(output.strip(), 'ACS-MSN2700') + + def test_device_desc_mgmt_ip(self): + argument = '-v "minigraph_mgmt_interface[\'addr\']" -M "' + self.sample_device_desc + '"' + output = self.run_script(argument) + self.assertEqual(output.strip(), '10.0.1.5') + def test_minigraph_sku(self): argument = '-v minigraph_hwsku -m "' + self.sample_graph + '"' output = self.run_script(argument)