Exporting device inventory from Prime to NetBox part 2: Preparing input data for NetBox


I finished part 1 with ready .yaml files. We need them in case the input data in Prime is not enough and we need to ssh into our devices to get supplementary data.

For our scenario I need the following data fields in Netbox:

Device function, manufacturer, device full name including the stack member number, mgmt ip address, device model, device ios type, device serial number, device short name (=virtual chassis name), virtual chassis position, virtual chassis priority, device status and site name.
To give an example of a two-device stack, I imagine the final output to be the following:

User_Access;Cisco;s01wro001-1;10.101.1.11;WS-C2960X-48FPS-L;IOS;FCQ1942A4UR;s01wro001;1;15;active;PL-WRO-1


User_Access;Cisco;s01wro001-2;10.101.1.11;WS-C2960X-48FPS-L;IOS;FCQ1942A4UR;s01wro001;2;14;active;PL-WRO-1

First, this is a small function that I will use to query Prime for a set of devices that contain the abbreviation ‚WRO’ in their names.

def query_prime(keyword):
   
      url="https://prime.yourcompany.com/webacs/api/v4/data/Devices?.full=true&.sort=ipAddress&deviceName=contains(%22"+keyword+"%22)"
response=requests.get(url,auth=primeCreds,verify=False)
obj=xmltodict.parse(response.text)
internal_obj=obj['queryResponse']
return internal_obj

This is the main function. It should use the response from Prime to save the data.

def get_virtual_chassis_data_for_topology_devices(internal_obj):
    #case - some search results returned
    if 'entity' in internal_obj.keys():
          entity=internal_obj['entity']

          #case - query returned more than 1 device
          if isinstance(entity,list):
             for item in entity:
  
                   device=item['devicesDTO']
                   write_row(device)

          #case - query returned exactly 1 device
          else:
                  device=entity['devicesDTO']
                  write_row(device)

    #case - no devices with this keyword have been found
    else:
       print('no search results returned')


Now I need to write up the function write_row from the code snipper above to have a nice .csv file with each device as a separate row with input data for Netbox.

Part 1

This is just the beginning of the write_row function

def write_row(device):
       
       f = open(filename, 'a')
       writer = csv.writer(f)
       device_name, device_name_short, device_type, device_ip, device_location, manuf_nrs_dict, manuf_list, software_type = get_device_properties(device)

Now the get_device_properties function is a small helper function to get the data fields that are already available in Prime

def get_device_properties(device):
   
    device_name = device['deviceName']
    device_name_short=device_name.replace('.schaeffler.com', '')
    device_type=device['deviceType']
    device_ip=device['ipAddress']
    if 'location' in device.keys():
         device_location=device['location']
    else:
         device_location='default location'
    manuf_nrs_dict=device['manufacturerPartNrs']
    manuf_list=manuf_nrs_dict['manufacturerPartNr']
    software_type=device['softwareType']
    return device_name, device_name_short, device_type, device_ip, device_location, manuf_nrs_dict, manuf_list, software_type

Now that we have some input data from Prime, there is also some data that Prime is not aware of, for example virtual chassis priorities etc. We need to ssh to our devices and get more data. To do that, we will divide our devices into three groups and use conditions for each group:

  • stackable switches,
  • vss-capable switches,
  • other devices

This is the code for the first group (stackable switches), which includes two subcases: stackable switches which form a stack, and individual non-stacked switches. The code does the following: if the device belongs to the first group, and if it it is a stack (isinstance List), use the .yaml inventory file to connect to the device, get ‚show switch’ data output, parse it, do an additional prime query to find out what the device function is (udf_url), produce the row with input data, save the row. If it is not a stack (manuf_list is not a list of items), there is no need to ssh into the device because there is no stack data to be had, so make a row with zeroes for virtual chassis data.

def write_row(device):
       
       f = open(filename, 'a')
       writer = csv.writer(f)
       device_name, device_name_short, device_type, device_ip, device_location, manuf_nrs_dict, manuf_list, software_type = get_device_properties(device)
       fixed_device_location = device_location.split(',')[0].split(' ')[0]
       if device['deviceType']=='Cisco Catalyst 29xx Stack-able Ethernet Switch' or device['deviceType']=='Cisco Catalyst38xx stack-able ethernet switch' or device['deviceType']=='Cisco Catalyst 9300 Switch':
          if isinstance(manuf_list,list):
    
             dev=load_yaml().devices[device_name_short]
             dev.connect()
             switch_vc_data=dev.parse('show switch')
  
             for list_item_dict in manuf_list:
           
                 switch_number, serial, model, vc_position, vc_priority = get_stack_data_from_stack(switch_vc_data, list_item_dict)
                 udf_url="https://prime.yourcompamy.com/webacs/api/v2/op/devices/exportDevices?ipAddress=%22" + device_ip + "%22"
                 udf_response=requests.get(udf_url,auth=primeCreds,verify=False)
                 udf_obj=xmltodict.parse(udf_response.text)
                 udf_value = udf_obj['mgmtResponse']['devicesExportResult']['devices']['device']['udfs']['udf']['value']

                 row = [udf_value + ';Cisco;' + device_name_short + '-' + switch_number + ';' + device_ip + ';' + model + ';' + software_type + ';' + serial + ';' + device_name_short + ';' + vc_position + ';' + vc_priority + ';' + 'active;' + fixed_device_location]
                 writer.writerow(row)
          else:
              switch_number=manuf_list['name']
              serial=manuf_list['serialNumber']
              model=manuf_list['partNumber']
              udf_url="https://prime.yourcompany.com/webacs/api/v2/op/devices/exportDevices?ipAddress=%22" + device_ip + "%22"
              response=requests.get(udf_url,auth=primeCreds,verify=False)
              udf_obj=xmltodict.parse(response.text)
              udf_value = udf_obj['mgmtResponse']['devicesExportResult']['devices']['device']['udfs']['udf']['value']
              row = [udf_value + ';Cisco;' + device_name_short + '-' + switch_number[-1] + ';' + device_ip + ';' + model + ';' + software_type + ';' + serial + ';' + device_name_short + ';' + '0; ' + '0;' + 'active;' + fixed_device_location]
              writer.writerow(row)

