Using the Cisco DNA Center SDK CLI Tool

Find previous blogs in this Cisco DNA Center blog series.

API flexibility, with Linux shell CLI convenience

Many of my blogs on the Cisco DNA Center API have been focused on Python scripts, most recently with the newly released SDK.  Thanks to Jose Bogarin and the team at Altus there is now a CLI wrapper for the SDK.

Why CLI, I hear you ask?  CLI tools are really flexible and powerful. The whole concept of the Linux shell is based around a powerful set of tools that can be linked together to perform a task. Rather than needing to build a new tool from scratch (writing code), I can solve my problem linking smaller existing tools together.

The DNA Center CLI tools provide all the flexibility of the API with the convenience of  Linux shell tools.

Installing

Both the  Cisco DNA Center SDK and CLI are available via PyPI, so all that is required is “pip install”

I would recommend using a virtual environment. This is optional, but means you do not require root access and helps keep different versions of Python libraries separate.   Once created, it needs to be activated, using the “source” command.

If you logout and back in, activation needs to be repeated.

python3 -m venv env3
source env3/bin/activate

To install, you just need to install the cli as dnacentersdk is a dependency.

pip install dnacentercli

You are now able to use the CLI tool.

Getting Started

If you just run the cli tool without any arguments, you will get a help message.  I have truncated for brevity

$ dnacentercli
Usage: dnacentercli [OPTIONS] COMMAND [ARGS]...

DNA Center API wrapper.

DNACenterAPI wraps all of the individual DNA Center APIs and represents
them in a simple hierarchical structure.

Options:
--help Show this message and exit.
<SNIP>

You will need to provide credentials and potentially the URL for your DNA Center. In this example I am going to use environment variables to simplify the username and password. I am also going to use the default DNAC, sandboxdnac2.cisco.com. You can change this to use your own DNA Center.

export DNA_CENTER_USERNAME='devnetuser'
export DNA_CENTER_PASSWORD='Cisco123!'
export DNA_CENTER_BASE_URL='https://sandboxdnac2.cisco.com'

# optional needs to be False if self signed certificate
export DNA_CENTER_VERIFY="True"

# optional. This is the default
export DNA_CENTER_VERSION="1.3.0"

To get a count of the number of network devices, I could use the following:

$ dnacentercli devices get-device-count
{"response": 14,"version": "1.0"}

The structure of the CLI follows the DNA Center SDK. To find out the valid suboptions for devices, simply use –help or run without and suboption. (I have truncated for brevity).

$ dnacentercli devices
Usage: dnacentercli devices [OPTIONS] COMMAND [ARGS]...

DNA Center Devices API (version: 1.3.0).

Wraps the DNA Center Devices API and exposes the API as native Python
commands.

Options:
--help Show this message and exit.

Commands:
add-device Adds the device with given...
delete-device-by-id Deletes the network device for...
export-device-list Exports the selected network...
get-all-interfaces Returns all available...
get-device-by-id Returns the network device...
get-device-by-serial-number Returns the network device with...
get-device-config-by-id Returns the device config by...
get-device-config-count Returns the count of device...
get-device-config-for-all-devices
Returns the config for all...
<SNIP>

You can also format the JSON output with the -pp option. It requires an argument to indicate the level of indentation. For example (truncated for brevity):

