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 withfind_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:
CDP has not been disabled globally with
no cdp run
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'>]