[Python Patterns] Working with the SolarWinds Orion SDK to get VLAN info from a Cisco device.

If you use SolarWinds, this script is for you.

If you have your SolarWinds NCM making nice little backups of your Cisco configs you can use this script as a starter for many projects. This script uses the SolarWinds Orion SDK and a nifty module called CiscoConfParse to pull the data from a backed up config to get a VLAN from an interface.

Once you have a SolarWinds API user setup you'd give the script an IP of your router and it should pull back the VLAN info on an interface if you do that sort of thing.

# get_vlan_info.py from SolarWinds NCM
# 
# Parses the VLAN from interfaces in a Cisco config stored in SolarWinds
#
# You will need a API user setup in SolarWinds for access
#
# How to use
# python get_vlan_info.py -i 10.0.0.1

import argparse
from ciscoconfparse import CiscoConfParse
import json
from netaddr import IPAddress
from orionsdk import SwisClient
import os
import requests
import sys


def main(**kwargs):
    # Get IP from args
    ip_addr = kwargs['ip_addr']

    # Disable SSL check because SW reasons
    requests.packages.urllib3.disable_warnings()

    # Connect to SWIS
    NPM_SERVER = 'solarwinds_instance_ip'
    USERNAME = 'the_api_user'
    PASSWORD = 'super_secret_password'
    swis = SwisClient(NPM_SERVER, USERNAME, PASSWORD)

    # Get NODE.ID from SolarWinds
    # TODO wrap this in a try/catch at some point
    query_result = swis.query(
        "SELECT ip.NodeID, ip.Node.Caption, ip.IPAddress, "
        "ip.SubnetMask, ip.Node.Vendor, ip.Node.MachineType "
        "FROM Orion.NodeIPAddresses ip WHERE ip.Node.caption"
        " IS NOT null and IPAddress = '{}'".format(ip_addr)
    )
    # print(query_result)

    if query_result['results'] != []:
        node_id = query_result['results'][0]['NodeID']
        # print(node_id)
    else:
        print("FAIL, No Device Found!")
        return {}

    # Download node configuration from NCM
    query = '''
        SELECT TOP 1 C.NodeID AS NcmNodeId, C.NodeProperties.CoreNodeId, 
        C.DownloadTime, C.ConfigType, C.Config
        FROM NCM.ConfigArchive C
        WHERE C.NodeProperties.CoreNodeID = @orionnodeid_par
        ORDER BY C.DownloadTime DESC
    '''
    params = {
        'orionnodeid_par': node_id
    }

    # TODO wrap this in a try/catch at some point
    query_results = swis.query(query, **params)
    # print(query_results)
    if query_results['results'] != []:
        last_config = query_results['results'][0]['Config']
        # print(last_config)
    else:
        print("FAIL, No Device Config Found!")
        return {}

    # Write the config out to text as CiscoConfParse expects a real config file
    # or a bunch of lines of config data, weird.
    # TODO wrap this in a try/catch at some point
    with open('cisco.txt', 'w') as f:
        f.write(last_config)
        f.close()

    # Parse the config
    parse = CiscoConfParse('cisco.txt', syntax='ios')
    # print(parse)
    # Clean up after ourselves
    os.remove('cisco.txt')

    vlan = ''
    helper_list = []
    cidr_out = ''
    # Parse out all interfaces
    all_int = parse.find_objects(r"^interface")
    # Iterate over the objects
    for obj in all_int:
        if obj.re_search_children(r"{}\b".format(ip_addr)):
            # This should be our interface with the vlan
            # print(obj.text)
            vlan = obj.text.split('Vlan')[1]
            # print(vlan)
            # OK got vlan, now lets get the children
            for child in obj.children:
                if 'helper-address' in child.text:
                    # print("HELPER {}".format(child.text))
                    helper_list.append(child.text.split(' ')[3])
                elif 'ip address' in child.text:
                    # print("IP ADDRESS INFO: {}".format(child.text))
                    sub_net = child.text.split(' ')[4]
                    gateway = child.text.split(' ')[3]
                    sub_net_bits = IPAddress(sub_net).netmask_bits()
                    cidr_out = "{}/{}".format(gateway, sub_net_bits)

    d = {
        "vlan": vlan,
        "ip_helpers": helper_list,
        "subnet": cidr_out
    }
    print(d)
    return d


if __name__ == '__main__':

    parser = argparse.ArgumentParser()

    parser.add_argument(
        '-i',
        dest='ip_addr',
        help='Enter the ip address',
        required=True
    )

    args = parser.parse_args()

    # Convert the argparse.Namespace to a dictionary: vars(args)
    arg_dict = vars(args)
    # pass dictionary to main
    main(**arg_dict)
    sys.exit(0)


My blog posts tagged with "Python Patterns" are designed to be a quick look reference for some Python code snippets I use a lot.  They are written to be a quick starting point for future projects so I do not need to type as much.