12-06-2022 05:26 AM
Hello,
I am trying to learn APIs for the first time whilst also trying to get a specific job done and need a little guidance.
We have some WiFi monitoring and reporting software that can see all the BSSID's flying around, but at the moment it cant give us any clear info about them. In order to do that I need to feed it an xml that will tell it which BSSIDs belong to which AP's (and the associated SSID and the band).
We have hundreds of AP's so going through the meraki portal and copying this data manually is painful - I'm hoping I can pull this data with a GET request?
So far I have setup postman with a new environment and set a API key and baseURL variables.
At this point I wanted to turn to some pre-made examples from here but its v0 not v1
https://documenter.getpostman.com/view/7928889/SVmsVg6K#9019f8d0-a1db-4eb1-b45d-c348dc800e32
will these examples be unusable in v1? If so is there a v1 set of basic API's like this somewhere? In any case I coudlnt find something that provided the data fields I'm after 😞
Also - we have lots of different networks and Id need this info for each network - does that complicate things? IE can you do a API call on all networks or is it per network etc?
Thanks!
Solved! Go to Solution.
12-07-2022 03:29 AM
I think I might be chasing down the wrong route here with the CSV. I think I should clarify my end-goal but not sure if it would be better as new post or keep going here do you think?
12-07-2022 03:31 AM
what is your end goal ?
12-07-2022 03:46 AM
takes a breath...
ok, so I need a CSV file that contains data for each and every access point in our organisation.
For each AP I need a list of that AP's name and every enabled BSSID with the BSSIDs band and SSID.
I can see I can get the BSSID/band/SSID info from /devices/serialnumber/wireless/status
{
"basicServiceSets": [
{
"ssidName": "Unconfigured SSID 1",
"ssidNumber": 0,
"enabled": false,
"band": "2.4 GHz",
"bssid": "00:00:00:00:00:00",
"channel": 6,
"channelWidth": "20 MHz",
"power": "16 dBm",
"visible": true,
"broadcasting": false
},
{
"ssidName": "Unconfigured SSID 1",
"ssidNumber": 0,
"enabled": false,
"band": "5 GHz",
"bssid": "00:00:00:00:00:00",
"channel": 44,
"channelWidth": "80 MHz",
"power": "19 dBm",
"visible": true,
"broadcasting": false
},
however you have to feed the API the AP serial number. I assume if I tried to feed it all the serial numbers in one API call, the return would just give me all the "basicServiceSets" without letting me know which belongs to which AP.
I can get the AP serial numbers and names from
/organizations/orgID/devices/statuses?productTypes[]=wireless
{
"name": "GD - AP - 2",
"serial": "0000-0000-0000",
"mac": "00:00:00:00:00:00",
"publicIp": "0.0.0.0",
"networkId": "N_0000000000000000",
"status": "online",
"lastReportedAt": "2022-12-07T11:21:44.044000Z",
"productType": "wireless",
"model": "MR33",
"tags": [ "recently-added" ],
"lanIp": "0.0.0.0",
"gateway": "0.0.0.0",
"ipType": "dhcp",
"primaryDns": "0.0.0.0",
"secondaryDns": "0.0.0.0"
},
I am struggling to imagine the necessary workflow. The serial number and name come from one API and all the other info I need come from another API, but the field that links the two API is the serial number which I don't actually need but its the only thing that ties the AP name to the other bits of info : /
12-07-2022 03:54 AM
The workflow would be as follows
The first API calls would be to get all the AP's serial number and save these details to a text file, so example of your text file could be
xxxxxx
yyyyyy
zzzzzz
aaaaa
bbbbb
cccccc
So call /organizations/orgID/devices/statuses?productTypes[]=wireless
once you have got this the next API call would be passing the serial number of the AP to the API call
You would read the text file one line at a time and once you have the data in json format, process this to extract the necessary details, write it to a text file and move onto the next serial number in the text file .
You are basically looping thru the text file, reading the serial number calling the API to get the data you need
Hope this makes sense
12-07-2022 05:13 AM
thanks,
so, would I be doing a single API call per serial number?
Since the data returned from /devices/serialnumber/wireless/status doesn't actually include the serial number or AP name, how would I link the API return data to the serial number that was used in the call?
12-07-2022 05:57 AM
@Adrian41 wrote:thanks,
so, would I be doing a single API call per serial number?
Since the data returned from /devices/serialnumber/wireless/status doesn't actually include the serial number or AP name, how would I link the API return data to the serial number that was used in the call?
once you export the data from the 2nd API call, where will this data be stored/saved ?
As I stated earlier, I write to a back end SQL database and this is how I would link the AP name or serial number to the data extracted. For your case you could write it to a text file with the AP serial number or name the header of the text file.
12-07-2022 07:01 AM
Quick and dirty example.
I just tested it on an org with over 12000 active AP-BSSIDs (took 2-3 minutes), it works ok.
You'll need Python 3 and the Meraki Python library installed.
Most of the code is setting things up and error detection, the action all happens in just a few lines.
If you want to add extra output fields, just edit the print statement.
import os
import sys
import meraki.aio
import asyncio
#import the org id and api key from the environment
#or you could hard code them, but that's less desirable
ORG_ID = os.environ.get("PARA0")
API_KEY = os.environ.get("PARA1")
async def processAp(aiomeraki: meraki.aio.AsyncDashboardAPI, ap):
try:
# get list of statuses for an AP
statuses = await aiomeraki.wireless.getDeviceWirelessStatus(ap['serial'])
except meraki.AsyncAPIError as e:
print(f'Meraki API error: {e}', file=sys.stderr)
sys.exit(0)
except Exception as e:
print(f'some other error: {e}', file=sys.stderr)
sys.exit(0)
for bss in statuses['basicServiceSets']:
if bss['enabled']:
print(f"{ap['name']},{bss['ssidName']},{bss['bssid']},{bss['band']}")
return
async def main():
async with meraki.aio.AsyncDashboardAPI(
api_key=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
) as aiomeraki:
#get the wireless devices
try:
aps = await aiomeraki.organizations.getOrganizationDevices(ORG_ID, perPage=1000, total_pages="all", productTypes = ["wireless"])
except meraki.AsyncAPIError as e:
print(f'Meraki API error: {e}', file=sys.stderr)
sys.exit(0)
except Exception as e:
print(f'some other error: {e}', file=sys.stderr)
sys.exit(0)
# process devices concurrently
apTasks = [processAp(aiomeraki, ap) for ap in aps]
for task in asyncio.as_completed(apTasks):
await task
if __name__ == '__main__':
asyncio.run(main())
12-07-2022 07:22 AM
thank you so much! this looks great. I cant test it at the moment as I'm having trouble installing the meraki module, but thats an entirely different problem.
Thanks again!
01-24-2023 07:38 AM
sorry, just one more follow up question....
it looks like you have put your key and org id in a module called "os"?
Where do you put modules you make like this? I am trying to use PyCharm but I'm a bit lost with the interface and where things should go, esp if they are modules I want to call in scripts.
01-24-2023 08:01 AM
The 'os' module is part of the base Python install, it does a bunch of things, in this case I'm using it to get the value of runtime environment variables.
I'm running on Unix-like systems, so I pull in environment variables from the shell, PARA0 and PARA1. Of course you can choose your own names, there is nothing special about these names, they just need to match the names you use in the environment.
In the shell that runs the script, I set these two environment variable like this...
PARA0="the org ID value"
PARA1="the API key value"
...then the script can pull in the values by variable name.
If you are on windows, you can set environment variables with the same names and it should work just the same, the way you set them depends where you are running the scripts from, there are some examples here of using Windows variables with scripts with the 'os' module...
https://www.twilio.com/blog/environment-variables-python
In general, if you install modules using pip, I think they should go in the right place automatically, for instance...
pip install meraki
...or to update the version...
pip install --upgrade meraki
...but I don't use Python on Windows so maybe there are some other/better ways!
03-11-2024 06:16 PM
Hey sungod, this was very helpful, thank you! Did you build this script from scratch or did you use documentation to assist?
03-12-2024 05:35 AM
It's mostly from adapting from another script I wrote, the basics of set-up, iterating by something, error handling etc. are common across scripts, so whenever I do something new I tend to see which old script I have that is closest in structure to what I need and then adapt it, and I'm lazy 😀
This one is using async I/O as it is generally more efficient, but it does affect readability.
If you look on the github site, there are a few examples you can use as starting points to experiment...
https://github.com/meraki/dashboard-api-python/tree/main/examples
03-12-2024 07:33 AM
I apreciate you sharing that. Thanks again!
03-12-2024 07:45 AM
PS- I used ChatGPT. I too start of my base script of calling up all my networks, and go from there.
03-12-2024 06:49 AM
I have just a bit of code here: jadexing/Merakicode: Code for Meraki Wi-Fi (github.com)
All mine is Wi-Fi based.
I open Powershell, go to the script directory and execute.
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide