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']
>>>