Skip to content

Commit

Permalink
Update test scripts and settings to netbox 3.3 (#508)
Browse files Browse the repository at this point in the history
Update test scripts and settings to NetBox 3.3
  • Loading branch information
arthanson authored Dec 9, 2022
1 parent cb86fd7 commit dd94cbe
Show file tree
Hide file tree
Showing 11 changed files with 224 additions and 78 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/py3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python: ["3.7", "3.10"]
netbox: ["2.11", "3.0", "3.1"]
python: ["3.8", "3.9", "3.10"]
netbox: ["3.3"]

steps:
- uses: actions/checkout@v2
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Pynetbox
Python API client library for [NetBox](https://github.com/netbox-community/netbox).

> **Note:** Version 6.7 and later of the library only supports NetBox 3.3 and above.
## Installation

Expand Down
2 changes: 1 addition & 1 deletion pynetbox/core/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(self, req):
)
)

super().__init__(message)
super().__init__(self.message)
self.req = req
self.request_body = req.request.body
self.base = req.url
Expand Down
26 changes: 24 additions & 2 deletions pynetbox/core/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,22 @@ def _parse_values(self, values):
values within.
"""

def generic_list_parser(key_name, list_item):
from pynetbox.models.mapper import CONTENT_TYPE_MAPPER

if (
isinstance(list_item, dict)
and "object_type" in list_item
and "object" in list_item
):
lookup = list_item["object_type"]
model = None
model = CONTENT_TYPE_MAPPER.get(lookup)
if model:
return model(list_item["object"], self.api, self.endpoint)

return list_item

def list_parser(key_name, list_item):
if isinstance(list_item, dict):
lookup = getattr(self.__class__, key_name, None)
Expand All @@ -364,6 +380,7 @@ def list_parser(key_name, list_item):
else:
model = lookup[0]
return model(list_item, self.api, self.endpoint)

return list_item

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

elif isinstance(v, list):
v = [list_parser(k, i) for i in v]
to_cache = list(v)
# check if GFK
if len(v) and isinstance(v[0], dict) and "object_type" in v[0]:
v = [generic_list_parser(k, i) for i in v]
to_cache = list(v)
else:
v = [list_parser(k, i) for i in v]
to_cache = list(v)
self._add_cache((k, to_cache))

else:
Expand Down
81 changes: 41 additions & 40 deletions pynetbox/models/dcim.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,35 @@


class TraceableRecord(Record):
def _get_obj_class(self, url):
uri_to_obj_class_map = {
"dcim/cables": Cables,
"dcim/front-ports": FrontPorts,
"dcim/interfaces": Interfaces,
"dcim/rear-ports": RearPorts,
}

# the url for this item will be something like:
# https://netbox/api/dcim/rear-ports/12761/
# TODO: Move this to a more general function.
app_endpoint = "/".join(
urlsplit(url).path[len(urlsplit(self.api.base_url).path) :].split("/")[1:3]
)
return uri_to_obj_class_map.get(
app_endpoint,
Record,
)

def _build_termination_data(self, termination_list):
terminations_data = []
for hop_item_data in termination_list:
return_obj_class = self._get_obj_class(hop_item_data["url"])
terminations_data.append(
return_obj_class(hop_item_data, self.endpoint.api, self.endpoint)
)

return terminations_data

def trace(self):
req = Request(
key=str(self.id) + "/trace",
Expand All @@ -31,38 +60,18 @@ def trace(self):
session_key=self.api.session_key,
http_session=self.api.http_session,
).get()
uri_to_obj_class_map = {
"dcim/cables": Cables,
"dcim/front-ports": FrontPorts,
"dcim/interfaces": Interfaces,
"dcim/rear-ports": RearPorts,
}

ret = []
for (termination_a_data, cable_data, termination_b_data) in req:
this_hop_ret = []
for hop_item_data in (termination_a_data, cable_data, termination_b_data):
# if not fully terminated then some items will be None
if not hop_item_data:
this_hop_ret.append(hop_item_data)
continue

# the url for this item will be something like:
# https://netbox/api/dcim/rear-ports/12761/
# TODO: Move this to a more general function.
app_endpoint = "/".join(
urlsplit(hop_item_data["url"])
.path[len(urlsplit(self.api.base_url).path) :]
.split("/")[1:3]
)
return_obj_class = uri_to_obj_class_map.get(
app_endpoint,
Record,
)
this_hop_ret.append(
return_obj_class(hop_item_data, self.endpoint.api, self.endpoint)
for (a_terminations_data, cable_data, b_terminations_data) in req:
ret.append(self._build_termination_data(a_terminations_data))
if not cable_data:
ret.append(cable_data)
else:
return_obj_class = self._get_obj_class(cable_data["url"])
ret.append(
return_obj_class(cable_data, self.endpoint.api, self.endpoint)
)

ret.append(this_hop_ret)
ret.append(self._build_termination_data(b_terminations_data))

return ret

Expand Down Expand Up @@ -225,14 +234,6 @@ def __str__(self):

class Cables(Record):
def __str__(self):
if all(
[
isinstance(i, Termination)
for i in (self.termination_a, self.termination_b)
]
):
return "{} <> {}".format(self.termination_a, self.termination_b)
if len(self.a_terminations) == 1 and len(self.b_terminations) == 1:
return "{} <> {}".format(self.a_terminations[0], self.b_terminations[0])
return "Cable #{}".format(self.id)

termination_a = Termination
termination_b = Termination
118 changes: 118 additions & 0 deletions pynetbox/models/mapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
from .circuits import Circuits, CircuitTerminations
from .dcim import (
DeviceTypes,
Devices,
Interfaces,
PowerOutlets,
PowerPorts,
ConsolePorts,
ConsoleServerPorts,
RackReservations,
VirtualChassis,
FrontPorts,
RearPorts,
Racks,
Termination,
Cables,
)
from .ipam import (
IpAddresses,
Prefixes,
Aggregates,
Vlans,
VlanGroups,
)
from .virtualization import VirtualMachines
from .wireless import WirelessLans


CONTENT_TYPE_MAPPER = {
"circuits.circuit": Circuits,
"circuits.circuittermination": CircuitTerminations,
"dcim.cable": Cables,
"dcim.cablepath": None,
"dcim.cabletermination": Termination,
"dcim.consoleport": ConsolePorts,
"dcim.consoleporttemplate": None,
"dcim.consoleserverport": ConsoleServerPorts,
"dcim.consoleserverporttemplate": None,
"dcim.device": Devices,
"dcim.devicebay": None,
"dcim.devicebaytemplate": None,
"dcim.devicerole": None,
"dcim.devicetype": DeviceTypes,
"dcim.frontport": FrontPorts,
"dcim.frontporttemplate": None,
"dcim.interface": Interfaces,
"dcim.interfacetemplate": None,
"dcim.inventoryitem": None,
"dcim.inventoryitemrole": None,
"dcim.inventoryitemtemplate": None,
"dcim.location": None,
"dcim.manufacturer": None,
"dcim.module": None,
"dcim.modulebay": None,
"dcim.modulebaytemplate": None,
"dcim.moduletype": None,
"dcim.platform": None,
"dcim.powerfeed": None,
"dcim.poweroutlet": PowerOutlets,
"dcim.poweroutlettemplate": None,
"dcim.powerpanel": None,
"dcim.powerport": ConsolePorts,
"dcim.powerporttemplate": None,
"dcim.rack": Racks,
"dcim.rackreservation": RackReservations,
"dcim.rackrole": None,
"dcim.rearport": RearPorts,
"dcim.rearporttemplate": None,
"dcim.region": None,
"dcim.site": None,
"dcim.sitegroup": None,
"dcim.virtualchassis": VirtualChassis,
"extras.configcontext": None,
"extras.configrevision": None,
"extras.customfield": None,
"extras.customlink": None,
"extras.exporttemplate": None,
"extras.imageattachment": None,
"extras.jobresult": None,
"extras.journalentry": None,
"extras.objectchange": None,
"extras.report": None,
"extras.script": None,
"extras.tag": None,
"extras.taggeditem": None,
"extras.webhook": None,
"ipam.aggregate": Aggregates,
"ipam.ASN": None,
"ipam.FHRPgroup": None,
"ipam.FHRPgroupassignment": None,
"ipam.IPaddress": IpAddresses,
"ipam.IPrange": None,
"ipam.L2VPN": None,
"ipam.L2VPNtermination": None,
"ipam.prefix": Prefixes,
"ipam.RIR": None,
"ipam.role": None,
"ipam.routetarget": None,
"ipam.service": None,
"ipam.servicetemplate": None,
"ipam.VLAN": Vlans,
"ipam.VLANgroup": VlanGroups,
"ipam.VRF": None,
"tenancy.contact": None,
"tenancy.contactassignment": None,
"tenancy.contactgroup": None,
"tenancy.contactrole": None,
"tenancy.tenant": None,
"tenancy.tenantgroup": None,
"virtualization.cluster": None,
"virtualization.clustergroup": None,
"virtualization.clustertype": None,
"virtualization.interface": None,
"virtualization.virtualmachine": VirtualMachines,
"wireless.WirelessLAN": WirelessLans,
"wireless.WirelessLANGroup": None,
"wireless.wirelesslink": None,
}
7 changes: 4 additions & 3 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
black~=22.0
pytest==6.2.*
pytest-docker==0.10.*
black~=22.10
pytest==7.1.*
pytest-docker==1.0.*
PyYAML==6.0
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
setup(
name="pynetbox",
description="NetBox API client library",
url="https://github.com/digitalocean/pynetbox",
url="https://github.com/netbox-community/netbox",
author="Zach Moody",
author_email="[email protected]",
license="Apache2",
Expand All @@ -20,6 +20,8 @@
"Intended Audience :: Developers",
"Development Status :: 5 - Production/Stable",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
],
)
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from packaging import version


DEFAULT_NETBOX_VERSIONS = "2.11, 3.0, 3.1"
DEFAULT_NETBOX_VERSIONS = "3.3"


def pytest_addoption(parser):
Expand Down
20 changes: 6 additions & 14 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,8 @@ def get_netbox_docker_version_tag(netbox_version):
"""
major, minor = netbox_version.major, netbox_version.minor

if (major, minor) == (3, 1):
tag = "1.5.1"
elif (major, minor) == (3, 0):
tag = "1.5.1"
elif (major, minor) == (2, 11):
tag = "1.2.0"
if (major, minor) == (3, 3):
tag = "2.2.0"
else:
raise NotImplementedError(
"Version %s is not currently supported" % netbox_version
Expand Down Expand Up @@ -385,14 +381,10 @@ def docker_netbox_service(
"""
netbox_integration_version = request.param

if netbox_integration_version >= version.Version("2.10"):
netbox_service_name = "netbox_v%s_netbox" % str(
netbox_integration_version
).replace(".", "_")
else:
netbox_service_name = "netbox_v%s_nginx" % str(
netbox_integration_version
).replace(".", "_")
netbox_service_name = "netbox_v%s_netbox" % str(netbox_integration_version).replace(
".", "_"
)

netbox_service_port = 8080
try:
# `port_for` takes a container port and returns the corresponding host port
Expand Down
Loading

0 comments on commit dd94cbe

Please sign in to comment.