KeyError: 'lanIp' - Meraki

Solved
Vmadathil
Here to help

KeyError: 'lanIp' - Meraki

I need to fetch the DNS details of the devices of all the Networks under a particular Organization. I applied the following script which ended up with an error.

Script:-

import meraki
import pandas as pd
import time

# Replace with your Meraki Dashboard API key
API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Organization ID (can be found in the URL when logged in to your dashboard)
ORGANIZATION_ID = "XXXXXX"

# Network ID (can be found in the network details URL)
NETWORK_ID = "XXXXXXXXX"

# Function to fetch AP information for a given network
def get_ap_info(network_id):
dashboard = meraki.DashboardAPI(API_KEY)
aps = None
while not aps:
try:
aps = dashboard.networks.getNetworkDevices(networkId=network_id)
except meraki.exceptions.APIError as e:
if e.status_code == 429:
print("API rate limit exceeded. Waiting before retrying...")
time.sleep(10) # Wait for 10 seconds before retrying
else:
raise # Re-raise the exception if it's not a rate limit error

ap_data = []
for ap in aps:
ap_info = {
"Name": ap["name"],
"AP ID": ap["serial"],
"Mode": ap["model"],
"Primary DNS": "",
"Secondary DNS": "",
"IP Type": "Static" if ap.get("lanIp") else "Dynamic" # Determine IP type (Static or Dynamic)
}

# Attempt to fetch additional details using device details API (may require additional permissions)
try:
device = dashboard.devices.getDevice(serial=ap["serial"])
ap_info["Primary DNS"] = device["lanIp"] # Assuming LAN IP is the primary DNS
# Fetching secondary DNS (if available) from the DHCP settings
if "dhcpSubnets" in device:
for subnet in device["dhcpSubnets"]:
if "dnsNameservers" in subnet:
ap_info["Secondary DNS"] = subnet["dnsNameservers"][1] if len(subnet["dnsNameservers"]) > 1 else "N/A"
break
except meraki.APIError as e:
print(f"Error fetching device details for {ap['serial']}: {e}")

ap_data.append(ap_info)

return ap_data

# Fetch AP information for the specified network
ap_data = get_ap_info(NETWORK_ID)

# Create pandas DataFrame and save to CSV
df = pd.DataFrame(ap_data)
df.to_csv("ap_information.csv", index=False)

print("AP information retrieved and saved to ap_information.csv")

 

Error:-

2024-04-24 09:35:50       meraki:     INFO > Meraki dashboard API session initialized with these parameters: {'version': '1.45.0', 'api_key': '************************************511c', 'base_url': 'https://api.meraki.com/api/v1', 'single_request_timeout': 60, 'certificate_path': '', 'requests_proxy': '', 'wait_on_rate_limit': True, 'nginx_429_retry_wait_time': 60, 'action_batch_retry_wait_time': 60, 'network_delete_retry_wait_time': 240, 'retry_4xx_error': False, 'retry_4xx_error_wait_time': 60, 'maximum_retries': 2, 'simulate': False, 'be_geo_id': None, 'caller': None, 'use_iterator_for_get_pages': False}
2024-04-24 09:35:50       meraki:     INFO > GET https://api.meraki.com/api/v1/networks/N_XXXXXXXXXXXXX/devices
2024-04-24 09:35:51       meraki:     INFO > networks, getNetworkDevices - 200 OK
Traceback (most recent call last):
  File "C:\Users\10011912\Documents\getdnsinfo 1.py", line 47, in <module>
    ap_data = get_ap_info(NETWORK_ID)
  File "C:\Users\10011912\Documents\getdnsinfo 1.py", line 26, in get_ap_info
    "IP Type": "Static" if ap["lanIp"] else "Dynamic"  # Determine IP type (Static or Dynamic)
KeyError: 'lanIp'

 

please help me to fix the issue.

1 Accepted Solution
Vmadathil
Here to help

I got the script.

import meraki
import pandas as pd
import json

# Replace with your Meraki Dashboard API key
API_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

# Organization ID (can be found in the URL when logged in to your dashboard)
ORGANIZATION_ID = "XXXXXX"

# Function to fetch management interface information for devices in a network
def get_management_interface_info(network_id, org_name, network_name):
dashboard = meraki.DashboardAPI(API_KEY)
devices = dashboard.networks.getNetworkDevices(network_id)

