From 9f075653e07543aa433d674a99f09aeee0a3cab6 Mon Sep 17 00:00:00 2001 From: mpenning Date: Sat, 28 Oct 2023 08:20:44 -0500 Subject: [PATCH] Add more CiscoRange() fixes --- ciscoconfparse/ccp_util.py | 81 ++++++++++++++++++++++---------------- tests/test_Ccp_Util.py | 39 +++++++++++++++++- 2 files changed, 85 insertions(+), 35 deletions(-) diff --git a/ciscoconfparse/ccp_util.py b/ciscoconfparse/ccp_util.py index c45b6b63..3ee837ff 100644 --- a/ciscoconfparse/ccp_util.py +++ b/ciscoconfparse/ccp_util.py @@ -3196,8 +3196,6 @@ def parse_single_interface(self, interface_name=None, debug=False): intf_short = self.parse_intf_short(re_intf_short=re_intf_short) _prefix = intf_short["prefix"] - if False: - _slot_card_port_subinterface_channel = intf_short["slot_card_port_subinterface_channel"] _sep1 = intf_short["sep1"] _sep2 = intf_short["sep1"] _slot = intf_short["slot"] @@ -3213,8 +3211,6 @@ def parse_single_interface(self, interface_name=None, debug=False): intf_long = self.parse_intf_long(re_intf_long=re_intf_long) _prefix = intf_long["prefix"] - if False: - _slot_card_port_subinterface_channel = intf_long["slot_card_port_subinterface_channel"] _sep1 = intf_long["sep1"] _sep2 = intf_long["sep1"] _slot = intf_long["slot"] @@ -3399,7 +3395,10 @@ def parse_intf_long(self, re_intf_long=None, debug=False): _slot = groupdict_slot_card_port["slot"] _card = groupdict_slot_card_port["card"] _port = groupdict_slot_card_port["port"] - _interface_class = self._interface_class + try: + _interface_class = re.split(r"\s+", groupdict_intf_long["slot_card_port_subinterface_channel"])[1] + except: + _interface_class = None # Handle Ethernet1/48, where 48 is initially assigned to # _card (should be port) @@ -3945,6 +3944,7 @@ def parse_integers(self, text, debug=False): # Append a whole range of interfaces... begin_ordinal = int(_csv_part.split("-")[0].strip()) end_ordinal = int(_csv_part.split("-")[1].strip()) + end_ordinal = int("".join(filter(str.isdigit, _csv_part.split("-")[1].strip()))) if debug is True: logger.info(f"CiscoRange(text={text}, debug=True) : end_ordinal={end_ordinal}") else: @@ -4005,9 +4005,10 @@ def parse_cisco_interfaces(self, text, debug=False): if debug is True: logger.info(f" CiscoRange() idx: {idx} for --> _csv_part: {_csv_part} <--") - ############################################################## + + ################################################################## # Build base instances of begin_obj and this_obj - ############################################################## + ################################################################## if idx == 0: # Set the begin_obj... begin_obj = CiscoInterface(_csv_part.split("-")[0], debug=debug) @@ -4015,17 +4016,33 @@ def parse_cisco_interfaces(self, text, debug=False): self.this_obj = CiscoInterface(interface_dict=begin_obj.as_dict()) intf_dict = begin_obj.as_dict() - ############################################################## + ############################################################## + # Manually add interface class because begin_obj won't see + # it by default: 'multipoint' in 'Serial1/0-5 multipoint' + ############################################################## + if "".join(filter(str.isdigit, text.split()[-1])) == '': + # if there are no digits in the last word, that's an + # interface_class... + _interface_class = text.split()[-1] + else: + _interface_class = None + # Rebuild begin_obj with the interface_class + if isinstance(_interface_class, str): + intf_dict["interface_class"] = _interface_class + begin_obj = CiscoInterface(interface_dict=intf_dict) + self.begin_obj = begin_obj + + ################################################################## # this_obj will also be modified in the large per # attribute if-clauses, below - ############################################################## + ################################################################## # WAS DEEPCOPY self.this_obj = copy.deepcopy(begin_obj) - ############################################################## + ################################################################## # Walk all possible attributes to find which target_attribute # we're iterating on... - ############################################################## + ################################################################## iterate_attribute = None for potential_iter_attr in ['channel', 'subinterface', 'port', 'card', 'slot']: if isinstance(getattr(begin_obj, potential_iter_attr), int): @@ -4072,7 +4089,9 @@ def parse_cisco_interfaces(self, text, debug=False): # Append a whole range of interfaces... obj = CiscoInterface(_csv_part.split("-")[0].strip(), debug=debug) begin_ordinal = getattr(obj, self.iterate_attribute) - end_ordinal = int(_csv_part.split("-")[1].strip()) + # parse end_ordinal from 'Eth1/2-4 multipoint' + # ref: https://stackoverflow.com/a/1450900 + end_ordinal = int("".join(filter(str.isdigit, _csv_part.split("-")[1].strip()))) if debug is True: logger.info(f"CiscoRange(text={text}, debug=True) : end_ordinal={end_ordinal}") else: @@ -4393,28 +4412,24 @@ def append(self, val, sort=True, ignore_errors=False, debug=False): @logger.catch(reraise=True) def insert(self, idx, val, sort=True): """CiscoRange().insert() is disabled because it currently generates a stackoverflow. Use CiscoRange().append() instead.""" - # I have to use a bizarre way to deprecate CiscoRange().insert() - # due to the way DeepSource analyzes the code... - if idx != -3.14159265359: - error = "CiscoRange().insert() is disabled because it currently generates a stackoverflow." - logger.critical(error) - raise NotImplementedError(error) - else: - # Insert at the end of the list with new_last_list_idx = len(self._list) - if val in self._list: - raise DuplicateMember(val) - # WAS DEEPCOPY - new_list = copy.deepcopy(self._list) + # This function currently generates a stackoverflow... + raise NotImplementedError() - #pragma warning disable S2190 - new_list = new_list.insert(int(idx), val) - #pragma warning restore S2190 - if sort is True: - retval = self.attribute_sort(new_list, attribute="sort_list", reverse=False) - else: - retval = new_list - self._list = retval - return self + # Insert at the end of the list with new_last_list_idx = len(self._list) + if val in self._list: + raise DuplicateMember(val) + # WAS DEEPCOPY + new_list = copy.deepcopy(self._list) + + #pragma warning disable S2190 + new_list = new_list.insert(int(idx), val) + #pragma warning restore S2190 + if sort is True: + retval = self.attribute_sort(new_list, attribute="sort_list", reverse=False) + else: + retval = new_list + self._list = retval + return self # This method is on CiscoRange() diff --git a/tests/test_Ccp_Util.py b/tests/test_Ccp_Util.py index 028e9b56..3844f1be 100755 --- a/tests/test_Ccp_Util.py +++ b/tests/test_Ccp_Util.py @@ -880,8 +880,43 @@ def test_CiscoRange_15(): uut_str = "Eth 2/1/1,2,3-5" assert CiscoRange(uut_str).as_set(result_type=str) == result_correct +def test_CiscoRange_16(): + """Basic interface port range test""" + result_correct = {"Eth7", "Eth8", "Eth9"} + uut_str = "Eth 7-9" + assert CiscoRange(uut_str).as_set(result_type=str) == result_correct + +def test_CiscoRange_17(): + """Basic interface slot and port range test""" + result_correct = {"Eth1/7", "Eth1/8", "Eth1/9"} + uut_str = "Eth 1/7-9" + assert CiscoRange(uut_str).as_set(result_type=str) == result_correct def test_CiscoRange_18(): + """Basic interface slot, card, and port range test""" + result_correct = {"Eth1/2/7", "Eth1/2/8", "Eth1/2/9"} + uut_str = "Eth 1/2/7-9" + assert CiscoRange(uut_str).as_set(result_type=str) == result_correct + +def test_CiscoRange_19(): + """Basic interface slot, card, port, and subinterface range test""" + result_correct = {"Eth1/2/3.7", "Eth1/2/3.8", "Eth1/2/3.9"} + uut_str = "Eth 1/2/3.7-9" + assert CiscoRange(uut_str).as_set(result_type=str) == result_correct + +def test_CiscoRange_20(): + """Basic interface slot, card, port, subinterface and channel range test""" + result_correct = {"Eth1/2/3.4:7", "Eth1/2/3.4:8", "Eth1/2/3.4:9"} + uut_str = "Eth 1/2/3.4:7-9" + assert CiscoRange(uut_str).as_set(result_type=str) == result_correct + +def test_CiscoRange_21(): + """Basic interface slot, card, port, subinterface, channel and interface_class range test""" + result_correct = {"Eth1/2/3.4:7 multipoint", "Eth1/2/3.4:8 multipoint", "Eth1/2/3.4:9 multipoint"} + uut_str = "Eth 1/2/3.4:7-9 multipoint" + assert CiscoRange(uut_str).as_set(result_type=str) == result_correct + +def test_CiscoRange_22(): """Parse a string with a common prefix on all of the CiscoRange() inputs""" result_correct = { "Eth1/1", "Eth1/10", "Eth1/12", "Eth1/13", "Eth1/14", @@ -892,7 +927,7 @@ def test_CiscoRange_18(): assert CiscoRange(uut_str).as_set(result_type=str) == result_correct -def test_CiscoRange_19(): +def test_CiscoRange_23(): """Check that the exact results are correct for CiscoRange().as_set() with a redundant input ('Eth1/1')""" result_correct = { CiscoInterface("Eth1/1"), @@ -906,7 +941,7 @@ def test_CiscoRange_19(): # CiscoRange(text="foo", result_type=None) returns CiscoInterface() instances... assert CiscoRange(uut_str, result_type=None).as_set(result_type=None) == result_correct -def test_CiscoRange_20(): +def test_CiscoRange_24(): """Check that the exact results are correct for CiscoRange().as_list() with a redundant input ('Eth1/1')""" result_correct = [ CiscoInterface("Eth1/1"),