Mist

Mist has brought true innovation to the networking space with
the world’s first AI-driven wired and wireless network.
The Mist AI Platform makes networking predictable, reliable
and measurable with unprecedented visibility into the user
experience. Time consuming manual IT tasks are replaced with
AI-driven proactive automation and self-healing, lowering
networking operational costs and saving substantial time and
money.
Mist also brings enterprise-grade Wi-Fi, Bluetooth® LE and
IoT together so businesses can increase the value of their
wireless networks through personalized location services,
such as wayfinding, proximity notifications, and asset location.
With Mist’s patented virtual BLE (vBLE) technology, no battery
beacons or manual calibration are required. Mist also extends
our AI operational efficiency and insights to the wired side of
the business.*
All operations are managed via Mist’s open and programmable
microservices cloud architecture. This delivers maximum
scalability and performance while also bringing DevOps agility
across the wired and wireless networking to wireless location
services.

From the beginning Mist has been a 100% API company. The API contains a set of tools known as endpoints for building software and applications that communicate with the Mist cloud. The Dashboard API is a modern, RESTful API using HTTPS requests to a URL and JSON as a human-readable format. The API is available to any customer with a Mist account and is very well documented.

mis086-webgraphic-microservices

Right now to the good stuff... (or not)

When using Python for my programmability challenges it becomes difficult to maintain and create projects that natively call different API's.

For example using the python module Requests getting information from the MIST API would look like the following:

import requests
import json

token = '<API TOKEN HERE>'

headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Token ' + token
}

url = 'https://api.mist.com/api/v1/sites/978c48e6-6ef6-11e6-8bbf-02e208b2d34f/devices'

results = requests.get(url, headers=headers)

devices = json.loads(results.text)

for device in devices:
    print(device['name'])

This is fine, however we will need to put a URL in there for every request we would like to query. We could get around this by using a base_url and adding different variables to create a URL but this would require managing multiple variables.

An example of this would be:

site_id = '978c48e6-6ef6-11e6-8bbf-02e208b2d34f'
base_url = 'https://api.mist.com/api/v1/'
sites_url = base_url + 'sites' + '/' + site_id + '/'

devices_url = sites_url + 'devices'
wlans_url = sites_url + 'wlans'
print(devices_url)
print(wlans_url)

output:
https://api.mist.com/api/v1/sites/978c48e6-6ef6-11e6-8bbf-02e208b2d34f/devices
https://api.mist.com/api/v1/sites/978c48e6-6ef6-11e6-8bbf-02e208b2d34f/wlans

Unfortunately there is not a Mist Python wrapper and I do not always have the time to create a wrapper simular to the Silverpeak one I created a while back.

Wouldn't it be cool if we were able to dynamically create a wrapper...

giphy

Python HTTP Client

This is a package to "Quickly and easily access any RESTful or RESTful-like API".

Installation

To install this wonderful package we just need to install it with:

pip install python_http_client

Usage

By importing the package with import python_http_client we can leverage it to dynamically create URL's using pythons dot operator.

For example we can define a base URL to use and then use the dot operator to dynamically create API requests.

This is a basic python script to set up the library and put the correct headers in:

import python_http_client
import json

token = '<API TOKEN HERE>'

headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Token ' + token
}

base_url = 'https://api.mist.com/api/v1/'

client = python_http_client.Client(host=base_url, request_headers=headers)

This can then be used to create our queries.

Devices Get

To get the list of AP's within Mist via API we need to call the following API endpoint:

https://api.mist.com/api/v1/sites/<site-id>/devices

This same API can be dynamically create within the Python_HTTP_Client with the following line:

devices = client.sites._(<site-id>).devices.get()

Replace <site-id> with your site id.

Calling this will return an object of which we will be able to access the following properties.

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_body', '_headers', '_status_code', 'body', 'headers', 'status_code', 'to_dict']

I will show a number of the available options:

print(devices.status_code)
> 200

print(devices.body)
> b'[{"switch_config":{"wds":{},"eth1":{},"module":{},"eth0":{}},"orientation":0.0,"disable_eth1":false,"heightSet":false,"x_m":3.655447553590063,"site_id":"978c48e6-6ef6-11e6-8bbf-02e208b2d34....

print(devices.headers)
> Access-Control-Allow-Credentials: true
  Access-Control-Allow-Origin: https://manage.mist.com
  Access-Control-Expose-Headers: X-CSRFTOKEN,X-Requested-With,X-Page-Page,X-Page-Total
  Allow: POST, OPTIONS, GET
  Cache-Control: no-cache, no-store
  Content-Type: application/json
  Date: Mon, 16 Dec 2019 10:30:52 GMT
  Pragma: no-cache
  Server: gunicorn/19.9.0
  Vary: Origin
  Via: kong/0.9.3
  X-Frame-Options: SAMEORIGIN
  X-Kong-Proxy-Latency: 0
  X-Kong-Upstream-Latency: 27
  Content-Length: 19635
  Connection: Close

print(devices.to_dict)
> [{'switch_config': {'wds': {}, 'eth1': {}, 'module': {}, 'eth0': {}}, 'orientation': 0.0, 'disable_eth1': False, 'heightSet': False, 'x_m': 3.655447553590063, 'site_id': '978c48e6-6ef6-11e6-8bbf-02e208b2d34f', 'modified_time': 1576124100, 'height': 0.0, 'image1_url': 'https://papi-production.s3.amazonaws.com/device_image1/00000000-0000-0000-1000-5c5b352f597d.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600&X-Amz-Date=20191216T103209Z&X-Amz-SignedHeaders=host&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEBgaCXVzLXdlc3QtMSJHMEUCIEbgqCcxdZYXrCYIV8gWIZoycn5n3jrFAQ1rQuxlJ%2B1jAiEA%2FwmCGriI4XUDOkGFNWlqU8goHRAn%2BMIZbSiyj9iVcMsqtAMIcRACGgw2NjA2MTAwMzQ5NjYiDJ3Vvujdf1jRd%2BNsvyqRA9%2Bi%2BrsFUtLPnP29OFBU6YsNGV3n2DGSJboNcKKHmuwZkGfP6c2kGEhVSYzc2lTFEK1w5ys4P2vH1RY1Iz8XupQmrf9RFN1nhLAQE82pk7%2BjrX2Txu0z0HFENTpbbxBLxxmXClQDvDicC2GW%2FFPB4y7X6cDh%2B8Ox5PjChsqn5ElGIt....

A get request is not the only method that you can do. You can replace .get() with a number of other methods such as:

.delete()
.post()
.put()
.patch()

I wont go through the details on how to use them but examples can be seen HERE.