Professional Documents
Culture Documents
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
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
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
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")
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
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>.
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
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.
• 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
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
int_config = python_response['interfaces']['interface']
oper_state = python_response['interfaces-state']['interface']
print(f"Name: {int_config['name']['#text']}")
print(f"")
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
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)
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))
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)
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)
- name: What is the IOS OS ver?
debug:
var: ansible_net_version
- name: Wha is the hostname?
debug:
var: ansible_net_hostname
- name: Interfaces output
debug:
var: if_data['stdout_line'][0]
- 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
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
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)
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.
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
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
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
• 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
• 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
• 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
• 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
• 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
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
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))
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)
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
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)
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.
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
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
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))
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
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')
• 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
• 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
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
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
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)
51 | P a g e
52 | P a g e