ciscoconfparse

ciscoconfparse is a Python library for querying Cisco-style configurations. The purpose of this workbook is to examine some features that are, in my view, not very well presented in the official documentation.


In [227]:
# The line below can be ignored but I didn't set up my environment properly 
import sys ; sys.path.append('/home/mjuenemann/.virtualenvs/ciscoconfparse/lib/python3.6/site-packages')

In [228]:
import ciscoconfparse

I am going to use a very stripped down version of the Secure IOS Template by Team Cymru. This is not a fully functional IOS configuration!


In [229]:
CONFIG = """
!
hostname router01
!
tacacs-server host 192.0.2.34
tacacs-server key cheezit
!
interface Ethernet2/0
 description Unprotected interface, facing towards Internet
 ip address 192.0.2.14 255.255.255.240
 no ip unreachables
 ntp disable
 no mop enable
 mtu 900
!
interface Ethernet2/1
 description Protected interface, facing towards DMZ
 ip address 192.0.2.17 255.255.255.240
 no mop enable
"""

First, he configuration must be parsed by creating an instance of ciscoconfparse.CiscoConfParse(). The class expects either a file object or a list of configuration lines.


In [230]:
config = ciscoconfparse.CiscoConfParse(CONFIG.split('\n'))
config


Out[230]:
<CiscoConfParse: 18 lines / syntax: ios / comment delimiter: '!' / factory: False>

When CiscoConfParse() reads a configuration, it stores parent-child relationships as a special IOSCfgLine object. IOSCfgLine instances are returned when one queries the parsed configuration.

Finding lines matching a regular expression


In [231]:
tacacs_lines = config.find_objects(r'^tacacs')
tacacs_lines