management_info = []
for device in devices:
try:
management_interface = dashboard.devices.getDeviceManagementInterface(serial=device['serial'])
device_info = {
"Organization": org_name,
"Organization ID": ORGANIZATION_ID,
"Network": network_name,
"Network ID": network_id,
"Serial": device['serial'],
"Device Name": device['name'],
"Active DDNS Hostname": management_interface.get('ddnsHostnames', {}).get('activeDdnsHostname', ''),
"DDNS Hostname WAN1": management_interface.get('ddnsHostnames', {}).get('ddnsHostnameWan1', ''),
"DDNS Hostname WAN2": management_interface.get('ddnsHostnames', {}).get('ddnsHostnameWan2', ''),
"WAN1 Enabled": management_interface.get('wan1', {}).get('wanEnabled', ''),
"WAN1 Using Static IP": management_interface.get('wan1', {}).get('usingStaticIp', ''),
"WAN1 Static IP": management_interface.get('wan1', {}).get('staticIp', ''),
"WAN1 Static Subnet Mask": management_interface.get('wan1', {}).get('staticSubnetMask', ''),
"WAN1 Static Gateway IP": management_interface.get('wan1', {}).get('staticGatewayIp', ''),
"WAN1 Static DNS": ', '.join(management_interface.get('wan1', {}).get('staticDns', [])),
"WAN1 VLAN": management_interface.get('wan1', {}).get('vlan', ''),
"WAN2 Enabled": management_interface.get('wan2', {}).get('wanEnabled', ''),
"WAN2 Using Static IP": management_interface.get('wan2', {}).get('usingStaticIp', ''),
"WAN2 Static IP": management_interface.get('wan2', {}).get('staticIp', ''),
"WAN2 Static Subnet Mask": management_interface.get('wan2', {}).get('staticSubnetMask', ''),
"WAN2 Static Gateway IP": management_interface.get('wan2', {}).get('staticGatewayIp', ''),
"WAN2 Static DNS": ', '.join(management_interface.get('wan2', {}).get('staticDns', [])),
"WAN2 VLAN": management_interface.get('wan2', {}).get('vlan', '')
}
management_info.append(device_info)
except meraki.APIError as e:
print(f"Error fetching management interface information for device {device['serial']}: {e}")

return management_info

# Function to fetch management interface information for devices in all networks of an organization
def get_organization_management_interface_info(org_id):
dashboard = meraki.DashboardAPI(API_KEY)
org_name = dashboard.organizations.getOrganization(org_id)['name']
networks = dashboard.organizations.getOrganizationNetworks(org_id)

org_management_info = []
for network in networks:
network_id = network['id']
network_name = network['name']
management_info = get_management_interface_info(network_id, org_name, network_name)
org_management_info.extend(management_info)

return org_management_info

# Fetch management interface information for devices within the organization
org_management_info = get_organization_management_interface_info(ORGANIZATION_ID)

# Convert to DataFrame
df = pd.DataFrame(org_management_info)

# Save to CSV
df.to_csv("organization_management_interface_info.csv", index=False)

print("Management interface information retrieved and saved to organization_management_interface_info.csv.")

 

View solution in original post

5 Replies 5
RaphaelL
Kind of a big deal
Kind of a big deal

Hi, 

3 things.

1- Never post your orgID , networkID or any other sensible info here. 

2 - getNetworkDevices is not efficient. You should use getOrganizationDevices

3- You should print(device) to see if that device contains or not the key lanIp.

Hi, I did necessary changes and tested. Now I am getting information without any errors. But getting the device IP on the DNS server column. and DNS server details is still missing.

Modified Script:-

import meraki
import pandas as pd

# Replace with your Meraki Dashboard API key
API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Organization ID (can be found in the URL when logged in to your dashboard)
ORGANIZATION_ID = "xxxxx"

# Function to fetch AP information for a given organization
def get_ap_info(org_id):
dashboard = meraki.DashboardAPI(API_KEY)
devices = dashboard.organizations.getOrganizationDevices(org_id)

ap_data = []
for device in devices:
# Print device details to inspect the structure and presence of 'lanIp' key
print(device)

ap_info = {
"Name": device["name"],
"AP ID": device["serial"],
"Mode": device["model"],
"Primary DNS": "",
"Secondary DNS": "",
"IP Type": "Dynamic" # Default to Dynamic IP
}

# Check if the device has a wired interface (and thus a LAN IP)
if "lanIp" in device:
ap_info["IP Type"] = "Static"
ap_info["Primary DNS"] = device["lanIp"]

# Attempt to fetch additional details using device details API (may require additional permissions)
try:
# Fetch device details
device_details = dashboard.devices.getDevice(serial=device["serial"])
# Fetching secondary DNS (if available) from the DHCP settings
if "dhcpSubnets" in device_details:
for subnet in device_details["dhcpSubnets"]:
if "dnsNameservers" in subnet:
ap_info["Secondary DNS"] = subnet["dnsNameservers"][1] if len(subnet["dnsNameservers"]) > 1 else "N/A"
break
except meraki.APIError as e:
print(f"Error fetching device details for {device['serial']}: {e}")

ap_data.append(ap_info)

return ap_data

# Fetch AP information for the specified organization
ap_data = get_ap_info(ORGANIZATION_ID)

