Metadata-Version: 2.4
Name: cisco_config_parser
Version: 3.0.0
Summary: This library is used for Network Automation involving Cisco Routers and Switch. It will parse Cisco IOS, IOS-XE, IOS-XR, and NXOS configuration file into objects and/or json format
Home-page: https://github.com/arezazadeh/cisco_config_parser
Author: Ahmad Rezazadeh
Author-email: ahmad@klevernet.io
License: MIT License
Description-Content-Type: text/markdown
Dynamic: author
Dynamic: author-email
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license
Dynamic: summary

# Cisco Configuration Parser

## Overview
The **Cisco Configuration Parser** is a Python library designed for network automation tasks, specifically focusing on parsing configuration files from Cisco routers and switches. It supports Cisco IOS, IOS-XE, IOS-XR, and NXOS platforms. This library allows you to convert running configuration files into structured Python objects or JSON/dictionary formats for easier analysis, modification, or automation.

## Key Features

### <li>  Platform Detection
This library is designed to be **platform agnostic**, meaning it can automatically determine the Cisco platform based on the syntax of the configuration file. In cases where the platform cannot be determined automatically, you can manually specify the platform by passing `platform="IOS/XR/NXOS"` to the parser.

### <li>  Flexible Data Handling
The parser can convert configuration data into either Python objects or JSON/dictionary formats. You can control the format by passing the `return_json=True` flag to the relevant method. This flexibility allows you to integrate with a wide range of automation workflows and tools.

### <li>  Configuration Hierarchy Recognition
Cisco configurations often feature a mix of parent-child relationships and standalone entries. This library is adept at recognizing and parsing these patterns to capture both common and critical configuration attributes. For instance:

- **Standalone Configuration Example:**
```bash
hostname switch01.net
``` 

- **Parent-Child Configuration Example:**

```bash
Vlan 100                <<<< Parent
  name DATA_VLAN        <<<< Child
```

This library tries to captures the important and common attributes of the configurations for different sections and convert them to python objects or json/dict format. 

Currently parsed sections (platform support noted per section):

```python
- Whole-device model via parser.parse()      # IOS / NXOS / XR
- Hostname, version, domain, name-servers    # IOS / NXOS / XR
- Users, AAA, SNMP, NTP, logging             # IOS / NXOS
- TACACS+/RADIUS servers, groups, CoA        # IOS / IOS-XE / NXOS / XR
- NetFlow (records, exporters, monitors,
  samplers, interface attachments)           # IOS / IOS-XE / XR
- Line vty / con (access-class, transport)   # IOS / NXOS
- Banner                                     # IOS / NXOS
- VLANs                                      # IOS / NXOS
- Interfaces (Layer2 access/trunk, Layer3)   # IOS / NXOS / XR
- VRF definitions (rd, route-targets, AFs)   # IOS / NXOS / XR
- Access-lists (named/numbered, std/ext)     # IOS / NXOS
- Prefix-lists                               # IOS / NXOS
- Prefix-sets                                # IOS-XR
- Community-lists (standard/expanded)        # IOS / NXOS
- Community-sets / extcommunity-sets (rt)    # IOS-XR
- Route-maps (match/set per clause)          # IOS / NXOS
- Route-policies (raw RPL)                   # IOS-XR
- Static Routes                              # IOS / NXOS / XR
- Dynamic Routes: OSPF, BGP                  # IOS / NXOS / XR
- Dynamic Routes: EIGRP                      # IOS
- HSRP / VRRP groups                         # IOS / NXOS
- Port-channels / bundles (+ NXOS vpc)       # IOS / NXOS / XR
- VXLAN/EVPN (nve, evpn vnis, vni maps,
  anycast gateway)                           # NXOS
- vPC (domain, peer-keepalive, peer-link,
  vpc port-channels)                         # NXOS
- TrustSec / CTS (sgt-maps, sgacl matrix,
  sxp, enforcement, cts manual ifaces)       # IOS-XE
- LISP / SD-Access fabric (locator-sets,
  services, instance-ids, dynamic-eids)      # IOS-XE
- Generic config hierarchy (ConfigTree)      # IOS / NXOS / XR
- Cross-reference validation (validate())    # IOS / NXOS / XR
```
Above configuration sections are captured along with the most common attributes/children. and the rest of them are captured inside method `obj.children`. Anything not modeled explicitly is still reachable through `ConfigTree` (see below) or the custom parent/child regex search.

