Practice Questions

Test your knowledge on this topic in the ENAUTO Exam Trainer — 186 questions across 5 interactive modes.

Cisco SD-WAN (vManage) – Deep Dive for ENAUTO v2.0

Exam Relevance

Topic 3.0 Controller-Based Network Automation (30%) — SD-WAN is the WAN-focused controller. Unlike Catalyst Center (token auth) or Meraki (API key), SD-WAN uses session-based authentication with cookies and requires an XSRF token for write operations.

Exam Topics Covered:
3.1 – Day-0 provisioning (controller-based)
3.2 – Python to manage and monitor configurations
3.3 – Jinja2 templates (feature templates, device templates)
3.5 – Security automation (centralized policies, segmentation)
3.6 – Troubleshoot REST API automation solutions
4.3 – Manage device software versions
4.4 – Monitor network health

Table of Contents


1 – Architecture Overview

Cisco SD-WAN (formerly Viptela) uses a multi-component architecture where the management plane (vManage) exposes REST APIs for automation.

┌─────────────────────────────────────────────┐
│              Your Python Script              │
│  (requests library  OR  catalystwan SDK)     │
└──────────────────┬──────────────────────────┘
                   │ HTTPS (port 443)
                   ▼
┌─────────────────────────────────────────────┐
│              vManage Controller              │
│          (Management Plane – NMS)            │
│                                             │
│  ┌──────────┐  ┌──────────┐  ┌───────────┐ │
│  │ Auth     │  │ Data     │  │ Config    │ │
│  │ /j_secu- │  │ Service  │  │ Template  │ │
│  │ rity_chk │  │ /data-   │  │ /data-    │ │
│  └──────────┘  │ service/ │  │ service/  │ │
│                └──────────┘  │ template/ │ │
│                              └───────────┘ │
└──────────────────┬──────────────────────────┘
                   │ DTLS / TLS Tunnels
          ┌────────┴────────┐
          ▼                 ▼
┌──────────────┐   ┌──────────────┐
│   vBond       │   │   vSmart      │
│ (Orchestrator)│   │ (Control Plane│
│ Zero-touch    │   │  OMP routes,  │
│ provisioning  │   │  policies)    │
└──────────────┘   └──────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────┐
│            WAN Edge Devices                 │
│     vEdge (Viptela OS) · cEdge (IOS-XE)    │
└─────────────────────────────────────────────┘

SD-WAN Component Roles (Exam Favourite)

ComponentRolePlane
vManageCentralized NMS, REST API host, GUIManagement
vBondOrchestrator, initial device auth, NAT traversalOrchestration
vSmartRoute reflector (OMP), policy enforcementControl
vEdgeWAN router (Viptela OS)Data
cEdgeWAN router (IOS-XE based, e.g., ISR, ASR, C8000)Data

Key API Base Path

All SD-WAN API calls go through vManage at: https://<vmanage-ip>/dataservice/...

The only exception is authentication, which uses: https://<vmanage-ip>/j_security_check


2 – Authentication with Python requests

Exam Topic: 3.2, 3.6

SD-WAN authentication is a two-step process: get a session cookie, then get a CSRF token for write operations. This is unique among Cisco controllers.

How It Works

  1. POST to /j_security_check with form-encoded credentials (NOT JSON, NOT Basic Auth).
  2. Receive a JSESSIONID cookie in the Set-Cookie response header.
  3. For write operations (POST/PUT/DELETE): fetch an XSRF token from /dataservice/client/token.
  4. Include the XSRF token as X-XSRF-TOKEN header in all write requests.

Full Working Example

import requests
import urllib3
 
# ── Disable self-signed cert warnings (lab only!) ──────────────
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
 
# ── Connection Details ─────────────────────────────────────────
BASE_URL = "https://sandboxsdwan.cisco.com"
USERNAME = "devnetuser"
PASSWORD = "Cisco123!"
 
# ── Step 1: Authenticate (get JSESSIONID cookie) ──────────────
session = requests.Session()
 
login_response = session.post(
    f"{BASE_URL}/j_security_check",
    data={                              # form-encoded, NOT json=
        "j_username": USERNAME,
        "j_password": PASSWORD
    },
    verify=False
)
 
# Check for successful login
# vManage returns 200 even on failure — check for redirect or body
if b"<html>" in login_response.content:
    raise Exception("Login failed — check credentials")
 
print(f"Session cookie: {session.cookies.get('JSESSIONID')[:20]}...")
 
# ── Step 2: Get XSRF Token (required for write operations) ────
token_response = session.get(
    f"{BASE_URL}/dataservice/client/token",
    verify=False
)
xsrf_token = token_response.text
 
# Add XSRF token to session headers for all future requests
session.headers.update({
    "X-XSRF-TOKEN": xsrf_token,
    "Content-Type": "application/json",
    "Accept": "application/json"
})
 
print(f"XSRF Token: {xsrf_token[:20]}...")
print("Authenticated successfully — session ready for API calls")

Exam Trap: Form-Encoded, NOT JSON

The /j_security_check endpoint expects application/x-www-form-urlencoded data:

data={"j_username": "admin", "j_password": "pass"}   # CORRECT
json={"j_username": "admin", "j_password": "pass"}    # WRONG

Using json= sends Content-Type: application/json, which vManage rejects silently (returns 200 but no session).

Exam Trap: Login Returns 200 Even on Failure

Unlike other controllers, vManage returns HTTP 200 even for bad credentials. You must inspect the response body or check for the JSESSIONID cookie to confirm success. An HTML body means login failed.

What Happens on Failure?

SymptomMeaningLikely Cause
200 with HTML bodyLogin failedWrong username/password
200 with empty bodyLogin succeededCredentials valid
403 Forbidden on writeMissing XSRF tokenDidn’t fetch /client/token
419 Token ExpiredSession timed outJSESSIONID expired (~24h default)
SSL ErrorCertificate issueMissing verify=False in lab

Why requests.Session() Is Critical

# ── WITHOUT Session — cookies are lost between calls ──────────
response1 = requests.post(f"{BASE_URL}/j_security_check", data=creds, verify=False)
# JSESSIONID cookie in response1 is NOT carried forward:
response2 = requests.get(f"{BASE_URL}/dataservice/device", verify=False)
# ^^^ This FAILS with 401 — no session cookie sent
 
