CiscoConfParse Fundamentals: Using Parent / Child Relationships

IOS Parent-child relationships

CiscoConfParse() reads an IOS configuration and breaks it into a list of parent-child relationships. Used correctly, these relationships can reveal a lot of useful information. The concept of IOS parent and child is pretty intuitive, but we’ll go through a simple example for clarity.

Note

CiscoConfParse assumes the configuration is in the exact format rendered by Cisco IOS devices when you use show runn or show start.

Line 1 is a parent:

policy-map QOS_1
 class GOLD
  priority percent 10
 class SILVER
  bandwidth 30
  random-detect
 class default
!

Child lines are indented more than parent lines; thus, lines 2, 4 and 7 are children of line 1:

policy-map QOS_1
 class GOLD
  priority percent 10
 class SILVER
  bandwidth 30
  random-detect
 class default
!

Furthermore, line 3 (highlighted) is a child of line 2:

policy-map QOS_1
 class GOLD
  priority percent 10
 class SILVER
  bandwidth 30
  random-detect
 class default
!

In short:

  • Line 1 is a parent, and its children are lines 2, 4, and 7.

  • Line 2 is also a parent, and it only has one child: line 3.

CiscoConfParse() uses these parent-child relationships to build queries. For instance, you can find a list of all parents with or without a child; or you can find all the configuration elements that are required to reconfigure a certain class-map.

IOSCfgLine objects

When CiscoConfParse() reads a configuration, it stores parent-child relationships as a special IOSCfgLine object. These objects are very powerful.

IOSCfgLine objects remember:

  • The original IOS configuration line

  • The parent configuration line

  • All child configuration lines

IOSCfgLine objects also know about child indentation, and they keep special configuration query methods in the object itself. For instance, if you found an IOSCfgLine object with children, you can search the children directly from the parent by using re_search_children().

Example: Retrieving text from an IOSCfgLine object

This example:

  • Parses through a configuration

  • Finds an IOSCfgLine object with find_objects()

  • Retrieves the configuration text from that object (highlighted in yellow)

>>> from ciscoconfparse import CiscoConfParse
>>> parse = CiscoConfParse([
...     '!',
...     'interface Serial1/0',
...     ' ip address 1.1.1.5 255.255.255.252'
...     ])
>>> for obj in parse.find_objects(r"interface"):
...     print("Object: " + str(obj))
...     print("Config text: " + str(obj.text))
...
Object: <IOSCfgLine # 1 'interface Serial1/0'>
Config text: interface Serial1/0
>>>
>>> quit()

In the example, obj.text refers to the IOSCfgLine text attribute, which retrieves the text of the original IOS configuration statement.

Baseline configuration for these examples

This tutorial will run all the queries against a sample configuration, which is shown below.

! Filename: /tftpboot/bucksnort.conf
!
policy-map QOS_1
 class GOLD
  priority percent 10
 class SILVER
  bandwidth 30
  random-detect
 class default
!
interface Ethernet0/0
 ip address 1.1.2.1 255.255.255.0
 no cdp enable
!
interface Serial1/0
 encapsulation ppp
 ip address 1.1.1.1 255.255.255.252
!
interface Serial1/1
 encapsulation ppp
 ip address 1.1.1.5 255.255.255.252
 service-policy output QOS_1
!
interface Serial1/2
 encapsulation hdlc
 ip address 1.1.1.9 255.255.255.252
!
class-map GOLD
 match access-group 102
class-map SILVER
 match protocol tcp
!

Example Usage: Finding interface names that match a substring

The following script will load a configuration file from /tftpboot/bucksnort.conf and use find_objects() to find the Serial interfaces.

Note that the ^ symbol at the beginning of the search string is a regular expression; ^interface Serial tells python to limit the search to lines that begin with interface Serial.

>>> from ciscoconfparse import CiscoConfParse
>>> parse = CiscoConfParse("/tftpboot/bucksnort.conf")
>>> serial_objs = parse.find_objects("^interface Serial")

