Skip to content

Commit dd94cbe

Browse files
authored
Update test scripts and settings to netbox 3.3 (#508)
Update test scripts and settings to NetBox 3.3
1 parent cb86fd7 commit dd94cbe

File tree

11 files changed

+224
-78
lines changed

11 files changed

+224
-78
lines changed

.github/workflows/py3.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ jobs:
1212
runs-on: ubuntu-latest
1313
strategy:
1414
matrix:
15-
python: ["3.7", "3.10"]
16-
netbox: ["2.11", "3.0", "3.1"]
15+
python: ["3.8", "3.9", "3.10"]
16+
netbox: ["3.3"]
1717

1818
steps:
1919
- uses: actions/checkout@v2

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Pynetbox
22
Python API client library for [NetBox](https://github.com/netbox-community/netbox).
33

4+
> **Note:** Version 6.7 and later of the library only supports NetBox 3.3 and above.
45
56
## Installation
67

pynetbox/core/query.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def __init__(self, req):
5757
)
5858
)
5959

60-
super().__init__(message)
60+
super().__init__(self.message)
6161
self.req = req
6262
self.request_body = req.request.body
6363
self.base = req.url

pynetbox/core/response.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,22 @@ def _parse_values(self, values):
354354
values within.
355355
"""
356356

357+
def generic_list_parser(key_name, list_item):
358+
from pynetbox.models.mapper import CONTENT_TYPE_MAPPER
359+
360+
if (
361+
isinstance(list_item, dict)
362+
and "object_type" in list_item
363+
and "object" in list_item
364+
):
365+
lookup = list_item["object_type"]
366+
model = None
367+
model = CONTENT_TYPE_MAPPER.get(lookup)
368+
if model:
369+
return model(list_item["object"], self.api, self.endpoint)
370+
371+
return list_item
372+
357373
def list_parser(key_name, list_item):
358374
if isinstance(list_item, dict):
359375
lookup = getattr(self.__class__, key_name, None)
@@ -364,6 +380,7 @@ def list_parser(key_name, list_item):
364380
else:
365381
model = lookup[0]
366382
return model(list_item, self.api, self.endpoint)
383+
367384
return list_item
368385

369386
for k, v in values.items():
@@ -382,8 +399,13 @@ def list_parser(key_name, list_item):
382399
self._add_cache((k, v))
383400

384401
elif isinstance(v, list):
385-
v = [list_parser(k, i) for i in v]
386-
to_cache = list(v)
402+
# check if GFK
403+
if len(v) and isinstance(v[0], dict) and "object_type" in v[0]:
404+
v = [generic_list_parser(k, i) for i in v]
405+
to_cache = list(v)
406+
else:
407+
v = [list_parser(k, i) for i in v]
408+
to_cache = list(v)
387409
self._add_cache((k, to_cache))
388410

389411
else:

pynetbox/models/dcim.py

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,35 @@
2323

2424

2525
class TraceableRecord(Record):
26+
def _get_obj_class(self, url):
27+
uri_to_obj_class_map = {
28+
"dcim/cables": Cables,
29+
"dcim/front-ports": FrontPorts,
30+
"dcim/interfaces": Interfaces,
31+
"dcim/rear-ports": RearPorts,
32+
}
33+
34+
# the url for this item will be something like:
35+
# https://netbox/api/dcim/rear-ports/12761/
36+
# TODO: Move this to a more general function.
37+
app_endpoint = "/".join(
38+
urlsplit(url).path[len(urlsplit(self.api.base_url).path) :].split("/")[1:3]
39+
)
40+
return uri_to_obj_class_map.get(
41+
app_endpoint,
42+
Record,
43+
)
44+
45+
def _build_termination_data(self, termination_list):
46+
terminations_data = []
47+
for hop_item_data in termination_list:
48+
return_obj_class = self._get_obj_class(hop_item_data["url"])
49+
terminations_data.append(
50+
return_obj_class(hop_item_data, self.endpoint.api, self.endpoint)
51+
)
52+
53+
return terminations_data
54+
2655
def trace(self):
2756
req = Request(
2857
key=str(self.id) + "/trace",
@@ -31,38 +60,18 @@ def trace(self):
3160
session_key=self.api.session_key,
3261
http_session=self.api.http_session,
3362
).get()
34-
uri_to_obj_class_map = {
35-
"dcim/cables": Cables,
36-
"dcim/front-ports": FrontPorts,
37-
"dcim/interfaces": Interfaces,
38-
"dcim/rear-ports": RearPorts,
39-
}
63+
4064
ret = []
41-
for (termination_a_data, cable_data, termination_b_data) in req:
42-
this_hop_ret = []
43-
for hop_item_data in (termination_a_data, cable_data, termination_b_data):
44-
# if not fully terminated then some items will be None
45-
if not hop_item_data:
46-
this_hop_ret.append(hop_item_data)
47-
continue
48-
49-
# the url for this item will be something like:
50-
# https://netbox/api/dcim/rear-ports/12761/
51-
# TODO: Move this to a more general function.
52-
app_endpoint = "/".join(
53-
urlsplit(hop_item_data["url"])
54-
.path[len(urlsplit(self.api.base_url).path) :]
55-
.split("/")[1:3]
56-
)
57-
return_obj_class = uri_to_obj_class_map.get(
58-
app_endpoint,
59-
Record,
60-
)
61-
this_hop_ret.append(
62-
return_obj_class(hop_item_data, self.endpoint.api, self.endpoint)
65+
for (a_terminations_data, cable_data, b_terminations_data) in req:
66+
ret.append(self._build_termination_data(a_terminations_data))
67+
if not cable_data:
68+
ret.append(cable_data)
69+
else:
70+
return_obj_class = self._get_obj_class(cable_data["url"])
71+
ret.append(
72+
return_obj_class(cable_data, self.endpoint.api, self.endpoint)
6373
)
64-
65-
ret.append(this_hop_ret)
74+
ret.append(self._build_termination_data(b_terminations_data))
6675

6776
return ret
6877

@@ -225,14 +234,6 @@ def __str__(self):
225234

226235
class Cables(Record):
227236
def __str__(self):
228-
if all(
229-
[
230-
isinstance(i, Termination)
231-
for i in (self.termination_a, self.termination_b)
232-
]
233-
):
234-
return "{} <> {}".format(self.termination_a, self.termination_b)
237+
if len(self.a_terminations) == 1 and len(self.b_terminations) == 1:
238+
return "{} <> {}".format(self.a_terminations[0], self.b_terminations[0])
235239
return "Cable #{}".format(self.id)
236-
237-
termination_a = Termination
238-
termination_b = Termination

pynetbox/models/mapper.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
from .circuits import Circuits, CircuitTerminations
2+
from .dcim import (
3+
DeviceTypes,
4+
Devices,
5+
Interfaces,
6+
PowerOutlets,
7+
PowerPorts,
8+
ConsolePorts,
9+
ConsoleServerPorts,
10+
RackReservations,
11+
VirtualChassis,
12+
FrontPorts,
13+
RearPorts,
14+
Racks,
15+
Termination,
16+
Cables,
17+
)
18+
from .ipam import (
19+
IpAddresses,
20+
Prefixes,
21+
Aggregates,
22+
Vlans,
23+
VlanGroups,
24+
)
25+
from .virtualization import VirtualMachines
26+
from .wireless import WirelessLans
27+
28+
29+
CONTENT_TYPE_MAPPER = {
30+
"circuits.circuit": Circuits,
31+
"circuits.circuittermination": CircuitTerminations,
32+
"dcim.cable": Cables,
33+
"dcim.cablepath": None,
34+
"dcim.cabletermination": Termination,
35+
"dcim.consoleport": ConsolePorts,
36+
"dcim.consoleporttemplate": None,
37+
"dcim.consoleserverport": ConsoleServerPorts,
38+
"dcim.consoleserverporttemplate": None,
39+
"dcim.device": Devices,
40+
"dcim.devicebay": None,
41+
"dcim.devicebaytemplate": None,
42+
"dcim.devicerole": None,
43+
"dcim.devicetype": DeviceTypes,
44+
"dcim.frontport": FrontPorts,
45+
"dcim.frontporttemplate": None,
46+
"dcim.interface": Interfaces,
47+
"dcim.interfacetemplate": None,
48+
"dcim.inventoryitem": None,
49+
"dcim.inventoryitemrole": None,
50+
"dcim.inventoryitemtemplate": None,
51+
"dcim.location": None,
52+
"dcim.manufacturer": None,
53+
"dcim.module": None,
54+
"dcim.modulebay": None,
55+
"dcim.modulebaytemplate": None,
56+
"dcim.moduletype": None,
57+
"dcim.platform": None,
58+
"dcim.powerfeed": None,
59+
"dcim.poweroutlet": PowerOutlets,
60+
"dcim.poweroutlettemplate": None,
61+
"dcim.powerpanel": None,
62+
"dcim.powerport": ConsolePorts,
63+
"dcim.powerporttemplate": None,
64+
"dcim.rack": Racks,
65+
"dcim.rackreservation": RackReservations,
66+
"dcim.rackrole": None,
67+
"dcim.rearport": RearPorts,
68+
"dcim.rearporttemplate": None,
69+
"dcim.region": None,
70+
"dcim.site": None,
71+
"dcim.sitegroup": None,
72+
"dcim.virtualchassis": VirtualChassis,
73+
"extras.configcontext": None,
74+
"extras.configrevision": None,
75+
"extras.customfield": None,
76+
"extras.customlink": None,
77+
"extras.exporttemplate": None,
78+
"extras.imageattachment": None,
79+
"extras.jobresult": None,
80+
"extras.journalentry": None,
81+
"extras.objectchange": None,
82+
"extras.report": None,
83+
"extras.script": None,
84+
"extras.tag": None,
85+
"extras.taggeditem": None,
86+
"extras.webhook": None,
87+
"ipam.aggregate": Aggregates,
88+
"ipam.ASN": None,
89+
"ipam.FHRPgroup": None,
90+
"ipam.FHRPgroupassignment": None,
91+
"ipam.IPaddress": IpAddresses,
92+
"ipam.IPrange": None,
93+
"ipam.L2VPN": None,
94+
"ipam.L2VPNtermination": None,
95+
"ipam.prefix": Prefixes,
96+
"ipam.RIR": None,
97+
"ipam.role": None,
98+
"ipam.routetarget": None,
99+
"ipam.service": None,
100+
"ipam.servicetemplate": None,
101+
"ipam.VLAN": Vlans,
102+
"ipam.VLANgroup": VlanGroups,
103+
"ipam.VRF": None,
104+
"tenancy.contact": None,
105+
"tenancy.contactassignment": None,
106+
"tenancy.contactgroup": None,
107+
"tenancy.contactrole": None,
108+
"tenancy.tenant": None,
109+
"tenancy.tenantgroup": None,
110+
"virtualization.cluster": None,
111+
"virtualization.clustergroup": None,
112+
"virtualization.clustertype": None,
113+
"virtualization.interface": None,
114+
"virtualization.virtualmachine": VirtualMachines,
115+
"wireless.WirelessLAN": WirelessLans,
116+
"wireless.WirelessLANGroup": None,
117+
"wireless.wirelesslink": None,
118+
}

requirements-dev.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
black~=22.0
2-
pytest==6.2.*
3-
pytest-docker==0.10.*
1+
black~=22.10
2+
pytest==7.1.*
3+
pytest-docker==1.0.*
4+
PyYAML==6.0

setup.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
setup(
44
name="pynetbox",
55
description="NetBox API client library",
6-
url="https://github.com/digitalocean/pynetbox",
6+
url="https://github.com/netbox-community/netbox",
77
author="Zach Moody",
88
author_email="[email protected]",
99
license="Apache2",
@@ -20,6 +20,8 @@
2020
"Intended Audience :: Developers",
2121
"Development Status :: 5 - Production/Stable",
2222
"Programming Language :: Python :: 3",
23-
"Programming Language :: Python :: 3.6",
23+
"Programming Language :: Python :: 3.8",
24+
"Programming Language :: Python :: 3.9",
25+
"Programming Language :: Python :: 3.10",
2426
],
2527
)

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from packaging import version
55

66

7-
DEFAULT_NETBOX_VERSIONS = "2.11, 3.0, 3.1"
7+
DEFAULT_NETBOX_VERSIONS = "3.3"
88

99

1010
def pytest_addoption(parser):

tests/integration/conftest.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,8 @@ def get_netbox_docker_version_tag(netbox_version):
2828
"""
2929
major, minor = netbox_version.major, netbox_version.minor
3030

31-
if (major, minor) == (3, 1):
32-
tag = "1.5.1"
33-
elif (major, minor) == (3, 0):
34-
tag = "1.5.1"
35-
elif (major, minor) == (2, 11):
36-
tag = "1.2.0"
31+
if (major, minor) == (3, 3):
32+
tag = "2.2.0"
3733
else:
3834
raise NotImplementedError(
3935
"Version %s is not currently supported" % netbox_version
@@ -385,14 +381,10 @@ def docker_netbox_service(
385381
"""
386382
netbox_integration_version = request.param
387383

388-
if netbox_integration_version >= version.Version("2.10"):
389-
netbox_service_name = "netbox_v%s_netbox" % str(
390-
netbox_integration_version
391-
).replace(".", "_")
392-
else:
393-
netbox_service_name = "netbox_v%s_nginx" % str(
394-
netbox_integration_version
395-
).replace(".", "_")
384+
netbox_service_name = "netbox_v%s_netbox" % str(netbox_integration_version).replace(
385+
".", "_"
386+
)
387+
396388
netbox_service_port = 8080
397389
try:
398390
# `port_for` takes a container port and returns the corresponding host port

0 commit comments

Comments
 (0)