Out[231]:
[<IOSCfgLine # 3 'tacacs-server host 192.0.2.34'>,
 <IOSCfgLine # 4 'tacacs-server key cheezit'>]

In [232]:
first_tacacs_line = tacacs_lines[0]
type(first_tacacs_line)


Out[232]:
ciscoconfparse.models_cisco.IOSCfgLine

IOSCfgLine instances have several useful attributes.


In [233]:
first_tacacs_line.linenum


Out[233]:
3

In [234]:
first_tacacs_line.indent


Out[234]:
0

In [235]:
first_tacacs_line.text


Out[235]:
'tacacs-server host 192.0.2.34'

Finding sections with children

The next example finds interfaces that have NTP disabled.


In [236]:
interfaces_with_ntp_disabled = config.find_objects_w_child(r'^interface', r'ntp disable')
interfaces_with_ntp_disabled


Out[236]:
[<IOSCfgLine # 6 'interface Ethernet2/0'>]

The .text attribute only returns the matching line whereas the .ioscfg attributes includes all children lines as a list.


In [237]:
interfaces_with_ntp_disabled[0].text


Out[237]:
'interface Ethernet2/0'

In [238]:
interfaces_with_ntp_disabled[0].ioscfg


Out[238]:
['interface Ethernet2/0',
 ' description Unprotected interface, facing towards Internet',
 ' ip address 192.0.2.14 255.255.255.240',
 ' no ip unreachables',
 ' ntp disable',
 ' no mop enable',
 ' mtu 900']

Finding sections without children

The next example finds interfaces that have NTP not disabled.


In [239]:
interfaces_with_ntp_not_disabled = config.find_objects_wo_child(r'^interface', r'ntp disable')
interfaces_with_ntp_not_disabled


Out[239]:
[<IOSCfgLine # 14 'interface Ethernet2/1'>]

In [240]:
interfaces_with_ntp_not_disabled[0].ioscfg


Out[240]:
['interface Ethernet2/1',
 ' description Protected interface, facing towards DMZ',
 ' ip address 192.0.2.17 255.255.255.240',
 ' no mop enable']

Finding sections with all children

The next example finds interfaces that have IP-Unreachables and MOP disabled.


In [241]:
results = config.find_objects_w_all_children(r'interface', [r'no ip unreachables', r'no mop enable'])
results


Out[241]:
[<IOSCfgLine # 6 'interface Ethernet2/0'>]

In [242]:
results[0].ioscfg


Out[242]:
['interface Ethernet2/0',
 ' description Unprotected interface, facing towards Internet',
 ' ip address 192.0.2.14 255.255.255.240',
 ' no ip unreachables',
 ' ntp disable',
 ' no mop enable',
 ' mtu 900']

Finding lines with parents

The .find_objects_w_parents() methods returns children and not their parents.


In [243]:
results = config.find_objects_w_parents(r'^interface', 'no mop enable')
results


Out[243]:
[<IOSCfgLine # 11 ' no mop enable' (parent is # 6)>,
 <IOSCfgLine # 17 ' no mop enable' (parent is # 14)>]

Deleting lines

This can be handy in combination with the IOSCfgLine.delete() method which I haven't covered yet.

IOSCfgLine objects provide several methods for changing an existing configuration. Let's delete all no mop enable lines.


In [244]:
for result in results:
    result.delete()
# Call .commit() after changing the configuration
config.commit()

The no mop enable lines are now missing.


In [245]:
config.ioscfg


Out[245]:
['!',
 'hostname router01',
 '!',
 'tacacs-server host 192.0.2.34',
 'tacacs-server key cheezit',
 '!',
 'interface Ethernet2/0',
 ' description Unprotected interface, facing towards Internet',
 ' ip address 192.0.2.14 255.255.255.240',
 ' no ip unreachables',
 ' ntp disable',
 ' mtu 900',
 '!',
 'interface Ethernet2/1',
 ' description Protected interface, facing towards DMZ',
 ' ip address 192.0.2.17 255.255.255.240']

Adding lines

Let's ensure that the configuration uses an NTP server.


In [246]:
config.append_line('ntp server 192.168.1.1')

# Call .commit() before searching again!!!
config.commit()
config.ioscfg


Out[246]:
['!',
 'hostname router01',
 '!',
 'tacacs-server host 192.0.2.34',
 'tacacs-server key cheezit',
 '!',
 'interface Ethernet2/0',
 ' description Unprotected interface, facing towards Internet',
 ' ip address 192.0.2.14 255.255.255.240',
 ' no ip unreachables',
 ' ntp disable',
 ' mtu 900',
 '!',
 'interface Ethernet2/1',
 ' description Protected interface, facing towards DMZ',
 ' ip address 192.0.2.17 255.255.255.240',
 'ntp server 192.168.1.1']

Adding lines to sections

Let's ensure that all Ethernet interfaces have an explicit MTU of 1500 configured. This is a two-step process were first any existing MTU lines are deleted and then the correct ones are added.


In [247]:
# Delete all existing MTU lines.
for interface in config.find_objects(r'^interface.+Ethernet'):
    interface.delete_children_matching('mtu \d+')
config.commit()

# Add the correct MTU. Note the use of the correct indentation value for children.
for interface in config.find_objects(r'^interface.+Ethernet'):
    interface.append_to_family('mtu 1500', indent=interface.child_indent)
config.commit()

In [248]:
config.ioscfg


Out[248]:
['!',
 'hostname router01',
 '!',
 'tacacs-server host 192.0.2.34',
 'tacacs-server key cheezit',
 '!',
 'interface Ethernet2/0',
 ' description Unprotected interface, facing towards Internet',
 ' ip address 192.0.2.14 255.255.255.240',
 ' no ip unreachables',
 ' ntp disable',
 ' mtu 1500',
 '!',
 'interface Ethernet2/1',
 ' description Protected interface, facing towards DMZ',
 ' ip address 192.0.2.17 255.255.255.240',
 ' mtu 1500',
 'ntp server 192.168.1.1']

In [ ]: