You are on page 1of 53

CISCO 300-435 ENAUTO

Exam Study Guide

Revision 9/10/2020
This guide should greatly aid in passing the exam as a strong source of needed information.

Qweklain
1.0 Network Programmability Foundation
1.1 - Utilize common version control operations with GIT

 Version Control System


◦ GIT: Developed in 2005 and is a distributed Version Control System
◦ Maintains a history of changes for reference and rollback
◦ Centralized Version Control Systems rely on a master database or controller to maintain all
data, history, etc.
◦ Files follow a three-step life cycle
▪ Unmodified
▪ Modified
▪ Staged
◦ CLI Commands
▪ git config –global user.name “AAA BBB”
• Create a user name
▪ git config –global user.email “AAA@BBBB.com”
• Create an e-mail
▪ git config --list
• Show users
▪ git init
• Initialize a new folder
▪ git add <file or all via *.*>
• Add files to commit (stage)
▪ git commit -, “<message>”
• Commit new files with a reason
• git commit –amend: replaces the previous commit
▪ git status
• Check status
▪ git log
• Show history of commit, etc.
▪ git checkout <ID>
• Revert working folder to time of specific commit
▪ git checkout master
• Revert to attached head state
▪ git reset
• Remove commits, resetting the head
▪ git revert
• Create reverse commits, preserving history
▪ git restore –staged <file>
• Unstage a staged file
▪ git rm <file>
• Delete file from file system and repository
▪ git rm <file> --cached
• Delete file from repository, but not from file system
▪ git branch <name>
• Creates a new branch
1|Page
▪ git diff
• Details the difference between two files (current copy and last commit) and shows
the differences in chunks (only what changed)
 Structure
◦ Branch Master: The timeline of changes
▪ Each commit adds a new unique ID to the timeline
◦ Head: Points to where the next commit goes
▪ Will become a detached state following a 'git checkout'
▪ Become attached again by 'git checkout master'
◦ Git Branch allows you to create parallel timelines that do not interfere with one another
 Collaboration
◦ Git Clone, Push, Pull, Fetch, Merge, and Remote for use with a wide-spread team
▪ Git Clone: Creates a local copy of a remote repository
• Clone from a local folder, SSH, HTTP, or 'GIT'
• Same as ZIPing a repository and bringing it over, except Clone retains where the
repository came from
▪ Git Push/Pull: Allows commits from one repository to be synchronized into another
▪ Git Merge: Combines two branches into a single timeline, consolidating their commit
history
• Merge Types
◦ Fast-Forward Merge: Merge the master and a branch into one if no updates were
made to the master
◦ 3-Way Merge: Combines changes to a branch and the master to retain changes to
both branches and merge to a master
• Target branch: The branch the changes are being pulled from
• Receiving branch: The branch the changes are being pulled into (typically the
'current' branch in working directory)
◦ Conflicts in file changes must be resolved manually

1.2 Describe characteristics of API styles (REST and RPC)

• RPC (Remote Procedure Calls)


◦ Identifying a remote procedure to execute ('method' or 'procedure') on the remote system
◦ Predates SOAP and REST
◦ Uses XML, JSON, and potentially any other transfer format
◦ Function and parameters specified in the URL
◦ Utilizes SSL for encryption
◦ Functional Order
1. Client call
2. Call procedure
3. Request message (contains remote procedure's parameter)
4. Receive request and start procedure execution
5. Procedure executes
6. Send reply
7. Reply message
8. Resume execution
• SOAP (Simple Access Object Protocol)
◦ Highly structured protocol for web service access
2|Page
◦ Requires XML
◦ Can be used on any underlying transport protocols (HTTP, SMTP, etc.)
◦ Supports discovery (WSDL (Web Service Discovery Language))
◦ Contained in a SOAP Envelope, with 0-1 headers, and 1 or more messages
• REST (Representational State Transfer)
◦ An architectural style for client-server communications
◦ Supports any data transfer format (XML, JSON, CSV, etc.)
◦ Structure is left up to the implementation
◦ RESTful APIs over HTTP
▪ Synchronous: API calls that must wait for each previous call to finish
▪ Asynchronous: API calls that can be invoked before the previous call finishes
• Python library import asyncio
▪ HTTP error codes are 3-digit response codes for error diagnosis
• 100-199 - Informational
• 200-299 - Success codes
◦ 200 - OK
◦ 201 – Created
◦ 202 – Request accepted for processing, but not completed
◦ 204 - No Content
◦ 206 - GET request included a Range Header, and the server responded with the
partial content matching the range.
• 300-399 - Redirects
• 400-499 - Client errors
◦ 400 - Bad Request
◦ 401 - Unauthorized
◦ 403 - Forbidden
◦ 404 - Not Found
◦ 405 - Not Allowed
◦ 409 - Conflict
◦ 429 - Too Many Requests
• 500-599 - Server errors
◦ 500 - Internal Server Error
▪ REST Methods and typical responses
• GET: Read
◦ Returns 200, 400, 404
• POST: Create
◦ Returns 201, 404, 409
• PUT: Update/Replace
◦ Returns 200, 201, 204, 404, 405
• PATCH: Update/Modify
◦ Returns 200, 204, 404, 405
• DELETE: Delete
◦ Returns 200, 404, 405

1.3 Describe the challenges encountered and patterns used when consuming APIs
synchronously and asynchronously

• Synchronous calls must be processed in the order they are entered, i.e., each call must wait for
3|Page
the previous call to finish

Synchronous Python Example


import requests
import json

base_url = 'http://httpbin.org/'

def get_delay(seconds):
    endpoint = f"/delay/{seconds}"

    print(f"Getting with {seconds} delay...")

    response = requests.get(base_url + endpoint)
    data = response.json()
    print(json.dumps(data, indent=4))

get_delay(3)

print("Okay! Finished GETting.")

• Asynchronous calls can be processed in any order, i.e., calls can be invoked before the previous
call has finished

Asynchronous Python Example


import asyncio
from aiohttp import ClientSession
import json

base_url = "http://httpbin.org/"

async def count():
    for i in [1, 2, 3, 4, 5]:
        print(i)
        await asyncio.sleep(1)
        (i + 1)

async def get_delay(seconds):
    endpoint = f"/delay/{seconds}"

    print(f"Getting with {seconds} delay...")

4|Page
    async with ClientSession() as session:
        async with session.get(base_url + endpoint) as response:
            response = await response.read()
            data = json.loads(response)
            print(json.dumps(data, indent=4))

async def main():
    await asyncio.gather(get_delay(5), count())

asyncio.run(main())

print("Finished GETting")

• Backoff scripts can be used when encountering error codes


◦ Python has built-in libraries for backoff scripts
▪ from requests.adapter import HTTPAdapter
◦ Good standards are a backoff factor, initial wait period, and a max retry count

Backoff Python Example


import logging
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

base_url = "https://www.discogs.com/"

logging.basicConfig(level = logging.DEBUG)

def get_releases(release_id):
    endpoint = f"/releases/{release_id}"
    session = requests.Session()
    retries = Retry(total=5, backoff_factor=1, status_forcelist=[404, 429, 500, 502, 503, 504])
    session.mount(base_url, HTTPAdapter(max_retries = retries))

    print(f"Getting release #{release_id}")

    resp = session.get(base_url + endpoint)
    resp_code = resp.status_code

    return resp_code

get_releases(249504)

5|Page
1.4 Interpret Python scripts containing data types, functions, classes, conditions, and looping

• Python is a popular language due to its simplicity and legibility


• Scripts are interpreted at run-time by a Python interpreter
• Supports most modern programming features including object-oriented programming,
asynchronous code, and multi-threading
• Python variables
◦ Associates a symbolic name with some unknown quantity of information
▪ <NAME>=<INFO> E.g.; Color = “Red”
◦ Has a value defined in one place to reuse multiple times throughout a program
◦ Collect information from user/database/remote system and use that value
◦ Python Data Types (natively knows strings, boolean, and integers)
▪ In Python: print(type(<variable>)))
▪ Variables have a specified 'type' that categorizes the information stored
• Numeric Types
• String (Text)
◦ f”My name is: {<variable>}”
• Boolean (True/False)
• Collections (Lists and Dictionaries)
◦ Lists are an ordered collection of unnamed values
▪ Defined in Python <LIST NAME> = [‘<value1>’, ‘<value2>’]
▪ Position indicator starts at 0 and increments
▪ Lists can be changed
• <LIST NAME>[<value>] = ‘<new value>’
• A tuple is an immutable list defined by () instead of []
◦ Dictionaries are an un-ordered collection of named (keyed) items
▪ Defined in Python as an array
• <DICT NAME> = {
◦ ‘Key 1’ : "Value 1"
◦ }
◦ The 'IF' statement allows you to write code that only executes under certain conditions
▪ IF <condition>: - Code to run when condition is true
▪ ELSE - Code to run when condition is false
▪ Multiple IF statements can be strung together using ELIF
◦ Looping code allows us to write and execute code multiple times
▪ FOR - Loops iterate over a sequence (or list)
• for <value> in <list name>
▪ WHILE - Loops iterate until a <condition> evaluates to false
◦ Bundling Code Into Functions
▪ A function is a named block of code that can be invoked at any other point during the
execution of the program
▪ Like a loop, Function allows code to be run multiple times over the course of a program
▪ Can both accept data prior to running (parameters) and return data after execution
• Created by def <function name>(): The parenthesis are the identifier/giveaway
• You can use variables within a function
6|Page
◦ Classes
▪ Your own data type
▪ Can represent complex 'objects' or idea like a blueprint; how to create a house (object)
• State - Properties/Variables
• Behavior - Methods/Functions

