diff --git a/netutils/config/parser.py b/netutils/config/parser.py index 3ee3f496..2681e551 100644 --- a/netutils/config/parser.py +++ b/netutils/config/parser.py @@ -1,4 +1,5 @@ """Parsers for different network operating systems.""" + # pylint: disable=no-member,super-with-arguments,invalid-overridden-method,raise-missing-from,invalid-overridden-method,inconsistent-return-statements,super-with-arguments,redefined-argument-from-local,no-else-break,useless-super-delegation,too-many-lines import re @@ -95,6 +96,8 @@ def is_banner_start(self, line: str) -> bool: True if line starts banner, else False. """ for banner_start in self.banner_start: + if not line: + return False if line.lstrip().startswith(banner_start): return True return False @@ -330,6 +333,9 @@ def build_config_relationship(self) -> t.List[ConfigLine]: break elif self.is_banner_start(line): line = self._build_banner(line) # type: ignore + # line can potentially be another banner start therefore we do a secondary check. + if self.is_banner_start(line): + line = self._build_banner(line) # type: ignore self._update_config_lines(line) return self.config_lines @@ -551,6 +557,9 @@ def _build_banner(self, config_line: str) -> t.Optional[str]: def is_banner_one_line(config_line: str) -> bool: """Determine if all banner config is on one line.""" _, delimeter, banner = config_line.partition("^C") + # if the banner is the delimeter is a single line empty banner. e.g banner motd ^C^C which ios allows. + if banner == "^C": + return True # Based on NXOS configs, the banner delimeter is ignored until another char is used banner_config_start = banner.lstrip(delimeter) if delimeter not in banner_config_start: diff --git a/tests/unit/mock/config/compliance/feature_compliance/ios_dual_banner_feature.py b/tests/unit/mock/config/compliance/feature_compliance/ios_dual_banner_feature.py index 6aa27755..24faf808 100755 --- a/tests/unit/mock/config/compliance/feature_compliance/ios_dual_banner_feature.py +++ b/tests/unit/mock/config/compliance/feature_compliance/ios_dual_banner_feature.py @@ -1,2 +1,2 @@ -feature = {"name": "exec banner", "ordered": False, "section": ["banner"]} +feature = {"name": "dual banner", "ordered": False, "section": ["banner"]} network_os = "cisco_ios" diff --git a/tests/unit/mock/config/compliance/feature_compliance/ios_dual_banner_motd_feature.py b/tests/unit/mock/config/compliance/feature_compliance/ios_dual_banner_motd_feature.py index cc99abc6..74a7e8d8 100644 --- a/tests/unit/mock/config/compliance/feature_compliance/ios_dual_banner_motd_feature.py +++ b/tests/unit/mock/config/compliance/feature_compliance/ios_dual_banner_motd_feature.py @@ -1,2 +1,2 @@ -feature = {"name": "exec banner", "ordered": False, "section": ["banner motd"]} +feature = {"name": "motd banner", "ordered": False, "section": ["banner motd"]} network_os = "cisco_ios" diff --git a/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_actual.txt b/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_actual.txt new file mode 100755 index 00000000..b4062f21 --- /dev/null +++ b/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_actual.txt @@ -0,0 +1,7 @@ +hostname emptybanner +! +banner motd ^C^C +! +line vty 0 4 + transport ssh +! \ No newline at end of file diff --git a/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_feature.py b/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_feature.py new file mode 100755 index 00000000..4d99ae94 --- /dev/null +++ b/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_feature.py @@ -0,0 +1,2 @@ +feature = {"name": "banner", "ordered": False, "section": ["banner"]} +network_os = "cisco_ios" diff --git a/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_intended.txt b/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_intended.txt new file mode 100755 index 00000000..84dfb63e --- /dev/null +++ b/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_intended.txt @@ -0,0 +1,9 @@ +hostname emptybanner +! +banner motd ^C +actual banner example +^C +! +line vty 0 4 + transport ssh +! \ No newline at end of file diff --git a/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_received.py b/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_received.py new file mode 100755 index 00000000..0626f29d --- /dev/null +++ b/tests/unit/mock/config/compliance/feature_compliance/ios_empty_banner_received.py @@ -0,0 +1,10 @@ +data = { + "actual": "banner motd ^C^C", + "cannot_parse": True, + "compliant": False, + "extra": "banner motd ^C^C", + "intended": "banner motd ^C\n" "actual banner example^C", + "missing": "banner motd ^C\n" "actual banner example^C", + "ordered_compliant": False, + "unordered_compliant": False, +} diff --git a/tests/unit/mock/config/parser/base/cisco_ios/ios_dual_banner_received.py b/tests/unit/mock/config/parser/base/cisco_ios/ios_dual_banner_received.py new file mode 100755 index 00000000..d1eccc3d --- /dev/null +++ b/tests/unit/mock/config/parser/base/cisco_ios/ios_dual_banner_received.py @@ -0,0 +1,12 @@ +from netutils.config.parser import ConfigLine + +data = [ + ConfigLine(config_line="hostname dual-banner", parents=()), + ConfigLine(config_line="banner exec ^C", parents=()), + ConfigLine(config_line="=========\nintended config exec banner\n-========^C", parents=("banner exec ^C",)), + ConfigLine(config_line="banner motd ^C", parents=()), + ConfigLine( + config_line="======\nintended config motd banner\n======\n || ($hostname) ||^C", parents=("banner motd ^C",) + ), + ConfigLine(config_line=None, parents=()), +] diff --git a/tests/unit/mock/config/parser/base/cisco_ios/ios_dual_banner_sent.txt b/tests/unit/mock/config/parser/base/cisco_ios/ios_dual_banner_sent.txt new file mode 100755 index 00000000..d5bdb903 --- /dev/null +++ b/tests/unit/mock/config/parser/base/cisco_ios/ios_dual_banner_sent.txt @@ -0,0 +1,14 @@ +hostname dual-banner +! +banner exec ^C +========= +intended config exec banner +-======== +^C +banner motd ^C +====== +intended config motd banner +====== + || ($hostname) || +^C +! \ No newline at end of file diff --git a/tests/unit/mock/config/parser/base/cisco_ios/ios_empty_banner_received.py b/tests/unit/mock/config/parser/base/cisco_ios/ios_empty_banner_received.py new file mode 100755 index 00000000..f3d8ebf9 --- /dev/null +++ b/tests/unit/mock/config/parser/base/cisco_ios/ios_empty_banner_received.py @@ -0,0 +1,8 @@ +from netutils.config.parser import ConfigLine + +data = [ + ConfigLine(config_line="hostname emptybanner", parents=()), + ConfigLine(config_line="banner motd ^C^C", parents=()), + ConfigLine(config_line="line vty 0 4", parents=()), + ConfigLine(config_line=" transport ssh", parents=("line vty 0 4",)), +] diff --git a/tests/unit/mock/config/parser/base/cisco_ios/ios_empty_banner_sent.txt b/tests/unit/mock/config/parser/base/cisco_ios/ios_empty_banner_sent.txt new file mode 100755 index 00000000..b4062f21 --- /dev/null +++ b/tests/unit/mock/config/parser/base/cisco_ios/ios_empty_banner_sent.txt @@ -0,0 +1,7 @@ +hostname emptybanner +! +banner motd ^C^C +! +line vty 0 4 + transport ssh +! \ No newline at end of file