From 9228723c2ba9195f0cfb067293950ae869dee733 Mon Sep 17 00:00:00 2001 From: Joe Wesch Date: Fri, 30 Jan 2026 15:48:06 -0600 Subject: [PATCH 1/4] Bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e89817f8..a0249c8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netutils" -version = "1.17.0" +version = "1.17.1a0" description = "Common helper functions useful in network automation." authors = ["Network to Code, LLC "] license = "Apache-2.0" From 2e20fae1f744d33add9a1bc6b989046f73eb6008 Mon Sep 17 00:00:00 2001 From: Joe Wesch Date: Tue, 3 Feb 2026 11:32:59 -0600 Subject: [PATCH 2/4] Fixed PanOS empty config parsing --- changes/803.fixed | 1 + netutils/config/parser.py | 6 ++++-- tests/unit/test_parser.py | 31 ++++++++++++++++++++----------- 3 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 changes/803.fixed diff --git a/changes/803.fixed b/changes/803.fixed new file mode 100644 index 00000000..101991ce --- /dev/null +++ b/changes/803.fixed @@ -0,0 +1 @@ +Fixed an issue where an empty config would raise an error when parsing Palo Alto Networks PanOS. \ No newline at end of file diff --git a/netutils/config/parser.py b/netutils/config/parser.py index 9fa18424..b38248f1 100644 --- a/netutils/config/parser.py +++ b/netutils/config/parser.py @@ -1553,12 +1553,14 @@ def build_config_relationship(self) -> t.List[ConfigLine]: # pylint: disable=to True """ if self.config_lines_only is None: - raise ValueError("Config is empty.") + return [] + config_lines = self.config_lines_only.splitlines() + if not config_lines: + return [] if "@dirtyId" in self.config_lines_only: # We have to specifically check for JSON format because it can be confused with the brace format raise ValueError("Found 'json' configuration format. Please provide in 'set' or 'default' (brace) format.") - config_lines = self.config_lines_only.splitlines() if any(line.endswith("{") for line in config_lines): converted_config = paloalto_panos_brace_to_set(cfg=self.config, cfg_type="string") list_config = converted_config.splitlines() diff --git a/tests/unit/test_parser.py b/tests/unit/test_parser.py index 84be8ccd..6c28cd94 100644 --- a/tests/unit/test_parser.py +++ b/tests/unit/test_parser.py @@ -13,17 +13,18 @@ base_parameters = [] find_all_children_parameters = [] find_children_w_parents_parameters = [] -for network_os in list(compliance.parser_map.keys()): - for _file in glob.glob(f"{MOCK_DIR}/base/{network_os}/*{TXT_FILE}"): - base_parameters.append([_file, network_os]) - for _file in glob.glob(f"{MOCK_DIR}/find_all_children/{network_os}/*{TXT_FILE}"): - find_all_children_parameters.append([_file, network_os]) - for _file in glob.glob(f"{MOCK_DIR}/find_children_w_parents/{network_os}/*{TXT_FILE}"): - find_children_w_parents_parameters.append([_file, network_os]) +all_network_os = list(compliance.parser_map.keys()) +for _network_os in all_network_os: + for _file in glob.glob(f"{MOCK_DIR}/base/{_network_os}/*{TXT_FILE}"): + base_parameters.append([_file, _network_os]) + for _file in glob.glob(f"{MOCK_DIR}/find_all_children/{_network_os}/*{TXT_FILE}"): + find_all_children_parameters.append([_file, _network_os]) + for _file in glob.glob(f"{MOCK_DIR}/find_children_w_parents/{_network_os}/*{TXT_FILE}"): + find_children_w_parents_parameters.append([_file, _network_os]) @pytest.mark.parametrize("_file, network_os", base_parameters) -def test_parser(_file, network_os, get_text_data, get_python_data): # pylint: disable=redefined-outer-name +def test_parser(_file, network_os, get_text_data, get_python_data): truncate_file = os.path.join(MOCK_DIR, "base", _file[: -len(TXT_FILE)]) device_cfg = get_text_data(os.path.join(MOCK_DIR, "base", _file)) @@ -33,7 +34,7 @@ def test_parser(_file, network_os, get_text_data, get_python_data): # pylint: d @pytest.mark.parametrize("_file, network_os", find_all_children_parameters) -def test_find_all_children(_file, network_os, get_text_data, get_json_data): # pylint: disable=redefined-outer-name +def test_find_all_children(_file, network_os, get_text_data, get_json_data): truncate_file = os.path.join(MOCK_DIR, "find_all_children", _file[: -len(TXT_FILE)]) device_cfg = get_text_data(os.path.join(MOCK_DIR, "find_all_children", _file)) @@ -44,7 +45,7 @@ def test_find_all_children(_file, network_os, get_text_data, get_json_data): # @pytest.mark.parametrize("_file, network_os", find_children_w_parents_parameters) -def test_find_children_w_parents(_file, network_os, get_text_data, get_json_data): # pylint: disable=redefined-outer-name +def test_find_children_w_parents(_file, network_os, get_text_data, get_json_data): truncate_file = os.path.join(MOCK_DIR, "find_children_w_parents", _file[: -len(TXT_FILE)]) device_cfg = get_text_data(os.path.join(MOCK_DIR, "find_children_w_parents", _file)) @@ -81,7 +82,7 @@ def test_duplicate_line(): @pytest.mark.parametrize("network_os", ["cisco_ios", "arista_eos", "cisco_iosxr"]) -def test_leading_spaces_config_start(network_os): # pylint: disable=redefined-outer-name +def test_leading_spaces_config_start(network_os): logging = ( "! Command: show running-config\n" " 24.1.4\n" @@ -95,3 +96,11 @@ def test_leading_spaces_config_start(network_os): # pylint: disable=redefined-o ) with pytest.raises(IndexError, match=r".*Validate the first line does not begin with a space.*"): compliance.parser_map[network_os](logging).config_lines # pylint: disable=expression-not-assigned + + +@pytest.mark.parametrize("network_os", all_network_os) +def test_empty_config(network_os): + "Test that an empty config returns an empty list and does not raise an error." + config = "" + os_parser = compliance.parser_map[network_os] + assert os_parser(config).config_lines == [] From 5fd76db15e82e33dfbbc4be697e8b0bb66b1b90e Mon Sep 17 00:00:00 2001 From: Joe Wesch Date: Tue, 3 Feb 2026 11:51:01 -0600 Subject: [PATCH 3/4] Fixed docstring format --- tests/unit/test_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_parser.py b/tests/unit/test_parser.py index 6c28cd94..f98f9751 100644 --- a/tests/unit/test_parser.py +++ b/tests/unit/test_parser.py @@ -100,7 +100,7 @@ def test_leading_spaces_config_start(network_os): @pytest.mark.parametrize("network_os", all_network_os) def test_empty_config(network_os): - "Test that an empty config returns an empty list and does not raise an error." + """Test that an empty config returns an empty list and does not raise an error.""" config = "" os_parser = compliance.parser_map[network_os] assert os_parser(config).config_lines == [] From 7432738ead697d43cf663318cff5e5d9bd4aff38 Mon Sep 17 00:00:00 2001 From: Joe Wesch Date: Wed, 4 Feb 2026 11:02:30 -0600 Subject: [PATCH 4/4] Release v1.17.1 --- changes/803.fixed | 1 - docs/admin/release_notes/version_1.17.md | 6 ++++++ pyproject.toml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) delete mode 100644 changes/803.fixed diff --git a/changes/803.fixed b/changes/803.fixed deleted file mode 100644 index 101991ce..00000000 --- a/changes/803.fixed +++ /dev/null @@ -1 +0,0 @@ -Fixed an issue where an empty config would raise an error when parsing Palo Alto Networks PanOS. \ No newline at end of file diff --git a/docs/admin/release_notes/version_1.17.md b/docs/admin/release_notes/version_1.17.md index 8554ec27..355c2461 100644 --- a/docs/admin/release_notes/version_1.17.md +++ b/docs/admin/release_notes/version_1.17.md @@ -3,6 +3,12 @@ This document describes all new features and changes in the release. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v1.17.1 (2026-02-04)](https://github.com/networktocode/netutils/releases/tag/v1.17.1) + +### Fixed + +- [#803](https://github.com/networktocode/netutils/issues/803) - Fixed an issue where an empty config would raise an error when parsing Palo Alto Networks PanOS. + ## [v1.17.0 (2026-01-30)](https://github.com/networktocode/netutils/releases/tag/v1.17.0) ### Added diff --git a/pyproject.toml b/pyproject.toml index a0249c8d..c07763bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "netutils" -version = "1.17.1a0" +version = "1.17.1" description = "Common helper functions useful in network automation." authors = ["Network to Code, LLC "] license = "Apache-2.0"