1.5 Describe the benefits of Python virtual environments

Note: Some of the PIP commands in this section may be slightly different depending on if you are
using multiple Python versions and what (or if) your PATH variable is set to. Mine is py as I only have
Python 3.8 installed. The technically correct install command is python -m pip install <package>.

• Virtual Environments: Used to isolate dependencies/projects from one another


◦ You will often refer to 3rd-party or non-standard libraries when writing scripts/programs
▪ Libraries are usually installed with 'PIP' or 'easy_install'
• The installed packages are in the site-packages folder
• Pypi.org, the Python package index, has a huge index of libraries
• To install, run pip install <library>
◦ Specific version can be installed with pip install <library>==<version>
◦ You can upgrade a version to the latest version with pip install --<library>
• To uninstall, run pip uninstall <library>
• You need to specify the version to install with if using multiple Python versions
◦ pip2: python 2
◦ pip3: python 3
◦ Virtual environments are created with py -m venv <project name>
▪ To activate the isolation, change to the directory (cd <project>) and run activate.ps1 to
activate and deactivate to leave it
• Powershell: .\scripts\activate
▪ An alternate (though older and not recommended) way is virtualenv
• Install with pip install virtualenv
• Use with virtualvenv <project name>
• The virtualenvwrapper wraps up some useful management tools for virtualenv
◦ Installed on windows windows with pip install virtualenvwrapper-win
▪ Can create a new project with mkvirtualenv <project>
▪ This will create the projects under your user profile, but can be changed with
system variables
• Virtual machines represent an isolated computer
◦ Operating system, Memory, Storage, Networking
◦ They are managed through software called a hypervisor
◦ They are heavy
▪ Requires an entire OS
▪ Duplicates many redundant resources
▪ Start-up and shutdown times
• There are also containers (Docker), so are isolated execution environments

1.6 Explain the benefits of using network configuration tools such as Ansible and Puppet for
automating IOS XE platforms

7|Page
• Configuration Management is a process for consistency and life
◦ Process: Leveraging infrastructure-as-code
◦ Consistency: Automated CI/CD (Continuous Integration/Continuous Development) tools
◦ Life: Provision, maintain, test, revert, and decommission
◦ Use Chef, Ansible, and Puppet for automation and are idempotent (desired state). They are
diagnostic and create a state output. They have many extensions (extensible).
▪ Ansible: Open-source CM tool that relies on SSH to communicate with target devices
• Owned by Redhat, Open-source, Agentless, Python-based, uses YAML
• Operates with Playbooks, which contain plays, that contains tasks
▪ Puppet: Open-source CM that relies on Ruby and uses a primary-secondary architecture
• Open-source, installed agent, configured with Ruby, runs on Ruby platforms
• Widespread adoption, powerful, robust docs and support, includes NETCONF
▪ Chef: Open-source CM that is ruby-based 'recipe' language to declare configurations
• Open-source, installed agent, configured with domain-specific-language, run L,O,W
• As popular as Ansible, extensible, available for NX-OS

2.0 Automate APIs and Protocols


2.1 Identify the JSON instance based on a YANG model

JSON ietf-interface Example


{
    "ietf-interfaces:interface": {
        "name": "GigabitEthernet2",
        "description": "Wide Area Network",
        "enabled": true,
        "ipv4": {
            "address": [
                {
                    "ip": "172.16.0.2",
                    "netmask": "255.255.255.0"
                }
            ]
        }
    }
}

2.2 Identify the XML instance based on a YANG model

XML ietf-interface Example


<?xml version="1.0" encoding="UTF-8" ?>
<interface xmlns="ietf-interfaces">
    <name>GigabitEthernet2</name>
    <description>Wide Area Network</description>
    <enabled>true</enabled>
8|Page
    <ipv4>
        <address>
            <ip>172.16.0.2</ip>
            <netmask>255.255.255.0</netmask>
        </address>
    </ipv4>
</interface>

2.3 Interpret a YANG module tree generated per RFC8340

   The full format, including spacing conventions, is:

     module: <module-name>
       +--<node>
       |  +--<node>
       |     +--<node>
       +--<node>
          +--<node>
             +--<node>

       augment <target-node>:
         +--<node>
            +--<node>
            +--<node>
               +--<node>
       augment <target-node>:
         +--<node>

       rpcs:
         +--<rpc-node>
         +--<rpc-node>
            +--<node>
            |  +--<node>
            +--<node>

       notifications:
         +--<notification-node>
         +--<notification-node>
            +--<node>
            |  +--<node>
            +--<node>

       grouping <grouping-name>:
         +--<node>
            +--<node>

9|Page
            |  +--<node>
            +--<node>
       grouping <grouping-name>:
         +--<node>

       yang-data <yang-data-name>:
         +--<node>
            +--<node>
            |  +--<node>
            +--<node>
       yang-data <yang-data-name>:
         +--<node>
         

Simplified/Relevant Format
+-- MODULE
    +-- CONTAINER
        +-- LEAF
            +-- LEAF-LIST

2.4 Compare functionality, benefits, and uses of OpenConfig, IETF, and native YANG models

• Data Models
◦ An agreed upon structure for exchanging data between devices; the structure/hierarchy
◦ YANG: IETF standard for a structured data model
▪ Structure
• Model: Name of deice
• Container: Two tiers
◦ Interface: Configuration
◦ Interfaces-oper: Operational state
▪ Leaf: Individual aspects of the container
◦ Native: Some vendors create their own YANG-style models. This allows a YANG-style
format to be used for features that are proprietary to the vendor.
◦ OpenConfig: Another creator of YANG data models. OpenConfig is more network-driven in
that the modules are designed to be vendor-neutral. Common capabilities shared by vendors
will likely be in one module, making multi-vendor network management easier.

2.5 Compare functionality, benefits, and uses of NETCONF and RESTCONF

• Netconf
◦ An industry standard protocol that succeeded SNMP in 2006
◦ Session-based
◦ Two primary separations
▪ Operational
• Statistics, Routing, etc.
▪ Config
• Retrieve configuration settings or changing them
10 | P a g e
◦ Operates on 830 with SSH so it has encryption and authentication
◦ Only operates with XML
▪ Messages: RPC
• <rpc>: Initial request; includes a request ID
• <rpc-reply>: Reply to the initial request; uses same ID in the reply
▪ Operations: Actions to take
• Three standard actions
◦ <get>: Get operational state
◦ <get-config>: Get actual raw configuration
◦ <edit-config>: Sets actual raw configuration
▪ Content: Configuration/Operational Data
• <data>: The actual detail/data you want
◦ E.g.; <interfaces> - <Gi0/1>
◦ Communication sequence
▪ 1 - Connect to device and say <hello>
▪ 2 - Retrieve capabilities

◦ List of YANG data models
▪ 3 - Investigate available models determine which to use
▪ 4 - Compose operation <get-config>
▪ 5 - Send message <rpc>
▪ 6 - Retrieve <rpc-reply>
▪ 7 - Process <data>
◦ Python library ncclient
▪ from ncclient import manager
• Manager connects to the network device using Netconf
• Restconf
◦ Came around in 2006; YANG added in 2011; Netconf became Restconf in 2017
◦ Uses HTTP as transport
◦ <METHOD>https://ip-address:port/root/datastore/YANGmodule:container/leaf
▪ The HTTP Method defines what we are doing
▪ ip-address: IP address or hostname
▪ port: Specify port for connectivity
▪ root: How the network knows you are trying to connect via restconf
• IOS-XE: restconf
▪ datastore: Which module you are trying to access
• IOS-XE: data
▪ YANGmodule: What data you are trying to get
▪ container: The container within the module you are trying to access
• E.g., ietf-interfaces:interfaces
◦ ietf-interfaces=module
◦ interfaces=container
▪ The content-type headers need to be ‘application/yang-data+json’.
▪ E.g., https://<IP>:<Port>/restconf/data/Cisco-XE-interfaces/interface=GigabitEthernet1

3.0 Network Device Programmability

11 | P a g e
3.1 Implement device management and monitoring using NetMiko

