Getting Config Values with CiscoConfParse

Now that we’re familiar with parent / child relationships, let’s tackle another common problem. How do you get specific values from a configuration?

For sure, you could use traditional Python techniques, such as Python’s re module; however, the re module is rather cumbersome when you’re retrieving a lot of config values.

CiscoConfParse introduces methods directly on CiscoConfParse objects which simplify getting values from a configuration:

For the next examples, we will use this configuration…

! Filename: short.conf
!
hostname IAHS1MDF-AR01A
!
vlan 10
 name 192.0.2.0_24_HoustonUsers_S1_Bldg_MDF
vlan 20
 name 128.66.0.0_24_HoustonPrinters_S1_Bldg_MDF
!
interface Vlan10
 description Connection to Houston office LAN switches
 ip address 192.0.2.2 255.255.255.0
 ip helper-address 198.51.100.12
 ip helper-address 203.0.113.12
 standby 10 ip 192.0.2.1
 standby 10 priority 110
 arp timeout 240
 no ip proxy-arp
!
interface Vlan20
 description Connection to Houston printer subnet
 ip address 128.66.0.2 255.255.255.0
 standby 20 ip 128.66.01
 standby 20 priority 110
!

re_match_typed(): Get a value from an object

Let’s suppose we want the hostname of short.conf above. One approach is to use find_objects() and then use re_match_typed() to get the hostname:

>>> from ciscoconfparse import CiscoConfParse
>>> parse = CiscoConfParse('short.conf')
>>> global_obj = parse.find_objects(r'^hostname')[0]
>>> hostname = global_obj.re_match_typed(r'^hostname\s+(\S+)', default='')
>>> hostname
'IAHS1MDF-AR01A'
>>>

Take note of the regex we used: r'hostname\s+(\S+)'. This regex has a capture group (bounded by the parenthesis), which re_match_typed() requires. re_match_typed() uses the contents of this capture group to return the value.

This technique is fine, but we have to tell Python to iterate over all config objects with find_objects() and then we extract the hostname from that object.

What if there was a way to get the hostname without calling find_objects()? As it happens, re_match_iter_typed() does it for you.

re_match_iter_typed(): Iterate over all children and get a value

re_match_iter_typed() iterates over child objects and returns the first value it finds. This is very useful because re_match_iter_typed() does all the iteration for us.

>>> from ciscoconfparse import CiscoConfParse
>>> parse = CiscoConfParse('short.conf')
>>> hostname = parse.re_match_iter_typed(r'^hostname\s+(\S+)', default='')
>>> hostname
'IAHS1MDF-AR01A'
>>>

Take note of the regex we used: r'hostname\s+(\S+)'. This regex has a capture group (bounded by the parenthesis), which re_match_iter_typed() requires. re_match_iter_typed() uses the contents of this capture group to return the value.

This code is better than the previous example, because it eliminates the call to find_objects() that we used above.

However, there are still times when you need to call find_objects(); one example is when you need to get the HSRP address from an interface.

>>> from ciscoconfparse import CiscoConfParse
>>> parse = CiscoConfParse('short.conf')
>>> intf_obj = parse.find_objects(r'^interface\s+Vlan10$')[0]
>>> hsrp_ip = intf_obj.re_match_iter_typed(r'standby\s10\sip\s(\S+)',
...     default='')
>>> hsrp_ip
'192.0.2.1'
>>>

The reason we had to call find_objects() is so we can get the specific inteface object that contains the HSRP address in question.

You may be wondering, “Why does this method have typed in its name?”. This is because re_match_iter_typed() can return the value cast as a python type. By default, all return values are cast as a Python str.

The following example looks for the ARP timeout on interface Vlan10, and returns it cast as a Python int.

>>> from ciscoconfparse import CiscoConfParse
>>> parse = CiscoConfParse('short.conf')
>>> intf_obj = parse.find_objects(r'^interface\s+Vlan10$')[0]
>>> arp_timeout = intf_obj.re_match_iter_typed(r'arp\s+timeout\s+(\d+)',
...     result_type=int, default=4*3600)
>>> arp_timeout
240
>>>

Finally, let’s talk about two more re_match_iter_typed() keywords: default and untyped_default.

re_match_iter_typed() has a default keyword, which specifies what the default value should be if the regular expression doesn’t match the configuration. The value in default is automatically cast as the result_type.

However, there may be times when you don’t want default’s value to be cast as result_type. If you find yourself in that situation, you can call re_match_iter_typed() with untyped_default=True.

>>> from ciscoconfparse import CiscoConfParse
>>> parse = CiscoConfParse('short.conf')
>>> intf_obj = parse.find_objects(r'^interface\s+Vlan20$')[0]
>>> arp_timeout = intf_obj.re_match_iter_typed(r'arp\s+timeout\s+(\d+)',
...     result_type=int,
        untyped_default=True, default='__no_explicit_value__')
>>> arp_timeout
'__no_explicit_value__'
>>>

Getting multiple values from an interface with re_match_typed()

re_match_typed() and re_match_iter_typed() does not` return mutliple values.

Suppose we want to get all the DHCP helper-addresses from an interface. The best way to do this is to manually iterate over the children and append the values we want to a list.

This script will get all the DHCP helper-addresses from Vlan10:

>>> from ciscoconfparse import CiscoConfParse
>>> parse = CiscoConfParse('short.conf')
>>> retval = list()
>>>
>>> HELPER_REGEX = r'ip\s+helper-address\s+(\S+)$'
>>> NO_MATCH = '__no_match__'
>>>
>>> # Iterate over matching interfaces
>>> for intf_obj in parse.find_objects(r'^interface\s+Vlan10$'):
...     for child_obj in intf_obj.children:  # Iterate over intf children
...         val = child_obj.re_match_typed(HELPER_REGEX, default=NO_MATCH)
...         if val!=NO_MATCH:
...             retval.append(val)
...
>>> retval
['198.51.100.12', '203.0.113.12']
>>>