- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Python Script - Rebooting Devices
Hey everyone
I worked with a team member of mine to come up with an automated script that runs on our Ansible server that will reboot whichever Meraki access point that has 'API' at the end of its name (or really just in the name), on a weekly scheduled basis, with email reporting. This could be used I'm sure for MX or MS as well, but we are only using it for access points.
Feel free to critique. I'm not a Python person by any means, but this works great:
import json import sys import requests import time import email import smtplib cisco_meraki_api_Key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' organizationId = 'XXXXXX' baseUrl = 'https://dashboard.meraki.com/api/v0/' inventory_api_url = "organizations/{}/inventory".format(organizationId) headers = { 'X-Cisco-Meraki-API-Key': cisco_meraki_api_Key, 'Content-Type': 'application/json' } get_inventory = requests.get(baseUrl+inventory_api_url, headers=headers, ) # Parse the get_inventory into json inventory_json = get_inventory.json() networkID = "" serial = "" # Opens or create a file name results f = open('results.txt', "w+") # loop over all the dictionaries inside inventory_json, # if API is inside the dictionary it will get the NetworkID and the serial and then write it to the string above for ap in inventory_json: try: if 'API' in ap['name']: networkID = ap['networkId'] serial = ap['serial'] ap_name = ap['name'] reboot_api_call = requests.post( baseUrl+'/networks/{}/devices/{}/reboot'.format(networkID, serial ), headers=headers) if reboot_api_call.status_code == 200: print('Rebooting --->', ap_name, '---> Successful' , file=f) print("---------------------------------------------------",file=f) if reboot_api_call.status_code == 400: print ('Rebooting --->', ap_name, '---> Bad Request' , file=f) print("---------------------------------------------------",file=f) if reboot_api_call.status_code == 403: print ('Rebooting --->', ap_name, '---> Forbidden' , file=f) print("---------------------------------------------------",file=f) if reboot_api_call.status_code == 404: print ('Rebooting --->', ap_name, '---> Not Found' , file=f) print("---------------------------------------------------",file=f) if reboot_api_call.status_code == 429: print ('Rebooting --->', ap_name, '---> Too Many Requests' , file=f) print("---------------------------------------------------",file=f) time.sleep(2) except: continue # closes the file f.close() # Setting up the SMTP Server sender = 'meraki_api@company.local' receiver = 'yourteam@company.com' smtp_server = smtplib.SMTP(host='X.X.X.X', port=25) f = open('results.txt', "r") # reads the information inside the file results script_result = f.read() message = """From: <meraki_api@company.local> To: <yourteam@company.com> Subject: Meraki AP Reboot API CODE: 200 = Successful API CODE: 400 = Bad Request API CODE: 403 = Forbidden API CODE: 404 = Not Found API CODE: 429 = Too Many Requests {} """.format(script_result) # Email execution try: smtp_server.sendmail(sender, receiver, message) print('Successfully sent email') except: print('Error: Unable to send email') # closes the file. f.close()
- Labels:
-
Code Sample
-
Dashboard API
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Is this more for just good measure to reboot APs weekly or do you manually change names on the problem APs that you want to get the weekly scheduled reboot?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Every now and then an AP's 5GHz radio (random) will just get hung up. Never been able to determine why. Has happened on different code versions. Usually takes a few weeks to months for it to crop up. Too much effort to review event logs for every AP (which makes it easy to see if the radio is bugged out or not), so its just easier to reboot them once a week so they never have enough 'bake time' to run into it.
MR32 happen much more often vs MR33
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Meraki has confirmed an issue with the beacons on the MR16 and are working on the fix. Currently, they are saying no other AP's are having this issue. We have seen similar issues on the MR18's as well however.
Thanks for the script, we will give it shot and see if it corrects our MR16 problems.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Nice script. The one thing I can see is using the inventory endpoint will return the inventory for the whole organization, including devices that have been claimed, but might not be assigned to a network and be active. I have organizations with multiple networks where we will use an AP at a site for awhile, remove it after a time, and then keep it claimed for future use. The device will retain the name it had, and if it had the API in it, the script would try to reboot it. Those devices also don't have network IDs, so you could get an exception just trying to reference that field. The exception handling would probably catch the error and keep going, but it generates extra errors and messages.
I would consider using the {{baseUrl}}/organizations/{{organizationId}}/deviceStatuses endpoint. It will only give you devices that are assigned to networks. You can also use the 'status' tag to check and see if the AP is 'offline'. No point in trying to reboot an AP that you will never be able to contact.
Just a thought.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Yeah i noticed it was trying to reboot access points that were not on a network yet, but had the network name in the AP name still. I'll see what I can do to tweak 😃
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You should just need to change the endpoint name. The fields you are using (name, serial, network ID) are in the device status endpoint too. If you want to check the status before trying to reboot, that is your call. Personally, it think it is a good check. Saves you can API call that would be going against your call limit.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
However, I have SPARE access points in the network that are 'offline' that still show as being rebooted. I'll have to tweak the script for that new API call you told me about and that way it will also ignore offline devices.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Just a side note. The documentation for the {{baseUrl}}/organizations/{{organizationId}}/inventory endpoint on documenter.getpostman.com is not complete. The sample response information does not include the "name" field. When I looked at the source code provided in this topic and I saw "name" referenced, I thought it was odd, since I had used this endpoint and thought I had to get the device name through other means. Since I took the documentation as gospel, I did not look at all of the return values in the debugger until this topic came up.
I don't know about anyone else, but when I look at API documentation, I kind of expect it to be correct in terms of what is going to be returned or what needs to be included when putting/posting information. This is not the first time I have found errors on documenter.getpostman.com, either. Is there another place I should be looking for comprehensive documentation for this API? If not, Meraki please keep documenter.getpostman.com updated with the correct data regarding the API.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Nolan
Thanks for sharing this with us. Do you have an updated script? Also, is this script written to only work on ansible since I see the import json, sys etc at the beginning of the script and not sure how I can automate this on windows task for example. Any help would be appreciated.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
@webrama wrote:Hi Nolan
Thanks for sharing this with us. Do you have an updated script? Also, is this script written to only work on ansible since I see the import json, sys etc at the beginning of the script and not sure how I can automate this on windows task for example. Any help would be appreciated.
If your using Windows, then you can use WSL2 to run an Ubuntu app, or do it in a container etc., but probably easier to just use the Ubuntu app from the Windows store.
Import JSON is to import the json python library so you can parse the return data from the Meraki inventory API call, when receives the inventory and returns it as JSON data.
I should clarifiy 'ansible server' is really just a linux server i called ansible server because we were/are using it for other actual ansible tasks. However this is just python, sorry about the confusion.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you so much for the info Nolan. I will go thru this again and let you know. Thanks!
~Rama
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks for the script, but when I use it, not all the APs get rebooted.
All my APs name end with "-AP" but about a third get rebooted.
Is there a way to just say reboot all APs?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
In my example it is using the keyword 'API' which must exist within the AP name for it to 'catch' it. You can change that to something else if you want. What I use is 'CRON' and i placed at the end of all my AP's for my small sites, '-CRON' for them to reboot. Been working great for me for a while now.
You can do this on a per-network basis too, so I have other sites that have AP's that start with say 'PHX' for the site code, so any AP name that has PHX will reboot for example, and if I don't want them to, I change the name to PH~X etc., to break it for that AP.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks for the quick response.
I do use "-AP" since that should match every APs name, but it doesn't.
I changed it to just "AP", but that actually rebooted less than using "-AP".
I then changed it to "Room" since where I see most problems are in the APs that have "Room" in the name, but again, that didn't reboot every AP that had "Room" in their name.
What I also noticed when I used "-AP", it was always the same APs that were rebooted.
I just have one network, all the APs are in it.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Out of curiosity, how are you validating if the AP was rebooted or not.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
One way is to view the txt file that is created and shows the APs that were rebooted.
I also verified if an AP was rebooted by running the script and going to a different office where the AP should have rebooted, but didn't.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Here's my own version of a bulk AP reboot script...
Because Meraki steadfastly refuse to add any sort of bulk AP reboot facility to the dashboard (I've asked them on several occasions and they say they won't consider it, and that I "shouldn't need to reboot multiple AP's" without considering the use case where yes, you do actually want to do this and every other vendor providing a way) a couple of years ago I wrote a stand alone python script to do bulk AP reboots based on device tags, so that I can reboot either all AP's in the network, or AP's with a specific tag.
As we have building name tags on our AP's to make it easier to group AP's based on building it makes it easy to reboot AP's in a certain building for example, and it would also be easy to add tags to AP's based on collections of AP's you might want to bulk reboot.
The script originally used the v0 API but when I recently tried to use it I found it was broken due to changes in the API (such as tags going from a space separated list in a single string, to an array of tags) and python Meraki module changes so I've updated it to work with the current V1 API and current Meraki python module.
#!/usr/bin/env python3
import json
import requests
import time
import meraki
import sys
import datetime
def reboot_ap(apikey, networkid, serial, suppressprint=False):
base_url = 'https://api.meraki.com/api/v1'
calltype = 'Device'
posturl = '{0}/networks/{1}/devices/{2}/reboot'.format(
str(base_url), str(networkid), str(serial))
headers = {
'x-cisco-meraki-api-key': format(str(apikey)),
'Content-Type': 'application/json'
}
postdata = {
'serial': format(str(serial))
}
dashboard = requests.post(posturl, data=json.dumps(postdata),
headers=headers)
print(dashboard.status_code, dashboard.text, calltype)
return
apikey = 'api_key_here'
network_id = 'network_id_here'
reboot_all = False
cmdline = False
if len(sys.argv) > 1:
cmdline = True
if sys.argv[1] == '--all':
reboot_tag = None
reboot_all = True
else:
reboot_tag = sys.argv[1]
dashboard = meraki.DashboardAPI(apikey, suppress_logging=True)
deviceList = dashboard.networks.getNetworkDevices(network_id)
tag_list = []
for device in deviceList:
new_tags = device['tags']
for tag in new_tags:
if not tag in tag_list:
tag_list.append(tag)
now = datetime.datetime.now()
print(now.strftime("%H:%M:%S %d-%m-%Y"))
print('Available Tags:\n')
for tag in tag_list:
print(tag)
if cmdline is False:
reboot_tag = input("\nEnter Tag of APs to reboot or press enter for all APs: ")
if not reboot_tag:
reboot_all = True
print('\nRebooting Devices:\n')
for device in deviceList:
if reboot_tag in device['tags'] or reboot_all:
try:
name = device['name']
except:
name = 'unknown'
print(name, device['serial'], device['lanIp'], device['tags'], '- ',end='')
reboot_ap(apikey, network_id, device['serial'])
time.sleep(0.5)
print()
I'm pretty rusty with Python and this was a quick expedient script to get the job done in a hurry so I'm sure the Pythonistas in the audience will be rolling their eyes at the coding... 🙂
If you run the script without command line arguments it will scan all the devices in the wireless network gathering the tags from each device to build a list of all available network device tags, these tags are then displayed. You are then prompted to either press enter (which will reboot ALL AP's in the network, be careful.. press CTRL-C if you want to back out) or enter a tag name to reboot only those AP's with that tag.
There is a 0.5 second delay between each reboot API call as if you call more than about every 0.2 seconds the API will reject the request. The success/failed result for each device will be displayed.
If you use a single command line argument '--all' can be used to reboot all AP's without prompting (for example from a cron script) or you can use a tag name to reboot devices with that Tag without prompting.
I use this script on Python 3.8 on Linux with the Meraki pip module installed. I don't think it relies on any other non-standard modules. While this version of the script hasn't been tested on Windows it should work as my original version did.
Incidentally the Meraki Python module doesn't seem to have a reboot API call, nor does the official documentation list a reboot call, hence why the actual reboot action is hand coded as an HTML query using the requests library - I simply took one of the other example requests, changed the action to reboot and it worked! 🙂
Hope someone finds this useful.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
https://developer.cisco.com/meraki/api-v1/#!reboot-device
There is a Meraki Python API call now for rebooting an individual device. You just need the device's serial number.
We wrote a quick script using this API call to do an overnight reboot of our MG devices every weekend. We were finding if we didn't do a weekly reboot on some of the MG's, they'd need manual power cycles (popping the power on the PoE adapter) every couple of weeks after they randomly would drop offline.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks for the heads up.
However to clarify, the /devices/{serial}/reboot API endpoint has always been there, (since the first release of the API a couple of years ago, as I was doing this already back then) but it was undocumented and it was also not exposed in the python client bindings, which is why I had to manually construct the http request to call to that endpoint in the reboot_ap() function.
I see from your link that the API endpoint is now finally documented, however I have not checked to see if there is a new version of the meraki python client bindings which add it as a proper python function call yet. (At the time of my post a month ago, it was not in the python client bindings)
In any case the script above is still working and I use it to auto reboot our AP's once a week.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Based on DBMandrake's script I created a reboot script that I have been using:
#!/usr/bin/env python3
# Script based on DBMandrake original script and edited and updated by
# Dean Warren and ChatGPT
# Github Repo: https://github.com/deawar/APReboot
import json
import requests
import os
import time
import sys
import datetime
from dotenv import load_dotenv
import meraki
def display_help():
help_text = """
Usage: AutoAPReboot.py [OPTIONS] <CONFIG_FILE>
Options:
--all Reboot all devices in the specified networks.
--help Display this help message.
Arguments:
CONFIG_FILE Path to the JSON configuration file containing network details.
Description:
This script reboots access points in multiple Meraki networks based on
tags provided in a JSON configuration file. To use the script, ensure the
following environment variable is defined in a .env file:
- apiKey: Meraki API key
The JSON configuration file should contain:
- networkId: Meraki network ID
- organizationId: Meraki organization ID
- orgName: Organization name (for logging)
- tags: List of tags to filter devices (leave empty to reboot all)
Examples:
Reboot all devices in all networks specified in config.json:
python AutoAPReboot.py --all config.json
Reboot just devices with tags specified in config.json file(can be anyname.json):
python AutoAPReboot.py myNetwork.json
"""
print(help_text)
def write_log(message):
"""Log message to a file."""
with open("reboot_log.txt", "a") as log_file:
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_file.write(f"{timestamp} - {message}\n")
# Load environment variables
load_dotenv()
apikey = os.getenv("apiKey")
# Display help if API key is missing
if not apikey:
print("Error: Missing required environment variable `apiKey`. Please check your .env file.")
display_help()
sys.exit(1)
def reboot_ap(apikey, network_id, serial):
"""Reboot an access point (AP) using Meraki API."""
base_url = 'https://api.meraki.com/api/v1'
post_url = f'{base_url}/networks/{network_id}/devices/{serial}/reboot'
headers = {
'x-cisco-meraki-api-key': apikey,
'Content-Type': 'application/json'
}
try:
response = requests.post(post_url, headers=headers)
if response.status_code == 202:
write_log(f"Reboot successful for device {serial}.")
print(f"Reboot successful for device {serial}.")
else:
error_msg = f"Failed to reboot device {serial}. Status code: {response.status_code}, Response: {response.text}"
write_log(error_msg)
print(f"Error: {error_msg}")
except requests.RequestException as e:
error_msg = f"Failed to reboot device {serial}. Exception: {e}"
write_log(error_msg)
print(f"Error: {error_msg}")
def reboot_devices_in_network(dashboard, network_id, tags, reboot_all):
"""Reboot devices in the specified network based on tags or all devices."""
try:
device_list = dashboard.networks.getNetworkDevices(network_id)
except Exception as e:
error_msg = f"Failed to retrieve devices for network {network_id}. Exception: {e}"
write_log(error_msg)
print(f"Error: {error_msg}")
return
for device in device_list:
if reboot_all or any(tag in device.get('tags', []) for tag in tags):
name = device.get('name', 'unknown')
print(f"Rebooting {name} ({device['serial']}) with tags {device.get('tags', [])}...")
reboot_ap(apikey, network_id, device['serial'])
time.sleep(0.5)
def main():
if '--help' in sys.argv:
display_help()
sys.exit(0)
# Check if configuration file is specified
if len(sys.argv) < 2 or (len(sys.argv) == 2 and sys.argv[1] in ['--all', '--help']):
print("Error: Missing JSON configuration file.")
display_help()
sys.exit(1)
# Determine the reboot mode and configuration file path
reboot_all = '--all' in sys.argv
config_file = sys.argv[-1] # Last argument should be the JSON config file
# Load networks from the specified JSON file
try:
with open(config_file, 'r') as f:
networks = json.load(f)
except Exception as e:
error_msg = f"Failed to load network configurations from '{config_file}'. Exception: {e}"
write_log(error_msg)
print(f"Error: {error_msg}")
sys.exit(1)
# Initialize Meraki dashboard API
dashboard = meraki.DashboardAPI(apikey, suppress_logging=True)
# Iterate over each network in the JSON file and reboot devices
for network in networks:
network_id = network.get('networkId')
tags = network.get('tags', [])
org_name = network.get('orgName', 'Unknown Organization')
if not network_id:
error_msg = f"Skipping network due to missing networkId for organization '{org_name}'."
write_log(error_msg)
print(f"Error: {error_msg}")
continue
print(f"\nProcessing network '{org_name}' (Network ID: {network_id})...")
write_log(f"Processing network '{org_name}' (Network ID: {network_id}).")
reboot_devices_in_network(dashboard, network_id, tags, reboot_all)
if __name__ == "__main__":
main()
that uses a .env file and takes a .json file as an argument for a list of networks and devices. My Repo is here: https://github.com/deawar/APReboot