Adding a Loopback
from netmiko import ConnectHandler

router = {
    'host': "ios-xe-mgmt-latest.cisco.com",
    'port': 8181,
    'username': "root",
    'password': "Cisco12345",
    'device_type': "cisco_ios"
}

configs = ["int loopback55, ip address 10.101.75.1 255.255.255.0, no shut"]

try:
    c = ConnectHandler(**router)
    c.enable()
    c.send_config_set(configs)
    response = c.send_command("show ip int brief")
    c.disconnect()
except Exception as ex:
    print(ex)
else:
    print(response)

3.2 Construct a Python script using ncclient that uses NETCONF to manage and monitor an
IOS XE device

Retrieving Capabilities
from ncclient import manager

router = {
    'host': "ios-xe-mgmt-latest.com",
    'port': "830",
    'user': "developer",
    'password': "Cisco12345"
}

with manager.connect(**router, host_key_verify=False) as m:
    for capability in m.server_capabilities:
        print("*" * 25)
        print(" ")
        print(capability)
12 | P a g e
Retrieving Config and Operation State (Packets In) of an Interface
from ncclient import manager
import xmltodict

router = {
    'host': "ios-xe-mgmt-latest.com",
    'port': "830",
    'user': "developer",
    'password': "Cisco12345"
}

int_filter = """
<filter>
    <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
        <interface>
            <name>GigabitEthernet2</name>
        </interface>
    </interfaces>
    <interfaces-state xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
        <interface>
            <name>GigabitEthernet2</name>
        </interface>
    <interfaces-state>
</filter>
"""

with manager.connect(**router, host_key_verify=False) as m:
    netconf_response = m.get(int_filter)     # Uses  GET  RPC  and  stores response  as  a  variable

python_response = xmltodict.parse(netconf_response.xml)['rpc-reply']['data']    #  Converts XML  response to Python 


Dict  and  parses out  the  'rpc-reply'  and  'data'

int_config = python_response['interfaces']['interface']
oper_state = python_response['interfaces-state']['interface']

print(f"Name: {int_config['name']['#text']}")
print(f"")

Changing Config (Update An Interface)

13 | P a g e
from ncclient import manager
import xmltodict

router = {
    'host': "ios-xe-mgmt-latest.com",
    'port': "830",
    'user': "developer",
    'password': "Cisco12345"
}

int_conf = """
<filter>
    <config>
        <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
            <interface>
                <name>GigabitEthernet2</name>
                <description>Port to Fabric</description>
                <enable>true</enabled>
            </interface>
        </interfaces>
    </config>
</filter>
"""

with manager.connect(**router, host_key_verify=False) as m:
    response = m.edit_config(int_conf, target="running")

3.3 Configure device using RESTCONF API utilizing Python requests library

Get NETCONF Capabilities


import requests
import json

router = {
    'host': "ios-xe-mgmt-latest.cisco.com",
    'port': "443",
    'user': "developer",
    'password': "Cisco12345"
}

headers = {
    'Accept': "application/yang-data+json",
    'Content-Type': "application/yang-data+json"
14 | P a g e
}

base_url = f"https://{router['host']}:{router['port']}/restconf/data/netconf-state/capabilities"

response = requests.get(url=base_url, headers=headers, auth=(router['username'], router['password']), verify=False)

if response.status_code == 200:
    resp_dict = response.json()
    for capability in resp_dict['ietf-netconf-monitoring:capabilities']['capability']:
        print("*" * 25)
        print(capability)

Get Routing Info


import requests
import json

router = {
    'host': "ios-xe-mgmt-latest.cisco.com",
    'port': "443",
    'user': "developer",
    'password': "Cisco12345"
}

headers = {
    'Accept': "application/yang-data+json",
    'Content-Type': "application/yang-data+json"
}

base_url = f"https://{router['host']}:{router['port']}/restconf/data/ietf-routing:routing"

response = requests.get(url=base_url, headers=headers, auth=(router['username'], router['password']), verify=False).js
on()

print(json.dumps(response, indent=4))

Change Config (Add Loopback)


import requests
import json

router = {
15 | P a g e
    'host': "ios-xe-mgmt-latest.cisco.com",
    'port': "443",
    'username': "developer",
    'password': "Cisco12345"
}

headers = {
    'Accept': "application/yang-data+json",
    'Content-Type': "application/yang-data+json"
}

base_url = f"https://{router['host']}:{router['port']}/restconf/data/ietf-interfaces:interfaces/"

response = requests.get(url=base_url, headers=headers, auth=(router['username'], router['password']), verify=False).js
on()

payload = {
    "ietf-interfaces:interface": {
        "name": "Loopback55",
        "description": "Test Added Loopback",
        "type": "iana-if-type:softwareLoopback",
        "enabled": True,
        "ietf-ip:ipv4": {
            "address": [
                {
                    "ip": "172.16.0.2",
                    "netmask": "255.255.255.0"
                }
            ]
        }
    }
}

response = requests.post(url=base_url, headers=headers, auth=(router['username'], router['password']), data=json.dum
ps(payload), verify=False)

if response.status_code == 201:
    print(response.text)

Delete Config (Remove Loopback)


import requests
import json

16 | P a g e
router = {
    'host': "ios-xe-mgmt-latest.cisco.com",
    'port': "443",
    'username': "developer",
    'password': "Cisco12345"
}

headers = {
    'Accept': "application/yang-data+json",
    'Content-Type': "application/yang-data+json"
}

base_url = f"https://{router['host']}:{router['port']}/restconf/data/ietf-interfaces:interfaces/interface=Loopback55"

response = requests.get(url=base_url, headers=headers, auth=(router['username'], router['password']), verify=False).js
on()

# payload =  {
#       "ietf-interfaces:interface": {
#             "name": "Loopback55",
#             "description": "Test  Added  Loopback",
#             "type":  "iana-if-type:softwareLoopback",
#             "enabled": True,
#             "ietf-ip:ipv4": {
#                   "address":  [
#                         {
#                               "ip":  "172.16.0.2",
#                               "netmask":  "255.255.255.0"
#                         }
#                   ]
#             }
#       }
# }

response = requests.delete(url=base_url, headers=headers, auth=(router['username'], router['password']), verify=False)

if response.status_code == 201:
    print(response.text)

3.4 Utilize Ansible to configure an IOS XE device

 After installing Ansible, playbooks are run with ‘ansible-playbook <file.yml>

Gather Facts Playbook


17 | P a g e
---
- name: Gather IOS-XE Facts
  hosts: sandbox
  connection: local
  tasks:
    - name: gather IOS Facts
    ios facts:
      provider:
        host: iox-xe-mgmt-latest.cisco.com
        port: 8181
        username: root
        password: Cisco12345

    - name: What is the IOS OS ver?
      debug:
        var: ansible_net_version
    
    - name: Wha is the hostname?
      debug:
        var: ansible_net_hostname
        

Issuing CLI Commands


---
- name: Issue show commands
  hosts: sandbox
  connection: local
  tasks:
    - name: Show ip int brief
      ios_commmand:
        commands:
        - show ip int brief
      provider:
        host: iox-xe-mgmt-latest.cisco.com
        port: 8181
        authorize: yes
        username: root
        password: Cisco12345
    register: if_data

    - name: Interfaces output
      debug:
        var: if_data['stdout_line'][0]

Add Config with ‘ios_config’


---
18 | P a g e
- name: Add Loopback
  hosts: sandbox
  connection: local
  tasks:
    - name: Add Loopback55
        provider:
        host: iox-xe-mgmt-latest.cisco.com
        port: 8181
        authorize: yes
        username: developer
        password: C1sco12345
      ios_config:
        parents: int loopback55
        lines:
        - ip address 110.25.66.1 255.255.255.0
        - no shut
        before:
        - int loopback55

Adding an Interface w/ ‘ios_l3_commands’ module


---
- name: Add Loopback
  hosts: sandbox
  connection: local
  tasks:
    - name: Add Loopback55
        provider:
        host: iox-xe-mgmt-latest.cisco.com
        port: 8181
        authorize: yes
        username: developer
        password: C1sco12345
      ios_config:
        parents: int loopback55
        lines:
        - ip address 110.25.66.1 255.255.255.0
        - no shut
        before:
        - int loopback55

    - name: Interface output
      debug:
        var: ansible_net_interfaces
        

19 | P a g e
3.5 Configure a subscription for model driven telemetry on an IOS XE device (CLI,
NETCONF, and RESTCONF)

CLI
# Step  1:  Create  destination

Router(config)# telemetry model-driven
Router(config-model-driven)# destination-group CPU-Health
Router(config-model-driven-dest)# address family ipv4  172.0.0.0 port  57500
Router(config-model-driven-dest-addr)#  encoding self-describing-gpb
Router(config-model-driven-dest-addr)#  protocol tcp
Router(config-model-driven-dest-addr)#  commit

