From c622bd4a1c23310fb74cdf2db910c23687974aad Mon Sep 17 00:00:00 2001 From: mpenning Date: Wed, 20 Dec 2023 06:59:14 -0600 Subject: [PATCH] Roll version number --- CHANGES.md | 6 ++ ciscoconfparse2/ciscoconfparse2.py | 157 ++++++++++++++++++----------- ciscoconfparse2/models_asa.py | 4 +- ciscoconfparse2/models_junos.py | 104 +++++++++---------- pyproject.toml | 2 +- sphinx-doc/api_CiscoConfParse.rst | 3 + tests/test_Models_Asa.py | 8 +- 7 files changed, 165 insertions(+), 119 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0777001..b427153 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,12 @@ - Summary: - Insert something here +## Version: 0.3.3 + +- Released: 2023-12-20 +- Summary: + - Documentation updates + ## Version: 0.3.2 - Released: 2023-12-19 diff --git a/ciscoconfparse2/ciscoconfparse2.py b/ciscoconfparse2/ciscoconfparse2.py index 5aacf49..b67d50e 100644 --- a/ciscoconfparse2/ciscoconfparse2.py +++ b/ciscoconfparse2/ciscoconfparse2.py @@ -62,7 +62,7 @@ from ciscoconfparse2.models_asa import ASAName from ciscoconfparse2.models_asa import ASAAclLine -# from ciscoconfparse2.models_junos import JunosIntfLine +from ciscoconfparse2.models_junos import JunosIntfLine from ciscoconfparse2.models_junos import JunosCfgLine from ciscoconfparse2.ccp_abc import BaseCfgLine @@ -115,7 +115,7 @@ ########################################################################## # JunosIntfLine is rather broken; JunosCfgLine should be enough ########################################################################## - # JunosIntfLine, + JunosIntfLine, JunosCfgLine, # JunosCfgLine MUST be last ] @@ -732,8 +732,32 @@ def __init__( :return: A :py:class:`ConfigList` instance. :rtype: :py:class:`ConfigList` - Attributes: - initlist (Union[List[str],tuple[str, ...]]): A sequence of text configuration statements + Attributes + ---------- + initlist : list, tuple + A sequence of text configuration statements + comment_delimiters : list + A sequence of text comment delimiters + factory : bool + Whether to derive beta-quality configuration attributes for Cisco configurations + ignore_blank_lines : bool + Whether to ignore blank lines in the configuration + syntax : str + One of 'ios', 'nxos', 'asa', 'iosxr', or 'junos' + auto_commit : bool + Whether to automatically commit changes to the configuration + debug : int + Debug level of this configuration instance + ccp_ref : CiscoConfParse + A reference to the CiscoConfParse instance which owns this ConfigList + dna : str + A string representing the type of CiscoConfParse instance this is + current_checkpoint : int + The value of the current checkpoint; this will be updated with each ConfigList change + commit_checkpoint : int + The value of the saved checkpoint; this will only be updated when a commit() is called + data : BaseCfgLine + An internal sequence of BaseCfgLine instances used to maintain the contents of this python UserList subclass """ # Use this with UserList() instead of super() @@ -892,21 +916,21 @@ def __len__(self) -> int: # This method is on ConfigList() @logger.catch(reraise=True) - def __getitem__(self, ii) -> BaseCfgLine: - if isinstance(ii, slice): - return self.__class__(self.data[ii]) + def __getitem__(self, value) -> BaseCfgLine: + if isinstance(value, slice): + return self.__class__(self.data[value]) else: - return self.data[ii] + return self.data[value] # This method is on ConfigList() @logger.catch(reraise=True) - def __setitem__(self, ii, val) -> None: - self.data[ii] = val + def __setitem__(self, idx, value) -> None: + self.data[idx] = value # This method is on ConfigList() @logger.catch(reraise=True) - def __delitem__(self, ii) -> None: - del self.data[ii] + def __delitem__(self, idx) -> None: + del self.data[idx] self.data = self.bootstrap(self.text, debug=self.debug) # This method is on ConfigList() @@ -996,10 +1020,11 @@ def __getattribute__(self, arg) -> Any: return ccp_method # This method is on ConfigList() - def get_checkpoint(self): - """Return an integer representing a unique version of this ConfigList() and its contents. - - This should be a classmethod to ensure that the results are cachable via memoization. + @logger.catch(reraise=True) + def get_checkpoint(self) -> int: + """ + :return: An integer representing a unique version of this ConfigList() and its contents. + :rtype: int """ total = 0 for idx, obj in enumerate(self.data): @@ -1046,8 +1071,6 @@ def commit(self) -> bool: logger.critical(error) raise eee - - # This method is on ConfigList() @property @logger.catch(reraise=True) @@ -1867,8 +1890,11 @@ def bootstrap(self, text_list: List[str]=None, debug: int=0) -> List[BaseCfgLine # This method is on ConfigList() @logger.catch(reraise=True) - def _build_banner_re_ios(self): - """Return a banner regexp for IOS (and at this point, NXOS).""" + def _build_banner_re_ios(self) -> re.Pattern: + """ + :return: A banner regexp for IOS (and at this point, NXOS). + :rtype: re.Pattern + """ banner_str = { "login", "motd", @@ -1880,14 +1906,19 @@ def _build_banner_re_ios(self): banner_all = [r"^(set\s+)*banner\s+{}".format(ii) for ii in banner_str] banner_all.append( "aaa authentication fail-message", - ) # Github issue #76 + ) # original ciscoconfparse Github issue #76 banner_re = re.compile("|".join(banner_all)) return banner_re # This method is on ConfigList() @logger.catch(reraise=True) - def _add_child_to_parent(self, _list, idx, indent, parentobj, childobj): + def _add_child_to_parent(self, _list, idx, indent, parentobj, childobj) -> None: + """ + Add the child object to the parent object; assign the parent + object to the child object. Finally set the ``child_indent`` attribute + on the parent object. + """ # parentobj could be None when trying to add a child that should not # have a parent if parentobj is None: @@ -1926,45 +1957,52 @@ def _add_child_to_parent(self, _list, idx, indent, parentobj, childobj): # This method is on ConfigList() @logger.catch(reraise=True) - def iter_with_comments(self, begin_index=0): + def iter_with_comments(self, begin_index: int=0) -> BaseCfgLine: + """ + :return: The BaseCfgLine instance at or greater than ``begin_index`` + :rtype: BaseCfgLine + """ for idx, obj in enumerate(self.data): if idx >= begin_index: yield obj # This method is on ConfigList() @logger.catch(reraise=True) - def iter_no_comments(self, begin_index=0): + def iter_no_comments(self, begin_index: int=0) -> BaseCfgLine: + """ + :return: The BaseCfgLine instance at or greater than ``begin_index`` if it is not a comment + :rtype: BaseCfgLine + """ for idx, obj in enumerate(self.data): if (idx >= begin_index) and (not obj.is_comment): yield obj # This method is on ConfigList() @logger.catch(reraise=True) - def reassign_linenums(self): + def reassign_linenums(self) -> None: + """Renumber the configuration line numbers""" # Call this after any insertion or deletion for idx, obj in enumerate(self.data): obj.linenum = idx # This method is on ConfigList() - @ property + @property @logger.catch(reraise=True) - def all_parents(self): + def all_parents(self) -> List[BaseCfgLine]: + """ + :return: A sequence of BaseCfgLine instances representing all parents in this ConfigList + :rtype: List[BaseCfgLine] + """ return [obj for obj in self.data if obj.has_children] - # This method is on ConfigList() - @ property - @logger.catch(reraise=True) - def last_index(self): - return self.__len__() - 1 - ########################################################################## # Special syntax='asa' methods... ########################################################################## # This method was on ASAConfigList(); now tentatively on ConfigList() - @ property + @property @logger.catch(reraise=True) - def names(self): + def asa_object_group_names(self) -> Dict[str,str]: """Return a dictionary of name to address mappings""" if self.syntax != "asa": raise RequirementFailure() @@ -1978,9 +2016,9 @@ def names(self): return retval # This method was on ASAConfigList(); now tentatively on ConfigList() - @ property + @property @logger.catch(reraise=True) - def object_group_network(self): + def asa_object_group_network(self) -> Dict[str,BaseCfgLine]: """Return a dictionary of name to object-group network mappings""" if self.syntax != "asa": raise RequirementFailure() @@ -1993,9 +2031,9 @@ def object_group_network(self): return retval # This method was on ASAConfigList(); now tentatively on ConfigList() - @ property + @property @logger.catch(reraise=True) - def access_list(self): + def asa_access_list(self) -> Dict[str,BaseCfgLine]: """Return a dictionary of ACL name to ACE (list) mappings""" if self.syntax != "asa": raise RequirementFailure() @@ -2051,6 +2089,15 @@ def __init__( """ Initialize CiscoConfParse. + .. note:: + + ``comment_delimiters`` always assumes the delimiter is one character wide. + + .. note:: + + ``ignore_blank_lines`` changes the original ciscoconfparse default value. + + :param config: A list of configuration lines or the filepath to the configuration. :type config: Union[str,List[str],tuple[str, ...]] :param syntax: The configuration type, default to 'ios'; it must be one of: 'ios', 'nxos', 'iosxr', 'asa', 'junos'. Use 'junos' for any brace-delimited network configuration (including F5, Palo Alto, etc...). @@ -2086,14 +2133,6 @@ def __init__( :return: A CiscoConfParse object :rtype: :py:class:`~ciscoconfparse2.CiscoConfParse` - .. note:: - - ``comment_delimiters`` always assumes the delimiter is one character wide. - - .. note:: - - ``ignore_blank_lines`` changes the original ciscoconfparse default value. - This example illustrates how to parse a simple Cisco IOS configuration with :class:`~ciscoconfparse2.CiscoConfParse` into a variable called ``parse``. This example also illustrates what the ``config_objs`` @@ -2116,28 +2155,26 @@ def __init__( ['logging trap debugging', 'logging 172.28.26.15'] >>> - """ - - if False: - """ - Attributes - ---------- - + Attributes + ---------- comment_delimiters : list A list of strings containing the comment-delimiters. Default: ["!"] - config_objs : :class:`~ciscoconfparse2.ConfigList` + objs : :class:`ConfigList` + An alias for ``config_objs`` + config_objs : :class:`ConfigList` A custom list, which contains all parsed :class:`~models_cisco.IOSCfgLine` instances. debug : int An int to enable verbose config parsing debugs. Default 0. ioscfg : list A list of text configuration strings - objs - An alias for `config_objs` openargs : dict Returns a dictionary of valid arguments for `open()` (these change based on the running python version). syntax : str A string holding the configuration type. Default: 'ios'. Must be one of: 'ios', 'nxos', 'iosxr', 'asa', 'junos'. Use 'junos' for any brace-delimited network configuration (including F5, Palo Alto, etc...). - """ + + + """ + if syntax not in ALL_VALID_SYNTAX: error = f"{syntax} is not a valid syntax." logger.error(error) @@ -2514,7 +2551,7 @@ def get_text(self) -> List[str]: .. warning:: - The original ciscoconfparse ``ioscfg`` property has been renamed to ``get_text()``. + The original ciscoconfparse ``ioscfg`@property has been renamed to ``get_text()``. """ return [ii.text for ii in self.config_objs] @@ -2624,7 +2661,7 @@ def find_object_branches( reverse: bool=False, debug: int=0, ) -> List[List[BaseCfgLine]]: - r"""Iterate over a tuple of regular expressions in `branchspec` and return matching objects in a list of lists (consider it similar to a table of matching config objects). `branchspec` expects to start at some ancestor and walk through the nested object hierarchy (with no limit on depth). + r"""Iterate over a tuple of regular expression strings in `branchspec` and return matching objects in a list of lists (consider it similar to a table of matching config objects). `branchspec` expects to start at some ancestor and walk through the nested object hierarchy (with no limit on depth). Previous CiscoConfParse() methods only handled a single parent regex and single child regex (such as :func:`~ciscoconfparse2.CiscoConfParse.find_objects`). diff --git a/ciscoconfparse2/models_asa.py b/ciscoconfparse2/models_asa.py index db9e571..f27a6c0 100644 --- a/ciscoconfparse2/models_asa.py +++ b/ciscoconfparse2/models_asa.py @@ -798,7 +798,7 @@ def network_strings(self): """Return a list of strings which represent the address space allowed by this object-group""" retval = list() - names = self.confobj.names + names = self.confobj.asa_object_group_names for obj in self.children: ## Parse out 'object-group ...' and 'group-object' lines... @@ -830,7 +830,7 @@ def network_strings(self): ) ) - group_nets = self.confobj.object_group_network.get(groupobject, None) + group_nets = self.confobj.asa_object_group_network.get(groupobject, None) if group_nets is None: raise ValueError( "FATAL: Cannot find group-object named {}".format(self.name) diff --git a/ciscoconfparse2/models_junos.py b/ciscoconfparse2/models_junos.py index 2c882e1..ab01d11 100644 --- a/ciscoconfparse2/models_junos.py +++ b/ciscoconfparse2/models_junos.py @@ -29,9 +29,8 @@ import ipaddress import re -if False: - from loguru import logger - import attrs +from loguru import logger +import attrs from ciscoconfparse2.ccp_abc import BaseCfgLine from ciscoconfparse2.ccp_util import IPv4Obj, IPv6Obj @@ -89,7 +88,7 @@ class JunosCfgLine(BaseCfgLine): """ # This method is on JunosCfgLine() - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def __init__(self, *args, **kwargs): r"""Accept an Junos line number and initialize family relationship attributes""" @@ -97,19 +96,19 @@ def __init__(self, *args, **kwargs): # This method is on JunosCfgLine() @classmethod - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_object_for(cls, all_lines, line, re=re): ## Default object, for now return True # This method is on JunosCfgLine() @classmethod - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_object_for_interface(cls, all_lines, line, re=re): return False @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def name(self): """If this is an interface, return a name such as 'ge-0/0/0 unit 0', otherwise return None""" return self.intf_name @@ -132,7 +131,7 @@ def intf_name(self): # This method is on JunosCfgLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_intf(self): # Includes subinterfaces / JunOS units r"""Returns a boolean (True or False) to answer whether this :class:`~models_junos.JunosCfgLine` is an interface; subinterfaces @@ -190,7 +189,7 @@ def is_intf(self): # This method is on JunosCfgLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_subintf(self): r"""Returns a boolean (True or False) to answer whether this :class:`~models_junos.JunosCfgLine` is a subinterface. @@ -233,7 +232,7 @@ def is_subintf(self): # This method is on JunosCfgLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_switchport(self): """Return True if this is a switchport interface""" if self.is_intf is True: @@ -244,7 +243,7 @@ def is_switchport(self): # This method is on JunosCfgLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_virtual_intf(self): intf_regex = ( r"^interface\s+(Loopback|Tunnel|Dialer|Virtual-Template|Port-Channel)" @@ -255,7 +254,7 @@ def is_virtual_intf(self): # This method is on JunosCfgLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_loopback_intf(self): r"""Returns a boolean (True or False) to answer whether this :class:`~models_junos.JunosCfgLine` is a loopback interface. @@ -293,7 +292,7 @@ def is_loopback_intf(self): # This method is on JunosCfgLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_ethernet_intf(self): r"""Returns a boolean (True or False) to answer whether this :class:`~models_junos.JunosCfgLine` is an ethernet interface. @@ -352,14 +351,14 @@ def is_ethernet_intf(self): class BaseJunosIntfLine(JunosCfgLine): # This method is on BaseJunosIntfLine() - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.ifindex = None # Optional, for user use self.default_ipv4_addr_object = IPv4Obj("0.0.0.1/32", strict=False) # This method is on BaseJunosIntfLine() - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def __repr__(self): parent_str = "" if self.parent: @@ -379,12 +378,13 @@ def __repr__(self): # This method is on BaseJunosIntfLine() @classmethod - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_object_for_interface(cls, all_lines, line, re=re): is_interfaces = False intf_idx = -1 parents = [] + _intf_level = -1 # This is the indent of the first interface line for lidx, lline in enumerate(all_lines): _llindent = len(lline) - len(lline.strip()) @@ -418,20 +418,20 @@ def is_object_for_interface(cls, all_lines, line, re=re): return False # This method is on BaseJunosIntfLine() - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def reset(self, commit=True): # Insert build_reset_string() before this line... self.insert_before(self.build_reset_string(), commit=commit) # This method is on BaseJunosIntfLine() - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def build_reset_string(self): # Junos interfaces are defaulted like this... return "default " + self.text # This method is on BaseJunosIntfLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def ipv4_addr_object(self): r"""Return a ccp_util.IPv4Obj object representing the address on this logical interface; if there is no address, return IPv4Obj()""" ###################################################################### @@ -453,7 +453,7 @@ def ipv4_addr_object(self): # This method is on BaseJunosIntfLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def ipv6_addr_object(self): r"""Return a ccp_util.IPv6Obj object representing the address on this logical interface; if there is no address, return IPv6Obj()""" ###################################################################### @@ -475,7 +475,7 @@ def ipv6_addr_object(self): # This method is on JunosIntfLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_switchport(self): for obj in self.parent.all_children: if obj.parent.text.split()[0] == "unit" and obj.text.split()[0:2] == ["family", "ethernet-switching"]: @@ -484,7 +484,7 @@ def is_switchport(self): # This method is on BaseJunosIntfLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def verbose(self): if not self.is_switchport: return ( @@ -516,13 +516,13 @@ def verbose(self): # This method is on BaseJunosIntfLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def name(self): raise NotImplementedError() # This method is on BaseJunosIntfLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def port(self): r"""Return the interface's port number @@ -561,7 +561,7 @@ def port(self): # This method is on BaseJunosIntfLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def port_type(self): r"""Return Loopback, ATM, GigabitEthernet, Virtual-Template, etc... @@ -601,7 +601,7 @@ def port_type(self): # This method is on BaseJunosIntfLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def ordinal_list(self): r"""Return a tuple of numbers representing card, slot, port for this interface. If you call ordinal_list on GigabitEthernet2/25.100, you'll get this python tuple of integers: (2, 25). If you call ordinal_list on GigabitEthernet2/0/25.100 you'll get this python list of integers: (2, 0, 25). This method strips all subinterface information in the returned value. @@ -652,7 +652,7 @@ def ordinal_list(self): # This method is on BaseJunosIntfLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def description(self): """Return the current interface description string. @@ -664,7 +664,7 @@ def description(self): # This method is on BaseJunosIntfLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def manual_bandwidth(self): retval = self.re_match_iter_typed( r"^\s*bandwidth\s+(\d+)$", result_type=int, default=0 @@ -673,7 +673,7 @@ def manual_bandwidth(self): # This method is on BaseJunosIntfLine() @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def manual_delay(self): retval = self.re_match_iter_typed( r"^\s*delay\s+(\d+)$", result_type=int, default=0 @@ -689,7 +689,7 @@ def manual_delay(self): class JunosIntfLine(BaseJunosIntfLine): # This method is on JunosIntfLine() - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def __init__(self, *args, **kwargs): r"""Accept a JunOS interface number and initialize family relationship attributes @@ -703,7 +703,7 @@ def __init__(self, *args, **kwargs): # This method is on JunosIntfLine() @classmethod - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_object_for(cls, all_lines, line, re=re): return cls.is_object_for_interface(all_lines, line, re=re) @@ -713,11 +713,11 @@ def is_object_for(cls, all_lines, line, re=re): class BaseJunosRouteLine(BaseCfgLine): - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def __repr__(self): return "<{} # {} '{}' info: '{}'>".format( self.classname, @@ -727,7 +727,7 @@ def __repr__(self): ) @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def routeinfo(self): ### Route information for the repr string if self.tracking_object_name: @@ -742,43 +742,43 @@ def routeinfo(self): return self.nexthop_str + " AD: " + str(self.admin_distance) @classmethod - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_object_for(cls, all_lines, line, re=re): return False @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def vrf(self): raise NotImplementedError @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def address_family(self): ## ipv4, ipv6, etc raise NotImplementedError @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def network(self): raise NotImplementedError @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def netmask(self): raise NotImplementedError @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def admin_distance(self): raise NotImplementedError @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def nexthop_str(self): raise NotImplementedError @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def tracking_object_name(self): raise NotImplementedError @@ -790,7 +790,7 @@ def tracking_object_name(self): #@attrs.define(repr=False) class JunosRouteLine(BaseJunosRouteLine): - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super(JunosRouteLine, self).__init__(*args, **kwargs) if "ipv6" in self.text: @@ -799,14 +799,14 @@ def __init__(self, *args, **kwargs): self.feature = "ip route" @classmethod - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def is_object_for(cls, all_lines, line, re=re): if re.search(r"^(ip|ipv6)\s+route\s+\S", line): return True return False @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def vrf(self): retval = self.re_match_typed( r"^(ip|ipv6)\s+route\s+(vrf\s+)*(\S+)", group=3, result_type=str, default="" @@ -814,7 +814,7 @@ def vrf(self): return retval @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def address_family(self): ## ipv4, ipv6, etc retval = self.re_match_typed( @@ -823,7 +823,7 @@ def address_family(self): return retval @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def network(self): if self.address_family == "ip": retval = self.re_match_typed( @@ -839,7 +839,7 @@ def network(self): return retval @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def netmask(self): if self.address_family == "ip": retval = self.re_match_typed( @@ -858,7 +858,7 @@ def netmask(self): return retval @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def network_object(self): try: if self.address_family == "ip": @@ -869,7 +869,7 @@ def network_object(self): return None @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def nexthop_str(self): if self.address_family == "ip": retval = self.re_match_typed( @@ -888,13 +888,13 @@ def nexthop_str(self): return retval @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def admin_distance(self): retval = self.re_match_typed(r"(\d+)$", group=1, result_type=int, default=1) return retval @property - #@logger.catch(reraise=True) + @logger.catch(reraise=True) def tracking_object_name(self): retval = self.re_match_typed( r"^ip(v6)*\s+route\s+.+?track\s+(\S+)", group=2, result_type=str, default="" diff --git a/pyproject.toml b/pyproject.toml index 1d69a9a..c66a0f5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ requires-python = ">=3.8" [tool.poetry] name = "ciscoconfparse2" -version = "0.3.2" +version = "0.3.3" description = "Parse, Audit, Query, Build, and Modify Cisco IOS-style and JunOS-style configs" license = "GPL-3.0-only" authors = [ diff --git a/sphinx-doc/api_CiscoConfParse.rst b/sphinx-doc/api_CiscoConfParse.rst index be7cdfb..ba55850 100644 --- a/sphinx-doc/api_CiscoConfParse.rst +++ b/sphinx-doc/api_CiscoConfParse.rst @@ -29,6 +29,9 @@ ciscoconfparse2.CiscoConfParse Object :undoc-members: :inherited-members: +ciscoconfparse2.Diff Object +--------------------------- + .. autoclass:: ciscoconfparse2.Diff :members: :undoc-members: diff --git a/tests/test_Models_Asa.py b/tests/test_Models_Asa.py index 9884be8..b38892c 100755 --- a/tests/test_Models_Asa.py +++ b/tests/test_Models_Asa.py @@ -47,7 +47,7 @@ def testVal_Access_List(parse_a01_factory): result_correct = {} - assert len(parse_a01_factory.objs.access_list["INSIDE_in"]) == 4 + assert len(parse_a01_factory.objs.asa_access_list["INSIDE_in"]) == 4 def testParse_asa_factory(config_a02): @@ -55,7 +55,7 @@ def testParse_asa_factory(config_a02): assert (parse is not None) -def testVal_Names(parse_a01, parse_a01_factory): +def testVal_Asa_Object_Group_Names(parse_a01, parse_a01_factory): result_correct = { "dmzsrv00": "1.1.3.10", "dmzsrv01": "1.1.3.11", @@ -63,8 +63,8 @@ def testVal_Names(parse_a01, parse_a01_factory): "dmzsrv03": "1.1.3.13", "loghost01": "1.1.2.20", } - assert parse_a01.config_objs.names == result_correct - assert parse_a01_factory.config_objs.names == result_correct + assert parse_a01.config_objs.asa_object_group_names == result_correct + assert parse_a01_factory.config_objs.asa_object_group_names == result_correct def testVal_object_group_network_01():