Skip to content

Commit 49be0c6

Browse files
authored
Merge pull request #804 from networktocode/u/joewesch-fix-empty-config
Fixed PanOS empty config parsing
2 parents 7db22a5 + 5fd76db commit 49be0c6

File tree

3 files changed

+25
-13
lines changed

3 files changed

+25
-13
lines changed

changes/803.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed an issue where an empty config would raise an error when parsing Palo Alto Networks PanOS.

netutils/config/parser.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,12 +1553,14 @@ def build_config_relationship(self) -> t.List[ConfigLine]: # pylint: disable=to
15531553
True
15541554
"""
15551555
if self.config_lines_only is None:
1556-
raise ValueError("Config is empty.")
1556+
return []
1557+
config_lines = self.config_lines_only.splitlines()
1558+
if not config_lines:
1559+
return []
15571560

15581561
if "@dirtyId" in self.config_lines_only:
15591562
# We have to specifically check for JSON format because it can be confused with the brace format
15601563
raise ValueError("Found 'json' configuration format. Please provide in 'set' or 'default' (brace) format.")
1561-
config_lines = self.config_lines_only.splitlines()
15621564
if any(line.endswith("{") for line in config_lines):
15631565
converted_config = paloalto_panos_brace_to_set(cfg=self.config, cfg_type="string")
15641566
list_config = converted_config.splitlines()

tests/unit/test_parser.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,18 @@
1313
base_parameters = []
1414
find_all_children_parameters = []
1515
find_children_w_parents_parameters = []
16-
for network_os in list(compliance.parser_map.keys()):
17-
for _file in glob.glob(f"{MOCK_DIR}/base/{network_os}/*{TXT_FILE}"):
18-
base_parameters.append([_file, network_os])
19-
for _file in glob.glob(f"{MOCK_DIR}/find_all_children/{network_os}/*{TXT_FILE}"):
20-
find_all_children_parameters.append([_file, network_os])
21-
for _file in glob.glob(f"{MOCK_DIR}/find_children_w_parents/{network_os}/*{TXT_FILE}"):
22-
find_children_w_parents_parameters.append([_file, network_os])
16+
all_network_os = list(compliance.parser_map.keys())
17+
for _network_os in all_network_os:
18+
for _file in glob.glob(f"{MOCK_DIR}/base/{_network_os}/*{TXT_FILE}"):
19+
base_parameters.append([_file, _network_os])
20+
for _file in glob.glob(f"{MOCK_DIR}/find_all_children/{_network_os}/*{TXT_FILE}"):
21+
find_all_children_parameters.append([_file, _network_os])
22+
for _file in glob.glob(f"{MOCK_DIR}/find_children_w_parents/{_network_os}/*{TXT_FILE}"):
23+
find_children_w_parents_parameters.append([_file, _network_os])
2324

2425

2526
@pytest.mark.parametrize("_file, network_os", base_parameters)
26-
def test_parser(_file, network_os, get_text_data, get_python_data): # pylint: disable=redefined-outer-name
27+
def test_parser(_file, network_os, get_text_data, get_python_data):
2728
truncate_file = os.path.join(MOCK_DIR, "base", _file[: -len(TXT_FILE)])
2829

2930
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
3334

3435

3536
@pytest.mark.parametrize("_file, network_os", find_all_children_parameters)
36-
def test_find_all_children(_file, network_os, get_text_data, get_json_data): # pylint: disable=redefined-outer-name
37+
def test_find_all_children(_file, network_os, get_text_data, get_json_data):
3738
truncate_file = os.path.join(MOCK_DIR, "find_all_children", _file[: -len(TXT_FILE)])
3839

3940
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): #
4445

4546

4647
@pytest.mark.parametrize("_file, network_os", find_children_w_parents_parameters)
47-
def test_find_children_w_parents(_file, network_os, get_text_data, get_json_data): # pylint: disable=redefined-outer-name
48+
def test_find_children_w_parents(_file, network_os, get_text_data, get_json_data):
4849
truncate_file = os.path.join(MOCK_DIR, "find_children_w_parents", _file[: -len(TXT_FILE)])
4950

5051
device_cfg = get_text_data(os.path.join(MOCK_DIR, "find_children_w_parents", _file))
@@ -81,7 +82,7 @@ def test_duplicate_line():
8182

8283

8384
@pytest.mark.parametrize("network_os", ["cisco_ios", "arista_eos", "cisco_iosxr"])
84-
def test_leading_spaces_config_start(network_os): # pylint: disable=redefined-outer-name
85+
def test_leading_spaces_config_start(network_os):
8586
logging = (
8687
"! Command: show running-config\n"
8788
" 24.1.4\n"
@@ -95,3 +96,11 @@ def test_leading_spaces_config_start(network_os): # pylint: disable=redefined-o
9596
)
9697
with pytest.raises(IndexError, match=r".*Validate the first line does not begin with a space.*"):
9798
compliance.parser_map[network_os](logging).config_lines # pylint: disable=expression-not-assigned
99+
100+
101+
@pytest.mark.parametrize("network_os", all_network_os)
102+
def test_empty_config(network_os):
103+
"""Test that an empty config returns an empty list and does not raise an error."""
104+
config = ""
105+
os_parser = compliance.parser_map[network_os]
106+
assert os_parser(config).config_lines == []

0 commit comments

Comments
 (0)