├── .gitignore ├── LICENSE ├── README.md ├── authentication ├── README.md ├── authentication-functions.py └── authentication-sdk.py ├── command-runner ├── README.md ├── command-runner-functions.py ├── command-runner-playbook.yml └── command-runner-sdk.py ├── credentials ├── README.md ├── credentials-functions.py ├── credentials-playbook.yml └── credentials-sdk.py ├── device-onboarding ├── README.md └── device-onboarding-functions.py ├── device-provisioning ├── README.md └── device-provisioning-functions.py ├── devices ├── README.md ├── devices-functions.py ├── devices-playbook.yml └── devices-sdk.py ├── discovery ├── README.md ├── discovery-functions.py ├── discovery-playbook.yml └── discovery-sdk.py ├── global-ip-pool ├── README.md └── global-ip-pool-functions.py ├── health-monitoring ├── README.md ├── health-monitoring-functions.py ├── health-monitoring-playbook.yml └── health-monitoring-sdk.py ├── hosts ├── path-trace ├── README.md ├── path-trace-functions.py ├── path-trace-playbook.yml └── path-trace-sdk.py ├── rma ├── README.md └── rma-functions.py ├── sda ├── README.md └── sda-functions.py ├── sites ├── README.md ├── sites-functions.py ├── sites-playbook.yml └── sites-sdk.py ├── swim ├── README.md ├── swim-functions.py ├── swim-playbook.yml └── swim-sdk.py └── topology ├── README.md ├── topology-functions.py ├── topology-playbook.yml └── topology-sdk.py /.gitignore: -------------------------------------------------------------------------------- 1 | pyproject.toml 2 | poetry.lock 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CISCO SAMPLE CODE LICENSE 2 | Version 1.1 3 | Copyright (c) 2018 Cisco and/or its affiliates 4 | 5 | These terms govern this Cisco Systems, Inc. ("Cisco"), example or demo 6 | source code and its associated documentation (together, the "Sample 7 | Code"). By downloading, copying, modifying, compiling, or redistributing 8 | the Sample Code, you accept and agree to be bound by the following terms 9 | and conditions (the "License"). If you are accepting the License on 10 | behalf of an entity, you represent that you have the authority to do so 11 | (either you or the entity, "you"). Sample Code is not supported by Cisco 12 | TAC and is not tested for quality or performance. This is your only 13 | license to the Sample Code and all rights not expressly granted are 14 | reserved. 15 | 16 | 1. LICENSE GRANT: Subject to the terms and conditions of this License, 17 | Cisco hereby grants to you a perpetual, worldwide, non-exclusive, non- 18 | transferable, non-sublicensable, royalty-free license to copy and 19 | modify the Sample Code in source code form, and compile and 20 | redistribute the Sample Code in binary/object code or other executable 21 | forms, in whole or in part, solely for use with Cisco products and 22 | services. For interpreted languages like Java and Python, the 23 | executable form of the software may include source code and 24 | compilation is not required. 25 | 26 | 2. CONDITIONS: You shall not use the Sample Code independent of, or to 27 | replicate or compete with, a Cisco product or service. Cisco products 28 | and services are licensed under their own separate terms and you shall 29 | not use the Sample Code in any way that violates or is inconsistent 30 | with those terms (for more information, please visit: 31 | www.cisco.com/go/terms). 32 | 33 | 3. OWNERSHIP: Cisco retains sole and exclusive ownership of the Sample 34 | Code, including all intellectual property rights therein, except with 35 | respect to any third-party material that may be used in or by the 36 | Sample Code. Any such third-party material is licensed under its own 37 | separate terms (such as an open source license) and all use must be in 38 | full accordance with the applicable license. This License does not 39 | grant you permission to use any trade names, trademarks, service 40 | marks, or product names of Cisco. If you provide any feedback to Cisco 41 | regarding the Sample Code, you agree that Cisco, its partners, and its 42 | customers shall be free to use and incorporate such feedback into the 43 | Sample Code, and Cisco products and services, for any purpose, and 44 | without restriction, payment, or additional consideration of any kind. 45 | If you initiate or participate in any litigation against Cisco, its 46 | partners, or its customers (including cross-claims and counter-claims) 47 | alleging that the Sample Code and/or its use infringe any patent, 48 | copyright, or other intellectual property right, then all rights 49 | granted to you under this License shall terminate immediately without 50 | notice. 51 | 52 | 4. LIMITATION OF LIABILITY: CISCO SHALL HAVE NO LIABILITY IN CONNECTION 53 | WITH OR RELATING TO THIS LICENSE OR USE OF THE SAMPLE CODE, FOR 54 | DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO DIRECT, INCIDENTAL, 55 | AND CONSEQUENTIAL DAMAGES, OR FOR ANY LOSS OF USE, DATA, INFORMATION, 56 | PROFITS, BUSINESS, OR GOODWILL, HOWEVER CAUSED, EVEN IF ADVISED OF THE 57 | POSSIBILITY OF SUCH DAMAGES. 58 | 59 | 5. DISCLAIMER OF WARRANTY: SAMPLE CODE IS INTENDED FOR EXAMPLE PURPOSES 60 | ONLY AND IS PROVIDED BY CISCO "AS IS" WITH ALL FAULTS AND WITHOUT 61 | WARRANTY OR SUPPORT OF ANY KIND. TO THE MAXIMUM EXTENT PERMITTED BY 62 | LAW, ALL EXPRESS AND IMPLIED CONDITIONS, REPRESENTATIONS, AND 63 | WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OR 64 | CONDITION OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON- 65 | INFRINGEMENT, SATISFACTORY QUALITY, NON-INTERFERENCE, AND ACCURACY, 66 | ARE HEREBY EXCLUDED AND EXPRESSLY DISCLAIMED BY CISCO. CISCO DOES NOT 67 | WARRANT THAT THE SAMPLE CODE IS SUITABLE FOR PRODUCTION OR COMMERCIAL 68 | USE, WILL OPERATE PROPERLY, IS ACCURATE OR COMPLETE, OR IS WITHOUT 69 | ERROR OR DEFECT. 70 | 71 | 6. GENERAL: This License shall be governed by and interpreted in 72 | accordance with the laws of the State of California, excluding its 73 | conflict of laws provisions. You agree to comply with all applicable 74 | United States export laws, rules, and regulations. If any provision of 75 | this License is judged illegal, invalid, or otherwise unenforceable, 76 | that provision shall be severed and the rest of the License shall 77 | remain in full force and effect. No failure by Cisco to enforce any of 78 | its rights related to the Sample Code or to a breach of this License 79 | in a particular situation will act as a waiver of such rights. In the 80 | event of any inconsistencies with any other terms, this License shall 81 | take precedence. 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center guides 2 | 3 | This repository has examples for different use cases using Cisco DNA Center APIs. All of the examples have a functions file that uses the python _requests_ library and some also show how to achieve the same outcome with the [Cisco DNA Center Python SDK](https://pypi.org/project/dnacentersdk/). 4 | 5 | To use any of the examples, it is needed for the developer to change the BASE_URL, USERNAME and PASSWORD variables. 6 | 7 | ## License 8 | 9 | This project is licensed to you under the terms of the [Cisco Sample Code License](./LICENSE). 10 | -------------------------------------------------------------------------------- /authentication/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Authentication guide 2 | 3 | These scripts will return an authorization token for the user to use in subsequent requests. 4 | 5 | The token is used to authorize to Cisco DNA Center API and be able to perform actions based on that authorization. 6 | 7 | ## Functions 8 | 9 | The [functions](./authentication-functions.py) file uses python requests library to interact with the API. 10 | 11 | ## SDK 12 | 13 | The [SDK](./authentication-sdk.py) uses [Cisco DNA Center Python SDK](https://pypi.org/project/dnacentersdk/) to simplify the interaction with the API. 14 | 15 | ## License 16 | 17 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 18 | -------------------------------------------------------------------------------- /authentication/authentication-functions.py: -------------------------------------------------------------------------------- 1 | # Module import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | 5 | # Disable SSL warnings. Not needed in production environments with valid certificates 6 | import urllib3 7 | urllib3.disable_warnings() 8 | 9 | # Authentication 10 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 11 | AUTH_URL = '/dna/system/api/v1/auth/token' 12 | USERNAME = '' # Example USERNAME = 'devnetuser' 13 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 14 | 15 | print('Printing authentication token...') 16 | response = requests.post(BASE_URL + AUTH_URL, auth=HTTPBasicAuth(USERNAME, PASSWORD), verify=False) 17 | print(response.json()['Token']) -------------------------------------------------------------------------------- /authentication/authentication-sdk.py: -------------------------------------------------------------------------------- 1 | from dnacentersdk import api 2 | import urllib3 3 | urllib3.disable_warnings() 4 | 5 | def print_devices_info(devices_info): 6 | for device in devices_info.response: 7 | print(device.id, device.hostname, 8 | device.managementIpAddress) 9 | 10 | 11 | 12 | dnac = api.DNACenterAPI(username="", # Example username="devnetuser", 13 | password="", # Example password="Cisco123!", 14 | base_url="", # Example base_url="https://sandboxdnac.cisco.com 15 | version='1.3.3', 16 | verify=False) 17 | 18 | print('Printing authentication token...') 19 | print(dnac.access_token) -------------------------------------------------------------------------------- /command-runner/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Command Runner guide 2 | 3 | ## Command Runner API 4 | 5 | Command runner is the feature in Cisco DNA Center that allows you to execute commands on the devices managed by Cisco DNA Center. 6 | 7 | At the moment, command runner supports read-only commands and not configuration ones. 8 | 9 | These scripts will execute a set of commands on multiple devices using the Cisco DNA Center API. 10 | 11 | ## Functions 12 | 13 | The [functions](./command-runner-functions.py) file uses python requests library to interact with the API. 14 | 15 | ## SDK 16 | 17 | The [SDK](./command-runner-sdk.py) uses [Cisco DNA Center Python SDK](https://pypi.org/project/dnacentersdk/) to simplify the interaction with the API. 18 | 19 | ## License 20 | 21 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 22 | -------------------------------------------------------------------------------- /command-runner/command-runner-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import json 5 | import time 6 | 7 | # Disable SSL warnings. Not needed in production environments with valid certificates 8 | import urllib3 9 | urllib3.disable_warnings() 10 | 11 | # Authentication 12 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 13 | AUTH_URL = '/dna/system/api/v1/auth/token' 14 | USERNAME = '' # Example USERNAME = 'devnetuser' 15 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 16 | 17 | # URLs 18 | DEVICES_URL = '/dna/intent/api/v1/network-device' 19 | COMMAND_RUNNER_SEND_URL = '/dna/intent/api/v1/network-device-poller/cli/read-request' 20 | TASK_BY_ID_URL = '/dna/intent/api/v1/task/{task_id}' 21 | FILE_GET_BY_ID = '/dna/intent/api/v1/file/{file_id}' 22 | 23 | 24 | # Get Authentication token 25 | def get_dnac_jwt_token(): 26 | response = requests.post(BASE_URL + AUTH_URL, 27 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 28 | verify=False) 29 | token = response.json()['Token'] 30 | return token 31 | 32 | # Get devices by platform ID 33 | def get_devices_ids(headers, query_string_params): 34 | response = requests.get(BASE_URL + DEVICES_URL, headers=headers, 35 | params=query_string_params, verify=False) 36 | devices = [] 37 | for device in response.json()['response']: 38 | devices.append(device['id']) 39 | return devices 40 | 41 | # Send commands to devices 42 | def send_commands(headers, payload): 43 | response = requests.post(BASE_URL + COMMAND_RUNNER_SEND_URL, json=payload, 44 | headers=headers, verify=False) 45 | return response.json()['response'] 46 | 47 | # Get Task result 48 | def get_task(headers, task_id): 49 | response = requests.get(BASE_URL + TASK_BY_ID_URL.format(task_id=task_id), headers=headers, verify=False) 50 | return response 51 | 52 | # Get file with command results 53 | def get_file(headers, file_id): 54 | response = requests.get(BASE_URL + FILE_GET_BY_ID.format(file_id=file_id), headers=headers, verify=False) 55 | return response.json() 56 | 57 | def main(): 58 | # obtain the Cisco DNA Center Auth Token 59 | token = get_dnac_jwt_token() 60 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 61 | 62 | query_string_params = {'platformId': 'C9500-40X'} 63 | devices = get_devices_ids(headers, query_string_params) 64 | payload = { 65 | "commands": [ 66 | "show version", 67 | "show ip int brief" 68 | ], 69 | "deviceUuids": devices, 70 | "timeout": 0 71 | } 72 | 73 | response = send_commands(headers, payload) 74 | 75 | # Wait to have a response back from the devices 76 | time.sleep(10) 77 | 78 | response = get_task(headers, response['taskId']) 79 | progress_json = json.loads(response.json()['response']['progress']) 80 | file_id = progress_json['fileId'] 81 | 82 | # Get file 83 | response = get_file(headers, file_id) 84 | print(response[0]['commandResponses']['SUCCESS']['show ip int brief']) 85 | 86 | if __name__ == "__main__": 87 | main() 88 | -------------------------------------------------------------------------------- /command-runner/command-runner-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Command Runner Example 2 | hosts: cisco 3 | gather_facts: false 4 | vars: 5 | dnac_host: 6 | dnac_username: 7 | dnac_password: 8 | dnac_verify: False 9 | dnac_port: 443 10 | dnac_version: 2.2.2.3 11 | dnac_debug: False 12 | device_hostname: 13 | 14 | tasks: 15 | - name: Get all Network Device 16 | cisco.dnac.network_device_info: 17 | dnac_host: "{{dnac_host}}" 18 | dnac_username: "{{dnac_username}}" 19 | dnac_password: "{{dnac_password}}" 20 | dnac_verify: "{{dnac_verify}}" 21 | dnac_port: "{{dnac_port}}" 22 | dnac_version: "{{dnac_version}}" 23 | dnac_debug: "{{dnac_debug}}" 24 | hostname: 25 | - "{{device_hostname}}" 26 | register: list_network_devices 27 | 28 | - name: Print list of network devices 29 | debug: 30 | var: list_network_devices 31 | 32 | - name: Execute command 33 | cisco.dnac.command_runner_run_command: 34 | dnac_host: "{{dnac_host}}" 35 | dnac_username: "{{dnac_username}}" 36 | dnac_password: "{{dnac_password}}" 37 | dnac_verify: "{{dnac_verify}}" 38 | dnac_port: "{{dnac_port}}" 39 | dnac_version: "{{dnac_version}}" 40 | dnac_debug: "{{dnac_debug}}" 41 | commands: 42 | - show version 43 | - show ip interface brief 44 | deviceUuids: 45 | - "{{list_network_devices.dnac_response.response[0].id}}" 46 | timeout: 0 47 | register: command_runner_task 48 | 49 | - name: Sleep for 60 seconds and continue with play 50 | wait_for: 51 | timeout: 60 52 | delegate_to: localhost 53 | 54 | - name: Get template deployment task information 55 | cisco.dnac.task_info: 56 | dnac_host: "{{dnac_host}}" 57 | dnac_username: "{{dnac_username}}" 58 | dnac_password: "{{dnac_password}}" 59 | dnac_verify: "{{dnac_verify}}" 60 | dnac_port: "{{dnac_port}}" 61 | dnac_version: "{{dnac_version}}" 62 | dnac_debug: "{{dnac_debug}}" 63 | taskId: "{{command_runner_task.dnac_response.response.taskId}}" 64 | register: task_info 65 | 66 | - name: Print command runner progress 67 | debug: 68 | var: " {{task_info.dnac_response.response.progress | from_json }}" 69 | 70 | - name: Create file variable 71 | set_fact: 72 | file: "{{ task_info.dnac_response.response.progress | from_json }}" 73 | 74 | - name: Get Command Runner File 75 | cisco.dnac.file_info: 76 | dnac_host: "{{dnac_host}}" 77 | dnac_username: "{{dnac_username}}" 78 | dnac_password: "{{dnac_password}}" 79 | dnac_verify: "{{dnac_verify}}" 80 | dnac_port: "{{dnac_port}}" 81 | dnac_version: "{{dnac_version}}" 82 | dnac_debug: "{{dnac_debug}}" 83 | fileId: "{{file.fileId}}" 84 | register: command_runner_file 85 | 86 | - name: Print command runner file 87 | debug: 88 | var: " {{ command_runner_file.dnac_response | from_json }}" 89 | 90 | -------------------------------------------------------------------------------- /command-runner/command-runner-sdk.py: -------------------------------------------------------------------------------- 1 | from dnacentersdk import api 2 | import urllib3 3 | import time 4 | import json 5 | urllib3.disable_warnings() 6 | 7 | dnac = api.DNACenterAPI(username="", # Example username="devnetuser", 8 | password="", # Example password="Cisco123!", 9 | base_url="", # Example base_url="https://sandboxdnac.cisco.com 10 | version='1.3.3', 11 | verify=False) 12 | 13 | devices = dnac.devices.get_device_list(platform_id='C9500-40X') 14 | device_ids = [] 15 | for device in devices.response: 16 | device_ids.append(device.id) 17 | 18 | print(device_ids) 19 | commands = ['show version', "show ip interface brief"] 20 | command_runner = dnac.command_runner.run_read_only_commands_on_devices(commands=commands, 21 | deviceUuids=device_ids, 22 | timeout=0) 23 | 24 | time.sleep(20) 25 | 26 | task = dnac.task.get_task_by_id(command_runner.response.taskId) 27 | progress_json = json.loads(task.response.progress) 28 | print(progress_json) 29 | file_id = progress_json['fileId'] 30 | print(file_id) 31 | 32 | file_info = dnac.file.download_a_file_by_fileid(file_id=file_id) 33 | print(file_info) -------------------------------------------------------------------------------- /credentials/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Credentials guide 2 | 3 | ## Credentials API 4 | 5 | Cisco DNA Center Credentials API allows you to manage the credentials used for discovery and management of network devices. 6 | 7 | Both command runner API and Discover APIs use the credentials to communicate with the devices. 8 | 9 | It supports different kind of credentials: SNMP (2/3), NETCONF, CLI (Telnet/SSH) and HTTP. 10 | 11 | This script creates different types of credentials. 12 | 13 | ## Functions 14 | 15 | The [functions](./credentials-functions.py) file uses python requests library to interact with the API. 16 | 17 | ## License 18 | 19 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 20 | -------------------------------------------------------------------------------- /credentials/credentials-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import time 5 | 6 | # Disable SSL warnings. Not needed in production environments with valid certificates 7 | import urllib3 8 | urllib3.disable_warnings() 9 | 10 | # Authentication 11 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 12 | AUTH_URL = '/dna/system/api/v1/auth/token' 13 | USERNAME = '' # Example USERNAME = 'devnetuser' 14 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 15 | 16 | # URLs 17 | CLI_CREDENTIALS_URL='/dna/intent/api/v1/global-credential/cli' 18 | CREDENTIALS_BY_ID_URL='/dna/intent/api/v1/global-credential/{credential_id}' 19 | CREDENTIALS_URL='/dna/intent/api/v1/global-credential' 20 | HTTP_WRITE_CREDENTIALS_URL='/dna/intent/api/v1/global-credential/http-write' 21 | SNMP_V3_CREDENTIALS_URL='/dna/intent/api/v1/global-credential/snmpv3' 22 | 23 | # Get Authentication token 24 | def get_dnac_jwt_token(): 25 | response = requests.post(BASE_URL + AUTH_URL, 26 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 27 | verify=False) 28 | token = response.json()['Token'] 29 | return token 30 | 31 | # Print credentials 32 | def print_credentials(credentials): 33 | for credential in credentials: 34 | print('Username: {0} Description: {1}'.format( 35 | credential['username'], 36 | credential['description'])) 37 | 38 | # Create SNMP v3 credentials 39 | def create_snmpv3_credentials(headers, credentials): 40 | response = requests.post(BASE_URL + SNMP_V3_CREDENTIALS_URL, 41 | json = credentials, 42 | headers=headers, verify=False) 43 | return response.json()['response'] 44 | 45 | # Create CLI credentials 46 | def create_cli_credentials(headers, credentials): 47 | response = requests.post(BASE_URL + CLI_CREDENTIALS_URL, 48 | json = credentials, 49 | headers=headers, verify=False) 50 | return response.json()['response'] 51 | 52 | # Create HTTP write credentials 53 | def create_http_write_credentials(headers, credentials): 54 | response = requests.post(BASE_URL + HTTP_WRITE_CREDENTIALS_URL, 55 | json = credentials, 56 | headers=headers, verify=False) 57 | return response.json()['response'] 58 | 59 | # Get credentials 60 | def get_credentials(headers, params): 61 | response = requests.get(BASE_URL + CREDENTIALS_URL, 62 | params=params, 63 | headers=headers, verify=False) 64 | return response.json()['response'] 65 | 66 | def main(): 67 | # obtain the Cisco DNA Center Auth Token 68 | token = get_dnac_jwt_token() 69 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 70 | 71 | # Create SNMPv3 credentials 72 | credentials = [ 73 | { 74 | "authType": "SHA", 75 | "authPassword": "DNAC-2020", 76 | "snmpMode": "AUTHPRIV", 77 | "username": "dnac-guide", 78 | "privacyType": "AES128", 79 | "privacyPassword": "DNAC-PRIV-2020" 80 | }, 81 | { 82 | "snmpMode": "NOAUTHNOPRIV", 83 | "username": "dnac-guide-2" 84 | } 85 | ] 86 | create_snmpv3_credentials(headers, credentials) 87 | time.sleep(3) 88 | 89 | # Get SNMP credentials 90 | print('Printing SNMP credentials...') 91 | query_string_params = { 92 | 'credentialSubType': 'SNMPV3' 93 | } 94 | response = get_credentials(headers, query_string_params) 95 | print_credentials(response) 96 | 97 | # HTTP Write credentials 98 | credentials = [ 99 | { 100 | "comments": "DNA Center HTTP credentials", 101 | "description": "HTTP Creds", 102 | "password": "HTTP-cr3d$", 103 | "port": "443", 104 | "secure": "true", 105 | "username": "dna-http-user" 106 | } 107 | ] 108 | create_http_write_credentials(headers, credentials) 109 | 110 | time.sleep(3) 111 | 112 | # Get HTTP Write credentials 113 | print('\nPrinting HTTP Write credentials...') 114 | query_string_params = { 115 | 'credentialSubType': 'HTTP_WRITE' 116 | } 117 | response = get_credentials(headers, query_string_params) 118 | print_credentials(response) 119 | 120 | # CLI Credentials 121 | credentials = [ 122 | { 123 | "comments": "CLI Credentials for the guide", 124 | "description": "Guide creds", 125 | "enablePassword": "Cisco123!", 126 | "password": "Cisco123!", 127 | "username": "dnac" 128 | } 129 | ] 130 | 131 | create_cli_credentials(headers, credentials) 132 | time.sleep(3) 133 | 134 | # Get CLI credentials 135 | print('\nPrinting CLI credentials...') 136 | query_string_params = { 137 | 'credentialSubType': 'CLI' 138 | } 139 | response = get_credentials(headers, query_string_params) 140 | print_credentials(response) 141 | 142 | if __name__ == "__main__": 143 | main() -------------------------------------------------------------------------------- /credentials/credentials-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Credentials Example 2 | hosts: localhost 3 | connection: local 4 | gather_facts: false 5 | vars: 6 | dnac_host: 7 | dnac_username: 8 | dnac_password: 9 | dnac_verify: False 10 | dnac_port: 443 11 | dnac_version: 2.2.2.3 12 | dnac_debug: False 13 | 14 | tasks: 15 | - name: Create SNMPv3 Credentials 16 | cisco.dnac.snmpv3_credential: 17 | dnac_host: "{{dnac_host}}" 18 | dnac_username: "{{dnac_username}}" 19 | dnac_password: "{{dnac_password}}" 20 | dnac_verify: "{{dnac_verify}}" 21 | dnac_port: "{{dnac_port}}" 22 | dnac_version: "{{dnac_version}}" 23 | dnac_debug: "{{dnac_debug}}" 24 | authType: "SHA" 25 | authPassword: "DNAC-2020" 26 | snmpMode: "AUTHPRIV" 27 | username: "dnac-guide" 28 | privacyType: "AES128" 29 | privacyPassword: "DNAC-PRIV-2020" 30 | description: "DNAC Guide" 31 | state: present 32 | 33 | - name: Create SNMPv3 Credentials - 2 34 | cisco.dnac.snmpv3_credential: 35 | dnac_host: "{{dnac_host}}" 36 | dnac_username: "{{dnac_username}}" 37 | dnac_password: "{{dnac_password}}" 38 | dnac_verify: "{{dnac_verify}}" 39 | dnac_port: "{{dnac_port}}" 40 | dnac_version: "{{dnac_version}}" 41 | dnac_debug: "{{dnac_debug}}" 42 | snmpMode: "NOAUTHNOPRIV" 43 | username: "dnac-guide-2" 44 | description: "DNAC Guide 2" 45 | state: present 46 | 47 | - name: Get SNMPv3 Credentials 48 | cisco.dnac.global_credential_info: 49 | dnac_host: "{{dnac_host}}" 50 | dnac_username: "{{dnac_username}}" 51 | dnac_password: "{{dnac_password}}" 52 | dnac_verify: "{{dnac_verify}}" 53 | dnac_port: "{{dnac_port}}" 54 | dnac_version: "{{dnac_version}}" 55 | dnac_debug: "{{dnac_debug}}" 56 | credentialSubType: "SNMPV3" 57 | register: snmpv3_credentials 58 | 59 | - name: Print SNMPv3 Credentials 60 | ansible.builtin.debug: 61 | var: snmpv3_credentials 62 | 63 | - name: Create HTTP Write Credentials 64 | cisco.dnac.http_write_credential: 65 | dnac_host: "{{dnac_host}}" 66 | dnac_username: "{{dnac_username}}" 67 | dnac_password: "{{dnac_password}}" 68 | dnac_verify: "{{dnac_verify}}" 69 | dnac_port: "{{dnac_port}}" 70 | dnac_version: "{{dnac_version}}" 71 | dnac_debug: "{{dnac_debug}}" 72 | description: "DNAC Guide" 73 | password: "HTTP-cr3d$" 74 | port: 443 75 | secure: true 76 | username: "dna-http-user" 77 | state: present 78 | 79 | - name: Get HTTP Write Credentials 80 | cisco.dnac.global_credential_info: 81 | dnac_host: "{{dnac_host}}" 82 | dnac_username: "{{dnac_username}}" 83 | dnac_password: "{{dnac_password}}" 84 | dnac_verify: "{{dnac_verify}}" 85 | dnac_port: "{{dnac_port}}" 86 | dnac_version: "{{dnac_version}}" 87 | dnac_debug: "{{dnac_debug}}" 88 | credentialSubType: "HTTP_WRITE" 89 | register: http_write_credentials 90 | 91 | - name: Print HTTP Write Credentials 92 | ansible.builtin.debug: 93 | var: http_write_credentials 94 | 95 | - name: Create CLI Credentials 96 | cisco.dnac.cli_credential: 97 | dnac_host: "{{dnac_host}}" 98 | dnac_username: "{{dnac_username}}" 99 | dnac_password: "{{dnac_password}}" 100 | dnac_verify: "{{dnac_verify}}" 101 | dnac_port: "{{dnac_port}}" 102 | dnac_version: "{{dnac_version}}" 103 | dnac_debug: "{{dnac_debug}}" 104 | description: "DNAC Guide" 105 | enablePassword: "Cisco123!" 106 | password: "Cisco123!" 107 | username: "dnac" 108 | state: present 109 | 110 | - name: Get CLI Credentials 111 | cisco.dnac.global_credential_info: 112 | dnac_host: "{{dnac_host}}" 113 | dnac_username: "{{dnac_username}}" 114 | dnac_password: "{{dnac_password}}" 115 | dnac_verify: "{{dnac_verify}}" 116 | dnac_port: "{{dnac_port}}" 117 | dnac_version: "{{dnac_version}}" 118 | dnac_debug: "{{dnac_debug}}" 119 | credentialSubType: "CLI" 120 | register: cli_credentials 121 | 122 | - name: Print CLI Credentials 123 | ansible.builtin.debug: 124 | var: cli_credentials 125 | -------------------------------------------------------------------------------- /credentials/credentials-sdk.py: -------------------------------------------------------------------------------- 1 | from dnacentersdk import api 2 | import urllib3 3 | import time 4 | import pprint 5 | urllib3.disable_warnings() 6 | 7 | pp = pprint.PrettyPrinter(indent=4) 8 | 9 | dnac_url = "https://" 10 | dnac_username = "" 11 | dnac_password = "" 12 | dnac_ssl_verify=False 13 | dnac_version = "2.2.2.3" 14 | 15 | dnacenter_client = api.DNACenterAPI(base_url=dnac_url, username=dnac_username, password=dnac_password, 16 | version=dnac_version, verify=dnac_ssl_verify) 17 | 18 | #SNMP v3 Credentials 19 | snmpv3_credentials = [ 20 | { 21 | "authType": "SHA", 22 | "authPassword": "DNAC-2020", 23 | "snmpMode": "AUTHPRIV", 24 | "username": "dnac-guide", 25 | "privacyType": "AES128", 26 | "privacyPassword": "DNAC-PRIV-2020", 27 | "comments": "DNAC Guide", 28 | "description": "DNAC Guide" 29 | }, 30 | { 31 | "snmpMode": "NOAUTHNOPRIV", 32 | "username": "dnac-guide-2", 33 | "comments": "DNAC Guide (2)", 34 | "description": "DNAC Guide 2" 35 | } 36 | ] 37 | 38 | dnacenter_client.discovery.create_snmpv3_credentials(payload=snmpv3_credentials) 39 | time.sleep(3) 40 | 41 | credentials = dnacenter_client.discovery.get_global_credentials(credential_sub_type='SNMPV3') 42 | pp.pprint(credentials) 43 | 44 | # HTTP Credentials 45 | credentials = [ 46 | { 47 | "description": "DNAC Guide", 48 | "comments": "DNAC Guide", 49 | "password": "HTTP-cr3d$", 50 | "port": 443, 51 | "secure": True, 52 | "username": "dna-http-user" 53 | } 54 | ] 55 | 56 | dnacenter_client.discovery.create_http_write_credentials(payload=credentials) 57 | time.sleep(3) 58 | 59 | credentials = dnacenter_client.discovery.get_global_credentials(credential_sub_type='HTTP_WRITE') 60 | pp.pprint(credentials) 61 | 62 | # CLI Credentials 63 | credentials = [ 64 | { 65 | "description": "DNAC Guide", 66 | "comments": "DNAC Guide", 67 | "enablePassword": "Cisco123!", 68 | "password": "Cisco123!", 69 | "username": "dnac" 70 | } 71 | ] 72 | dnacenter_client.discovery.create_cli_credentials(payload=credentials) 73 | time.sleep(3) 74 | 75 | credentials = dnacenter_client.discovery.get_global_credentials(credential_sub_type='CLI') 76 | pp.pprint(credentials) 77 | 78 | -------------------------------------------------------------------------------- /device-onboarding/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Device Onboarding guide 2 | 3 | ## Device Onboarding API 4 | 5 | Cisco DNA Center API can be used to provision configurations to the devices it manages. It supports network configurations that belong to the site the device is part of and also templates that can be deployed to devices, these templates support variables that can be replaced at the moment of deployment. 6 | 7 | Templates can be used for _Day Zero Provisioning_, in order to send configuration for devices that are being onboarded to the network and Cisco DNA Center 8 | 9 | This script creates a template for _Day Zero Provisioning_ and uses it to onboard a device. 10 | 11 | ## Functions 12 | 13 | The [functions](./device-onboarding-functions.py) file uses python requests library to interact with the API. 14 | 15 | ## License 16 | 17 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 18 | -------------------------------------------------------------------------------- /device-onboarding/device-onboarding-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import time 5 | import sys 6 | 7 | # Disable SSL warnings. Not needed in production environments with valid certificates 8 | import urllib3 9 | urllib3.disable_warnings() 10 | 11 | # Authentication 12 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 13 | AUTH_URL = '/dna/system/api/v1/auth/token' 14 | USERNAME = '' # Example USERNAME = 'devnetuser' 15 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 16 | 17 | # URLs 18 | SITE_PROFILE_ADD_SITE_URL = '/api/v1/siteprofile/{site_profile_id}/site/{site_id}' 19 | SITE_PROFILE_URL = '/api/v1/siteprofile' 20 | SITE_URL = '/dna/intent/api/v1/site' 21 | TASK_BY_ID_URL = '/dna/intent/api/v1/task/{task_id}' 22 | TEMPLATE_PROJECT_URL = '/dna/intent/api/v1/template-programmer/project' 23 | TEMPLATE_URL = '/dna/intent/api/v1/template-programmer/project/{project_id}/template' 24 | TEMPLATE_VERSION_URL = '/dna/intent/api/v1/template-programmer/template/version' 25 | ONBOARDING_PNP_IMPORT_URL = '/dna/intent/api/v1/onboarding/pnp-device/import' 26 | ONBOARDING_CLAIM_DEVICE_URL = '/dna/intent/api/v1/onboarding/pnp-device/site-claim' 27 | 28 | # Get Authentication token 29 | def get_dnac_jwt_token(): 30 | response = requests.post(BASE_URL + AUTH_URL, 31 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 32 | verify=False) 33 | token = response.json()['Token'] 34 | return token 35 | 36 | # Get list of sites 37 | def get_sites(headers): 38 | response = requests.get(BASE_URL + SITE_URL, 39 | headers=headers, verify=False) 40 | return response.json()['response'] 41 | 42 | # Get template configuration project 43 | def get_configuration_template_project(headers): 44 | response = requests.get(BASE_URL + TEMPLATE_PROJECT_URL, 45 | headers=headers, 46 | verify=False) 47 | return response.json() 48 | 49 | # Create template 50 | def create_configuration_template(headers, project_id, template): 51 | response = requests.post(BASE_URL + TEMPLATE_URL.format(project_id=project_id), 52 | headers=headers, json=template, 53 | verify=False) 54 | return response.json()['response'] 55 | 56 | # Create configuration template version 57 | def create_configuration_template_version(headers, template_version): 58 | response = requests.post(BASE_URL + TEMPLATE_VERSION_URL, 59 | headers=headers, json=template_version, 60 | verify=False) 61 | return response.json()['response'] 62 | 63 | # Create site profile 64 | def create_site_profile(headers, site_profile_info): 65 | response = requests.post(BASE_URL + SITE_PROFILE_URL, 66 | headers=headers, json=site_profile_info, 67 | verify=False) 68 | return response.json()['response'] 69 | 70 | # Assign Site to Site Profile 71 | def assign_site_to_site_profile(headers, site_profile_id, site_id): 72 | response = requests.post(BASE_URL + 73 | SITE_PROFILE_ADD_SITE_URL.format(site_profile_id=site_profile_id, 74 | site_id=site_id), 75 | headers=headers, verify=False) 76 | return response.json() 77 | 78 | # Import device to PnP process 79 | def import_device_to_pnp(headers, pnp_import_info): 80 | response = requests.post(BASE_URL + ONBOARDING_PNP_IMPORT_URL, 81 | headers=headers, json=pnp_import_info, 82 | verify=False) 83 | return response.json() 84 | 85 | # Import device to PnP process 86 | def claim_device_to_site(headers, claim_info): 87 | response = requests.post(BASE_URL + ONBOARDING_PNP_IMPORT_URL, 88 | headers=headers, json=claim_info, 89 | verify=False) 90 | return response.json() 91 | 92 | # Get Task result 93 | def get_task(headers, task_id): 94 | response = requests.get(BASE_URL + TASK_BY_ID_URL.format(task_id=task_id), 95 | headers=headers, verify=False) 96 | return response.json()['response'] 97 | 98 | def main(): 99 | # obtain the Cisco DNA Center Auth Token 100 | token = get_dnac_jwt_token() 101 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 102 | 103 | # Get Site ID 104 | site_name = 'DNA Center Guide Building' 105 | response = get_sites(headers) 106 | for site in response: 107 | if site['name'] == site_name: 108 | site_id = site['id'] 109 | 110 | print('Printing site name "{site_name}" site id {site_id}'.format(site_name=site_name, 111 | site_id=site_id)) 112 | 113 | # # Get Device IP and Name using Serial Number 114 | device_serial = '919L3GOS8QC' 115 | device_name = 'CAT9k-DNAC-Guide' 116 | device_pid = 'C9500-40X' 117 | 118 | # Get Project information 119 | project_name = "Onboarding Configuration" 120 | response = get_configuration_template_project(headers) 121 | project_id = '' 122 | for project in response: 123 | if project['name'] == project_name: 124 | project_id = project['id'] 125 | 126 | # Create Configuration Template 127 | template_info = { 128 | "name": "DNA Center Guide - Day0", 129 | "description": "Guide Configuration Template", 130 | "tags": [], 131 | "deviceTypes": [ 132 | { 133 | "productFamily": "Switches and Hubs", 134 | "productSeries": "Cisco Catalyst 9500 Series Switches" 135 | } 136 | ], 137 | "softwareType": "IOS-XE", 138 | "softwareVariant": "XE", 139 | "templateContent": "ip access-list extended $permitACLName\npermit ip 10.0.0.0 0.255.255.25.0 any\npermit ip 172.16.0.0 0.15.255.255 any\npermit ip 192.168.0.0 0.0.255.255 any\n!\n\nip access-list extended $denyACLName\ndeny ip 10.0.0.0 0.255.255.25.0 any\ndeny ip 172.16.0.0 0.15.255.255 any\ndeny ip 192.168.0.0 0.0.255.255 any\n!\n", 140 | "rollbackTemplateContent": "", 141 | "templateParams": [ 142 | { 143 | "parameterName": "permitACLName", 144 | "dataType": "STRING", 145 | "defaultValue": None, 146 | "description": None, 147 | "required": True, 148 | "notParam": False, 149 | "paramArray": False, 150 | "displayName": None, 151 | "instructionText": None, 152 | "group": None, 153 | "order": 1, 154 | "selection": { 155 | "selectionType": None, 156 | "selectionValues": {}, 157 | "defaultSelectedValues": [] 158 | }, 159 | "range": [], 160 | "key": None, 161 | "provider": None, 162 | "binding": "" 163 | }, 164 | { 165 | "parameterName": "denyACLName", 166 | "dataType": "STRING", 167 | "defaultValue": None, 168 | "description": None, 169 | "required": True, 170 | "notParam": False, 171 | "paramArray": False, 172 | "displayName": None, 173 | "instructionText": None, 174 | "group": None, 175 | "order": 2, 176 | "selection": { 177 | "selectionType": None, 178 | "selectionValues": {}, 179 | "defaultSelectedValues": [] 180 | }, 181 | "range": [], 182 | "key": None, 183 | "provider": None, 184 | "binding": "" 185 | } 186 | ], 187 | "rollbackTemplateParams": [], 188 | "composite": False, 189 | "containingTemplates": [] 190 | } 191 | 192 | 193 | response = create_configuration_template(headers, project_id, template_info) 194 | task_id = response['taskId'] 195 | 196 | time.sleep(3) 197 | response = get_task(headers, task_id) 198 | 199 | template_id = response['data'] 200 | 201 | # Create Template version 202 | template_version = { 203 | "comments": "DNAC Guide Day 0 Initial Version", 204 | "templateId": template_id 205 | } 206 | create_configuration_template_version(headers, template_version) 207 | 208 | # Create Configuration Template 209 | site_profile_info = { 210 | "name": "DNA Center Guide Profile", 211 | "namespace": "switching", 212 | "profileAttributes": [ 213 | { 214 | "key": "day0.templates", 215 | "attribs": [ 216 | { 217 | "key": "device.family", 218 | "value": "Switches and Hubs", 219 | "attribs": [ 220 | { 221 | "key": "device.series", 222 | "value": "Cisco Catalyst 9500 Series Switches", 223 | "attribs": [ 224 | { 225 | "key": "device.type", 226 | "attribs": [ 227 | { 228 | "key": "template.id", 229 | "value": template_id, 230 | }, 231 | { 232 | "key": "device.tag", 233 | "value": "", 234 | "attribs": [ 235 | 236 | ] 237 | } 238 | ] 239 | } 240 | ] 241 | } 242 | ] 243 | } 244 | ] 245 | } 246 | ] 247 | } 248 | 249 | response = create_site_profile(headers, site_profile_info) 250 | 251 | task_id = response['taskId'] 252 | 253 | time.sleep(3) 254 | response = get_task(headers, task_id) 255 | 256 | site_profile_id = response['data'] 257 | 258 | response = assign_site_to_site_profile(headers, site_profile_id, site_id) 259 | 260 | # Add device to PnP process 261 | 262 | pnp_import_info = [ 263 | { 264 | "deviceInfo": { 265 | "hostname": device_name, 266 | "serialNumber": device_serial, 267 | "pid": device_pid, 268 | "sudiRequired": False, 269 | "userSudiSerialNos": [], 270 | "aaaCredentials": { 271 | "username": "", 272 | "password": "" 273 | } 274 | } 275 | } 276 | ] 277 | 278 | response = import_device_to_pnp(headers, pnp_import_info) 279 | 280 | device_id = response['successList'][0]['id'] 281 | 282 | claim_info = { 283 | "siteId": site_id, 284 | "deviceId": device_id, 285 | "type": "Default", 286 | "configInfo": { 287 | "configId": template_id, 288 | "configParameters": { 289 | "permitACLName": "GUIDE-ALLOW-ACL", 290 | "denyACLName": "GUIDE - DENY - ACL" 291 | } 292 | } 293 | } 294 | 295 | claim_device_to_site(headers, claim_info) 296 | 297 | 298 | 299 | if __name__ == "__main__": 300 | main() 301 | -------------------------------------------------------------------------------- /device-provisioning/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Device Provisioning guide 2 | 3 | ## Device Provisioning API 4 | 5 | Cisco DNA Center API can be used to provision configurations to the devices it manages. It supports network configurations that belong to the site the device is part of and also templates that can be deployed to devices, these templates support variables that can be replaced at the moment of deployment. 6 | 7 | The script creates a Device Provision template and uses it to provision a device. 8 | 9 | ## Functions 10 | 11 | The [functions](./device-provisioning-functions.py) file uses python requests library to interact with the API. 12 | 13 | ## License 14 | 15 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 16 | -------------------------------------------------------------------------------- /device-provisioning/device-provisioning-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import time 5 | import sys 6 | 7 | # Disable SSL warnings. Not needed in production environments with valid certificates 8 | import urllib3 9 | urllib3.disable_warnings() 10 | 11 | # Authentication 12 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 13 | AUTH_URL = '/dna/system/api/v1/auth/token' 14 | USERNAME = '' # Example USERNAME = 'devnetuser' 15 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 16 | 17 | # URLs 18 | DEVICES_BY_SERIAL_URL = '/dna/intent/api/v1/network-device/serial-number/{serial_number}' 19 | NETWORK_URL = '/dna/intent/api/v1/network/{site_id}' 20 | SITE_DEVICE_URL = '/dna/intent/api/v1/site/{site_id}/device' 21 | SITE_URL = '/dna/intent/api/v1/site' 22 | TASK_BY_ID_URL = '/dna/intent/api/v1/task/{task_id}' 23 | TEMPLATE_DEPLOY_URL = '/dna/intent/api/v1/template-programmer/template/deploy' 24 | TEMPLATE_PROJECT_URL = '/dna/intent/api/v1/template-programmer/project' 25 | TEMPLATE_URL = '/dna/intent/api/v1/template-programmer/project/{project_id}/template' 26 | TEMPLATE_VERSION_URL = '/dna/intent/api/v1/template-programmer/template/version' 27 | 28 | # Get Authentication token 29 | def get_dnac_jwt_token(): 30 | response = requests.post(BASE_URL + AUTH_URL, 31 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 32 | verify=False) 33 | token = response.json()['Token'] 34 | return token 35 | 36 | # Get list of sites 37 | def get_sites(headers): 38 | response = requests.get(BASE_URL + SITE_URL, 39 | headers=headers, verify=False) 40 | return response.json()['response'] 41 | 42 | # Get device by serial 43 | def get_device_by_serial(headers, serial_number): 44 | response = requests.get(BASE_URL + DEVICES_BY_SERIAL_URL.format(serial_number=serial_number), 45 | headers=headers, 46 | verify=False) 47 | return response.json()['response'] 48 | 49 | # Add devices to site 50 | def add_devices_site(headers, site_id, devices): 51 | headers['__runsync'] = 'true' 52 | headers['__runsynctimeout'] = '30' 53 | response = requests.post(BASE_URL + SITE_DEVICE_URL.format(site_id=site_id), 54 | headers=headers, json=devices, 55 | verify=False) 56 | return response.json() 57 | 58 | # Create template configuration project 59 | def create_network(headers, site_id, network): 60 | response = requests.post(BASE_URL + NETWORK_URL.format(site_id=site_id), 61 | headers=headers, json=network, 62 | verify=False) 63 | return response.json() 64 | 65 | # Get template configuration project 66 | def get_configuration_template_project(headers): 67 | response = requests.get(BASE_URL + TEMPLATE_PROJECT_URL, 68 | headers=headers, 69 | verify=False) 70 | return response.json() 71 | 72 | # Create template 73 | def create_configuration_template(headers, project_id, template): 74 | response = requests.post(BASE_URL + TEMPLATE_URL.format(project_id=project_id), 75 | headers=headers, json=template, 76 | verify=False) 77 | return response.json()['response'] 78 | 79 | # Create configuration template version 80 | def create_configuration_template_version(headers, template_version): 81 | response = requests.post(BASE_URL + TEMPLATE_VERSION_URL, 82 | headers=headers, json=template_version, 83 | verify=False) 84 | return response.json()['response'] 85 | 86 | # Deploy template 87 | def deploy_configuration_template(headers, deployment_info): 88 | response = requests.post(BASE_URL + TEMPLATE_DEPLOY_URL, 89 | headers=headers, json=deployment_info, 90 | verify=False) 91 | return response.json() 92 | 93 | # Get Task result 94 | def get_task(headers, task_id): 95 | response = requests.get(BASE_URL + TASK_BY_ID_URL.format(task_id=task_id), 96 | headers=headers, verify=False) 97 | return response.json()['response'] 98 | 99 | def main(): 100 | # obtain the Cisco DNA Center Auth Token 101 | token = get_dnac_jwt_token() 102 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 103 | 104 | # Get Site ID 105 | site_name = 'DNA Center Guide Building' 106 | response = get_sites(headers) 107 | for site in response: 108 | if site['name'] == site_name: 109 | site_id = site['id'] 110 | 111 | print('Printing site name "{site_name}" site id {site_id}'.format(site_name=site_name, 112 | site_id=site_id)) 113 | 114 | # Get Device IP and Name using Serial Number 115 | serial_number = '919L3GOS8QC' 116 | response = get_device_by_serial(headers, serial_number) 117 | device_ip = response['managementIpAddress'] 118 | device_name = response['hostname'] 119 | device_ips = [device_ip] 120 | 121 | print('\nPrinting device serial {serial_number} device IP {ip}'.format(serial_number=serial_number, 122 | ip=device_ip)) 123 | # Create Site Network 124 | network = { 125 | "settings": { 126 | "dhcpServer": [ 127 | "172.30.200.5" 128 | ], 129 | "dnsServer": { 130 | "domainName": "devnet.local", 131 | "primaryIpAddress": "172.30.200.6", 132 | "secondaryIpAddress": "172.30.200.7" 133 | }, 134 | "syslogServer": { 135 | "ipAddresses": [ 136 | "10.255.0.1" 137 | ], 138 | "configureDnacIP": True 139 | } 140 | } 141 | } 142 | response = create_network(headers, site_id, network) 143 | 144 | site_devices = { 145 | 'device': device_ips 146 | } 147 | 148 | # Assign device to site 149 | response = add_devices_site(headers, site_id, site_devices) 150 | print(response['result']['progress']) 151 | 152 | # Get Project information 153 | project_name = "Onboarding Configuration" 154 | response = get_configuration_template_project(headers) 155 | project_id = '' 156 | for project in response: 157 | if project['name'] == project_name: 158 | project_id = project['id'] 159 | 160 | # Create Configuration Template 161 | template_info = { 162 | "name": "DNA Center Guide", 163 | "description": "Guide Configuration Template", 164 | "tags": [], 165 | "deviceTypes": [ 166 | { 167 | "productFamily": "Routers", 168 | "productSeries": "Cisco 1000 Series Integrated Services Routers" 169 | } 170 | ], 171 | "softwareType": "IOS-IOS", 172 | "softwareVariant": "IOS", 173 | "templateContent": "ip access-list extended $permitACLName\npermit ip 10.0.0.0 0.255.255.25.0 any\npermit ip 172.16.0.0 0.15.255.255 any\npermit ip 192.168.0.0 0.0.255.255 any\n!\n\nip access-list extended $denyACLName\ndeny ip 10.0.0.0 0.255.255.25.0 any\ndeny ip 172.16.0.0 0.15.255.255 any\ndeny ip 192.168.0.0 0.0.255.255 any\n!\n", 174 | "rollbackTemplateContent": "", 175 | "templateParams": [ 176 | { 177 | "parameterName": "permitACLName", 178 | "dataType": "STRING", 179 | "defaultValue": None, 180 | "description": None, 181 | "required": True, 182 | "notParam": False, 183 | "paramArray": False, 184 | "displayName": None, 185 | "instructionText": None, 186 | "group": None, 187 | "order": 1, 188 | "selection": { 189 | "selectionType": None, 190 | "selectionValues": {}, 191 | "defaultSelectedValues": [] 192 | }, 193 | "range": [], 194 | "key": None, 195 | "provider": None, 196 | "binding": "" 197 | }, 198 | { 199 | "parameterName": "denyACLName", 200 | "dataType": "STRING", 201 | "defaultValue": None, 202 | "description": None, 203 | "required": True, 204 | "notParam": False, 205 | "paramArray": False, 206 | "displayName": None, 207 | "instructionText": None, 208 | "group": None, 209 | "order": 2, 210 | "selection": { 211 | "selectionType": None, 212 | "selectionValues": {}, 213 | "defaultSelectedValues": [] 214 | }, 215 | "range": [], 216 | "key": None, 217 | "provider": None, 218 | "binding": "" 219 | } 220 | ], 221 | "rollbackTemplateParams": [], 222 | "composite": False, 223 | "containingTemplates": [] 224 | } 225 | response = create_configuration_template(headers, project_id, template_info) 226 | task_id = response['taskId'] 227 | 228 | time.sleep(3) 229 | response = get_task(headers, task_id) 230 | 231 | template_id = response['data'] 232 | 233 | # Create Template version 234 | template_version = { 235 | "comments": "DNAC Guide Initial Version", 236 | "templateId": template_id 237 | } 238 | create_configuration_template_version(headers, template_version) 239 | 240 | # Deploy Template to device 241 | deployment_info = { 242 | "forcePushTemplate": False, 243 | "isComposite": False, 244 | "targetInfo": [ 245 | { 246 | "hostName": device_name, 247 | "params": { 248 | "permitACLName": "GUIDE-ALLOW-ACL", 249 | "denyACLName": "GUIDE-DENY-ACL" 250 | }, 251 | "type": "MANAGED_DEVICE_IP" 252 | } 253 | ], 254 | "templateId": template_id 255 | } 256 | response = deploy_configuration_template(headers, deployment_info) 257 | print(response) 258 | 259 | 260 | if __name__ == "__main__": 261 | main() 262 | -------------------------------------------------------------------------------- /devices/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Devices guide 2 | 3 | ## Devices API 4 | 5 | Cisco DNA Center has information of the devices that are part of the network. The API allows you to query that information to use it for inventory, more operations within DNA Center or as a source of truth of your network. 6 | 7 | These scripts retrieve information about the network devices on Cisco DNA Center. 8 | 9 | ## Functions 10 | 11 | The [functions](./devices-functions.py) file uses python requests library to interact with the API. 12 | 13 | ## SDK 14 | 15 | The [SDK](./devices-sdk.py) uses [Cisco DNA Center Python SDK](https://pypi.org/project/dnacentersdk/) to simplify the interaction with the API. 16 | 17 | ## License 18 | 19 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 20 | -------------------------------------------------------------------------------- /devices/devices-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import sys 5 | 6 | # Disable SSL warnings. Not needed in production environments with valid certificates 7 | import urllib3 8 | urllib3.disable_warnings() 9 | 10 | # Authentication 11 | BASE_URL = 'https://sandboxdnac2.cisco.com' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 12 | AUTH_URL = '/dna/system/api/v1/auth/token' 13 | USERNAME = 'admin' # Example USERNAME = 'devnetuser' 14 | PASSWORD = 'Cisco1234!' # Example PASSWORD = 'Cisco123!' 15 | 16 | # URLs 17 | DEVICES_COUNT_URL = '/dna/intent/api/v1/network-device/count' 18 | DEVICES_URL = '/dna/intent/api/v1/network-device' 19 | DEVICES_BY_ID_URL = '/dna/intent/api/v1/network-device/' 20 | 21 | def print_devices_info(devices): 22 | # Print id, hostname and management IP 23 | for item in devices: 24 | print(item['id'], item['hostname'], item['managementIpAddress']) 25 | 26 | # Get Authentication token 27 | def get_dnac_jwt_token(): 28 | response = requests.post(BASE_URL + AUTH_URL, 29 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 30 | verify=False) 31 | token = response.json()['Token'] 32 | return token 33 | 34 | # Get count of devices 35 | def get_devices_count(headers): 36 | response = requests.get(BASE_URL + DEVICES_COUNT_URL, 37 | headers = headers, 38 | verify=False) 39 | return response.json()['response'] 40 | 41 | # Get list of devices 42 | def get_devices_list(headers, query_string_params): 43 | response = requests.get(BASE_URL + DEVICES_URL, 44 | headers = headers, 45 | params = query_string_params, 46 | verify=False) 47 | return response.json()['response'] 48 | 49 | # Get information from one device 50 | def get_devices_by_id(headers, device_id): 51 | response = requests.get(BASE_URL + DEVICES_BY_ID_URL + device_id, headers = headers, verify=False) 52 | return response.json()['response'] 53 | 54 | def main(): 55 | # obtain the Cisco DNA Center Auth Token 56 | token = get_dnac_jwt_token() 57 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 58 | 59 | # Print devices count 60 | devices_count = get_devices_count(headers) 61 | print('Printing device count ...') 62 | print('Device count is', devices_count) 63 | 64 | # print devices list 65 | print('\nPrinting device list ...') 66 | response = get_devices_list(headers, {}) 67 | print_devices_info(response) 68 | 69 | # print devices list filtered by hostname 70 | print('\nPrinting device list filtered by hostname ...') 71 | query_string_params = {'hostname': 'CSR1Kv-01.devnet.local'} 72 | response = get_devices_list(headers, query_string_params) 73 | print_devices_info(response) 74 | 75 | # print devices list filtered by platform Id 76 | print('\nPrinting device list filtered by platform id...') 77 | query_string_params = {'platformId': 'C9500-40X'} 78 | response = get_devices_list(headers, query_string_params) 79 | print_devices_info(response) 80 | 81 | # print device information 82 | print('\nPrinting device info by device id...') 83 | device_id = response[0]['id'] 84 | response = get_devices_by_id(headers, device_id) 85 | print(response) 86 | 87 | if __name__ == "__main__": 88 | main() 89 | -------------------------------------------------------------------------------- /devices/devices-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Devices Example 2 | hosts: cisco 3 | gather_facts: false 4 | vars: 5 | dnac_host: 6 | dnac_username: 7 | dnac_password: 8 | dnac_verify: False 9 | dnac_port: 443 10 | dnac_version: 2.2.2.3 11 | dnac_debug: False 12 | 13 | tasks: 14 | - name: Get all Network Device Count 15 | cisco.dnac.network_device_count_info: 16 | dnac_host: "{{dnac_host}}" 17 | dnac_username: "{{dnac_username}}" 18 | dnac_password: "{{dnac_password}}" 19 | dnac_verify: "{{dnac_verify}}" 20 | dnac_port: "{{dnac_port}}" 21 | dnac_version: "{{dnac_version}}" 22 | dnac_debug: "{{dnac_debug}}" 23 | register: network_device_count 24 | 25 | - name: Print network device count 26 | ansible.builtin.debug: 27 | var: network_device_count 28 | 29 | - name: Get all Network Devices 30 | cisco.dnac.network_device_info: 31 | dnac_host: "{{dnac_host}}" 32 | dnac_username: "{{dnac_username}}" 33 | dnac_password: "{{dnac_password}}" 34 | dnac_verify: "{{dnac_verify}}" 35 | dnac_port: "{{dnac_port}}" 36 | dnac_version: "{{dnac_version}}" 37 | dnac_debug: "{{dnac_debug}}" 38 | register: network_devices_list 39 | 40 | - name: Print network devices list 41 | ansible.builtin.debug: 42 | var: network_devices_list 43 | 44 | - name: Get Network Device Filtered by Hostname 45 | cisco.dnac.network_device_info: 46 | dnac_host: "{{dnac_host}}" 47 | dnac_username: "{{dnac_username}}" 48 | dnac_password: "{{dnac_password}}" 49 | dnac_verify: "{{dnac_verify}}" 50 | dnac_port: "{{dnac_port}}" 51 | dnac_version: "{{dnac_version}}" 52 | dnac_debug: "{{dnac_debug}}" 53 | hostname: 54 | - "ISR-4K--Branch-SFO" # CHANGE ME 55 | register: network_devices_by_hostname 56 | 57 | - name: Print network device by hostname 58 | ansible.builtin.debug: 59 | var: network_devices_by_hostname 60 | 61 | - name: Get Network Device Filtered by Platform 62 | cisco.dnac.network_device_info: 63 | dnac_host: "{{dnac_host}}" 64 | dnac_username: "{{dnac_username}}" 65 | dnac_password: "{{dnac_password}}" 66 | dnac_verify: "{{dnac_verify}}" 67 | dnac_port: "{{dnac_port}}" 68 | dnac_version: "{{dnac_version}}" 69 | dnac_debug: "{{dnac_debug}}" 70 | platformId: 71 | - C9300-48U # CHANGE ME 72 | register: network_devices_by_platform 73 | 74 | - name: Print network device by platform 75 | ansible.builtin.debug: 76 | var: network_devices_by_platform 77 | 78 | - name: Get Network Device by ID 79 | cisco.dnac.network_device_info: 80 | dnac_host: "{{dnac_host}}" 81 | dnac_username: "{{dnac_username}}" 82 | dnac_password: "{{dnac_password}}" 83 | dnac_verify: "{{dnac_verify}}" 84 | dnac_port: "{{dnac_port}}" 85 | dnac_version: "{{dnac_version}}" 86 | dnac_debug: "{{dnac_debug}}" 87 | id: "{{network_devices_list.dnac_response.response[0].id}}" 88 | register: network_device 89 | 90 | - name: Print network device by id 91 | ansible.builtin.debug: 92 | var: network_device 93 | -------------------------------------------------------------------------------- /devices/devices-sdk.py: -------------------------------------------------------------------------------- 1 | from dnacentersdk import api 2 | import urllib3 3 | urllib3.disable_warnings() 4 | 5 | def print_devices_info(devices_info): 6 | for device in devices_info.response: 7 | print(device.id, device.hostname, 8 | device.managementIpAddress) 9 | 10 | 11 | 12 | dnac = api.DNACenterAPI(username="admin", # Example username="devnetuser", 13 | password="Cisco1234!", # Example password="Cisco123!", 14 | base_url="https://sandboxdnac2.cisco.com", # Example base_url="https://sandboxdnac.cisco.com 15 | version='1.3.3', 16 | verify=False) 17 | 18 | print('Printing device count ...') 19 | devices_count = dnac.devices.get_device_count() 20 | print('Device count is', devices_count.response) 21 | 22 | print('\nPrinting device list ...') 23 | devices = dnac.devices.get_device_list() 24 | for device in devices.response: 25 | print(device.id, device.hostname, device.managementIpAddress, device.platformId) 26 | 27 | print('\nPrinting device list filtered by hostname ...') 28 | devices = dnac.devices.get_device_list(hostname='CSR1Kv-01.devnet.local') 29 | print_devices_info(devices) 30 | 31 | print('\nPrinting device list filtered by platform id...') 32 | devices = dnac.devices.get_device_list(platformId='AIR-AP1141N-A-K9') 33 | print_devices_info(devices) 34 | 35 | print('\nPrinting device info by device id...') 36 | device = dnac.devices.get_device_by_id(id=devices.response[0].id) 37 | print(device.response.id, device.response.hostname) 38 | -------------------------------------------------------------------------------- /discovery/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Discovery guide 2 | 3 | ## Discovery API 4 | 5 | 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. 6 | 7 | Leveraging Discovery API, the user can quickly get its devices into Cisco DNA Center and perform operations related to them. 8 | 9 | This script triggers a network discovery and assigns the devices to a site. 10 | 11 | ## Functions 12 | 13 | The [functions](./discovery-functions.py) file uses python requests library to interact with the API. 14 | 15 | ## License 16 | 17 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 18 | -------------------------------------------------------------------------------- /discovery/discovery-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import time 5 | import sys 6 | 7 | # Disable SSL warnings. Not needed in production environments with valid certificates 8 | import urllib3 9 | urllib3.disable_warnings() 10 | 11 | # Authentication 12 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 13 | AUTH_URL = '/dna/system/api/v1/auth/token' 14 | USERNAME = '' # Example USERNAME = 'devnetuser' 15 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 16 | 17 | # URLs 18 | CREDENTIALS_URL = '/dna/intent/api/v1/global-credential/cli' 19 | DISCOVERY_URL = '/dna/intent/api/v1/discovery' 20 | TASK_BY_ID_URL = '/dna/intent/api/v1/task/{task_id}' 21 | SITE_URL = '/dna/intent/api/v1/site' 22 | DISCOVERY_DEVICES_URL = '/dna/intent/api/v1/discovery/{discovery_id}/network-device' 23 | SITE_DEVICE_URL = '/dna/system/api/v1/site/{site_id}/device' 24 | 25 | # Get Authentication token 26 | def get_dnac_jwt_token(): 27 | response = requests.post(BASE_URL + AUTH_URL, 28 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 29 | verify=False) 30 | token = response.json()['Token'] 31 | return token 32 | 33 | # Create site 34 | def create_site(headers, site): 35 | headers['__runsync'] = 'true' 36 | headers['__runsynctimeout'] = '30' 37 | response = requests.post(BASE_URL + SITE_URL, 38 | headers=headers, json=site, 39 | verify=False) 40 | return response.json() 41 | 42 | # Add devices to site 43 | def add_devices_site(headers, site_id, devices): 44 | headers['__runsync'] = 'true' 45 | headers['__runsynctimeout'] = '30' 46 | response = requests.post(BASE_URL + SITE_DEVICE_URL.format(site_id=site_id), 47 | headers=headers, json=devices, 48 | verify=False) 49 | return response.json() 50 | 51 | 52 | def create_credentials(headers, credentials): 53 | response = requests.post(BASE_URL + CREDENTIALS_URL, headers=headers, 54 | json=credentials, verify=False) 55 | return response.json()['response'] 56 | 57 | def create_discovery(headers, discovery): 58 | response = requests.post(BASE_URL + DISCOVERY_URL, headers=headers, 59 | json=discovery, verify=False) 60 | return response.json()['response'] 61 | 62 | # Get devices from discovery 63 | def get_discovery_devices(headers, discovery_id): 64 | response = requests.get(BASE_URL + DISCOVERY_DEVICES_URL.format(discovery_id=discovery_id), 65 | headers=headers, verify=False) 66 | return response.json()['response'] 67 | 68 | # Get Task result 69 | def get_task(headers, task_id): 70 | response = requests.get(BASE_URL + TASK_BY_ID_URL.format(task_id=task_id), 71 | headers=headers, verify=False) 72 | return response.json()['response'] 73 | 74 | def main(): 75 | # obtain the Cisco DNA Center Auth Token 76 | token = get_dnac_jwt_token() 77 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 78 | 79 | site_area = { 80 | "type": "area", 81 | "site": { 82 | "area": { 83 | "name": "DNA Center Guide", 84 | "parentName": "Global" 85 | } 86 | } 87 | } 88 | 89 | print('Printing area ID...') 90 | response = create_site(headers, site_area) 91 | area_id = response['siteId'] 92 | print(area_id) 93 | 94 | site_building = { 95 | "type": "building", 96 | "site": { 97 | "building": { 98 | "name": "DNA Center Guide Building", 99 | "parentName": "Global/DNA Center Guide", 100 | "latitude": "37.409424", 101 | "longitude": "-121.928868" 102 | } 103 | } 104 | } 105 | 106 | print('\nPrinting building ID...') 107 | response = create_site(headers, site_building) 108 | building_id = response['siteId'] 109 | print(building_id) 110 | 111 | credentials = [ 112 | { 113 | 'description': 'DNA Center Guide Credentials', 114 | 'enablePassword': 'C!scow02', 115 | 'password': 'C!scow02', 116 | 'username': 'devnet', 117 | 'credentialType': 'GLOBAL' 118 | } 119 | ] 120 | response = create_credentials(headers, credentials) 121 | task_id = response['taskId'] 122 | 123 | time.sleep(5) 124 | 125 | print('\nPrinting credential id...') 126 | response = get_task(headers, task_id) 127 | credential_id = response['progress'] 128 | print(credential_id) 129 | 130 | discovery = { 131 | "name": "Discovery-Guide", 132 | "discoveryType": "Range", 133 | "ipAddressList": "10.255.3.11-10.255.3.19", 134 | "protocolOrder": "ssh", 135 | "timeOut": 5, 136 | "retryCount": 3, 137 | "isAutoCdp": False, 138 | "globalCredentialIdList": [ 139 | credential_id 140 | ] 141 | } 142 | response = create_discovery(headers, discovery) 143 | task_id = response['taskId'] 144 | 145 | print('\nWaiting 10 seconds for discovery to be created...') 146 | time.sleep(10) 147 | 148 | print('\nPrinting discovery id') 149 | response = get_task(headers, task_id) 150 | discovery_id = response['progress'] 151 | print(discovery_id) 152 | 153 | print('\nWaiting 30 seconds for discovery to end...') 154 | time.sleep(30) 155 | response = get_discovery_devices(headers, discovery_id) 156 | device_ips = [] 157 | for device in response: 158 | device_ips.append({'ip': device['managementIpAddress']}) 159 | 160 | site_devices = { 161 | 'device': device_ips 162 | } 163 | 164 | print('\nPrinting device inclusion in site...') 165 | response = add_devices_site(headers, building_id, site_devices) 166 | print(response['result']['progress']) 167 | 168 | 169 | if __name__ == "__main__": 170 | main() -------------------------------------------------------------------------------- /discovery/discovery-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Discovery example 2 | hosts: localhost 3 | connection: local 4 | gather_facts: false 5 | vars: 6 | dnac_host: 7 | dnac_username: 8 | dnac_password: 9 | dnac_verify: False 10 | dnac_port: 443 11 | dnac_version: 2.2.2.3 12 | dnac_debug: False 13 | 14 | tasks: 15 | - name: Get Global Credentials 16 | cisco.dnac.global_credential_info: 17 | dnac_host: "{{dnac_host}}" 18 | dnac_username: "{{dnac_username}}" 19 | dnac_password: "{{dnac_password}}" 20 | dnac_verify: "{{dnac_verify}}" 21 | dnac_port: "{{dnac_port}}" 22 | dnac_version: "{{dnac_version}}" 23 | dnac_debug: "{{dnac_debug}}" 24 | credentialSubType: "CLI" 25 | register: credentials 26 | 27 | - name: Create discovery 28 | cisco.dnac.discovery: 29 | dnac_host: "{{dnac_host}}" 30 | dnac_username: "{{dnac_username}}" 31 | dnac_password: "{{dnac_password}}" 32 | dnac_verify: "{{dnac_verify}}" 33 | dnac_port: "{{dnac_port}}" 34 | dnac_version: "{{dnac_version}}" 35 | dnac_debug: "{{dnac_debug}}" 36 | state: present 37 | name: "Discovery-Guide" 38 | discoveryType: Range 39 | globalCredentialIdList: 40 | - "{{credentials.dnac_response.response[0].id}}" 41 | ipAddressList: "10.255.3.11-10.255.3.19" 42 | timeOut: 5 43 | retryCount: 3 44 | protocolOrder: "ssh" 45 | isAutoCdp: false 46 | register: discovery_task 47 | 48 | - name: Pause for 10 seconds for discovery to be created 49 | pause: 50 | seconds: 10 51 | 52 | - name: Get Task by id 53 | cisco.dnac.task_info: 54 | dnac_host: "{{dnac_host}}" 55 | dnac_username: "{{dnac_username}}" 56 | dnac_password: "{{dnac_password}}" 57 | dnac_verify: "{{dnac_verify}}" 58 | dnac_port: "{{dnac_port}}" 59 | dnac_version: "{{dnac_version}}" 60 | dnac_debug: "{{dnac_debug}}" 61 | taskId: "{{discovery_task.dnac_response.response.taskId}}" 62 | register: discovery_progress 63 | 64 | - name: Print discovery progress 65 | debug: 66 | var: discovery_progress 67 | 68 | - name: Pause for 60 seconds for discovery to end 69 | pause: 70 | seconds: 60 71 | 72 | - name: Get all Discovery Device 73 | cisco.dnac.discovery_device_info: 74 | dnac_host: "{{dnac_host}}" 75 | dnac_username: "{{dnac_username}}" 76 | dnac_password: "{{dnac_password}}" 77 | dnac_verify: "{{dnac_verify}}" 78 | dnac_port: "{{dnac_port}}" 79 | dnac_version: "{{dnac_version}}" 80 | dnac_debug: "{{dnac_debug}}" 81 | id: "{{discovery_progress.dnac_response.response.progress}}" 82 | register: discovery_devices 83 | 84 | - name: Get Global Site 85 | cisco.dnac.site_info: 86 | dnac_host: "{{dnac_host}}" 87 | dnac_username: "{{dnac_username}}" 88 | dnac_password: "{{dnac_password}}" 89 | dnac_verify: "{{dnac_verify}}" 90 | dnac_port: "{{dnac_port}}" 91 | dnac_version: "{{dnac_version}}" 92 | dnac_debug: "{{dnac_debug}}" 93 | name: Global 94 | register: global_site 95 | 96 | - name: Print discovery progress 97 | debug: 98 | var: discovery_devices 99 | 100 | - name: Assign Discovered Devices to Site 101 | cisco.dnac.site_assign_device: 102 | dnac_host: "{{dnac_host}}" 103 | dnac_username: "{{dnac_username}}" 104 | dnac_password: "{{dnac_password}}" 105 | dnac_verify: "{{dnac_verify}}" 106 | dnac_port: "{{dnac_port}}" 107 | dnac_version: "{{dnac_version}}" 108 | dnac_debug: "{{dnac_debug}}" 109 | device: 110 | - ip: "{{item.managementIpAddress}}" 111 | siteId: "{{global_site.dnac_response.response[0].id}}" 112 | with_items: discovery_devices.dnac_response.response 113 | -------------------------------------------------------------------------------- /discovery/discovery-sdk.py: -------------------------------------------------------------------------------- 1 | from dnacentersdk import api 2 | import urllib3 3 | import time 4 | import pprint 5 | urllib3.disable_warnings() 6 | 7 | pp = pprint.PrettyPrinter(indent=4) 8 | 9 | dnac_url = "https://" 10 | dnac_username = "" 11 | dnac_password = "" 12 | dnac_ssl_verify=False 13 | dnac_version = "2.2.2.3" 14 | 15 | dnacenter_client = api.DNACenterAPI(base_url=dnac_url, username=dnac_username, password=dnac_password, 16 | version=dnac_version, verify=dnac_ssl_verify) 17 | 18 | 19 | def main(): 20 | 21 | sites = dnacenter_client.sites.get_site(name='Global') 22 | print(f'\n"Global" Site ID is: {sites.response[0].id}') 23 | 24 | print('\nGetting Global CLI credentials to do discovery') 25 | credentials = dnacenter_client.discovery.get_global_credentials(credential_sub_type="CLI") 26 | credentials_ids = [credential.id for credential in credentials.response] 27 | 28 | print('\nCreating discovery...') 29 | discovery_task_id = dnacenter_client.discovery.start_discovery(name="Discovery-Guide", 30 | discoveryType="Range", ipAddressList="10.255.3.11-10.255.3.19", 31 | protocolOrder="ssh", timeout=5, retry=3, globalCredentialIdList=credentials_ids) 32 | 33 | print('\nWaiting 10 seconds for discovery to be created...') 34 | time.sleep(10) 35 | print(f'\nTask is: {discovery_task_id.response.taskId}') 36 | 37 | discovery_task = dnacenter_client.task.get_task_by_id(task_id=discovery_task_id.response.taskId) 38 | time.sleep(30) 39 | print(f'\nDiscovery ID is: {discovery_task.response.progress}') 40 | 41 | discovery = dnacenter_client.discovery.get_discovered_network_devices_by_discovery_id(id=discovery_task.response.progress) 42 | 43 | device_ips = [device.managementIpAddress for device in discovery.response] 44 | 45 | site_devices = { 46 | "device": device_ips 47 | } 48 | 49 | dnacenter_client.sites.assign_device_to_site(site_id=sites.response[0].id, device=site_devices) 50 | 51 | if __name__ =="__main__": 52 | main() -------------------------------------------------------------------------------- /global-ip-pool/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Global IP Pool guide 2 | 3 | As part of the Site Design, the Global IP Pool endpoints support for the developer to manage IP Address pools in Cisco DNA Center in places like virtual networks for Software Defined Access. 4 | 5 | This script will create a global IP pool to be used for virtual networks in SDA. 6 | 7 | ## Functions 8 | 9 | The [functions](./global-ip-pool-functions.py) file uses python requests library to interact with the API. 10 | 11 | ## License 12 | 13 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 14 | -------------------------------------------------------------------------------- /global-ip-pool/global-ip-pool-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import time 5 | 6 | # Disable SSL warnings. Not needed in production environments with valid certificates 7 | import urllib3 8 | urllib3.disable_warnings() 9 | 10 | # Authentication 11 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 12 | AUTH_URL = '/dna/system/api/v1/auth/token' 13 | USERNAME = '' # Example USERNAME = 'devnetuser' 14 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 15 | 16 | # URLs 17 | GLOBAL_IP_POOLS_URL='/dna/intent/api/v1/global-pool' 18 | 19 | # Get Authentication token 20 | def get_dnac_jwt_token(): 21 | response = requests.post(BASE_URL + AUTH_URL, 22 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 23 | verify=False) 24 | token = response.json()['Token'] 25 | return token 26 | 27 | # Get Global pools 28 | def get_global_ip_pools(headers, query_params): 29 | response = requests.get(BASE_URL + GLOBAL_IP_POOLS_URL, 30 | params=query_params, 31 | headers=headers, verify=False) 32 | return response.json()['response'] 33 | 34 | # Create Global pools 35 | def create_global_ip_pools(headers, pool_information): 36 | response = requests.post(BASE_URL + GLOBAL_IP_POOLS_URL, 37 | json=pool_information, 38 | headers=headers, verify=False) 39 | return response.json() 40 | 41 | def main(): 42 | # obtain the Cisco DNA Center Auth Token 43 | token = get_dnac_jwt_token() 44 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 45 | pool_information = { 46 | "settings": { 47 | "ippool": [ 48 | { 49 | "ipPoolName": "DNAC-Guide_Pool", 50 | "type": "Generic", 51 | "ipPoolCidr": "172.30.200.0/24", 52 | "gateway": "172.30.200.1", 53 | "dhcpServerIps": ["10.255.3.50"], 54 | "dnsServerIps": ["10.255.3.50"], 55 | "IpAddressSpace":"IPv4" 56 | } 57 | ] 58 | } 59 | } 60 | 61 | response = create_global_ip_pools(headers, pool_information) 62 | 63 | time.sleep(5) 64 | 65 | response = get_global_ip_pools(headers, {}) 66 | for credential in response: 67 | print(credential['id'], credential['ipPoolName'], credential['ipPoolCidr']) 68 | 69 | if __name__ == "__main__": 70 | main() 71 | -------------------------------------------------------------------------------- /health-monitoring/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Health Monitoring guide 2 | 3 | Cisco DNA Center Intent APIs provide an easy way for the developer to get an overview of the network health and drill down if needed. 4 | 5 | The APIs seperate the information between wired and wireless clients, but also several health categories: good, fair, idle, amongst others.. 6 | 7 | ## Functions 8 | 9 | The [functions](./health-monitoring-functions.py) file uses python requests library to interact with the API. 10 | 11 | ## SDK 12 | 13 | The [SDK](./health-monitoring-sdk.py) uses [Cisco DNA Center Python SDK](https://pypi.org/project/dnacentersdk/) to simplify the interaction with the API. 14 | 15 | ## License 16 | 17 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 18 | -------------------------------------------------------------------------------- /health-monitoring/health-monitoring-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | 5 | # Disable SSL warnings. Not needed in production environments with valid certificates 6 | import urllib3 7 | urllib3.disable_warnings() 8 | 9 | # Authentication 10 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 11 | AUTH_URL = '/dna/system/api/v1/auth/token' 12 | USERNAME = '' # Example USERNAME = 'devnetuser' 13 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 14 | 15 | # URLs 16 | TASK_BY_ID_URL = '/dna/intent/api/v1/task/{task_id}' 17 | SITE_HEALTH = '/dna/intent/api/v1/site-health' 18 | NETWORK_HEALTH = '/dna/intent/api/v1/network-health' 19 | CLIENT_HEALTH = '/dna/intent/api/v1/client-health' 20 | 21 | # Get Authentication token 22 | def get_dnac_jwt_token(): 23 | response = requests.post(BASE_URL + AUTH_URL, 24 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 25 | verify=False) 26 | token = response.json()['Token'] 27 | return token 28 | 29 | 30 | # Get site health 31 | def get_site_health(headers): 32 | response = requests.get(BASE_URL + SITE_HEALTH, 33 | headers=headers, verify=False) 34 | return response.json()['response'] 35 | 36 | # Get network health 37 | def get_network_health(headers): 38 | response = requests.get(BASE_URL + NETWORK_HEALTH, 39 | headers=headers, verify=False) 40 | return response.json()['response'] 41 | 42 | # Get client health 43 | def get_client_health(headers): 44 | response = requests.get(BASE_URL + CLIENT_HEALTH, 45 | headers=headers, verify=False) 46 | return response.json()['response'] 47 | 48 | 49 | def main(): 50 | # obtain the Cisco DNA Center Auth Token 51 | token = get_dnac_jwt_token() 52 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 53 | 54 | # Site health 55 | print('Printing site health...') 56 | response = get_site_health(headers) 57 | # pp.pprint(response) 58 | for site in response: 59 | print('Site: {0}, Health: {1}'.format(site['siteName'], site['networkHealthAverage'])) 60 | 61 | # Network health 62 | print('\nPrinting network health...') 63 | response = get_network_health(headers) 64 | # pp.pprint(response) 65 | print('Good: {0}, Bad: {1}, Health score: {2}'.format( 66 | response[0]['goodCount'], response[0]['badCount'], 67 | response[0]['healthScore'] 68 | )) 69 | 70 | # Client health 71 | print('\nPrinting client health...') 72 | response = get_client_health(headers) 73 | # pp.pprint(response) 74 | for score in response[0]['scoreDetail']: 75 | print('Type: {0}, Count: {1}, Score: {2}'.format( 76 | score['scoreCategory']['value'], 77 | score['clientCount'], score['scoreValue'])) 78 | try: 79 | for category in score['scoreList']: 80 | print('\tType: {0}, Count: {1}'.format( 81 | category['scoreCategory']['value'], 82 | category['clientCount'])) 83 | except: 84 | pass 85 | 86 | if __name__ == "__main__": 87 | main() -------------------------------------------------------------------------------- /health-monitoring/health-monitoring-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Health Monitoring Example 2 | hosts: cisco 3 | gather_facts: false 4 | vars: 5 | dnac_host: 6 | dnac_username: 7 | dnac_password: 8 | dnac_verify: False 9 | dnac_port: 443 10 | dnac_version: 2.2.2.3 11 | dnac_debug: False 12 | 13 | tasks: 14 | - name: Get all Site Health 15 | cisco.dnac.site_health_info: 16 | dnac_host: "{{dnac_host}}" 17 | dnac_username: "{{dnac_username}}" 18 | dnac_password: "{{dnac_password}}" 19 | dnac_verify: "{{dnac_verify}}" 20 | dnac_port: "{{dnac_port}}" 21 | dnac_version: "{{dnac_version}}" 22 | dnac_debug: "{{dnac_debug}}" 23 | register: site_health 24 | 25 | - name: Print Site Health 26 | ansible.builtin.debug: 27 | var: site_health 28 | 29 | - name: Get all Topology Network Health 30 | cisco.dnac.topology_network_health_info: 31 | dnac_host: "{{dnac_host}}" 32 | dnac_username: "{{dnac_username}}" 33 | dnac_password: "{{dnac_password}}" 34 | dnac_verify: "{{dnac_verify}}" 35 | dnac_port: "{{dnac_port}}" 36 | dnac_version: "{{dnac_version}}" 37 | dnac_debug: "{{dnac_debug}}" 38 | register: network_health 39 | 40 | - name: Print Network Health 41 | ansible.builtin.debug: 42 | var: network_health 43 | 44 | - name: Get all Client Health 45 | cisco.dnac.client_health_info: 46 | dnac_host: "{{dnac_host}}" 47 | dnac_username: "{{dnac_username}}" 48 | dnac_password: "{{dnac_password}}" 49 | dnac_verify: "{{dnac_verify}}" 50 | dnac_port: "{{dnac_port}}" 51 | dnac_version: "{{dnac_version}}" 52 | dnac_debug: "{{dnac_debug}}" 53 | register: client_health 54 | 55 | - name: Print Client Health 56 | ansible.builtin.debug: 57 | var: client_health 58 | 59 | -------------------------------------------------------------------------------- /health-monitoring/health-monitoring-sdk.py: -------------------------------------------------------------------------------- 1 | from dnacentersdk import api 2 | import urllib3 3 | 4 | urllib3.disable_warnings() 5 | 6 | dnac = api.DNACenterAPI(username="", # Example username="devnetuser", 7 | password="", # Example password="Cisco123!", 8 | base_url="", # Example base_url="https://sandboxdnac.cisco.com 9 | version='1.3.3', 10 | verify=False) 11 | 12 | # Site health 13 | print('Printing site health...') 14 | site_health = dnac.sites.get_site_health() 15 | for site in site_health.response: 16 | print('Site: {0}, Health: {1}'.format(site.siteName, site.networkHealthAverage)) 17 | 18 | # Network health 19 | print('\nPrinting network health...') 20 | network_health = dnac.topology.get_overall_network_health() 21 | for network in network_health.response: 22 | print('Good: {0}, Bad: {1}, Health score: {2}'.format( 23 | network.goodCount, network.badCount, 24 | network.healthScore 25 | )) 26 | for network in network_health.healthDistirubution: 27 | print('Category: {0} --> Good: {1}, Bad: {2}, Health score: {3}'.format( 28 | network.category, network.goodCount, 29 | network.badCount, network.healthScore 30 | )) 31 | 32 | # Client health 33 | print('\nPrinting client health...') 34 | client_health = dnac.clients.get_overall_client_health() 35 | for score in client_health.response[0]['scoreDetail']: 36 | print('Type: {0}, Count: {1}, Score: {2}'.format( 37 | score['scoreCategory']['value'], 38 | score['clientCount'], score['scoreValue'])) 39 | try: 40 | for category in score['scoreList']: 41 | print('\tType: {0}, Count: {1}'.format( 42 | category['scoreCategory']['value'], 43 | category['clientCount'])) 44 | except: 45 | pass -------------------------------------------------------------------------------- /hosts: -------------------------------------------------------------------------------- 1 | [cisco] 2 | dnac 3 | -------------------------------------------------------------------------------- /path-trace/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Path Trace guide 2 | 3 | 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. 4 | 5 | It can be used to troubleshoot network problems like packet loss, traffic not reaching its destination because of ACLs or routing issues. 6 | 7 | ## Functions 8 | 9 | The [functions](./path-trace-functions.py) file uses python requests library to interact with the API. 10 | 11 | ## License 12 | 13 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 14 | -------------------------------------------------------------------------------- /path-trace/path-trace-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import sys 5 | import time 6 | 7 | # Disable SSL warnings. Not needed in production environments with valid certificates 8 | import urllib3 9 | urllib3.disable_warnings() 10 | 11 | # Authentication 12 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 13 | AUTH_URL = '/dna/system/api/v1/auth/token' 14 | USERNAME = '' # Example USERNAME = 'devnetuser' 15 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 16 | 17 | 18 | # URLs 19 | DEVICES_URL = '/dna/intent/api/v1/network-device' 20 | PATH_TRACE_URL = '/dna/intent/api/v1/flow-analysis' 21 | PATH_TRACE_ID_URL = '/dna/intent/api/v1/flow-analysis/{flow_analysis_id}' 22 | 23 | def get_dnac_jwt_token(): 24 | response = requests.post(BASE_URL + AUTH_URL, 25 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 26 | verify=False) 27 | token = response.json()['Token'] 28 | return token 29 | 30 | # Get list of devices 31 | def get_devices(headers, query_string_params): 32 | response = requests.get(BASE_URL + DEVICES_URL, 33 | headers = headers, 34 | params = query_string_params, 35 | verify=False) 36 | return response.json()['response'] 37 | 38 | # Create path trace 39 | def create_path_trace(headers, path_trace_payload): 40 | response = requests.post(BASE_URL + PATH_TRACE_URL, headers=headers, 41 | json=path_trace_payload, verify=False) 42 | return response.json()['response'] 43 | 44 | # Get path trace result 45 | def get_path_trace_by_id(headers, flow_analysis_id): 46 | response = requests.get(BASE_URL + PATH_TRACE_ID_URL.format(flow_analysis_id=flow_analysis_id), 47 | headers=headers, verify=False) 48 | return response.json()['response'] 49 | 50 | # Get path trace summary 51 | def get_path_traces_summary(headers, query_string_params): 52 | response = requests.get(BASE_URL + PATH_TRACE_URL, 53 | params=query_string_params, 54 | headers=headers, verify=False) 55 | return response.json()['response'] 56 | 57 | 58 | # Delete path trace 59 | def delete_path_trace(headers, flow_analysis_id): 60 | response = requests.delete(BASE_URL + PATH_TRACE_ID_URL.format(flow_analysis_id=flow_analysis_id), 61 | headers=headers, verify=False) 62 | return response 63 | 64 | 65 | def main(): 66 | # obtain the Cisco DNA Center Auth Token 67 | token = get_dnac_jwt_token() 68 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 69 | 70 | # Get src device IP 71 | print('Printing source device IP ...') 72 | query_string_params = {'hostname': 'CSR1Kv-01.devnet.local'} 73 | response = get_devices(headers, query_string_params) 74 | src_ip_address = response[0]['managementIpAddress'] 75 | print(src_ip_address) 76 | 77 | # print devices list 78 | print('\nPrinting destination device IP ...') 79 | query_string_params = {'hostname': 'CSR1Kv-09.devnet.local'} 80 | response = get_devices(headers, query_string_params) 81 | dst_ip_address = response[0]['managementIpAddress'] 82 | print(dst_ip_address) 83 | 84 | # Generate a new path trace 85 | print('\nPrinting flow analysis id ...') 86 | path_trace_payload = { 87 | 'sourceIP': src_ip_address, 88 | 'destIP': dst_ip_address, 89 | 'inclusions': [ 90 | 'INTERFACE-STATS', 91 | 'DEVICE-STATS', 92 | 'ACL-TRACE', 93 | 'QOS-STATS' 94 | ], 95 | 'protocol': 'icmp' 96 | } 97 | response = create_path_trace(headers, path_trace_payload) 98 | flow_analysis_id = response['flowAnalysisId'] 99 | print(flow_analysis_id) 100 | 101 | # Waiting until the path trace is done 102 | time.sleep(10) 103 | 104 | # Get path trace result 105 | print('\nPrinting path trace result ...') 106 | response = get_path_trace_by_id(headers, flow_analysis_id) 107 | print(response) 108 | 109 | # Get path traces summary 110 | print('\nPrinting path trace summary...') 111 | query_string_params = {'destIP': dst_ip_address, 'limit': 2} 112 | response = get_path_traces_summary(headers, query_string_params) 113 | print(response) 114 | 115 | # Delete path trace summary 116 | print('\nPrinting path trace delete status code ...') 117 | response = delete_path_trace(headers, flow_analysis_id) 118 | print(response.status_code) 119 | 120 | 121 | if __name__ == "__main__": 122 | main() 123 | -------------------------------------------------------------------------------- /path-trace/path-trace-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Path Trace Example 2 | hosts: localhost 3 | connection: local 4 | gather_facts: false 5 | vars: 6 | dnac_host: 7 | dnac_username: 8 | dnac_password: 9 | dnac_verify: False 10 | dnac_port: 443 11 | dnac_version: 2.2.2.3 12 | dnac_debug: True 13 | 14 | tasks: 15 | - name: Get Network Device Filtered by Hostname 16 | cisco.dnac.network_device_info: 17 | dnac_host: "{{dnac_host}}" 18 | dnac_username: "{{dnac_username}}" 19 | dnac_password: "{{dnac_password}}" 20 | dnac_verify: "{{dnac_verify}}" 21 | dnac_port: "{{dnac_port}}" 22 | dnac_version: "{{dnac_version}}" 23 | dnac_debug: "{{dnac_debug}}" 24 | hostname: 25 | - "ISR-4K--Branch-SFO" # CHANGE ME 26 | register: network_devices_by_hostname 27 | 28 | - name: Get Network Device Filtered by Hostname 29 | cisco.dnac.network_device_info: 30 | dnac_host: "{{dnac_host}}" 31 | dnac_username: "{{dnac_username}}" 32 | dnac_password: "{{dnac_password}}" 33 | dnac_verify: "{{dnac_verify}}" 34 | dnac_port: "{{dnac_port}}" 35 | dnac_version: "{{dnac_version}}" 36 | dnac_debug: "{{dnac_debug}}" 37 | hostname: 38 | - "C9K-Branch-SFO.clus-demo.com" # CHANGE ME 39 | register: network_devices_by_hostname_2 40 | 41 | - name: Create path trace 42 | cisco.dnac.path_trace: 43 | dnac_host: "{{dnac_host}}" 44 | dnac_username: "{{dnac_username}}" 45 | dnac_password: "{{dnac_password}}" 46 | dnac_verify: "{{dnac_verify}}" 47 | dnac_port: "{{dnac_port}}" 48 | dnac_version: "{{dnac_version}}" 49 | dnac_debug: "{{dnac_debug}}" 50 | sourceIP: "{{network_devices_by_hostname.dnac_response.response[0].managementIpAddress}}" 51 | destIP: "{{network_devices_by_hostname_2.dnac_response.response[0].managementIpAddress}}" 52 | inclusions: 53 | - INTERFACE-STATS 54 | - DEVICE-STATS 55 | - ACL-TRACE 56 | - QOS-STATS 57 | protocol: icmp 58 | flowAnalysisId: "" 59 | register: path_trace_task 60 | 61 | - name: Sleep for 60 seconds and continue with play 62 | wait_for: 63 | timeout: 60 64 | delegate_to: localhost 65 | 66 | - name: Get Path Trace 67 | cisco.dnac.path_trace_info: 68 | dnac_host: "{{dnac_host}}" 69 | dnac_username: "{{dnac_username}}" 70 | dnac_password: "{{dnac_password}}" 71 | dnac_verify: "{{dnac_verify}}" 72 | dnac_port: "{{dnac_port}}" 73 | dnac_version: "{{dnac_version}}" 74 | dnac_debug: "{{dnac_debug}}" 75 | flowAnalysisId: "{{path_trace_task.dnac_response.response.flowAnalysisId}}" 76 | register: path_trace 77 | 78 | - name: Print Path Trace 79 | ansible.builtin.debug: 80 | var: path_trace -------------------------------------------------------------------------------- /path-trace/path-trace-sdk.py: -------------------------------------------------------------------------------- 1 | from dnacentersdk import api 2 | import urllib3 3 | import time 4 | import pprint 5 | urllib3.disable_warnings() 6 | 7 | pp = pprint.PrettyPrinter(indent=4) 8 | 9 | dnac_url = "https://" 10 | dnac_username = "" 11 | dnac_password = "" 12 | dnac_ssl_verify=False 13 | dnac_version = "2.2.2.3" 14 | 15 | dnacenter_client = api.DNACenterAPI(base_url=dnac_url, username=dnac_username, password=dnac_password, 16 | version=dnac_version, verify=dnac_ssl_verify) 17 | 18 | 19 | network_devices = dnacenter_client.devices.get_device_list(hostname='ISR-4K--Branch-SFO') # CHANGE ME 20 | network_devices_2 = dnacenter_client.devices.get_device_list(hostname='C9K-Branch-SFO.clus-demo.com') #CHANGE ME 21 | 22 | 23 | path_trace = dnacenter_client.path_trace.initiate_a_new_pathtrace(destIP=network_devices_2.response[0].managementIpAddress, 24 | sourceIP=network_devices.response[0].managementIpAddress, 25 | inclusions=['INTERFACE-STATS', 'DEVICE-STATS', 'ACL-TRACE', 'QOS-STATS'], 26 | protocol='icmp') 27 | 28 | time.sleep(60) 29 | flow_analysis = dnacenter_client.path_trace.retrieves_previous_pathtrace(flow_analysis_id=path_trace.response.flowAnalysisId) 30 | pp.pprint(flow_analysis) 31 | -------------------------------------------------------------------------------- /rma/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Authentication guide 2 | 3 | Return merchandise authorization (RMA) APIs simplify the workflow to replace a faulty device with an existing device with the same characteristics (model, platform, etc). Using this functionality, Cisco DNA Centers manages the configuration changes and all relevant logical tasks to replace the old device with the new one, minimizing human error and speeding up the recovery process 4 | 5 | This script triggers the RMA workflow. 6 | 7 | ## Functions 8 | 9 | The [functions](./rma-functions.py) file uses python requests library to interact with the API. 10 | 11 | ## License 12 | 13 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 14 | -------------------------------------------------------------------------------- /rma/rma-functions.py: -------------------------------------------------------------------------------- 1 | # Module import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | 5 | # Disable SSL warnings. Not needed in production environments with valid certificates 6 | import urllib3 7 | urllib3.disable_warnings() 8 | 9 | # Authentication 10 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 11 | AUTH_URL = '/dna/system/api/v1/auth/token' 12 | USERNAME = '' # Example USERNAME = 'devnetuser' 13 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 14 | 15 | # URLs 16 | DEVICE_REPLACEMENT_URL = '/dna/intent/api/v1/device-replacement' 17 | DEVICE_REPLACEMENT_WORKFLOW_URL = '/dna/intent/api/v1/device-replacement/workflow' 18 | 19 | 20 | # Get Authentication token 21 | def get_dnac_jwt_token(): 22 | response = requests.post(BASE_URL + AUTH_URL, 23 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 24 | verify=False) 25 | token = response.json()['Token'] 26 | return token 27 | 28 | # Mark device for replacement 29 | def mark_device_for_replacement(headers, device_info): 30 | response = requests.post(BASE_URL + DEVICE_REPLACEMENT_URL, 31 | headers=headers, json=device_info, 32 | verify=False) 33 | return response.json() 34 | 35 | # Trigger device replacement workflow 36 | def trigger_device_replacement_workflow(headers, device_info): 37 | response = requests.post(BASE_URL + DEVICE_REPLACEMENT_WORKFLOW_URL, 38 | headers=headers, json=device_info, 39 | verify=False) 40 | return response.json() 41 | 42 | def main(): 43 | # obtain the Cisco DNA Center Auth Token 44 | token = get_dnac_jwt_token() 45 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 46 | 47 | # Mark device for replacement 48 | faulty_serial_id = '' 49 | device_info = [ 50 | { 51 | "faultyDeviceSerialNumber": faulty_serial_id 52 | } 53 | ] 54 | 55 | response = mark_device_for_replacement(headers, device_info) 56 | 57 | # Device replacement worklfow 58 | replacement_serial_id = '' 59 | device_info = [ 60 | { 61 | "faultyDeviceSerialNumber": faulty_serial_id, 62 | "replacementDeviceSerialNumber": replacement_serial_id 63 | } 64 | ] 65 | 66 | response = trigger_device_replacement_workflow(headers, device_info) 67 | 68 | 69 | if __name__ == "__main__": 70 | main() 71 | -------------------------------------------------------------------------------- /sda/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Software Defined Access guide 2 | 3 | Software Defined Access (SDA) API allows the developer to manage SDA network using Cisco DNA Center. It is a powerful set of APIs that the user can use to create and manage border, edge and control plane devices, assign ports for user devices and access points and deploy a SDA fabric. 4 | 5 | This script creates a software defined access fabric. 6 | 7 | ## Functions 8 | 9 | The [functions](./sda-functions.py) file uses python requests library to interact with the API. 10 | 11 | ## License 12 | 13 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 14 | -------------------------------------------------------------------------------- /sda/sda-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import sys 5 | import time 6 | 7 | # Disable SSL warnings. Not needed in production environments with valid certificates 8 | import urllib3 9 | urllib3.disable_warnings() 10 | 11 | # Authentication 12 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 13 | AUTH_URL = '/dna/system/api/v1/auth/token' 14 | USERNAME = '' # Example USERNAME = 'devnetuser' 15 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 16 | 17 | # URLs 18 | SDA_FABRIC_URL = '/dna/intent/api/v1/business/sda/fabric' 19 | SITE_URL = '/dna/intent/api/v1/site' 20 | SITE_FABRIC_URL = '/dna/intent/api/v1/business/sda/fabric-site' 21 | FABRIC_CONTROL_PLANE_URL = '/dna/intent/api/v1/business/sda/control-plane-device' 22 | FABRIC_BORDER_URL = '/dna/intent/api/v1/business/sda/border-device' 23 | FABRIC_EDGE_URL = '/dna/intent/api/v1/business/sda/edge-device' 24 | SDA_AUTHENTICATION_PROFILE_URL = '/dna/intent/api/v1/business/sda/authentication-profile' 25 | VIRTUAL_NETWORK_URL = '/dna/intent/api/v1/business/sda/virtual-network' 26 | VIRTUAL_NETWORK_IPPOOL_URL = '/dna/intent/api/v1/business/sda/virtual-network/ippool' 27 | SDA_PORT_ASSIGNMENT_URL = '/dna/intent/api/v1/business/sda/hostonboarding/user-device' 28 | SDA_AP_ASSIGNMENT_URL = '/dna/intent/api/v1/business/sda/hostonboarding/access-point' 29 | 30 | def get_dnac_jwt_token(): 31 | response = requests.post(BASE_URL + AUTH_URL, 32 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 33 | verify=False) 34 | token = response.json()['Token'] 35 | return token 36 | 37 | # Get list of devices 38 | def get_sda_fabric(headers, query_string_params): 39 | response = requests.get(BASE_URL + SDA_FABRIC_URL, 40 | headers = headers, params = query_string_params, 41 | verify=False) 42 | return response.json() 43 | 44 | # Create sda fabric 45 | def create_sda_fabric(headers, sda_fabric_info): 46 | response = requests.post(BASE_URL + SDA_FABRIC_URL, headers=headers, 47 | json=sda_fabric_info, verify=False) 48 | return response.json() 49 | 50 | # Get list of sites 51 | def get_sites(headers, site_params): 52 | response = requests.get(BASE_URL + SITE_URL, headers=headers, 53 | params=site_params, verify=False) 54 | return response.json()['response'] 55 | 56 | # Add site to SDA fabric 57 | def add_site_to_sda_fabric(headers, site_fabric_info): 58 | response = requests.post(BASE_URL + SITE_FABRIC_URL, headers=headers, 59 | json=site_fabric_info, verify=False) 60 | return response.json() 61 | 62 | # Add Control Plane device 63 | def add_control_plane_device(headers, device_info): 64 | response = requests.post(BASE_URL + FABRIC_CONTROL_PLANE_URL, headers=headers, 65 | json=device_info, verify=False) 66 | return response.json() 67 | 68 | # Add Border device 69 | def add_border_device(headers, device_info): 70 | response = requests.post(BASE_URL + FABRIC_BORDER_URL, headers=headers, 71 | json=device_info, verify=False) 72 | return response.json() 73 | 74 | # Add Edge device 75 | def add_edge_device(headers, device_info): 76 | response = requests.post(BASE_URL + FABRIC_EDGE_URL, headers=headers, 77 | json=device_info, verify=False) 78 | return response.json() 79 | 80 | # Add default authentication profile in SDA Fabric 81 | def add_authentication_profile(headers, authentication_profile): 82 | response = requests.post(BASE_URL + SDA_AUTHENTICATION_PROFILE_URL, headers=headers, 83 | json=authentication_profile, verify=False) 84 | return response.json() 85 | 86 | # Add virtual network in the SDA Fabric 87 | def add_virtual_network(headers, virtual_network): 88 | response = requests.post(BASE_URL + VIRTUAL_NETWORK_URL, headers=headers, 89 | json=virtual_network, verify=False) 90 | return response.json() 91 | 92 | # Add Virtual Network IP Pool 93 | def add_virtual_network_ip_pool(headers, ip_pool): 94 | response = requests.post(BASE_URL + VIRTUAL_NETWORK_IPPOOL_URL, headers=headers, 95 | json=ip_pool, verify=False) 96 | return response.json() 97 | 98 | # Add user device port assignment 99 | def add_user_device_port_assignment(headers, port_assignment): 100 | response = requests.post(BASE_URL + SDA_PORT_ASSIGNMENT_URL, headers=headers, 101 | json=port_assignment, verify=False) 102 | return response.json() 103 | 104 | # Add port assignment 105 | def add_ap_port_assignment(headers, port_assignment): 106 | response = requests.post(BASE_URL + SDA_PORT_ASSIGNMENT_URL, headers=headers, 107 | json=port_assignment, verify=False) 108 | return response.json() 109 | 110 | 111 | 112 | def main(): 113 | # obtain the Cisco DNA Center Auth Token 114 | token = get_dnac_jwt_token() 115 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 116 | 117 | # Create SDA Fabric 118 | sda_fabric_info = { 119 | "fabricName": "DNAC_Guide_Fabric" 120 | } 121 | response = create_sda_fabric(headers, sda_fabric_info) 122 | 123 | site_fabric_info = { 124 | "fabricName": "DNAC_Guide_Fabric", 125 | "siteNameHierarchy": "Global/DNA_Center_Guide/DNA_Center_Guide_Building" 126 | } 127 | 128 | response = add_site_to_sda_fabric(headers, site_fabric_info) 129 | 130 | authentication_profile = [ 131 | { 132 | "siteNameHierarchy": "Global/DNA_Center_Guide/DNA_Center_Guide_Building", 133 | "authenticateTemplateName": "No Authentication" 134 | } 135 | ] 136 | 137 | response = add_authentication_profile(headers, authentication_profile) 138 | 139 | virtual_network_info = { 140 | "virtualNetworkName": "INFRA_VN", 141 | "siteNameHierarchy": "Global/DNA_Center_Guide/DNA_Center_Guide_Building" 142 | } 143 | 144 | response = add_virtual_network(headers, virtual_network_info) 145 | 146 | ip_pool_info = [ 147 | { 148 | "virtualNetworkName": "INFRA_VN", 149 | "ipPoolName": "data_ip_pool1", 150 | "trafficType": "DATA", 151 | "authenticationPolicyName": "No Authentication", 152 | "scalableGroupName": "", 153 | "isL2FloodingEnabled": True, 154 | "isThisCriticalPool": True, 155 | "poolType": "AP" 156 | } 157 | ] 158 | 159 | response = add_virtual_network_ip_pool(headers, ip_pool_info) 160 | 161 | ip_pool_info = [ 162 | { 163 | "virtualNetworkName": "INFRA_VN", 164 | "ipPoolName": "voice_ip_pool1", 165 | "trafficType": "VOICE", 166 | "authenticationPolicyName": "No Authentication", 167 | "scalableGroupName": "", 168 | "isL2FloodingEnabled": True, 169 | "isThisCriticalPool": True, 170 | "poolType": "Extended" 171 | } 172 | ] 173 | 174 | response = add_virtual_network_ip_pool(headers, ip_pool_info) 175 | 176 | device_info = [ 177 | { 178 | "deviceManagementIpAddress": "10.195.192.96", 179 | "siteNameHierarchy": "Global/DNA_Center_Guide/DNA_Center_Guide_Building" 180 | } 181 | ] 182 | 183 | response = add_control_plane_device(headers, device_info) 184 | print(response) 185 | 186 | device_info = [ 187 | { 188 | "deviceManagementIpAddress": "10.195.192.95", 189 | "siteNameHierarchy": "Global/DNA_Center_Guide/DNA_Center_Guide_Building", 190 | "externalDomainRoutingProtocolName": "BGP", 191 | "externalConnectivityIpPoolName": "sda_1", 192 | "internalAutonomouSystemNumber": "65002", 193 | "borderSessionType": "EXTERNAL", 194 | "connectedToInternet": False, 195 | "externalConnectivitySettings": [ 196 | { 197 | "interfaceName": "FortyGigabitEthernet1/1/1", 198 | "externalAutonomouSystemNumber": "65003", 199 | "l3Handoff": [ 200 | { 201 | "virtualNetwork": { 202 | "virtualNetworkName": "INFRA_VN" 203 | } 204 | } 205 | ] 206 | } 207 | ] 208 | } 209 | ] 210 | response = add_border_device(headers, device_info) 211 | 212 | device_info = [ 213 | { 214 | "deviceManagementIpAddress": "10.195.192.95", 215 | "siteNameHierarchy": "Global/DNA_Center_Guide/DNA_Center_Guide_Building" 216 | } 217 | ] 218 | 219 | response = add_edge_device(headers, device_info) 220 | 221 | port_assignment_info = [ 222 | { 223 | "siteNameHierarchy": "Global/DNA_Center_Guide/DNA_Center_Guide_Building", 224 | "deviceManagementIpAddress": "10.195.192.95", 225 | "interfaceName": "GigabitEthernet1/0/1", 226 | "dataIpAddressPoolName": "data_ip_pool1", 227 | "voiceIpAddressPoolName": "voice_ip_pool1", 228 | "scalableGroupName": "testGroupName", 229 | "authenticateTemplateName": "No Authentication" 230 | } 231 | ] 232 | 233 | response = add_user_device_port_assignment(headers, port_assignment_info) 234 | 235 | port_assignment_info = [ 236 | { 237 | "siteNameHierarchy": "Global/DNA_Center_Guide/DNA_Center_Guide_Building", 238 | "deviceManagementIpAddress": "10.195.192.95", 239 | "interfaceName": "GigabitEthernet1/0/1", 240 | "addressPoolName": "data_ip_pool1", 241 | "authenticateTemplateName": "No Authentication" 242 | } 243 | ] 244 | response = add_ap_port_assignment(headers, port_assignment_info) 245 | 246 | if __name__ == "__main__": 247 | main() 248 | -------------------------------------------------------------------------------- /sites/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Authentication guide 2 | 3 | ## Sites API 4 | 5 | Site API allows the user to create a hierarchy on its network with area, buildings and floors, thus replicating the physical world infrastructure and grouping devices on Cisco DNA Center like they are setup in the real world. 6 | 7 | Once the hierarchy is created and has devices assigned to it, it is possible to use the Site API to query the site health. 8 | 9 | This script creates and retrieves sites information. 10 | 11 | ## Functions 12 | 13 | The [functions](./sites-functions.py) file uses python requests library to interact with the API. 14 | 15 | ## License 16 | 17 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 18 | -------------------------------------------------------------------------------- /sites/sites-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import time 5 | import sys 6 | 7 | # Disable SSL warnings. Not needed in production environments with valid certificates 8 | import urllib3 9 | urllib3.disable_warnings() 10 | 11 | # Authentication 12 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 13 | AUTH_URL = '/dna/system/api/v1/auth/token' 14 | USERNAME = '' # Example USERNAME = 'devnetuser' 15 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 16 | 17 | # URLs 18 | SITE_URL = '/dna/intent/api/v1/site' 19 | SITE_COUNT_URL = '/dna/intent/api/v1/site/count' 20 | MEMBERSHIP_SITE_URL = '/dna/intent/api/v1/membership/{site_id}' 21 | SITE_HEALTH_URL = '/dna/intent/api/v1/site-health' 22 | 23 | # Get Authentication token 24 | def get_dnac_jwt_token(): 25 | response = requests.post(BASE_URL + AUTH_URL, 26 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 27 | verify=False) 28 | token = response.json()['Token'] 29 | return token 30 | 31 | # Create site 32 | def create_site(headers, site): 33 | headers['__runsync'] = 'true' 34 | headers['__runsynctimeout'] = '30' 35 | response = requests.post(BASE_URL + SITE_URL, 36 | headers=headers, json=site, 37 | verify=False) 38 | return response.json() 39 | 40 | # Get list of site's devices 41 | def get_site_devices(headers, site_id): 42 | response = requests.get(BASE_URL + MEMBERSHIP_SITE_URL.format(site_id=site_id), 43 | headers=headers, verify=False) 44 | return response.json()['site']['response'] 45 | 46 | # Get list of sites 47 | def get_sites(headers): 48 | response = requests.get(BASE_URL + SITE_URL, 49 | headers=headers, verify=False) 50 | return response.json()['response'] 51 | 52 | # Get sites count 53 | def get_site_count(headers): 54 | response = requests.get(BASE_URL + SITE_COUNT_URL, 55 | headers=headers, verify=False) 56 | return response.json()['response'] 57 | 58 | # Get site's health 59 | def get_site_health(headers, site_id): 60 | response = requests.get(BASE_URL + SITE_HEALTH_URL.format(site_id=site_id), 61 | headers=headers, verify=False) 62 | return response.json()['response'] 63 | 64 | 65 | 66 | def main(): 67 | # obtain the Cisco DNA Center Auth Token 68 | token = get_dnac_jwt_token() 69 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 70 | 71 | site_area = { 72 | "type": "area", 73 | "site": { 74 | "area": { 75 | "name": "DNA Center Guide", 76 | "parentName": "Global" 77 | } 78 | } 79 | } 80 | 81 | print('Printing area ID...') 82 | response = create_site(headers, site_area) 83 | area_id = response['siteId'] 84 | print(area_id) 85 | 86 | site_building = { 87 | "type": "building", 88 | "site": { 89 | "building": { 90 | "name": "DNA Center Guide Building", 91 | "parentName": "Global/DNA Center Guide", 92 | "latitude": "37.409424", 93 | "longitude": "-121.928868" 94 | } 95 | } 96 | } 97 | 98 | print('\nPrinting building ID...') 99 | response = create_site(headers, site_building) 100 | building_id = response['siteId'] 101 | print(building_id) 102 | 103 | print('\nPrinting site count...') 104 | response = get_site_count(headers) 105 | print(response) 106 | 107 | print('\nPrinting sites') 108 | response = get_sites(headers) 109 | print(response) 110 | 111 | print('\nPrinting site\'s devices') 112 | site_id = response[1]['id'] 113 | response = get_site_devices(headers, site_id) 114 | print(len(response)) 115 | 116 | print('\nPrinting site\'s health') 117 | response = get_site_health(headers, site_id) 118 | site_name = response[0]['siteName'] 119 | site_health = response[0]['healthyNetworkDevicePercentage'] 120 | print('Name: {site_name}, Average health: {site_health}' 121 | .format(site_name=site_name, site_health=site_health)) 122 | 123 | 124 | 125 | 126 | if __name__ == "__main__": 127 | main() -------------------------------------------------------------------------------- /sites/sites-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Sites Example 2 | hosts: localhost 3 | connection: local 4 | gather_facts: false 5 | vars: 6 | dnac_host: 7 | dnac_username: 8 | dnac_password: 9 | dnac_verify: False 10 | dnac_port: 443 11 | dnac_version: 2.2.2.3 12 | dnac_debug: False 13 | 14 | tasks: 15 | - name: Create site Altus 16 | cisco.dnac.site_create: 17 | dnac_host: "{{dnac_host}}" 18 | dnac_username: "{{dnac_username}}" 19 | dnac_password: "{{dnac_password}}" 20 | dnac_verify: "{{dnac_verify}}" 21 | dnac_port: "{{dnac_port}}" 22 | dnac_version: "{{dnac_version}}" 23 | dnac_debug: "{{dnac_debug}}" 24 | site: 25 | area: 26 | name: "Altus" 27 | parentName: "Global/Costa Rica" 28 | type: area 29 | 30 | - name: Create building HQ 31 | cisco.dnac.site_create: 32 | dnac_host: "{{dnac_host}}" 33 | dnac_username: "{{dnac_username}}" 34 | dnac_password: "{{dnac_password}}" 35 | dnac_verify: "{{dnac_verify}}" 36 | dnac_port: "{{dnac_port}}" 37 | dnac_version: "{{dnac_version}}" 38 | dnac_debug: "{{dnac_debug}}" 39 | site: 40 | building: 41 | address: "C022, A077" 42 | latitude: 9.7489 43 | longitude: -83.7534 44 | name: "HQ" 45 | parentName: "Global/Costa Rica/Altus" 46 | type: building 47 | 48 | - name: Create floor 4 49 | cisco.dnac.site_create: 50 | dnac_host: "{{dnac_host}}" 51 | dnac_username: "{{dnac_username}}" 52 | dnac_password: "{{dnac_password}}" 53 | dnac_verify: "{{dnac_verify}}" 54 | dnac_port: "{{dnac_port}}" 55 | dnac_version: "{{dnac_version}}" 56 | dnac_debug: "{{dnac_debug}}" 57 | site: 58 | floor: 59 | height: 100 60 | length: 100 61 | width: 20 62 | rfModel: "Drywall Office Only" 63 | name: "Floor4" 64 | parentName: "Global/Costa Rica/Altus/HQ" 65 | type: floor 66 | 67 | - name: Get all Site 68 | cisco.dnac.site_info: 69 | dnac_host: "{{dnac_host}}" 70 | dnac_username: "{{dnac_username}}" 71 | dnac_password: "{{dnac_password}}" 72 | dnac_verify: "{{dnac_verify}}" 73 | dnac_port: "{{dnac_port}}" 74 | dnac_version: "{{dnac_version}}" 75 | dnac_debug: "{{dnac_debug}}" 76 | register: sites 77 | 78 | 79 | - name: Print all sites 80 | ansible.builtin.debug: 81 | var: sites -------------------------------------------------------------------------------- /sites/sites-sdk.py: -------------------------------------------------------------------------------- 1 | from dnacentersdk import api 2 | import urllib3 3 | import time 4 | import pprint 5 | urllib3.disable_warnings() 6 | 7 | pp = pprint.PrettyPrinter(indent=4) 8 | 9 | dnac_url = "https://" 10 | dnac_username = "" 11 | dnac_password = "" 12 | dnac_ssl_verify=False 13 | dnac_version = "2.2.2.3" 14 | 15 | dnacenter_client = api.DNACenterAPI(base_url=dnac_url, username=dnac_username, password=dnac_password, 16 | version=dnac_version, verify=dnac_ssl_verify) 17 | 18 | area = { 19 | "type": "area", 20 | "site": { 21 | "area": { 22 | "name": "Altus", 23 | "parentName": "Global/Costa Rica" 24 | } 25 | } 26 | } 27 | 28 | dnacenter_client.sites.create_site(payload=area) 29 | 30 | building = { 31 | "type": "building", 32 | "site": { 33 | "building": { 34 | "name": "HQ", 35 | "parentName": "Global/Costa Rica/Altus", 36 | "latitude": 9.7489, 37 | "longitude": -83.7534 38 | } 39 | } 40 | } 41 | 42 | dnacenter_client.sites.create_site(payload=building) 43 | 44 | floor = { 45 | "type": "floor", 46 | "site": { 47 | "floor": { 48 | "name": "Floor4", 49 | "parentName": "Global/Costa Rica/Altus/HQ", 50 | "height": 100, 51 | "length": 100, 52 | "width": 20, 53 | "rfModel": "Drywall Office Only" 54 | } 55 | } 56 | } 57 | 58 | dnacenter_client.sites.create_site(payload=floor) 59 | 60 | sites = dnacenter_client.sites.get_site() 61 | pp.pprint(sites) 62 | 63 | -------------------------------------------------------------------------------- /swim/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Software Image Management guide 2 | 3 | ## Software Image Management (SWIM) API 4 | 5 | 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. 6 | 7 | This script triggers the software image management workflow to upgrade the image of a device. 8 | 9 | ## Functions 10 | 11 | The [functions](./swim-functions.py) file uses python requests library to interact with the API. 12 | 13 | ## License 14 | 15 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 16 | -------------------------------------------------------------------------------- /swim/swim-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import time 5 | 6 | # Disable SSL warnings. Not needed in production environments with valid certificates 7 | import urllib3 8 | urllib3.disable_warnings() 9 | 10 | # Authentication 11 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 12 | AUTH_URL = '/dna/system/api/v1/auth/token' 13 | USERNAME = '' # Example USERNAME = 'devnetuser' 14 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 15 | 16 | # URLs 17 | DEVICES_URL = '/dna/intent/api/v1/network-device' 18 | SOFTWARE_IMAGE_URL='/dna/intent/api/v1/image/importation' 19 | SOFTWARE_IMAGE_IMPORT_URL = '/dna/intent/api/v1/image/importation/source/url' 20 | SOFTWARE_IMAGE_DISTRIBUTION_URL = '/dna/intent/api/v1/image/distribution' 21 | SOFTWARE_IMAGE_ACTIVATION_URL = '/dna/intent/api/v1/image/activation/device' 22 | TASK_BY_ID_URL = '/dna/intent/api/v1/task/{task_id}' 23 | 24 | # Get Authentication token 25 | def get_dnac_jwt_token(): 26 | response = requests.post(BASE_URL + AUTH_URL, 27 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 28 | verify=False) 29 | token = response.json()['Token'] 30 | return token 31 | 32 | # Import software image 33 | def import_image(headers, import_info): 34 | response = requests.post(BASE_URL + SOFTWARE_IMAGE_IMPORT_URL, 35 | json=import_info, 36 | headers=headers, verify=False) 37 | return response.json()['response'] 38 | 39 | # Get Software Images 40 | def get_software_images(headers, query_params): 41 | response = requests.get(BASE_URL + SOFTWARE_IMAGE_URL, 42 | params=query_params, 43 | headers=headers, verify=False) 44 | return response.json()['response'] 45 | 46 | # Get devices 47 | def get_devices(headers, query_params): 48 | response = requests.get(BASE_URL + DEVICES_URL, 49 | params=query_params, 50 | headers=headers, verify=False) 51 | return response.json()['response'] 52 | 53 | # Distribute image 54 | def distribute_image(headers, distribution_info): 55 | response = requests.post(BASE_URL + SOFTWARE_IMAGE_DISTRIBUTION_URL, 56 | json=distribution_info, 57 | headers=headers, verify=False) 58 | return response.json()['response'] 59 | 60 | # Activate image 61 | def activate_image(headers, activation_info): 62 | response = requests.post(BASE_URL + SOFTWARE_IMAGE_ACTIVATION_URL, 63 | json=activation_info, 64 | headers=headers, verify=False) 65 | return response.json()['response'] 66 | 67 | # Get Task result 68 | def get_task(headers, task_id): 69 | response = requests.get(BASE_URL + TASK_BY_ID_URL.format(task_id=task_id), 70 | headers=headers, verify=False) 71 | return response.json()['response'] 72 | 73 | 74 | def main(): 75 | # obtain the Cisco DNA Center Auth Token 76 | token = get_dnac_jwt_token() 77 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 78 | 79 | # Import image 80 | import_info = { 81 | "sourceURL": "http://10.104.49.64/cat3k_caa_universalk9.16.12.03a.SPA.bin" 82 | } 83 | response = import_image(headers, import_info) 84 | 85 | time.sleep(30) 86 | 87 | # Get software images 88 | query_params ={ 89 | 'family': 'cat3k' 90 | } 91 | response = get_software_images(headers, query_params) 92 | image_id = response[0]['imageUuid'] 93 | 94 | # Get device 95 | query_params = { 96 | 'hostname': 'CAT3K-03.devnet.local' 97 | } 98 | 99 | response = get_devices(headers, query_params) 100 | device_id = response[0]['id'] 101 | 102 | distribution_info = [ 103 | { 104 | 'deviceUuid': device_id, 105 | 'imageUuid': image_id 106 | } 107 | ] 108 | 109 | response = distribute_image(headers, distribution_info) 110 | task_id = response['taskId'] 111 | 112 | time.sleep(10) 113 | 114 | response = get_task(headers, task_id) 115 | print(response['data']) 116 | 117 | activate_info = [ 118 | { 119 | 'deviceUuid': device_id, 120 | 'imageUuidList': [ 121 | image_id 122 | ] 123 | } 124 | ] 125 | 126 | response = activate_image(headers, activate_info) 127 | task_id = response['taskId'] 128 | 129 | time.sleep(10) 130 | 131 | response = get_task(headers, task_id) 132 | print(response['data']) 133 | 134 | if __name__ == "__main__": 135 | main() -------------------------------------------------------------------------------- /swim/swim-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Software Image Management Example 2 | hosts: localhost 3 | connection: local 4 | gather_facts: false 5 | vars: 6 | dnac_host: 192.168.196.2 7 | dnac_username: altus 8 | dnac_password: Altus123 9 | dnac_verify: False 10 | dnac_port: 443 11 | dnac_version: 2.2.2.3 12 | dnac_debug: True 13 | image_url: http://10.104.49.64/cat3k_caa_universalk9.16.12.03a.SPA.bin 14 | 15 | tasks: 16 | - name: Import Image from URL 17 | cisco.dnac.swim_import_via_url: 18 | dnac_host: "{{dnac_host}}" 19 | dnac_username: "{{dnac_username}}" 20 | dnac_password: "{{dnac_password}}" 21 | dnac_verify: "{{dnac_verify}}" 22 | dnac_port: "{{dnac_port}}" 23 | dnac_version: "{{dnac_version}}" 24 | dnac_debug: "{{dnac_debug}}" 25 | payload: 26 | - sourceURL: "{{image_url}}" 27 | 28 | - name: Pause until image is imported 29 | ansible.builtin.pause: 30 | seconds: 30 31 | 32 | - name: Get all Swim Image Details 33 | cisco.dnac.swim_image_details_info: 34 | dnac_host: "{{dnac_host}}" 35 | dnac_username: "{{dnac_username}}" 36 | dnac_password: "{{dnac_password}}" 37 | dnac_verify: "{{dnac_verify}}" 38 | dnac_port: "{{dnac_port}}" 39 | dnac_version: "{{dnac_version}}" 40 | dnac_debug: "{{dnac_debug}}" 41 | family: cat3k 42 | register: software_images 43 | 44 | - name: Print software software images 45 | ansible.builtin.debug: 46 | var: software_images 47 | 48 | - name: Get all Network Device 49 | cisco.dnac.network_device_info: 50 | dnac_host: "{{dnac_host}}" 51 | dnac_username: "{{dnac_username}}" 52 | dnac_password: "{{dnac_password}}" 53 | dnac_verify: "{{dnac_verify}}" 54 | dnac_port: "{{dnac_port}}" 55 | dnac_version: "{{dnac_version}}" 56 | dnac_debug: "{{dnac_debug}}" 57 | hostname: 58 | - CAT3K-03.devnet.local 59 | register: network_devices 60 | 61 | - name: Print software network devices 62 | ansible.builtin.debug: 63 | var: network_devices 64 | 65 | - name: Trigger image distribution and activation 66 | cisco.dnac.swim_trigger_activation: 67 | dnac_host: "{{dnac_host}}" 68 | dnac_username: "{{dnac_username}}" 69 | dnac_password: "{{dnac_password}}" 70 | dnac_verify: "{{dnac_verify}}" 71 | dnac_port: "{{dnac_port}}" 72 | dnac_version: "{{dnac_version}}" 73 | dnac_debug: "{{dnac_debug}}" 74 | payload: 75 | - imageUuid: "{{software_images.dnac_response.response[0].imageUuid}}" 76 | deviceUuid: "{{network_devices.dnac_response.response[0].id}}" 77 | distributeIfNeeded: yes 78 | -------------------------------------------------------------------------------- /swim/swim-sdk.py: -------------------------------------------------------------------------------- 1 | from dnacentersdk import api 2 | import urllib3 3 | import time 4 | import pprint 5 | urllib3.disable_warnings() 6 | 7 | pp = pprint.PrettyPrinter(indent=4) 8 | 9 | dnac_url = "" 10 | dnac_username = "" 11 | dnac_password = "" 12 | dnac_ssl_verify=False 13 | dnac_version = "2.2.2.3" 14 | 15 | dnacenter_client = api.DNACenterAPI(base_url=dnac_url, username=dnac_username, password=dnac_password, 16 | version=dnac_version, verify=dnac_ssl_verify) 17 | 18 | 19 | def main(): 20 | payload = { 21 | "sourceURL": "http://10.104.49.64/cat3k_caa_universalk9.16.12.03a.SPA.bin" 22 | } 23 | dnacenter_client.software_image_management_swim.import_software_image_via_url(payload=payload) 24 | 25 | time.sleep(30) 26 | 27 | software_images = dnacenter_client.software_image_management_swim.get_software_image_details(family='cat3k') 28 | 29 | devices = dnacenter_client.devices.get_device_list(hostname='CAT3K-03.devnet.local') 30 | 31 | image_distribution = [ 32 | { 33 | 'deviceUuid': devices.response[0].id, 34 | 'imageUuid': devices.response[0].imageUuid, 35 | 'distributeIfNeeded': True 36 | } 37 | ] 38 | 39 | dnacenter_client.software_image_management_swim.trigger_software_image_activation(payload=image_distribution) 40 | 41 | 42 | 43 | 44 | if __name__ == "__main__": 45 | main() -------------------------------------------------------------------------------- /topology/README.md: -------------------------------------------------------------------------------- 1 | # Cisco DNA Center Authentication guide 2 | 3 | ## Topology API 4 | 5 | Cisco DNA Center has a Topology Intent API that enables the developer to get an overall look of the physical (Layer 2), logical (layer 3) and site topology. 6 | 7 | This script retrieves information about the physical, vlan and layer 3 topologies. 8 | 9 | ## Functions 10 | 11 | The [functions](./topology-functions.py) file uses python requests library to interact with the API. 12 | 13 | ## License 14 | 15 | This project is licensed to you under the terms of the [Cisco Sample Code License](../LICENSE). 16 | -------------------------------------------------------------------------------- /topology/topology-functions.py: -------------------------------------------------------------------------------- 1 | # Modules import 2 | import requests 3 | from requests.auth import HTTPBasicAuth 4 | import sys 5 | import time 6 | 7 | # Disable SSL warnings. Not needed in production environments with valid certificates 8 | import urllib3 9 | urllib3.disable_warnings() 10 | 11 | # Authentication 12 | BASE_URL = 'https://' # Example BASE_URL = 'https://sandboxdnac.cisco.com' 13 | AUTH_URL = '/dna/system/api/v1/auth/token' 14 | USERNAME = '' # Example USERNAME = 'devnetuser' 15 | PASSWORD = '' # Example PASSWORD = 'Cisco123!' 16 | 17 | # URLs 18 | DEVICES_URL = '/dna/intent/api/v1/network-device' 19 | SITE_TOPOLOGY_URL = '/dna/intent/api/v1/topology/site-topology' 20 | PHYSICAL_TOPOLOGY_URL = '/dna/intent/api/v1/topology/physical-topology' 21 | TASK_BY_ID_URL = '/dna/intent/api/v1/task/{task_id}' 22 | L2_TOPOLOGY_URL = '/dna/intent/api/v1/topology/l2/{vlan_id}' 23 | 24 | # Get nodes/links 25 | def get_nodes_links(data): 26 | nodes = {} 27 | for node in data['nodes']: 28 | nodes[node['id']] = node['label'] 29 | 30 | for link in data['links']: 31 | print('Source: {0}({1}) Target: {2}({3}) Status: {4}'.format( 32 | nodes[link['source']], link.get('startPortName', ''), 33 | nodes[link['target']], link.get('endPortName', ''), 34 | link['linkStatus'] 35 | )) 36 | 37 | # Get Authentication token 38 | def get_dnac_jwt_token(): 39 | response = requests.post(BASE_URL + AUTH_URL, 40 | auth=HTTPBasicAuth(USERNAME, PASSWORD), 41 | verify=False) 42 | token = response.json()['Token'] 43 | return token 44 | 45 | # Get site topology 46 | def get_site_topology(headers): 47 | response = requests.get(BASE_URL + SITE_TOPOLOGY_URL, 48 | headers=headers, verify=False) 49 | return response.json()['response'] 50 | 51 | # Get physical topology 52 | def get_physical_topology(headers): 53 | response = requests.get(BASE_URL + PHYSICAL_TOPOLOGY_URL, 54 | headers=headers, verify=False) 55 | return response.json()['response'] 56 | 57 | # Get L2 topology 58 | def get_l2_topology(headers, vlan_id): 59 | response = requests.get(BASE_URL + L2_TOPOLOGY_URL.format(vlan_id=vlan_id), 60 | headers=headers, verify=False) 61 | return response.json()['response'] 62 | 63 | # Get Task result 64 | def get_task(headers, task_id): 65 | response = requests.get(BASE_URL + TASK_BY_ID_URL.format(task_id=task_id), 66 | headers=headers, verify=False) 67 | return response.json()['response'] 68 | 69 | def main(): 70 | # obtain the Cisco DNA Center Auth Token 71 | token = get_dnac_jwt_token() 72 | headers = {'X-Auth-Token': token, 'Content-Type': 'application/json'} 73 | 74 | print('Printing site topology...') 75 | response = get_site_topology(headers) 76 | for site in response['sites']: 77 | print(site['name'], '-->' ,site['groupNameHierarchy']) 78 | 79 | print('\nPrinting physical topology...') 80 | response = get_physical_topology(headers) 81 | get_nodes_links(response) 82 | 83 | print('\nPrinting L2 topology...') 84 | response = get_l2_topology(headers, 3001) 85 | get_nodes_links(response) 86 | 87 | if __name__ == "__main__": 88 | main() -------------------------------------------------------------------------------- /topology/topology-playbook.yml: -------------------------------------------------------------------------------- 1 | - name: Sites Example 2 | hosts: localhost 3 | connection: local 4 | gather_facts: false 5 | vars: 6 | dnac_host: 7 | dnac_username: 8 | dnac_password: 9 | dnac_verify: False 10 | dnac_port: 443 11 | dnac_version: 2.2.2.3 12 | dnac_debug: False 13 | 14 | tasks: 15 | - name: Get all Topology Site 16 | cisco.dnac.topology_site_info: 17 | dnac_host: "{{dnac_host}}" 18 | dnac_username: "{{dnac_username}}" 19 | dnac_password: "{{dnac_password}}" 20 | dnac_verify: "{{dnac_verify}}" 21 | dnac_port: "{{dnac_port}}" 22 | dnac_version: "{{dnac_version}}" 23 | dnac_debug: "{{dnac_debug}}" 24 | register: site_topology 25 | 26 | - name: Print Topology Site 27 | ansible.builtin.debug: 28 | var: site_topology 29 | 30 | - name: Get all Topology Physical 31 | cisco.dnac.topology_physical_info: 32 | dnac_host: "{{dnac_host}}" 33 | dnac_username: "{{dnac_username}}" 34 | dnac_password: "{{dnac_password}}" 35 | dnac_verify: "{{dnac_verify}}" 36 | dnac_port: "{{dnac_port}}" 37 | dnac_version: "{{dnac_version}}" 38 | dnac_debug: "{{dnac_debug}}" 39 | register: physical_topology 40 | 41 | - name: Print Topology Site 42 | ansible.builtin.debug: 43 | var: physical_topology 44 | 45 | - name: Get Topology Layer 2 by id 46 | cisco.dnac.topology_layer_2_info: 47 | dnac_host: "{{dnac_host}}" 48 | dnac_username: "{{dnac_username}}" 49 | dnac_password: "{{dnac_password}}" 50 | dnac_verify: "{{dnac_verify}}" 51 | dnac_port: "{{dnac_port}}" 52 | dnac_version: "{{dnac_version}}" 53 | dnac_debug: "{{dnac_debug}}" 54 | vlanID: 3001 55 | register: l2_topology 56 | 57 | - name: Print Topology Site 58 | ansible.builtin.debug: 59 | var: l2_topology 60 | -------------------------------------------------------------------------------- /topology/topology-sdk.py: -------------------------------------------------------------------------------- 1 | from dnacentersdk import api 2 | import urllib3 3 | import time 4 | import pprint 5 | urllib3.disable_warnings() 6 | 7 | pp = pprint.PrettyPrinter(indent=4) 8 | 9 | dnac_url = "" 10 | dnac_username = "" 11 | dnac_password = "" 12 | dnac_ssl_verify=False 13 | dnac_version = "2.2.2.3" 14 | 15 | dnacenter_client = api.DNACenterAPI(base_url=dnac_url, username=dnac_username, password=dnac_password, 16 | version=dnac_version, verify=dnac_ssl_verify) 17 | 18 | def get_nodes_links(data): 19 | nodes = {} 20 | for node in data['nodes']: 21 | nodes[node['id']] = node['label'] 22 | 23 | for link in data['links']: 24 | print('Source: {0}({1}) Target: {2}({3}) Status: {4}'.format( 25 | nodes[link['source']], link.get('startPortName', ''), 26 | nodes[link['target']], link.get('endPortName', ''), 27 | link['linkStatus'] 28 | )) 29 | 30 | def main(): 31 | site_topology = dnacenter_client.topology.get_site_topology() 32 | 33 | for site in site_topology.response.sites: 34 | print(f'{site.name} --> {site.groupNameHierarchy}') 35 | 36 | physical_topology = dnacenter_client.topology.get_physical_topology() 37 | 38 | get_nodes_links(physical_topology.response) 39 | 40 | l2_topology = dnacenter_client.topology.get_topology_details(vlan_id="1") 41 | get_nodes_links(l2_topology.response) 42 | 43 | 44 | 45 | if __name__ == "__main__": 46 | main() --------------------------------------------------------------------------------