# ── WITH Session — cookies persist automatically ──────────────
session = requests.Session()
session.post(f"{BASE_URL}/j_security_check", data=creds, verify=False)
# JSESSIONID cookie is stored in session.cookies and sent automatically:
response = session.get(f"{BASE_URL}/dataservice/device", verify=False)
# ^^^ This WORKS — session cookie is included

Key Difference: requests.Session() vs requests.post()

SD-WAN is the only Cisco controller where you must use requests.Session(). Catalyst Center uses a token header, Meraki uses an API key, ISE uses Basic Auth — all of which work with individual requests. SD-WAN relies on a cookie that must persist across calls.


3 – Authentication with the Catalyst WAN SDK

Exam Topic: 3.2

The SDK handles session management, XSRF tokens, and reconnection automatically.

Installation

pip install catalystwan

Full Working Example

from catalystwan.session import create_manager_session
 
# The SDK handles:
#   - Session cookie management
#   - XSRF token fetching
#   - Automatic re-authentication on session expiry
#   - SSL verification config
session = create_manager_session(
    url="sandboxsdwan.cisco.com",
    username="devnetuser",
    password="Cisco123!",
    port=443
)
 
print(f"Connected to vManage: {session.server_url}")
print(f"Session active: {session.session_type}")
 
# ── Always close the session when done ────────────────────────
# This invalidates the JSESSIONID on the server
session.close()
from catalystwan.session import create_manager_session
 
# Automatic cleanup with context manager
with create_manager_session(
    url="sandboxsdwan.cisco.com",
    username="devnetuser",
    password="Cisco123!"
) as session:
    # All API calls here...
    devices = session.api.devices.get()
    print(f"Total devices: {len(devices)}")
# Session is automatically closed here

SDK vs requests — When to Use What

Aspectrequests + Sessioncatalystwan SDK
Auth managementManual (login + XSRF)Automatic
Session expiryHandle yourselfAuto-reconnect
XSRF tokenFetch and attach manuallyTransparent
Template operationsRaw JSON payloadsTyped Python objects
Exam relevanceHigh — must know the raw flowHigh — must know the SDK exists
Production useMore control, more codeLess code, batteries included

4 – Get Devices and System Status

Exam Topics: 3.2, 4.4

The device endpoint is the foundation for inventory and health monitoring.

Get All Devices (requests)

# ── List all SD-WAN devices ──────────────────────────────────
response = session.get(
    f"{BASE_URL}/dataservice/device",
    verify=False
)
response.raise_for_status()
 
devices = response.json()["data"]
 
for device in devices:
    print(f"{device.get('host-name', 'N/A'):<25} "
          f"{device.get('system-ip', 'N/A'):<16} "
          f"{device.get('device-type', 'N/A'):<15} "
          f"{device.get('reachability', 'N/A')}")

Get Devices by Type

# ── Filter by device type ─────────────────────────────────────
# Device types: "vedge", "vsmart", "vbond", "vmanage"
response = session.get(
    f"{BASE_URL}/dataservice/device",
    params={"device-type": "vedge"},    # WAN edge routers
    verify=False
)
vedges = response.json()["data"]
 
# ── Get controllers only ──────────────────────────────────────
response = session.get(
    f"{BASE_URL}/dataservice/device/controllers",
    verify=False
)
controllers = response.json()["data"]
 
for ctrl in controllers:
    print(f"{ctrl.get('deviceType'):<12} "
          f"{ctrl.get('host-name', 'N/A'):<20} "
          f"{ctrl.get('system-ip', 'N/A')}")

Device Response Structure (Key Fields)

{
  "data": [
    {
      "deviceId": "10.10.1.1",
      "system-ip": "10.10.1.1",
      "host-name": "Branch-cEdge-01",
      "device-type": "vedge",
      "device-model": "vedge-C8000V",
      "reachability": "reachable",
      "status": "normal",
      "site-id": "100",
      "uuid": "C8K-aaaa-bbbb-cccc-dddd",
      "version": "17.9.4a",
      "board-serial": "FDO12345678",
      "uptime-date": 1708097400000,
      "certificate-validity": "Valid",
      "controlConnections": "3",
      "personality": "vedge"
    }
  ]
}

Exam Note: system-ip vs deviceId

In SD-WAN, the system-ip is the loopback address that uniquely identifies a device in the overlay network. The deviceId field typically holds the same value. The uuid is used for template attachment and device-specific operations.

Get Device Interface Details

# ── Get interfaces for a specific device ──────────────────────
system_ip = "10.10.1.1"
 
response = session.get(
    f"{BASE_URL}/dataservice/device/interface",
    params={"deviceId": system_ip},
    verify=False
)
interfaces = response.json()["data"]
 
for intf in interfaces:
    print(f"{intf.get('ifname', 'N/A'):<20} "
          f"{intf.get('ip-address', 'N/A'):<16} "
          f"Admin: {intf.get('if-admin-status', 'N/A'):<6} "
          f"Oper: {intf.get('if-oper-status', 'N/A')}")

Get Running Configuration

# ── Get running config of a device ────────────────────────────
device_id = "C8K-aaaa-bbbb-cccc-dddd"    # UUID, not system-ip
 
response = session.get(
    f"{BASE_URL}/dataservice/device/config",
    params={"deviceId": device_id},
    verify=False
)
 
running_config = response.json()
print(running_config)

5 – Get Devices (SDK)

Exam Topic: 3.2

from catalystwan.session import create_manager_session
 
with create_manager_session(
    url="sandboxsdwan.cisco.com",
    username="devnetuser",
    password="Cisco123!"
) as session:
 
    # ── Get all devices ──────────────────────────────────────
    devices = session.api.devices.get()
    for device in devices:
        print(f"{device.hostname:<25} "
              f"{device.local_system_ip:<16} "
              f"{device.reachability}")
 
    # ── Filter by device type ────────────────────────────────
    controllers = session.api.devices.controllers()
    edges = session.api.devices.get()  # Filter in Python:
    vedges = [d for d in edges if d.personality == "vedge"]

6 – Feature Templates and Device Templates

Exam Topics: 3.2, 3.3

SD-WAN uses a two-tier template system: Feature Templates define individual settings (VPN, interface, etc.), and Device Templates combine them into a complete device configuration.

Template Architecture