### <li>  Parent/Child Regex Parsing Capabilities
In addition to above default behavior or the library, you also have the option to pass your own regex to the `ConfigParser` as either `parent_regex=r"<custom_regex>", child_regex=r"<custom_regex>"` or only `parent_regex=r"<custom_regex>"`. 

**Note:** If you pass both `parent_regex` and `child_regex`, the library attempt to search for the parent and the only child that you have searched for. If you would like to get all the children, then just pass the `parent_regex` to the class. I will have some example at the bottom this document. 


##  <li> Install the package
This package is available on `pypi.org` you can install the package via `pip`.

https://pypi.org/project/cisco-config-parser/

```bash
pip install cisco-config-parser
```


##  <li> Current Classes in this library

```python
IOSStaticRouteConfig
IOSOSPFConfig
IOSEIGRPConfig
IOSBGPConfig
L3InterfaceParser
L2InterfaceParser
ParentChildParser
ACLParser
PrefixListParser
RouteMapParser
VRFParser
DeviceIdentityParser
ConfigTree
```

There more Classes are being built, and will be released in the upcoming versions. 

## <li> Whole-Device Model — `parser.parse()`

If you are feeding configs to an automation pipeline or an AI agent, the most useful entry point is `parse()`. It returns **one JSON-able dictionary for the whole device** — no need to call fifteen getters:

```python
>>> model = parser.parse()
>>> model.keys()
dict_keys(['platform', 'hostname', 'identity', 'vrfs', 'vlans',
           'l3_interfaces', 'l2_access_interfaces', 'l2_trunk_interfaces',
           'access_lists', 'prefix_lists', 'community_lists', 'route_maps',
           'fhrp_groups', 'port_channels', 'routing', 'banner',
           'unsupported', 'parse_errors'])

>>> model["hostname"]
'lab-rtr-01'
>>> model["routing"]["bgp"][0]["router_id"]
'192.0.2.1'
```

- Sections that are not implemented for the detected platform are listed under `model["unsupported"]` instead of raising.
- Unexpected parser failures land in `model["parse_errors"]` so one bad section never loses the whole device.
- On IOS-XR the model also includes `prefix_sets`, `community_sets` and `route_policies`.
- Pass `parse(include_raw_tree=True)` to attach the full config hierarchy under `config_tree`.

## <li> Access-Lists

Named and numbered, standard and extended ACLs — with per-entry protocol/source/port/destination breakdown and remarks:

```python
>>> acls = parser.get_access_lists(return_json=True)
[
    {
        "name": "ACL-MGMT-IN",
        "type": "extended",
        "style": "named",
        "remarks": ["--- allow ssh from mgmt subnets ---"],
        "entries": [
            {
                "sequence": null,
                "action": "permit",
                "protocol": "tcp",
                "source": "10.99.99.0 0.0.0.255",
                "source_port": null,
                "destination": "any",
                "destination_port": "eq 22",
                "options": null,
                "raw": "permit tcp 10.99.99.0 0.0.0.255 any eq 22"
            }
        ]
    }
]
```

## <li> Prefix-Lists and Route-Maps

```python
>>> parser.get_prefix_lists(return_json=True)
[
    {
        "name": "PL-CUST-A",
        "description": null,
        "entries": [
            {"sequence": "10", "action": "permit", "prefix": "172.16.50.0/24", "le": null, "ge": null,
             "raw": "ip prefix-list PL-CUST-A seq 10 permit 172.16.50.0/24"}
        ]
    }
]

>>> parser.get_route_maps(return_json=True)
[
    {
        "name": "RM-CORE-IN",
        "clauses": [
            {"action": "deny", "sequence": "5",  "description": null,
             "match": ["ip address prefix-list PL-DENY-RFC1918"], "set": [], "children": [...]},
            {"action": "permit", "sequence": "10", "description": null,
             "match": ["ip address prefix-list PL-DEFAULT-ONLY"], "set": ["local-preference 200"], "children": [...]}
        ]
    }
]
```

On IOS-XR use `parser.get_prefix_sets()` and `parser.get_route_policies()` for the RPL equivalents.

## <li> Community-Lists

BGP community-lists — IOS named/numbered (standard 1-99, expanded 100-500) and NXOS sequenced lists:

