Usage
Api#
At first, get Api
object and run openapi
coroutine
from anac import api
a = api(
"https://demo.netbox.dev",
token="cf1dc7b04de5f27cfc93aba9e3f537d2ad6fdf8c",
)
# get openapi spec and create attributes/endpoints
# with python interpreter autocompletion
await a.openapi()
Tip
Use IPython or Python 3.8+ with python -m asyncio
to try this code interactively, as they support executing async
/await
expressions in the console.
Endpoint*#
The fundamental anac principle is a minimalistic user interface.
anac provides 5 Endpoint*
classes, whose objects are a coroutine or coroutine iterators, for interacting with the NetBox API endpoints.
Here is the diagram with these objects/coroutines:
EndpointIdAsIterator
/
EndpointIdIterator/EndpointId
/ \
Api-Endpoint EndpointId
\
EndpointAsIterator
Endpoint
object is coroutine, that can return EndpointIdIterator
/EndpointId
or EndpointAsIterator
objects, where *Iterator
are coroutine iterators, and not *Iterator
is a coroutine.
Endpoint#
# use 'tab' autocompletion
In [1]: a.dcim_devices
Out[1]: Endpoint(api=Api, url='https://demo.netbox.dev/api',
endpoint='/dcim/devices/')
In [2]: type(a.dcim_devices)
Out[2]: anac.core.endpoint.Endpoint
In [3]: a.circuits_circuits
Out[3]: Endpoint(api=Api, url='https://demo.netbox.dev/api',
endpoint='/circuits/circuits/')
In [4]: type(a.circuits_circuits)
Out[4]: anac.core.endpoint.Endpoint
In [5]: a.dcim_cables_id
Out[5]: Endpoint(api=Api, url='https://demo.netbox.dev/api',
endpoint='/dcim/cables/{id}/')
In [6]: type(a.dcim_cables_id)
Out[6]: anac.core.endpoint.Endpoint
Endpoint
describes NetBox API endpoint object ('/dcim/devices'
, '/ipam/roles'
, ...) and, at the same time, it's a coroutine for interacting with NetBox API endpoint.
You can run each Endpoint
coroutine with:
-
dict argument to send http request and get result as
EndpointId
orEndpointIdIterator
objectIn [7]: some_device = await a.dcim_devices(get={"name": "dmi01-scranton-rtr01"}) In [8]: some_device Out[8]: EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/9/', endpoint='/dcim/devices/')
To continue working with
EndpointId
seeEndpointId
# by default, you get 50 results, # but you can specify the required number with 'get={"limit": 100}' In [9]: all_devices = await a.dcim_devices(get={}) # or In [10]: all_devices = await a.dcim_devices() In [11]: all_devices Out[11]: EndpointIdIterator(api=Api, url='https://demo.netbox.dev/api', endpoint='/dcim/devices/')
To continue working with
EndpointIdIterator
seeEndpointIdIterator
-
list of dicts arguments to get coroutine iterator as
EndpointAsIterator
object for pending http requests and running them in event loop with asyncioIn [12]: devices = await a.dcim_devices( ...: get=[ ...: {"name": "dmi01-scranton-rtr01"}, ...: {"name": "dmi01-scranton-sw01"}, ...: ], ...: post=[ ...: { ...: "name": "dmi01-scranton-rtr02", ...: "device_role": 1, ...: "site": 10, ...: "device_type": 6, ...: "status": "planned", ...: }, ...: { ...: "name": "dmi01-scranton-sw02", ...: "device_role": 1, ...: "site": 10, ...: "device_type": 7, ...: "status": "planned", ...: }, ...: ], ...: ) In [13]: devices Out[13]: EndpointAsIterator(api=Api, url='https://demo.netbox.dev/api', endpoint='/dcim/devices/')
To continue working with
EndpointAsIterator
seeEndpointAsIterator
EndpointId#
EndpointId
describes NetBox API endpoint id object ('/dcim/devices/{id}'
, '/ipam/roles/{id}'
, ...) and, at the same time, it's a coroutine for interacting with NetBox API endpoint id.
EndpointId
object has all attributes of NetBox object.
EndpointId
object has .response
attribute, containing httpx.Response
object.
Where does some_device
from? See Endpoint
# we sent 1 http request earlier:
# some_device = await a.dcim_devices(get={"name": "dmi01-scranton-rtr01"})
In [14]: some_device
Out[14]: EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/9/',
endpoint='/dcim/devices/')
# use 'tab' autocompletion
In [15]: some_device.id
Out[15]: 9
In [16]: some_device.name
Out[16]: 'dmi01-scranton-rtr01'
In [17]: some_device.device_type.model
Out[17]: 'ISR 1111-8P'
In [18]: some_device.response.json()
Out[18]:
{'count': 1,
'next': None,
'previous': None,
'results': [{'id': 9,
'url': 'https://demo.netbox.dev/api/dcim/devices/9/',
'display': 'dmi01-scranton-rtr01',
'name': 'dmi01-scranton-rtr01',
'device_type': {'id': 6,
'url': 'https://demo.netbox.dev/api/dcim/device-types/6/',
'display': 'ISR 1111-8P',
'manufacturer': {'id': 3,
'url': 'https://demo.netbox.dev/api/dcim/manufacturers/3/',
'display': 'Cisco',
'name': 'Cisco',
'slug': 'cisco'},
'model': 'ISR 1111-8P',
'slug': 'isr1111'},
'device_role': {'id': 1,
...
You can run each EndpointId
object:
- to send http request and get results/new
EndpointId
objectIn [19]: some_device = await some_device(patch={"status": "planned"}) # some_device is a new 'EndpointId' object In [20]: some_device Out[20]: EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/9/', endpoint='/dcim/devices/{id}/') In [21]: some_device.status Out[21]: {'value': 'planned', 'label': 'Planned'}
-
to send http requests and get
EndpointIdAsIterator
with results/newEndpointId
objects. All http requests in this coroutine will be run serially usingawait
.In [22]: some_devices = await some_device(get={}, patch={"status": "active"}) In [23]: some_devices Out[23]: EndpointIdAsIterator()
To continue working with
EndpointIdAsIterator
seeEndpointIdAsIterator
EndpointIdIterator#
EndpointIdIterator
is an iterator with EndpointId
objects.
EndpointIdIterator
contains the multiple EndpointId
objects, if Endpoint
http
request returns more than one result (httpx.Response.json()['results']
) and, at the
same time, EndpointIdIterator
object is a service coroutine for using with service EndpointBase.request
coroutine
Where does all_devices
from? See Endpoint
# we sent 1 http request earlier:
# all_devices = await a.dcim_devices(get={})
In [31]: all_devices
Out[31]: EndpointIdIterator(api=Api, url='https://demo.netbox.dev/api',
endpoint='/dcim/devices/')
In [32]: list(all_devices)
Out[32]:
[EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/107/',
endpoint='/dcim/devices/'),
EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/88/',
endpoint='/dcim/devices/'),
EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/89/',
endpoint='/dcim/devices/'),
...]
In [33]: all_devices[0].name
Out[33]: 'Dingdong'
In [34]: all_devices[49].name
Out[34]: 'ncsu118-distswitch1'
In [35]: len(all_devices)
Out[35]: 50
EndpointAsIterator#
EndpointAsIterator
is an iterator with EndpointBase.request
(it's service http
request coroutine) coroutines for pending http requests and running in event loop with asyncio.gather
or asyncio.as_completed
.
Where does devices
from? See Endpoint
# we planned to send 4 http requests earlier:
# devices = await a.dcim_devices(
# get=[
# {"name": "dmi01-scranton-rtr01"},
# {"name": "dmi01-scranton-sw01"},
# ],
# post=[
# {
# "name": "dmi01-scranton-rtr02",
# "device_role": 1,
# "site": 10,
# "device_type": 6,
# "status": "planned",
# },
# {
# "name": "dmi01-scranton-sw02",
# "device_role": 1,
# "site": 10,
# "device_type": 7,
# "status": "planned",
# },
# ],
# )
In [36]: devices
Out[36]: EndpointAsIterator(api=Api, url='https://demo.netbox.dev/api',
endpoint='/dcim/devices/')
In [37]: import asyncio
In [38]: devices = await asyncio.gather(*some_devices)
In [39]: devices
Out[39]:
[EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/9/',
endpoint='/dcim/devices/'),
EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/22/',
endpoint='/dcim/devices/'),
EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/115/',
endpoint='/dcim/devices/'),
EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/114/',
endpoint='/dcim/devices/')]
EndpointIdAsIterator#
EndpointIdAsIterator
is an iterator with EndpointId
objects.
EndpointIdAsIterator
contains the results of modifying a single NetBox object (with endpoint id), using the EndpointId
coroutine, running with multiple arguments.
These results are iterated according to the order of the EndpointId
arguments (see example below).
Where does some_devices
from? See EndpointId
# we sent 2 http requests earlier:
# some_devices = await some_devices(get={}, patch={"status": "active"})
In [24]: some_devices
Out[24]: EndpointIdAsIterator()
In [25]: list(some_devices)
Out[25]:
[EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/9/',
endpoint='/dcim/devices/{id}/'),
EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/9/',
endpoint='/dcim/devices/{id}/')]
# 'get={}' results
In [26]: some_devices[0].name
Out[26]: 'dmi01-scranton-rtr01'
In [27]: some_devices[0].status
Out[27]: {'value': 'planned', 'label': 'Planned'}
# 'patch={"status": "active"}' results
In [28]: some_devices[1].name
Out[28]: 'dmi01-scranton-rtr01'
In [29]: some_devices[1].status
Out[29]: {'value': 'active', 'label': 'Active'}
In [30]: len(some_devices)
Out[30]: 2
Examples#
At first, get Api
object and run openapi
coroutine(btw, it works with async context
manager)
from anac import api
a = api(
"https://demo.netbox.dev",
token="cf1dc7b04de5f27cfc93aba9e3f537d2ad6fdf8c",
)
# get openapi spec and create attributes/endpoints
await a.openapi()
get
some device and patch
it#
In [1]: some_device = await a.dcim_devices(get={"name": "dmi01-rochster-sw01"})
In [2]: some_device.name
Out[2]: 'dmi01-rochster-sw01'
In [3]: some_device.status
Out[3]: {'value': 'active', 'label': 'Active'}
In [5]: some_device = await some_device(patch={"status": "failed"})
In [6]: some_device.status
Out[6]: {'value': 'failed', 'label': 'Failed'}
get
some 2 devices and put
+ patch
them#
In [7]: some_devices = await a.dcim_devices(
...: get=[
...: {"name": "dmi01-rochster-sw01"},
...: {"name": "dmi01-rochester-rtr01"},
...: ]
...: )
# EndpointAsIterator is a coroutine iterator with 2 coroutines
In [8]: some_devices
Out[8]: EndpointAsIterator(api=Api, url='https://demo.netbox.dev/api',
endpoint='/dcim/devices/')
In [9]: import asyncio
# run 2 coroutines in the event loop
In [10]: some_devices = await asyncio.gather(*some_devices)
# EndpointId is a NetBox '/dcim/devices/' object and coroutine
In [11]: some_devices
Out[11]:
[EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/21/',
endpoint='/dcim/devices/'),
EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/8/',
endpoint='/dcim/devices/')]
In [12]: patch_some_devices = [coro(patch={"status": "failed"}) for coro in some_devices]
# run 2 coroutines in the event loop
In [13]: patch_some_devices = await asyncio.gather(*patch_some_devices)
# EndpointId is a coroutine, again
In [14]: patch_some_devices
Out[14]:
[EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/21/',
endpoint='/dcim/devices/{id}/'),
EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/8/',
endpoint='/dcim/devices/{id}/')]
In [15]: patch_some_devices[0].name
Out[15]: 'dmi01-rochster-sw01'
In [16]: patch_some_devices[0].status
Out[16]: {'value': 'failed', 'label': 'Failed'}
In [17]: patch_some_devices[1].name
Out[17]: 'dmi01-rochester-rtr01'
In [18]: patch_some_devices[1].status
Out[18]: {'value': 'failed', 'label': 'Failed'}
get
all devices#
In [19]: all_devices = await a.dcim_devices(get={})
# or
In [20]: all_devices = await a.dcim_devices()
# EndpointIdIterator is an coroutine iterator with EndpointId objects
In [21]: all_devices
Out[21]: EndpointIdIterator(api=Api, url='https://demo.netbox.dev/api',
endpoint='/dcim/devices/')
In [22]: len(all_devices)
Out[22]: 50
In [23]: all_devices[49]
Out[23]: EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/95/',
endpoint='/dcim/devices/')
In [24]: all_devices[49].name
Out[24]: 'ncsu118-distswitch1'
# by default, 'limit' parameter = 50,
# but you can run 'get' request with custom 'limit'
In [25]: all_devices = await a.dcim_devices(get={"limit": 100})
In [26]: len(all_devices)
Out[26]: 75
In [27]: all_devices[74]
Out[27]: EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/106/',
endpoint='/dcim/devices/')
In [28]: all_devices[74].id
Out[28]: 106
get
all devices and post
2 new devices#
In [29]: all_test = await a.dcim_devices(
...: get={},
...: post=[
...: {
...: "name": "test1",
...: "device_role": 1,
...: "site": 1,
...: "device_type": 1,
...: "status": "planned",
...: },
...: {
...: "name": "test2",
...: "device_role": 1,
...: "site": 1,
...: "device_type": 1,
...: "status": "planned",
...: },
...: ],
...: )
# run 3 coroutines in the event loop
In [30]: all_test = await asyncio.gather(*all_test)
# EndpointIdIterator is an coroutine iterator with EndpointId objects
# EndpointId is a NetBox '/dcim/devices/' object and coroutine
In [31]: all_test
Out[31]:
[EndpointIdIterator(api=Api, url='https://demo.netbox.dev/api',
endpoint='/dcim/devices/'),
EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/110/',
endpoint='/dcim/devices/'),
EndpointId(api=Api, url='https://demo.netbox.dev/api/dcim/devices/111/',
endpoint='/dcim/devices/')]
In [32]: all_test[0][49].name
Out[32]: 'ncsu118-distswitch1'
In [33]: all_test[1].name
Out[34]: 'test1'
In [35]: all_test[2].name
Out[35]: 'test2'
# httpx.Response is available with .response attribute
In [36]: all_test[1].response.json()
Out[36]:
{'id': 110,
'url': 'https://demo.netbox.dev/api/dcim/devices/110/',
'display': 'test1',
'name': 'test1',
'device_type': {'id': 1,
'url': 'https://demo.netbox.dev/api/dcim/device-types/1/',
'display': 'MX480',
'manufacturer': {'id': 7,
...
}
post
or put
using a name
instead of an id
#
It is not always convenient to send a post
or put
http request, using only object id
s.
You can use name
s or other keys. For example:
In [37]: test3_device = await a.dcim_devices(
...: post={
...: "name": "test3",
...: "device_role": {"name": "Access Switch"},
...: "site": {"name": "DM-Rochester"},
...: "device_type": {"model": "C9200-48P"},
...: "status": "planned",
...: }
...: )
In [38]: test3_device.name
Out[38]: 'test3'
In [39]: test3_device.device_role.name
Out[39]: 'Access Switch'
In [40]: test3_device.site.name
Out[40]: 'DM-Rochester'
In [41]: test3_device.device_type.model
Out[41]: 'C9200-48P'
Or you can write a coroutine, that will search the id
by key:
In [42]: from typing import Dict, Any
...:
...:
...: async def finder(endpoint: "Endpoint", kwargs: Dict[str, Any]):
...: endpoint_id = await endpoint(get=kwargs)
...: return endpoint_id.id
In [43]: d = {
...: "device_role": (a.dcim_device_roles, {"name": "Access Switch"}),
...: "site": (a.dcim_sites, {"name": "DM-Rochester"}),
...: "device_type": (a.dcim_device_types, {"model": "C9200-48P"}),
...: }
In [44]: coros = [finder(*v) for v in d.values()]
In [45]: ids = await asyncio.gather(*coros)
In [46]: ids
Out[46]: [4, 9, 7]
In [47]: d = {"name": "test3", "status": "planned", **dict(zip(d, ids))}
In [48]: d
Out[48]:
{'name': 'test3',
'status': 'planned',
'device_role': 4,
'site': 9,
'device_type': 7}
In [49]: test3_device = await a.dcim_devices(post=d)
In [50]: test3_device.device_role.name
Out[50]: 'Access Switch'
In [51]: test3_device.site.name
Out[51]: 'DM-Rochester'
In [52]: test3_device.device_type.model
Out[52]: 'C9200-48P'