┌──────────────────────────────────────────────────────┐
│                Device Template                        │
│  "Branch-cEdge-Template"                             │
│                                                      │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│  │ System       │ │ VPN 0        │ │ VPN 512      │ │
│  │ Feature      │ │ Feature      │ │ Feature      │ │
│  │ Template     │ │ Template     │ │ Template     │ │
│  │ (hostname,   │ │ (WAN intf,   │ │ (mgmt intf,  │ │
│  │  system-ip,  │ │  tunnels,    │ │  DNS, NTP)   │ │
│  │  site-id)    │ │  TLOC)       │ │              │ │
│  └──────────────┘ └──────────────┘ └──────────────┘ │
│                                                      │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│  │ VPN 1        │ │ Security     │ │ AAA          │ │
│  │ Feature      │ │ Policy       │ │ Feature      │ │
│  │ Template     │ │ Feature      │ │ Template     │ │
│  │ (LAN intf,   │ │ Template     │ │              │ │
│  │  services)   │ │              │ │              │ │
│  └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────┘

VPN Numbering Convention (Exam Favourite)

VPN IDPurposeAlso Known As
VPN 0Transport (WAN underlay)Transport VPN
VPN 512ManagementOut-of-band management
VPN 1–511Service-side (LAN segments)Service VPNs
VPN 0 carries the DTLS/TLS tunnels to controllers. VPN 512 is for device management. Everything else is user traffic.

List Feature Templates

# ── Get all feature templates ─────────────────────────────────
response = session.get(
    f"{BASE_URL}/dataservice/template/feature",
    verify=False
)
feature_templates = response.json()["data"]
 
for ft in feature_templates:
    print(f"{ft['templateName']:<35} "
          f"Type: {ft['templateType']:<20} "
          f"Attached: {ft.get('devicesAttached', 0)}")

List Device Templates

# ── Get all device templates ──────────────────────────────────
response = session.get(
    f"{BASE_URL}/dataservice/template/device",
    verify=False
)
device_templates = response.json()["data"]
 
for dt in device_templates:
    print(f"{dt['templateName']:<35} "
          f"Model: {dt['deviceType']:<20} "
          f"Attached: {dt.get('devicesAttached', 0)}")

Create a Feature Template (VPN)

# ── Create a VPN feature template ────────────────────────────
vpn_template = {
    "templateName": "Branch-VPN10-Service",
    "templateDescription": "Service VPN 10 for branch LAN",
    "templateType": "vpn-vedge",
    "deviceType": ["vedge-C8000V"],
    "templateMinVersion": "15.0.0",
    "templateDefinition": {
        "vpn-id": {
            "vipObjectType": "object",
            "vipType": "constant",
            "vipValue": 10
        },
        "name": {
            "vipObjectType": "object",
            "vipType": "constant",
            "vipValue": "Branch-LAN"
        },
        "dns": [{
            "dns-addr": {
                "vipObjectType": "object",
                "vipType": "constant",
                "vipValue": "8.8.8.8"
            },
            "role": {
                "vipObjectType": "object",
                "vipType": "constant",
                "vipValue": "primary"
            }
        }]
    },
    "factoryDefault": False
}
 
response = session.post(
    f"{BASE_URL}/dataservice/template/feature",
    json=vpn_template,
    verify=False
)
response.raise_for_status()
template_id = response.json()["templateId"]
print(f"Feature template created: {template_id}")

vipType Values — Critical for the Exam

SD-WAN feature templates use a special vipType system to control variable binding:

vipTypeMeaningExample
"constant"Fixed value — same on all devices"vipValue": 10 (VPN ID)
"variableName"Device-specific variable — set during attach"vipValue": "vpn10_dns"
"ignore"Use device default — omit from configN/A

The exam tests whether you understand when to use constant (hardcoded), variableName (per-device), or ignore (skip).

Create a Device Template

# ── Create a device template combining feature templates ──────
device_template = {
    "templateName": "Branch-cEdge-Full",
    "templateDescription": "Complete branch cEdge configuration",
    "deviceType": "vedge-C8000V",
    "configType": "template",         # "template" = feature-based
    "generalTemplates": [
        {
            "templateId": "<system-feature-template-id>",
            "templateType": "cisco_system"
        },
        {
            "templateId": "<vpn0-feature-template-id>",
            "templateType": "cisco_vpn",
            "subTemplates": [
                {
                    "templateId": "<wan-interface-template-id>",
                    "templateType": "cisco_vpn_interface"
                }
            ]
        },
        {
            "templateId": "<vpn10-feature-template-id>",
            "templateType": "cisco_vpn",
            "subTemplates": [
                {
                    "templateId": "<lan-interface-template-id>",
                    "templateType": "cisco_vpn_interface"
                }
            ]
        },
        {
            "templateId": "<vpn512-feature-template-id>",
            "templateType": "cisco_vpn"
        }
    ],
    "policyId": "",
    "featureTemplateUidRange": []
}
 
response = session.post(
    f"{BASE_URL}/dataservice/template/device/feature",
    json=device_template,
    verify=False
)
response.raise_for_status()
device_template_id = response.json()["templateId"]
print(f"Device template created: {device_template_id}")

configType Values

ValueMeaning
"template"Feature template-based (recommended, modular)
"file"CLI-based template (raw text, like a Jinja2 file)
The exam may ask about both, but feature template-based is the modern approach.

7 – Attach and Detach Device Templates

Exam Topics: 3.2, 3.1

Attaching a device template is how SD-WAN pushes configuration to devices. This is a multi-step async process.

Step 1: Get Required Variables for a Template

# ── Before attaching, get the variables the template needs ────
template_id = "<device-template-id>"
 
response = session.get(
    f"{BASE_URL}/dataservice/template/device/config/input",
    params={"templateId": template_id},
    verify=False
)
input_schema = response.json()
 
# This tells you what variables need values per device
print("Required variables:")
for column in input_schema.get("header", {}).get("columns", []):
    print(f"  {column['property']}: {column.get('title', '')}")

Step 2: Attach a Device Template

# ── Attach template to one or more devices ────────────────────
attach_payload = {
    "deviceTemplateList": [
        {
            "templateId": template_id,
            "device": [
                {
                    "csv-status": "complete",
                    "csv-deviceId": "C8K-aaaa-bbbb-cccc-dddd",
                    "csv-deviceIP": "10.10.1.1",
                    "csv-host-name": "Branch-cEdge-01",
                    "//system/host-name": "Branch-cEdge-01",
                    "//system/system-ip": "10.10.1.1",
                    "//system/site-id": "100",
                    "/0/vpn0-interface/ip/address": "192.168.1.1/24",
                    "/0/vpn0-interface/tunnel-interface/color": "biz-internet"
                }
            ],
            "isEdited": False,
            "isMasterEdited": False
        }
    ]
}
 