# Step  2:  Specify the data to stream  via  Sensor Path

Router(config)# telemetry model-driven
Router(config-model-driven)# sensor-group  Monitor-CPU
Router(config-model-driven-snsr-grp)# sensor-path  Cisco-IOS-XR-wdsysmon-fd-oper:system-monitoring/cpu-
utilization
Router(config-model-driven-snsr-grp)# commit

# Step  3:  Subscribe to the data.  The  subscription binds the destination-group and sensor-path

Router(config)# telemetry model-driven   
Router(config-model-driven)# subscription  CPU-Utilization  
Router(config-model-driven-subs)#  sensor-group-id  Monitor-CPU sample-interval  30000 
Router(config-model-driven-subs)#  destination-id  CPU-Health 
Router(config-model-driven-subs)#  source-interface Interface1
Router(config-model-driven-subs)#  commit

Verify config: show run telemtry model-driven
Verify subscription: show telemtry model-driven subscription

NETCONF
from ncclient import manager
import xmltodict
from lxml.etree import fromstring

router = {
    "host":"ios-xe-mgmt-latest.cisco.com",
    "port":"10000",
    "user":"root",
    "password":"<password>",

20 | P a g e
    "hostkey_verify": False,
    "device_params": {"name": "csr"}
}

with manager.connect(**router) as m:
    subs = ["/memory-ios-xe-oper:memory-statistics/memory-statistic"]
    for sub in subs:
        rpc = f"""
            <establish-subscription xmlns='urn:ietf:params:xml:ns:yang:ietf-event-notifications' xmlns:yp='urn:ietf:param
s:xml:ns:yang:ietf-yang-push'>
                <stream>yp:yang-push</stream>
                <yp:xpath-filter>{sub}</yp:xpath-filter>
                <yp:period>500</yp:period>
            </establish-subscription>
        """
        
        response = m.dispatch(fromstring(rpc))
        python_resp = xmltodict.parse(response.xml)
        print(python_resp['rpc-reply']['subscription-result']['#text'])
        print(python_resp['rpc-reply']['subscription-id']['#text'])

        while True:
            sub_data = m.take_notification()
            python_sub_data = xmltodict.parse(sub_data.notification_xml)
            print(f"Sub ID: {python_sub_data['notification']['push-update']['subscription-id']}")
            print(python_sub_data)
            print(f"Name: {python_sub_data['notification']['push-update']['datastore-contents-xml']['memory-statistics']
['memory-statistic'][0]['name']}")

RESTCONF
import requests
import json

router = {
   'ip': '10.10.20.30',
   'port': '443',
   'username': 'admin',
   'password': 'Cisco123'
}

headers = {
      "Accept" : "application/yang-data+json", 
      "Content-Type" : "application/yang-data+json"
   }
21 | P a g e
module = "Cisco-IOS-XE-mdt-cfg:mdt-config-data"

url = f"https://{router['ip']}:{router['port']}/restconf/data/{module}"
print(url)

payload = {
    "mdt-config-data": {
        "mdt-subscription": {
                "subscription-id": 100,
                "base": {
                    "stream": "yang-push",
                    "encoding": "encode-kvgpb",
                    "xpath": "/process-cpu-ios-xe-oper:cpu-usage/cpu-utilization/five-seconds",
                    "period": 1000
                },
                "mdt-receivers": {
                    "address": "10.0.19.188",
                    "port": 42518,
                    "protocol": "grpc-tcp"
                }
            }
        }
}

print(payload)

response = requests.post(url, headers=headers, data=json.dumps(payload), auth=(router['username'], router['password'
]), verify=False)

print(response)

3.6 Compare publication and subscription telemetry models

 Traditional models operate under a PULL methodology via SNMP, Syslog, or CLI. This data is
typically only obtained when the collector requests it (typically at intervals). As a network
expands, these methods can become resource-intensive which can result in gaps of collected
data and manual intervention.
 Modern telemetry models shift operation to a PUSH methodology that streams data from the
device (data exporter) which can then be subscribed to from various data collectors. The
collected data can then be analyzed and acted upon as necessary by a data analyzer through
analytics, queries, etc.
o ELK stack
22 | P a g e
 Elastisearch: Helps narrow and parse the information
 Logstash: Store the information
 Kabana: Graphs out the information
 Runs like a developer environment of a publisher and subscriber
 Subscribe to information (computer) that is being published (pushed) from the
device
 The information will be in a YANG model (xpath-filter)
 The YANG model is referred to as a Sensor Path
o E.g., Cisco-IOS-XR-wdsysmon-fd-oper:system-monitoring/cpu-
utilization
 Transport
o gRPC (Google RPC): Used in both dial-in and dial-out modes
o TCP: Used only for dial-out mode
o UDP: Used only for dial-out mode (not recommended/mostly unsupported)
 Methods
o Dial-out Mode: The device dials-out to the received to establish a connection,
eliminating the need to manage the ports for inbound traffic
o Dial-in Mode: The receiver dial-in to the device, eliminated the need to create
destinations in the configuration.

3.6.a Periodic / cadence

Cadence-driven telemetry continually streams data (operational statistics and state transitions) at a
configured cadence (interval). The higher frequency of the data that is continuously streamed helps you
closely identify emerging patterns in the network.

3.6.b On-change

Event-driven telemetry optimizes data that is collected at the receiver and streams data only when a
state transition occurs and thus optimizes data that is collected at the receiver. For example, EDT
streams data about interface state transitions, IP route updates, and so on.

3.7 Describe the benefits and usage of telemetry data in troubleshooting the network

 Remote Management: Remote insight to allow analysis and action.


 Traffic Optimization: Easier to adjust links and traffic direction with constant data intervals for
faster response times to necessary adjustments.
 Preventive (Proactive) Troubleshooting: Allows state indicators, statistics, and infrastructure
information at the application layer due to high frequency and granularity.
 Data Visualization: Analytic tools and applications to visualize network insight.
 Monitor and Control: Monitoring is de-coupled from storage and analysis which reduces device
dependency while providing flexibility for data transformation.

3.8 Describe Day 0 provisioning methods

23 | P a g e
When a network device like a router or a switch comes on-line, a fair amount of manual configuration
must happen before it is fully functional. At minimum, it needs to be updated to the proper software
image and it needs a base configuration. Day zero techniques automate these processes, bringing up
network devices into a functional state with minimal to no-touch.

3.8.a iPXE

Preboot Execution Environment


 An enhanced version of PXE which is an open standard for network booting
 A boot source is detected on power up from a preconfigured DHCP server
o Booting an image located on FTP, HTTP, or TFTP
 Unlike PnP or ZTP, a functional software image is not required
o iPXE begins from the bootloader (netboot)

Code Example (from ISC DHCP)


host Switch2 {
fixed-address 192.168.1.20 ;
hardware ethernet CC:D8:C1:85:6F:11 ;
#user-class = length of string + ASCII code for iPXE
if exits user-class and option user-class = 04:68:50:58:45 {
filename "http://192.168.1.146/test-image.bin"
}
}

3.8.b PnP

Plug-n-Play
 Offers a unified, near zero-touch deployment.
o Requires a server running the PnP protocol
 APIC-EM
 Software images and configuration files are on the APIC-EM server. When the new PnP-
capable device becomes active, it will pull a DHCP address and initiate PnP via option 43.
24 | P a g e
o Option 43 is a vendor specific identifier which is used by the PnP agent to locate and
connect to the PnP server. The example above is ASCII converted to hex.

Code Example
ip dhcp pool pnp_device_pool <-- Name of DHCP pool
network 192.168.1.0 255.255.255.0 <-- Range of IPs assigned to clients
default-router 192.168.1.1 <-- Gateway address
option 43 ascii "5A1N;B2;K4;I172.19.45.222;J80" <-- Option 43 string

3.8.c ZTP

Zero-Touch Provisioning
 Like PnP but works with standard protocols like TFTP and SCP. This is not a turnkey solution
like PnP but offers compatibility with other device types.
 When a device boots with no configuration, it enters ZTP mode.
o Locates DHCP server and bootstraps IP, gateway, DNS, and enables Guest Shell
o The device then obtains the IP/URL of TFTP and downloads the config Python script
 Guest Shell provides the environment for the Python script to run
 Guess Shell will remain enabled after configuration

Code Example
ip dhcp pool ztp_device_pool <-- Name of DHCP pool
vrf Mgmt-vrf <-- Management VRF
network 10.1.1.0 255.255.255.0 <-- Range of client IP addresses
default-router 10.1.1.1 <-- Gateway address
option 150 ip 203.0.113.254 <-- Script server
option 67 ascii /sample_python_dir/python_script.py <-- Python script name

4.0 Cisco DNA Center


4.1 Compare traditional versus software-defined networks

