Discovery guide

Introduction

Discovery API allows the user to execute a discovery of network devices on the infrastructure using different techniques like IP Range, CDP, etc. Also, it can be done with SNMP, SSH, NETCONF, amongst others.

Leveraging Discovery API, the user can quickly get its devices into Cisco DNA Center and perform operations related to them.

Goal

The goals of this guide are:

  1. Create an area
  2. Create a building
  3. Define credentials for the discovery
  4. Execute a network discovery
  5. Get the discovery results
  6. Assign the newly discovered devices to the recently created building

Discovery workflow

Endpoints and methods used

  • POST /dna/system/api/v1/auth/token
  • POST /dna/intent/api/v1/site
  • POST /dna/intent/api/v1/global-credential/cli
  • POST /dna/intent/api/v1/discovery
  • GET /dna/intent/api/v1/task/{task_id}
  • GET /dna/intent/api/v1/discovery/{discovery_id}/network-device
  • POST /dna/system/api/v1/site/{site_id}/device

Prerequisites

For this guide, it is recommended that the developer is familiar with authenticating to Cisco DNA Center API and with the SITE API.

Environment

This guide was developed using:

Discovery API

The Discovery API is composed of 34 endpoints, that consist of credentials management and actual discovery process endpoints.

It can be used to get the network devices under the management of Cisco DNA Center in an automated way, without the user having to add every single device manually.

It supports different discovery options, like IP Range or CDP, plus different set of credentials for the discovery process: SNMP, CLI (SSH/Telnet) and/or NETCONF, amongsts others.

For this guide, we will execute a discovery but also create a site and assign the discovered devices to the site:

  1. Authenticate against the DNA Center API
  2. Create an area and then a building using Site API
  3. Create the credentials to be used for the discovery
  4. Trigger the discovery process
  5. Get a list of discovered devices
  6. Assigned the recently discovered devices to the building created in step 2

Create site area and building

First, we need to authenticate and retrieve a token from the API.

Do not use verify=False or urllib3.disable_warnings() if you are not sure of its purpose. Read Authentication and Authorization.

import requests
from requests.auth import HTTPBasicAuth
import time
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']
headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'}

With the token, we can procede to create the area and building. We will need the siteId of the building to be used after the discovery to assign the devices to that building.

SITE_URL = '/dna/intent/api/v1/site'
site_area = {
    "type": "area",
    "site": {
        "area": {
            "name": "DNA Center Guide",
            "parentName": "Global"
        }
    }
}

site_headers = headers
site_headers['__runsync'] = 'true'
site_headers['__runsynctimeout'] = '30'
response = requests.post(BASE_URL + SITE_URL,
                         headers=site_headers, json=site_area,
                         verify=False)
area_id = response.json()['siteId']

site_building = {
    "type": "building",
    "site": {
        "building": {
            "name": "DNA Center Guide Building",
            "parentName": "Global/DNA Center Guide",
            "latitude": "37.409424",
            "longitude": "-121.928868"
        }
    }
}

response = requests.post(BASE_URL + SITE_URL,
                         headers=site_headers, json=site_building,
                         verify=False)

Global Credentials API

The discovery process can be executed with different options, like SNMP, SSH or NETCONF, and the credentials can be per site, just for the discovery or global. In this case we will use the global credentials API to create a set of SSH credentials to be used for the discovery process.

The global credentials API is part of the Discovery API.

We will need the credential id to be used for the discovery process.

CREDENTIALS_URL = '/dna/intent/api/v1/global-credential/cli'
TASK_BY_ID_URL = '/dna/intent/api/v1/task/{task_id}'
credentials = [
    {
        'description': 'DNA Center Guide Credentials',
        'enablePassword': 'C!scow02',
        'password': 'C!scow02',
        'username': 'devnet',
        'credentialType': 'GLOBAL'
    }
]

response = requests.post(BASE_URL + CREDENTIALS_URL, headers=headers,
                        json=credentials, verify=False)

task_id = response.json()['taskId']

time.sleep(5)

response = requests.get(BASE_URL + TASK_BY_ID_URL.format(task_id=task_id),
                        headers=headers, verify=False)
credential_id = response.json()['progress']

Trigger Discovery

Continuing with the progress from the last section, we have the credential_id variable with the information of the credentials we will use for this discovery.

We will need the discovery_id to query which devices were discovered so we can assign them to a site.

DISCOVERY_URL = '/dna/intent/api/v1/discovery'
discovery = {
    "name": "Discovery-Guide",
    "discoveryType": "Range",
    "ipAddressList": "10.255.3.11-10.255.3.19",
    "protocolOrder": "ssh",
    "timeOut": 5,
    "retryCount": 3,
    "isAutoCdp": False,
    "globalCredentialIdList": [
        credential_id
    ]
}

response = requests.post(BASE_URL + DISCOVERY_URL, headers=headers,
                        json=discovery, 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)
discovery_id = response['progress']

Get Discovered Devices

Once we executed a discovery, we will use the information of the discovery_id to query which devices were discovered and use the list of devices to assign them to the building we created earlier.

