18.1 Python Fundamentals for Network Engineers & Cisco Libraries
Key Takeaways
- Python core data types (int, float, str, bool) and collections (list, dict, tuple) form the data manipulation bedrock of network automation, where dictionaries and lists map directly to serialized JSON objects and arrays.
- Data parsing functions transform structured data across formats: json.loads() and json.dumps() convert between JSON strings and Python dictionaries, while yaml.safe_load() and yaml.dump() securely parse and emit human-readable YAML documents.
- The requests library delivers synchronous HTTP/REST API capabilities, managing authentication, headers, JSON body serialization, SSL verification (verify=False), and response status code validation.
- Netmiko provides multi-vendor SSH CLI automation via ConnectHandler, executing operational inspection with send_command() and configuration transactional pushes with send_config_set() and save_config().
- The ncclient library establishes secure NETCONF sessions over SSH (port 830) using manager.connect(), executing XML Remote Procedure Calls (RPCs) such as get_config() and edit_config() against structured YANG datastores.
18.1 Python Fundamentals for Network Engineers & Cisco Libraries
Core Blueprint Focus: Cisco 350-401 ENCOR v1.2 topics 6.1 (interpret basic Python components and scripts) and 6.2 (construct valid JSON-encoded files), in the domain v1.2 renamed "Automation and Artificial Intelligence", test a candidate's ability to construct, interpret, and troubleshoot Python automation scripts interacting with Cisco enterprise infrastructure. Mastery requires proficiency in native Python data structures (lists, dictionaries, tuples), serialization formats (JSON and YAML), and the three dominant automation libraries:
requestsfor RESTful API controllers,netmikofor multi-vendor SSH CLI interactions, andncclientfor NETCONF XML RPC transactions.
Modern network engineering requires shifting from manual CLI configuration to scalable, programmatic infrastructure management. Python has emerged as the industry-standard language for network automation due to its expressive syntax, extensive standard library, and robust ecosystem of networking packages.
+---------------------------------------------------------------------------------------------------+
| PYTHON NETWORK AUTOMATION ARCHITECTURE |
+---------------------------------------------------------------------------------------------------+
| |
| +---------------------------------------------------------------------------------------------+ |
| | PYTHON AUTOMATION SCRIPT | |
| | - Primitive Types: int, float, str, bool - Data Collections: list, dict, tuple | |
| | - Serialization Parsers: json (loads/dumps) - YAML Parsers: PyYAML (safe_load/dump) | |
| +---------------------------------------------------------------------------------------------+ |
| | | | |
| v v v |
| +-------------------+ +----------------------+ +--------------------+ |
| | REQUESTS (HTTP) | | NETMIKO (SSH) | | NCCLIENT (NETCONF)| |
| | - HTTPS Port 443 | | - SSH Port 22 | | - SSH Port 830 | |
| | - JSON Payloads | | - CLI Text Engine | | - XML / YANG RPCs | |
| | - RESTful APIs | | - send_command() | | - edit-config | |
| | - Webhooks / SSO | | - send_config_set | | - get-config | |
| +-------------------+ +----------------------+ +--------------------+ |
| | | | |
| v v v |
| +-------------------+ +----------------------+ +--------------------+ |
| | Cisco Catalyst | | Cisco IOS-XE / NX-OS | | Cisco Catalyst | |
| | Center / vManage | | Switches & Routers | | 9000 Series (YANG) | |
| +-------------------+ +----------------------+ +--------------------+ |
+---------------------------------------------------------------------------------------------------+
1. Python Data Types & Core Data Structures
Network automation scripts process configuration templates, interface states, routing tables, and API responses. Understanding how Python handles primitive types and complex collections is fundamental to writing reliable automation.
Primitive Data Types
| Data Type | Python Keyword | Description | Example Network Value |
|---|---|---|---|
| Integer | int | Whole numerical values (no decimal points) | vlan_id = 100, bgp_as = 65001 |
| Float | float | Decimal numerical values | cpu_util = 42.8, snr_db = 28.5 |
| String | str | Textual data enclosed in quotes | hostname = "core-sw01", ip = "10.1.1.1" |
| Boolean | bool | Logical truth values (True or False) | is_up = True, spanning_tree_enabled = False |
Collections: Lists, Dictionaries, and Tuples
# 1. LIST: Ordered, mutable, indexed by integer sequence, allows duplicates
interfaces = ["GigabitEthernet1/0/1", "GigabitEthernet1/0/2", "TenGigabitEthernet1/0/1"]
interfaces.append("Loopback0") # Append element to end
primary_uplink = interfaces[0] # Zero-based indexing -> "GigabitEthernet1/0/1"
sub_interfaces = interfaces[1:3] # Slicing -> ["GigabitEthernet1/0/2", "TenGigabitEthernet1/0/1"]
# 2. DICTIONARY: Key-value mapping, unordered (Python 3.7+ preserves insertion order), mutable
device = {
"hostname": "dist-rtr-01",
"ip_address": "192.168.10.1",
"vendor": "cisco",
"os": "iosxe",
"vlans": [10, 20, 30, 99]
}
hostname = device["hostname"] # Direct key lookup -> "dist-rtr-01"
management_ip = device.get("mgmt_ip", "10.0.0.1") # Safe lookup with default fallback
device["os_version"] = "17.9.4a" # Add or update key-value pair
# 3. TUPLE: Ordered, immutable sequence, fixed footprint, often used for constants
socket_binding = ("192.168.1.10", 830) # IP address and TCP port pair
# socket_binding[1] = 22 # ERROR: Tuples cannot be modified after instantiation
+---------------------------------------------------------------------------------------------------+
| DATA STRUCTURE COMPARISON MATRIX |
+---------------------------------------------------------------------------------------------------+
| Structure | Syntax Literal | Ordered? | Mutable? | Key Lookup / Access | Primary Network Use |
| :--------- | :-------------- | :------- | :------- | :------------------ | :------------------------- |
| **List** | `[elem1, elem2]`| Yes | Yes | Integer Index `[0]` | Interface lists, IP pools |
| **Dict** | `{"k": "v"}` | Yes (3.7)| Yes | Key Name `["k"]` | Device configs, JSON payloads|
| **Tuple** | `(elem1, elem2)`| Yes | No | Integer Index `[0]` | Static (host, port) pairs |
+---------------------------------------------------------------------------------------------------+
2. Serialization Formats: JSON & YAML Parsing
Modern APIs, controllers (Catalyst Center, vManage), and configuration management tools exchange data using serialized formats. Python provides built-in and third-party modules to serialize (convert Python objects to text) and deserialize (parse text into Python objects).
JSON Parsing with the json Module
JSON (JavaScript Object Notation) is the dominant data format for RESTful APIs. It maps directly to Python primitives:
- JSON Objects $\leftrightarrow$ Python Dictionaries
- JSON Arrays $\leftrightarrow$ Python Lists
- JSON Strings $\leftrightarrow$ Python Strings
- JSON Numbers $\leftrightarrow$ Python Integers / Floats
- JSON Booleans (
true/false) $\leftrightarrow$ Python Booleans (True/False) - JSON
null$\leftrightarrow$ PythonNone
import json
# Raw JSON string received from a REST API
api_response = '{"response": [{"hostname": "sw-access-01", "managementIp": "10.10.20.1", "reachabilityStatus": "Reachable"}]}'
# DESERIALIZATION: Parse JSON string into Python dict (json.loads -> Load String)
pdata = json.loads(api_response)
first_device = pdata["response"][0]
print(f"Device {first_device['hostname']} is {first_device['reachabilityStatus']}")
# Output: Device sw-access-01 is Reachable
# SERIALIZATION: Convert Python dictionary into formatted JSON string (json.dumps -> Dump String)
payload = {
"interface": "GigabitEthernet1/0/24",
"description": "Uplink to Core",
"enabled": True,
"speed": 1000
}
json_string = json.dumps(payload, indent=2, sort_keys=True)
print(json_string)
# Output:
# {
# "description": "Uplink to Core",
# "enabled": true,
# "interface": "GigabitEthernet1/0/24",
# "speed": 1000
# }
YAML Parsing with PyYAML (yaml.safe_load)
YAML (YAML Ain't Markup Language) is human-readable, relying on indentation rather than brackets. It is widely used in Ansible playbooks, CI/CD pipelines, and configuration modeling.
[!IMPORTANT] Always use
yaml.safe_load()instead ofyaml.load(). Callingyaml.load()without specifying a SafeLoader can execute arbitrary code embedded inside untrusted YAML strings, creating a critical remote code execution (RCE) vulnerability.
import yaml
yaml_document = """
---
device:
hostname: core-rtr-01
bgp_as: 65100
peers:
- neighbor: 10.255.255.2
remote_as: 65200
description: Transit ISP-A
- neighbor: 10.255.255.6
remote_as: 65300
description: Transit ISP-B
"""
# Securely parse YAML string into Python dictionary
config_dict = yaml.safe_load(yaml_document)
print(f"BGP AS: {config_dict['device']['bgp_as']}")
for peer in config_dict['device']['peers']:
print(f" Peer {peer['neighbor']} (AS {peer['remote_as']}) -> {peer['description']}")
# Export Python dictionary back to YAML string
yaml_output = yaml.dump(config_dict, default_flow_style=False)
3. Cisco Automation Library 1: requests (REST APIs)
The requests package is the standard Python library for interacting with HTTP-based REST APIs exposed by network controllers like Cisco Catalyst Center, Cisco SD-WAN vManage, and Cisco Meraki.
Core Methods and Request Components
- HTTP Methods:
requests.get(),requests.post(),requests.put(),requests.patch(),requests.delete() - Authentication:
auth=('admin', 'password')for HTTP Basic Auth - Headers:
headers={'Content-Type': 'application/json', 'Accept': 'application/json'} - Payload Serialization:
json=payload_dictautomatically serializes dictionaries to JSON and sets theContent-Type: application/jsonheader - SSL Verification:
verify=Falseignores untrusted/self-signed SSL certificates (disables TLS validation) - Response Handling:
response.status_code(e.g., 200, 201),response.json()(deserializes JSON response directly to Python dict),response.text(raw text body)
import requests
import urllib3
# Suppress insecure HTTPS request warnings when verify=False
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
url = "https://catalyst-center.enterprise.local/dna/intent/api/v1/network-device"
headers = {
"x-auth-token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"Accept": "application/json"
}
params = {"managementIpAddress": "10.1.100.1"}
try:
response = requests.get(url, headers=headers, params=params, verify=False, timeout=10)
response.raise_for_status() # Raises HTTPError for 4xx or 5xx status codes
device_data = response.json()
print(f"HTTP Status: {response.status_code}")
print(f"Device Family: {device_data['response'][0]['family']}")
print(f"Software Version: {device_data['response'][0]['softwareVersion']}")
except requests.exceptions.HTTPError as err:
print(f"HTTP Error encountered: {err}")
except requests.exceptions.ConnectionError:
print("Failed to connect to Catalyst Center. Verify IP and routing reachability.")
4. Cisco Automation Library 2: netmiko (SSH CLI Automation)
netmiko (developed by Kirk Byers) is a multi-vendor library built on top of Paramiko. It abstracts low-level SSH handling, terminal screen scraping, prompt detection, and privilege elevation across Cisco IOS-XE, NX-OS, IOS-XR, and third-party operating systems.
Essential netmiko Functions
ConnectHandler(**device_dict): Establishes an encrypted SSHv2 connection to the network device.send_command(command_string): Executes an operationalshowcommand, waits for the CLI prompt to return, and returns the output as a string.send_config_set(config_commands): Enters configuration mode (config t), sends a list or tuple of configuration commands, exits config mode, and returns the output.save_config(): Writes the running configuration to non-volatile memory (write memoryorcopy running-config startup-config).disconnect(): Gracefully closes the SSH session.
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoTimeoutException, NetmikoAuthenticationException
cisco_switch = {
"device_type": "cisco_ios", # Specifies device OS driver
"host": "192.168.1.50",
"username": "admin",
"password": "C1sco123!",
"secret": "EnableSecret456!", # Enable password for privilege escalation
"port": 22,
"timeout": 10,
}
try:
print(f"Connecting to {cisco_switch['host']}...")
net_connect = ConnectHandler(**cisco_switch)
net_connect.enable() # Enters privileged EXEC mode (# prompt)
# 1. Operational Command Execution
version_output = net_connect.send_command("show version | include uptime")
print(f"Uptime info:\n{version_output}")
# 2. Transactional Configuration Push
config_payload = [
"interface Loopback100",
"description Automation Management Interface",
"ip address 10.100.100.1 255.255.255.255",
"no shutdown"
]
config_result = net_connect.send_config_set(config_payload)
print(f"Configuration Result:\n{config_result}")
# 3. Save Running Configuration to NVRAM
save_result = net_connect.save_config()
print(f"Save status: {save_result}")
net_connect.disconnect()
except NetmikoTimeoutException:
print(f"Connection timed out to device {cisco_switch['host']}.")
except NetmikoAuthenticationException:
print(f"Authentication failed for user {cisco_switch['username']}.")
5. Cisco Automation Library 3: ncclient (NETCONF XML RPCs)
ncclient is a Python client library for NETCONF (RFC 6241). It establishes an SSH connection over TCP port 830 to communicate with YANG-modeled network devices using structured XML Remote Procedure Calls (RPCs).
Key Methods of ncclient.manager
manager.connect(): Establishes NETCONF connection with parametershost,port=830,username,password,hostkey_verify=False, anddevice_params={'name': 'iosxe'}.m.get_config(source='running', filter=xml_filter): Retrieves configuration data from the designated datastore filtered by a specific XML subtree.m.get(filter=xml_filter): Retrieves both configuration data AND operational/state data (e.g., interface counters, CPU utilization).m.edit_config(target='running', config=xml_config): Modifies configuration elements in the target datastore.m.commit(): Commits staged changes (required on candidate-datastore platforms such as IOS-XR).m.close_session(): Gracefully terminates the NETCONF session.
from ncclient import manager
import xmltodict
router = {
"host": "192.168.1.1",
"port": 830,
"username": "admin",
"password": "Cisco123!",
"hostkey_verify": False,
"device_params": {"name": "iosxe"}
}
# XML Filter targeting the ietf-interfaces YANG model
xml_filter = """
<filter xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>GigabitEthernet1</name>
</interface>
</interfaces>
</filter>
"""
with manager.connect(**router) as m:
# Execute NETCONF <get-config> RPC
netconf_reply = m.get_config(source="running", filter=("subtree", xml_filter))
# Parse returned XML into a native Python dictionary using xmltodict
data_dict = xmltodict.parse(netconf_reply.xml)
intf = data_dict['rpc-reply']['data']['interfaces']['interface']
print(f"Interface: {intf['name']}")
print(f"Admin Status: {intf.get('enabled', 'unknown')}")
Comparison: requests vs netmiko vs ncclient
| Attribute | requests | netmiko | ncclient |
|---|---|---|---|
| Protocol | HTTP / HTTPS | SSHv2 | NETCONF over SSH |
| Standard Port | TCP 80 / 443 | TCP 22 | TCP 830 |
| Data Format | JSON / Form-Data | Unstructured Plain Text (CLI) | Structured XML (YANG schema) |
| Target Systems | Controllers (Catalyst Center, vManage) | IOS-XE, NX-OS, IOS-XR CLI | IOS-XE, IOS-XR, NX-OS NETCONF |
| Idempotency | Method dependent (PUT/DELETE) | Non-idempotent (CLI text scripts) | Idempotent (State-defined XML) |
| Parsing Burden | Low (response.json()) | High (Regex / TextFSM / TTP) | Low (XML tree / xmltodict) |
A network automation engineer is building a Python script to retrieve operational state data from a Cisco Catalyst 9300 switch using programmatic data models. The engineer must connect over the standard NETCONF port, execute an XML RPC to extract interface IP addresses, and parse the structured output into Python primitives. Which Python library and connection parameter combination is required?
A network engineer needs to parse a configuration file formatted in YAML that was pulled from an external repository. The script must deserialize the content into a Python dictionary while mitigating security risks associated with arbitrary code execution. Which Python snippet implements the recommended method?
A Python automation script uses the requests library to query the Cisco Catalyst Center REST API for a list of reachable switches. The corporate testing lab utilizes self-signed SSL certificates, causing the script to fail with an SSLCertVerificationError. Which parameter modification allows the request to complete while disabling TLS certificate verification?
An engineer executes a Netmiko script to push a standard Access Control List (ACL) configuration to fifty Cisco Catalyst switches. The script initializes ConnectHandler and attempts to push a multi-line list of configuration commands. Which Netmiko method should be invoked to send the configuration block and return the resulting session output?