The helper function get_stack_data_from_stack is as follows:

def get_stack_data_from_stack(switch_vc_data, list_item_dict):
       
        pre_switch_number = list_item_dict['name']
        switch_number=pre_switch_number[-1]
        
        serial=list_item_dict['serialNumber']
        model=list_item_dict['partNumber']
        vc_position=switch_number
        vc_priority=switch_vc_data['switch']['stack'][str(switch_number)]['priority']
        return switch_number, serial, model, vc_position, vc_priority

Now the code for the second group – the vss capable devices:

   elif device['deviceType']=='Cisco Catalyst 4500 Virtual Switching System' or device['deviceType']=='Cisco Catalyst 6500 Virtual Switching System' or device['deviceType']=='Cisco Catalyst 9500 SVL Switch':
           if isinstance(manuf_list, list):
              vss_data = get_vss_properties(device_name_short)
              switch_number = 1
              for item in manuf_list:
                  role = vss_data[switch_number]['role']
                  model = vss_data[switch_number]['model']
                  serial = vss_data[switch_number]['serial']
                  vss_position = str(vss_data[switch_number]['switch number'])
                  vss_priority = str(vss_data[switch_number]['priority'])
                  udf_url="https://prime.yourcompany.com/webacs/api/v2/op/devices/exportDevices?ipAddress=%22" + device_ip + "%22"
                  udf_response=requests.get(udf_url,auth=primeCreds,verify=False)
                  udf_obj=xmltodict.parse(udf_response.text)
                  udf_value = udf_obj['mgmtResponse']['devicesExportResult']['devices']['device']['udfs']['udf']['value']
                  row = [udf_value + ';Cisco;' + device_name_short + '-' + str(switch_number) + ';' + device_ip + ';' + model + ';' + software_type + ';' + serial + ';' + device_name_short + ';' + vss_position + ';' + vss_priority + ';' + 'active;' + fixed_device_location]
                  writer.writerow(row)
                  switch_number +=1

…with the helper function get_vss_properties:

def get_vss_properties(device_name_short):
    '''
    helper function to get data from vss devices
    '''
    dev=load_yaml().devices[device_name_short]
    dev.connect()
    obj1 = dev.execute('show switch virtual role')
    role_regex = re.compile(r'^(?P<ROLE>(LOCAL)|(REMOTE))\s+ (?P<SWNO>\d)\s+(UP|DOWN)\s+\w+[(]\w\s[)]\s+(?P<PRIORITY>\d+)[(]\d+[)]\s+(?P<STATUS>\w+)\s+\d+\s+\d+')
    virtual_role_dict = {}
    inventory_index = 1
    for line in obj1.splitlines():
        line = line.strip()
        result = role_regex.match(line)
        if result:
            sub_dict = virtual_role_dict.setdefault(inventory_index,{})
            group = result.groupdict()
            sub_dict['role'] = group['ROLE']
            sub_dict['switch number'] = group['SWNO']
            sub_dict['priority'] = group['PRIORITY']
            inventory_index = inventory_index + 1
            continue
        virtual_role_dict.pop(3, None)
        virtual_role_dict.pop(4, None)
    obj = dev.execute('show module')
    p1 = re.compile(r'^(Switch Number:)\s(?P<NUMBER>\d)\s(Role:)\s(Virtual Switch)\s(?P<ROLE>(Active|Standby))')
    p2 = re.compile(r'^(?P<SWNUMBER>\d)\s+\d+\s+.*(?P<MODEL>(WS-C\d{4}X-\d+))\s+(?P<SERIAL>\w{3}\d{4}.{4})')
    parsed_dict = {}
    inventory_index = 1
    for line in obj.splitlines():
            line = line.strip()
            result = p1.match(line)
            result2 = p2.match(line)
            if result:
                inventory_dict = virtual_role_dict.setdefault(inventory_index,{})
                group = result.groupdict()
                inventory_dict['switch number'] = group['NUMBER']
                inventory_dict['role'] = group['ROLE']
                continue
            if result2:
                group = result2.groupdict()
                inventory_dict['model'] = group['MODEL']
                inventory_dict['serial'] = group ['SERIAL']
                inventory_index +=1
                continue
    return virtual_role_dict

