From 712ed330956ab34b6cec82c19dd9e8e6596f2be0 Mon Sep 17 00:00:00 2001 From: Arthur Date: Wed, 12 Apr 2023 16:16:01 -0700 Subject: [PATCH 1/2] #8 add available apis --- netbox_python/api/__init__.py | 4 ++++ netbox_python/api/asn_range.py | 10 ++++++++++ netbox_python/api/ip_range.py | 10 ++++++++++ netbox_python/api/ipam.py | 10 ++++++++++ netbox_python/api/prefix.py | 14 +++++++++++++ netbox_python/api/vlan_group.py | 10 ++++++++++ netbox_python/baseapi.py | 35 +++++++++++++++++++++++++++++---- 7 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 netbox_python/api/asn_range.py create mode 100644 netbox_python/api/ip_range.py create mode 100644 netbox_python/api/prefix.py create mode 100644 netbox_python/api/vlan_group.py diff --git a/netbox_python/api/__init__.py b/netbox_python/api/__init__.py index b027461..2a950da 100644 --- a/netbox_python/api/__init__.py +++ b/netbox_python/api/__init__.py @@ -1,10 +1,14 @@ +from netbox_python.api.asn_range import asn_range from netbox_python.api.circuits import circuits from netbox_python.api.core import core from netbox_python.api.dcim import dcim from netbox_python.api.extras import extras +from netbox_python.api.ip_range import ip_range from netbox_python.api.ipam import ipam from netbox_python.api.plugins import plugins +from netbox_python.api.prefix import prefix 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.vlan_group import vlan_group from netbox_python.api.wireless import wireless diff --git a/netbox_python/api/asn_range.py b/netbox_python/api/asn_range.py new file mode 100644 index 0000000..9d91eef --- /dev/null +++ b/netbox_python/api/asn_range.py @@ -0,0 +1,10 @@ +from netbox_python.baseapi import AvailableAPIResource + + +class asn_range: + def __init__(self, client): + self.available_asns = self._available_asns(client) + super().__init__() + + class _available_asns(AvailableAPIResource): + path = "ipam/asn-ranges/{id}/available-asns" diff --git a/netbox_python/api/ip_range.py b/netbox_python/api/ip_range.py new file mode 100644 index 0000000..239555f --- /dev/null +++ b/netbox_python/api/ip_range.py @@ -0,0 +1,10 @@ +from netbox_python.baseapi import AvailableAPIResource + + +class ip_range: + def __init__(self, client): + self.available_ips = self._available_ips(client) + super().__init__() + + class _available_ips(AvailableAPIResource): + path = "ipam/ip-ranges/{id}/available-ips" diff --git a/netbox_python/api/ipam.py b/netbox_python/api/ipam.py index 4240c2a..edf5885 100644 --- a/netbox_python/api/ipam.py +++ b/netbox_python/api/ipam.py @@ -1,3 +1,7 @@ +from netbox_python.api.asn_range import asn_range +from netbox_python.api.ip_range import ip_range +from netbox_python.api.prefix import prefix +from netbox_python.api.vlan_group import vlan_group from netbox_python.baseapi import APIResource @@ -5,10 +9,13 @@ class ipam: def __init__(self, client): self.aggregates = self._aggregates(client) self.asns = self._asns(client) + self.asn_ranges = self._asn_ranges(client) + self.asn_range = asn_range(client) self.fhrp_group_assignments = self._fhrp_group_assignments(client) self.fhrp_groups = self._fhrp_groups(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) @@ -28,6 +35,9 @@ class _aggregates(APIResource): class _asns(APIResource): path = "ipam/asns/" + class _asn_ranges(APIResource): + path = "ipam/asn-ranges/" + class _fhrp_group_assignments(APIResource): path = "ipam/fhrp-group-assignments/" diff --git a/netbox_python/api/prefix.py b/netbox_python/api/prefix.py new file mode 100644 index 0000000..9dd4a04 --- /dev/null +++ b/netbox_python/api/prefix.py @@ -0,0 +1,14 @@ +from netbox_python.baseapi import AvailableAPIResource + + +class prefix: + def __init__(self, client): + self.available_ips = self._available_ips(client) + self.available_prefixes = self._available_prefixes(client) + super().__init__() + + class _available_ips(AvailableAPIResource): + path = "ipam/prefixes/{id}/available-ips" + + class _available_prefixes(AvailableAPIResource): + path = "ipam/prefixes/{id}/available-prefixes" diff --git a/netbox_python/api/vlan_group.py b/netbox_python/api/vlan_group.py new file mode 100644 index 0000000..f20a6d8 --- /dev/null +++ b/netbox_python/api/vlan_group.py @@ -0,0 +1,10 @@ +from netbox_python.baseapi import AvailableAPIResource + + +class vlan_group: + def __init__(self, client): + self.available_vlans = self._available_vlans(client) + super().__init__() + + class _available_vlans(AvailableAPIResource): + path = "ipam/vlan-groups/{id}/available-vlans" diff --git a/netbox_python/baseapi.py b/netbox_python/baseapi.py index d26289b..b62ffc3 100644 --- a/netbox_python/baseapi.py +++ b/netbox_python/baseapi.py @@ -10,8 +10,11 @@ def __init__(self, client): class CreateableAPIResource: + def _create(self, path, *args, **kwargs) -> Result: + return self.client.post(path, json=args[0] if args else kwargs) + def create(self, *args, **kwargs) -> Result: - return self.client.post(self.path, json=args[0] if args else kwargs) + return self._create(self.path, *args, **kwargs) class DeletableAPIResource: @@ -33,12 +36,15 @@ def paginate(self, result: Result) -> Result: yield result next_token = result.pagination["next"] + def _list(self, path, **kwargs) -> Result: + return self.client.get(path, params=kwargs) + def list(self, **kwargs) -> Result: - return self.client.get(self.path, params=kwargs) + return self._list(self.path, **kwargs) - def all(self, **kwargs): + def _all(self, path, **kwargs): result = None - for page in self.paginate(self.client.get(self.path, params=kwargs)): + for page in self.paginate(self._list(path, **kwargs)): if not result: result = page else: @@ -48,6 +54,9 @@ def all(self, **kwargs): result.pagination["previous"] = None return result + def all(self, **kwargs): + return self._all(self.path, **kwargs) + class RetrievableAPIResource: def get(self, id: str | int) -> Result: @@ -86,3 +95,21 @@ class ROAPIResource( UpdateableAPIResource, ): pass + + +class AvailableAPIResource( + baseapi, + CreateableAPIResource, + ListableAPIResource, +): + def create(self, id: str | int, *args, **kwargs) -> Result: + path = self.path.format(id=id) + return self._create(path, *args, **kwargs) + + def list(self, id: str | int, **kwargs) -> Result: + path = self.path.format(id=id) + return self._list(path, **kwargs) + + def all(self, id: str | int, **kwargs): + path = self.path.format(id=id) + return self._all(path, **kwargs) From 8509915cb0cf60a9c4aa24fbc4ae40a50e7a2751 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 8 Jun 2023 12:12:52 -0700 Subject: [PATCH 2/2] v0.1.6 --- docs/index.md | 106 ++++++++++++++++++++++++++++++++++++ netbox_python/api/dcim.py | 6 +- netbox_python/api/extras.py | 4 ++ setup.cfg | 2 +- setup.py | 2 +- 5 files changed, 117 insertions(+), 3 deletions(-) diff --git a/docs/index.md b/docs/index.md index 7f3a4b2..4558f66 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,3 +6,109 @@ [![License](https://img.shields.io/github/license/netbox-community/netbox-python)](https://img.shields.io/github/license/netbox-community/netbox-python) Python NetBox API Client + +## Installation + +To install run `pip install netbox-python`. + +Alternatively, you can clone the repo and run `python setup.py install`. + +## Usage + +To begin, import the NetBox client and instantiate it: + +``` +from netbox_python import NetBoxClient, Result +nb = NetBoxClient( + base_url="http://127.0.0.1:8000/", token="1dc6fa5bfcef8390dd83a261c36ed8f1551b2d6b" +) +``` +The first argument NetBoxClient takes is the NetBox URL. The 'token' argument is from NetBox, see the [Authentication documentation](https://docs.netbox.dev/en/stable/integrations/rest-api/#authentication) in the NetBox docs for more about creating and using API Tokens. + +Now using the client you can make calls to the api. + +### Basic CRUD APIs + +Each of these objects has the standard CRUD endpoints as follows: + +``` +# 1. List (paginated) +ret = nb.dcim.sites.list(limit=3) + +# 2. Filtered List +ret = nb.dcim.sites.list(region_id="43") + +# 3. All +ret = nb.dcim.sites.all() + +# 4. Get +ret = nb.dcim.sites.get(24) + +# 5. Create +ret = nb.dcim.sites.create(name="foo3", slug="foo3") + +# 6. Update +ret = nb.dcim.sites.update(26, name="foo2-new", slug="foo2-new-slug") + +# 7. Delete +ret = nb.dcim.sites.delete(37) +``` + +### Bulk APIs + +In addition, bulk operations are available on the API's as well: +``` +# 8. Bulk Create +data = [ + {"name": "foo4", "slug": "foo4"}, + {"name": "foo5", "slug": "foo5"}, + {"name": "foo6", "slug": "foo6"}, +] +ret = nb.dcim.sites.create(data) + +# 8. Bulk Update +data = [ + {"id": 28, "name": "foo4-new", "slug": "foo4-new"}, + {"id": 29, "name": "foo5-new", "slug": "foo5-new"}, +] +ret = nb.dcim.sites.update(data) + +# 10. Bulk Delete +data = [{"id": 25}, {"id": 27}] +ret = nb.dcim.sites.delete(data) +``` +### Special APIs + +In addition to the standard API calls above, devices also have a special API for rendering config context: +``` +ret = nb.dcim.devices.render_config(107) +``` + +### Endpoints + +The methods on the api's correspond to the NetBox REST API - the best reference to the objects that can be called is by using the [browsable API](https://demo.netbox.dev/api/) on the netbox instance. The root objects that can be called are: + +- circuits +- core +- dcim +- extras +- ipam +- plugins +- status +- tenancy +- users +- virtualization +- wireless + +circuits would have 'circuit_terminations', 'circuit_types', etc... off of it. Each of the endpoints has 'list', 'get', 'create', 'update' and 'delete' functions. + + +## Return Object + +The return object from the API calls is a dictionary with two values (response and data). **data** is the actual data returned from the call and response contains detailed information on the call, including the HTTP status code returned. Netbox-python is a wrapper around the python [requests](https://github.com/psf/requests) library. Detailed information on the response object can be found in python requests library [documentation](https://requests.readthedocs.io/en/latest/). After making an API call you can check the status code and get the returned data as follows: + +``` +ret = nb.dcim.sites.all() +print(f"status code: {ret.response.status_code}") +print(ret.data) +``` diff --git a/netbox_python/api/dcim.py b/netbox_python/api/dcim.py index 220fb25..91720d6 100644 --- a/netbox_python/api/dcim.py +++ b/netbox_python/api/dcim.py @@ -1,4 +1,5 @@ -from netbox_python.baseapi import APIResource +from netbox_python.baseapi import APIResource, CreateableAPIResource +from netbox_python.rest import Result class dcim: @@ -83,6 +84,9 @@ class _device_types(APIResource): class _devices(APIResource): path = "dcim/devices/" + def render_config(self, id: str | int, *args, **kwargs) -> Result: + return self._create(f"{self.path}{id}/render-config/", *args, **kwargs) + class _front_port_templates(APIResource): path = "dcim/front-port-templates/" diff --git a/netbox_python/api/extras.py b/netbox_python/api/extras.py index 3628f5e..2580ed5 100644 --- a/netbox_python/api/extras.py +++ b/netbox_python/api/extras.py @@ -1,4 +1,5 @@ from netbox_python.baseapi import APIResource +from netbox_python.rest import Result class extras: @@ -26,6 +27,9 @@ class _config_contexts(APIResource): class _config_templates(APIResource): path = "extras/config-templates/" + def render(self, id: str | int, *args, **kwargs) -> Result: + return self._create(f"{self.path}{id}/render/", *args, **kwargs) + class _content_types(APIResource): path = "extras/content-types/" diff --git a/setup.cfg b/setup.cfg index 3e77248..6088f14 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.1.5 +current_version = 0.1.6 commit = True tag = True diff --git a/setup.py b/setup.py index 96ef357..46e817d 100644 --- a/setup.py +++ b/setup.py @@ -42,6 +42,6 @@ test_suite="tests", tests_require=test_requirements, url="https://github.com/netbox-community/netbox_python", - version="0.1.5", + version="0.1.6", zip_safe=False, )