```python
>>> parser.get_community_lists(return_json=True)
[
    {
        "name": "CL-CUSTOMER",
        "type": "standard",
        "style": "named",
        "entries": [
            {"sequence": null, "action": "permit", "communities": ["65000:100"],
             "raw": "ip community-list standard CL-CUSTOMER permit 65000:100"}
        ]
    }
]
```

On IOS-XR use `parser.get_community_sets()`.

## <li> VRF Definitions

Handles IOS `vrf definition`, legacy `ip vrf`, NXOS `vrf context` and XR `vrf`:

```python
>>> parser.get_vrfs(return_json=True)
[
    {
        "name": "CUST-A",
        "rd": "65000:100",
        "description": null,
        "address_families": [
            {"name": "ipv4", "route_target_import": ["65000:100"], "route_target_export": ["65000:100"], "children": [...]}
        ],
        "children": [...]
    }
]
```

## <li> Device Identity / Management Plane

The compliance staples in one object — hostname, version, domain, users, AAA, SNMP, NTP, logging and line vty/con:

```python
>>> identity = parser.get_device_identity(return_json=True)
>>> identity["hostname"], identity["version"]
('lab-rtr-01', '17.3')
>>> identity["lines"]
[
    {"name": "vty 0 4", "access_class": "ACL-MGMT-IN", "transport_input": "ssh",
     "exec_timeout": "10 0", "children": [...]}
]
>>> identity["snmp"]["communities"]
[{"community": "REDACTED-RO", "mode": "RO", "acl": "ACL-SNMP-RO"}]
```

On NXOS, `identity["features"]` lists the enabled `feature` flags.

## <li> TrustSec and SD-Access — `get_trustsec()` / `get_lisp_config()`

For Catalyst 9K fabric nodes running Cisco TrustSec / SD-Access:

```python
>>> cts = parser.get_trustsec(return_json=True)
>>> cts["sgt_maps"][0]
{"vrf": "CAMPUS", "address": "10.100.60.5", "vlan_list": None, "sgt": "20131"}
>>> cts["permissions"][0]
{"from_sgt": "2", "to_sgt": "2", "sgacls": ["FALLBACK"]}
>>> cts["sxp_connections"][0]["mode"]
'peer speaker'
>>> cts["interfaces"][0]      # per-interface "cts manual" blocks
{"interface": "GigabitEthernet1/0/1", "mode": "manual", "static_sgt": "25000", ...}

>>> lisp = parser.get_lisp_config(return_json=True)
>>> lisp["instances"][0]["dynamic_eids"][0]
{"name": "LAB_USERS-IPV4", "database_mappings": [{"prefix": "10.100.0.0/24", "locator_set": "rloc_lab_set"}]}
```

Secrets are never stored: sxp default passwords and lisp map-server key hashes are reduced to booleans / `<hidden>`.

## <li> Cross-Reference Validation — `parser.validate()`

Audits the config for **references to policy objects that are never defined** (broken config) and **defined objects that are never referenced** (dead config) — across route-maps, route-policies, prefix-lists/sets, community-lists/sets, ACLs and VRFs:

```python
>>> report = parser.validate()
>>> report["undefined_references"]
{
    "route_map": [
        {"name": "RM-MISSING", "lines": ["neighbor 10.0.0.1 route-map RM-MISSING in"]}
    ],
    "access_list": [
        {"name": "ACL-DOES-NOT-EXIST", "lines": ["ip access-group ACL-DOES-NOT-EXIST in"]}
    ]
}
>>> report["unused_definitions"]
{"route_map": ["RM-ORPHAN"], "community_list": ["CL-CUSTOMER"]}
```

Implicit vrfs (`management`, `default`, ...) are not flagged. "Unused" is a best-effort audit hint — an object may be used by a feature the library does not scan yet.

## <li> Generic Config Hierarchy — `ConfigTree`

Everything not explicitly modeled is still one query away. `ConfigTree` parses the whole config into an indentation-based tree in a single pass and works on IOS, NXOS and XR:

```python
>>> from cisco_config_parser.config_tree import ConfigTree
>>> tree = ConfigTree(running_config)

>>> bgp = tree.find_objects(r"^router bgp")[0]
>>> vrf_af = bgp.find_children(r"^address-family ipv4 vrf CUST-A")[0]
>>> vrf_af.child_lines()
['network 172.16.1.0 mask 255.255.255.252', 'neighbor 172.16.1.1 remote-as 65100', ...]

>>> tree.find_all(r"^neighbor 172\.16\.1\.1")   # any depth
>>> bgp.section_text()                           # reconstruct the section as text
>>> tree.to_dict()                               # whole config as nested dicts
```

## <li>  Get Example usage:
A short documentation is embeded in the code base. you can view those example by importing `helper` and calling the class

```python
from cisco_config_parser.helper import helper 

helper = helper.IOSStaticRouteConfig()

print(helper)
```

**Output**:
```

Example Usage:
====================================================================================================

with open("show_run.txt", "r") as file_output:
    content = file_output.read()


obj = ConfigParser(content)

obj.get_static_routes() << Returns a list of objects
obj.get_static_routes(return_json=True) << Returns a list of dictionaries 

```
##  <li> Examples:

## <li>  Loading the Running-Config File 

```python
>>> ios_file = "/Users/fileFolder/devFolder/config.txt"
>>> with open(ios_file, "r") as f:
        running_config = f.read()

>>> from cisco_config_parser import ConfigParser
>>> 
>>> parser = ConfigParser(running_config)
```


## <li>  Layer3 Interface

In layer-3 interface, the class attempts to capture all the important attributes of the interface, such as:

- IPv4 Address
- IPv6 Address
- Subnet 
- Subnet Mask
- Secondary IPv4 Address
- Secondary Subnet 
- Secondary Subnet Mask 
- HSRP or VRRP Config
    - VIP 

### <li> Subnet list and their Usages 

One of the great feature in this library is it captures all the subnets that are being used in your Network Device along with their usage. 
Also it tells you how many of each subnet masks are being used as well. I personaly use this feature when i need to automate DHCP/IPAM documentaion cleanup. 

**Example below illustriates this feature:**

```python
>>> subnet_usage = parser.get_subnet_and_usage(include_subnet_count=True)
>>> print(json.dumps(subnet_usage, indent=4))

{
    "Loopback100": "10.241.17.8/32",
    "Loopback200": "10.252.248.1/32",
    "Vlan200": "10.252.182.0/23",
    "Vlan300": "10.244.16.160/27",
    "Vlan310": "10.241.101.80/28",
    "Vlan400": "10.39.10.32/27",
    "Vlan700": "172.31.81.0/25",
    "Vlan1100": "10.242.12.0/26",
    "Vlan1125": "10.241.8.0/27",
    "Vlan1126": "10.241.8.32/27",
    "Vlan1127": "10.241.8.64/27",
    "subnet_count": {
        "/32": 2,
        "/23": 1,
        "/27": 5,
        "/28": 1,
        "/25": 1,
        "/26": 1,
    }
}
```

### <li>  Layer3 Interfaces - List of Objects

```python
>>> l3_interfaces = parser.get_l3_interfaces()
>>>for l3 in l3_interfaces:
        print(l3.name)
        print(l3.ip_address, l3.subnet)
        print("!")

Loopback100
10.241.17.8 10.241.17.8/32
!
Loopback200
10.252.248.1 10.252.248.1/32
!
Loopback202
10.245.0.199 10.245.0.199/32
```
### <li>  Layer3 Interfaces - Json/Dict Format

```python
>>> l3_interfaces = parser.get_l3_interfaces(return_json=True)
>>> print(json.dumps(l3_interfaces, indent=4))
[
    {
        "name": "Loopback100",
        "description": "SWITCH01-SA01 Loopback IP",
        "ip_address": "10.241.17.8",
        "ipv6_address": "2001:DB8::1/64",
        "mask": "255.255.255.255",
        "subnet": "10.241.17.8/32",
        "helpers": null,
        "sec_ip_address": null,
        "sec_mask": null,
        "sec_subnet": null,
        "vrf": "mgt100",
        "state": null,
        "vip": null,
        "children": [
            "description SWITCH01-SA01 Loopback IP",
            "ip vrf forwarding mgt100",
            "ip address 10.241.17.8 255.255.255.255",
            "ipv6 address 2001:DB8::1/64",
            "ip pim sparse-mode",
            "ip ospf 100 area 0"
        ]
    },
]
```

### <li>  Layer3 Interfaces - Custom Key/Value and Regex

