Software Image Management Guide
Introduction
Cisco DNA Center provides APIs to manage the operating system images of the devices registered to it, where the developer can upload, distribute and activate an image on a device.
Goal
The goals of this guide are:
- Get the device information
- Get a list of images filtered by family
- Distribute the golden image to the selected device
- Activate the golden image
Endpoints and methods used
- GET
/dna/intent/api/v1/network-device
- GET
/dna/intent/api/v1/image/importation
- POST
/dna/intent/api/v1/image/distribution
- POST
/dna/intent/api/v1/image/activation/device
- GET
/dna/intent/api/v1/task/{task_id}
Prerequisites
For this module, it is recommended that the user already has experience authenticating with Cisco DNA Center, getting devices information and asynchronous tasks:
Environment
This guide was developed using:
Software Image Management API
The software image management API is composed of 5 endpoints that support image uploading, distribution and activation.
Get Device Information
We are going to update the image on the CSR1Kv-03.devnet.local
device.
First, we need to authenticate and retrieve a token from the API.
Do not use
verify=False
orurllib3.disable_warnings()
if you are not sure of its purpose. Read Authentication and Authorization.
import requests
from requests.auth import HTTPBasicAuth
import urllib3
urllib3.disable_warnings()
BASE_URL = 'https://<IP Address>'
AUTH_URL = '/dna/system/api/v1/auth/token'
USERNAME = '<USERNAME>'
PASSWORD = '<PASSWORD>'
response = requests.post(BASE_URL + AUTH_URL, auth=HTTPBasicAuth(USERNAME, PASSWORD), verify=False)
token = response.json()['Token']
With the token, we can procede to query the devices API to get the device ID of CSR1Kv-03.devnet.local
:
# Get device
headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'}
query_params = {
'hostname': 'CSR1Kv-03.devnet.local'
}
DEVICES_URL = '/dna/intent/api/v1/network-device'
response = requests.get(BASE_URL + DEVICES_URL,
params=query_params,
headers=headers, verify=False)
device_id = response.json()['response'][0]['id']
Image importation
The developer can use this endpoint to import an image, using HTTP or FTP, to Cisco DNA Center.
# Print software images
SOFTWARE_IMAGE_IMPORT_URL = '/dna/intent/api/v1/image/importation/source/url'
import_info = {
"sourceURL": "http://10.104.49.64/cat3k_caa_universalk9.16.12.03a.SPA.bin"
}
response = requests.post(BASE_URL + SOFTWARE_IMAGE_IMPORT_URL,
json=import_info,
headers=headers, verify=False)
image_id = response.json()['response']
Image details
With this endpoint, it is possible to get the information available on Cisco DNA Center. In this case, we are filtering the results based on the family of the image.
# Print software images
SOFTWARE_IMAGE_URL='/dna/intent/api/v1/image/importation'
query_params ={
'family': 'cat3k'
}
response = requests.get(BASE_URL + SOFTWARE_IMAGE_URL,
params=query_params,
headers=headers, verify=False)
image_id = response.json()['response'][0]['imageUuid']
Image Distribution
After getting the image, it is possible to distribute to the devices on the network, but first it is needed for the user to set the image as golden using the web interface.
For the image distribution it is needed to have the device ID and the image ID. This is an asynchronous API so in order to obtain the result, it is needed to query the task API.
SOFTWARE_IMAGE_DISTRIBUTION_URL = '/dna/intent/api/v1/image/distribution'
TASK_BY_ID_URL = '/dna/intent/api/v1/task/{task_id}'
distribution_info = [
{
'deviceUuid': device_id,
'imageUuid': image_id
}
]
response = requests.post(BASE_URL + SOFTWARE_IMAGE_DISTRIBUTION_URL,
json=distribution_info,
headers=headers, verify=False)
task_id = response.json()['response']['taskId']
time.sleep(10)
response = requests.get(BASE_URL + TASK_BY_ID_URL.format(task_id=task_id),
headers=headers, verify=False)
print(response.json()['response']['data'])
Image activation
Finally, after the image has been distributed, using the API it is possible to ask the device to activate the image. This is also an asynchronous API hence the result needs to be queried from the task API.
SOFTWARE_IMAGE_ACTIVATION_URL = '/dna/intent/api/v1/image/activation/device'
activate_info = [
{
'deviceUuid': device_id,
'imageUuidList': [
image_id
]
}
]
response = requests.post(BASE_URL + SOFTWARE_IMAGE_ACTIVATION_URL,
json=activation_info,
headers=headers, verify=False)
task_id = response.json()['response']['taskId']
time.sleep(10)
response = requests.get(BASE_URL + TASK_BY_ID_URL.format(task_id=task_id),
headers=headers, verify=False)
print(response.json()['response']['data'])
Code
The repository for this guide is here. The final code with functions is shown below.
# Modules import
import requests
from requests.auth import HTTPBasicAuth
import time
# Disable SSL warnings. Not needed in production environments with valid certificates
import urllib3
urllib3.disable_warnings()
# Authentication
BASE_URL = 'https://<IP Address>'
AUTH_URL = '/dna/system/api/v1/auth/token'
USERNAME = '<USERNAME>'
PASSWORD = '<PASSWORD>'
# URLs
DEVICES_URL = '/dna/intent/api/v1/network-device'
SOFTWARE_IMAGE_URL='/dna/intent/api/v1/image/importation'
SOFTWARE_IMAGE_IMPORT_URL = '/dna/intent/api/v1/image/importation/source/url'
SOFTWARE_IMAGE_DISTRIBUTION_URL = '/dna/intent/api/v1/image/distribution'
SOFTWARE_IMAGE_ACTIVATION_URL = '/dna/intent/api/v1/image/activation/device'
TASK_BY_ID_URL = '/dna/intent/api/v1/task/{task_id}'
# Get Authentication token
def get_dnac_jwt_token():
response = requests.post(BASE_URL + AUTH_URL,
auth=HTTPBasicAuth(USERNAME, PASSWORD),
verify=False)
token = response.json()['Token']
return token
# Import software image
def import_image(headers, import_info):
response = requests.post(BASE_URL + SOFTWARE_IMAGE_IMPORT_URL,
json=import_info,
headers=headers, verify=False)
return response.json()['response']
# Get Software Images
def get_software_images(headers, query_params):
response = requests.get(BASE_URL + SOFTWARE_IMAGE_URL,
params=query_params,
headers=headers, verify=False)
return response.json()['response']
# Get devices
def get_devices(headers, query_params):
response = requests.get(BASE_URL + DEVICES_URL,
params=query_params,
headers=headers, verify=False)
return response.json()['response']
# Distribute image
def distribute_image(headers, distribution_info):
response = requests.post(BASE_URL + SOFTWARE_IMAGE_DISTRIBUTION_URL,
json=distribution_info,
headers=headers, verify=False)
return response.json()['response']
# Activate image
def activate_image(headers, activation_info):
response = requests.post(BASE_URL + SOFTWARE_IMAGE_ACTIVATION_URL,
json=activation_info,
headers=headers, verify=False)
return response.json()['response']
# Get Task result
def get_task(headers, task_id):
response = requests.get(BASE_URL + TASK_BY_ID_URL.format(task_id=task_id),
headers=headers, verify=False)
return response.json()['response']
def main():
# obtain the Cisco DNA Center Auth Token
token = get_dnac_jwt_token()
headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'}
# Import image
import_info = {
"sourceURL": "http://10.104.49.64/cat3k_caa_universalk9.16.12.03a.SPA.bin"
}
response = import_image(headers, import_info)
time.sleep(30)
# Get software images
query_params ={
'family': 'cat3k'
}
response = get_software_images(headers, query_params)
image_id = response[0]['imageUuid']
# Get device
query_params = {
'hostname': 'CAT3K-03.devnet.local'
}
response = get_devices(headers, query_params)
device_id = response[0]['id']
distribution_info = [
{
'deviceUuid': device_id,
'imageUuid': image_id
}
]
response = distribute_image(headers, distribution_info)
task_id = response['taskId']
time.sleep(10)
response = get_task(headers, task_id)
print(response['data'])
activate_info = [
{
'deviceUuid': device_id,
'imageUuidList': [
image_id
]
}
]
response = activate_image(headers, activate_info)
task_id = response['taskId']
time.sleep(10)
response = get_task(headers, task_id)
print(response['data'])
if __name__ == "__main__":
main()