mpenning/ciscoconfparse

Make Junos-style configs searchable

mpenning opened this issue · 5 comments

Some people want ciscoconfparse to search Junos configurations... this bug tracks implementation of the feature request.

We now parse Junos configurations into Cisco IOS-style (I already hear the distant cries of "heretic")... nevertheless, if you want to see it in action, here's how I parse configs/sample_01.junos:

>>> from ciscoconfparse import CiscoConfParse
>>> parse = CiscoConfParse('configs/sample_01.junos', syntax='junos', comment='#!')
>>> for line in parse.ioscfg:
...     print line
... 
!# Last commit: 2015-06-28 13:00:59 CST by mpenning
system 
    host-name TEST01_EX
    domain-name pennington.net
    domain-search [ pennington.net lab.pennington.net ]
    location 
        country-code 001
        building HQ_005
        floor 1
    root-authentication 
        encrypted-password "$1$y7ArHxKU$zUbdeLfBirgkCsKiOJ5Qa0"
    name-server 
        172.16.3.222
    login 
        announcement "Test Lab Switch"
            class super-user
            authentication 
                encrypted-password "$1$y7ArHxKU$zUbdeLfBirgkCsKiOJ5Qa0"
    services 
        ssh 
            root-login allow
        telnet
        web-management 
            http
    syslog 
        user * 
            any emergency
        file messages 
            any notice
            authorization info
        file interactive-commands 
            interactive-commands any
    ntp 
        boot-server time.apple.com
        server 172.16.8.3
vlans 
    Management 
        vlan-id 1
        interface 
            ge-0/0/0.0
            ge-0/0/1.0
            ge-0/0/2.0
            ge-0/0/3.0
    VLAN_FOO 
        vlan-id 5
    vlan1 
        vlan-id 1
        l3-interface vlan.1
    vlan800 
        vlan-id 800
ethernet-switching-options 
    storm-control 
        interface all
interfaces 
    ge-0/0/0 
        unit 0 
            family ethernet-switching 
                port-mode access
                vlan 
                    members VLAN_FOO
    ge-0/0/1 
        unit 0 
            family ethernet-switching 
                port-mode trunk
                vlan 
                    members all
                native-vlan-id 1
    vlan 
        unit 0 
            family inet 
                address 172.16.15.5/22
routing-options 
    static 
        route 0.0.0.0/0 next-hop 172.16.12.1
        route 192.168.36.0/25 next-hop 172.16.12.1

This is a hack, to be sure... so I'm leaving the enhancement open until I get a few more details nailed down... in the mean time, if people want to experiment with searching Junos as IOS, it is here :-)

Junos Searches with find_object_branches()

Recommended: Use the find_object_branches() method as illustrated below._

Junos Searches with find_objects_w_*()

Not Recommended: Juniper configurations are quite hierarchical... To get the interface object for a Junos interface such as ge-0/0/1, you'd need to use something like parse.find_objects_w_parents(r'^\s*interface', r'^\s+ge-0/0/1'), which returns the object for ge-0/0/1 in a list. Then you can iterate through for children of ge-0/0/1 as you like.

However, if you don't require the interface object itself, you can still enforce Junos interface configuration policies in python via parse.find_objects_w_child(r'^\s+ge-0/0/1', r'port-mode\saccess'); that policy checks whether ge-0/0/1 is port-mode access.

Hi Mike

I am trying to parse the F5 Networks configurations. Do you have a syntax ready for bigIP or any timeplan :)
currently i am getting following error on at

line 263, in __init__
    config = self.convert_braces_to_ios(rgx.split(text))
line 389, in convert_braces_to_ios
    line, line_offset = line_level(tmp.strip())
line 383, in line_level
    raise ValueError("Could not parse: '{0}'".format(input))
ValueError: Could not parse: '} else {'

Running the code like

from ciscoconfparse import CiscoConfParse
parse = CiscoConfParse("/Users/python/lbj.junos", syntax='junos', comment='#')
for line in parse.ioscfg:
    print (line)

@kthned, "} else {" should parse correctly in the latest versions... please let me know if you run into problems

Another junos-parsing example using find_object_branches()

Suppose you want to associate junos interface names with their IP addresses from this example config...

config = r"""
interfaces {                                                 
    xe-0/0/0 {                                               
        unit 0 {                                             
            family inet {                                    
                address 1.1.1.6/31;                     
            }                                                
            family inet6 {                                   
                address FD00:DEAD:CAFE:6000::4/127;         
            }                                                
        }                                                    
    }                                                        
    xe-0/0/1 {                                               
        unit 0 {                                             
            family inet {                                    
                address 2.2.2.10/31;                    
            }                                                
            family inet6 {                                   
                address FD00:DEAD:CAFE:7903::2/64;             
            }                                                
        }                                                    
    }                                                        
    xe-0/0/2 {                                               
        mtu 9192;                                            
        unit 0 {                                             
            family inet {                                    
                address 3.3.3.14/31;                    
            }                                                
            family inet6 {                                   
                address FD00:DEAD:CAFE:6000::26/127;        
            }                                                
        }                                                    
    }                                                        
}
""".splitlines()
from ciscoconfparse import CiscoConfParse

parse = CiscoConfParse(config, syntax='junos', comment='#')
branchspec = (
    r'interfaces',
    r'^\s+\S+',   # Detect any intf name...
    r'^\s+unit',
    r'^\s+family',
    r'^\s+address',
)
for params in parse.find_object_branches(branchspec):

    # Select the params with the corresponding index of the regex specified in branchspec, above... 
    intf_obj, intf_family_obj, addr_obj = params[1], params[3], params[4]

    intf_name = intf_obj.re_match_typed('^\s+(\S+)')
    intf_family = intf_family_obj.re_match_typed('\s+family\s+(inet\S*)')
    addr = addr_obj.re_match_typed('^\s+address\s+(\S+)')

    print("{} {} {}".format(intf_name, intf_family, addr))

When you run the script above, you will get this as output...

xe-0/0/0 inet 1.1.1.6/31
xe-0/0/0 inet6 FD00:DEAD:CAFE:6000::4/127
xe-0/0/1 inet 2.2.2.10/31
xe-0/0/1 inet6 FD00:DEAD:CAFE:7903::2/64
xe-0/0/2 inet 3.3.3.14/31
xe-0/0/2 inet6 FD00:DEAD:CAFE:6000::26/127