Automation of Operational Tasks on Mist Infrastructure

Ever since I posted my first python script to upgrade Mist Infrastructure, I was intrigued with the endless opportunities to automate daily tasks on Mist. I also got the chance to improve my python skills and learned how using modules can enable code reusability and improve efficiency. In this post I am sharing my Github repository which contains fours basic scripts to help with some operational tasks. The repository will be updated frequently with new scripts that will help in increased automation.

Save this repository to your computer

Be sure you have [git](https://git-scm.com/downloads) installed then run the command

git clone https://github.com/opravipati/MistScripts.git

Install Dependencies

The first step required to run the scripts is install the dependencies.
In the directory that you have the script“`pip3 install -r requirements.txt “`

Generate Token

Once you have the packages installed, the next requirement is to populate your token and OrID information in the mistmodule.py file. For steps to create your token and identify your OrgID please please visit https://www.mist.com/documentation/using-postman/

Mist API Documentation

Home Page https://api.mist.com/api/v1/docs/Home

Repository currently contains five files.

  • mistmodule.py contains the functions that will be used from other scripts. This will also be the file where you store your token and Organization ID information

To start using these scripts , please add your token and Org Id to mistmodule.py file

def getVar():
    token = "#Token Here"  
    orgId= "#ORGID Here" 
    return token,orgId
  • orgUpgrades.py can be used to upgrade infrastructure at all sites in the organization

To upgrade access points at all sites in your Organization, please set the version in orgUpgrades script in the function shown below

def siteUpgrade(siteId):  # Function Upgrades access points at a site.
    payload = {
        "version": "0.5.1944", #Version to upgrade to
        "enable_p2p": True #Enabling peer to peer upgrade
    }

If you want to upgrade only specific model of APs, the payload can be modified accordingly.

{
    "version": "3.1.5",
    "enable_p2p": false,
    "models": [
        "AP41"   
    ]
}
  • siteUpgrade.py can be used to upgrade infrastructure at any one site in the organization. This will prompt you to enter a site name. The version on the payload needs to be updated in this file similar to orgUpgrades.py.
  • orgOfflineAPs.py can be used to provide a list of offline APs in the organization. Running this script will result in a CSV file with the list of APs. Please note the APs need to have a name in order to appear in the list. Mist APs by default display the mac address under the name on UI. Such APs will not be included in the csv file.
  • trackClients.py can be used to track client details every 60 seconds. When this script is execute, it prompts you to enter a mac address of the client and the site name to track. Mist updates its client information every 60 seconds and so the default frequency to track client has been set to the same value.

A Simple Script to Upgrade Mist Infrastructure

The “API first” approach of Mist Systems is undoubtedly one of the main reasons to become an attractive solution for large enterprises. It has simplified and enabled automation of wireless networks by providing REST APIs for almost every configuration and monitoring task that can be performed on a WLAN. In this post, I will be providing details on a Python script that can upgrade all the sites of your Organization with minimal downtime. The assumption is you have python already installed and have some basic knowledge on the language. The libraries we are going to use in the script are “requests” and “json”. While json module is included in python, you may have to install requests module.

The API to upgrade the devices at a site is shown below

POST /api/v1/sites/:site_id/devices/upgrade

The API call requires payload in the following JSON format to perform the action.

{
    "version": "3.1.5",
    "enable_p2p": false,
    // filters that can be used, if no filter specified, it means entire site
    "device_ids": [
        "00000000-0000-0000-1000-5c5b35584a6f"
    ]
    "models": [
        "AP41"   
    ]
}

Version is the OS version you would like to upgrade to and enable_p2p allows you to upgrade only few devices at a time. Setting it to True will result in only up to 3 or 4 devices upgrading and rebooting simultaneously at each site while other APs continue to serve the clients. This will minimize downtime. But if the preference is to upgrade all of them at the same time during a change window, this has to be set to False. The device_ids and models are only filters that can be used if required. If no filters are specified, all the devices at the site will be upgraded.

As observed, to make the API call, the site_id variable has to be provided. Mist also provides an API to get list of sites in an Organization.

GET /api/v1/orgs/:org_id/sites

So, in the script we are going to be build, we need to first get a list of site IDs and use them to upgrade sites with json payload containing version and other variables.

To accomplish this, we define two functions that will perform each task.

The first function is to get the list of site IDs.

def getSiteDict(orgId):
    sitesUrl = "https://api.mist.com/api/v1/orgs/" + orgId + "/sites"   #API to get list of sites in your Ogranization
    getSiteIds = requests.request("GET", sitesUrl , headers=headers) #API call performed
    getSiteIds_json = getSiteIds.json() #Decodes the JSON data from API Request into a list
    siteDict={}
    for site in getSiteIds_json:    #Loops through the list and appends site names and site IDs to the siteDict
        try:
            siteDict.update({site['name']:site['id']})
        except Exception as e:
            print(e)
    return siteDict

We need to pass the OrgID as an argument to this function. This will enable the function to be reused if you are managing multiple organizations. The function will return a dictionary with <siteName, siteId> as key value pairs. Next we define a function to send the upgrade devices API call to each site. This function takes siteID as an argument.

def siteUpgrade(siteId):
    payload = {
        "version": "0.5.17360", #Version to upgrade to
        "enable_p2p": True #Enabling peer to peer upgrade
    }
    payload=json.dumps(payload)     #Converts the payload to JSON format
    upgradeUrl = "https://api.mist.com/api/v1/sites/" + siteId + "/devices/upgrade" #API URL To upgrade the devices
    upgradeCall = requests.request("POST", upgradeUrl , data=payload, headers=headers)    #API call performed with the payload
    return upgradeCall.status_code    #Returns the HTTP Response code for the API call.

Now that we have the functions defined all we have to do is complete the puzzle to make function calls and perform the tasks. To do this we need OrgID and header which contains Token. The token needs to have read write privileges to perform the operations in this script.

orgId= "#Enter Org ID Here"  
headers = {
    'Authorization': "Token #here", # Token with read write acccess required
    'Content-Type': "application/json",
        }
siteDict=getSiteDict(orgId) #Function call to get siteDict
for siteName,siteId in siteDict.items():  #Loops through the Dictionary to make API calls for each site.
    try:
        upgradeStatus=siteUpgrade(siteId)
        if upgradeStatus == 200: #HTTP Response is 200 for successful API calls.
            print("Upgrade Successful for site " , siteName)
        else:
            print("Error Occurred ", upgradeStatus) 
    except Exception as e:
            print(e)  

The full script can be found below.

import requests, json

headers = {
    'Authorization': "Token #here", # Token with read write access required
    'Content-Type': "application/json",
        }
def getSiteDict(orgId):
    sitesUrl = "https://api.mist.com/api/v1/orgs/" + orgId + "/sites"   #API to get list of sites in your Organization
    getSiteIds = requests.request("GET", sitesUrl , headers=headers) #API call performed
    getSiteIds_json = getSiteIds.json() #Decodes the JSON data from API Request into a list
    siteDict={}
    for site in getSiteIds_json:    #Loops through the list and appends site names and site IDs to the siteDict
        try:
            siteDict.update({site['name']:site['id']})
        except Exception as e:
            print(e)
    return siteDict

def siteUpgrade(siteId):
    payload = {
        "version": "0.5.17360", #Version to upgrade to
        "enable_p2p": True #Enabling peer to peer upgrade
    }
    payload=json.dumps(payload)     #Converts the payload to JSON format
    upgradeUrl = "https://api.mist.com/api/v1/sites/" + siteId + "/devices/upgrade" #API URL To upgrade the devices
    upgradeCall = requests.request("POST", upgradeUrl , data=payload, headers=headers)    #API call performed with the payload
    return upgradeCall.status_code    #Returns the HTTP Response code for the API call.

orgId= "#Enter Org ID Here"  

siteDict=getSiteDict(orgId) #Function call to get siteDict
for siteName,siteId in siteDict.items():  #Loops through the Dictionary to make API calls for each site.
    try:
        upgradeStatus=siteUpgrade(siteId)
        if upgradeStatus == 200: #HTTP Response is 200 for successful API calls.
            print("Upgrade API call Successful for site " , siteName)
        else:
            print("Error Occurred ", upgradeStatus) 
    except Exception as e:
            print(e)