response = session.post(
    f"{BASE_URL}/dataservice/template/device/config/attachfeature",
    json=attach_payload,
    verify=False
)
response.raise_for_status()
action_id = response.json()["id"]
print(f"Template attach initiated: action ID = {action_id}")

Step 3: Monitor Attach Status

import time
 
def wait_for_action(session, base_url, action_id, timeout=300, interval=10):
    """Poll an SD-WAN action until completion."""
    elapsed = 0
 
    while elapsed < timeout:
        response = session.get(
            f"{base_url}/dataservice/device/action/status/{action_id}",
            verify=False
        )
        status_data = response.json()
 
        summary = status_data.get("summary", {})
        status = summary.get("status", "unknown")
 
        print(f"Status: {status} "
              f"(Success: {summary.get('count', {}).get('success', 0)}, "
              f"Failure: {summary.get('count', {}).get('failure', 0)})")
 
        if status in ("done", "Done - Scheduled"):
            return status_data
        if summary.get("count", {}).get("failure", 0) > 0:
            # Check detailed error
            for device in status_data.get("data", []):
                if device.get("statusId") == "failure":
                    print(f"  FAILED: {device.get('host-name')} "
                          f"— {device.get('activity', ['Unknown error'])}")
            return status_data
 
        time.sleep(interval)
        elapsed += interval
 
    raise TimeoutError(f"Action {action_id} did not complete within {timeout}s")
 
result = wait_for_action(session, BASE_URL, action_id)

Template Attach Is Asynchronous

Like Catalyst Center, SD-WAN write operations (template attach, policy activate, software upgrade) are asynchronous. You submit the request, get an action_id, and must poll /dataservice/device/action/status/{id} for completion. The exam loves testing this pattern under topic 3.6.

Detach a Template

# ── Detach a device from its template ─────────────────────────
detach_payload = {
    "deviceType": "vedge",
    "devices": [
        {
            "deviceId": "C8K-aaaa-bbbb-cccc-dddd",
            "deviceIP": "10.10.1.1"
        }
    ]
}
 
response = session.post(
    f"{BASE_URL}/dataservice/template/config/device/mode/cli",
    json=detach_payload,
    verify=False
)
action_id = response.json()["id"]
print(f"Detach action: {action_id}")

Detach = CLI Mode

When you detach a device template, the device reverts to CLI mode — it keeps its current running config but is no longer managed by templates. This is a significant operational change. The exam may test whether you understand this consequence.


8 – Day-0 Provisioning

Exam Topic: 3.1 – Construct a controller-based network automation solution for Day-0 provisioning

SD-WAN Day-0 provisioning involves bootstrapping WAN edge devices into the SD-WAN fabric through vBond orchestration.

How SD-WAN Day-0 Works

┌──────────────┐     ┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│  New Device  │────►│   vBond      │────►│   vManage    │────►│   Device     │
│  Powers on   │     │ Orchestrator │     │  Pushes      │     │  In fabric   │
│  (bootstrap  │     │ Authenticates│     │  device      │     │  with full   │
│   config)    │     │ device certs │     │  template    │     │  config      │
└──────────────┘     └──────────────┘     └──────────────┘     └──────────────┘
    Step 1               Step 2               Step 3               Step 4
  (manual/ZTP)         (automatic)          (API-driven)         (automatic)

Day-0 Bootstrap Config

Every SD-WAN edge device needs a minimal bootstrap config to reach vBond:

system
 system-ip         10.10.1.1
 site-id           100
 organization-name "MyOrg"
 vbond 198.51.100.1
!
vpn 0
 interface GigabitEthernet1
  ip address 192.168.1.1/24
  tunnel-interface
   color biz-internet
  !
  no shutdown
 !
 ip route 0.0.0.0/0 192.168.1.254
!

The bootstrap tells the device how to reach vBond. Once it connects, vManage pushes the full configuration.

Step 1: Get Available (Unconfigured) Devices

# ── List devices that are available for provisioning ──────────
response = session.get(
    f"{BASE_URL}/dataservice/device/available",
    verify=False
)
available_devices = response.json()["data"]
 
for device in available_devices:
    print(f"UUID: {device.get('uuid'):<40} "
          f"Model: {device.get('deviceModel', 'N/A'):<20} "
          f"Serial: {device.get('board-serial', 'N/A')}")

Step 2: Upload WAN Edge Device List (Serial File)

# ── Upload the WAN edge authorized serial number list ─────────
# This is the serial number file from Cisco Smart Account
# Format: each line has uuid,serial,model,organization-name,...
 
serial_file_content = """uuid1,SERIAL001,vedge-C8000V,MyOrg,NA,true,false
uuid2,SERIAL002,vedge-C8000V,MyOrg,NA,true,false"""
 
response = session.post(
    f"{BASE_URL}/dataservice/device/vedge/serialFile",
    files={"file": ("serial_file.csv", serial_file_content, "text/csv")},
    verify=False
)
response.raise_for_status()
print(f"Serial file uploaded: {response.json()}")

Step 3: Attach Device Template for Day-0

# ── Full Day-0 workflow: Upload serial → attach template ──────
 
# 1. Get the device template ID
templates = session.get(
    f"{BASE_URL}/dataservice/template/device",
    verify=False
).json()["data"]
 
target_template = next(
    (t for t in templates if "Branch" in t["templateName"]), None
)
 
if not target_template:
    print("No matching template found!")
    exit()
 
template_id = target_template["templateId"]
 
# 2. Get the template input variables
inputs = session.get(
    f"{BASE_URL}/dataservice/template/device/config/input",
    params={"templateId": template_id},
    verify=False
).json()
 
# 3. Prepare device-specific variable values
device_variables = {
    "csv-status": "complete",
    "csv-deviceId": "C8K-aaaa-bbbb-cccc-dddd",
    "csv-deviceIP": "10.10.1.1",
    "csv-host-name": "Branch-cEdge-01",
    "//system/host-name": "Branch-cEdge-01",
    "//system/system-ip": "10.10.1.1",
    "//system/site-id": "100"
}
 
# 4. Attach the template
attach_response = session.post(
    f"{BASE_URL}/dataservice/template/device/config/attachfeature",
    json={
        "deviceTemplateList": [{
            "templateId": template_id,
            "device": [device_variables],
            "isEdited": False,
            "isMasterEdited": False
        }]
    },
    verify=False
)
action_id = attach_response.json()["id"]
print(f"Day-0 provisioning initiated: {action_id}")
 
# 5. Monitor progress
result = wait_for_action(session, BASE_URL, action_id)

SD-WAN Day-0 vs Other Controllers

AspectSD-WANCatalyst CenterMeraki
DiscoveryvBond orchestration + certsDHCP Option 43 / DNSCloud auto-connect
AuthCertificate-based (PKI)Certificate + PnPSerial claim
Config pushDevice template attachPnP templateNetwork-level settings
BootstrapManual or ZTP serial fileFactory default + DHCPPower on + claim
ComplexityHigh — certs, vBond, overlayMedium — DHCP, templatesLow — claim & go

9 – Centralized Policy Management

Exam Topics: 3.5, 3.2

SD-WAN policies control traffic flow across the overlay. Understanding the policy hierarchy is critical for the exam.

Policy Architecture

┌─────────────────────────────────────────────────────┐
│                   vSmart Policy                      │
│                                                     │
│  ┌─────────────────┐    ┌─────────────────────────┐ │
│  │ Policy Building │    │ Policy Application      │ │
│  │ Blocks          │    │                         │ │
│  │                 │    │ Topology ──► vSmarts    │ │
│  │ • VPN Lists     │    │ Control  ──► vSmarts    │ │
│  │ • Site Lists    │    │ Data     ──► vEdges     │ │
│  │ • SLA Classes   │    │ AppRoute ──► vEdges     │ │
│  │ • Prefix Lists  │    │ Security ──► vEdges     │ │
│  │ • App Lists     │    │                         │ │
│  └─────────────────┘    └─────────────────────────┘ │
└─────────────────────────────────────────────────────┘

Centralized vs Localized Policies

TypeApplied ByScopeExamples
CentralizedvSmart controllerNetwork-wideControl policy (route manipulation), data policy (traffic steering)
LocalizedWAN edge devicePer-deviceAccess lists, QoS, routing protocols
The exam focuses on centralized policies because they’re managed via the vManage API.

Get All Policies

# ── List all centralized policies ─────────────────────────────
response = session.get(
    f"{BASE_URL}/dataservice/template/policy/vsmart",
    verify=False
)
policies = response.json()["data"]
 
for policy in policies:
    print(f"{policy['policyName']:<30} "
          f"Active: {policy.get('isPolicyActivated', False):<6} "
          f"ID: {policy['policyId']}")

Get Policy Building Blocks

# ── List policy definitions (building blocks) ────────────────
 
# VPN Lists
vpn_lists = session.get(
    f"{BASE_URL}/dataservice/template/policy/list/vpn",
    verify=False
).json()["data"]
 
# Site Lists
site_lists = session.get(
    f"{BASE_URL}/dataservice/template/policy/list/site",
    verify=False
).json()["data"]
 
# SLA Classes
sla_classes = session.get(
    f"{BASE_URL}/dataservice/template/policy/list/sla",
    verify=False
).json()["data"]
 
# Application-Aware Routing Policies
app_route_policies = session.get(
    f"{BASE_URL}/dataservice/template/policy/definition/approute",
    verify=False
).json()["data"]
 
print(f"VPN Lists: {len(vpn_lists)}")
print(f"Site Lists: {len(site_lists)}")
print(f"SLA Classes: {len(sla_classes)}")
print(f"App-Route Policies: {len(app_route_policies)}")

Create a VPN List

# ── Create a VPN list (policy building block) ─────────────────
vpn_list_payload = {
    "name": "Branch-Service-VPNs",
    "description": "VPNs for branch office services",
    "type": "vpn",
    "entries": [
        {"vpn": "10"},
        {"vpn": "20"},
        {"vpn": "30"}
    ]
}
 
response = session.post(
    f"{BASE_URL}/dataservice/template/policy/list/vpn",
    json=vpn_list_payload,
    verify=False
)
response.raise_for_status()
vpn_list_id = response.json()["listId"]
print(f"VPN list created: {vpn_list_id}")

Create a Site List

# ── Create a site list ────────────────────────────────────────
site_list_payload = {
    "name": "Branch-Sites",
    "description": "All branch office sites",
    "type": "site",
    "entries": [
        {"siteId": "100"},
        {"siteId": "200"},
        {"siteId": "300-399"}    # Range syntax
    ]
}
 
response = session.post(
    f"{BASE_URL}/dataservice/template/policy/list/site",
    json=site_list_payload,
    verify=False
)
site_list_id = response.json()["listId"]
print(f"Site list created: {site_list_id}")

Activate a Centralized Policy

# ── Activate a policy (push to vSmarts and edges) ─────────────
policy_id = "<policy-id>"
 
response = session.post(
    f"{BASE_URL}/dataservice/template/policy/vsmart/activate/{policy_id}",
    json={},
    verify=False
)
response.raise_for_status()
action_id = response.json()["id"]
print(f"Policy activation initiated: {action_id}")
 
# Monitor activation — same async pattern as template attach
result = wait_for_action(session, BASE_URL, action_id)

Deactivate a Policy

# ── Deactivate a centralized policy ───────────────────────────
response = session.post(
    f"{BASE_URL}/dataservice/template/policy/vsmart/deactivate/{policy_id}",
    json={},
    verify=False
)
action_id = response.json()["id"]
print(f"Policy deactivation initiated: {action_id}")

Activate vs Deactivate Are Both POST

Both activation and deactivation are POST requests with an empty JSON body {}. The exam may test this — neither is a PUT, DELETE, or GET.

Policy Activation Flow

Create lists (VPN, site, prefix) → Create policy definition →
Create vSmart policy (assembles definitions) → Activate policy →
vManage pushes to vSmart → vSmart enforces on fabric

Each step returns an ID you use in the next step. The final activation is the only async step that requires polling.


10 – Network Health Monitoring

Exam Topics: 4.4, 3.2

SD-WAN provides rich health APIs for WAN tunnels, control connections, and application-aware routing.

Device Health Summary

# ── Get device health summary ─────────────────────────────────
response = session.get(
    f"{BASE_URL}/dataservice/device/monitor",
    verify=False
)
devices = response.json()["data"]
 
for device in devices:
    print(f"{device.get('host-name', 'N/A'):<25} "
          f"Status: {device.get('status', 'N/A'):<10} "
          f"Reach: {device.get('reachability', 'N/A'):<12} "
          f"CPU: {device.get('cpu-load', 'N/A')}%")

BFD Sessions (Tunnel Health)

# ── Get BFD sessions (shows tunnel health between edges) ──────
system_ip = "10.10.1.1"
 
response = session.get(
    f"{BASE_URL}/dataservice/device/bfd/sessions",
    params={"deviceId": system_ip},
    verify=False
)
bfd_sessions = response.json()["data"]
 
for bfd in bfd_sessions:
    print(f"Peer: {bfd.get('system-ip', 'N/A'):<16} "
          f"State: {bfd.get('state', 'N/A'):<8} "
          f"Color: {bfd.get('local-color', 'N/A'):<15} "
          f"Loss: {bfd.get('loss-percentage', 0)}%")

BFD (Bidirectional Forwarding Detection) in SD-WAN

BFD runs inside every IPsec tunnel between SD-WAN edges. It measures:

  • Loss — packet loss percentage
  • Latency — round-trip time
  • Jitter — variation in latency

These metrics feed application-aware routing decisions. The exam tests whether you know that BFD provides the SLA measurements for app-route policies.

Control Connections

# ── Get control connections (device to controllers) ───────────
response = session.get(
    f"{BASE_URL}/dataservice/device/control/connections",
    params={"deviceId": system_ip},
    verify=False
)
connections = response.json()["data"]
 
for conn in connections:
    print(f"Peer: {conn.get('system-ip', 'N/A'):<16} "
          f"Type: {conn.get('peer-type', 'N/A'):<10} "
          f"State: {conn.get('state', 'N/A'):<8} "
          f"Protocol: {conn.get('protocol', 'N/A')}")

OMP (Overlay Management Protocol) Routes

# ── Get OMP routes (SD-WAN overlay routing) ───────────────────
response = session.get(
    f"{BASE_URL}/dataservice/device/omp/routes/received",
    params={"deviceId": system_ip},
    verify=False
)
omp_routes = response.json()["data"]
 
for route in omp_routes[:10]:
    print(f"Prefix: {route.get('prefix', 'N/A'):<20} "
          f"Origin: {route.get('from-peer', 'N/A'):<16} "
          f"Site: {route.get('site-id', 'N/A')}")

Application-Aware Routing Statistics

# ── Get app-route stats (SLA measurements per tunnel) ─────────
response = session.get(
    f"{BASE_URL}/dataservice/device/app-route/statistics",
    params={"deviceId": system_ip},
    verify=False
)
app_stats = response.json()["data"]
 
for stat in app_stats:
    print(f"Remote: {stat.get('remote-system-ip', 'N/A'):<16} "
          f"Color: {stat.get('local-color', 'N/A'):<15} "
          f"Latency: {stat.get('latency', 'N/A'):<8} "
          f"Loss: {stat.get('loss', 'N/A'):<8} "
          f"Jitter: {stat.get('jitter', 'N/A')}")

Alarms

# ── Get active alarms ─────────────────────────────────────────
response = session.get(
    f"{BASE_URL}/dataservice/alarms",
    params={"query": '{"query":{"condition":"AND","rules":[{"field":"active","type":"boolean","value":["true"]}]}}'},
    verify=False
)
alarms = response.json()["data"]
 
for alarm in alarms[:5]:
    print(f"[{alarm.get('severity', 'N/A')}] "
          f"{alarm.get('type', 'N/A')}: "
          f"{alarm.get('message', 'N/A')[:60]} "
          f"— {alarm.get('host-name', 'N/A')}")

Health Dashboard Script

def sdwan_health_dashboard(session, base_url):
    """Print a summary health dashboard for the SD-WAN fabric."""
 
    # Get all devices
    devices = session.get(
        f"{base_url}/dataservice/device",
        verify=False
    ).json()["data"]
 
    # Separate by type
    edges = [d for d in devices if d.get("personality") == "vedge"]
    controllers = [d for d in devices if d.get("personality") != "vedge"]
 
    print("=" * 70)
    print(f"{'DEVICE':<25} {'TYPE':<12} {'STATUS':<10} {'REACHABLE':<12}")
    print("=" * 70)
 
    for device in sorted(devices, key=lambda d: d.get("host-name", "")):
        print(f"{device.get('host-name', 'N/A'):<25} "
              f"{device.get('device-type', 'N/A'):<12} "
              f"{device.get('status', 'N/A'):<10} "
              f"{device.get('reachability', 'N/A'):<12}")
 
    # Summary
    reachable = sum(1 for d in devices if d.get("reachability") == "reachable")
    total = len(devices)
    print(f"\nSummary: {reachable}/{total} devices reachable")
    print(f"  Controllers: {len(controllers)}")
    print(f"  WAN Edges:   {len(edges)}")
 
    # Check alarms
    alarms = session.get(
        f"{base_url}/dataservice/alarms",
        verify=False
    ).json()["data"]
 
    critical = [a for a in alarms if a.get("severity") == "Critical"]
    if critical:
        print(f"\n  {len(critical)} CRITICAL alarm(s):")
        for a in critical[:5]:
            print(f"    - {a.get('type')}: {a.get('message', '')[:50]}")
 
sdwan_health_dashboard(session, BASE_URL)

11 – Software Image Management

Exam Topic: 4.3 – Construct a controller-based automation solution to manage device software versions

List Available Software Images

# ── Get software images in the vManage repository ─────────────
response = session.get(
    f"{BASE_URL}/dataservice/device/action/software/images",
    verify=False
)
images = response.json()["data"]
 
for img in images:
    print(f"{img.get('versionName', 'N/A'):<30} "
          f"Type: {img.get('versionType', 'N/A'):<10} "
          f"Platform: {img.get('platformFamily', 'N/A')}")

Upload a Software Image

# ── Upload a new software image to vManage ────────────────────
image_path = "/path/to/c8000v-universalk9.17.09.04a.SPA.bin"
 
