Path trace guide

Introduction

Path trace is the feature in Cisco DNA Center that allows you to execute a trace between two devices in your network. It is possible to get device and interface statistics, QoS details and/or ACLs traces and do it using different ports and protocols.

It can be used to troubleshoot network problems like packet loss, traffic not reaching its destination because of ACLs or routing issues.

Goal

The goals of this guide are:

  1. Retrieve a list of previously executed path traces
  2. Execute a path trace
  3. Obtain the path trace result
  4. Delete the path trace

Path trace workflow

Endpoints and methods used

  • POST /dna/system/api/v1/auth/token
  • GET /dna/intent/api/v1/network-device
  • GET /dna/intent/api/v1/flow-analysis
  • POST /dna/intent/api/v1/flow-analysis
  • GET /dna/intent/api/v1/flow-analysis/{flowAnalysisId}
  • DELETE /dna/intent/api/v1/flow-analysis/{flowAnalysisId}

Prerequisites

For this guide, it is recommended that the developer is familiar with authenticating and obtaining the device ID of the target devices.

Environment

This guide was developed using:

Authentication

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 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'}

Get Devices' IPs

With the token, we can procede to query the devices API using the hostnames of the two devices we want to use for the path trace.

headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'}
DEVICES_URL = '/dna/intent/api/v1/network-device'
src_ip_address = ''
query_string_params = {'hostname': 'CSR1Kv-01.devnet.local'}
response = requests.get(BASE_URL + DEVICES_URL, headers = headers, params=query_string_params, verify=False)
src_ip_address = response.json()['response'][0]['managementIpAddress']

dst_ip_address = ''
query_string_params = {'hostname': 'CSR1Kv-09.devnet.local'}
response = requests.get(BASE_URL + DEVICES_URL, headers = headers, params=query_string_params, verify=False)
dst_ip_address = response.json()['response'][0]['managementIpAddress']

Path Trace API

The Path Trace API is composed of four endpoints, used to create a path trace, get the result, delete it or get a summary of all path traces in the system.

This is the API that enables an user to execute a path trace between two devices known by Cisco DNA Center and Path trace is an asynchronous API, so the result of the execution is not obtained immediately. Check the async guide for mor information.

Generate a new Path trace

With the IPs, we can create a new path trace. Source and destination IPs are mandatory. It is possible to ask for more details using the inclusions list with one or several of the following options:

  • INTERFACE-STATS
  • DEVICE-STATS
  • ACL-TRACE
  • QOS-STATS

The following snippet shows the path trace creation:

PATH_TRACE_URL = '/dna/intent/api/v1/flow-analysis'
path_trace_payload = {
    'sourceIP': src_ip_address,
    'destIP': dst_ip_address,
    'inclusions': [
        'INTERFACE-STATS',
        'DEVICE-STATS',
        'ACL-TRACE',
        'QOS-STATS'
        ],
    'protocol': 'icmp'
}
response = requests.post(BASE_URL + PATH_TRACE_URL, headers=headers, json=path_trace_payload, verify=False)
flow_analysis_id = response.json()['response']['flowAnalysisId']

Get path trace result

The result of the path trace needs to be queried from the API:

PATH_TRACE_ID_URL = '/dna/intent/api/v1/flow-analysis/{flow_analysis_id}'
response = requests.get(BASE_URL + PATH_TRACE_ID_URL.format(flow_analysis_id=flow_analysis_id), headers=headers, verify=False)
print(response.json['response'])

Get path trace summary

It is also possible to get a summary of all the path traces in the system, filtered by different parameters for example destination IP address.

query_string_params = {'destIP': dst_ip_address}
response = requests.get(BASE_URL + PATH_TRACE_URL, params=query_string_params, headers=headers, verify=False)
print(response.json())

Delete path trace

Finally, we can delete our path trace if we want to.

response = requests.delete(BASE_URL + PATH_TRACE_ID_URL.format(flow_analysis_id=flow_analysis_id), headers=headers, verify=False)

Code

The repository for this guide is here.

# Modules import
import requests
from requests.auth import HTTPBasicAuth
import sys
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'
PATH_TRACE_URL = '/dna/intent/api/v1/flow-analysis'
PATH_TRACE_ID_URL = '/dna/intent/api/v1/flow-analysis/{flow_analysis_id}'

def get_dnac_jwt_token():
    response = requests.post(BASE_URL + AUTH_URL,
                             auth=HTTPBasicAuth(USERNAME, PASSWORD),
                             verify=False)
    token = response.json()['Token']
    return token

# Get list of devices
def get_devices(headers, query_string_params):
    response = requests.get(BASE_URL + DEVICES_URL,
                            headers = headers,
                            params = query_string_params,
                            verify=False)
    return response.json()['response']

# Create path trace
def create_path_trace(headers, path_trace_payload):
    response = requests.post(BASE_URL + PATH_TRACE_URL, headers=headers,
                             json=path_trace_payload, verify=False)
    return response.json()['response']

# Get path trace result
def get_path_trace_by_id(headers, flow_analysis_id):
    response = requests.get(BASE_URL + PATH_TRACE_ID_URL.format(flow_analysis_id=flow_analysis_id),
                            headers=headers, verify=False)
    return response.json()['response']

# Get path trace summary
def get_path_traces_summary(headers, query_string_params):
    response = requests.get(BASE_URL + PATH_TRACE_URL,
                            params=query_string_params,
                            headers=headers, verify=False)
    return response.json()['response']

# Delete path trace
def delete_path_trace(headers, flow_analysis_id):
    response = requests.delete(BASE_URL + PATH_TRACE_ID_URL.format(flow_analysis_id=flow_analysis_id),
                               headers=headers, verify=False)
    return response

def main():
    # obtain the Cisco DNA Center Auth Token
    token = get_dnac_jwt_token()
    headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'}

    # Get src device IP
    print('Printing source device IP ...')
    query_string_params = {'hostname': 'CSR1Kv-01.devnet.local'}
    response = get_devices(headers, query_string_params)
    src_ip_address = response[0]['managementIpAddress']
    print(src_ip_address)

    # print devices list
    print('\nPrinting destination device IP ...')
    query_string_params = {'hostname': 'CSR1Kv-09.devnet.local'}
    response = get_devices(headers, query_string_params)
    dst_ip_address = response[0]['managementIpAddress']
    print(dst_ip_address)

    # Generate a new path trace
    print('\nPrinting flow analysis id ...')
    path_trace_payload = {
        'sourceIP': src_ip_address,
        'destIP': dst_ip_address,
        'inclusions': [
            'INTERFACE-STATS',
            'DEVICE-STATS',
            'ACL-TRACE',
            'QOS-STATS'
        ],
        'protocol': 'icmp'
    }
    response = create_path_trace(headers, path_trace_payload)
    flow_analysis_id = response['flowAnalysisId']
    print(flow_analysis_id)

    # Waiting until the path trace is done
    time.sleep(10)

    # Get path trace result
    print('\nPrinting path trace result ...')
    response = get_path_trace_by_id(headers, flow_analysis_id)
    print(response)

    # Get path traces summary
    print('\nPrinting path trace summary...')
    query_string_params = {'destIP': dst_ip_address, 'limit': 2}
    response = get_path_traces_summary(headers,  query_string_params)
    print(response)

    # Delete path trace summary
    print('\nPrinting path trace delete status code ...')
    response = delete_path_trace(headers, flow_analysis_id)
    print(response.status_code)


if __name__ == "__main__":
    main()