• Traditional WAN
◦ A series of edge routers to connect to different sites/branches over IPSEC VPN, MPLS, etc.
◦ Each edge device is individually configured, which is labor intensive
◦ Bandwidth requirements are expensive and limited
◦ No direct access to cloud resources from the branches due to required backhauling
◦ Application performance over internet links lacks predictability
◦ Includes many one-function devices and appliances, inducing complexity
• SD-WAN
◦ Software-defined configuration handled by a single controller (cloud or virtual appliance)
◦ Traffic backhauling no longer required
▪ Cloud can interact with applications thanks to cloud architecture
▪ End-to-end encryption and inspection
• Next-gen security mechanism, anti-malware and botnet intervention, etc.
◦ Is an overlay network (virtual infrastructure)
▪ Transport independence
• Cisco SD-WAN
25 | P a g e
◦ Series of four products from Viptela purchase
▪ vManage: Cloud or on-prem network management (vManage API)
• Management and Orchestration Plane
• GUI interface that represents your intent for creating device templates, policies, etc.
• Configuration, monitoring, and provisioning
• Control functionality through Device Templates that contain Feature Templates
▪ vBond: On-boarding and orchestration
• Management and Orchestration Plane
• How the network is constructed, and inter-connected components work together
• Offers zero-touch provisioning
◦ Can configure a new device without admin intervention
• Helps network devices find the controller
▪ vSmart Controller: Policy and service control place
• Control Plane
• The “brain” of the SD-WAN operation
• Represents network devices themselves; instructs devices on what to do
• Responsible for the enforcement of policies created by vManage
• Route information from branch locations is received via OMP (Overlay
Management Protocol)
◦ Uses known policies to control traffic flow
▪ vEdge Router: The actual edge routers (hardware or software)
• Data plane
• Responsible for establishing the network and forwarding traffic
• https://<IP>/dataservice/
◦ Anything after dataservice specifies where you are going
▪ /system is for device inventory
▪ /device/vpn/deviceId=deviceId is for VPN instance list
▪ /certificate is for certificate management
▪ /template is for template/feature management
▪ /alarms is for alarm statistics
◦ data will be parsed out as JSON
◦ CRUD (Create - Read - Update - Delete)
▪ Reserve a lab
• After reservation, obtain IP from SD-WAN vManage --> Attributes
▪ User Account Creation
• Done through the Admin API
• Policies/roles are issued via the Group Membership
◦ Netadmin group is network administration
▪ Has admin privileges to SD-WAN topology

4.2 Describe the features and capabilities of Cisco DNA Center

• Policy
◦ Translate business intent into zero-trust network policies. Identify all endpoints and
optimize user experience based on business requirements.
• Automation
◦ Automate deployment and management of network devices and integration of security
solutions, to promote consistency across configurations, reduce errors, and save time.
26 | P a g e
◦ Save time by using a single dashboard to manage and automate your network. Quickly scale
your business with intuitive workflows and reusable templates. Configure and provision
thousands of network devices across your enterprise in minutes, not hours.
◦ Automate additions, changes, policy provisioning, and upgrades
• Assurance
◦ Analytics and AI/ML combined with Cisco best practices help to optimize your network's
performance, reduce troubleshooting time, and lower the cost of network operations.
◦ Understand what was happening when an issue occurred with a granular, timeline view of
deice or client performance.
◦ Quick overview of every network device and client on the network, wired or wireless.
• Security
◦ Discover rogue devices, configuration inconsistencies, and persistent threats through
extended integrations with Cisco ISE, Stealthwatch, Umbrella, and third-party NAC/NAS.
◦ Automated access policies to secure any user, device, or app, anywhere.
◦ Apply security parameters to thousands of locations from one interface.
◦ Continuous Visibility: Utilize continuous real-time insights to identify and resolve events
faster. Identify who and what is on the network, how they are communicating, and
determine risk profile and compliance.
◦ Zero-Trust Access: Secure the workplace, workloads, and the workforce. Create logical
segmentation of users, devices and applications, verify user identity, and assess device
posture.
◦ Constant Protection: Identify risks and vulnerabilities through automation. Build threat
prevention, detection and response into, not on, every network device—from the WAN edge
to the campus core.
• DNA Center Appliance
◦ SDN Controller
◦ Analytics engine
◦ Telemetry storage
• DNA Center Functions
◦ Workflows
▪ Design
• Geography
▪ Policy
• Setup users, groups, and resources
▪ Provision
• Find devices and deploy to the geography
▪ Assurance
• Real-time monitoring with machine learning built-in
▪ Platform
• Where the automation takes place and APIs reside
◦ Uses model-driven programmability to communicate with infrastructure devices using
Netconf and Restconf
◦ Platforms
▪ Sits on top of DNA Center
◦ Systems
▪ Where DNA Center sits
◦ Products
▪ Intent-based infrastructure
27 | P a g e
▪ WiFi, switches, firewalls, routers

4.2.a Network assurance APIs

• Westbound REST API – Provides mechanisms for integrating Cisco DNA Assurance workflows
and data with third-party IT Service Management (ITSM) solutions.
◦ ITSM minimizes handoffs, reduces duplication of issues, and optimizes processes by
integrating DNA Center into incident management, change management, and problem
management systems.
◦ Can also integrate into approval and pre-approval chains while linking with formal change
and maintenance window schedules.
• For monitoring overall, network, client, and application health

4.2.b Intent APIs

• Northbound REST API – Exposes specific capabilities of the Cisco DNA Center platform.
• Provides policy-based abstraction of business intent, allowing focus on an outcome rather than
struggling with individual mechanisms steps.
• Uses HTTPS verbs (GET, POST, PUT, and DELETE) with JSON structures to discover and
control the network.
• Grouped, hierarchically into functional 'domains' and 'subdomains' of service.
◦ Authentication Domain
▪ Obtains a security token that identifies the privileges of a REST caller.
◦ Know Your Network Domain
▪ GET information about clients, sites, topology, devices, and issues
▪ Create (POST) and manage (PUT, DELETE) sites, devices, IP pools, edge and border
devices, and authentication profiles.
◦ Site Management
◦ Connectivity
▪ Provides the means for configuring and managing Fabric Wired and Non-Fabric
Wireless Networks
◦ Operational Tasks
▪ Enable access to CLI keywords, discover network devices, trace paths through the
network, identify and discover network components using SNMP and other discovery
protocols.
◦ Policy
◦ Event Management
▪ Provides the ability to receive custom notifications when specific events are triggered

4.2.c Multivendor support (3rd party SDKs)

• Southbound REST API – Allows the ability to manage non-Cisco devices via Device Packages
◦ A Device Package enables DNA to communicate with third-party devices by mapping DNA
features to their southbound protocols
• Encapsulation of third-party components for an integrated view of the network

28 | P a g e
4.2.d Events and notifications

• Eastbound REST API – Provides the ability to establish a notification handler when specific
events are triggered like Cisco DNA Assurance and Automation (SWIM) events.
• Enables external systems to take actions in response to an event

4.3 Implement Cisco DNA Center event outbound webhooks

Webhook With Two Events


import requests
from requests.exceptions import HTTPError
import json

base_url = "https://sandboxdnac.cisco.com/dna"

def get_token():
    auth_url = f"{base_url}/system/api/v1/auth/token"
    user = "devnetuser"
    password = "Cisco123!"

    auth_header = {
        'content-type': "application/json"
    }

    try:
        auth_att = requests.post(url=f"{auth_url}", auth=(user, password), headers=auth_header, verify=False).json()
['Token']
    except HTTPError as err_http:
        print(f"HTTP error occurred: \n ---> {err_http}")
    else:
        return auth_att

token = get_token()
# print(token)

headers = {
    'x-auth-token': token,
    'accept': "application/json",
    'content-type': "application/json"
}

event_url = f"{base_url}/intent/api/v1/events?tags=ASSURANCE"
event_resp = requests.get(url=f"{event_url}", headers=headers, verify=False).json()
print(json.dumps(event_resp, indent=2))

29 | P a g e
event_list = ["NETWORK-DEVICES-3-252", "NETWORK-DEVICES-3-105"]

payload = [
    {
        'name': "Test Sub",
        'subscriptionEndpoints':[
            {
                'subscriptionDetails': {
                    'connectorType': "REST",
                    'name': "Test Azure Python Function App",
                    'description': "Ingest payload into DB",
                    'method': "POST",
                    'url': "https://sometest.azurewebsites.net/DNA"
                }
            }
        ],
        'filter': {
            'eventIds': event_list
        }
    }
]

ev_sub_url = f"{base_url}/intent/api/v1/event/subscription"
event_sub_resp = requests.post(url=f"{ev_sub_url}", headers=headers, data=json.dumps(payload), verify=False)
print(event_sub_resp.status_code)
print(event_sub_resp.text)

4.4 Implement API requests for Cisco DNA Center to accomplish network management tasks

4.4.a Intent APIs

Device Inventory
 You will want to pay attention to and understand a few key points of the response.
