From 8eb6cbd4282286d41d682543c2ea8834ccd508b5 Mon Sep 17 00:00:00 2001 From: Taoyu Li Date: Wed, 4 Jan 2017 03:14:02 +0000 Subject: [PATCH 1/2] add sonic config render engine --- src/sonic-config-engine/debian/changelog | 5 + src/sonic-config-engine/debian/compat | 1 + src/sonic-config-engine/debian/control | 12 + src/sonic-config-engine/debian/rules | 9 + src/sonic-config-engine/debian/source/format | 1 + src/sonic-config-engine/debian/source/options | 1 + src/sonic-config-engine/minigraph.py | 373 ++++++++++++++++++ src/sonic-config-engine/render_config | 63 +++ src/sonic-config-engine/setup.py | 13 + 9 files changed, 478 insertions(+) create mode 100644 src/sonic-config-engine/debian/changelog create mode 100644 src/sonic-config-engine/debian/compat create mode 100644 src/sonic-config-engine/debian/control create mode 100755 src/sonic-config-engine/debian/rules create mode 100644 src/sonic-config-engine/debian/source/format create mode 100644 src/sonic-config-engine/debian/source/options create mode 100644 src/sonic-config-engine/minigraph.py create mode 100755 src/sonic-config-engine/render_config create mode 100644 src/sonic-config-engine/setup.py diff --git a/src/sonic-config-engine/debian/changelog b/src/sonic-config-engine/debian/changelog new file mode 100644 index 000000000000..3bb710093b6d --- /dev/null +++ b/src/sonic-config-engine/debian/changelog @@ -0,0 +1,5 @@ +sonic-config-engine (1.0-1) unstable; urgency=low + + * source package automatically created by stdeb 0.6.0+git + + -- Taoyu Li Wed, 04 Jan 2017 02:45:47 +0000 diff --git a/src/sonic-config-engine/debian/compat b/src/sonic-config-engine/debian/compat new file mode 100644 index 000000000000..7f8f011eb73d --- /dev/null +++ b/src/sonic-config-engine/debian/compat @@ -0,0 +1 @@ +7 diff --git a/src/sonic-config-engine/debian/control b/src/sonic-config-engine/debian/control new file mode 100644 index 000000000000..184e3a865cd6 --- /dev/null +++ b/src/sonic-config-engine/debian/control @@ -0,0 +1,12 @@ +Source: sonic-config-engine +Maintainer: Taoyu Li +Section: python +Priority: optional +Build-Depends: python-all (>= 2.6.6-3), debhelper (>= 7.4.3) +Standards-Version: 3.9.1 + +Package: sonic-config-engine +Architecture: all +Depends: ${misc:Depends}, ${python:Depends}, python-netaddr, python-ipaddr, python-jinja2, python-lxml, python-yaml +Description: Utilities for generating SONiC configuration files + diff --git a/src/sonic-config-engine/debian/rules b/src/sonic-config-engine/debian/rules new file mode 100755 index 000000000000..5004e0644cbe --- /dev/null +++ b/src/sonic-config-engine/debian/rules @@ -0,0 +1,9 @@ +#!/usr/bin/make -f + +# This file was automatically generated by stdeb 0.6.0+git at +# Wed, 04 Jan 2017 02:45:47 +0000 + +%: + dh $@ --with python2 --buildsystem=python_distutils + + diff --git a/src/sonic-config-engine/debian/source/format b/src/sonic-config-engine/debian/source/format new file mode 100644 index 000000000000..163aaf8d82b6 --- /dev/null +++ b/src/sonic-config-engine/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/src/sonic-config-engine/debian/source/options b/src/sonic-config-engine/debian/source/options new file mode 100644 index 000000000000..4d82e2208ce7 --- /dev/null +++ b/src/sonic-config-engine/debian/source/options @@ -0,0 +1 @@ +extend-diff-ignore="\.egg-info" \ No newline at end of file diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py new file mode 100644 index 000000000000..7a64c7d2dc12 --- /dev/null +++ b/src/sonic-config-engine/minigraph.py @@ -0,0 +1,373 @@ +#!/usr/bin/env python +import calendar +import os +import sys +import socket +import struct +import json +import copy +import ipaddr as ipaddress +from collections import defaultdict + +from lxml import etree as ET +from lxml.etree import QName + +DOCUMENTATION = ''' +--- +module: minigraph_facts +version_added: "1.9" +author: Guohan Lu (gulv@microsoft.com) +short_description: Retrive minigraph facts for a device. +description: + - Retrieve minigraph facts for a device, the facts will be + inserted to the ansible_facts key. +options: + host: + description: + - Set to target snmp server (normally {{inventory_hostname}}) + required: true +''' + +EXAMPLES = ''' +# Gather minigraph facts +- name: Gathering minigraph facts about the device + minigraph_facts: host={{ hostname }} +''' + +ns = "Microsoft.Search.Autopilot.Evolution" +ns1 = "http://schemas.datacontract.org/2004/07/Microsoft.Search.Autopilot.Evolution" +ns2 = "Microsoft.Search.Autopilot.NetMux" +ns3 = "http://www.w3.org/2001/XMLSchema-instance" + +class minigraph_encoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, (ipaddress.IPv4Network, ipaddress.IPv6Network, ipaddress.IPv4Address, ipaddress.IPv6Address)): + return str(obj) + return json.JSONEncoder.default(self, obj) + +def parse_png(png, hname): + neighbors = {} + devices = {} + console_dev = '' + console_port = '' + mgmt_dev = '' + mgmt_port = '' + for child in png: + if child.tag == str(QName(ns, "DeviceInterfaceLinks")): + for link in child.findall(str(QName(ns, "DeviceLinkBase"))): + linktype = link.find(str(QName(ns, "ElementType"))).text + if linktype != "DeviceInterfaceLink" and linktype != "UnderlayInterfaceLink": + continue + + enddevice = link.find(str(QName(ns, "EndDevice"))).text + endport = link.find(str(QName(ns, "EndPort"))).text + startdevice = link.find(str(QName(ns, "StartDevice"))).text + startport = link.find(str(QName(ns, "StartPort"))).text + + if enddevice == hname: + neighbors[endport] = {'name': startdevice, 'port': startport} + else: + neighbors[startport] = {'name': enddevice, 'port': endport} + 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 + + devices[name] = {'lo_addr': lo_addr, 'type': d_type, 'mgmt_addr': mgmt_addr, 'hwsku': hwsku} + + if child.tag == str(QName(ns, "DeviceInterfaceLinks")): + for if_link in child.findall(str(QName(ns, 'DeviceLinkBase'))): + if str(QName(ns3, "type")) in if_link.attrib: + link_type = if_link.attrib[str(QName(ns3, "type"))] + if link_type == 'DeviceSerialLink': + for node in if_link: + if node.tag == str(QName(ns, "EndPort")): + console_port = node.text.split()[-1] + elif node.tag == str(QName(ns, "EndDevice")): + console_dev = node.text + elif link_type == 'DeviceMgmtLink': + for node in if_link: + if node.tag == str(QName(ns, "EndPort")): + mgmt_port = node.text.split()[-1] + elif node.tag == str(QName(ns, "EndDevice")): + mgmt_dev = node.text + + + return (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port) + + +def parse_dpg(dpg, hname): + for child in dpg: + hostname = child.find(str(QName(ns, "Hostname"))) + if hostname.text != hname: + continue + + ipintfs = child.find(str(QName(ns, "IPInterfaces"))) + intfs = [] + vlan_map = {} + for ipintf in ipintfs.findall(str(QName(ns, "IPInterface"))): + intfname = ipintf.find(str(QName(ns, "AttachTo"))).text + ipprefix = ipintf.find(str(QName(ns, "Prefix"))).text + ipn = ipaddress.IPNetwork(ipprefix) + ipaddr = ipn.ip + prefix_len = ipn.prefixlen + addr_bits = ipn.max_prefixlen + subnet = ipaddress.IPNetwork(str(ipn.network) + '/' + str(prefix_len)) + ipmask = ipn.netmask + + intf = {'addr': ipaddr, 'subnet': subnet} + if isinstance(ipn, ipaddress.IPv4Network): + intf['mask'] = ipmask + else: + intf['mask'] = str(prefix_len) + + if intfname[0:4] == "Vlan": + if intfname in vlan_map: + vlan_map[intfname].append(intf) + + else: + vlan_map[intfname] = [intf] + else: + intf.update({'name': intfname, 'prefixlen': int(prefix_len)}) + + if port_alias_map.has_key(intfname): + intf['alias'] = port_alias_map[intfname] + else: + intf['alias'] = intfname + + # TODO: remove peer_addr after dependency removed + ipaddr_val = int(ipn.ip) + peer_addr_val = None + if int(prefix_len) == addr_bits - 2: + if ipaddr_val & 0x3 == 1: + peer_addr_val = ipaddr_val + 1 + else: + peer_addr_val = ipaddr_val - 1 + elif int(prefix_len) == addr_bits - 1: + if ipaddr_val & 0x1 == 0: + peer_addr_val = ipaddr_val + 1 + else: + peer_addr_val = ipaddr_val - 1 + + if peer_addr_val is not None: + intf['peer_addr'] = ipaddress.IPAddress(peer_addr_val) + intfs.append(intf) + + pcintfs = child.find(str(QName(ns, "PortChannelInterfaces"))) + pc_intfs = [] + for pcintf in pcintfs.findall(str(QName(ns, "PortChannel"))): + pcintfname = pcintf.find(str(QName(ns, "Name"))).text + pcintfmbr = pcintf.find(str(QName(ns, "AttachTo"))).text + mbr_list = pcintfmbr.split(';', 1) + pc_intfs.append({'name': pcintfname, 'members': mbr_list}) + + lointfs = child.find(str(QName(ns, "LoopbackIPInterfaces"))) + lo_intfs = [] + for lointf in lointfs.findall(str(QName(ns1, "LoopbackIPInterface"))): + intfname = lointf.find(str(QName(ns, "AttachTo"))).text + ipprefix = lointf.find(str(QName(ns1, "PrefixStr"))).text + ipn = ipaddress.IPNetwork(ipprefix) + ipaddr = ipn.ip + prefix_len = ipn.prefixlen + ipmask = ipn.netmask + lo_intf = {'name': intfname, '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) + + mgmtintfs = child.find(str(QName(ns, "ManagementIPInterfaces"))) + mgmt_intf = None + for mgmtintf in mgmtintfs.findall(str(QName(ns1, "ManagementIPInterface"))): + ipprefix = mgmtintf.find(str(QName(ns1, "PrefixStr"))).text + mgmtipn = ipaddress.IPNetwork(ipprefix) + ipaddr = mgmtipn.ip + prefix_len = str(mgmtipn.prefixlen) + ipmask = mgmtipn.netmask + gwaddr = ipaddress.IPAddress(int(mgmtipn.network) + 1) + mgmt_intf = {'addr': ipaddr, 'prefixlen': prefix_len, 'mask': ipmask, 'gwaddr': gwaddr} + + vlanintfs = child.find(str(QName(ns, "VlanInterfaces"))) + vlan_intfs = [] + for vintf in vlanintfs.findall(str(QName(ns, "VlanInterface"))): + vintfname = vintf.find(str(QName(ns, "Name"))).text + vlanid = vintf.find(str(QName(ns, "VlanID"))).text + members = vintf.find(str(QName(ns, "AttachTo"))).text + members = " ".join(members.split(';')) + vlan_attributes = {'name': vintfname, 'members': members, 'vlanid': vlanid} + for addrtuple in vlan_map.get(vintfname, []): + vlan_attributes.update(addrtuple) + vlan_intfs.append(copy.deepcopy(vlan_attributes)) + + return intfs, lo_intfs, mgmt_intf, vlan_intfs, pc_intfs + return None, None, None, None, None + +def parse_cpg(cpg, hname): + bgp_sessions = [] + myasn = None + for child in cpg: + tag = child.tag + if tag == str(QName(ns, "PeeringSessions")): + for session in child.findall(str(QName(ns, "BGPSession"))): + start_router = session.find(str(QName(ns, "StartRouter"))).text + start_peer = session.find(str(QName(ns, "StartPeer"))).text + end_router = session.find(str(QName(ns, "EndRouter"))).text + end_peer = session.find(str(QName(ns, "EndPeer"))).text + if end_router == hname: + bgp_sessions.append({ + 'name': start_router, + 'addr': start_peer, + 'peer_addr': end_peer + }) + else: + bgp_sessions.append({ + 'name': end_router, + 'addr': end_peer, + 'peer_addr': start_peer + }) + elif child.tag == str(QName(ns, "Routers")): + for router in child.findall(str(QName(ns1, "BGPRouterDeclaration"))): + asn = router.find(str(QName(ns1, "ASN"))).text + hostname = router.find(str(QName(ns1, "Hostname"))).text + if hostname == hname: + myasn = int(asn) + else: + for bgp_session in bgp_sessions: + if hostname == bgp_session['name']: + bgp_session['asn'] = int(asn) + + return bgp_sessions, myasn + + +def get_console_info(devices, dev, port): + for k, v in devices.items(): + if k == dev: + break + else: + return {} + + ret_val = v + ret_val.update({ + 'ts_port': port, + 'ts_dev': dev + }) + + return ret_val + +def get_mgmt_info(devices, dev, port): + for k, v in devices.items(): + if k == dev: + break + else: + return {} + + ret_val = v + ret_val.update({ + 'mgmt_port': port, + 'mgmt_dev': dev + }) + + return ret_val + +def parse_xml(filename): + root = ET.parse(filename).getroot() + mini_graph_path = filename + + u_neighbors = None + u_devices = None + hwsku = None + bgp_sessions = None + bgp_asn = None + intfs = None + vlan_intfs = None + pc_intfs = None + mgmt_intf = None + lo_intf = None + neighbors = None + devices = None + hostname = None + + hwsku_qn = QName(ns, "HwSku") + hostname_qn = QName(ns, "Hostname") + for child in root: + if child.tag == str(hwsku_qn): + hwsku = child.text + if child.tag == str(hostname_qn): + hostname = child.text + + # port_alias_map maps ngs port name to sonic port name + if hwsku == "Force10-S6000": + for i in range(0, 128, 4): + port_alias_map["fortyGigE0/%d" % i] = "Ethernet%d" % i + elif hwsku == "Arista-7050-QX32": + for i in range(1, 25): + port_alias_map["Ethernet%d/1" % i] = "Ethernet%d" % ((i - 1) * 4) + for i in range(25, 33): + port_alias_map["Ethernet%d" % i] = "Ethernet%d" % ((i - 1) * 4) + + for child in root: + if child.tag == str(QName(ns, "DpgDec")): + (intfs, lo_intfs, mgmt_intf, vlan_intfs, pc_intfs) = parse_dpg(child, hostname) + elif child.tag == str(QName(ns, "CpgDec")): + (bgp_sessions, bgp_asn) = parse_cpg(child, hostname) + elif child.tag == str(QName(ns, "PngDec")): + (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port) = parse_png(child, hostname) + elif child.tag == str(QName(ns, "UngDec")): + (u_neighbors, u_devices, _, _, _, _) = parse_png(child, hostname) + + # Replace port with alias in port channel interfaces members + for pc in pc_intfs: + for i,member in enumerate(pc['members']): + pc['members'][i] = port_alias_map[member] + + Tree = lambda: defaultdict(Tree) + + results = Tree() + 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. + # TODO: alternatively (preferred), implement class containers for multiple-attribute entries, enabling sort by attr + results['minigraph_bgp'] = sorted(bgp_sessions, key=lambda x: x['addr']) + results['minigraph_bgp_asn'] = bgp_asn + # TODO: sort does not work properly on all interfaces of varying lengths. Need to sort by integer group(s). + results['minigraph_interfaces'] = sorted(intfs, key=lambda x: x['name']) + results['minigraph_vlan_interfaces'] = vlan_intfs + results['minigraph_portchannel_interfaces'] = pc_intfs + results['minigraph_mgmt_interface'] = mgmt_intf + results['minigraph_lo_interfaces'] = lo_intfs + results['minigraph_neighbors'] = neighbors + results['minigraph_devices'] = devices + results['minigraph_underlay_neighbors'] = u_neighbors + results['minigraph_underlay_devices'] = u_devices + results['minigraph_as_xml'] = mini_graph_path + results['minigraph_console'] = get_console_info(devices, console_dev, console_port) + results['minigraph_mgmt'] = get_mgmt_info(devices, mgmt_dev, mgmt_port) + results['inventory_hostname'] = hostname + + return results + + +port_alias_map = {} + + +def print_parse_xml(filename): + results = parse_xml(filename) + print(json.dumps(results, indent=3, cls=minigraph_encoder)) + + diff --git a/src/sonic-config-engine/render_config b/src/sonic-config-engine/render_config new file mode 100755 index 000000000000..e20b15977d18 --- /dev/null +++ b/src/sonic-config-engine/render_config @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +import sys +import os.path +import argparse +import yaml +import jinja2 +import netaddr +from minigraph import parse_xml + + +def is_ipv4(value): + if not value: + return False + if isinstance(value, netaddr.IPAddress): + addr = value + else: + try: + addr = netaddr.IPAddress(str(value)) + except: + return False + return addr.version == 4 + +def is_ipv6(value): + if not value: + return False + if isinstance(value, netaddr.IPAddress): + addr = value + else: + try: + addr = netaddr.IPAddress(str(value)) + except: + return False + return addr.version == 6 + + +def main(): + parser=argparse.ArgumentParser(description="Render configuration file from minigraph data and jinja2 template.") + parser.add_argument("template") + parser.add_argument("-m", "--minigraph", required=True, help="minigraph xml file") + parser.add_argument("-v", "--var-file", help="yaml file that contains addtional variables") + args = parser.parse_args() + + minigraph = args.minigraph + template_file = os.path.abspath(args.template) + + data = parse_xml(minigraph) + + if args.var_file != None: + with open(args.var_file, 'r') as stream: + additional_data = yaml.load(stream) + data.update(additional_data) + + env = jinja2.Environment(loader=jinja2.FileSystemLoader('/'), trim_blocks=True) + env.filters['ipv4'] = is_ipv4 + env.filters['ipv6'] = is_ipv6 + template = env.get_template(template_file) + + print template.render(data) + +if __name__ == "__main__": + main() + diff --git a/src/sonic-config-engine/setup.py b/src/sonic-config-engine/setup.py new file mode 100644 index 000000000000..baf317a702b9 --- /dev/null +++ b/src/sonic-config-engine/setup.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +from distutils.core import setup + +setup(name='sonic-config-engine', + version='1.0', + description='Utilities for generating SONiC configuration files', + author='Taoyu Li', + author_email='taoyl@microsoft.com', + url='https://github.com/Azure/sonic-buildimage', + py_modules=['minigraph'], + scripts=['render_config'], + ) From 2cde0619dff4edf67e4c4b5352497636e722c05e Mon Sep 17 00:00:00 2001 From: Taoyu Li Date: Fri, 6 Jan 2017 01:21:46 +0000 Subject: [PATCH 2/2] config-engine: Fix according to CR and fit into build system --- rules/sonic-config.mk | 5 +++++ src/sonic-config-engine/debian/changelog | 5 ----- src/sonic-config-engine/debian/compat | 1 - src/sonic-config-engine/debian/control | 12 ------------ src/sonic-config-engine/debian/rules | 9 --------- src/sonic-config-engine/debian/source/format | 1 - src/sonic-config-engine/debian/source/options | 1 - src/sonic-config-engine/minigraph.py | 18 +++++++++++++----- src/sonic-config-engine/setup.py | 5 +++-- .../{render_config => sonic-cfggen} | 0 10 files changed, 21 insertions(+), 36 deletions(-) create mode 100644 rules/sonic-config.mk delete mode 100644 src/sonic-config-engine/debian/changelog delete mode 100644 src/sonic-config-engine/debian/compat delete mode 100644 src/sonic-config-engine/debian/control delete mode 100755 src/sonic-config-engine/debian/rules delete mode 100644 src/sonic-config-engine/debian/source/format delete mode 100644 src/sonic-config-engine/debian/source/options rename src/sonic-config-engine/{render_config => sonic-cfggen} (100%) diff --git a/rules/sonic-config.mk b/rules/sonic-config.mk new file mode 100644 index 000000000000..ba2fdacc2c50 --- /dev/null +++ b/rules/sonic-config.mk @@ -0,0 +1,5 @@ +# sonic-config-engine package + +SONIC_CONFIG_ENGINE = sonic-config-engine_1.0-1_all.deb +$(SONIC_CONFIG_ENGINE)_SRC_PATH = $(SRC_PATH)/sonic-config-engine +SONIC_PYTHON_STDEB_DEBS += $(SONIC_CONFIG_ENGINE) diff --git a/src/sonic-config-engine/debian/changelog b/src/sonic-config-engine/debian/changelog deleted file mode 100644 index 3bb710093b6d..000000000000 --- a/src/sonic-config-engine/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -sonic-config-engine (1.0-1) unstable; urgency=low - - * source package automatically created by stdeb 0.6.0+git - - -- Taoyu Li Wed, 04 Jan 2017 02:45:47 +0000 diff --git a/src/sonic-config-engine/debian/compat b/src/sonic-config-engine/debian/compat deleted file mode 100644 index 7f8f011eb73d..000000000000 --- a/src/sonic-config-engine/debian/compat +++ /dev/null @@ -1 +0,0 @@ -7 diff --git a/src/sonic-config-engine/debian/control b/src/sonic-config-engine/debian/control deleted file mode 100644 index 184e3a865cd6..000000000000 --- a/src/sonic-config-engine/debian/control +++ /dev/null @@ -1,12 +0,0 @@ -Source: sonic-config-engine -Maintainer: Taoyu Li -Section: python -Priority: optional -Build-Depends: python-all (>= 2.6.6-3), debhelper (>= 7.4.3) -Standards-Version: 3.9.1 - -Package: sonic-config-engine -Architecture: all -Depends: ${misc:Depends}, ${python:Depends}, python-netaddr, python-ipaddr, python-jinja2, python-lxml, python-yaml -Description: Utilities for generating SONiC configuration files - diff --git a/src/sonic-config-engine/debian/rules b/src/sonic-config-engine/debian/rules deleted file mode 100755 index 5004e0644cbe..000000000000 --- a/src/sonic-config-engine/debian/rules +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/make -f - -# This file was automatically generated by stdeb 0.6.0+git at -# Wed, 04 Jan 2017 02:45:47 +0000 - -%: - dh $@ --with python2 --buildsystem=python_distutils - - diff --git a/src/sonic-config-engine/debian/source/format b/src/sonic-config-engine/debian/source/format deleted file mode 100644 index 163aaf8d82b6..000000000000 --- a/src/sonic-config-engine/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/src/sonic-config-engine/debian/source/options b/src/sonic-config-engine/debian/source/options deleted file mode 100644 index 4d82e2208ce7..000000000000 --- a/src/sonic-config-engine/debian/source/options +++ /dev/null @@ -1 +0,0 @@ -extend-diff-ignore="\.egg-info" \ No newline at end of file diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index 7a64c7d2dc12..2bc7bf43cdfa 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -173,8 +173,8 @@ def parse_dpg(dpg, hname): for pcintf in pcintfs.findall(str(QName(ns, "PortChannel"))): pcintfname = pcintf.find(str(QName(ns, "Name"))).text pcintfmbr = pcintf.find(str(QName(ns, "AttachTo"))).text - mbr_list = pcintfmbr.split(';', 1) - pc_intfs.append({'name': pcintfname, 'members': mbr_list}) + pcmbr_list = pcintfmbr.split(';', 1) + pc_intfs.append({'name': pcintfname, 'members': pcmbr_list}) lointfs = child.find(str(QName(ns, "LoopbackIPInterfaces"))) lo_intfs = [] @@ -208,9 +208,9 @@ def parse_dpg(dpg, hname): for vintf in vlanintfs.findall(str(QName(ns, "VlanInterface"))): vintfname = vintf.find(str(QName(ns, "Name"))).text vlanid = vintf.find(str(QName(ns, "VlanID"))).text - members = vintf.find(str(QName(ns, "AttachTo"))).text - members = " ".join(members.split(';')) - vlan_attributes = {'name': vintfname, 'members': members, 'vlanid': vlanid} + vintfmbr = vintf.find(str(QName(ns, "AttachTo"))).text + vmbr_list = vintfmbr.split(';')) + vlan_attributes = {'name': vintfname, 'members': vmbr_list, 'vlanid': vlanid} for addrtuple in vlan_map.get(vintfname, []): vlan_attributes.update(addrtuple) vlan_intfs.append(copy.deepcopy(vlan_attributes)) @@ -331,6 +331,14 @@ def parse_xml(filename): elif child.tag == str(QName(ns, "UngDec")): (u_neighbors, u_devices, _, _, _, _) = parse_png(child, hostname) + # Replace port with alias in Vlan interfaces members + for vlan in vlan_intfs: + for i,member in enumerate(vlan['members']): + vlan['members'][i] = port_alias_map[member] + + # Convert vlan members into a space-delimited string + vlan['members'] = " ".join(vlan['members']) + # Replace port with alias in port channel interfaces members for pc in pc_intfs: for i,member in enumerate(pc['members']): diff --git a/src/sonic-config-engine/setup.py b/src/sonic-config-engine/setup.py index baf317a702b9..3223735b971a 100644 --- a/src/sonic-config-engine/setup.py +++ b/src/sonic-config-engine/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from distutils.core import setup +from setuptools import setup setup(name='sonic-config-engine', version='1.0', @@ -9,5 +9,6 @@ author_email='taoyl@microsoft.com', url='https://github.com/Azure/sonic-buildimage', py_modules=['minigraph'], - scripts=['render_config'], + scripts=['sonic-cfggen'], + install_requires=['lxml', 'jinja2', 'netaddr', 'ipaddr', 'yaml'], ) diff --git a/src/sonic-config-engine/render_config b/src/sonic-config-engine/sonic-cfggen similarity index 100% rename from src/sonic-config-engine/render_config rename to src/sonic-config-engine/sonic-cfggen