```python
>>> l3_interfaces = parser.get_l3_interfaces(ip_pim="ip pim.*")
>>> for i in l3_interfaces:
        print(i.name)
        print(i.ip_pim)
        print("!")

Loopback100
ip pim sparse-mode
!
Loopback200
ip pim sparse-mode
!
```

## <li>  Layer2 `Access` and `Trunk` Interface

###  <li> `Access Port` List of Objects 
 
```python
>>> access_ports = parser.get_l2_access_interfaces()
>>> 
>>> for intf in access_ports:
    print(intf.name)
    print(intf.description)
    print(intf.children)
    print("!")
... 
GigabitEthernet1/1
Data Users
['description Data Users', 'switchport access vlan 200', 'switchport mode access', 'switchport voice vlan 700', 'no logging event power-inline-status', 'ip dhcp snooping information option allow-untrusted']
!
GigabitEthernet1/2
Data Users
['description Data Users', 'switchport access vlan 200', 'switchport mode access', 'switchport voice vlan 700', 'no logging event power-inline-status', 'ip dhcp snooping information option allow-untrusted']
!
```

###  <li> `Access Port` the json/dict format:

```python
>>> access_ports = parser.get_l2_access_interfaces(return_json=True)

>>> print(json.dumps(access_ports, indent=4))

[
    {
        "name": "GigabitEthernet1/1",
        "description": "DATA Users",
        "data_vlan": "200",
        "voice_vlan": "700",
        "state": null,
        "spanning_tree": null,
        "native_vlan": null,
        "children": [
            "description DATA Users",
            "switchport access vlan 200",
            "switchport mode access",
            "switchport voice vlan 700",
            "no logging event power-inline-status",
            "ip dhcp snooping information option allow-untrusted"
        ]
    },
]
```
### <li>  `Access Port` Custom Key/method search

You can use your own custom regex with your own key, this key then become a dynamic method of the class where you can either call it or recieve it as json format. 


```python
>>> access_ports = parser.get_l2_access_interfaces(logging="no loggin.*", return_json=True)

>>> print(json.dumps(access_ports, indent=4))

[
    {
        "name": "GigabitEthernet1/1",
        "description": "SHC-Users",
        "data_vlan": "200",
        "voice_vlan": "700",
        "state": null,
        "spanning_tree": null,
        "native_vlan": null,
        "children": [
            "description SHC-Users",
            "switchport access vlan 200",
            "switchport mode access",
            "switchport voice vlan 700",
            "no logging event power-inline-status",
            "ip dhcp snooping information option allow-untrusted"
        ],
        "logging": "no logging event power-inline-status"                       <<<<< custom Key to find logging command
    },
]
```

### <li> `Trunk Port` List of Objects

```python
>>> trunk_interfaces = parser.get_l2_trunk_interfaces()

>>> for intf in trunk_interfaces:
        print(intf.name)
        print(intf.description)
        print("!")

TenGigabitEthernet5/1
(MP)SWITCH-SD03:Twe1/0/6
!
TenGigabitEthernet5/2
Link to SWITCH-SA01 Te6/2 Decomm
!
TenGigabitEthernet6/1
(MP)SWITCH-SD04:Te1/1
!
```

### <li>  `Trunk Port` Json/Dict format

```python
>>> trunk_interfaces = parser.get_l2_trunk_interfaces(return_json=True)
>>> print(json.dumps(trunk_interfaces, indent=4))

[
    {
        "name": "TenGigabitEthernet5/1",
        "description": "(MP)SWITCH-SD03:Twe1/0/6",
        "allowed_vlans": null,
        "dhcp_snooping": null,
        "dhcp_relay": null,
        "voice_vlan": null,
        "state": null,
        "spanning_tree": null,
        "native_vlan": "256",
        "children": [
            "description (MP)SWITCH-SD03:Twe1/0/6",
            "switchport trunk native vlan 256",
            "switchport trunk allowed vlan 182,256,504,1100,3101,3201,3301,3311,3321,3331",
            "switchport trunk allowed vlan add 3351,3401,3411,3911",
            "switchport mode trunk",
            "load-interval 30",
            "udld port aggressive",
            "service-policy output egress_queueing",
            "ip dhcp snooping trust"
        ],
        "allowed_vlan": "182,256,504,1100,3101,3201,3301,3311,3321,3331"
    },
]
```
### <li> `Trunk Port` Custom Key/method search

