Get Request for Multiple Sites

Solved
PeteWhit
Comes here often

Get Request for Multiple Sites

Hi team, hoping someone can help me out here.

 

I have a customer that wants a report of all their Fixed IP Assignments in each MX across their fleet.

 

There are over 100 MXs, all with fixed IPs in the DHCP scope.

 

When using postman, I can get the info from an individual network and Vlan using the getNetworkApplianceVlan command.

 

 

However, this isn't really scalable as you can imagine.

 

I'm hoping to be able to pull the fixed IP assignement setting for all 100 MXs inside the same org.  Any idea's how I can expan the getNetworkApplianceVlan command to scrape all networks and vlans, not just one? 

1 Accepted Solution
JasonM
Meraki Employee
Meraki Employee

There is an endpoint, Network Appliance Vlans, that covers all VLANs for an MX network. Additionally, there is a "Looper" feature that will allow you to select multiple networks, all networks, or even multiple organizations.

 

Screenshot 2024-08-29 at 10.19.42 AM.png

 

Hope this helps!

View solution in original post

9 Replies 9
PhilipDAth
Kind of a big deal
Kind of a big deal

To clarify, you want to know all the DHCP host reservations?

PeteWhit
Comes here often

Yeah that's it. Need to pull the DHCP reservations (Fixed IP address assignment as Meraki call it) across all the networks in the org.

 

Customer has 50+ sites, and wants a report for all sites. 

 

I can do it for 1 site no dramas, but pulling it for the whole org is proving a bit more difficult. Keen to hear how it can be done. 

 

I suppose the concept is the same for any setting across multiple Networks. 

sungod
Kind of a big deal

This is where you are probably better off using Python/whatever to create a script to do it. ..

 

get list of networks in org

for each network

    get mx data

 

You'll probably get other requests over time that would require this sort of approach to perform efficiently, may as well start now 😀

JasonM
Meraki Employee
Meraki Employee

A personal favorite of mine for a quick no-code solution is the Meraki Tools for Google Sheets integration. You can use the Looper feature in it to select all networks to get a nice report pretty easily.

PeteWhit
Comes here often

Thanks for that, the Google sheet is a great tool.  It's so close to giving me what I need.  However, It still only does 1 Network and 1 Vlan at a time when requesting Vlan info. So I'd have to click into each network and Vlan to extra the data.

 

Meraki Reports > Group = Appliance > Reports = Network Appliance Vlan.  But I have to select a Network and a Vlan.  It gives me the data I want, but still on an individual level.

 

Ideally it needs to be all MXs and all Vlans.... give me the output. Might have to finally learn some python. 

JasonM
Meraki Employee
Meraki Employee

There is an endpoint, Network Appliance Vlans, that covers all VLANs for an MX network. Additionally, there is a "Looper" feature that will allow you to select multiple networks, all networks, or even multiple organizations.

 

Screenshot 2024-08-29 at 10.19.42 AM.png

 

Hope this helps!

PeteWhit
Comes here often

Thanks @JasonM - that's what I was after.  It spits out the raw data, but it was good enough to get me the info I needed.  Thanks for the assist.  Works well for those of us who aren't great with scripting.  

Craieggg
Here to help

Hi @PeteWhit,

A script would definitely be the way to go with Meraki API. You can use the following logic as SunGod suggested -


1. Get a list of networks - Get Organization Networks - Meraki Dashboard API v1 - Cisco Meraki Developer Hub
2. FOR Loop through the Networks

3. Use the NetworkID - and run Get Network Appliance Vlans - Meraki Dashboard API v1 - Cisco Meraki Developer Hub for every network

4. Look for "fixedIpAssignments" and that should be the data you are looking for.

You can use the Code Snippet section in each relevant API call to understand the code equivalent which simplifies the job a lot!

amabt
Building a reputation

Below is what I came up with a while ago. It will output an excel file with all the DHCP reservations for all networks for all orgs that the API key can access.

