In the first post in this series, I talked briefly about utilizing Regular Expressions (Regex) to parse network device output. Next, I delved into the difference between status and configuration when it comes to network device output, and why you might use one tool vs another depending on which type of output you are working with.

As a brief refresher, network device output (from a traditional CLI session) can generally come in two forms:

  • Status
    • An output that presents some attribute or information about the current state of the device or its performance.
    • Example commands for gathering status include:
      • show version
      • show interfaces
    • Example commands for modifying status include:
      • clear counters
      • reload
  • Configuration
    • An output that presents the desired/requested operating condition of the device.
    • Example commands for gathering configuration include:
      • show running-config
      • show startup-config

In this post, we’ll look at one of the open-source parsing libraries that exist for network device configuration: CiscoConfParse.

Why Not Just Use Regex?#

CiscoConfParse, as well as Hierarchical Configuration (heir_config) which we’ll cover in the next post, provide much more power than simply using Regex to match and extract a pattern from a line of status or configuration. These parsing libraries provide a framework to understand and manipulate “Cisco style” configuration (with indented blocks representing related items), or even “Juniper style” curly brace ({}) delineated configuration.

If you think about the previous Regex example, how would we utilize Regex to easily parse the following configuration output, specifically looking to answer this question:

What interfaces have security-level 0 configured on them?

!
 interface GigabitEthernet0/0
  description OUTSIDE-VLAN300-10.10.10.0/24
  nameif OUTSIDE
  security-level 0
  ip address 10.10.10.50 255.255.255.0 
 !
 interface GigabitEthernet0/1
  no nameif
  no security-level
  no ip address
 !
 interface GigabitEthernet0/1.400
  description INSIDE-VLAN400-10.10.40.0/24
  vlan 400
  nameif INSIDE
  security-level 100
  ip address 10.10.40.1 255.255.255.0 
 !
interface Management0/0
  management-only
  nameif MGMT
  security-level 0
  ip address 10.10.60.2 255.255.255.0

For example, you can certainly search for the string “security-level 0”, however how do you then tie it back to the interface above it? Remember that when we’re evaluating a string with Regex, we can work on a multi-line string. We would have to set up capturing groups for the interface and nameif, and then only match if there was also our target string. And while, of course, this is doable, it quickly becomes cumbersome to do for every item you wish to match. Also consider, what if you want to output the entire interface configuration for any interface that matches “security-level 0”? There is a better and simpler way to handle this.

Enter CiscoConfParse#

CiscoConfParse solves this problem, and more, by breaking the configuration into parent/child chunks and allowing you to search and find based on the contents of either. For an in-depth walk-through and examples I highly recommend the CiscoConfParse documentation; below we’ll simply demonstrate a quick use case.

For our test environment, we’re utilizing the same firewall pair and management station from the previous post:

Image of a Person sitting at a computer connecting to two firewall devices.

In this series of posts, we’ll just use the simple network shown here. There is a Management Station with Python 3.8 installed, and two Cisco ASAv firewalls (ASAv1 and ASAv2) in a tiered setup.

Code samples and requirements.txt for this post can be found on my Github.

Below is an example script, based on the previous examples in this series, where we log into these ASAs, gather the output of show run interface, and print out the interface(s) with security-level 0 configured.

#!/usr/bin/env python3

"""
Example code for Network-Notes.com entry on Parsing Network Device Output
"""

import getpass
import sys


from ciscoconfparse import CiscoConfParse
from netmiko import ConnectHandler


"""
Test parsing network device configuration
"""

# Gather the needed credentials
net_device_username = input("Username: ")
net_device_password = getpass.getpass()
# Set enable/secret = password for now
net_device_secret = net_device_password

# Setup a dict with our ASAvs in it, in real world this could be read
# from a CSV or the CLI or any other source
firewalls = {
    "ASAv1": {"ip": "10.10.10.50", "platform": "cisco_asa"},
    "ASAv2": {"ip": "10.10.60.2", "platform": "cisco_asa"},
}

