CiscoConfParse
Fundamentals: Using Parent / Child Relationships
IOS Parent-child relationships
As we saw in a previous section, 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 another example
for in detail 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.
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 ciscoconfparse2.ciscoconfparse2 import CiscoConfParse
>>> parse = CiscoConfParse([
... '!',
... 'interface Serial1/0',
... ' ip address 1.1.1.5 255.255.255.252'
... ])
>>> for cmd in parse.find_objects(r"interface"):
... print("Object: " + repr(cmd))
... print("Config text: " + cmd.text)
...
Object: <IOSCfgLine # 1 'interface Serial1/0'>
Config text: interface Serial1/0
>>>
>>> quit()
In the example, cmd.text
refers to the IOSCfgLine
.text
attribute, which retrieves the text of the original IOS configuration
statement. You can also use the python [str] function to the the configuration text.
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 ciscoconfparse2.ciscoconfparse2 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… we should use find_parent_objects()
because
we want the interface name (which is the parent line).
find_parent_objects()
>>> parse = CiscoConfParse("/tftpboot/bucksnort.conf")
>>> qos_intfs = parse.find_parent_objects([r"^interf", r"service-policy output QOS_1"])
...
>>> # Note that `qos_intfs` is a list
>>> qos_intfs
[<IOSCfgLine # 18 'interface Serial1/1'>]
>>>
>>> # Use index number zero to get the first (and only) element
>>> qos_intfs[0]
<IOSCfgLine # 18 'interface Serial1/1'>
>>>
>>> # Use the `.text` property to get the actual command text
>>> qos_intfs[0].text
'interface Serial1/1'
Note
find_parent_objects()
supports an arbitrary list of search terms; this makes searching multiple child levels very simple.
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'>]