Skip to content

Commit

Permalink
Add IPv4 secondary address / network parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
mpenning committed Oct 27, 2023
1 parent 1ba6951 commit f3785bf
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 11 deletions.
13 changes: 13 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@
- Summary:
- Insert something here

## Version: 1.9.8

- Released: 2023-10-26
- Summary:
- Add IPv4 secondary addresses / networks to `ciscoconfparse/models_cisco.py`
- Update `README.md` example

## Version: 1.9.7

- Released: 2023-10-26
- Summary:
- Fix `README.md` example

## Version: 1.9.6

- Released: 2023-10-26
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ for ccp_obj in parse.find_objects('^interface'):
# IPv4 netmask object: ipaddress.IPv4Address()
intf_v4masklength = ccp_obj.ipv4_addr_object.masklength

# set() of IPv4 secondary address/prefixlen strings
intf_v4secondary_networks = ccp_obj.ip_secondary_networks

# set() of IPv4 secondary address strings
intf_v4secondary_addresses = ccp_obj.ip_secondary_addresses

# List of HSRP IPv4 addrs from the ciscoconfpasre/models_cisco.py HSRPInterfaceGroup()
intf_hsrp_addresses = [hsrp_grp.ip for hsrp_grp in ccp_obj.hsrp_interfaces]

Expand Down Expand Up @@ -352,10 +358,8 @@ If you already git cloned the repo and want to manually run tests either run wit

```shell
$ cd tests
$ pytest -vvs ./test_CiscoConfParse.py
$ pytest -vvs ./test_*py
...
$ pytest -vvs ./test_Ccp_Util.py
etc...
```

## Editing the Package
Expand Down
68 changes: 60 additions & 8 deletions ciscoconfparse/models_cisco.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ class HSRPInterfaceGroup(BaseCfgLine):
# This method is on HSRPInterfaceGroup()
@logger.catch(reraise=True)
def __init__(self, group, parent):
"""A HSRP Interface Group object"""
super().__init__()
self.feature = "hsrp"
self._group = int(group)
Expand Down Expand Up @@ -217,24 +218,28 @@ def __eq__(self, other):
@property
@logger.catch(reraise=True)
def hsrp_group(self):
"""Return the integer HSRP group number for this HSRP group"""
return int(self._group)

# This method is on HSRPInterfaceGroup()
@property
@logger.catch(reraise=True)
def group(self):
"""Return the integer HSRP group number for this HSRP group"""
return self.hsrp_group

# This method is on HSRPInterfaceGroup()
@property
@logger.catch(reraise=True)
def ip(self):
"""Return the string IPv4 HSRP address for this HSRP group"""
return self.ipv4

# This method is on HSRPInterfaceGroup()
@property
@logger.catch(reraise=True)
def ipv4(self):
"""Return the string IPv4 HSRP address for this HSRP group"""
## NOTE: I have no intention of checking self.is_shutdown here
## People should be able to check the sanity of interfaces
## before they put them into production
Expand All @@ -250,6 +255,7 @@ def ipv4(self):
@property
@logger.catch(reraise=True)
def has_ipv6(self):
"""Return a boolean for whether this interface is configured with an IPv6 HSRP address"""
## NOTE: I have no intention of checking self.is_shutdown here
## People should be able to check the sanity of interfaces
## before they put them into production
Expand Down Expand Up @@ -280,23 +286,25 @@ def interface_name(self):
@property
@logger.catch(reraise=True)
def interface_tracking(self):
return self.get_hsrp_tracked_interfaces()
"""Return a list of HSRP TrackingInterface() objects for this HSRPInterfaceGroup()"""
return self.get_hsrp_tracking_interfaces()

# This method is on HSRPInterfaceGroup()
@logger.catch(reraise=True)
def get_glbp_tracked_interfaces(self):
def get_glbp_tracking_interfaces(self):
"""Get a list of unique GLBP tracked interfaces. This may never be supported by HSRPInterfaceGroup()"""
raise NotImplementedError()

# This method is on HSRPInterfaceGroup()
@logger.catch(reraise=True)
def get_vrrp_tracked_interfaces(self):
def get_vrrp_tracking_interfaces(self):
"""Get a list of unique VRRP tracked interfaces. This may never be supported by HSRPInterfaceGroup()"""
raise NotImplementedError()

# This method is on HSRPInterfaceGroup()
@logger.catch(reraise=True)
def get_hsrp_tracked_interfaces(self):
def get_hsrp_tracking_interfaces(self):
"""Return a list of HSRP TrackingInterface() interfaces for this HSRPInterfaceGroup()"""
######################################################################
# Find decrement and interface
######################################################################
Expand Down Expand Up @@ -1290,32 +1298,76 @@ def ipv4_addr_object(self):
logger.warning(f"intf='{self.name}' ipv4_addr='{self.ipv4_addr}' ipv4_netmask='{self.ipv4_netmask}'")
return self.default_ipv4_addr_object

# This method is on BaseIOSIntfLine()
@property
@logger.catch(reraise=True)
def has_ip_secondary(self):
r"""Return an boolean for whether this interface has IPv4 secondary addresses"""
retval = self.re_match_iter_typed(
r"^\s*ip\s+address\s+\S+\s+\S+\s+(?P<secondary>secondary)\s*$",
groupdict={"secondary": bool},
default=False
)
return retval["secondary"]

