Skip to content

Commit e331a1f

Browse files
authored
Merge pull request #1461 from dkaplan1/nxos-improvements
Update NXOS response processing for more informative error messaging
2 parents 0509b9f + 32969bc commit e331a1f

File tree

7 files changed

+111
-23
lines changed

7 files changed

+111
-23
lines changed

napalm/nxapi_plumbing/api_client.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,7 @@ def _send_request(self, commands, method):
8282
)
8383
raise NXAPIAuthError(msg)
8484

85-
if response.status_code not in [200]:
86-
msg = """Invalid status code returned on NX-API POST
87-
commands: {}
88-
status_code: {}""".format(
89-
commands, response.status_code
90-
)
91-
raise NXAPIPostError(msg)
92-
93-
return response.text
85+
return response
9486

9587

9688
class RPCClient(RPCBase):
@@ -143,7 +135,7 @@ def _process_api_response(self, response, commands, raw_text=False):
143135
structured data.
144136
"""
145137

146-
response_list = json.loads(response)
138+
response_list = json.loads(response.text)
147139
if isinstance(response_list, dict):
148140
response_list = [response_list]
149141

@@ -154,7 +146,7 @@ def _process_api_response(self, response, commands, raw_text=False):
154146
new_response = []
155147
for response in response_list:
156148

157-
# Dectect errors
149+
# Detect errors
158150
self._error_check(response)
159151

160152
# Some commands like "show run" can have a None result
@@ -239,7 +231,15 @@ def _build_payload(self, commands, method, xml_version="1.0", version="1.0"):
239231
return payload
240232

241233
def _process_api_response(self, response, commands, raw_text=False):
242-
xml_root = etree.fromstring(response)
234+
if response.status_code not in [200]:
235+
msg = """Invalid status code returned on NX-API POST
236+
commands: {}
237+
status_code: {}""".format(
238+
commands, response.status_code
239+
)
240+
raise NXAPIPostError(msg)
241+
242+
xml_root = etree.fromstring(response.text)
243243
response_list = xml_root.xpath("outputs/output")
244244
if len(commands) != len(response_list):
245245
raise NXAPIXMLError(

napalm/nxos/nxos.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1175,7 +1175,7 @@ def get_interfaces_ip(self):
11751175
ipv6_interf_table_vrf = self._get_command_table(
11761176
ipv6_command, "TABLE_intf", "ROW_intf"
11771177
)
1178-
except napalm.nxapi_plumbing.errors.NXAPIPostError:
1178+
except napalm.nxapi_plumbing.errors.NXAPICommandError:
11791179
return interfaces_ip
11801180

11811181
for interface in ipv6_interf_table_vrf:

test/nxapi_plumbing/conftest.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def pytest_addoption(parser):
3939
@pytest.fixture(scope="module")
4040
def mock_pynxos_device(request):
4141
"""Create a mock pynxos test device."""
42+
response_status_code = getattr(request, "param", 200)
4243
device = {
4344
"host": "nxos1.fake.com",
4445
"username": "admin",
@@ -49,13 +50,14 @@ def mock_pynxos_device(request):
4950
"timeout": 60,
5051
"verify": False,
5152
}
52-
conn = MockDevice(**device)
53+
conn = MockDevice(response_status_code=response_status_code, **device)
5354
return conn
5455

5556

5657
@pytest.fixture(scope="module")
5758
def mock_pynxos_device_xml(request):
5859
"""Create a mock pynxos test device."""
60+
response_status_code = getattr(request, "param", 200)
5961
device = {
6062
"host": "nxos1.fake.com",
6163
"username": "admin",
@@ -66,7 +68,7 @@ def mock_pynxos_device_xml(request):
6668
"timeout": 60,
6769
"verify": False,
6870
}
69-
conn = MockDevice(**device)
71+
conn = MockDevice(response_status_code=response_status_code, **device)
7072
return conn
7173

7274

test/nxapi_plumbing/mock_device.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def __init__(
6161
port=None,
6262
timeout=30,
6363
verify=True,
64+
response_status_code=200,
6465
):
6566
super().__init__(
6667
host,
@@ -81,6 +82,7 @@ def __init__(
8182
port=port,
8283
timeout=timeout,
8384
verify=verify,
85+
response_status_code=response_status_code,
8486
)
8587
elif api_format == "xml":
8688
self.api = MockXMLClient(
@@ -91,10 +93,33 @@ def __init__(
9193
port=port,
9294
timeout=timeout,
9395
verify=verify,
96+
response_status_code=response_status_code,
9497
)
9598

9699

97100
class MockRPCClient(RPCClient):
101+
def __init__(
102+
self,
103+
host,
104+
username,
105+
password,
106+
transport="https",
107+
port=None,
108+
timeout=30,
109+
verify=True,
110+
response_status_code=200,
111+
):
112+
self.response_status_code = response_status_code
113+
super().__init__(
114+
host=host,
115+
username=username,
116+
password=password,
117+
transport=transport,
118+
port=port,
119+
timeout=timeout,
120+
verify=verify,
121+
)
122+
98123
def _send_request(self, commands, method="cli"):
99124
payload = self._build_payload(commands, method)
100125

@@ -112,12 +137,34 @@ def _send_request(self, commands, method="cli"):
112137

113138
response_obj = FakeResponse()
114139
response_obj.text = mock_response
115-
response_obj.status_code = 200
140+
response_obj.status_code = self.response_status_code
116141

117-
return response_obj.text
142+
return response_obj
118143

119144

120145
class MockXMLClient(XMLClient):
146+
def __init__(
147+
self,
148+
host,
149+
username,
150+
password,
151+
transport="https",
152+
port=None,
153+
timeout=30,
154+
verify=True,
155+
response_status_code=200,
156+
):
157+
self.response_status_code = response_status_code
158+
super().__init__(
159+
host=host,
160+
username=username,
161+
password=password,
162+
transport=transport,
163+
port=port,
164+
timeout=timeout,
165+
verify=verify,
166+
)
167+
121168
def _send_request(self, commands, method="cli_show"):
122169
payload = self._build_payload(commands, method)
123170

@@ -135,6 +182,6 @@ def _send_request(self, commands, method="cli_show"):
135182

136183
response_obj = FakeResponse()
137184
response_obj.text = mock_response
138-
response_obj.status_code = 200
185+
response_obj.status_code = self.response_status_code
139186

140-
return response_obj.text
187+
return response_obj

test/nxapi_plumbing/test_config.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import pytest
2+
3+
from napalm.nxapi_plumbing import NXAPICommandError
4+
from napalm.nxapi_plumbing.errors import NXAPIPostError
5+
6+
17
def test_config_jsonrpc(mock_pynxos_device):
28
result = mock_pynxos_device.config("logging history size 200")
39
assert result is None
@@ -36,3 +42,31 @@ def test_config_xml_list(mock_pynxos_device_xml):
3642
msg = element.find("./msg")
3743
assert status_code.text == "200"
3844
assert msg.text == "Success"
45+
46+
47+
@pytest.mark.parametrize("mock_pynxos_device", [500], indirect=True)
48+
def test_config_jsonrpc_raises_NXAPICommandError_on_non_200_config_error(
49+
mock_pynxos_device,
50+
):
51+
with pytest.raises(
52+
NXAPICommandError, match='The command "bogus command" gave the error'
53+
):
54+
mock_pynxos_device.config("bogus command")
55+
56+
57+
@pytest.mark.parametrize("mock_pynxos_device_xml", [500], indirect=True)
58+
def test_config_xml_raises_NXAPIPostError_on_non_200_post_error(mock_pynxos_device_xml):
59+
with pytest.raises(
60+
NXAPIPostError, match="Invalid status code returned on NX-API POST"
61+
):
62+
mock_pynxos_device_xml.config("logging history size 200")
63+
64+
65+
@pytest.mark.parametrize("mock_pynxos_device_xml", [200], indirect=True)
66+
def test_config_xml_raises_NXAPICommandError_on_200_config_error(
67+
mock_pynxos_device_xml,
68+
):
69+
with pytest.raises(
70+
NXAPICommandError, match='The command "bogus command" gave the error'
71+
):
72+
mock_pynxos_device_xml.config("bogus command")

test/nxos/conftest.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
from builtins import super
33

44
import pytest
5+
from napalm.base.mock import raise_exception
56
from napalm.base.test import conftest as parent_conftest
6-
77
from napalm.base.test.double import BaseTestDouble
8-
98
from napalm.nxos import nxos
109

1110

@@ -74,6 +73,8 @@ def show(self, command, raw_text=False):
7473
result = self.read_txt_file(full_path)
7574
else:
7675
result = self.read_json_file(full_path)
76+
if "exception" in result:
77+
raise_exception(result)
7778

7879
return result
7980

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
{
2-
"exception": "nxapi_plumbing.api_client.NXAPIPostError"
3-
}
2+
"exception": "napalm.nxapi_plumbing.errors.NXAPICommandError",
3+
"kwargs": {
4+
"command": "show ipv6 interface",
5+
"message": ""
6+
}
7+
}

0 commit comments

Comments
 (0)