o “family”: Family of device types
o “type”: The specific type of device
o “softwareVersion”: Version of software running
o “platformId”: OS it is running
o “series”: The series of devices it belongs to

import requests
from requests.exceptions import HTTPError
import json

30 | P a g e
base_url = "https://sandboxdnac.cisco.com/dna"

def get_token():
    auth_url = f"{base_url}/system/api/v1/auth/token"
    user = "devnetuser"
    password = "Cisco123!"

    try:
        auth_att = requests.post(url=f"{auth_url}", auth=(user, password), verify=False).json()
    except HTTPError as err_http:
        print(f"HTTP error occurred: \n ---> {err_http}")
    except Exception as ex:
        print(f"Other error occurred: \n ---> {ex}")
    else:
        auth_token = auth_att['Token']
        return auth_token

dna_token = get_token()
# print(dna_token)

headers = {
    'x-auth-token': dna_token,
    'accept': "application/json",
    'content-type': "application/json"
}

def get_device_list():
    dev_list_url = f"{base_url}/intent/api/v1/network-device"

    try:
        get_list = requests.get(url=f"{dev_list_url}", headers=headers, verify=False).json()['response']
    except Exception as ex:
        print(f"Error has occurred: \n ---> {ex}")
    else:
        return get_list

dev_list_resp = get_device_list()
print(json.dumps(dev_list_resp, indent=2))

GET Topology
import requests
from requests.exceptions import HTTPError

31 | P a g e
import json

base_url = "https://sandboxdnac.cisco.com/dna"

def get_token():
    auth_url = f"{base_url}/system/api/v1/auth/token"
    user = "devnetuser"
    password = "Cisco123!"

    auth_header = {
        'content-type': "application/json"
    }

    try:
        auth_att = requests.post(url=f"{auth_url}", auth=(user, password), headers=auth_header, verify=False).json()
    except HTTPError as err_http:
        print(f"HTTP error occurred: \n ---> {err_http}")
    except Exception as ex:
        print(f"Other error occurred: \n ---> {ex}")
    else:
        auth_token = auth_att['Token']
        return auth_token

dna_token = get_token()
# print(get_token())

headers = {
    'x-auth-token': dna_token,
    'accept': 'application/json',
    'content-type': 'application/json'
}

def get_topology():
    topo_url = f"{base_url}/intent/api/v1/topology/site-topology"

    try:
        get_topo = requests.get(url=f"{topo_url}", headers=headers, verify=False).json()['response']
    except Exception as ex:
        print(f"Error has occurred: \n ---> {ex}")
    else:
        return get_topo

topo_resp = get_topology()

32 | P a g e
print(json.dumps(topo_resp, indent=2))

4.4.b Command Runner APIs

• Retrieve real-time device configuration and CLI keywords

Run ‘show ver’ & ‘show ip int br’


import requests
from requests.exceptions import HTTPError
import json

base_url = "https://sandboxdnac.cisco.com/dna"

def get_token():
    auth_url = f"{base_url}/system/api/v1/auth/token"
    user = "devnetuser"
    password = "Cisco123!"

    try:
        auth_att = requests.post(url=f"{auth_url}", auth=(user, password), verify=False).json()
    except HTTPError as err_http:
        print(f"HTTP error occurred: \n ---> {err_http}")
    except Exception as ex:
        print(f"Other error occurred: \n ---> {ex}")
    else:
        auth_token = auth_att['Token']
        return auth_token

dna_token = get_token()
# print(dna_token)

headers = {
    'x-auth-token': dna_token,
    'accept': "application/json",
    'content-type': "application/json"
}

def get_device_list():
    dev_list_url = f"{base_url}/intent/api/v1/network-device"

    try:
        get_list = requests.get(url=f"{dev_list_url}", headers=headers, verify=False).json()['response']
    except Exception as ex:

33 | P a g e
        print(f"Error has occurred: \n ---> {ex}")
    else:
        print("*** get_list success ***")
    
    device_plaformIds = []
    for device in get_list:
        plat_id = device['platformId']
        device_plaformIds.append(plat_id)
    
    return device_plaformIds

dev_PIDs = get_device_list()
# print(dev_PIDs)

def run_CLI_comm(payload):
    command_url = f"{base_url}/intent/api/v1/network-device-poller/cli/read_request"

    try:
        run_cmds = requests.post(url=f"{command_url}", headers=headers, data=json.dumps(f"{payload}"), verify=Fals
e)
    except Exception as ex:
        print(f"Error has occurred: \n ---> {ex}")
    else:
        return run_cmds

payload = {
   'commands': [
        "show version",
        "show ip int br"
    ],
    'deviceUuids': dev_PIDs
}

cmd_resp = run_CLI_comm(payload)
print(cmd_resp)

4.4.c Site APIs

GET Sites
import requests
from requests.exceptions import HTTPError
import json

base_url = "https://sandboxdnac.cisco.com/dna"
34 | P a g e
def get_token():
    auth_url = f"{base_url}/system/api/v1/auth/token"
    user = "devnetuser"
    password = "Cisco123!"

    auth_header = {
        'content-type': "application/json"
    }

    try:
        auth_att = requests.post(url=f"{auth_url}", auth=(user, password), headers=auth_header, verify=False).json()
    except HTTPError as err_http:
        print(f"HTTP error occurred: \n ---> {err_http}")
    except Exception as ex:
        print(f"Other error occurred: \n ---> {ex}")
    else:
        auth_token = auth_att['Token']
        return auth_token

dna_token = get_token()
# print(get_token())

headers = {
    'x-auth-token': dna_token,
    'accept': 'application/json',
    'content-type': 'application/json'
}

def get_sites():
    sites_url = f"{base_url}/intent/api/v1/site"

    try:
        get_sites = requests.get(url=f"{sites_url}", headers=headers, verify=False).json()['response']
    except Exception as ex:
        print(f"Error occurred: \n ---> {ex}")
    else:
        return get_sites

site_resp = get_sites()
print(json.dumps(site_resp, indent=2))

Assurance
import requests
35 | P a g e
from requests.exceptions import HTTPError
import json

base_url = "https://sandboxdnac.cisco.com/dna"

def get_token():
    auth_url = f"{base_url}/system/api/v1/auth/token"
    user = "devnetuser"
    password = "Cisco123!"

    try:
        auth_att = requests.post(url=f"{auth_url}", auth=(user, password), verify=False).json()
    except HTTPError as err_http:
        print(f"HTTP error occurred: \n ---> {err_http}")
    except Exception as ex:
        print(f"Other error occurred: \n ---> {ex}")
    else:
        auth_token = auth_att['Token']
        return auth_token

dna_token = get_token()
# print(dna_token)

headers = {
    'x-auth-token': dna_token,
    'accept': "application/json",
    'content-type': "application/json"
}

def get_client_health():
    health_url = f"{base_url}/intent/api/v1/client-health"
    querystring = {"timestamp": ""} #  The  timestamp  for  the  health  query

    try:
        get_health = requests.get(url=f"{health_url}", headers=headers, params=querystring, verify=False).json()
['response']
    except Exception as ex:
        print(f"Error has occurred: \n ---> {ex}")
    else:
        return get_health

health_resp = get_client_health()
# print(json.dumps(health_resp, indent=2))

36 | P a g e
# This  for  loop  helps  parse  the  info  a  bit to see  how many of each  health  category  exist  for  wired devices
for score in health_resp[0]['scoreDetail']:
    if score['scoreCategory']['value'] == 'WIRED':
        print(f"Wired Clients: {score['clientCount']}")
        score_values = score['scoreList']
        for score_value in score_values:
            print(f"{score_value['scoreCategory']['value']}: {score_value['clientCount']}")

4.5 Implement API requests for Cisco DNA Center to accomplish network management tasks
using these APIs

4.5.a Network discovery and device APIs

Discover Devices and Append Their IPs To A List


import requests
from requests.exceptions import HTTPError
import time
import json

base_url = "https://sandboxdnac.cisco.com/dna"

def get_token():
    auth_url = f"{base_url}/system/api/v1/auth/token"
    user = "devnetuser"
    password = "Cisco123!"

    auth_header = {
        'content-type': "application/json"
    }

    try:
        auth_att = requests.post(url=f"{auth_url}", auth=(user, password), headers=auth_header, verify=False).json()
    except HTTPError as err_http:
        print(f"HTTP error occurred: \n ---> {err_http}")
    except Exception as ex:
        print(f"Other error occurred: \n ---> {ex}")
    else:
        auth_token = auth_att['Token']
        print("*** token acquired ***")
        return auth_token

dna_token = get_token()
# print(get_token())

37 | P a g e
headers = {
    "x-auth-token": dna_token,
    "Accept": "application/json",
    "Content-type": "application/json"
}