Note API Key is retrieved from an Environment variable.

 

import csv
from datetime import datetime
import os
import time
import meraki
import pandas as pd
import json
import xlsxwriter
import openpyxl
from collections import OrderedDict
import time

# This script get a list of all VLANs for All Meraki Networks in All Meraki Orgs the API Key has access to
#This also grab single subnet networks and skip any networks that has a passthrough appliances as they do not have a subnet.

#It then get all the DHCP reservations from each VLANs.

# Meraki API Key name. Allows to switch between different keys
MERAKI_DASHBOARD_API_KEY_NAME = 'MERAKI_API_KEY GOES HERE'  # Readonly key

#Set CSV / XLXS folder path
output_path = f"{os.path.dirname(__file__)}/output"

def main():
    if MERAKI_DASHBOARD_API_KEY_NAME in os.environ:
        print(f"API Key Environment variable [{MERAKI_DASHBOARD_API_KEY_NAME}] found.")

    else:
        print(f"API Key Environment variable [{MERAKI_DASHBOARD_API_KEY_NAME}] Does not exists!")
        exit(1)

    MERAKI_DASHBOARD_API_KEY = os.getenv(MERAKI_DASHBOARD_API_KEY_NAME)  # Get the actual Meraki API Key

    # Start a Meraki dashboard API session
    try:
        print(f"\nTrying to connect to Meraki API Using [{MERAKI_DASHBOARD_API_KEY_NAME}]")

        dashboard = meraki.DashboardAPI(
            api_key=MERAKI_DASHBOARD_API_KEY,
            base_url='https://api.meraki.com/api/v1/',
            print_console=False, output_log=False, suppress_logging=True,
            wait_on_rate_limit=True,
            maximum_retries=100
        )

    except meraki.APIError as e:
        print("Unable to connect to Meraki API.")
        print(f'Meraki API error: {e}')
        print(f'status code = {e.status}')
        print(f'reason = {e.reason}')
        print(f'error = {e.message}')

    else:

        # Get list of organizations to which the supplied API key has access to.
        organizations = dashboard.organizations.getOrganizations()
        organizations = sorted(organizations, key=lambda x: x['name'])  # Sort Org by name

        # Print name of the Orgs found
        print(f"Found: [{len(organizations)}] organisations.")
        for org in organizations:
            print(f"{org['name']}")

        all_orgs_networks_vlans = list() #Store a list of all the org vlans
        all_orgs_networks_vlans_dhcp_reservations = list()  # Store a list of all the org vlans reservations

        # Iterate through list of Orgs and gather clients details of all VLAN for each networks where there is an MX
        for org in organizations:

            all_org_networks = []  # Store all the networks in all orgs so we can write to a file later.

            print(f"\nAnalysing organization {org['name']}:")
            org_id = org['id']  # Save ID name for use later
            org_name = org['name']  # Save Org name for use later

            # Get list of networks in current organization
            try:
                org_networks = dashboard.organizations.getOrganizationNetworks(org_id)

            except meraki.APIError as e:

                print(f'Meraki API error: {e}')
                print(f'status code = {e.status}')
                print(f'reason = {e.reason}')
                print(f'error = {e.message}')
                continue

            except Exception as e:
                print(f'some other error: {e}')
                continue  # Skip error in current Mersaki network

            # Sort List of network by name
            org_networks = sorted(org_networks, key=lambda x: x['name'])

            # Iterate through networks in current organisation
            total = len(org_networks)  # count number of Meraki networks in this org
            counter = 1  # Keep count of number of network as we iterate through them.

            print(f'Iterating through {total} networks in organization in {org["name"]} to get list of VLAN information')

            # Create a new list networks
            current_org_networks_info = []

            # Loop through the list of networks and retrieve the VLAN info
            for network in org_networks:

                print(f'Processing [{org["name"]}] Network: {network["name"]}')

                #We can check if a network has vlans by using below endpoint
                # https://developer.cisco.com/meraki/api/get-network-appliance-vlans-settings/
                #
                #However to reduce the number of API calls. This will be skipped

                # Create a new dictionary to store the network info
                network_info = {}

                # Add the required fields to the new dictionary
                network_info["id"] = network["id"]
                network_info["organizationId"] = network["organizationId"]
                network_info["name"] = network["name"]
                network_info["productTypes"] = network["productTypes"]
                network_info["timeZone"] = network["timeZone"]
                network_info["tags"] = network["tags"]
                network_info["isBoundToConfigTemplate"] = network["isBoundToConfigTemplate"]
                network_info["url"] = network["url"]

                network_vlans = list() #Use to hold all the vlan or empty if no vlans

                vlan_details = {} #Store the vlan details for each vlan


                #If product type has "appliance" then it will have a vlan subnet for us to grab
                if 'appliance' in network['productTypes']:

                    #Get VLAN Info
                    try:
                        vlans = dashboard.appliance.getNetworkApplianceVlans(network["id"])

                        #vlans now has the VLAN details including any DHCP reservations
                        #Now loop through all the VLAn and get the DHCP Reservations clients MAC & IP
                        for vlan in vlans:

                            fixedIpAssignments = vlan['fixedIpAssignments']

                            for key in fixedIpAssignments:
                                # vlan_details['organizationId'] = network['organizationId']
                                # vlan_details['net_name'] = network['name']
                                # vlan_details["vlan_name"] = vlan['name']
                                # vlan_details['dnsNameservers'] = vlan['dnsNameservers']

                                # vlan_details['reservation_name'] = fixedIpAssignments[f"{key}"]['name']
                                # vlan_details['reservation_mac'] = f"{key}"
                                vlan_details['reservation_ip'] = fixedIpAssignments[f"{key}"]['ip']

                                all_orgs_networks_vlans_dhcp_reservations.append(
                                                                                 {'organizationId': network['organizationId'],
                                                                                  'net_name': network['name'],
                                                                                  'vlan_name': vlan['name'],
                                                                                 'dnsNameservers': vlan['dnsNameservers'],
                                                                                  'reservation_name': fixedIpAssignments[f"{key}"]['name'],
                                                                                  'reservation_mac': f"{key}",
                                                                                  'reservation_ip': fixedIpAssignments[f"{key}"]['ip']
                                                                                  }
                                                                                 )

                    except meraki.APIError as e:

                        if e.status == 400: #Vlan not enable for this network as only a single subnet

                            #Get the single VLAN subnet and assume VLAN ID of '1'

                            #print(f'Error Network: {network["name"]}: {e.message}')

                            #Check and skip any network & appliances in passthrough mode.
                            #They won't have any subnet to retrieve

                            # if 'ACM-VNET-MEL-APAS-ACM-01' in network['name']:
                            #     print('Pass through mode network')

                            #Get the single VLAN
                            try:
                                single_vlan_info = dashboard.appliance.getNetworkApplianceSingleLan(network["id"])

                                fixedIpAssignments = single_vlan_info['fixedIpAssignments']

                                for key in fixedIpAssignments:

                                    vlan_details['reservation_ip'] = fixedIpAssignments[f"{key}"]['ip']

                                    all_orgs_networks_vlans_dhcp_reservations.append(
                                        {'organizationId': network['organizationId'],
                                         'net_name': network['name'],
                                         'vlan_name': 'Default',
                                         'dnsNameservers': single_vlan_info['dnsCustomNameservers'],
                                         'reservation_name': fixedIpAssignments[f"{key}"]['name'],
                                         'reservation_mac': f"{key}",
                                         'reservation_ip': fixedIpAssignments[f"{key}"]['ip']
                                         }
                                    )
                                    # print(f"Done")


                            except meraki.APIError as e2:

                                if e2.status == 400:
                                    print(f"Network in Passthrough mode or some other error. Skipping!")
                                    #'Single LAN is not available for networks in passthrough mode'
                                    continue
                                else:
                                    print(f'Some other error for network without VLANs.')
                                    continue

                            #If we are here then there is a single subnet to grab
                            vlan_subnet = {1, single_vlan_info['subnet']}
                            print(f"VLAN [1]: - Subnet [{single_vlan_info['subnet']}]")
                            network_vlans.append(vlan_subnet)

                            # Also store the VLAN in the All Org VLAN list for later usage
                            all_orgs_networks_vlans.append({'net_name': network["name"], 'net_id': network["id"],
                                                            'vlan_id': 1, 'vlan_subnet': single_vlan_info['subnet']})
                            continue

                        elif e.status == 429:
                            #We have hit the rate limit. Back off by sleeping for a bit
                            print(f'Error Network: {network["name"]}: {e.message}. Sleeping for 5 seconds.')
                            #time.sleep(int(response.headers["Retry-After"]))
                            time.sleep(5)  # Sleep for 5 seconds
                            continue

                        else:
                            print(f'Error Network: {network["name"]}')
                            print(f'Meraki API error: {e}')
                            print(f'status code = {e.status}')
                            print(f'reason = {e.reason}')
                            print(f'error = {e.message}')
                            continue

                    #If we are here then there are some vlan subnets to process
                    # Loop through the appliance vlan info and get all VLANs subnets
                    for vlan in vlans:
                        # print(f"VLAN ID: [{$vlan['id']}] Subnet: {$vlan['subnet']} ")
                        vlan_subnet = {vlan['id'], vlan['subnet']}
                        print(f"VLAN [{vlan['id']}]: - Subnet [{vlan['subnet']}]")
                        network_vlans.append(vlan_subnet)

                        #Also store the VLAN in the All Org VLAN list for later usage
                        all_orgs_networks_vlans.append({'net_name': network["name"], 'net_id': network["id"],
                                              'vlan_id': vlan['id'], 'vlan_subnet': vlan['subnet']})

                #store the network vlans (or empty if not an appliance network
                network_info['vlans'] = network_vlans

                # Add the new dictionary to the list
                current_org_networks_info.append(network_info)

            print(f"Network info gathering for [{org['name'] }] completed.")

            #Combine the curen org network info into the all_org dict for later use
            all_org_networks = current_org_networks_info + all_org_networks

        #Sort the networks by name
        #all_orgs_networks_vlans
        df_all_orgs_networks_vlans = pd.DataFrame(all_orgs_networks_vlans)
        df_all_orgs_networks_vlans_dhcp_reservations = pd.DataFrame(all_orgs_networks_vlans_dhcp_reservations)


        #df_all_org_networks_info = pd.DataFrame(all_org_networks)
        #
        # write the DataFrame to a CSV file
        current_datetime = datetime.now().strftime("%Y-%m-%d %H-%M-%S")
        filename = f"{output_path}/all-orgs_networks-vlan-info--{current_datetime}.xlsx"

        #df_all_org_networks_info.to_excel(filename)
        df_all_orgs_networks_vlans.to_excel(filename)
        print(f"Exported[VLANS to file] filename: {filename}")


        #Write DHCP reservation to file
        current_datetime = datetime.now().strftime("%Y-%m-%d %H-%M-%S")
        filename = f"{output_path}/all-orgs_networks-dhcp-reservation-info--{current_datetime}.xlsx"

        #df_all_org_networks_info.to_excel(filename)
        df_all_orgs_networks_vlans_dhcp_reservations.to_excel(filename)
        print(f"Exported [DHCP reservation to file] filename: {filename}")

if __name__ == "__main__":
    # Call Main to do all the actual work.
    start_time = datetime.now()
    main()
    end_time = datetime.now()
    print(f'\nScript complete, total runtime {end_time - start_time}')

 

 

 

Get notified when there are additional replies to this discussion.