Cisco DevNet Associate course 007 Software Development and Design Parsing of common data format YAML to Python data structures
Watch Full Demo on YouTube:
Introduction:
In the world of network automation and software-driven infrastructure, structured data formats like YAML, JSON, and XML are essential. YAML, in particular, is widely used in tools such as Ansible, Kubernetes, Docker Compose, and configuration files for DevOps pipelines.
While you may already be familiar with JSON and XML, YAML stands out for its clean and human-readable syntax. In this post, we’ll break down how to load and navigate YAML data in Python — a valuable skill when preparing for the DevNet Associate exam or working on real-world automation scripts.
📂YAML File: devices.yaml
Let’s use a YAML file that represents a network device configuration, similar to an XML or JSON structure you’d receive from an API or automation tool:
NetworkDeviceConfig:
Device:
Hostname: Router01
Location: DataCenter1
Vendor: Cisco
Model: ISR4451-X
OSVersion: IOS-XE 17.9.3
Interfaces:
- Name: GigabitEthernet0/0
Description: WAN Link
IPAddress: 203.0.113.1
SubnetMask: 255.255.255.252
Status: up
- Name: GigabitEthernet0/1
Description: LAN Segment
IPAddress: 192.168.1.1
SubnetMask: 255.255.255.0
Status: up
Routing:
StaticRoutes:
- Destination: 0.0.0.0
Mask: 0.0.0.0
NextHop: 203.0.113.2
OSPF:
ProcessID: 1
RouterID: 1.1.1.1
Networks:
- IP: 192.168.1.0
Wildcard: 0.0.0.255
Area: 0
🧠 Line-by-Line Breakdown
🔹 NetworkDeviceConfig
(Top-Level Key
)
NetworkDeviceConfig
(Top-Level Key
This is the root key of the YAML file. Everything under it is indented and grouped as part of this dictionary. Think of it like the “root” element in XML or the outermost object in JSON.
NetworkDeviceConfig:
🔹 Device
Section
Device
SectionThis is a nested dictionary describing the general attributes of the network device.
Device:
Hostname: Router01
Location: DataCenter1
Vendor: Cisco
Model: ISR4451-X
OSVersion: IOS-XE 17.9.3
What each field means:
- Hostname: Name of the device in the network (e.g.,
Router01
) - Location: Physical or logical location (e.g.,
DataCenter1
) - Vendor: Manufacturer, like
Cisco
,Juniper
, etc. - Model: Device model, e.g., Cisco ISR 4451-X
- OSVersion: Operating system version (important for compatibility and automation scripts)
🧠 In Python, this section becomes a dictionary:
{
'Hostname': 'Router01',
'Location': 'DataCenter1',
'Vendor': 'Cisco',
'Model': 'ISR4451-X',
'OSVersion': 'IOS-XE 17.9.3'
}
🔹 Interfaces
Section
Interfaces
Section
This section is a list (YAML uses -
to indicate list items). Each list item is a dictionary representing a network interface.
Interfaces:
- Name: GigabitEthernet0/0
Description: WAN Link
IPAddress: 203.0.113.1
SubnetMask: 255.255.255.252
Status: up
- Name: GigabitEthernet0/1
Description: LAN Segment
IPAddress: 192.168.1.1
SubnetMask: 255.255.255.0
Status: up
Each interface dictionary includes:
- Name: Interface ID (e.g.,
GigabitEthernet0/0
) - Description: Human-readable label
- IPAddress: Assigned IPv4 address
- SubnetMask: The subnet mask
- Status: Whether the interface is up or down
🧠 In Python, this will become a list of dictionaries:
[
{
'Name': 'GigabitEthernet0/0',
'Description': 'WAN Link',
'IPAddress': '203.0.113.1',
'SubnetMask': '255.255.255.252',
'Status': 'up'
},
{
'Name': 'GigabitEthernet0/1',
'Description': 'LAN Segment',
'IPAddress': '192.168.1.1',
'SubnetMask': '255.255.255.0',
'Status': 'up'
}
]
🔹 Routing
Section
Routing
Section
This part holds both static and dynamic routing information. It’s another nested dictionary under NetworkDeviceConfig
.
Routing:
StaticRoutes:
- Destination: 0.0.0.0
Mask: 0.0.0.0
NextHop: 203.0.113.2
🔸 StaticRoutes
This is a list of static route dictionaries. Each entry has:
- Destination: The destination network
- Mask: The subnet mask
- NextHop: The next-hop IP address
🧠 Python equivalent:
[
{
'Destination': '0.0.0.0',
'Mask': '0.0.0.0',
'NextHop': '203.0.113.2'
}
]
🔸 OSPF
(Dynamic Routing)
OSPF:
ProcessID: 1
RouterID: 1.1.1.1
Networks:
- IP: 192.168.1.0
Wildcard: 0.0.0.255
Area: 0
This dictionary contains settings for the OSPF dynamic routing protocol:
- ProcessID: The OSPF process ID (usually an integer)
- RouterID: Unique router ID for OSPF operations
- Networks: A list of network advertisements (each with IP, wildcard mask, and area)
🧠 This is parsed in Python as:
{
'ProcessID': 1,
'RouterID': '1.1.1.1',
'Networks': [
{
'IP': '192.168.1.0',
'Wildcard': '0.0.0.255',
'Area': 0
}
]
}
🐍 Python Script to Parse YAML: parse_yaml_config.py
We’ll use the PyYAML
library, a popular Python package for working with YAML data. Install it with:
bash
pip install pyyaml
Here’s the script:
python
import yaml
def parse_yaml_config(file_path):
with open(file_path, 'r') as f:
config = yaml.safe_load(f)
device_config = config["NetworkDeviceConfig"]
# Device Info
print("=== Device Information ===")
for key, value in device_config["Device"].items():
print(f"{key}: {value}")
# Interfaces
print("\n=== Interfaces ===")
for interface in device_config["Interfaces"]:
print("--- Interface ---")
for key, value in interface.items():
print(f"{key}: {value}")
# Static Routes
print("\n=== Static Routes ===")
for route in device_config["Routing"]["StaticRoutes"]:
print(" - Route:")
for key, value in route.items():
print(f" {key}: {value}")
# OSPF
print("\n=== OSPF Configuration ===")
ospf = device_config["Routing"]["OSPF"]
print(f" ProcessID: {ospf['ProcessID']}")
print(f" RouterID: {ospf['RouterID']}")
print(" Networks:")
for network in ospf["Networks"]:
print(" - Network:")
for key, value in network.items():
print(f" {key}: {value}")
if __name__ == "__main__":
yaml_file = "device.yaml"
parse_yaml_config(yaml_file)
🧠 Line-by-Line Explanation
✅ Load and Read YAML File
import yaml
with open(file_path, 'r') as f:
config = yaml.safe_load(f)
This loads your YAML file into a native Python dictionary using safe_load()
for security and reliability.
🖥️ Device Information
for key, value in device_config["Device"].items():
print(f"{key}: {value}")
We access the Device
section directly and iterate through the dictionary to print hostname, location, model, etc.
🌐 Interface Details
for interface in device_config["Interfaces"]:
for key, value in interface.items():
print(f"{key}: {value}")
Here, we loop through each interface object in the list and print all its attributes, such as IP and status.
🛣️ Static Routes
for route in device_config["Routing"]["StaticRoutes"]:
for key, value in route.items():
print(f"{key}: {value}")
We parse the list of routes to get destination and next-hop values.
📡 OSPF Settings
ospf = device_config["Routing"]["OSPF"]
From here, we access the OSPF configuration dictionary directly and print out process ID, router ID, and advertised networks.
▶️ Running the Script
python3 parse_yaml_config.py
Make sure the device.yaml
file is in the same directory or adjust the path accordingly.
🧾 Conclusion
YAML is a favorite in automation platforms due to its simplicity and readability. Whether you’re configuring Ansible playbooks, managing Kubernetes deployments, or building DevNet automation scripts — understanding how to load and traverse YAML in Python is a must-have skill.
In this post, we parsed a nested YAML file into Python data structures and explained how to extract key device details, interface data, and routing configurations. You can easily extend this foundation to parse more complex configurations and integrate it into your automation workflows.
References:
Cisco Certified DevNet Expert (v1.0) Equipment and Software List