$ dnacentercli devices get-device-list -pp 2
{
"response": [
{
"apManagerInterfaceIp": "",
"associatedWlcIp": "",
"bootDateTime": "2019-01-19 02:33:05",
"collectionInterval": "Global Default",

<SNIP>

Advanced Use Cases

One common requirement is to get a csv file of the devices in the inventory. The JSON output can be processed with the jq command. jq is a really useful utility that can parse JSON fields. For example, getting a csv file of the DNAC inventory with specific fields.   In the example below, jq is looking at each of the list entries in the response, and extracting the hostname, managementIpAddress, softwareVersion, and id fields.  It then formats them as a csv.  The “-r” option for jq, provides raw output.

$ dnacentercli v1-3-0 devices get-device-list | jq -r '.response[] | [.hostname, .managementIpAddress, .softwareVersion, .id] |@csv'
"3504_WLC","10.10.20.51","8.5.140.0","50c96308-84b5-43dc-ad68-cda146d80290"
"leaf1.labb.local","10.10.20.81","16.6.4a","6a49c827-9b28-490b-8df0-8b6c3b582d8a"
"leaf2.labb.local","10.10.20.82","16.6.4a","d101ef07-b508-4cc9-bfe3-2acf7e8a1015"
"spine1.abc.in","10.10.20.80","16.3.5b","b558bdcc-6835-4420-bfe8-26efa3fcf0b9"
"T1-1","10.10.20.241","8.6.101.0","8cd186fc-f86e-4123-86ed-fe2b2a41e3fc"
"T1-10","10.10.20.250","8.6.101.0","a2168b2d-adef-4589-b3b5-2add5f37daeb"
"T1-2","10.10.20.242","8.6.101.0","0367820f-3aa4-434a-902f-9bd39a8bcd21"
"T1-3","10.10.20.243","8.6.101.0","8336ae01-e1a8-47ea-b0bf-68c83618de9e"
"T1-4","10.10.20.244","8.6.101.0","b65cea84-b0c2-4c44-a2e8-1668460bd876"
"T1-5","10.10.20.245","8.6.101.0","0aafed14-666b-4f9d-a172-6f169798631a"
"T1-6","10.10.20.246","8.6.101.0","e641ce97-dbba-4024-b64c-2f88620bcc23"
"T1-7","10.10.20.247","8.6.101.0","3aaffd4f-0638-4a54-b242-1533e87de9a7"
"T1-8","10.10.20.248","8.6.101.0","a4e0a3ab-de5f-4ee2-822d-a5437b3eaf49"
"T1-9","10.10.20.249","8.6.101.0","10cdbf6d-3672-4b4d-ae75-5b661fa0a5bc"

One big advantage of CLI tools is linking them together with standard Linux tools such as xargs. This provides a way to run a command based on a set of arguments. For example if I wanted to get a dump of all of the configuration files for devices, I would need to call the “get-device-config-by-id” API for each device, giving it an argument of a device UUID (as seen above in the id field). To start with I am just going to get the device ID.

I am choosing switches as the Access Points do not have a configuration (the configuration for AP is stored on the Wireless LAN controller).

$ dnacentercli devices get-device-list --family "Switches and Hubs"| jq -r '.response[] | .id | @text '
6a49c827-9b28-490b-8df0-8b6c3b582d8a
d101ef07-b508-4cc9-bfe3-2acf7e8a1015
b558bdcc-6835-4420-bfe8-26efa3fcf0b9

Next I use the xargs command to run request for the configuration with each of the id as an argument. The cli command is dnacentercli devices get-device-config-by-id –network_device_id

The xargs command creates an instance of get-device-config-by-id for each id.
The output is quite long so I have truncated it.

$ dnacentercli devices get-device-list --family "Switches and Hubs"| jq -r '.response[] | .id | @text ' | xargs -n1 dnacentercli devices get-device-config-by-id --network_device_id
{"response": "\nBuilding configuration...\n\nCurrent configuration : 18816 bytes\n!\n! Last configuration change at 02:02:05 UTC Sat Aug 31 2019 by admin\n!\nversion 16.6\nno service pad\nservice

This is an example of deleting a device with the ip address 10.10.15.200. Of course i could turn this into a script or a shell macro, and provide the IP address as an argument. I can either run a seperate command to get the status, or i could link it yet again. I am going to use a separate command in this case.

$ dnacentercli devices get-network-device-by-ip --ip_address 10.10.15.200 | jq -r '.response.id | @text ' | xargs dnacentercli devices delete-device-by-id --id
{"response": {"taskId": "cc8b8c29-98cd-4cd4-8c6c-eee96af31057","url": "/api/v1/task/cc8b8c29-98cd-4cd4-8c6c-eee96af31057"},"version": "1.0"}

$ dnacentercli task get-task-by-id --task_id cc8b8c29-98cd-4cd4-8c6c-eee96af31057 -pp 2
{
"response": {
"endTime": 1570147128225,
"id": "cc8b8c29-98cd-4cd4-8c6c-eee96af31057",
"instanceTenantId": "5d817bf369136f00c74cb23b",
"isError": false,
"lastUpdate": 1570147117840,
"progress": "Network device deleted successfully",
"rootId": "cc8b8c29-98cd-4cd4-8c6c-eee96af31057",
"serviceType": "Inventory service",
"startTime": 1570147117759,
"version": 1570147117840
},
"version": "1.0"
}

The final example is using the CLI to create a site. –type and –area are required arguments.   As this is a POST, we can force the call to run synchronously by passing the __runsync header.  You can see the site was successfully created.

$ dnacentercli sites create-site --type "area" --site '{ "area" : { "name":"Adam","parentName":"Global"}}' --headers '{"__runsync" : true }'
{"result": {"result": {"endTime": 1570132102038,"progress": "Site Creation completed successfully","startTime": 1570132101935}},"siteId": "2ea3f4c2-04e2-4d01-8c12-4459c1e7a2c1","status": "True"}

Want to know more about Cisco DNA Center?

You can find more blogs in this series on other aspects of Cisco DNA Center.

Visit DevNet’s Cisco DNA Center website for additional information and explanations about Cisco DNA Center topics.

Thanks for reading.

@adamradford123