Skip to content

Commit ad13803

Browse files
authored
Cache openapi dump locally (#617)
Cache the OpenAPI schema for a faster nb_inventory.
1 parent b9f706e commit ad13803

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

plugins/inventory/nb_inventory.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@
258258
import json
259259
import uuid
260260
import math
261+
import os
261262
from copy import deepcopy
262263
from functools import partial
263264
from sys import version as python_version
@@ -268,6 +269,7 @@
268269
from ipaddress import ip_interface
269270
from packaging import specifiers, version
270271

272+
from ansible.constants import DEFAULT_LOCAL_TMP
271273
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
272274
from ansible.module_utils.ansible_release import __version__ as ansible_version
273275
from ansible.errors import AnsibleError
@@ -1194,9 +1196,30 @@ def wrapper():
11941196
thread_exceptions = None
11951197

11961198
def fetch_api_docs(self):
1197-
openapi = self._fetch_information(
1198-
self.api_endpoint + "/api/docs/?format=openapi"
1199-
)
1199+
try:
1200+
status = self._fetch_information(self.api_endpoint + "/api/status")
1201+
netbox_api_version = ".".join(status["netbox-version"].split(".")[:2])
1202+
except:
1203+
netbox_api_version = 0
1204+
1205+
tmp_dir = os.path.split(DEFAULT_LOCAL_TMP)[0]
1206+
tmp_file = os.path.join(tmp_dir, "netbox_api_dump.json")
1207+
1208+
try:
1209+
with open(tmp_file) as file:
1210+
openapi = json.load(file)
1211+
except:
1212+
openapi = {}
1213+
1214+
cached_api_version = openapi.get("info", {}).get("version")
1215+
1216+
if netbox_api_version != cached_api_version:
1217+
openapi = self._fetch_information(
1218+
self.api_endpoint + "/api/docs/?format=openapi"
1219+
)
1220+
1221+
with open(tmp_file, "w") as file:
1222+
json.dump(openapi, file)
12001223

12011224
self.api_version = version.parse(openapi["info"]["version"])
12021225
self.allowed_device_query_parameters = [

tests/unit/inventory/test_nb_inventory.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import pytest
1010
import os
1111
from functools import partial
12-
from unittest.mock import patch, MagicMock, Mock, call
12+
from unittest.mock import patch, MagicMock, Mock, call, mock_open
1313
from packaging import version
1414

1515
try:
@@ -190,3 +190,33 @@ def test_get_resource_list_chunked(
190190
mock_get_resource_list.assert_has_calls(map(call, expected))
191191
assert mock_get_resource_list.call_count == len(expected)
192192
assert resources == mock_get_resource_list.return_value * len(expected)
193+
194+
195+
@patch(
196+
"ansible_collections.netbox.netbox.plugins.inventory.nb_inventory.DEFAULT_LOCAL_TMP",
197+
"/fake/path/asdasd3456",
198+
)
199+
@pytest.mark.parametrize("netbox_ver", ["2.0.2", "3.0.0"])
200+
def test_fetch_api_docs(inventory_fixture, netbox_ver):
201+
mock_fetch_information = Mock()
202+
mock_fetch_information.side_effect = [
203+
{"netbox-version": netbox_ver},
204+
{"info": {"version": "3.0"}},
205+
]
206+
207+
inventory_fixture._fetch_information = mock_fetch_information
208+
209+
with pytest.raises(KeyError, match="paths"):
210+
with patch("builtins.open", mock_open()) as filemock:
211+
with patch(
212+
"ansible_collections.netbox.netbox.plugins.inventory.nb_inventory.json"
213+
) as json_mock:
214+
json_mock.load.return_value = {"info": {"version": "2.0"}}
215+
inventory_fixture.fetch_api_docs()
216+
217+
ref_args_list = [call("/fake/path/netbox_api_dump.json")]
218+
if netbox_ver == "3.0.0":
219+
ref_args_list.append(call("/fake/path/netbox_api_dump.json", "w"))
220+
221+
assert filemock.call_args_list == ref_args_list
222+
assert str(inventory_fixture.api_version) == netbox_ver[:-2]

0 commit comments

Comments
 (0)