#!/usr/bin/env python import calendar import math 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 from portconfig import get_port_config """minigraph.py version_added: "1.9" author: Guohan Lu (gulv@microsoft.com) short_description: Parse minigraph xml file and device description xml file """ 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_device(device): lo_prefix = None mgmt_prefix = None d_type = None # don't shadow type() hwsku = None name = None deployment_id = 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 elif node.tag == str(QName(ns, "DeploymentId")): deployment_id = node.text return (lo_prefix, mgmt_prefix, name, hwsku, d_type, deployment_id) def parse_png(png, hname): neighbors = {} devices = {} console_dev = '' console_port = '' mgmt_dev = '' mgmt_port = '' port_speeds = {} console_ports = {} 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 == "DeviceSerialLink": 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 baudrate = link.find(str(QName(ns, "Bandwidth"))).text flowcontrol = 1 if link.find(str(QName(ns, "FlowControl"))) is not None and link.find(str(QName(ns, "FlowControl"))).text == 'true' else 0 if enddevice.lower() == hname.lower(): console_ports[endport] = { 'remote_device': startdevice, 'baud_rate': baudrate, 'flow_control': flowcontrol } else: console_ports[startport] = { 'remote_device': enddevice, 'baud_rate': baudrate, 'flow_control': flowcontrol } continue 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 bandwidth_node = link.find(str(QName(ns, "Bandwidth"))) bandwidth = bandwidth_node.text if bandwidth_node is not None else None if enddevice.lower() == hname.lower(): if port_alias_map.has_key(endport): endport = port_alias_map[endport] neighbors[endport] = {'name': startdevice, 'port': startport} if bandwidth: port_speeds[endport] = bandwidth else: if port_alias_map.has_key(startport): startport = port_alias_map[startport] neighbors[startport] = {'name': enddevice, 'port': endport} if bandwidth: port_speeds[startport] = bandwidth if child.tag == str(QName(ns, "Devices")): for device in child.findall(str(QName(ns, "Device"))): (lo_prefix, 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 } if deployment_id: device_data['deployment_id'] = deployment_id devices[name] = device_data 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, port_speeds, console_ports) def parse_dpg(dpg, hname): for child in dpg: hostname = child.find(str(QName(ns, "Hostname"))) if hostname.text.lower() != hname.lower(): continue ipintfs = child.find(str(QName(ns, "IPInterfaces"))) intfs = {} for ipintf in ipintfs.findall(str(QName(ns, "IPInterface"))): intfalias = ipintf.find(str(QName(ns, "AttachTo"))).text intfname = port_alias_map.get(intfalias, intfalias) ipprefix = ipintf.find(str(QName(ns, "Prefix"))).text intfs[(intfname, ipprefix)] = {} 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 lo_intfs[(intfname, ipprefix)] = {} mgmtintfs = child.find(str(QName(ns, "ManagementIPInterfaces"))) mgmt_intf = {} for mgmtintf in mgmtintfs.findall(str(QName(ns1, "ManagementIPInterface"))): intfname = mgmtintf.find(str(QName(ns, "AttachTo"))).text ipprefix = mgmtintf.find(str(QName(ns1, "PrefixStr"))).text mgmtipn = ipaddress.IPNetwork(ipprefix) gwaddr = ipaddress.IPAddress(int(mgmtipn.network) + 1) mgmt_intf[(intfname, ipprefix)] = {'gwaddr': gwaddr} pcintfs = child.find(str(QName(ns, "PortChannelInterfaces"))) pc_intfs = [] pcs = {} pc_members = {} intfs_inpc = [] # List to hold all the LAG member interfaces 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 pcmbr_list = pcintfmbr.split(';') pc_intfs.append(pcintfname) for i, member in enumerate(pcmbr_list): pcmbr_list[i] = port_alias_map.get(member, member) intfs_inpc.append(pcmbr_list[i]) pc_members[(pcintfname, pcmbr_list[i])] = {'NULL': 'NULL'} if pcintf.find(str(QName(ns, "Fallback"))) != None: pcs[pcintfname] = {'members': pcmbr_list, 'fallback': pcintf.find(str(QName(ns, "Fallback"))).text, 'min_links': str(int(math.ceil(len() * 0.75)))} else: pcs[pcintfname] = {'members': pcmbr_list, 'min_links': str(int(math.ceil(len(pcmbr_list) * 0.75)))} vlanintfs = child.find(str(QName(ns, "VlanInterfaces"))) vlan_intfs = [] vlans = {} vlan_members = {} 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 vintfmbr = vintf.find(str(QName(ns, "AttachTo"))).text vmbr_list = vintfmbr.split(';') for i, member in enumerate(vmbr_list): vmbr_list[i] = port_alias_map.get(member, member) sonic_vlan_member_name = "Vlan%s" % (vlanid) vlan_members[(sonic_vlan_member_name, vmbr_list[i])] = {'tagging_mode': 'untagged'} vlan_attributes = {'vlanid': vlanid} # If this VLAN requires a DHCP relay agent, it will contain a element # containing a list of DHCP server IPs vintf_node = vintf.find(str(QName(ns, "DhcpRelays"))) if vintf_node is not None and vintf_node.text is not None: vintfdhcpservers = vintf_node.text vdhcpserver_list = vintfdhcpservers.split(';') vlan_attributes['dhcp_servers'] = vdhcpserver_list sonic_vlan_name = "Vlan%s" % vlanid if sonic_vlan_name != vintfname: vlan_attributes['alias'] = vintfname vlans[sonic_vlan_name] = vlan_attributes aclintfs = child.find(str(QName(ns, "AclInterfaces"))) acls = {} for aclintf in aclintfs.findall(str(QName(ns, "AclInterface"))): aclname = aclintf.find(str(QName(ns, "InAcl"))).text.upper().replace(" ", "_").replace("-", "_") aclattach = aclintf.find(str(QName(ns, "AttachTo"))).text.split(';') acl_intfs = [] is_mirror = False # TODO: Ensure that acl_intfs will only ever contain front-panel interfaces (e.g., # maybe we should explicity ignore management and loopback interfaces?) because we # decide an ACL is a Control Plane ACL if acl_intfs is empty below. for member in aclattach: member = member.strip() if pcs.has_key(member): # If try to attach ACL to a LAG interface then we shall add the LAG to # to acl_intfs directly instead of break it into member ports, ACL attach # to LAG will be applied to all the LAG members internally by SAI/SDK acl_intfs.append(member) elif vlans.has_key(member): print >> sys.stderr, "Warning: ACL " + aclname + " is attached to a Vlan interface, which is currently not supported" elif port_alias_map.has_key(member): acl_intfs.append(port_alias_map[member]) # Give a warning if trying to attach ACL to a LAG member interface, correct way is to attach ACL to the LAG interface if port_alias_map[member] in intfs_inpc: print >> sys.stderr, "Warning: ACL " + aclname + " is attached to a LAG member interface " + port_alias_map[member] + ", instead of LAG interface" elif member.lower() == 'erspan': is_mirror = True; # Erspan session will be attached to all front panel ports, # if panel ports is a member port of LAG, should add the LAG # to acl table instead of the panel ports acl_intfs = pc_intfs for panel_port in port_alias_map.values(): if panel_port not in intfs_inpc: acl_intfs.append(panel_port) break; if acl_intfs: acls[aclname] = {'policy_desc': aclname, 'ports': acl_intfs, 'type': 'MIRROR' if is_mirror else 'L3'} elif is_mirror: acls[aclname] = {'policy_desc': aclname, 'type': 'MIRROR'} else: # This ACL has no interfaces to attach to -- consider this a control plane ACL try: aclservice = aclintf.find(str(QName(ns, "Type"))).text # If we already have an ACL with this name and this ACL is bound to a different service, # append the service to our list of services if aclname in acls: if acls[aclname]['type'] != 'CTRLPLANE': print >> sys.stderr, "Warning: ACL '%s' type mismatch. Not updating ACL." % aclname elif acls[aclname]['services'] == aclservice: print >> sys.stderr, "Warning: ACL '%s' already contains service '%s'. Not updating ACL." % (aclname, aclservice) else: acls[aclname]['services'].append(aclservice) else: acls[aclname] = {'policy_desc': aclname, 'type': 'CTRLPLANE', 'services': [aclservice]} except: print >> sys.stderr, "Warning: Ignoring Control Plane ACL %s without type" % aclname return intfs, lo_intfs, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls return None, None, None, None, None, None, None def parse_cpg(cpg, hname): bgp_sessions = {} myasn = None bgp_peers_with_range = {} 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 rrclient = 1 if session.find(str(QName(ns, "RRClient"))) is not None else 0 if session.find(str(QName(ns, "HoldTime"))) is not None: holdtime = session.find(str(QName(ns, "HoldTime"))).text else: holdtime = 180 if session.find(str(QName(ns, "KeepAliveTime"))) is not None: keepalive = session.find(str(QName(ns, "KeepAliveTime"))).text else: keepalive = 60 nhopself = 1 if session.find(str(QName(ns, "NextHopSelf"))) is not None else 0 if end_router.lower() == hname.lower(): bgp_sessions[start_peer.lower()] = { 'name': start_router, 'local_addr': end_peer.lower(), 'rrclient': rrclient, 'holdtime': holdtime, 'keepalive': keepalive, 'nhopself': nhopself } else: bgp_sessions[end_peer.lower()] = { 'name': end_router, 'local_addr': start_peer.lower(), 'rrclient': rrclient, 'holdtime': holdtime, 'keepalive': keepalive, 'nhopself': nhopself } 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.lower() == hname.lower(): myasn = 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 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 ip_range_group = ip_range.split(';') if ip_range and ip_range != "" else [] bgp_peers_with_range[name] = { 'name': name, 'ip_range': ip_range_group } else: for peer in bgp_sessions: bgp_session = bgp_sessions[peer] if hostname.lower() == bgp_session['name'].lower(): bgp_session['asn'] = asn bgp_sessions = { key: bgp_sessions[key] for key in bgp_sessions if bgp_sessions[key].has_key('asn') and int(bgp_sessions[key]['asn']) != 0 } return bgp_sessions, myasn, bgp_peers_with_range def parse_meta(meta, hname): syslog_servers = [] dhcp_servers = [] ntp_servers = [] tacacs_servers = [] mgmt_routes = [] erspan_dst = [] deployment_id = 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(): properties = device.find(str(QName(ns1, "Properties"))) for device_property in properties.findall(str(QName(ns1, "DeviceProperty"))): name = device_property.find(str(QName(ns1, "Name"))).text value = device_property.find(str(QName(ns1, "Value"))).text value_group = value.strip().split(';') if value and value != "" else [] if name == "DhcpResources": dhcp_servers = value_group elif name == "NtpResources": ntp_servers = value_group elif name == "SyslogResources": syslog_servers = value_group elif name == "TacacsServer": tacacs_servers = value_group elif name == "ForcedMgmtRoutes": mgmt_routes = value_group elif name == "ErspanDestinationIpv4": erspan_dst = value_group elif name == "DeploymentId": deployment_id = value return syslog_servers, dhcp_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id def parse_deviceinfo(meta, hwsku): port_speeds = {} port_descriptions = {} for device_info in meta.findall(str(QName(ns, "DeviceInfo"))): dev_sku = device_info.find(str(QName(ns, "HwSku"))).text if dev_sku == hwsku: interfaces = device_info.find(str(QName(ns, "EthernetInterfaces"))) for interface in interfaces.findall(str(QName(ns1, "EthernetInterface"))): alias = interface.find(str(QName(ns, "InterfaceName"))).text speed = interface.find(str(QName(ns, "Speed"))).text desc = interface.find(str(QName(ns, "Description"))) if desc != None: port_descriptions[port_alias_map.get(alias, alias)] = desc.text port_speeds[port_alias_map.get(alias, alias)] = speed return port_speeds, port_descriptions def parse_xml(filename, platform=None, port_config_file=None): 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 vlans = None vlan_members = None pcs = None mgmt_intf = None lo_intf = None neighbors = None devices = None hostname = None docker_routing_config_mode = "unified" port_speeds_default = {} port_speed_png = {} port_descriptions = {} console_ports = {} syslog_servers = [] dhcp_servers = [] ntp_servers = [] tacacs_servers = [] mgmt_routes = [] erspan_dst = [] bgp_peers_with_range = None deployment_id = None hwsku_qn = QName(ns, "HwSku") hostname_qn = QName(ns, "Hostname") docker_routing_config_mode_qn = QName(ns, "DockerRoutingConfigMode") for child in root: if child.tag == str(hwsku_qn): hwsku = child.text if child.tag == str(hostname_qn): hostname = child.text if child.tag == str(docker_routing_config_mode_qn): docker_routing_config_mode = child.text (ports, alias_map) = get_port_config(hwsku, platform, port_config_file) port_alias_map.update(alias_map) for child in root: if child.tag == str(QName(ns, "DpgDec")): (intfs, lo_intfs, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls) = parse_dpg(child, hostname) elif child.tag == str(QName(ns, "CpgDec")): (bgp_sessions, bgp_asn, bgp_peers_with_range) = parse_cpg(child, hostname) elif child.tag == str(QName(ns, "PngDec")): (neighbors, devices, console_dev, console_port, mgmt_dev, mgmt_port, port_speed_png, console_ports) = parse_png(child, hostname) elif child.tag == str(QName(ns, "UngDec")): (u_neighbors, u_devices, _, _, _, _, _, _) = parse_png(child, hostname) elif child.tag == str(QName(ns, "MetadataDeclaration")): (syslog_servers, dhcp_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id) = parse_meta(child, hostname) elif child.tag == str(QName(ns, "DeviceInfos")): (port_speeds_default, port_descriptions) = parse_deviceinfo(child, hwsku) current_device = [devices[key] for key in devices if key.lower() == hostname.lower()][0] results = {} results['DEVICE_METADATA'] = {'localhost': { 'bgp_asn': bgp_asn, 'deployment_id': deployment_id, 'docker_routing_config_mode': docker_routing_config_mode, 'hostname': hostname, 'hwsku': hwsku, 'type': current_device['type'] }} results['BGP_NEIGHBOR'] = bgp_sessions results['BGP_PEER_RANGE'] = bgp_peers_with_range if mgmt_routes: # TODO: differentiate v4 and v6 mgmt_intf.itervalues().next()['forced_mgmt_routes'] = mgmt_routes results['MGMT_PORT'] = {} results['MGMT_INTERFACE'] = {} mgmt_intf_count = 0 mgmt_alias_reverse_mapping = {} for key in mgmt_intf: alias = key[0] if mgmt_alias_reverse_mapping.has_key(alias): name = mgmt_alias_reverse_mapping[alias] else: name = 'eth' + str(mgmt_intf_count) mgmt_intf_count += 1 mgmt_alias_reverse_mapping[alias] = name results['MGMT_PORT'][name] = {'alias': alias, 'admin_status': 'up'} results['MGMT_INTERFACE'][(name, key[1])] = mgmt_intf[key] results['LOOPBACK_INTERFACE'] = lo_intfs phyport_intfs = {} vlan_intfs = {} pc_intfs = {} vlan_invert_mapping = { v['alias']:k for k,v in vlans.items() if v.has_key('alias') } for intf in intfs: if intf[0][0:4] == 'Vlan': vlan_intfs[intf] = {} elif vlan_invert_mapping.has_key(intf[0]): vlan_intfs[(vlan_invert_mapping[intf[0]], intf[1])] = {} elif intf[0][0:11] == 'PortChannel': pc_intfs[intf] = {} else: phyport_intfs[intf] = {} results['INTERFACE'] = phyport_intfs results['VLAN_INTERFACE'] = vlan_intfs for port_name in port_speeds_default: # ignore port not in port_config.ini if not ports.has_key(port_name): continue ports.setdefault(port_name, {})['speed'] = port_speeds_default[port_name] for port_name in port_speed_png: # not consider port not in port_config.ini if port_name not in ports: print >> sys.stderr, "Warning: ignore interface '%s' as it is not in the port_config.ini" % port_name continue ports.setdefault(port_name, {})['speed'] = port_speed_png[port_name] for port_name, port in ports.items(): if port.get('speed') == '100000': port['fec'] = 'rs' for port_name in port_descriptions: # ignore port not in port_config.ini if not ports.has_key(port_name): continue ports.setdefault(port_name, {})['description'] = port_descriptions[port_name] # set default port MTU as 9100 for port in ports.itervalues(): port['mtu'] = '9100' # set physical port default admin status up for port in phyport_intfs: if port[0] in ports: ports.get(port[0])['admin_status'] = 'up' for member in pc_members.keys() + vlan_members.keys(): port = ports.get(member[1]) if port: port['admin_status'] = 'up' results['PORT'] = ports results['CONSOLE_PORT'] = console_ports if port_config_file: port_set = set(ports.keys()) for (pc_name, mbr_map) in pcs.items(): # remove portchannels that contain ports not existing in port_config.ini # when port_config.ini exists if not set(mbr_map['members']).issubset(port_set): print >> sys.stderr, "Warning: ignore '%s' as part of its member interfaces is not in the port_config.ini" % pc_name del pcs[pc_name] # set default port channel MTU as 9100 and admin status up for pc in pcs.itervalues(): pc['mtu'] = '9100' pc['admin_status'] = 'up' results['PORTCHANNEL'] = pcs results['PORTCHANNEL_MEMBER'] = pc_members for pc_intf in pc_intfs.keys(): # remove portchannels not in PORTCHANNEL dictionary if pc_intf[0] not in pcs: print >> sys.stderr, "Warning: ignore '%s' interface '%s' as '%s' is not in the valid PortChannel list" % (pc_intf[0], pc_intf[1], pc_intf[0]) del pc_intfs[pc_intf] results['PORTCHANNEL_INTERFACE'] = pc_intfs results['VLAN'] = vlans results['VLAN_MEMBER'] = vlan_members for nghbr in neighbors.keys(): # remove port not in port_config.ini if nghbr not in ports: print >> sys.stderr, "Warning: ignore interface '%s' in DEVICE_NEIGHBOR as it is not in the port_config.ini" % nghbr del neighbors[nghbr] results['DEVICE_NEIGHBOR'] = neighbors results['DEVICE_NEIGHBOR_METADATA'] = { key:devices[key] for key in devices if key.lower() != hostname.lower() } results['SYSLOG_SERVER'] = dict((item, {}) for item in syslog_servers) results['DHCP_SERVER'] = dict((item, {}) for item in dhcp_servers) results['NTP_SERVER'] = dict((item, {}) for item in ntp_servers) results['TACPLUS_SERVER'] = dict((item, {'priority': '1', 'tcp_port': '49'}) for item in tacacs_servers) results['ACL_TABLE'] = acls mirror_sessions = {} if erspan_dst: lo_addr = '0.0.0.0' for lo in lo_intfs: lo_network = ipaddress.IPNetwork(lo[1]) if lo_network.version == 4: lo_addr = str(lo_network.ip) break count = 0 for dst in erspan_dst: mirror_sessions['everflow{}'.format(count)] = {"dst_ip": dst, "src_ip": lo_addr} count += 1 results['MIRROR_SESSION'] = mirror_sessions 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['DEVICE_METADATA'] = {'localhost': { 'hostname': hostname, 'hwsku': hwsku, }} results['LOOPBACK_INTERFACE'] = {('lo', lo_prefix): {}} mgmt_intf = {} mgmtipn = ipaddress.IPNetwork(mgmt_prefix) gwaddr = ipaddress.IPAddress(int(mgmtipn.network) + 1) results['MGMT_INTERFACE'] = {('eth0', mgmt_prefix): {'gwaddr': gwaddr}} return results port_alias_map = {} def print_parse_xml(filename): results = parse_xml(filename) print(json.dumps(results, indent=3, cls=minigraph_encoder))