# Setup an empty dict for our results:
results = {}

# Instantiate netmiko connection objects and gather the output of
# `show run interface` on these two firewalls
for fw_name, fw_data in firewalls.items():
    print(f"Connecting to {fw_name}...")
    fw_connection = ConnectHandler(
        ip=fw_data["ip"],
        device_type=fw_data["platform"],
        username=net_device_username,
        password=net_device_password,
        secret=net_device_secret,
    )
    results[fw_name] = fw_connection.send_command(
        command_string="show run interface"
    )

# Parse our results:
print("Parsing Results...")
outside_interfaces = {}
for fw_name, config in results.items():
    interface_config = CiscoConfParse(config=config.splitlines())
    outside_interfaces[fw_name] = interface_config.find_objects_w_child(
        parentspec=r"^interface", childspec="security-level 0"
    )

# Print our results
for fw_name, interface_list in outside_interfaces.items():
    print(f"==== ==== [ {fw_name} \"Outside\" interfaces ] ==== ====\n")
    for interface in interface_list:
        print(f"     ==== [ {interface.text} ] ====     \n")
        for line in interface.ioscfg:
            print(f"{line}")
        print("\n")

It will produce the following output when executed:

Username: cisco
Password: 
Connecting to ASAv1…
Connecting to ASAv2…
Parsing Results…
==== ==== [ ASAv1 "Outside" interfaces ] ==== ====
     ==== [ interface GigabitEthernet0/0 ] ====     
interface GigabitEthernet0/0
 description OUTSIDE-VLAN300-10.10.10.0/24
 nameif OUTSIDE
 security-level 0
 ip address 10.10.10.50 255.255.255.0 
 ospf authentication-key 
 ospf message-digest-key 1 md5 
 ospf authentication message-digest
==== ==== [ ASAv2 "Outside" interfaces ] ==== ====
     ==== [ interface Management0/0 ] ====     
interface Management0/0
 management-only
 nameif MGMT
 security-level 0
 ip address 10.10.60.2 255.255.255.0 

As you can see, we easily found the interfaces we needed, and have the entire configuration. Now let’s break apart what happened a little bit more.

Parent/Child Relationships#

CiscoConfParse understands the hierarchical nature of Cisco-style configuration. In our example:

  • Parent: interface GigabitEthernet0/0
  • Children: All indented lines under that interface (description, nameif, security-level, etc.)

The key method in our script is:

interface_config.find_objects_w_child(
    parentspec=r"^interface", childspec="security-level 0"
)

This searches for:

  • parentspec: Lines matching regex ^interface (lines starting with “interface”)
  • childspec: That have a child line containing “security-level 0”

The result is a list of IOSCfgLine objects representing matching parent interfaces.

Key CiscoConfParse Methods#

  • find_objects() - Find lines matching a regex pattern
  • find_objects_w_child() - Find parents with specific children
  • find_objects_wo_child() - Find parents without specific children
  • find_children() - Find all children of a parent

Each object provides:

  • .text - The configuration line text
  • .ioscfg - Complete configuration block (parent + children)
  • .children - List of child objects

Why This Matters#

Compare this to the regex approach from Part 1. To find interfaces with security-level 0 using regex, you’d need complex multi-line patterns and capturing groups. CiscoConfParse makes it intuitive by understanding configuration structure.

This is particularly powerful for:

  • Security audits (find interfaces with specific settings)
  • Configuration validation (ensure required settings exist)
  • Bulk changes (identify what needs updating)

What’s Next#

In the next post, we’ll explore Hierarchical Configuration (hier_config), which builds on these concepts to provide configuration comparison and remediation capabilities.

Conclusion#

CiscoConfParse transforms configuration parsing from complex regex gymnastics into readable, maintainable code. By understanding parent/child relationships in network configurations, it makes complex parsing tasks simple and intuitive.