From 20037cb7070b852686342156aefbeeb8a1e27251 Mon Sep 17 00:00:00 2001 From: Johan van den Dorpe Date: Fri, 24 Nov 2023 11:12:07 +0000 Subject: [PATCH] Update adva parser to deplicate parent lines i.e. configure snmp --- docs/dev/include_parser_list.md | 1 + netutils/config/parser.py | 123 ++++++++++++++++++ .../adva_embedded_os/adva_full_received.py | 38 ++++++ .../base/adva_embedded_os/adva_full_sent.txt | 57 ++++++++ 4 files changed, 219 insertions(+) create mode 100644 tests/unit/mock/config/parser/base/adva_embedded_os/adva_full_received.py create mode 100644 tests/unit/mock/config/parser/base/adva_embedded_os/adva_full_sent.txt diff --git a/docs/dev/include_parser_list.md b/docs/dev/include_parser_list.md index 721a2255..49a1337c 100644 --- a/docs/dev/include_parser_list.md +++ b/docs/dev/include_parser_list.md @@ -1,5 +1,6 @@ | OS Name | Parser Class | | ---------- | ------ | +| adva_embedded_os | netutils.config.parser.AdvaConfigParser | | arista_eos | netutils.config.parser.EOSConfigParser | | aruba_aoscx | netutils.config.parser.ArubaConfigParser | | bigip_f5 | netutils.config.parser.F5ConfigParser | diff --git a/netutils/config/parser.py b/netutils/config/parser.py index d4a34713..dd4a83ea 100644 --- a/netutils/config/parser.py +++ b/netutils/config/parser.py @@ -1583,7 +1583,130 @@ class AdvaConfigParser(BaseSpaceConfigParser): comment_chars: t.List[str] = ["#", "home", "back"] banner_start: t.List[str] = [] + + def __init__(self, config: str): + """Create ConfigParser Object. + + Args: + config: The config text to parse. + """ + self.config = config + self._config: t.Optional[str] = None + self._current_parents: t.Tuple[str, ...] = () + self.generator_config = (line for line in self.config_lines_only.splitlines()) + self.config_lines: t.List[ConfigLine] = [] + self.all_config_lines: t.List[ConfigLine] = [] + self.build_config_relationship() + @property def banner_end(self) -> str: """Demarcate End of Banner char(s).""" raise NotImplementedError("Adva Embedded OS platform doesn't have a banner.") + + def _update_config_lines(self, config_line: str) -> None: + """Add a ``ConfigLine`` object to ``self.config_lines``. + + Args: + config_line: The current config line being evaluated. + + Returns: + None + """ + entry = ConfigLine(config_line, self._current_parents) + self.all_config_lines.append(entry) + + # Deduplicate lines - parent sections i.e. "configure snmp" should be recorded only once + if entry not in self.config_lines: + self.config_lines.append(entry) + + def _build_nested_config(self, line: str) -> t.Optional[str]: + """Handle building child config sections. + + Args: + line: A configuration line from the configuration text. + + Returns: + The next top-level configuration line in the configuration text or None when the last line of configuration + text is a nested configuration line. + + Raises: + IndexError: When the number of parents does not match the expected deindent level. + """ + self._update_config_lines(line) + for line in self.generator_config: + if not line[0].isspace(): + self._current_parents = () + self.indent_level = 0 + return line + + spaces = self.get_leading_space_count(line) + if spaces == self.indent_level: + pass + elif spaces > self.indent_level: + previous_config = self.all_config_lines[-1] + self._current_parents += (previous_config.config_line,) + else: + self._current_parents = self._remove_parents(line, spaces) + + if spaces != self.indent_level: + self.indent_level = spaces + + if self.is_banner_start(line): + banner_line = self._build_banner(line) + if banner_line is None or not banner_line[0].isspace(): + self._current_parents = () + self.indent_level = 0 + return banner_line + line = banner_line + + self._update_config_lines(line) + return None + + + def build_config_relationship(self) -> t.List[ConfigLine]: + r"""Parse text tree of config lines and their parents. + + Examples: + >>> config = ( + ... "interface Ethernet1/1\n" + ... " vlan 10\n" + ... " no shutdown\n" + ... "interface Ethernet1/2\n" + ... " shutdown\n" + ... ) + >>> config_tree = BaseSpaceConfigParser(config) + >>> config_tree.build_config_relationship() == \ + ... [ + ... ConfigLine(config_line='interface Ethernet1/1', parents=()), + ... ConfigLine(config_line=' vlan 10', parents=('interface Ethernet1/1',)), + ... ConfigLine(config_line=' no shutdown', parents=('interface Ethernet1/1',)), + ... ConfigLine(config_line='interface Ethernet1/2', parents=(),), + ... ConfigLine(config_line=' shutdown', parents=('interface Ethernet1/2',)) + ... ] + True + """ + for line in self.generator_config: + if not line[0].isspace(): + self._current_parents = () + if self.is_banner_start(line): + line = self._build_banner(line) # type: ignore + else: + previous_config = self.all_config_lines[-1] + self._current_parents = (previous_config.config_line,) + self.indent_level = self.get_leading_space_count(line) + if not self.is_banner_start(line): + line = self._build_nested_config(line) # type: ignore + else: + line = self._build_banner(line) # type: ignore + if line is not None and line[0].isspace(): + line = self._build_nested_config(line) # type: ignore + else: + self._current_parents = () + + if line is None: + break + elif self.is_banner_start(line): + line = self._build_banner(line) # type: ignore + + self._update_config_lines(line) + return self.config_lines \ No newline at end of file diff --git a/tests/unit/mock/config/parser/base/adva_embedded_os/adva_full_received.py b/tests/unit/mock/config/parser/base/adva_embedded_os/adva_full_received.py new file mode 100644 index 00000000..39ee942d --- /dev/null +++ b/tests/unit/mock/config/parser/base/adva_embedded_os/adva_full_received.py @@ -0,0 +1,38 @@ +from netutils.config.parser import ConfigLine + +data = [ + ConfigLine(config_line='configure snmp', parents=()), + ConfigLine(config_line=' delete community "private"', parents=('configure snmp',)), + ConfigLine(config_line=' delete community "public"', parents=('configure snmp',)), + ConfigLine(config_line=' add community "secret" readwrite', parents=('configure snmp',)), + ConfigLine(config_line=' add target-params "Params" snmpv2c snmpv2c "secret" no-auth', parents=('configure snmp',)), + ConfigLine(config_line=' add target-address "target1" "10.0.0.2:161" ipv4 60 3 "info" "Params" enabled', parents=('configure snmp',)), + ConfigLine(config_line=' configure target-address "target1" bulk-traps-control disabled', parents=('configure snmp',)), + ConfigLine(config_line=' add target-address "target2" "10.0.0.5:161" ipv4 60 3 "info" "Params" enabled', parents=('configure snmp',)), + ConfigLine(config_line=' configure target-address "target2" bulk-traps-control disabled', parents=('configure snmp',)), + ConfigLine(config_line='network-element ne-1', parents=()), + ConfigLine(config_line=' configure nte ntexg108-1-1-1', parents=('network-element ne-1',)), + ConfigLine(config_line=' configure access-port access-1-1-1-3', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1')), + ConfigLine(config_line=' admin-state unassigned', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' service-type evpl', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' alias "CUST-HANDOFF"', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' port-vlan-id 1-0', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' a2n-push-port-vid disabled', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' priority-mapping-profile prio_map_profile-1', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-00 pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-01 pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-02 pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-03 pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-04 pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-05 pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-06 pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-07 pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-08 pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-09 pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-0a pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-0b pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-0c pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-0d pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-0e pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), + ConfigLine(config_line=' cpd-filter 01-80-c2-00-00-0f pass-thru', parents=('network-element ne-1', ' configure nte ntexg108-1-1-1', ' configure access-port access-1-1-1-3')), +] diff --git a/tests/unit/mock/config/parser/base/adva_embedded_os/adva_full_sent.txt b/tests/unit/mock/config/parser/base/adva_embedded_os/adva_full_sent.txt new file mode 100644 index 00000000..9111f429 --- /dev/null +++ b/tests/unit/mock/config/parser/base/adva_embedded_os/adva_full_sent.txt @@ -0,0 +1,57 @@ +home +configure snmp + delete community "private" + +home +configure snmp + delete community "public" + +home +configure snmp + add community "secret" readwrite + +home +configure snmp + add target-params "Params" snmpv2c snmpv2c "secret" no-auth + +home +configure snmp + add target-address "target1" "10.0.0.2:161" ipv4 60 3 "info" "Params" enabled + configure target-address "target1" bulk-traps-control disabled + +home +configure snmp + add target-address "target2" "10.0.0.5:161" ipv4 60 3 "info" "Params" enabled + configure target-address "target2" bulk-traps-control disabled + +home +network-element ne-1 + configure nte ntexg108-1-1-1 + configure access-port access-1-1-1-3 + admin-state unassigned + service-type evpl + alias "CUST-HANDOFF" + port-vlan-id 1-0 + a2n-push-port-vid disabled + priority-mapping-profile prio_map_profile-1 + +home +network-element ne-1 + configure nte ntexg108-1-1-1 + configure access-port access-1-1-1-3 + cpd-filter 01-80-c2-00-00-00 pass-thru + cpd-filter 01-80-c2-00-00-01 pass-thru + cpd-filter 01-80-c2-00-00-02 pass-thru + cpd-filter 01-80-c2-00-00-03 pass-thru + cpd-filter 01-80-c2-00-00-04 pass-thru + cpd-filter 01-80-c2-00-00-05 pass-thru + cpd-filter 01-80-c2-00-00-06 pass-thru + cpd-filter 01-80-c2-00-00-07 pass-thru + cpd-filter 01-80-c2-00-00-08 pass-thru + cpd-filter 01-80-c2-00-00-09 pass-thru + cpd-filter 01-80-c2-00-00-0a pass-thru + cpd-filter 01-80-c2-00-00-0b pass-thru + cpd-filter 01-80-c2-00-00-0c pass-thru + cpd-filter 01-80-c2-00-00-0d pass-thru + cpd-filter 01-80-c2-00-00-0e pass-thru + cpd-filter 01-80-c2-00-00-0f pass-thru \ No newline at end of file