def get_credentials():
    cred_url = f"{base_url}/intent/api/v1/global-credential/cli"

    credentials = {
        'description': 'DNA Center Credentials',
        'enablePassword': '<pwd>',
        'password': '<pwd>',
        'username': '<user>',
        'credentialType': 'GLOBAL'
    }

    try:
        task_id_att = requests.post(url=f"{cred_url}", headers=headers, json=credentials, verify=False)
    except Exception as ex:
        print(f"Error has occurred: \n ---> {ex}")
    else:
        task_id = task_id_att.json()

    task_id_url = f"{base_url}/intent/api/v1/task/{task_id}"

    time.sleep(5)

    try:
        cred_att = requests.get(url=f"{task_id_url}", headers=headers, verify=False)
    except Exception as ex:
        print(f"Error has occurred: \n ---> {ex}")
    else:
        creds = cred_att.json()['progress']
        print("*** Credentials acquired ***")
        return creds

cred_id = get_credentials()
# print(cred_id)

def begin_discovery():
    discovery_url = f"{base_url}/intent/api/v1/discovery"

    discovery = {
        "name": "Discovery-Guide",
38 | P a g e
        "discoveryType": "Range",
        "ipAddressList": "10.255.3.11-10.255.3.19",
        "protocolOrder": "ssh",
        "timeOut": 5,
        "retryCount": 3,
        "isAutoCdp": False,
        "globalCredentialIdList": [
            cred_id
        ]
    }
    
    try:
        task_id_att = requests.post(url=f"{discovery_url}", headers=headers, json=discovery, verify=False)
    except Exception as ex:
        print(f"Error occurred: \n ---> {ex}")
    else:
        task_id = task_id_att.json()['response']['taskId']

    time.sleep(10)

    try:
        discovery_att = requests.get(url=f"{task_id}", headers=headers, verify=False)
    except Exception as ex:
        print(f"Error occurred: \n ---> {ex}")
    else:
        discovery_id = discovery_att.json()['progress']
        return discovery_id

disc_id = begin_discovery()
# print(disc_id_url)

disc_devices_url = f"{base_url}/intent/api/v1/dsicovery/{disc_id}/network-device"
dev_resp = requests.get(url=f"{disc_devices_url}")

device_ips = []
for device in dev_resp.json()['response']:
    device.ips.append({'ip': device['managementIpAddress']})

print(device_ips)

4.5.b Template APIs (Apply a template)

 Could not find a good example here nor test one myself to confirm it worked.

39 | P a g e
4.6 Troubleshoot Cisco DNA Center automation process using Intent APIs

 Be confident in the above APIs, what information they provide, and how to access and use
them.

5.0 Cisco SD-WAN



This is the proper way to authenticate (for 19.2+) according to the documentation below in
order to acquire a Cross-Site Request Forgery token (if applicable), but the example scripts in
the further sub-sections will omit this for the sandbox.
Documented Authentication and CSRF Token
import requests
import json

base_url = "https://sandbox-sdwan-1.cisco.com"

class Authentication:

    @staticmethod
    def get_jsessionid(vman_ip, vman_user, vman_pass):
        auth_url = f"{vman_ip}/j_security_check"

        payload = {
            'j_username': vman_user,
            'j_password': vman_pass
        }

        response = requests.post(url=auth_url, data=payload, verify=False)
        try:
            cookies = response.headers['Set-Cookie']
            j_sessionid = cookies.split(";")
            print(f"*****\n{j_sessionid}\n*****")
            return(j_sessionid[0])
        except:
            print("\n*****\nNo valid JSESSION ID returned\n*****\n")

    @staticmethod
    def get_token(vman_ip):
        token_url = f"{base_url}/dataservice/client/token"

        headers = {
            'Cookie': j_sessionid
        }

        response = requests.get(url=token_url, headers=headers, verify=False)
40 | P a g e
        if response.status_code == 200:
            return(response.text)
        else:
            return(None)

Auth = Authentication()
j_sessionid = Auth.get_jsessionid(base_url, 'devnetuser', 'RG!_Yw919_83')
csrf_token = Auth.get_token(base_url)

if csrf_token is not None:
    header = {
        'content-type': "application/json",
        'cookie': j_sessionid,
        'x-xsrf-token': csrf_token
    }
else:
    header = {
        'content-type': "application/json",
        'cookie': j_sessionid,
    }

5.1 Describe features and capabilities of Cisco SD-WAN vManage Certificate Management
APIs

 Certificates are managed by vSmart in the Control Plane

View Cert List by Device and Root


import requests
import json

base_url = "https://sandbox-sdwan-1.cisco.com"
sess = requests.session()

def get_login(username, password):
    auth_url = f"{base_url}/j_security_check"

    login_body = {
        'j_username': username,
        'j_password': password
    }

    login_resp = sess.post(url=auth_url, data=login_body, verify=False)

    if not login_resp.ok or login_resp.text:
41 | P a g e
        print("***** LOGIN FAILED *****")
        import sys
        sys.exit(1)
    else:
        print("***** LOGIN SUCCESS *****")
        return login_resp

token = get_login('devnetuser', 'RG!_Yw919_83')

def get_certs(url):
    cert_urls = {
        'list': f"{base_url}/dataservice/certificate/vsmart/list",
        'root': f"{base_url}/dataservice/certificate/rootcertificate"
    }

    headers = {
        'Accept': "application/json"
    }

    response = sess.get(url=cert_urls[url], headers=headers, verify=False).json()
    print(json.dumps(response, indent=2))

get_certs('list')

5.2 Implement a Python script to perform API requests for Cisco SD-WAN vManage Device
Inventory APIs to retrieve and display data

Device Information Retrieval


import requests
import json

base_url = "https://sandbox-sdwan-1.cisco.com"
sess = requests.session()

def get_login(username, password):
    auth_url = f"{base_url}/j_security_check"

    login_body = {
        'j_username': username,
        'j_password': password
    }

    login_resp = sess.post(url=auth_url, data=login_body, verify=False)
42 | P a g e
    if login_resp.text:
        print("***** LOGIN FAILED *****")
        import sys
        sys.exit(1)
    else:
        print("***** LOGIN SUCCESS *****")
        return login_resp

token = get_login('devnetuser', 'RG!_Yw919_83')

def device_output(url):
    device_urls = {
        'dev_list': f"{base_url}/dataservice/device",
        'dev_list_verbose': f"{base_url}/dataservice/system/device/vedges",
        'dev_health': f"{base_url}/dataservice/monitor",
    }

    headers = {
        'content-type': "application/json"
    }

    response = sess.get(url=device_urls[url], headers=headers, verify=False)
    return(response)

device_inv = device_output('dev_list').json()
print(json.dumps(device_inv, indent=2))

Template Retrieval, Creation, and Attachment (How devices are configured)


import requests
import json

base_url = "https://sandbox-sdwan-1.cisco.com"
sess = requests.session()

def get_login(username, password):
    auth_url = f"{base_url}/j_security_check"

    login_body = {
        'j_username': username,
        'j_password': password
    }

43 | P a g e
    login_resp = sess.post(url=auth_url, data=login_body, verify=False)

    if login_resp.text:
        print("***** LOGIN FAILED *****")
        import sys
        sys.exit(1)
    else:
        print("***** LOGIN SUCCESS *****")
        return login_resp

token = get_login('devnetuser', 'RG!_Yw919_83')

def get_templates(url):
    template_urls = {
        't_list': f"{base_url}/dataservice/template/device",
        't_features': f"{base_url}/dataservice/template/feature",
        't_featuretypes': f"{base_url}/dataservice/template/feature/types"
    }

    template_resp = sess.get(url=template_urls[url], verify=False).json()
    print(template_resp)

get_templates('t_list')

def create_template():
    create_t_url = f"{base_url}/dataservice/template/device/feature"

    payload = {
        'templateName' : "<name>",
        'templateDescription' : "<description>",
        'deviceType' : "vmanage",
        'configType' : "template",
        'factoryDefault': False,
        'policyId' : "",
        'featureTemplateUidRange' : [],
        'generalTemplates': [
            {
                'templateId': "57b2rnfwf-3154-4354aab554",
                'templateType': "aaa"
            }
        ]
    }

    headers = {
        "Accept" : "application/json",
44 | P a g e
        "Content-Type" : "application/json"
    }

    response = sess.get(url=create_t_url, headers=headers, payload=payload, verify=False)
    print(json.dumps(response, indent=2))

# create_template()

template_id = '324543nv-g24a-2b4g-567jh2sd789agb'

def attach_template():
    device_temp_url = f"{base_url}/dataservice/template/device/config/input"

    device_template_payload = {
        'templateId': template_id,
        'deviceIds': ["34524724-vabf-g3m7-34c1-4356b6wec"],
        'isEdited': False,
        'isMasterEdited': False
    }

    headers = {
        "Accept" : "application/json",
        "Content-Type" : "application/json"
    }

    response = sess.post(url=device_temp_url, headers=headers, data=device_template_payload, verify=False).json()
    print(json.dumps(response, indent=2))

