Using CiscoConfParse to Parse Cisco Configurations

by: George El., June 2019, Reading time: 3 minutes

In this post I will present a python module that makes the parsing of cisco configuration files easy. The module is called CiscoConfParse

Lets start by installing it

pip install ciscoconfparse

I will use the following cisco file which is taken from the documentation site, but I have added a few lines in the beginning

username cisco password 123
username cisco1 password 456
username admin password 789

!
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
!

Delete users and replace passwords

Lets assume we want a list of the users

from ciscoconfparse import CiscoConfParse
parse = CiscoConfParse("cisco.conf")
  
users = parse.find_objects("username")
for user in users:
    print(user.text)

this will print

username cisco password 123
username cisco1 password 456
username admin password 789

Lets say we want to delete these lines. This is accomplished by

parse.delete_lines(linespec="username")
parse.save_as('cisco.conf.new')

Now lets say we want to replace passwords with xxx. there is a method replace_lines but only works with strings. So we will use another method that accepts regex

users = parse.find_objects("username")
for user in users:
    user.replace(r'password\s(\S+)', r'password xxx')
    print(user.text)

the output is

username cisco password xxx
username cisco1 password xxx
username admin password xxx

Adding portfast to access ports

Of course we can accomplish all these with plain regex but the real power of this module is that it automatically creates parent child relationships between the lines, so you e.g check all interfaces if they have a certain command.

We will check if all interfaces that are in access mode have spanning-tree portfast and if not we will add it.

interface FastEthernet0/1
 switchport mode access
 switchport access vlan 532
 spanning-tree portfast
!
interface FastEthernet0/2
 switchport mode trunk
 switchport trunk allowed 300,532
!
interface FastEthernet0/3
 switchport mode access
 switchport access vlan 300
!

There are many ways to do this. One way is to find all interfaces and then check for the two conditions

from ciscoconfparse import CiscoConfParse
parse = CiscoConfParse('short.conf')

for intf in parse.find_objects(r'^interface.+?thernet'):

    is_switchport_access = intf.has_child_with(r'switchport mode access')
    has_spanningtree_portfast = intf.has_child_with(r'spanning-tree portfast')
    if is_switchport_access and (not has_spanningtree_portfast):
        intf.append_to_family(' spanning-tree portfast')

## Write the new configuration
parse.save_as('short.conf.new')

the output is

interface FastEthernet0/1
 switchport mode access
 switchport access vlan 532
 spanning-tree portfast
!
interface FastEthernet0/2
 switchport mode trunk
 switchport trunk allowed 300,532
!
interface FastEthernet0/3
 switchport mode access
 switchport access vlan 300
 spanning-tree portfast
!

we can also use the method find_objects_w_child

from ciscoconfparse import CiscoConfParse
p = CiscoConfParse("short.conf")
intfs_access_mode = p.find_objects_w_child('^interface', 'switchport mode access')
for intf in intfs_access_mode:
    if not intf.has_child_with(r'spanning-tree portfast'):
        intf.append_to_family(' spanning-tree portfast')

p.save_as('short.conf.new2')

the result will be the same as before.

Remove portfast from trunk ports

we can also find interfaces that have multiple children. Lets assume that by mistake we put spanning-tree portfast on a trunk interface. We can delete this line with

from ciscoconfparse import CiscoConfParse
p = CiscoConfParse("short.conf")
intfs_trunk_portfast = p.find_objects_w_all_children('^interface', \
    ['switchport mode trunk', 'spanning-tree portfast'])
for intf in intfs_trunk_portfast:
        intf.delete_children_matching(r'spanning-tree portfast')

p.save_as('short.conf.new2')

so if we have this config

interface FastEthernet0/3
 switchport mode access
 switchport access vlan 300
!
interface FastEthernet0/5
 switchport mode trunk
 switchport trunk allowed 300,532
 spanning-tree portfast

it will turn to

 interface FastEthernet0/3
  switchport mode access
  switchport access vlan 300
!
interface FastEthernet0/5
 switchport mode trunk
 switchport trunk allowed 300,532

As you can see this module is very handy when you want to do some compliance testing on your configurations. You could accomplish all these with plain regular expressions but it would take a long time.

comments powered by Disqus