From c84ca29e2232cb463cc9351c2a34e1f9a879eaaf Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Thu, 8 Aug 2024 14:09:16 +0700 Subject: [PATCH] Develop (#635) --- .github/ISSUE_TEMPLATE/bug_report.yaml | 7 +++---- .github/workflows/py3.yml | 4 ++-- README.md | 13 +++++++++++++ docs/conf.py | 8 ++++---- pynetbox/__init__.py | 6 ++---- pynetbox/core/api.py | 2 +- pynetbox/core/app.py | 6 +++--- pynetbox/core/endpoint.py | 3 ++- pynetbox/core/query.py | 1 + pynetbox/core/response.py | 5 ++--- pynetbox/models/dcim.py | 11 +++-------- pynetbox/models/extras.py | 2 +- pynetbox/models/ipam.py | 2 +- pynetbox/models/mapper.py | 23 ++++++++--------------- pynetbox/models/users.py | 2 +- pynetbox/models/virtualization.py | 2 +- requirements.txt | 2 +- setup.py | 18 +++++++----------- tests/conftest.py | 3 +-- tests/fixtures/dcim/interface.json | 22 ++++++++-------------- tests/integration/conftest.py | 19 +++++++------------ tests/test_api.py | 2 +- tests/test_app.py | 1 - tests/test_circuits.py | 2 +- tests/test_tenancy.py | 2 +- tests/test_users.py | 2 +- tests/test_virtualization.py | 2 +- tests/test_wireless.py | 2 +- tests/unit/test_detailendpoint.py | 1 - tests/unit/test_endpoint.py | 10 +++++++++- tests/unit/test_response.py | 4 ++-- 31 files changed, 89 insertions(+), 100 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 0d7de397..acab2160 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -11,14 +11,14 @@ body: attributes: label: pynetbox version description: What version of pynetbox are you currently running? - placeholder: v7.1.0 + placeholder: v7.4.0 validations: required: true - type: input attributes: label: NetBox version description: What version of NetBox are you currently running? - placeholder: v3.6.0 + placeholder: v4.0.8 validations: required: true - type: dropdown @@ -26,10 +26,9 @@ body: label: Python version description: What version of Python are you currently running? options: - - "3.8" - - "3.9" - "3.10" - "3.11" + - "3.12" validations: required: true - type: textarea diff --git a/.github/workflows/py3.yml b/.github/workflows/py3.yml index 824bc34d..a38e3bc9 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", "3.11"] - netbox: ["3.4", "3.5", "3.6", "3.7"] + python: ["3.10", "3.11", "3.12"] + netbox: ["3.6", "3.7", "4.0"] steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index ae40b943..0e3d9ffa 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,19 @@ Python API client library for [NetBox](https://github.com/netbox-community/netbo > **Note:** Version 6.7 and later of the library only supports NetBox 3.3 and above. +## Compatibility + +Each pyNetBox Version listed below has been tested with its corresponding NetBox Version. + +| NetBox Version | Plugin Version | +|:--------------:|:--------------:| +| 4.0.6 | 7.4.0 | +| 4.0.0 | 7.3.4 | +| 3.7 | 7.3.0 | +| 3.6 | 7.2.0 | +| 3.5 | 7.1.0 | +| 3.3 | 7.0.0 | + ## Installation To install run `pip install pynetbox`. diff --git a/docs/conf.py b/docs/conf.py index 2f9af91f..2c11cb79 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,10 +8,10 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -project = 'pynetbox' -copyright = '2023, NetBox' -author = 'Abhimanyu Saharan' -release = 'Apache2' +project = "pynetbox" +copyright = "2023, NetBox" +author = "Abhimanyu Saharan" +release = "Apache2" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/pynetbox/__init__.py b/pynetbox/__init__.py index 07fcf69a..00e30d87 100644 --- a/pynetbox/__init__.py +++ b/pynetbox/__init__.py @@ -1,6 +1,4 @@ -from importlib.metadata import version - -from pynetbox.core.query import RequestError, AllocationError, ContentError from pynetbox.core.api import Api as api +from pynetbox.core.query import AllocationError, ContentError, RequestError -__version__ = version(__name__) +__version__ = "7.4.0" diff --git a/pynetbox/core/api.py b/pynetbox/core/api.py index fa30d03c..ac17d758 100644 --- a/pynetbox/core/api.py +++ b/pynetbox/core/api.py @@ -16,8 +16,8 @@ import requests -from pynetbox.core.query import Request from pynetbox.core.app import App, PluginsApp +from pynetbox.core.query import Request from pynetbox.core.response import Record diff --git a/pynetbox/core/app.py b/pynetbox/core/app.py index 1cd1083f..10cc6445 100644 --- a/pynetbox/core/app.py +++ b/pynetbox/core/app.py @@ -17,12 +17,12 @@ from pynetbox.core.endpoint import Endpoint from pynetbox.core.query import Request from pynetbox.models import ( - dcim, - ipam, - virtualization, circuits, + dcim, extras, + ipam, users, + virtualization, wireless, ) diff --git a/pynetbox/core/endpoint.py b/pynetbox/core/endpoint.py index c1ca7155..dfd989ec 100644 --- a/pynetbox/core/endpoint.py +++ b/pynetbox/core/endpoint.py @@ -286,8 +286,9 @@ def filter(self, *args, **kwargs): offset = kwargs.pop("offset") if "offset" in kwargs else None if limit == 0 and offset is not None: raise ValueError("offset requires a positive limit value") + filters = {x: y if y is not None else "null" for x, y in kwargs.items()} req = Request( - filters=kwargs, + filters=filters, base=self.url, token=self.token, http_session=self.api.http_session, diff --git a/pynetbox/core/query.py b/pynetbox/core/query.py index 08790bf3..eb5a4fe5 100644 --- a/pynetbox/core/query.py +++ b/pynetbox/core/query.py @@ -16,6 +16,7 @@ import concurrent.futures as cf import json + from packaging import version diff --git a/pynetbox/core/response.py b/pynetbox/core/response.py index b4954d3d..fd8cfaef 100644 --- a/pynetbox/core/response.py +++ b/pynetbox/core/response.py @@ -16,13 +16,12 @@ import copy from collections import OrderedDict +from urllib.parse import urlsplit import pynetbox.core.app -from urllib.parse import urlsplit from pynetbox.core.query import Request from pynetbox.core.util import Hashabledict - # List of fields that are lists but should be treated as sets. LIST_AS_SET = ("tags", "tagged_vlans") @@ -68,7 +67,7 @@ def flatten_custom(custom_dict): current_val = val.get("id", val) if isinstance(val, list): - current_val = [v.get("id") if isinstance(v, dict) else v for v in val] + current_val = [v.get("id", v) if isinstance(v, dict) else v for v in val] ret[k] = current_val return ret diff --git a/pynetbox/models/dcim.py b/pynetbox/models/dcim.py index 2e8b6dab..02533c03 100644 --- a/pynetbox/models/dcim.py +++ b/pynetbox/models/dcim.py @@ -16,11 +16,11 @@ from urllib.parse import urlsplit +from pynetbox.core.endpoint import DetailEndpoint, RODetailEndpoint from pynetbox.core.query import Request -from pynetbox.core.response import Record, JsonField -from pynetbox.core.endpoint import RODetailEndpoint, DetailEndpoint -from pynetbox.models.ipam import IpAddresses +from pynetbox.core.response import JsonField, Record from pynetbox.models.circuits import Circuits +from pynetbox.models.ipam import IpAddresses class TraceableRecord(Record): @@ -150,13 +150,8 @@ def __str__(self): return self.interface.name -class ConnectedEndpoint(Record): - device = Devices - - class Interfaces(TraceableRecord): interface_connection = InterfaceConnection - connected_endpoint = ConnectedEndpoint class PowerOutlets(TraceableRecord): diff --git a/pynetbox/models/extras.py b/pynetbox/models/extras.py index 168d2b76..e98acef7 100644 --- a/pynetbox/models/extras.py +++ b/pynetbox/models/extras.py @@ -14,7 +14,7 @@ limitations under the License. """ -from pynetbox.core.response import Record, JsonField +from pynetbox.core.response import JsonField, Record class ConfigContexts(Record): diff --git a/pynetbox/models/ipam.py b/pynetbox/models/ipam.py index 64e0f73c..3505b949 100644 --- a/pynetbox/models/ipam.py +++ b/pynetbox/models/ipam.py @@ -14,8 +14,8 @@ limitations under the License. """ -from pynetbox.core.response import Record from pynetbox.core.endpoint import DetailEndpoint +from pynetbox.core.response import Record class IpAddresses(Record): diff --git a/pynetbox/models/mapper.py b/pynetbox/models/mapper.py index bbd4ed27..1dcfc998 100644 --- a/pynetbox/models/mapper.py +++ b/pynetbox/models/mapper.py @@ -1,31 +1,24 @@ from .circuits import Circuits, CircuitTerminations from .dcim import ( - DeviceTypes, + Cables, + ConsolePorts, + ConsoleServerPorts, Devices, + DeviceTypes, + FrontPorts, Interfaces, PowerOutlets, PowerPorts, - ConsolePorts, - ConsoleServerPorts, RackReservations, - VirtualChassis, - FrontPorts, - RearPorts, Racks, + RearPorts, Termination, - Cables, -) -from .ipam import ( - IpAddresses, - Prefixes, - Aggregates, - Vlans, - VlanGroups, + VirtualChassis, ) +from .ipam import Aggregates, IpAddresses, Prefixes, VlanGroups, Vlans from .virtualization import VirtualMachines from .wireless import WirelessLans - CONTENT_TYPE_MAPPER = { "circuits.circuit": Circuits, "circuits.circuittermination": CircuitTerminations, diff --git a/pynetbox/models/users.py b/pynetbox/models/users.py index a82f6523..18aa3281 100644 --- a/pynetbox/models/users.py +++ b/pynetbox/models/users.py @@ -14,7 +14,7 @@ limitations under the License. """ -from pynetbox.core.response import Record, JsonField +from pynetbox.core.response import JsonField, Record class Users(Record): diff --git a/pynetbox/models/virtualization.py b/pynetbox/models/virtualization.py index 3c0a27a3..98ad6ed3 100644 --- a/pynetbox/models/virtualization.py +++ b/pynetbox/models/virtualization.py @@ -14,7 +14,7 @@ limitations under the License. """ -from pynetbox.core.response import Record, JsonField +from pynetbox.core.response import JsonField, Record from pynetbox.models.ipam import IpAddresses diff --git a/requirements.txt b/requirements.txt index c84e8abe..92e2fce1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ requests>=2.20.0,<3.0 -packaging \ No newline at end of file +packaging diff --git a/setup.py b/setup.py index 1b1a8786..390b9bb7 100644 --- a/setup.py +++ b/setup.py @@ -1,31 +1,27 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup setup( name="pynetbox", description="NetBox API client library", url="https://github.com/netbox-community/pynetbox", - author="Zach Moody", - author_email="zmoody@do.co", + author="Zach Moody, Arthur Hanson", + author_email="ahanson@netboxlabs.com", license="Apache2", include_package_data=True, use_scm_version=True, setup_requires=["setuptools_scm"], packages=find_packages(exclude=["tests", "tests.*"]), - long_description=open('README.md').read(), - long_description_content_type='text/markdown', - install_requires=[ - "requests>=2.20.0,<3.0", - "packaging" - ], + long_description=open("README.md").read(), + long_description_content_type="text/markdown", + install_requires=["requests>=2.20.0,<3.0", "packaging"], zip_safe=False, keywords=["netbox"], classifiers=[ "Intended Audience :: Developers", "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ], ) diff --git a/tests/conftest.py b/tests/conftest.py index aee6f30b..dde207fa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,8 +3,7 @@ import pytest from packaging import version - -DEFAULT_NETBOX_VERSIONS = "3.7" +DEFAULT_NETBOX_VERSIONS = "4.0" def pytest_addoption(parser): diff --git a/tests/fixtures/dcim/interface.json b/tests/fixtures/dcim/interface.json index 7ccfc66e..f5336572 100644 --- a/tests/fixtures/dcim/interface.json +++ b/tests/fixtures/dcim/interface.json @@ -22,22 +22,16 @@ "mgmt_only": false, "description": "", "is_connected": true, - "connected_endpoint": { - "id": 1, - "url": "http://localhost:8000/api/dcim/interfaces/1/", - "device": { + "connected_endpoints": [ + { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", - "name": "tst-endpoint", - "display_name": "tst-endpoint" - }, - "name": "eth0", - "cable": 1 - }, - "connection_status": { - "value": true, - "label": "Connected" - }, + "display": "tst-endpoint", + "name": "tst-endpoint" + } + ], + "connected_endpoints_type": "dcim.device", + "connected_endpoints_reachable": true, "cable": { "id": 1, "url": "http://localhost:8000/api/dcim/cables/1/", diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index b185e981..43e67870 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,15 +1,14 @@ +import atexit import os - import subprocess as subp import time -import yaml from http.client import RemoteDisconnected -import atexit -import pynetbox import pytest import requests +import yaml +import pynetbox DOCKER_PROJECT_PREFIX = "pytest_pynetbox" @@ -27,16 +26,12 @@ def get_netbox_docker_version_tag(netbox_version): """ major, minor = netbox_version.major, netbox_version.minor - if (major, minor) == (3, 3): - tag = "2.3.0" - elif (major, minor) == (3, 4): - tag = "2.5.3" - elif (major, minor) == (3, 5): - tag = "2.6.1" - elif (major, minor) == (3, 6): + if (major, minor) == (3, 6): tag = "2.7.0" elif (major, minor) == (3, 7): tag = "2.8.0" + elif (major, minor) == (4, 0): + tag = "2.9.1" else: raise NotImplementedError( "Version %s is not currently supported" % netbox_version @@ -235,7 +230,7 @@ def docker_compose_file(pytestconfig, netbox_docker_repo_dirpaths): ) compose_data["networks"] = {docker_network_name: {}} # https://docs.docker.com/compose/compose-file/compose-file-v3/#network-configuration-reference - if compose_data["version"] >= "3.5": + if "version" not in compose_data or compose_data["version"] >= "3.5": compose_data["networks"][docker_network_name][ "name" ] = docker_network_name diff --git a/tests/test_api.py b/tests/test_api.py index 48a927c9..ed6beafd 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -2,8 +2,8 @@ from unittest.mock import patch import pynetbox -from .util import Response +from .util import Response host = "http://localhost:8000" diff --git a/tests/test_app.py b/tests/test_app.py index 398415ab..4d9d53c4 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -3,7 +3,6 @@ import pynetbox - host = "http://localhost:8000" def_kwargs = { diff --git a/tests/test_circuits.py b/tests/test_circuits.py index 809178b4..16076a3a 100644 --- a/tests/test_circuits.py +++ b/tests/test_circuits.py @@ -1,9 +1,9 @@ import unittest from unittest.mock import patch -from .util import Response import pynetbox +from .util import Response api = pynetbox.api( "http://localhost:8000", diff --git a/tests/test_tenancy.py b/tests/test_tenancy.py index 211940b3..3c15fc6c 100644 --- a/tests/test_tenancy.py +++ b/tests/test_tenancy.py @@ -2,8 +2,8 @@ from unittest.mock import patch import pynetbox -from .util import Response +from .util import Response api = pynetbox.api( "http://localhost:8000", diff --git a/tests/test_users.py b/tests/test_users.py index e47022a1..08a31755 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -2,8 +2,8 @@ from unittest.mock import patch import pynetbox -from .util import Response +from .util import Response api = pynetbox.api( "http://localhost:8000", diff --git a/tests/test_virtualization.py b/tests/test_virtualization.py index 3b5fe136..b3d24846 100644 --- a/tests/test_virtualization.py +++ b/tests/test_virtualization.py @@ -1,9 +1,9 @@ import unittest from unittest.mock import patch -from .util import Response import pynetbox +from .util import Response api = pynetbox.api( "http://localhost:8000", diff --git a/tests/test_wireless.py b/tests/test_wireless.py index 47a7009a..5fe719f9 100644 --- a/tests/test_wireless.py +++ b/tests/test_wireless.py @@ -2,8 +2,8 @@ from unittest.mock import patch import pynetbox -from .util import Response +from .util import Response api = pynetbox.api("http://localhost:8000") diff --git a/tests/unit/test_detailendpoint.py b/tests/unit/test_detailendpoint.py index a2956b15..24a08ee0 100644 --- a/tests/unit/test_detailendpoint.py +++ b/tests/unit/test_detailendpoint.py @@ -3,7 +3,6 @@ import pynetbox - nb = pynetbox.api("http://localhost:8000") diff --git a/tests/unit/test_endpoint.py b/tests/unit/test_endpoint.py index 1aa84a3b..344b1168 100644 --- a/tests/unit/test_endpoint.py +++ b/tests/unit/test_endpoint.py @@ -1,5 +1,5 @@ import unittest -from unittest.mock import patch, Mock +from unittest.mock import Mock, patch from pynetbox.core.endpoint import Endpoint @@ -23,6 +23,14 @@ def test_filter_invalid_pagination_args(self): with self.assertRaises(ValueError) as _: test_obj.filter(offset=1) + def test_filter_replace_none_with_null(self): + api = Mock(base_url="http://localhost:8000/api") + app = Mock(name="test") + test_obj = Endpoint(api, app, "test") + test = test_obj.filter(name=None, id=0) + + self.assertEqual(test.request.filters, {"name": "null", "id": 0}) + def test_all_invalid_pagination_args(self): api = Mock(base_url="http://localhost:8000/api") app = Mock(name="test") diff --git a/tests/unit/test_response.py b/tests/unit/test_response.py index 68752ead..7f5d8a84 100644 --- a/tests/unit/test_response.py +++ b/tests/unit/test_response.py @@ -1,8 +1,8 @@ import unittest -from unittest.mock import patch, Mock +from unittest.mock import Mock, patch -from pynetbox.core.response import Record, RecordSet from pynetbox.core.endpoint import Endpoint +from pynetbox.core.response import Record, RecordSet class RecordTestCase(unittest.TestCase):