From 3f83f6192e47ec39cba242948be8e17771430572 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Wed, 24 Jan 2024 16:39:10 -0800 Subject: [PATCH] NetBox 3.7 VPN API Changes (#24) --- .pre-commit-config.yaml | 3 --- netbox_python/__init__.py | 3 ++- netbox_python/api/circuits.py | 2 +- netbox_python/api/dcim.py | 6 ++++- netbox_python/api/ipam.py | 9 +------ netbox_python/api/vpn.py | 47 +++++++++++++++++++++++++++++++++++ netbox_python/baseapi.py | 34 +++++++++++-------------- netbox_python/netbox.py | 6 ++--- netbox_python/rest.py | 6 ++--- requirements_dev.txt | 17 ++++++------- tests/test_netbox_python.py | 23 ----------------- 11 files changed, 83 insertions(+), 73 deletions(-) create mode 100644 netbox_python/api/vpn.py delete mode 100644 tests/test_netbox_python.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 47d6177..4055ad5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,9 +48,6 @@ repos: - id: python-no-log-warn - id: python-no-eval - id: python-use-type-annotations - - id: rst-backticks - - id: rst-directive-colons - - id: rst-inline-touching-normal - repo: https://github.com/mgedmin/check-manifest rev: "0.47" diff --git a/netbox_python/__init__.py b/netbox_python/__init__.py index acf853f..3b48ec2 100644 --- a/netbox_python/__init__.py +++ b/netbox_python/__init__.py @@ -1,8 +1,9 @@ """Top-level package for NetBox Python.""" +from importlib.metadata import version __author__ = """Arthur Hanson""" __email__ = "ahanson@netboxlabs.com" -__version__ = "0.1.0" +__version__ = version(__name__) from netbox_python.exceptions import NetBoxException from netbox_python.netbox import NetBoxClient diff --git a/netbox_python/api/circuits.py b/netbox_python/api/circuits.py index ca55a7a..9d8cdcc 100644 --- a/netbox_python/api/circuits.py +++ b/netbox_python/api/circuits.py @@ -21,7 +21,7 @@ class _circuits(APIResource): path = "circuits/circuits/" class _provider_networks(APIResource): - path = "circuits/provider_networks/" + path = "circuits/provider-networks/" class _providers(APIResource): path = "circuits/providers/" diff --git a/netbox_python/api/dcim.py b/netbox_python/api/dcim.py index 0934694..a80b273 100644 --- a/netbox_python/api/dcim.py +++ b/netbox_python/api/dcim.py @@ -1,4 +1,8 @@ -from netbox_python.baseapi import APIResource, CreateableAPIResource, ElevationAPIResource +from netbox_python.baseapi import ( + APIResource, + CreateableAPIResource, + ElevationAPIResource, +) from netbox_python.rest import Result diff --git a/netbox_python/api/ipam.py b/netbox_python/api/ipam.py index edf5885..3ae81e2 100644 --- a/netbox_python/api/ipam.py +++ b/netbox_python/api/ipam.py @@ -16,8 +16,6 @@ def __init__(self, client): self.ip_addresses = self._ip_addresses(client) self.ip_ranges = self._ip_ranges(client) self.ip_range = ip_range(client) - self.l2vpn_terminations = self._l2vpn_terminations(client) - self.l2vpns = self._l2vpns(client) self.prefixes = self._prefixes(client) self.rirs = self._rirs(client) self.roles = self._roles(client) @@ -25,6 +23,7 @@ def __init__(self, client): self.service_templates = self._service_templates(client) self.services = self._services(client) self.vlan_groups = self._vlan_groups(client) + self.vlan_group = vlan_group(client) self.vlans = self._vlans(client) self.vrfs = self._vrfs(client) super().__init__() @@ -50,12 +49,6 @@ class _ip_addresses(APIResource): class _ip_ranges(APIResource): path = "ipam/ip-ranges/" - class _l2vpn_terminations(APIResource): - path = "ipam/l2vpn-terminations/" - - class _l2vpns(APIResource): - path = "ipam/l2vpns/" - class _prefixes(APIResource): path = "ipam/prefixes/" diff --git a/netbox_python/api/vpn.py b/netbox_python/api/vpn.py new file mode 100644 index 0000000..26ab87e --- /dev/null +++ b/netbox_python/api/vpn.py @@ -0,0 +1,47 @@ +from netbox_python.baseapi import APIResource + + +class vpn: + def __init__(self, client): + self.ike_policies = self._ike_policies(client) + self.ike_proposals = self._ike_proposals(client) + self.ipsec_policies = self._ipsec_policies(client) + self.ipsec_proposals = self._ipsec_proposals(client) + self.ipsec_profiles = self._ipsec_profiles(client) + self.tunnel_groups = self._tunnel_groups(client) + self.tunnels = self._tunnels(client) + self.tunnel_terminations = self._tunnel_terminations(client) + self.l2vpns = self._l2vpns(client) + self.l2vpn_terminations = self._l2vpn_terminations(client) + + super().__init__() + + class _ike_policies(APIResource): + path = "vpn/ike-policies/" + + class _ike_proposals(APIResource): + path = "vpn/ike-proposals/" + + class _ipsec_policies(APIResource): + path = "vpn/ipsec-policies/" + + class _ipsec_proposals(APIResource): + path = "vpn/ipsec-proposals/" + + class _ipsec_profiles(APIResource): + path = "vpn/ipsec-profiles/" + + class _tunnel_groups(APIResource): + path = "vpn/tunnel-groups/" + + class _tunnels(APIResource): + path = "vpn/tunnels/" + + class _tunnel_terminations(APIResource): + path = "vpn/tunnel-terminations/" + + class _l2vpns(APIResource): + path = "vpn/l2vpns/" + + class _l2vpn_terminations(APIResource): + path = "vpn/l2vpn-terminations/" diff --git a/netbox_python/baseapi.py b/netbox_python/baseapi.py index e7b1752..3a2ce29 100644 --- a/netbox_python/baseapi.py +++ b/netbox_python/baseapi.py @@ -30,9 +30,7 @@ def paginate(self, result: Result) -> Result: next_token = result.pagination["next"] yield result while next_token: - result = self.client.get( - self.path, url_override=next_token, params=result.params - ) + result = self.client.get(self.path, url_override=next_token, params=result.params) yield result next_token = result.pagination["next"] @@ -97,31 +95,29 @@ class ROAPIResource( pass -class AvailableAPIResource( - baseapi, - CreateableAPIResource, - ListableAPIResource, -): - def create(self, id: str | int, *args, **kwargs) -> Result: +class CreateablePathAPIResource: + def create(self, *args, **kwargs) -> Result: path = self.path.format(id=id) return self._create(path, *args, **kwargs) - def list(self, id: str | int, **kwargs) -> Result: + +class ListablePathAPIResource: + def list(self, **kwargs) -> Result: path = self.path.format(id=id) return self._list(path, **kwargs) - def all(self, id: str | int, **kwargs): + def all(self, **kwargs): path = self.path.format(id=id) return self._all(path, **kwargs) -class ElevationAPIResource( + +class AvailableAPIResource( baseapi, - ListableAPIResource + CreateablePathAPIResource, + ListablePathAPIResource, ): - def list(self, id: str | int, **kwargs) -> Result: - path = self.path.format(id=id) - return self._list(path, **kwargs) + pass - def all(self, id: str | int, **kwargs): - path = self.path.format(id=id) - return self._all(path, **kwargs) + +class ElevationAPIResource(baseapi, ListablePathAPIResource): + pass diff --git a/netbox_python/netbox.py b/netbox_python/netbox.py index 02cfab5..3302079 100644 --- a/netbox_python/netbox.py +++ b/netbox_python/netbox.py @@ -9,6 +9,7 @@ from netbox_python.api.tenancy import tenancy from netbox_python.api.users import users from netbox_python.api.virtualization import virtualization +from netbox_python.api.vpn import vpn from netbox_python.api.wireless import wireless from netbox_python.baseapi import RetrievableRootAPIResource, baseapi from netbox_python.rest import RestClient @@ -30,9 +31,7 @@ def __init__(self, base_url: str, token: str, headers: Dict[str, str] = None): if token: headers["authorization"] = f"Token {token}" - url = base_url = "{}/api".format( - base_url if base_url[-1] != "/" else base_url[:-1] - ) + url = base_url = "{}/api".format(base_url if base_url[-1] != "/" else base_url[:-1]) self.circuits = circuits(self) self.core = core(self) @@ -43,6 +42,7 @@ def __init__(self, base_url: str, token: str, headers: Dict[str, str] = None): self.tenancy = tenancy(self) self.users = users(self) self.virtualization = virtualization(self) + self.vpn = vpn(self) self.wireless = wireless(self) super().__init__(base_url=url, headers=headers) diff --git a/netbox_python/rest.py b/netbox_python/rest.py index 23c5922..5a601e8 100644 --- a/netbox_python/rest.py +++ b/netbox_python/rest.py @@ -58,9 +58,7 @@ def request(self, method: str, path: str, **kwargs) -> Result: response = self._session.request(method=method, url=url, **kwargs) response.raise_for_status() except requests.HTTPError as http_error: - raise NetBoxException( - f"Invalid request from {self.base_url}: {http_error}" - ) from http_error + raise NetBoxException(f"Invalid request from {self.base_url}: {http_error}") from http_error except requests.RequestException as err: raise NetBoxException("Request failed") from err @@ -73,7 +71,7 @@ def request(self, method: str, path: str, **kwargs) -> Result: # If status_code in 200-299 range, return success Result with data, otherwise raise exception is_success = 299 >= response.status_code >= 200 # 200 to 299 is OK - no_content_success = response.status_code == 204 # 204 is OK. Means no content + no_content_success = response.status_code == 204 # 204 is OK. Means no content if is_success: # check if list - fixme: should have cleaner way to do this pagination = None diff --git a/requirements_dev.txt b/requirements_dev.txt index a9fafd0..664a8bb 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,12 +1,9 @@ -pip==23.0.1 -bump2version==1.0.1 -wheel==0.38.4 -watchdog==2.3.0 -flake8==6.0.0 -tox==4.4.6 -coverage==7.2.1 -twine==4.0.2 - -pytest==7.2.1 black==23.1.0 +flake8==6.0.0 +pip==23.0.1 pre-commit==3.1.0 + +pytest==7.2.* +twine==4.0.2 +watchdog==2.3.0 +wheel==0.38.4 diff --git a/tests/test_netbox_python.py b/tests/test_netbox_python.py deleted file mode 100644 index fc7b3b2..0000000 --- a/tests/test_netbox_python.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -"""Tests for `netbox_python` package.""" - -import pytest - -from netbox_python import netbox_python - - -@pytest.fixture -def response(): - """Sample pytest fixture. - - See more at: http://doc.pytest.org/en/latest/fixture.html - """ - # import requests - # return requests.get('https://github.com/audreyr/cookiecutter-pypackage') - - -def test_content(response): - """Sample pytest test function with the pytest fixture as an argument.""" - # from bs4 import BeautifulSoup - # assert 'GitHub' in BeautifulSoup(response.content).title.string