From 52b6a32c62f1453b349f6e13ca3b0d61d0f77b64 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 24 Oct 2022 10:30:48 -0700 Subject: [PATCH 01/21] Update test scripts and settings to netbox 3.3 --- .github/workflows/py3.yml | 4 ++-- requirements-dev.txt | 6 +++--- setup.py | 6 ++++-- tests/conftest.py | 2 +- tests/integration/conftest.py | 10 +++++----- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.github/workflows/py3.yml b/.github/workflows/py3.yml index af53b0e1..8583edb3 100644 --- a/.github/workflows/py3.yml +++ b/.github/workflows/py3.yml @@ -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.1", "3.2", "3.3"] steps: - uses: actions/checkout@v2 diff --git a/requirements-dev.txt b/requirements-dev.txt index f1cbd8c6..85de0ffb 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,3 @@ -black~=22.0 -pytest==6.2.* -pytest-docker==0.10.* +black~=22.10 +pytest==7.1.* +pytest-docker==1.0.* diff --git a/setup.py b/setup.py index 61f02a0d..9e1d429c 100644 --- a/setup.py +++ b/setup.py @@ -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="zmoody@do.co", license="Apache2", @@ -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", ], ) diff --git a/tests/conftest.py b/tests/conftest.py index bb202617..beadf8e6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ from packaging import version -DEFAULT_NETBOX_VERSIONS = "2.11, 3.0, 3.1" +DEFAULT_NETBOX_VERSIONS = "3.1, 3.2, 3.3" def pytest_addoption(parser): diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index aa0cc61f..d3635bd9 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -28,12 +28,12 @@ def get_netbox_docker_version_tag(netbox_version): """ major, minor = netbox_version.major, netbox_version.minor - if (major, minor) == (3, 1): + if (major, minor) == (3, 3): + tag = "2.2.0" + elif (major, minor) == (3, 2): + tag = "2.1.0" + elif (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" else: raise NotImplementedError( "Version %s is not currently supported" % netbox_version From c72780a7faa879403067e1a8fadc8d28adf06c23 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 24 Oct 2022 10:45:18 -0700 Subject: [PATCH 02/21] backout change --- requirements-dev.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 85de0ffb..f1cbd8c6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,3 @@ -black~=22.10 -pytest==7.1.* -pytest-docker==1.0.* +black~=22.0 +pytest==6.2.* +pytest-docker==0.10.* From d06fb3061910d2011c810827527e961c2b64933e Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 24 Oct 2022 10:57:07 -0700 Subject: [PATCH 03/21] backout change --- .github/workflows/py3.yml | 4 ++-- tests/conftest.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/py3.yml b/.github/workflows/py3.yml index 8583edb3..2eda96fe 100644 --- a/.github/workflows/py3.yml +++ b/.github/workflows/py3.yml @@ -12,8 +12,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['3.8', '3.9', '3.10'] - netbox: ["3.1", "3.2", "3.3"] + python: ["3.8", "3.9", "3.10"] + netbox: ["3.1"] steps: - uses: actions/checkout@v2 diff --git a/tests/conftest.py b/tests/conftest.py index beadf8e6..f295cae2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ from packaging import version -DEFAULT_NETBOX_VERSIONS = "3.1, 3.2, 3.3" +DEFAULT_NETBOX_VERSIONS = "3.1" def pytest_addoption(parser): From 2eab192aae01e89ce42bb8145c5ec7e22e4a47b6 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 24 Oct 2022 11:06:03 -0700 Subject: [PATCH 04/21] update to netbox 3.2 --- .github/workflows/py3.yml | 2 +- tests/conftest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/py3.yml b/.github/workflows/py3.yml index 2eda96fe..83cc13d0 100644 --- a/.github/workflows/py3.yml +++ b/.github/workflows/py3.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: python: ["3.8", "3.9", "3.10"] - netbox: ["3.1"] + netbox: ["3.1", "3.2"] steps: - uses: actions/checkout@v2 diff --git a/tests/conftest.py b/tests/conftest.py index f295cae2..24e2236c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ from packaging import version -DEFAULT_NETBOX_VERSIONS = "3.1" +DEFAULT_NETBOX_VERSIONS = "3.1, 3.2" def pytest_addoption(parser): From 44a89c69d40157733424d8fa4f9362afa599d5c5 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 24 Oct 2022 11:20:27 -0700 Subject: [PATCH 05/21] update dev requirements --- requirements-dev.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index f1cbd8c6..ce2b2e5f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -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 From fb8c6c7eed006563cc13d8fcb740a758a66d3dbf Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 24 Oct 2022 11:31:18 -0700 Subject: [PATCH 06/21] update to netbox 3.3 --- .github/workflows/py3.yml | 2 +- tests/conftest.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/py3.yml b/.github/workflows/py3.yml index 83cc13d0..49815edc 100644 --- a/.github/workflows/py3.yml +++ b/.github/workflows/py3.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: python: ["3.8", "3.9", "3.10"] - netbox: ["3.1", "3.2"] + netbox: ["3.1", "3.2", "3.3"] steps: - uses: actions/checkout@v2 diff --git a/tests/conftest.py b/tests/conftest.py index 24e2236c..beadf8e6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ from packaging import version -DEFAULT_NETBOX_VERSIONS = "3.1, 3.2" +DEFAULT_NETBOX_VERSIONS = "3.1, 3.2, 3.3" def pytest_addoption(parser): From 95112f62d23d9dcc40c9acb3120ef5cc2857c4dc Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 24 Oct 2022 13:50:26 -0700 Subject: [PATCH 07/21] remove 3.1 --- .github/workflows/py3.yml | 2 +- tests/conftest.py | 2 +- tests/integration/conftest.py | 14 +++++--------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/.github/workflows/py3.yml b/.github/workflows/py3.yml index 49815edc..5a7662be 100644 --- a/.github/workflows/py3.yml +++ b/.github/workflows/py3.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: python: ["3.8", "3.9", "3.10"] - netbox: ["3.1", "3.2", "3.3"] + netbox: ["3.2", "3.3"] steps: - uses: actions/checkout@v2 diff --git a/tests/conftest.py b/tests/conftest.py index beadf8e6..bcdbd8a6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ from packaging import version -DEFAULT_NETBOX_VERSIONS = "3.1, 3.2, 3.3" +DEFAULT_NETBOX_VERSIONS = "3.2, 3.3" def pytest_addoption(parser): diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index d3635bd9..29ff0359 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -33,7 +33,7 @@ def get_netbox_docker_version_tag(netbox_version): elif (major, minor) == (3, 2): tag = "2.1.0" elif (major, minor) == (3, 1): - tag = "1.5.1" + tag = "1.6.0" else: raise NotImplementedError( "Version %s is not currently supported" % netbox_version @@ -385,14 +385,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 From 19a3563438764059821fc84fa14ba35d0d756363 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 24 Oct 2022 14:04:04 -0700 Subject: [PATCH 08/21] fix message error in pynetbox --- pynetbox/core/query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pynetbox/core/query.py b/pynetbox/core/query.py index 5ab16e33..af9daf4b 100644 --- a/pynetbox/core/query.py +++ b/pynetbox/core/query.py @@ -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 From 942b7f80a65aefd1a59b526211a5dc2fe293ae24 Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 6 Dec 2022 13:37:00 -0800 Subject: [PATCH 09/21] 491 add gfk mapping for cable terminations --- pynetbox/core/query.py | 1 + pynetbox/core/response.py | 22 ++++++++++++++++++++-- pynetbox/models/dcim.py | 17 +++++++---------- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/pynetbox/core/query.py b/pynetbox/core/query.py index 5ab16e33..f8fb5dcf 100644 --- a/pynetbox/core/query.py +++ b/pynetbox/core/query.py @@ -315,6 +315,7 @@ def get(self, add_params=None): # if non-zero limit and some offset -> add offset add_params["offset"] = self.offset req = self._make_call(add_params=add_params) + # print(f"req: {req}") if isinstance(req, dict) and req.get("results") is not None: self.count = req["count"] if self.offset is not None: diff --git a/pynetbox/core/response.py b/pynetbox/core/response.py index 96fcb8fd..0bba6b1b 100644 --- a/pynetbox/core/response.py +++ b/pynetbox/core/response.py @@ -354,6 +354,18 @@ 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) @@ -364,6 +376,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(): @@ -382,8 +395,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: diff --git a/pynetbox/models/dcim.py b/pynetbox/models/dcim.py index 5b1e2783..4170fdf9 100644 --- a/pynetbox/models/dcim.py +++ b/pynetbox/models/dcim.py @@ -225,14 +225,11 @@ 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 all( + # [ + # isinstance(i, Termination) + # for i in (self.a_terminations, self.b_terminations) + # ] + # ): + # return "{} <> {}".format(self.a_terminations, self.b_terminations) return "Cable #{}".format(self.id) - - termination_a = Termination - termination_b = Termination From 044b84da99f1dd5b2db107fdf48b2b21188bccfd Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 6 Dec 2022 13:56:36 -0800 Subject: [PATCH 10/21] 491 cleanup --- pynetbox/core/query.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pynetbox/core/query.py b/pynetbox/core/query.py index f8fb5dcf..5ab16e33 100644 --- a/pynetbox/core/query.py +++ b/pynetbox/core/query.py @@ -315,7 +315,6 @@ def get(self, add_params=None): # if non-zero limit and some offset -> add offset add_params["offset"] = self.offset req = self._make_call(add_params=add_params) - # print(f"req: {req}") if isinstance(req, dict) and req.get("results") is not None: self.count = req["count"] if self.offset is not None: From b08b4163f76f4d55e930ae35a07f40fc4f06a30b Mon Sep 17 00:00:00 2001 From: Arthur Date: Tue, 6 Dec 2022 13:57:30 -0800 Subject: [PATCH 11/21] 491 add mapping file --- pynetbox/models/mapper.py | 118 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 pynetbox/models/mapper.py diff --git a/pynetbox/models/mapper.py b/pynetbox/models/mapper.py new file mode 100644 index 00000000..a57c07f4 --- /dev/null +++ b/pynetbox/models/mapper.py @@ -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, +} From 54e8435115756f0153c0945ad5e4d40db572bb15 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 7 Dec 2022 11:31:39 -0800 Subject: [PATCH 12/21] remove older netbox --- .github/workflows/py3.yml | 2 +- tests/conftest.py | 2 +- tests/integration/conftest.py | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/py3.yml b/.github/workflows/py3.yml index 5a7662be..5a7f7322 100644 --- a/.github/workflows/py3.yml +++ b/.github/workflows/py3.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: python: ["3.8", "3.9", "3.10"] - netbox: ["3.2", "3.3"] + netbox: ["3.3"] steps: - uses: actions/checkout@v2 diff --git a/tests/conftest.py b/tests/conftest.py index bcdbd8a6..ee66593d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ from packaging import version -DEFAULT_NETBOX_VERSIONS = "3.2, 3.3" +DEFAULT_NETBOX_VERSIONS = "3.3" def pytest_addoption(parser): diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 29ff0359..7829bea0 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -30,10 +30,6 @@ def get_netbox_docker_version_tag(netbox_version): if (major, minor) == (3, 3): tag = "2.2.0" - elif (major, minor) == (3, 2): - tag = "2.1.0" - elif (major, minor) == (3, 1): - tag = "1.6.0" else: raise NotImplementedError( "Version %s is not currently supported" % netbox_version From 961d2b4e28cb27283801abca9cde2d32166680cb Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 7 Dec 2022 11:34:43 -0800 Subject: [PATCH 13/21] fix black formatting --- pynetbox/core/response.py | 6 +++++- tests/integration/conftest.py | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pynetbox/core/response.py b/pynetbox/core/response.py index 0bba6b1b..85028320 100644 --- a/pynetbox/core/response.py +++ b/pynetbox/core/response.py @@ -357,7 +357,11 @@ def _parse_values(self, values): 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: + 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) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 7829bea0..c3e8d8c1 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -381,9 +381,9 @@ def docker_netbox_service( """ netbox_integration_version = request.param - netbox_service_name = "netbox_v%s_netbox" % str( - netbox_integration_version - ).replace(".", "_") + netbox_service_name = "netbox_v%s_netbox" % str(netbox_integration_version).replace( + ".", "_" + ) netbox_service_port = 8080 try: From 42b58ce8ce8f69b3dcfa7d867bda9481ccc0f4bf Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 7 Dec 2022 12:53:29 -0800 Subject: [PATCH 14/21] fix tests --- tests/integration/test_dcim.py | 36 ++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/tests/integration/test_dcim.py b/tests/integration/test_dcim.py index 6f5d2880..11e2ff4f 100644 --- a/tests/integration/test_dcim.py +++ b/tests/integration/test_dcim.py @@ -207,10 +207,14 @@ def power_port(self, api, device): @pytest.fixture(scope="class") def power_cable(self, api, power_outlet, power_port): cable = api.dcim.cables.create( - termination_a_id=power_port.id, - termination_a_type="dcim.powerport", - termination_b_id=power_outlet.id, - termination_b_type="dcim.poweroutlet", + a_terminations=[{ + "object_type": "dcim.powerport", + "object_id": power_port.id + }, ], + b_terminations=[{ + "object_type": "dcim.poweroutlet", + "object_id": power_outlet.id + }, ], ) yield cable cable.delete() @@ -247,10 +251,14 @@ def console_port(self, api, device): @pytest.fixture(scope="class") def console_cable(self, api, console_port, console_server_port): ret = api.dcim.cables.create( - termination_a_id=console_port.id, - termination_a_type="dcim.consoleport", - termination_b_id=console_server_port.id, - termination_b_type="dcim.consoleserverport", + a_terminations=[{ + "object_type": "dcim.consoleport", + "object_id": console_port.id + }, ], + b_terminations=[{ + "object_type": "dcim.consoleserverport", + "object_id": console_server_port.id + }, ], ) yield ret ret.delete() @@ -291,10 +299,14 @@ def interface_a(self, api, device): @pytest.fixture(scope="class") def interface_cable(self, api, interface_a, interface_b): ret = api.dcim.cables.create( - termination_a_id=interface_a.id, - termination_a_type="dcim.interface", - termination_b_id=interface_b.id, - termination_b_type="dcim.interface", + a_terminations=[{ + "object_type": "dcim.interface", + "object_id": interface_a.id + }, ], + b_terminations=[{ + "object_type": "dcim.interface", + "object_id": interface_b.id + }, ], ) yield ret ret.delete() From 08350064e2bd8f4223f76a298158d73548087cd0 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 7 Dec 2022 12:59:04 -0800 Subject: [PATCH 15/21] fix black formatting --- tests/integration/test_dcim.py | 45 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/tests/integration/test_dcim.py b/tests/integration/test_dcim.py index 11e2ff4f..1f99d023 100644 --- a/tests/integration/test_dcim.py +++ b/tests/integration/test_dcim.py @@ -207,14 +207,12 @@ def power_port(self, api, device): @pytest.fixture(scope="class") def power_cable(self, api, power_outlet, power_port): cable = api.dcim.cables.create( - a_terminations=[{ - "object_type": "dcim.powerport", - "object_id": power_port.id - }, ], - b_terminations=[{ - "object_type": "dcim.poweroutlet", - "object_id": power_outlet.id - }, ], + a_terminations=[ + {"object_type": "dcim.powerport", "object_id": power_port.id}, + ], + b_terminations=[ + {"object_type": "dcim.poweroutlet", "object_id": power_outlet.id}, + ], ) yield cable cable.delete() @@ -251,14 +249,15 @@ def console_port(self, api, device): @pytest.fixture(scope="class") def console_cable(self, api, console_port, console_server_port): ret = api.dcim.cables.create( - a_terminations=[{ - "object_type": "dcim.consoleport", - "object_id": console_port.id - }, ], - b_terminations=[{ - "object_type": "dcim.consoleserverport", - "object_id": console_server_port.id - }, ], + a_terminations=[ + {"object_type": "dcim.consoleport", "object_id": console_port.id}, + ], + b_terminations=[ + { + "object_type": "dcim.consoleserverport", + "object_id": console_server_port.id, + }, + ], ) yield ret ret.delete() @@ -299,14 +298,12 @@ def interface_a(self, api, device): @pytest.fixture(scope="class") def interface_cable(self, api, interface_a, interface_b): ret = api.dcim.cables.create( - a_terminations=[{ - "object_type": "dcim.interface", - "object_id": interface_a.id - }, ], - b_terminations=[{ - "object_type": "dcim.interface", - "object_id": interface_b.id - }, ], + a_terminations=[ + {"object_type": "dcim.interface", "object_id": interface_a.id}, + ], + b_terminations=[ + {"object_type": "dcim.interface", "object_id": interface_b.id}, + ], ) yield ret ret.delete() From 35bab61fb0188068b3003c2e377b13450865c08a Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 7 Dec 2022 13:22:15 -0800 Subject: [PATCH 16/21] fix tests --- pynetbox/models/dcim.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pynetbox/models/dcim.py b/pynetbox/models/dcim.py index 4170fdf9..3959f635 100644 --- a/pynetbox/models/dcim.py +++ b/pynetbox/models/dcim.py @@ -225,11 +225,6 @@ def __str__(self): class Cables(Record): def __str__(self): - # if all( - # [ - # isinstance(i, Termination) - # for i in (self.a_terminations, self.b_terminations) - # ] - # ): - # return "{} <> {}".format(self.a_terminations, self.b_terminations) + 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) From d292fff2f3d6160234d49d15482f635700f70744 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 7 Dec 2022 15:17:39 -0800 Subject: [PATCH 17/21] fix trace --- pynetbox/models/dcim.py | 66 +++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/pynetbox/models/dcim.py b/pynetbox/models/dcim.py index 3959f635..c22c5a38 100644 --- a/pynetbox/models/dcim.py +++ b/pynetbox/models/dcim.py @@ -23,6 +23,36 @@ 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) + ) + + def trace(self): req = Request( key=str(self.id) + "/trace", @@ -31,36 +61,20 @@ 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: + for (a_terminations_data, cable_data, b_terminations_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(self._build_termination_data(a_terminations_data)) + if not cable_data: + this_hop_ret.append(cable_data) + else: + return_obj_class = self._get_obj_class(cable_data["url"]) this_hop_ret.append( - return_obj_class(hop_item_data, self.endpoint.api, self.endpoint) + return_obj_class(cable_data, self.endpoint.api, self.endpoint) ) + this_hop_ret.append(self._build_termination_data(b_terminations_data)) ret.append(this_hop_ret) From cdb5b833f0b9c103df219691e7ffa678701d5174 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 7 Dec 2022 15:17:46 -0800 Subject: [PATCH 18/21] fix trace --- pynetbox/models/dcim.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pynetbox/models/dcim.py b/pynetbox/models/dcim.py index c22c5a38..08709c57 100644 --- a/pynetbox/models/dcim.py +++ b/pynetbox/models/dcim.py @@ -35,9 +35,7 @@ def _get_obj_class(self, url): # 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] + urlsplit(url).path[len(urlsplit(self.api.base_url).path) :].split("/")[1:3] ) return uri_to_obj_class_map.get( app_endpoint, @@ -52,7 +50,6 @@ def _build_termination_data(self, termination_list): return_obj_class(hop_item_data, self.endpoint.api, self.endpoint) ) - def trace(self): req = Request( key=str(self.id) + "/trace", From 2a91f25dd9daecfc0f2993cae4c14e1d005ced17 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 7 Dec 2022 15:36:56 -0800 Subject: [PATCH 19/21] fix trace --- pynetbox/models/dcim.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pynetbox/models/dcim.py b/pynetbox/models/dcim.py index 08709c57..43c5dde2 100644 --- a/pynetbox/models/dcim.py +++ b/pynetbox/models/dcim.py @@ -50,6 +50,8 @@ def _build_termination_data(self, termination_list): return_obj_class(hop_item_data, self.endpoint.api, self.endpoint) ) + return terminations_data + def trace(self): req = Request( key=str(self.id) + "/trace", @@ -61,19 +63,15 @@ def trace(self): ret = [] for (a_terminations_data, cable_data, b_terminations_data) in req: - this_hop_ret = [] - - this_hop_ret.append(self._build_termination_data(a_terminations_data)) + ret.append(self._build_termination_data(a_terminations_data)) if not cable_data: - this_hop_ret.append(cable_data) + ret.append(cable_data) else: return_obj_class = self._get_obj_class(cable_data["url"]) - this_hop_ret.append( + ret.append( return_obj_class(cable_data, self.endpoint.api, self.endpoint) ) - this_hop_ret.append(self._build_termination_data(b_terminations_data)) - - ret.append(this_hop_ret) + ret.append(self._build_termination_data(b_terminations_data)) return ret From 52a867c29c17caf45718e08dd62b21562a54b60b Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 7 Dec 2022 15:46:44 -0800 Subject: [PATCH 20/21] fix trace --- tests/integration/test_dcim.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_dcim.py b/tests/integration/test_dcim.py index 1f99d023..0f60bdc7 100644 --- a/tests/integration/test_dcim.py +++ b/tests/integration/test_dcim.py @@ -322,4 +322,4 @@ def test_trace(self, interface_a): test = interface_a.trace() assert test assert test[0][0].name == "Ethernet1" - assert test[0][2].name == "Ethernet1" + assert test[2][0].name == "Ethernet1" From bae5aa674c2e258100fa9ca4d10552ef686f9339 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 7 Dec 2022 18:03:25 -0800 Subject: [PATCH 21/21] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 72ec8416..522550c4 100644 --- a/README.md +++ b/README.md @@ -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