```python
>>> access_ports = parser.parser.get_l2_trunk_interfaces(load="load.*", return_json=True)

>>> print(json.dumps(access_ports, indent=4))

    {
        "name": "TenGigabitEthernet5/1",
        "description": "(MP)SWITCH-SD03:Twe1/0/6",
        "allowed_vlans": null,
        "dhcp_snooping": null,
        "dhcp_relay": null,
        "voice_vlan": null,
        "state": null,
        "spanning_tree": null,
        "native_vlan": "256",
        "children": [
            "description (MP)STNMED-LPCH-SD03:Twe1/0/6",
            "switchport trunk native vlan 256",
            "switchport trunk allowed vlan 182,256,504,1100,3101,3201,3301,3311,3321,3331",
            "switchport trunk allowed vlan add 3351,3401,3411,3911",
            "switchport mode trunk",
            "load-interval 30",
            "udld port aggressive",
            "service-policy output egress_queueing",
            "ip dhcp snooping trust"
        ],
        "allowed_vlan": "182,256,504,1100,3101,3201,3301,3311,3321,3331",
        "load": "load-interval 30"                                      <<<<< Custom Key and Value
    },
```


## <li> Static Route Config

**Features**
- Parse Static Routes: Extract and parse static routing commands (ip route entries) from Cisco running configurations.
- Convert to Python Objects: Represent these routes as structured Python objects, enabling programmatic manipulation and easy integration with Python-based tools.
- Export to JSON/Dict: Convert static routing information into JSON or dictionary format, making it straightforward to use in web applications, APIs, or data storage solutions.

#### Example

**Python Object:**
```python

>>> static = parser.get_static_config()
>>> for i in static:
        print(i.subnet, i.name, i.nexthop_ip)

0.0.0.0/0 MC-RS01 10.240.129.3
0.0.0.0/0 NC-RS01 10.240.129.11
10.243.98.32/28 P2P_Subnet 10.243.99.186
```

**Json/Dict:**
```python
static = parser.get_static_config()
[
    {
        "network": "0.0.0.0",
        "mask": "0.0.0.0",
        "nexthop_ip": "10.240.129.3",
        "subnet": "0.0.0.0/0",
        "vrf": "default",
        "name": "MC-RS01",
        "admin_distance": null
    },
]
```


## <li> Dynamic Route Config

### <li> EIGRP 

`eigrp_configs = parser.get_eigrp_config(return_json=True)`

```json
[
    {
        "as_number": 300,
        "network": [
            {
                "network": "10.242.96.240",
                "subnet_mask": "255.255.255.252",
                "wildcard_mask": "0.0.0.3"
            },
            {
                "network": "10.242.97.244",
                "subnet_mask": "255.255.255.252",
                "wildcard_mask": "0.0.0.3"
            },
            {
                "network": "10.242.98.248",
                "subnet_mask": "255.255.255.252",
                "wildcard_mask": "0.0.0.3"
            },
            {
                "network": "10.242.99.252",
                "subnet_mask": "255.255.255.252",
                "wildcard_mask": "0.0.0.3"
            }
        ],
        "vrf": "Global",
        "wild_card_mask": null,
        "subnet": null,
        "has_vrf": true,
        "vrf_count": 8,
        "passive_interface": null,
        "no_passive_interface": null,
        "auto_cost": null,
        "interfaces": null,
        "children": [
            "network 10.242.96.240 0.0.0.3",
            "network 10.242.97.244 0.0.0.3",
            "network 10.242.98.248 0.0.0.3",
            "network 10.242.99.252 0.0.0.3",
            "no passive-interface GigabitEthernet1/9",
            "no passive-interface GigabitEthernet1/10",
            "passive-interface default",
            "no auto-summary"
        ],
        "vrf_children": [
            {
                "vrf": "mgt100",
                "network": [
                    {
                        "network": "10.242.96.240",
                        "subnet_mask": "255.255.255.252",
                        "wildcard_mask": "0.0.0.3"
                    }
                ],
                "wild_card_mask": null,
                "subnet": null,
                "passive_interface": null,
                "no_passive_interface": null,
                "auto_cost": null,
                "interfaces": null,
                "children": [
                    "redistribute bgp 65240 metric 1000 100 255 1 1500 route-map MGT100_EIGRP_REDIST_BGP",
                    "network 10.242.96.240 0.0.0.3",
                    "passive-interface default",
                    "no passive-interface TenGigabitEthernet1/9.3131",
                    "distribute-list route-map route_map_inbound in",
                    "autonomous-system 300",
                    "exit-address-family"
                ]
            },
        ]
    }
    
]
```