# Create pandas DataFrame and save to CSV
df = pd.DataFrame(ap_data)
df.to_csv("ap_information.csv", index=False)

print("AP information retrieved and saved to ap_information.csv")

sungod
Head in the Cloud

As @RaphaelL says, check that lanIp is present.

The API is not consistent in how missing/undefined elements are handled in response data. The element may not be there at all, or it may have Null value. Often elements are context dependent.

It's not documented, so you need to code defensively to cope with these situations.

I've not tested this specific call, but I'd guess that if an AP exists but has not yet been assigned an IP (either static or dynamic), perhaps the lanIp is simply left out of the response. I.e. an AP that is in inventory and assigned to a network, but it has not yet been plugged in.

Btw when posting code, it helps if you use the formatting option "Insert/Edit code sample", as without the indentation Python scripts can be ambiguous/invalid/hard to read.

Actually, we have around 10+ organizations, and a significant number of networks under each organization. 90% devices are online. Unfortunately, the script is not capable enough to fetch the DNS details. You can also suggest if there is any working script to get the DNS details of the devices including network and organization details.

Vmadathil
Here to help

I got the script.

import meraki
import pandas as pd
import json

# Replace with your Meraki Dashboard API key
API_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

# Organization ID (can be found in the URL when logged in to your dashboard)
ORGANIZATION_ID = "XXXXXX"

# Function to fetch management interface information for devices in a network
def get_management_interface_info(network_id, org_name, network_name):
dashboard = meraki.DashboardAPI(API_KEY)
devices = dashboard.networks.getNetworkDevices(network_id)

management_info = []
for device in devices:
try:
management_interface = dashboard.devices.getDeviceManagementInterface(serial=device['serial'])
device_info = {
"Organization": org_name,
"Organization ID": ORGANIZATION_ID,
"Network": network_name,
"Network ID": network_id,
"Serial": device['serial'],
"Device Name": device['name'],
"Active DDNS Hostname": management_interface.get('ddnsHostnames', {}).get('activeDdnsHostname', ''),
"DDNS Hostname WAN1": management_interface.get('ddnsHostnames', {}).get('ddnsHostnameWan1', ''),
"DDNS Hostname WAN2": management_interface.get('ddnsHostnames', {}).get('ddnsHostnameWan2', ''),
"WAN1 Enabled": management_interface.get('wan1', {}).get('wanEnabled', ''),
"WAN1 Using Static IP": management_interface.get('wan1', {}).get('usingStaticIp', ''),
"WAN1 Static IP": management_interface.get('wan1', {}).get('staticIp', ''),
"WAN1 Static Subnet Mask": management_interface.get('wan1', {}).get('staticSubnetMask', ''),
"WAN1 Static Gateway IP": management_interface.get('wan1', {}).get('staticGatewayIp', ''),
"WAN1 Static DNS": ', '.join(management_interface.get('wan1', {}).get('staticDns', [])),
"WAN1 VLAN": management_interface.get('wan1', {}).get('vlan', ''),
"WAN2 Enabled": management_interface.get('wan2', {}).get('wanEnabled', ''),
"WAN2 Using Static IP": management_interface.get('wan2', {}).get('usingStaticIp', ''),
"WAN2 Static IP": management_interface.get('wan2', {}).get('staticIp', ''),
"WAN2 Static Subnet Mask": management_interface.get('wan2', {}).get('staticSubnetMask', ''),
"WAN2 Static Gateway IP": management_interface.get('wan2', {}).get('staticGatewayIp', ''),
"WAN2 Static DNS": ', '.join(management_interface.get('wan2', {}).get('staticDns', [])),
"WAN2 VLAN": management_interface.get('wan2', {}).get('vlan', '')
}
management_info.append(device_info)
except meraki.APIError as e:
print(f"Error fetching management interface information for device {device['serial']}: {e}")

return management_info

# Function to fetch management interface information for devices in all networks of an organization
def get_organization_management_interface_info(org_id):
dashboard = meraki.DashboardAPI(API_KEY)
org_name = dashboard.organizations.getOrganization(org_id)['name']
networks = dashboard.organizations.getOrganizationNetworks(org_id)

org_management_info = []
for network in networks:
network_id = network['id']
network_name = network['name']
management_info = get_management_interface_info(network_id, org_name, network_name)
org_management_info.extend(management_info)

return org_management_info

# Fetch management interface information for devices within the organization
org_management_info = get_organization_management_interface_info(ORGANIZATION_ID)

# Convert to DataFrame
df = pd.DataFrame(org_management_info)

# Save to CSV
df.to_csv("organization_management_interface_info.csv", index=False)

print("Management interface information retrieved and saved to organization_management_interface_info.csv.")

 

Get notified when there are additional replies to this discussion.