Finally, the third group (non-stackable, non-vss devices):

  else:
           switch_number=manuf_list['name']
           serial=manuf_list['serialNumber']
           model=manuf_list['partNumber']
           udf_url="https://prime.yourcompany.com/webacs/api/v2/op/devices/exportDevices?ipAddress=%22" + device_ip + "%22"
           response=requests.get(udf_url,auth=primeCreds,verify=False)
           udf_obj=xmltodict.parse(response.text)
           udf_value = udf_obj['mgmtResponse']['devicesExportResult']['devices']['device']['udfs']['udf']['value']
           row = [udf_value + ';Cisco;' + device_name_short + '-' + switch_number[-1] + ';' + device_ip + ';' + model + ';' + software_type + ';' + serial + ';' + device_name_short + ';' + '0; ' + '0;' + 'active;' + fixed_device_location]
           writer.writerow(row)

Now I need a main function to put it all together:

get_virtual_chassis_data_for_topology_devices(query_prime('wro'))

I run it and the output is as follows (serial numbers have been modified post-run because i don’t want to show my real serial numbers)

User_Access;Cisco;c01wro005-1;10.101.1.105;WS-C2960X-48FPS-L;IOS;PCW1942A4UW;c01wro005;1;15;active;PL-Wro-1
User_Access;Cisco;c01wro005-2;10.101.1.105;WS-C2960X-48FPD-L;IOS;POD1917T1PN;c01wro005;2;14;active;PL-Wro-1
User_Access;Cisco;c01wro006-1;10.101.1.113;WS-C2960X-48FPD-L;IOS;POD1917T1Q5;c01wro006;0; 0;active;wroisio
User_Access;Cisco;c01wro007-1;10.101.1.121;WS-C2960X-48FPD-L;IOS;POD1917T1QM;c01wro007;0; 0;active;PL-Wro-1
User_Access;Cisco;c01wro008-1;10.101.1.129;WS-C2960X-48FPD-L;IOS;POD1917T1PR;c01wro008;0; 0;active;PL-Wro-1
Foreign_Controller;Cisco;b01wro001-s;10.101.1.137;AIR-CT5508-K9;Cisco Controller;PCW1919B3BB;b01wro001;0; 0;active;PL-Wro-1
Production_Router;Cisco;r01wro003-1;10.101.1.252;WS-C3850-24P-E;IOS-XE;PCW1912D14R;r01wro003;0; 0;active;PL-Wro-1
Production_Router;Cisco;r01wro002-1;10.101.1.253;WS-C3850-24P-E;IOS-XE;POD1912X14J;r01wro002;0; 0;active;PL-Wro-1
Core;Cisco;r01wro001-vss-1;10.101.1.254;WS-C4500X-16;IOS-XE;JAF191812PW;r01wro001-vss;1;120;active;default
Core;Cisco;r01wro001-vss-2;10.101.1.254;WS-C4500X-16;IOS-XE;JAF191812MJ;r01wro001-vss;2;110;active;default
Server_Access;Cisco;c01wro101-1;10.101.1.65;WS-C3850-24T-L;IOS-XE;POD1913R0AL;c01wro101;1;15;active;PL-Wro-1
Server_Access;Cisco;c01wro101-2;10.101.1.65;WS-C3850-24T-L;IOS-XE;POD1913X0AP;c01wro101;2;14;active;PL-Wro-1
User_Access;Cisco;c01wro001-1;10.101.1.73;WS-C2960X-48FPD-L;IOS;PCW1917B2AC;c01wro001;1;15;active;PL-Wro-1
User_Access;Cisco;c01wro001-2;10.101.1.73;WS-C2960X-48FPD-L;IOS;POD1917S1QK;c01wro001;2;14;active;PL-Wro-1
User_Access;Cisco;c01wro001-3;10.101.1.73;WS-C2960X-48FPD-L;IOS;POD1917S1H3;c01wro001;3;1;active;PL-Wro-1
User_Access;Cisco;c01wro002-1;10.101.1.81;WS-C2960X-48FPD-L;IOS;PCW1917B2A7;c01wro002;0; 0;active;PL-Wro-1
User_Access;Cisco;c01wro003-1;10.101.1.89;WS-C2960X-48FPD-L;IOS;POD1917S1R0;c01wro003;1;15;active;PL-Wro-1
User_Access;Cisco;c01wro003-2;10.101.1.89;WS-C2960X-48FPD-L;IOS;POD1917S1Q4;c01wro003;2;14;active;PL-Wro-1
User_Access;Cisco;c01wro004-1;10.101.1.97;WS-C2960X-48FPD-L;IOS;POD1917S1HA;c01wro004;1;15;active;PL-Wro-1
User_Access;Cisco;c01wro004-2;10.101.1.97;WS-C2960X-48FPD-L;IOS;POD1917S1RE;c01wro004;2;14;active;PL-Wro-1

Now I can start writing the code to export this to NetBox.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Google

Komentujesz korzystając z konta Google. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s

%d blogerów lubi to: