Test your knowledge on this topic in the ENAUTO Exam Trainer — 186 questions across 5 interactive modes.
Device-Level Network Automation – Deep Dive for ENAUTO v2.0
Exam Relevance
Topic 2.0 Device-Level Network Automation (25%) is the second-highest weighted domain.
This guide covers direct device interaction — SSH-based automation (Netmiko), model-driven programmability (NETCONF/RESTCONF), Ansible device modules, Day-0 provisioning, and on-box automation (EEM, Guest Shell). Every code example maps to a specific exam objective.
Exam Topics Covered:
2.1 – Netmiko for configuration management
2.2 – ncclient for NETCONF operations
2.3 – RESTCONF with Python requests
2.4 – Ansible for device-level automation
2.5 – Day-0 provisioning (device-level)
2.6 – Troubleshoot RESTCONF, NETCONF, YANG
2.7 – On-box automation (EEM, guest shell, on-box Python)
Netmiko is the go-to library for SSH-based device automation. Understand the abstraction layers, the key methods, and how TextFSM provides structured output.
TextFSM templates are matched automatically by device_type + command.
Full Working Example
from netmiko import ConnectHandler# ── DevNet IOS XE Always-On Sandbox ─────────────────────────────device = { "device_type": "cisco_ios", "host": "sandbox-iosxe-latest-1.cisco.com", "username": "admin", "password": "C1sco12345",}# ── Connect ─────────────────────────────────────────────────────net_connect = ConnectHandler(**device)print(f"Connected to: {net_connect.find_prompt()}")# ── Show command ────────────────────────────────────────────────version_output = net_connect.send_command("show version")print(version_output[:200]) # First 200 chars# ── Structured show command (TextFSM) ──────────────────────────interfaces = net_connect.send_command( "show ip interface brief", use_textfsm=True)for intf in interfaces: print(f" {intf['interface']:30s} {intf['ip_address']:15s} {intf['status']}")# ── Configure a loopback interface ──────────────────────────────config_commands = [ "interface Loopback99", "description Configured by Netmiko", "ip address 10.99.99.1 255.255.255.0", "no shutdown",]output = net_connect.send_config_set(config_commands)print(output)# ── Save configuration ─────────────────────────────────────────net_connect.save_config()# ── Verify ──────────────────────────────────────────────────────verify = net_connect.send_command("show ip interface brief | include Loopback99")print(f"Verification: {verify}")# ── Disconnect ──────────────────────────────────────────────────net_connect.disconnect()print("Session closed.")
send_command() Advanced Parameters
# Wait for a non-standard prompt (e.g., confirmation dialog)output = net_connect.send_command( "reload", expect_string=r"confirm", # regex pattern to wait for)# Increase delay_factor for slow devicesoutput = net_connect.send_command( "show tech-support", delay_factor=4, # multiplier for all internal delays read_timeout=120, # max seconds to wait for output)
2 – ncclient for NETCONF Operations
Exam Topic: 2.2
NETCONF is a model-driven, XML-based protocol. ncclient is the Python library for NETCONF operations. Know the protocol stack, XML filters, and edit_config operations.
After connecting, check which YANG models the device supports:
for capability in m.server_capabilities: if "ietf-interfaces" in capability: print(capability)# Output: urn:ietf:params:xml:ns:yang:ietf-interfaces?module=ietf-interfaces&revision=2014-05-08
# Lock prevents other NETCONF sessions from making changesm.lock(target="running")try: m.edit_config(target="running", config=config_payload)finally: m.unlock(target="running")
commit() — For Candidate Datastore Devices
# Some devices (IOS XR, Junos) use a candidate datastore# IOS XE typically operates on the running datastore directly# Workflow for candidate datastore:m.lock(target="candidate")m.edit_config(target="candidate", config=config_payload)m.commit() # Apply candidate → runningm.unlock(target="candidate")
RESTCONF has its own media types. Using plain application/json may work on some platforms but is technically incorrect and may cause 415 Unsupported Media Type errors.
# ── Get hostname using Cisco native YANG model ─────────────────response = requests.get( f"{BASE_URL}/Cisco-IOS-XE-native:native/hostname", auth=AUTH, headers=HEADERS, verify=False,)print(response.json()) # {"Cisco-IOS-XE-native:hostname": "csr1000v"}
Ansible uses SSH (network_cli connection plugin) for device-level automation. Know the key modules, the difference between imperative and declarative approaches, and the inventory format.
Connection Plugin: network_cli
Unlike controller-based Ansible collections (which use httpapi), device-level automation uses network_cli — a persistent SSH connection to the device CLI.
- name: Run show commands hosts: iosxe gather_facts: false tasks: - name: Get show version cisco.ios.ios_command: commands: - show version - show ip interface brief register: output - name: Display output ansible.builtin.debug: msg: "{{ output.stdout_lines }}"
ios_config — Push Configuration
- name: Configure device hosts: iosxe gather_facts: false tasks: - name: Configure loopback interface cisco.ios.ios_config: lines: - description Configured by Ansible - ip address 10.99.99.1 255.255.255.0 - no shutdown parents: interface Loopback99 save_when: modified # save only if changes were made - name: Configure NTP cisco.ios.ios_config: lines: - ntp server 10.0.0.1
ios_config Key Parameters
Parameter
Purpose
lines
List of config commands to push
parents
Parent command(s) to enter before lines (e.g., interface Gi1)
save_when
always, modified, changed, never — when to save config
before
Commands to run before lines
after
Commands to run after lines
backup
yes — backup running config before changes
match
line, strict, exact, none — how to compare existing config
iPXE is used for automated OS installation on bare-metal or virtual network devices.
Device power-on → iPXE firmware → DHCP (option 67 = iPXE script URL)
→ Download boot image → Install OS → Boot into provisioned state
iPXE is primarily relevant for virtualized environments (CSR1000v, Catalyst 8000v) and data center provisioning. ZTP and PnP are more commonly tested.
6 – Troubleshooting NETCONF, RESTCONF, and YANG
Exam Topic: 2.6
Expect exam questions that present error messages or logs and ask you to identify the root cause. Know the common errors for both NETCONF and RESTCONF.
Common NETCONF Errors
Error
Cause
Fix
Connection refused
SSH not enabled, wrong port, netconf-yang not configured
Enable with netconf-yang on device, verify port 830
Capability mismatch
YANG model not supported on device/version
Check m.server_capabilities, upgrade IOS XE
lock-denied
Another session holds the lock
Close other NETCONF sessions, or use <kill-session>
data-missing
Trying to delete data that does not exist
Verify target data exists first with get_config
invalid-value
Wrong data type or value in XML payload
Check YANG model constraints (range, enum, pattern)
malformed-message
Invalid XML syntax
Validate XML before sending (close all tags, correct namespaces)
access-denied
Insufficient privileges
Use admin-level credentials
Timeout
Device too slow or large response
Increase timeout in manager.connect(timeout=60)
NETCONF Error Response Example
<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> <rpc-error> <error-type>application</error-type> <error-tag>invalid-value</error-tag> <error-severity>error</error-severity> <error-message> "Loopback" is not a valid interface type string </error-message> </rpc-error></rpc-reply>
Common RESTCONF Errors
HTTP Status
Meaning
Common Cause
400 Bad Request
Invalid request
Wrong YANG path, malformed JSON, invalid value
401 Unauthorized
Authentication failed
Wrong credentials, auth not configured
403 Forbidden
Authorized but not permitted
User lacks privilege for this operation
404 Not Found
Resource does not exist
Wrong URL, module not enabled (restconf not configured)
405 Method Not Allowed
HTTP method not valid for resource
POST to existing resource, DELETE on non-deletable
409 Conflict
Resource already exists
POST when resource exists — use PUT or PATCH instead
415 Unsupported Media Type
Wrong Content-Type
Use application/yang-data+json not application/json
404 vs 409 — Exam Trap
404: The URL path itself is wrong (typo in YANG module, wrong container name).
409: The URL is correct but the operation conflicts (e.g., POST an interface that already exists).
Fix for 409: Change POST to PUT (replace) or PATCH (merge).
YANG Model Troubleshooting
Using pyang to Visualize YANG Models
# Install pyangpip install pyang# Download YANG model# From: https://github.com/YangModels/yang# Generate tree viewpyang -f tree ietf-interfaces.yang# Output:# module: ietf-interfaces# +--rw interfaces# +--rw interface* [name]# +--rw name string# +--rw description? string# +--rw type identityref# +--rw enabled? boolean
Checking Capabilities at Runtime
from ncclient import managerm = manager.connect( host="sandbox-iosxe-latest-1.cisco.com", port=830, username="admin", password="C1sco12345", hostkey_verify=False,)# Check if a specific YANG model is supportedfor cap in m.server_capabilities: if "openconfig" in cap: print(cap)# Check NETCONF base versionfor cap in m.server_capabilities: if "base" in cap: print(cap)# urn:ietf:params:netconf:base:1.0# urn:ietf:params:netconf:base:1.1
YANG Suite
YANG Suite
YANG Suite is a Cisco tool for exploring YANG models visually. It helps:
Browse YANG model trees interactively
Generate NETCONF/RESTCONF payloads from models
Test API calls directly
Validate XML/JSON payloads against YANG constraints
Install: pip install yangsuite or use the Docker container.
Debugging Tips
# ── NETCONF: Enable verbose logging ────────────────────────────import logginglogging.basicConfig(level=logging.DEBUG)# This will show the raw XML RPC exchange# ── RESTCONF: Inspect full response ────────────────────────────response = requests.get(url, auth=AUTH, headers=HEADERS, verify=False)print(f"Status: {response.status_code}")print(f"Headers: {dict(response.headers)}")print(f"Body: {response.text}")# ── On device: Enable NETCONF/RESTCONF debugging ───────────────# debug netconf all# debug restconf all# terminal monitor
IOS XE Prerequisite Commands
If NETCONF/RESTCONF is not working, verify these are configured on the device:
netconf-yang
restconf
ip http server
ip http secure-server
ip http authentication local
On-box automation runs directly on the network device rather than from an external script. Know EEM applets, Guest Shell, and the on-box Python cli module.
EEM (Embedded Event Manager)
EEM is IOS/IOS XE’s built-in event-driven automation framework. It detects events and executes actions — no external tools required.
# Enter guest shell from IOS XE CLIDevice# guestshell# You are now in a Linux shell[guestshell@guestshell ~]$ python3 --versionPython 3.6.8# Install packages[guestshell@guestshell ~]$ pip3 install requests# Run a script[guestshell@guestshell ~]$ python3 /home/guestshell/backup.py# Run from IOS XE CLI directly (without entering guest shell)Device# guestshell run python3 /home/guestshell/backup.py
Guest Shell Python: Accessing IOS CLI
#!/usr/bin/env python3"""Guest Shell script: backup running config to TFTP server.Uses the cli module available only inside Guest Shell."""import cliimport time# ── Get hostname ────────────────────────────────────────────────hostname = cli.execute("show run | include hostname").split()[-1]# ── Generate timestamped filename ───────────────────────────────timestamp = time.strftime("%Y%m%d_%H%M%S")filename = f"{hostname}_{timestamp}.cfg"# ── Copy running config to TFTP ────────────────────────────────cli.execute(f"copy running-config tftp://10.0.0.100/{filename}")print(f"Backup saved: {filename}")
cli Module Methods
Method
Behavior
cli.cli("show version")
Returns output as a string (includes command echo)
cli.execute("show version")
Returns output as a string (cleaner, no echo)
cli.configure(["hostname R1", "int Lo0"])
Enters config mode, runs commands, exits
cli.configurep(["hostname R1"])
Same as configure but prints output
Exam trap:cli.cli() includes the command echo in output; cli.execute() does not.
On-Box Python (Direct Interpreter)
IOS XE 16.x+ includes a Python 3 interpreter accessible from the CLI without Guest Shell.
A) The reload command requires enable mode
B) The device disconnects during reload, breaking the SSH session
C) send_command() is waiting for the device prompt, but reload shows a confirmation dialog
D) Netmiko does not support the reload command
Answer
C) send_command() is waiting for the device prompt, but reload shows a confirmation dialog.
send_command() waits for the router prompt (e.g., Router#) to appear. The reload command shows Proceed with reload? [confirm] instead. The fix is to use expect_string:
An engineer tries to edit the running config via NETCONF but receives a lock-denied error. What should they check?
A) The device does not support NETCONF
B) Another NETCONF session holds the lock on the running datastore
C) The YANG model is not installed on the device
D) The XML payload is malformed
Answer
B) Another NETCONF session holds the lock on the running datastore.
Only one session can hold a lock at a time. Use <kill-session> to terminate the other session, or wait for it to release the lock. Check active sessions on IOS XE with show netconf-yang sessions.
Scenario 3 — RESTCONF (Exam Topic: 2.3)
A Python script sends a POST request to create a loopback interface but receives a 409 Conflict response. The interface already exists. Which HTTP method should be used instead to update it?
A) GET
B) PUT
C) PATCH
D) DELETE
Answer
C) PATCH — PATCH merges changes into the existing resource without requiring the full resource definition. PUT would also work but requires the complete resource representation. PATCH is the safest choice for updating existing config.
Scenario 4 — Ansible (Exam Topic: 2.4)
An Ansible playbook uses cisco.ios.ios_interfaces with state: overridden and specifies only Loopback0 in the config. After running the playbook, all other interfaces lose their descriptions and are set to defaults. Why?
A) The overridden state has a bug
B) The overridden state replaces config for ALL interfaces, resetting unspecified ones to defaults
C) The playbook did not include gather_facts: true
D) The network_cli connection does not support declarative modules
Answer
B) The overridden state replaces config for ALL interfaces, resetting unspecified ones to defaults.
overridden enforces the desired state across the entire resource scope (all interfaces). To only update specific interfaces, use state: replaced (per-interface replacement) or state: merged (additive only).
Scenario 5 — ZTP (Exam Topic: 2.5)
A new IOS XE switch boots with no configuration. The DHCP server is configured with Option 67 pointing to http://10.0.0.100/ztp.py. The switch downloads the script but fails to execute it. Which is the most likely cause?
A) ZTP requires DHCP Option 43, not Option 67
B) The script uses import cli but cli is not available outside Guest Shell
C) The script has a syntax error or the file is not valid Python 3
D) ZTP only supports TFTP, not HTTP
Answer
C) The script has a syntax error or the file is not valid Python 3.
Option 67 is correct for ZTP. The cli module is available in the ZTP execution environment (which runs inside Guest Shell). HTTP is supported. The most common cause of execution failure is a Python syntax error in the script. Always test ZTP scripts locally before deployment.
Scenario 6 — EEM and Guest Shell (Exam Topic: 2.7)
An engineer wants to run a Python script on an IOS XE device every time a syslog message containing “DUPLEX_MISMATCH” appears. Which combination of technologies achieves this?
A) EEM applet with event syslog trigger and action cli command "guestshell run python3 /home/guestshell/fix_duplex.py"
B) Netmiko script polling the device log every 60 seconds
C) RESTCONF webhook subscription for syslog events
D) Ansible playbook with ios_command running show logging
Answer
A) EEM applet with event syslog trigger and action cli command "guestshell run python3 ..." .
EEM is the correct on-box event-driven framework. The event syslog pattern "DUPLEX_MISMATCH" detector triggers immediately when the message appears, and the action runs the Python script in Guest Shell. The other options are external and poll-based, not event-driven.