with open(image_path, "rb") as f:
    response = session.post(
        f"{BASE_URL}/dataservice/device/action/software/package",
        files={"file": f},
        verify=False
    )
 
response.raise_for_status()
print(f"Image uploaded: {response.json()}")

Install (Upgrade) Software on a Device

# ── Initiate a software upgrade on WAN edge devices ───────────
upgrade_payload = {
    "action": "install",
    "input": {
        "vEdgeVPN": 0,
        "vSmartVPN": 0,
        "version": "17.09.04a",
        "reboot": True,
        "confirm": True
    },
    "devices": [
        {
            "deviceIP": "10.10.1.1",
            "deviceId": "C8K-aaaa-bbbb-cccc-dddd"
        }
    ],
    "deviceType": "vedge"
}
 
response = session.post(
    f"{BASE_URL}/dataservice/device/action/install",
    json=upgrade_payload,
    verify=False
)
response.raise_for_status()
action_id = response.json()["id"]
print(f"Software upgrade initiated: {action_id}")
 
# Monitor — same async pattern
result = wait_for_action(session, BASE_URL, action_id, timeout=600)

Get Device Software Version Inventory

# ── Check current software versions across all devices ────────
response = session.get(
    f"{BASE_URL}/dataservice/device",
    verify=False
)
devices = response.json()["data"]
 
print(f"{'DEVICE':<25} {'VERSION':<15} {'MODEL':<20}")
print("-" * 60)
for device in sorted(devices, key=lambda d: d.get("host-name", "")):
    print(f"{device.get('host-name', 'N/A'):<25} "
          f"{device.get('version', 'N/A'):<15} "
          f"{device.get('device-model', 'N/A'):<20}")

SD-WAN SWIM Workflow

  1. Upload image to vManage repository
  2. Install image on target devices (copies + sets boot var)
  3. Activate (reboot device with new image)

Steps 2–3 can be combined with "reboot": True in the install payload. Each step is async — poll the action status endpoint.


12 – Common Patterns and Gotchas

Pattern: Complete Workflow (Auth → Get → Template → Attach → Verify)

import requests
import time
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
 
BASE_URL = "https://sandboxsdwan.cisco.com"
 
# ── 1. Authenticate ──────────────────────────────────────────
session = requests.Session()
session.post(
    f"{BASE_URL}/j_security_check",
    data={"j_username": "devnetuser", "j_password": "Cisco123!"},
    verify=False
)
xsrf = session.get(f"{BASE_URL}/dataservice/client/token", verify=False).text
session.headers.update({
    "X-XSRF-TOKEN": xsrf,
    "Content-Type": "application/json"
})
 
# ── 2. Get device inventory ──────────────────────────────────
devices = session.get(
    f"{BASE_URL}/dataservice/device", verify=False
).json()["data"]
 
edges = [d for d in devices if d.get("personality") == "vedge"]
print(f"Found {len(edges)} WAN edge devices")
 
# ── 3. Get device templates ──────────────────────────────────
templates = session.get(
    f"{BASE_URL}/dataservice/template/device", verify=False
).json()["data"]
 
for t in templates:
    print(f"  {t['templateName']}: {t.get('devicesAttached', 0)} attached")
 
# ── 4. Check BFD tunnel health ───────────────────────────────
if edges:
    bfd = session.get(
        f"{BASE_URL}/dataservice/device/bfd/sessions",
        params={"deviceId": edges[0]["system-ip"]},
        verify=False
    ).json()["data"]
 
    up_tunnels = sum(1 for b in bfd if b.get("state") == "up")
    print(f"\nDevice {edges[0]['host-name']}: "
          f"{up_tunnels}/{len(bfd)} tunnels UP")
 
# ── 5. Check alarms ──────────────────────────────────────────
alarms = session.get(
    f"{BASE_URL}/dataservice/alarms", verify=False
).json()["data"]
print(f"\nActive alarms: {len(alarms)}")
 
# ── 6. Logout (invalidate session) ───────────────────────────
session.get(f"{BASE_URL}/logout", verify=False)
print("Session closed")

Gotchas for the Exam

Top SD-WAN Exam Pitfalls

1. Auth uses data={}, NOT json={} The /j_security_check endpoint expects application/x-www-form-urlencoded. Sending JSON silently fails with a 200 response but no valid session.

2. requests.Session() is mandatory — cookies must persist SD-WAN is the only Cisco controller where bare requests.get() / requests.post() won’t work. You must use a Session object to carry the JSESSIONID cookie.

3. Write operations need the X-XSRF-TOKEN header GET requests work with just the session cookie. POST/PUT/DELETE also need the XSRF token from /dataservice/client/token. Forgetting this causes 403 Forbidden.

4. API base path is /dataservice/... Not /api/v1/... (Meraki), not /dna/intent/... (Catalyst Center). All SD-WAN endpoints start with /dataservice/.

5. Response data is in ["data"], not ["response"] Catalyst Center wraps results in {"response": [...]}. SD-WAN uses {"data": [...]}.

6. Template operations are asynchronous Template attach/detach and policy activation return an action ID. You must poll /dataservice/device/action/status/{id} — same concept as Catalyst Center task polling, different endpoint.

7. vipType controls variable binding in feature templates "constant" = fixed, "variableName" = per-device, "ignore" = default. Getting this wrong in template creation is a common exam trap.

8. Login success returns 200 with EMPTY body Login failure also returns 200 but with an HTML body. Don’t check status_code == 200 to verify login — check the response content or cookie jar.


13 – Exam-Style Scenarios

Scenario 1: Authentication Failure (Topic 3.6)

You run the following code and all subsequent API calls return 401 Unauthorized. What is wrong?

import requests
BASE_URL = "https://vmanage.example.com"
 
response = requests.post(
    f"{BASE_URL}/j_security_check",
    data={"j_username": "admin", "j_password": "Cisco123!"},
    verify=False
)
 
devices = requests.get(
    f"{BASE_URL}/dataservice/device",
    verify=False
)

Scenario 2: Write Operation Forbidden (Topic 3.6)

Your script can read device data successfully, but when you try to create a feature template, you get 403 Forbidden. What's missing?

session = requests.Session()
session.post(f"{BASE_URL}/j_security_check", data=creds, verify=False)
 
session.post(
    f"{BASE_URL}/dataservice/template/feature",
    json=template_payload,
    verify=False
)   # 403 Forbidden!