The assuming we use the configuration in the example above, find_objects() scans the configuration for matching config objects and stores a list of IOSCfgLine objects in serial_objs.

>>> serial_objs
[<IOSCfgLine # 14 'interface Serial1/0'>,
<IOSCfgLine # 18 'interface Serial1/1'>,
<IOSCfgLine # 23 'interface Serial1/2'>]

As you can see, the config statements are stored inside IOSCfgLine objects. If you want to access the text inside the IOSCfgLine objects, just call their text attribute. For example…

>>> for obj in serial_objs:
...     print(obj.text)
...
interface Serial1/0
interface Serial1/1
interface Serial1/2

Going forward, I will assume that you know how to use regular expressions; if you would like to know more about regular expressions, O’Reilly’s Mastering Regular Expressions book is very good.

Example Usage: Finding parents with a specific child

Suppose we need to find interfaces with the QOS_1 service-policy applied outbound…

Method 1: for-loop to iterate over objects and search children

>>> parse = CiscoConfParse("/tftpboot/bucksnort.conf")
>>> all_intfs = parse.find_objects(r"^interf")
>>> qos_intfs = list()
>>> for obj in all_intfs:
...     if obj.re_search_children(r"service-policy\soutput\sQOS_1"):
...         qos_intfs.append(obj)
...
>>> qos_intfs
[<IOSCfgLine # 18 'interface Serial1/1'>]

This script iterates over the interface objects, and searches the children for the qos policy. It’s worth mentioning that Python also has something called a list-comprehension, which makes the script for this task a little more compact…

Method 2: list-comprehension to iterate over objects and search children

>>> parse = CiscoConfParse("/tftpboot/bucksnort.conf")
>>> qos_intfs = [obj for obj in parse.find_objects(r"^interf") \
...     if obj.re_search_children(r"service-policy\soutput\sQOS_1")]
...
>>> qos_intfs
[<IOSCfgLine # 18 'interface Serial1/1'>]

Method 3: find_parent_objects()

>>> parse = CiscoConfParse("/tftpboot/bucksnort.conf")
>>> qos_intfs = parse.find_parent_objects(parentspec=r"^interf", \
...     childspec=r"service-policy\soutput\sQOS_1")
...
>>> qos_intfs
[<IOSCfgLine # 18 'interface Serial1/1'>]

You can choose any of these methods to accomplish your task… some might question why we cover the first two methods when find_parent_objects() solves the problem completely. In this case, they have a point; however, find_parent_objects() is much slower when you have more than one child line to inspect per interface, because find_parent_objects() performs a line-by-line search of the whole configuration line each time it is called. By contrast, Method 1 is more efficient because you could simply call re_search_children() multiple times for each interface object. re_search_children() only searches the child lines of that IOSCfgLine() interface object.

Example Usage: Finding parents without a specific child

Let’s suppose you wanted a list of all interfaces that have CDP enabled; this implies a couple of things:

  1. CDP has not been disabled globally with no cdp run

  2. The interfaces in question are not configured with no cdp enable

find_parent_objects_wo_child() is a function to find parents without a specific child; it requires arguments similar to find_parent_objects():

  • The first argument is a regular expression to match the parents

  • The second argument is a regular expression to match the child’s exclusion

Since we need to find parents that do not have no cdp enable, we will use find_parent_objects_wo_child() for this query. Note that the script below makes use of a special property of python lists… empty lists test False in Python; thus, we can use if not bool(parse.find_objects(r'no cdp run')) to ensure that CDP is running globally on this device.

>>> parse = CiscoConfParse("/tftpboot/bucksnort.conf")
>>> if not bool(parse.find_objects(r'no cdp run')):
...     cdp_intfs = parse.find_parent_objects_wo_child(r'^interface',
...         r'no cdp enable')

Results:

>>> cdp_intfs
[<IOSCfgLine # 14 'interface Serial1/0'>, <IOSCfgLine # 18 'interface Serial1/1'>, <IOSCfgLine # 23 'interface Serial1/2'>]