diff --git a/CHANGES.md b/CHANGES.md index 45afeec..782f978 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,18 @@ - Summary: - Insert something here +## Version: 1.9.44 + +- Released: 2023-11-29 +- Summary: + - Documentation updates + - Remove `ciscoconfparse.find_blocks()` + - Remove `ciscoconfparse.find_children()` + - Remove `ciscoconfparse.find_all_children()` + - Remove `ciscoconfparse.insert_before()` + - Remove `ciscoconfparse.insert_after()` + - Remove `ciscoconfparse.insert_after_child()` + ## Version: 1.9.43 - Released: 2023-11-29 diff --git a/ciscoconfparse/ciscoconfparse.py b/ciscoconfparse/ciscoconfparse.py index 8a1d0aa..59f9c6b 100644 --- a/ciscoconfparse/ciscoconfparse.py +++ b/ciscoconfparse/ciscoconfparse.py @@ -1497,262 +1497,6 @@ def find_lines(self, linespec, exactmatch=False, ignore_ws=False): filter(re.compile("^%s$" % linespec).search, self.ioscfg), ) - # This method is on CiscoConfParse() - @logger.catch(reraise=True) - def find_children(self, linespec, exactmatch=False, ignore_ws=False): - """Returns the parents matching the linespec, and their immediate - children. This method is different than :meth:`find_all_children`, - because :meth:`find_all_children` finds children of children. - :meth:`find_children` only finds immediate children. - - Parameters - ---------- - linespec : str - Text regular expression for the line to be matched - exactmatch : bool - boolean that controls whether partial matches are valid - ignore_ws : bool - boolean that controls whether whitespace is ignored - - Returns - ------- - list - A list of matching configuration lines - - Examples - -------- - - >>> from ciscoconfparse import CiscoConfParse - >>> config = ['username ddclient password 7 107D3D232342041E3A', - ... 'archive', - ... ' log config', - ... ' logging enable', - ... ' hidekeys', - ... ' path ftp://ns.foo.com//tftpboot/Foo-archive', - ... '!', - ... ] - >>> p = CiscoConfParse(config=config) - >>> p.find_children('^archive') - ['archive', ' log config', ' path ftp://ns.foo.com//tftpboot/Foo-archive'] - >>> - """ - if ignore_ws is True: - linespec = build_space_tolerant_regex(linespec) - - if exactmatch is False: - parentobjs = self._find_line_OBJ(linespec) - else: - parentobjs = self._find_line_OBJ("^%s$" % linespec) - - allobjs = set() - for parent in parentobjs: - if parent.has_children is True: - allobjs.update(set(parent.children)) - allobjs.add(parent) - - return [ii.text for ii in sorted(allobjs)] - - # This method is on CiscoConfParse() - @logger.catch(reraise=True) - def find_all_children(self, linespec, exactmatch=False, ignore_ws=False): - """Returns the parents matching the linespec, and all their children. - This method is different than :meth:`find_children`, because - :meth:`find_all_children` finds children of children. - :meth:`find_children` only finds immediate children. - - Parameters - ---------- - linespec : str - Text regular expression for the line to be matched - exactmatch : bool - boolean that controls whether partial matches are valid - ignore_ws : bool - boolean that controls whether whitespace is ignored - - Returns - ------- - list - A list of matching configuration lines - - Examples - -------- - Suppose you are interested in finding all `archive` statements in - the following configuration... - - .. code:: - - username ddclient password 7 107D3D232342041E3A - archive - log config - logging enable - hidekeys - path ftp://ns.foo.com//tftpboot/Foo-archive - ! - - Using the config above, we expect to find the following config lines... - - .. code:: - - archive - log config - logging enable - hidekeys - path ftp://ns.foo.com//tftpboot/Foo-archive - - We would accomplish this by querying `find_all_children('^archive')`... - - >>> from ciscoconfparse import CiscoConfParse - >>> config = ['username ddclient password 7 107D3D232342041E3A', - ... 'archive', - ... ' log config', - ... ' logging enable', - ... ' hidekeys', - ... ' path ftp://ns.foo.com//tftpboot/Foo-archive', - ... '!', - ... ] - >>> p = CiscoConfParse(config=config) - >>> p.find_all_children('^archive') - ['archive', ' log config', ' logging enable', ' hidekeys', ' path ftp://ns.foo.com//tftpboot/Foo-archive'] - >>> - """ - - if ignore_ws: - linespec = build_space_tolerant_regex(linespec) - - if exactmatch is False: - parentobjs = self._find_line_OBJ(linespec) - else: - parentobjs = self._find_line_OBJ("^%s$" % linespec) - - allobjs = set() - for parent in parentobjs: - allobjs.add(parent) - allobjs.update(set(parent.all_children)) - return [ii.text for ii in sorted(allobjs)] - - # This method is on CiscoConfParse() - @logger.catch(reraise=True) - def find_blocks(self, linespec, exactmatch=False, ignore_ws=False): - """Find all siblings matching the linespec, then find all parents of - those siblings. Return a list of config lines sorted by line number, - lowest first. Note: any children of the siblings should NOT be - returned. - - Parameters - ---------- - linespec : str - Text regular expression for the line to be matched - exactmatch : bool - boolean that controls whether partial matches are valid - ignore_ws : bool - boolean that controls whether whitespace is ignored - - Returns - ------- - list - A list of matching configuration lines - - - Examples - -------- - This example finds `bandwidth percent` statements in following config, - the siblings of those `bandwidth percent` statements, as well - as the parent configuration statements required to access them. - - .. code:: - - ! - policy-map EXTERNAL_CBWFQ - class IP_PREC_HIGH - priority percent 10 - police cir percent 10 - conform-action transmit - exceed-action drop - class IP_PREC_MEDIUM - bandwidth percent 50 - queue-limit 100 - class class-default - bandwidth percent 40 - queue-limit 100 - policy-map SHAPE_HEIR - class ALL - shape average 630000 - service-policy EXTERNAL_CBWFQ - ! - - The following config lines should be returned: - - .. code:: - - policy-map EXTERNAL_CBWFQ - class IP_PREC_MEDIUM - bandwidth percent 50 - queue-limit 100 - class class-default - bandwidth percent 40 - queue-limit 100 - - We do this by quering `find_blocks('bandwidth percent')`... - - .. code-block:: python - :emphasize-lines: 22,25 - - >>> from ciscoconfparse import CiscoConfParse - >>> config = ['!', - ... 'policy-map EXTERNAL_CBWFQ', - ... ' class IP_PREC_HIGH', - ... ' priority percent 10', - ... ' police cir percent 10', - ... ' conform-action transmit', - ... ' exceed-action drop', - ... ' class IP_PREC_MEDIUM', - ... ' bandwidth percent 50', - ... ' queue-limit 100', - ... ' class class-default', - ... ' bandwidth percent 40', - ... ' queue-limit 100', - ... 'policy-map SHAPE_HEIR', - ... ' class ALL', - ... ' shape average 630000', - ... ' service-policy EXTERNAL_CBWFQ', - ... '!', - ... ] - >>> p = CiscoConfParse(config=config) - >>> p.find_blocks('bandwidth percent') - ['policy-map EXTERNAL_CBWFQ', ' class IP_PREC_MEDIUM', ' bandwidth percent 50', ' queue-limit 100', ' class class-default', ' bandwidth percent 40', ' queue-limit 100'] - >>> - >>> p.find_blocks(' class class-default') - ['policy-map EXTERNAL_CBWFQ', ' class IP_PREC_HIGH', ' class IP_PREC_MEDIUM', ' class class-default'] - >>> - - """ - tmp = set() - - if ignore_ws: - linespec = build_space_tolerant_regex(linespec) - - # Find line objects maching the spec - if exactmatch is False: - objs = self._find_line_OBJ(linespec) - else: - objs = self._find_line_OBJ("^%s$" % linespec) - - for obj in objs: - tmp.add(obj) - # Find the siblings of this line - sib_objs = self._find_sibling_OBJ(obj) - for sib_obj in sib_objs: - tmp.add(sib_obj) - - # Find the parents for everything - pobjs = set() - for lineobject in tmp: - for pobj in lineobject.all_parents: - pobjs.add(pobj) - tmp.update(pobjs) - - return [ii.text for ii in sorted(tmp)] - # This method is on CiscoConfParse() @logger.catch(reraise=True) def find_objects_w_child(self, parentspec, childspec, ignore_ws=False, recurse=False): @@ -2484,290 +2228,6 @@ def has_line_with(self, linespec): ) return bool(matching_conftext) - # This method is on CiscoConfParse() - @ logger.catch(reraise=True) - def insert_before( - self, - exist_val=None, - new_val=None, - exactmatch=False, - ignore_ws=False, - atomic=False, - new_val_indent=-1, - **kwargs - ): - r""" - Find all :class:`~models_cisco.IOSCfgLine` objects whose text - matches ``exist_val``, and insert ``new_val`` before those line - objects. - - If ``new_val_indent`` >= 0, then ``new_val`` will be inserted with - the requested indent regardless of any existing indent on ``new_val``. - - Parameters - ---------- - exist_val : str - Text regular expression for the line to be matched - new_val : str - text to be inserted before all occurances of exist_val - exactmatch : bool - if exactmatch is True, do not match on substrings - ignore_ws : bool - if ignore_ws is True, ignore whitespace differences - atomic : bool - if atomic is True, this change will be commited - new_val_ident : int - integer indent for new_val - - Examples - -------- - - .. code-block:: python - :emphasize-lines: 15 - - >>> from ciscoconfparse import CiscoConfParse - >>> config = ['!', - ... 'interface FastEthernet 0/1', - ... ' description Test intf to CloudFlare', - ... ' ip address 192.0.2.1 255.255.255.252', - ... ' no ip unreachables', - ... '!', - ... 'interface FastEthernet 0/2', - ... ' description ProxySG model 8100', - ... ' ip address 192.0.2.5 255.255.255.252', - ... ' no ip unreachables', - ... '!', - ... ] - >>> p = CiscoConfParse(config=config) - >>> p.insert_before(r"interface\s+FastEthernet\s+0\/2", "default interface FastEthernet 0/2", new_val_indent=0) - """ - - ###################################################################### - # - # CiscoConfParse().insert_before is a wrapper for CiscoConfParse().ConfigObjs.insert_before() - # - # Named parameter migration warnings... - # - `linespec` is now called exist_val - # - `insertstr` is now called new_val - ###################################################################### - if exist_val is None: - error = f"exist_val must not be {type(new_val)}" - logger.critical(error) - raise InvalidParameters(error) - - if new_val is None: - error = f"Cannot insert new_val {type(new_val)}" - logger.critical(error) - raise InvalidParameters(error) - - if kwargs.get("linespec", "") != "": - exist_val = kwargs.get("linespec") - logger.info( - "The parameter named `linespec` is deprecated. Please use `exist_val` instead", - ) - if kwargs.get("insertstr", "") != "": - new_val = kwargs.get("insertstr") - logger.info( - "The parameter named `insertstr` is deprecated. Please use `new_val` instead", - ) - - err_exist_val = "FATAL: exist_val:'%s' must be a non-empty string" % exist_val - if not isinstance(exist_val, str) or exist_val == "": - raise ValueError(err_exist_val) - err_new_val = "FATAL: new_val:'%s' must be a string" % new_val - if not isinstance(new_val, str) or new_val == "": - raise ValueError(err_new_val) - - enforce_valid_types(exactmatch, (bool,), "exactmatch parameter must be a bool.") - enforce_valid_types(ignore_ws, (bool,), "ignore_ws parameter must be a bool.") - enforce_valid_types( - new_val_indent, (int,), "new_val_indent parameter must be a int." - ) - - objs = self.find_objects( - linespec=exist_val, exactmatch=exactmatch, ignore_ws=ignore_ws - ) - for _obj in objs: - - exist_indent = len(_obj._text) - len(_obj._text.lstrip()) - if exist_indent != _obj.indent: - raise RequirementFailure() - - if new_val_indent >= 0: - # Forces an indent on ``new_val``... - self.ConfigObjs.insert_before( - exist_val, new_val_indent * " " + new_val.lstrip() - ) - else: - # Does not force an indent on ``new_val``... - self.ConfigObjs.insert_before(exist_val, new_val) - - if atomic is True: - self.atomic() - return [ii.text for ii in sorted(objs)] - - # This method is on CiscoConfParse() - @ logger.catch(reraise=True) - def insert_after( - self, - exist_val=None, - new_val=None, - exactmatch=False, - ignore_ws=False, - atomic=False, - new_val_indent=-1, - **kwargs - ): - r""" - Find all :class:`~models_cisco.IOSCfgLine` objects whose text - matches ``exist_val``, and insert ``new_val`` after those line - objects. - - If ``new_val_indent`` >= 0, then ``new_val`` will be inserted with - the requested indent regardless of any existing indent on ``new_val``. - - Parameters - ---------- - exist_val : str - Text regular expression for the line to be matched - new_val : str - text to be inserted after all occurances of exist_val - exactmatch : bool - if exactmatch is True, do not match on substrings - ignore_ws : bool - if ignore_ws is True, ignore whitespace differences - atomic : bool - if atomic is True, this change will be commited - new_val_ident : int - integer indent for new_val - - Examples - -------- - - .. code-block:: python - :emphasize-lines: 15 - - >>> from ciscoconfparse import CiscoConfParse - >>> config = ['!', - ... 'interface FastEthernet 0/1', - ... ' description Test intf to CloudFlare', - ... ' ip address 192.0.2.1 255.255.255.252', - ... ' no ip unreachables', - ... '!', - ... 'interface FastEthernet 0/2', - ... ' description ProxySG model 8100', - ... ' ip address 192.0.2.5 255.255.255.252', - ... ' no ip unreachables', - ... '!', - ... ] - >>> p = CiscoConfParse(config=config) - >>> p.insert_after(r"interface\s+FastEthernet\s+0\/2", "no ip proxy-arp", new_val_indent=1) - """ - - ###################################################################### - # - # CiscoConfParse().insert_after is a wrapper for CiscoConfParse().ConfigObjs.insert_after() - # - # - # Named parameter migration warnings... - # - `linespec` is now called exist_val - # - `insertstr` is now called new_val - ###################################################################### - if exist_val is None: - error = f"exist_val must not be {type(new_val)}" - logger.critical(error) - raise InvalidParameters(error) - - if new_val is None: - error = f"Cannot insert new_val {type(new_val)}" - logger.critical(error) - raise InvalidParameters(error) - - if kwargs.get("linespec", "") != "": - exist_val = kwargs.get("linespec") - logger.info( - "The parameter named `linespec` is deprecated. Please use `exist_val` instead", - ) - if kwargs.get("insertstr", "") != "": - new_val = kwargs.get("insertstr") - logger.info( - "The parameter named `insertstr` is deprecated. Please use `new_val` instead", - ) - - err_exist_val = "FATAL: exist_val:'%s' must be a string" % exist_val - err_new_val = "FATAL: new_val:'%s' must be a string" % new_val - enforce_valid_types(exist_val, (str,), err_exist_val) - enforce_valid_types(new_val, (str,), err_new_val) - enforce_valid_types(exactmatch, (bool,), "exactmatch must be a bool") - enforce_valid_types(ignore_ws, (bool,), "ignore_ws must be a bool") - enforce_valid_types(new_val_indent, (int,), "new_val_indent must be a int") - - objs = self.find_objects( - linespec=exist_val, exactmatch=exactmatch, ignore_ws=ignore_ws - ) - for _obj in objs: - - exist_indent = len(_obj._text) - len(_obj._text.lstrip()) - if exist_indent != _obj.indent: - raise RequirementFailure() - - if new_val_indent >= 0: - # Forces an indent on ``new_val``... - self.ConfigObjs.insert_after( - exist_val, new_val_indent * " " + new_val.lstrip() - ) - else: - # Does not force an indent on ``new_val``... - self.ConfigObjs.insert_after(exist_val, new_val) - - if atomic is True: - self.atomic() - return [ii.text for ii in sorted(objs)] - - # This method is on CiscoConfParse() - @ logger.catch(reraise=True) - def insert_after_child( - self, - parentspec, - childspec, - insertstr=None, - exactmatch=False, - excludespec=None, - ignore_ws=False, - atomic=False, - ): - """ - Find all :class:`~models_cisco.IOSCfgLine` objects whose text - matches ``linespec`` and have a child matching ``childspec``, and - insert an :class:`~models_cisco.IOSCfgLine` object for ``insertstr`` - after those child objects. - """ - if insertstr is None: - error = f"Cannot insert `insertstr` {type(insertstr)}" - logger.critical(error) - raise InvalidParameters(error) - - retval = list() - for pobj in self._find_line_OBJ(parentspec, exactmatch=exactmatch): - if excludespec and re.search(excludespec, pobj.text): - # Exclude replacements on pobj lines which match excludespec - continue - for cobj in pobj.children: - if excludespec and re.search(excludespec, cobj.text): - # Exclude replacements on pobj lines which match excludespec - continue - elif re.search(childspec, cobj.text): - retval.append( - self.ConfigObjs.insert_after( - cobj, - insertstr, - atomic=atomic, - ), - ) - else: - pass - return retval - # This method is on CiscoConfParse() @ logger.catch(reraise=True) def delete_lines(self, linespec, exactmatch=False, ignore_ws=False): diff --git a/tests/test_CiscoConfParse.py b/tests/test_CiscoConfParse.py index cefa824..d6a9a3a 100644 --- a/tests/test_CiscoConfParse.py +++ b/tests/test_CiscoConfParse.py @@ -1260,95 +1260,6 @@ def testValues_find_lines(parse_c01): assert correct_result == test_result -def testValues_find_children(parse_c01): - """Test whether find_children() works correctly.""" - c01_pmap_children = [ - "policy-map QOS_1", - " class GOLD", - " class SILVER", - " class BRONZE", - ] - - # The linespec below will match the substring, b/c exactmatch is False - args = {"linespec": "policy-map", "exactmatch": False} - test_result_01 = parse_c01.find_children(**args) - # Check if the test_result is correct... - assert test_result_01 == c01_pmap_children - - # The linespec below will not match the substring, b/c exactmatch is True - args = {"linespec": "policy-map", "exactmatch": True} - test_result_02 = parse_c01.find_children(**args) - # Check if the test_result is correct... - assert test_result_02 == [] - - -def testValues_find_all_children01(parse_c01): - ## test find_all_chidren - c01_pmap_all_children = [ - "policy-map QOS_1", - " class GOLD", - " priority percent 10", - " class SILVER", - " bandwidth 30", - " random-detect", - " class BRONZE", - " random-detect", - ] - - find_all_children_Values = ( - ({"linespec": "policy-map", "exactmatch": False}, c01_pmap_all_children), - ({"linespec": "policy-map", "exactmatch": True}, []), - ) - - for args, correct_result in find_all_children_Values: - test_result = parse_c01.find_all_children(**args) - assert correct_result == test_result - - -def testValues_find_all_chidren02(): - """Ensure we don't need a comment at the end of a """ - """ parent / child block to identify the end of the family""" - CONFIG = [ - "thing1", - " foo", - " bar", - " 100", - " 200", - " 300", - " 400", - "thing2", - ] - correct_result = [ - "thing1", - " foo", - " bar", - " 100", - " 200", - " 300", - " 400", - ] - cfg = CiscoConfParse(CONFIG) - test_result = cfg.find_all_children("^thing1") - assert correct_result == test_result - - -def testValues_find_blocks(parse_c01): - correct_result = [ - "banner login ^C", - "This is a router, and you cannot have it.", - "Log off now while you still can type. I break the fingers", - "of all tresspassers.", - "^C", - ] - - test_result = parse_c01.find_blocks("tresspasser") - assert correct_result == test_result - - -def testValues_list_before_01(): - """Ensure that CiscoConfParse() raises an error on parsing empty config lists.""" - - def testValues_list_insert_before_01(): """test whether we can insert list elements""" c01 = [