DISCOVERY_DEVICES_URL = '/dna/intent/api/v1/discovery/{discovery_id}/network-device'
response = requests.get(BASE_URL + DISCOVERY_DEVICES_URL.format(discovery_id=discovery_id),
                        headers=headers, verify=False)
device_ips = []
for device in response.json()['response']:
    device_ips.append({'ip': device['managementIpAddress']})

Site Assigning API

Finally, as part of the Site API, we have an endpoint that allows the user to assign devices to a site.

We would use the previously obtained building_id and device_ips to assign the recently discovered devices to the DNA Center Guide Building we created at the start of the guide.

SITE_DEVICE_URL = '/dna/system/api/v1/site/{site_id}/device'
site_devices = {
    'device': device_ips
}
response = requests.post(BASE_URL + SITE_DEVICE_URL.format(site_id=site_id),
                            headers=headers, json=devices,
                            verify=False)

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
import sys

# Disable SSL warnings. Not needed in production environments with valid certificates
import urllib3
urllib3.disable_warnings()

# Authentication
BASE_URL = 'https://10.10.10.181'
AUTH_URL = '/api/system/v1/auth/token'
USERNAME = 'altus'
PASSWORD = 'x6n7j4sMoojWMp0Wm/@E'

# URLs
CREDENTIALS_URL = '/dna/intent/api/v1/global-credential/cli'
DISCOVERY_URL = '/dna/intent/api/v1/discovery'
TASK_BY_ID_URL = '/dna/intent/api/v1/task/{task_id}'
SITE_URL = '/dna/intent/api/v1/site'
DISCOVERY_DEVICES_URL = '/dna/intent/api/v1/discovery/{discovery_id}/network-device'
SITE_DEVICE_URL = '/dna/system/api/v1/site/{site_id}/device'

# 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

# Create site
def create_site(headers, site):
    headers['__runsync'] = 'true'
    headers['__runsynctimeout'] = '30'
    response = requests.post(BASE_URL + SITE_URL,
                             headers=headers, json=site,
                             verify=False)
    return response.json()

# Add devices to site
def add_devices_site(headers, site_id, devices):
    headers['__runsync'] = 'true'
    headers['__runsynctimeout'] = '30'
    response = requests.post(BASE_URL + SITE_DEVICE_URL.format(site_id=site_id),
                             headers=headers, json=devices,
                             verify=False)
    return response.json()


def create_credentials(headers, credentials):
    response = requests.post(BASE_URL + CREDENTIALS_URL, headers=headers,
                            json=credentials, verify=False)
    return response.json()['response']

def create_discovery(headers, discovery):
    response = requests.post(BASE_URL + DISCOVERY_URL, headers=headers,
                            json=discovery, verify=False)
    return response.json()['response']

# Get devices from discovery
def get_discovery_devices(headers, discovery_id):
    response = requests.get(BASE_URL + DISCOVERY_DEVICES_URL.format(discovery_id=discovery_id),
                            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'}

    site_area = {
        "type": "area",
        "site": {
            "area": {
                "name": "DNA Center Guide",
                "parentName": "Global"
            }
        }
    }

    print('Printing area ID...')
    response = create_site(headers, site_area)
    area_id = response['siteId']
    print(area_id)

    site_building = {
        "type": "building",
        "site": {
            "building": {
                "name": "DNA Center Guide Building",
                "parentName": "Global/DNA Center Guide",
                "latitude": "37.409424",
                "longitude": "-121.928868"
            }
        }
    }

    print('\nPrinting building ID...')
    response = create_site(headers, site_building)
    building_id = response['siteId']
    print(building_id)

    credentials = [
        {
            'description': 'DNA Center Guide Credentials',
            'enablePassword': 'C!scow02',
            'password': 'C!scow02',
            'username': 'devnet',
            'credentialType': 'GLOBAL'
        }
    ]
    response = create_credentials(headers, credentials)
    task_id = response['taskId']

    time.sleep(5)

    print('\nPrinting credential id...')
    response = get_task(headers, task_id)
    credential_id = response['progress']
    print(credential_id)

    discovery = {
        "name": "Discovery-Guide",
        "discoveryType": "Range",
        "ipAddressList": "10.255.3.11-10.255.3.19",
        "protocolOrder": "ssh",
        "timeOut": 5,
        "retryCount": 3,
        "isAutoCdp": False,
        "globalCredentialIdList": [
            credential_id
        ]
    }
    response = create_discovery(headers, discovery)
    task_id = response['taskId']

    print('\nWaiting 10 seconds for discovery to be created...')
    time.sleep(10)

    print('\nPrinting discovery id')
    response = get_task(headers, task_id)
    discovery_id = response['progress']
    print(discovery_id)

    print('\nWaiting 30 seconds for discovery to end...')
    time.sleep(30)
    response = get_discovery_devices(headers, discovery_id)
    device_ips = []
    for device in response:
        device_ips.append({'ip': device['managementIpAddress']})

    site_devices = {
        'device': device_ips
    }

    print('\nPrinting device inclusion in site...')
    response = add_devices_site(headers, building_id, site_devices)
    print(response['result']['progress'])


if __name__ == "__main__":
    main()