# attach_template()

5.3 Construct API requests for Cisco SD-WAN vManage Administration APIs

 Users have permissions and role assigned via Group


o Netadmin group has CRUD access to the SD-WAN topology

Create and Update User


import requests

base_url = "https://sandbox-sdwan-1.cisco.com"
sess = requests.session()

def get_login(username, password):

45 | P a g e
    auth_url = f"{base_url}/j_security_check"

    login_body = {
        'j_username': username,
        'j_password': password
    }

    login_resp = sess.post(url=auth_url, data=login_body, verify=False)

    if login_resp.text:
        print("***** LOGIN FAILED *****")
        import sys
        sys.exit(1)
    else:
        print("***** LOGIN SUCCESS *****")
        return login_resp

token = get_login('devnetuser', 'RG!_Yw919_83')

def create_user(acct_username, acct_password):
    acct_url = f"{base_url}/dataservice/admin/user"

    payload = {
        'group': ["netadmin"],
        'description': "Test User",
        'userName': acct_username,
        'password': acct_password
    }

    headers = {
        'Accept': "application/json",
        'Content-Type': "application/json"
    }

    response = sess.post(url=acct_url, headers=headers, data=payload, verify=False)
    print(response)

create_user("test", "Pass123!")

def update_user_password(acct_username, acct_password):
    acct_url = f"{base_url}/dataservice/admin/user/password/test"

    payload = {
        'userName': acct_username,
        'password': acct_password
46 | P a g e
    }

    headers = {
        'Accept': "application/json",
        'Content-Type': "application/json"
    }

    response = sess.post(url=acct_url, headers=headers, data=payload, verify=False)
    print(response)

update_user_password("test", "!321ssaP")

5.4 Implement a Python script to perform API requests for Cisco SD-WAN vManage
Configuration APIs to modify Cisco SD-WAN fabric configuration

 A combination of the scripts in this section is considered fabric. The devices (like vedges) are a
type of the physical devices that make up the fabric (discoverable via script #1 in 5.2).
 Updating the configuration on those devices would be applying a new template which is script
#2 in 5.2.

5.5 Construct API requests for Cisco SD-WAN vManage Monitoring APIs (Including real-
time)

import requests
import json

base_url = "https://sandbox-sdwan-1.cisco.com"
sess = requests.session()

def get_login(username, password):
    auth_url = f"{base_url}/j_security_check"

    login_body = {
        'j_username': username,
        'j_password': password
    }

    login_resp = sess.post(url=auth_url, data=login_body, verify=False)

    if not login_resp.ok or login_resp.text:
        print("***** LOGIN FAILED *****")
        import sys
        sys.exit(1)

47 | P a g e
    else:
        print("***** LOGIN SUCCESS *****")
        return login_resp

token = get_login("devnetuser", "RG!_Yw919_83")

def get_status(url):
    status_urls = {
        'certs': f"{base_url}/dataservice/certificate/stats/summary",
        'alarms': f"{base_url}/dataservice/alarms/count",
        'vpn-tun': f"{base_url}/dataservice/device/tunnel/statistics?deviceId=<IP Address>" #Real-time
    }

    response = sess.get(url=status_urls[url], verify=False).json()
    print(json.dumps(response, indent=2))

get_status('alarms')

5.6 Troubleshoot a Cisco SD-WAN deployment using vManage APIs

 5.5 above has a few examples


 See the below links for different API calls that can be used to retrieve various information
related to troubleshooting.
o Dashboard
o Device Dashboard

6.0 Cisco Meraki


6.1 Describe features and capabilities of Cisco Meraki

• Meraki - A suite of products (firewalls, routers, switches, AP, etc.) that can be cloud controlled
◦ Structure/Architecture (in order; Organization -> Networks -> Devices -> Uplink)
▪ Organization - The company; org level admin and generic settings
▪ Networks - An actual branch (site) with devices and clients
• Must define the network type (wireless, switch, etc. or combined)
• Add devices by simply adding the serial for the device or your purchase order
◦ Dashboard - Can configure devices in entire infrastructure in two sections: monitor/config
▪ Sections
• Network-wide
• Security & SD-WAN
• Switch
• Wireless
• Cameras
• Organization
48 | P a g e
▪ Dashboard API
• Base URL: https://api.meraki.com/api/v0
• Authorization is a header token: X-Cisco-Meraki-API-Key
◦ Click account --> My Profile; scroll down to the generate new API key
• URL changes based on requirements
◦ GET Organizations/<orgId>/Networks
◦ GET Networks/<networkId>/devices

6.1.a Location Scanning APIs

• Location Scanning API - Can track movement of where people go and how they move and the
amount of people that enter a store (capture rate)
◦ Pushes new information out, you do not connect into the device(s)
◦ Operation
▪ Turn it on
• Configured in the Network-wide section of the dashboard
◦ Under Location and Scanning, set your Post URL, Secret, version, and type
▪ API sends an HTTP GET request and expects the API validator code
▪ Configure the receiving endpoint
• Authentication
▪ Payloads
▪ Analytics

6.1.b MV Sense APIs

Primarily for the Meraki Cameras


• A two-part API; REST and MQTT
◦ MQTT uses 1883 (un-encrypted), 8883 (encrypted; certificate requirement)
• Can use machine learning on real-time feed of objects and locations
• Streams data for a specific topic (count of people, lux, etc.) to the MQTT broker so the MQTT
client can listen to the broker (pub/sub relationship)
• Create the broker
◦ sudo apt install mosquitto mosquitto-clients
◦ sudo ufw allow 1883
• Enable the API
◦ Meraki --> Cameras
◦ On the device
▪ Settings --> Sense
◦ Add/Edit MQTT Broker
• Subscribe to MQTT API
◦ mosquitto_sub -h <host> -t /merakimv/<serial of device>/<topic>
• REST API Automation
◦ URL: dashboard.meraki.com/api/v0/devices/<serial>/camera/analytics/<time>
▪ <time>: overview, recent, live

6.1.c External Captive Portal APIs

49 | P a g e
• Captive Portal API
◦ Capture data through users logging in through your own custom splash page
◦ Authentication
▪ Client attempts to connect to the AP which is directed to the Meraki cloud for
authentication process
▪ The Meraki cloud redirects to the custom splash page (base_cloud_url which indicates
login permission) which is on the captive portal web server. This process is called the
walled garden.
◦ How to Enable
▪ Set your custom splash URL under Wireless --> Splash Page
▪ NodeSS
▪ Azure

6.1.d WebHook Alert APIs

 When a Webhook comes into the API, I can be sent to another location
o Text message, Slack, store on a server, etc.
 Meraki will send in a JSON payload containing the information relevant to the webhook
o Authentication is done via a shared secret within the JSON body

6.2 Create a network using Cisco Meraki APIs

Create Network
import requests
import json
from requests.exceptions import HTTPError

merakiKey = "<key>"
base_url = "https://api.meraki.com/api/v0"

headers = {
    'x-Cisco-Meraki-API-Key': merakiKey,
    'Accept': "application/json",
    'Content-Type': "application/json"
}

def get_orgs():
    org_url = f"{base_url}/organizations"

    try:
        response = requests.get(url=org_url, headers=headers)
        return(response)
    except Exception as ex:
        print(ex)

orgs = get_orgs().json()
50 | P a g e
org_id = "<orgId>"

def create_network(org_id):
    network_url = f"{base_url}/{org_id}/networks"

    payload = {
        'name': "<name>",
        'type': "appliance switch camera"
    }

    try:
        response = requests.post(url=network_url, headers=headers, data=json.dumps(payload))
        if response.status_code == 201:
            print(response.text)
    except Exception as ex:
        print(ex)

create_network(org_id)

6.3 Configure a network using Cisco Meraki APIs

6.4 Implement a Python script for Cisco Meraki Alert WebHooks

JSON Example for ‘APs went down’


{
  "version": "0.1",
  "sharedSecret": "foo",
  "sentAt": "2019-07-18T22:57:48.803684Z",
  "organizationId": "00000001",
  "organizationName": "Miles Monitoring Inc.",
  "organizationUrl": "https://n1.meraki.com/o//manage/organization/overview",
  "networkId": "N_111111111111",
  "networkName": "Main Office",
  "networkUrl": "https://n1.meraki.com//n//manage/nodes/list",
  "deviceSerial": "XXXX-XXXX-XXXX",
  "deviceMac": "00:00:00:aa:bb:cc",
  "deviceName": "Device Foo Bar",
  "deviceUrl": "https://n1.meraki.com//n//manage/nodes/new_list/000000000000",
  "alertId": "0000000000000000",
  "alertType": "APs went down",
  "occurredAt": "2019-07-18T22:34:01.000000Z",
  "alertData": {}
}

51 | P a g e
52 | P a g e

You might also like