Scenario 3: Silent Login Failure (Topic 3.6)

The following code completes without errors, but devices is empty and no exception is raised. What's wrong?

session = requests.Session()
response = session.post(
    f"{BASE_URL}/j_security_check",
    json={"j_username": "admin", "j_password": "Cisco123!"},
    verify=False
)
print(f"Status: {response.status_code}")   # prints "Status: 200"
 
devices = session.get(f"{BASE_URL}/dataservice/device", verify=False)
print(devices.json())  # empty or error

Scenario 4: Template Variable Types (Topic 3.3)

You create a VPN feature template where the VPN ID should always be 10, but the hostname should be different per device. Which vipType values should you use?

"vpn-id": {
    "vipType": "______",     # Always VPN 10
    "vipValue": 10
},
"host-name": {
    "vipType": "______",     # Different per device
    "vipValue": "hostname_var"
}

Scenario 5: Data Key Confusion (Topic 3.6)

You migrated a working Catalyst Center script to SD-WAN. The auth works, but this line throws KeyError: 'response':

devices = session.get(f"{BASE_URL}/dataservice/device", verify=False)
for d in devices.json()["response"]:
    print(d["hostname"])

Scenario 6: Policy Activation (Topic 3.5)

You've created a centralized traffic data policy with all the correct lists and definitions. You verify it exists in vManage. But traffic is still taking the old path. What step are you missing?


Scenario 7: Day-0 Bootstrap (Topic 3.1)

A new cEdge router powers on with a factory default configuration. It has IP connectivity to the internet. What minimum information must be in its bootstrap config to join the SD-WAN fabric?


Scenario 8: SDK Authentication (Topic 3.2)

Fill in the blanks to authenticate using the Catalyst WAN SDK:

from catalystwan.session import ____________
 
session = ____________(
    url="vmanage.example.com",
    username="admin",
    password="Cisco123!"
)

Scenario 9: BFD and App-Aware Routing (Topic 4.4)

Your monitoring script shows BFD sessions between two sites with these metrics:

  • Tunnel 1 (MPLS): loss=0%, latency=20ms, jitter=2ms
  • Tunnel 2 (Internet): loss=2%, latency=80ms, jitter=15ms

You have an SLA class requiring loss < 1% and latency < 50ms. Which tunnel will app-aware routing select?


14 – Quick Reference Cheat Sheet

Authentication Flow

POST /j_security_check
├── Body: j_username=admin&j_password=pass (form-encoded!)
├── Response: JSESSIONID cookie (empty body on success)
└── Then: GET /dataservice/client/token → X-XSRF-TOKEN header

Requires: requests.Session() to persist cookies

CRUD Operations Map

OperationHTTP MethodEndpointReturns
LoginPOST/j_security_checkJSESSIONID cookie
Get XSRF tokenGET/dataservice/client/tokenToken string
List devicesGET/dataservice/deviceDevice list
Device controllersGET/dataservice/device/controllersController list
Device interfacesGET/dataservice/device/interface?deviceId=Interface list
Feature templatesGET/dataservice/template/featureTemplate list
Create feature tmplPOST/dataservice/template/featureTemplate ID
Device templatesGET/dataservice/template/deviceTemplate list
Create device tmplPOST/dataservice/template/device/featureTemplate ID
Template inputsGET/dataservice/template/device/config/input?templateId=Variable schema
Attach templatePOST/dataservice/template/device/config/attachfeatureAction ID
Detach templatePOST/dataservice/template/config/device/mode/cliAction ID
Action statusGET/dataservice/device/action/status/{id}Status object
List policiesGET/dataservice/template/policy/vsmartPolicy list
Activate policyPOST/dataservice/template/policy/vsmart/activate/{id}Action ID
Deactivate policyPOST/dataservice/template/policy/vsmart/deactivate/{id}Action ID
VPN listsGET/dataservice/template/policy/list/vpnList entries
Site listsGET/dataservice/template/policy/list/siteList entries
BFD sessionsGET/dataservice/device/bfd/sessions?deviceId=BFD data
Control connectionsGET/dataservice/device/control/connections?deviceId=Connection list
OMP routesGET/dataservice/device/omp/routes/received?deviceId=Route table
App-route statsGET/dataservice/device/app-route/statistics?deviceId=SLA metrics
AlarmsGET/dataservice/alarmsAlarm list
Software imagesGET/dataservice/device/action/software/imagesImage list
Install softwarePOST/dataservice/device/action/installAction ID
LogoutGET/logoutSession invalidated

SDK Quick Reference

from catalystwan.session import create_manager_session
 
session = create_manager_session(url=url, username=u, password=p)
 
# Device management
session.api.devices.get()                    # All devices
session.api.devices.controllers()            # Controllers only
 
# Templates
session.api.templates.get_feature_templates()
session.api.templates.get_device_templates()
 
# Always close when done
session.close()

Compare: Auth Methods Across All Four Controllers

ControllerAuth MechanismHeader/CookieWrite Requirement
Catalyst CenterPOST Basic → TokenX-Auth-Token: <token>Token only
SD-WANPOST form → CookieCookie: JSESSIONID=<id>Cookie + X-XSRF-TOKEN
MerakiAPI Key (no exchange)X-Cisco-Meraki-API-Key: <key>Key only
ISEBasic Auth (per request)Authorization: Basic <b64>Same Basic header

Memory Aid — Auth Comparison

Catalyst = Custom Token header (X-Auth-Token) SD-WAN = Session cookie + XSRF token (two-step!) Meraki = API Key header (simplest) ISE = Basic auth every time (no session)

Compare: Write Operations Across Controllers

ControllerWrite BehaviorHow to Track
Catalyst CenterAsynchronous → taskIdPoll /dna/intent/api/v1/task/{taskId}
SD-WANAsynchronous → action idPoll /dataservice/device/action/status/{id}
MerakiSynchronous (immediate)Response = result
ISESynchronous (immediate)Response = result

Compare: Data Wrapper in API Responses

ControllerResponse KeyExample
Catalyst Centerresponse.json()["response"]{"response": [...], "version": "1.0"}
SD-WANresponse.json()["data"]{"data": [...]}
Merakiresponse.json() (no wrapper)[...] directly
ISEresponse.json()["SearchResult"]Varies by endpoint

Further Study


enauto ccnp sdwan vmanage python api controller-based day0 templates policies health-monitoring

See Also