### <li> BGP Route Config:

**Example:**
`bgp_configs = parser.get_bgp_config(return_json=True)`

```json
[
    {
        "router_id": "10.240.129.45",
        "vrf_list": [
            "blu300",
        ],
        "vrf": "Global",
        "network": [
            "10.245.49.132/32"
        ],
        "peer_group": [
            {
                "peer_group": {
                    "name": "RR",
                    "remote_as": "65234",
                    "update_source": "Loopback1",
                    "route_map": {
                        "in": "PEER_GROUP_RR_INBOUND",
                        "out": "PEER_GROUP_RR_OUTBOUND"
                    },
                    "neighbors": [
                        {
                            "ip": "10.252.248.251",
                            "description": "RR-Router-01"
                        },
                    ]
                }
            },
        ],
        "neighbors": null,
        "redistribute": [
            {
                "vrf": "Global",
                "protocol": "eigrp 100",
                "route_map": ""
            }
        ],
        "vrf_children": [
            {
                "vrf": "blu300",
                "network": [
                    "10.245.17.132/32"
                ],
                "peer_group": null,
                "neighbors": [
                    {
                        "vrf": "blu300",
                        "ip": "10.245.20.30",
                        "remote_as": "65245",
                        "description": "REMOTE_ROUTER-SA01_BLU300",
                        "update_source": "TenGigabitEthernet10/14.3301",
                        "route_map": {
                            "in": "ALLVRF_BGP_RM_INBOUND",
                            "out": "ALLVRF_BGP_RM_OUTBOUND"
                        }
                    }
                ],
                "redistribute": [
                    {
                        "vrf": "blu300",
                        "protocol": "eigrp 252",
                        "route_map": " BLU300_BGP_REDIST_EIGRP"
                    },
                    {
                        "vrf": "blu300",
                        "protocol": "static",
                        "route_map": " BLU300_BGP_REDIST_STATIC"
                    }
                ],
            }
        ]
    }
]
```

### <li> OSPF Route Config

In ospf, ConfigParser attemps to capture any interfaces that are participating in OSPF as well. 

**Example:**

`ospf = parser.get_ospf_config(return_json=True)`

```json
[
    {
        "process_id": "240",
        "router_id": "10.240.129.45",
        "network": [
            {
                "network": "10.3.3.0",
                "subnet_mask": "255.255.255.0",
                "wildcard_mask": "0.0.0.255",
                "area": "0"
            }
        ],
        "wild_card_mask": null,
        "subnet": null,
        "vrf": null,
        "passive_interface": [
            {
                "interface": "default"
            }
        ],
        "no_passive_interface": [
            {
                "interface": "GigabitEthernet1/0"
            }
        ],
        "auto_cost": "10000",
        "interfaces": [
            {
                "interface": "Loopback0",
                "area": "0",
                "process_id": "240"
            }
        ]
    }
]
```

## <li> Custom Parsing

**Example:**
`custom_search = parser.get_parent_child(parent_regex="aaa.*", child_regex="server.*", return_json=True)`

```json

[
    {
        "parent": "aaa group server tacacs+ ISE_TACACS",
        "children": "server tacacs+ ISE_TACACS"
    }
]
```



## Future Enhancements
I am actively working on expanding the capabilities of this library. Upcoming features include:

- **Spanning-tree, object-groups, crypto PKI, QoS class/policy-maps, ip sla, device-tracking**: next batch of section parsers.
- **IOS-XR router hsrp/vrrp**: XR-style FHRP sections (interface-level HSRP/VRRP is covered for IOS/NXOS).

Stay tuned for these updates and more in future versions of the library!



## Contribution
Contributions are welcome! To contribute:

1. Fork this repository.
2. Create a feature branch.
3. Make your changes and add tests if applicable.
4. Submit a pull request for review.
5. Feel free to open an issue for any bug reports or feature requests.



## License
This project is licensed under the MIT License.