# This method is on BaseIOSIntfLine()
@property
@logger.catch(reraise=True)
def ip_secondary_addresses(self):
r"""Return a set of IPv4 secondary addresses (as strings)"""
retval = set()
for obj in self.parent.all_children:
_gg = obj.re_match_iter_typed(
r"^\s*ip\s+address\s+(?P<secondary>\S+\s+\S+)\s+secondary\s*$",
groupdict={"secondary": IPv4Obj},
default=False
)
if _gg["secondary"]:
retval.add(str(_gg["secondary"].ip))
return retval

# This method is on BaseIOSIntfLine()
@property
@logger.catch(reraise=True)
def ip_secondary_networks(self):
r"""Return a set of IPv4 secondary addresses / prefixlen"""
retval = set()
for obj in self.parent.all_children:
_gg = obj.re_match_iter_typed(
r"^\s*ip\s+address\s+(?P<secondary>\S+\s+\S+)\s+secondary\s*$",
groupdict={"secondary": IPv4Obj},
default=False
)
if _gg["secondary"]:
retval.add(f"{_gg['secondary'].ip}/{_gg['secondary'].prefixlen}")
return retval

# This method is on BaseIOSIntfLine()
@property
@logger.catch(reraise=True)
def has_no_ipv4(self):
r"""Return an ccp_util.IPv4Obj object representing the subnet on this interface; if there is no address, return ccp_util.IPv4Obj('0.0.0.1/32')"""
r"""Return an ccp_util.IPv4Obj object representing the subnet on this interface; if there is no address, return ccp_util.IPv4Obj()"""
return self.ipv4_addr_object == IPv4Obj()

# This method is on BaseIOSIntfLine()
@property
@logger.catch(reraise=True)
def ip(self):
r"""Return an ccp_util.IPv4Obj object representing the IPv4 address on this interface; if there is no address, return ccp_util.IPv4Obj('0.0.0.1/32')"""
r"""Return an ccp_util.IPv4Obj object representing the IPv4 address on this interface; if there is no address, return ccp_util.IPv4Obj()"""
return self.ipv4_addr_object

# This method is on BaseIOSIntfLine()
@property
@logger.catch(reraise=True)
def ipv4(self):
r"""Return an ccp_util.IPv4Obj object representing the IPv4 address on this interface; if there is no address, return ccp_util.IPv4Obj('0.0.0.1/32')"""
r"""Return an ccp_util.IPv4Obj object representing the IPv4 address on this interface; if there is no address, return ccp_util.IPv4Obj()"""
return self.ipv4_addr_object

# This method is on BaseIOSIntfLine()
@property
@logger.catch(reraise=True)
def ipv4_network_object(self):
r"""Return an ccp_util.IPv4Obj object representing the subnet on this interface; if there is no address, return ccp_util.IPv4Obj('0.0.0.1/32')"""
r"""Return an ccp_util.IPv4Obj object representing the subnet on this interface; if there is no address, return ccp_util.IPv4Obj()"""
return self.ip_network_object

# This method is on BaseIOSIntfLine()
Expand Down
44 changes: 44 additions & 0 deletions tests/test_Models_Cisco.py
Original file line number Diff line number Diff line change
Expand Up @@ -1822,6 +1822,49 @@ def testVal_IOSRouteLine_12():
assert 1 == obj.admin_distance
assert "" == obj.tag

###
### ------ IPv4 secondary addresses
###

def testVal_IOSIntfLine_ip_secondary01():
"""Test that a single secondary IPv4 address is detected"""
config = """!
interface Vlan21
ip address 172.16.1.1 255.255.255.0
ip address 172.16.21.1 255.255.255.0 secondary
no ip proxy-arp
!
"""
cfg = CiscoConfParse(config.splitlines(), factory=True)
intf_obj = cfg.find_objects("^interface")[0]
assert intf_obj.has_ip_secondary is True

def testVal_IOSIntfLine_ip_secondary02():
"""Test that a multiple secondary IPv4 addresses are detected"""
config = """!
interface Vlan21
ip address 172.16.1.1 255.255.255.0
ip address 172.16.21.1 255.255.255.0 secondary
ip address 172.16.31.1 255.255.255.0 secondary
no ip proxy-arp
!
"""
cfg = CiscoConfParse(config.splitlines(), factory=True)
intf_obj = cfg.find_objects("^interface")[0]
assert intf_obj.has_ip_secondary is True

def testVal_IOSIntfLine_ip_secondary03():
"""Test that a missing secondary IPv4 addresses are detected"""
config = """!
interface Vlan21
ip address 172.16.1.1 255.255.255.0
no ip proxy-arp
!
"""
cfg = CiscoConfParse(config.splitlines(), factory=True)
intf_obj = cfg.find_objects("^interface")[0]
assert intf_obj.has_ip_secondary is False


###
### ------ IPv4 Helper-Addresses --------
Expand Down Expand Up @@ -1936,3 +1979,4 @@ def testVal_IOSAaaGroupServerLine_02():
assert set(["192.0.2.10", "192.0.2.11"]) == obj.server_private
assert "VRF_001" == obj.vrf
assert "FastEthernet0/48" == obj.source_interface

0 comments on commit f3785bf

Please sign in to comment.