API - netdisco/netdisco GitHub Wiki
- Overview of the API
- Endpoint Documentation and Testing
- Client Authentication
- Access when using Delegated or Disabled Authentication
- Tenancy Support
- Client Code Examples
Since version 2.45 Netdisco has included an API which provides read-only access to data, searching, and report results. Since version 2.59 there are methods to write ARP table and MAC address table data into Netdisco. Since version 2.61.1 you can manage the job queue content via the API.
You can call the API using standard toolchain techniques such as curl
,
Perl’s Mojo::UserAgent
, or Python’s requests
module, and all data is
submitted and retured in JSON structures.
All endpoints are documented and can be tested in your browser under the
/swagger-ui
path of your installation (for example
http://localhost:5000/swagger-ui). There is a link to this in the user’s
menu in the Netdisco titlebar (which works for tenanices as well).
You can explore the API on a live demo at https://netdisco2-demo.herokuapp.com/swagger-ui/?url=/swagger.json. There is no need to log in on that page.
If you can programatically generate client libraries for an OpenAPI 2.0
Specification, then download /swagger.json
from your Netdisco installation
(for example http://localhost:5000/swagger.json).
There are five categories of endpoint: General, Search, Objects, Reports, and Queue. Broadly the API follows the pages and tabs of the web user interface (as it was retrofitted and this was the easiest way).
Within the General category are the login and logout endpoints.
Within the Search category you can search for devices, ports, vlans, and nodes, just the same as from the top titlebar in the web interface.
Within the Objects category you can retrieve rows from Netdisco’s database tables, and their relations, such as devices, ports, and nodes on the ports. Here you can also submit ARP table and MAC address table data to Netdisco.
Within the Reports category is every report available in Netdisco, both the canned ones we supply and any custom reports you have added to your own installation (all custom reports automagically become API endpoints).
Withi the Queue category are endpoints to list, submit, and delete some or all of the queued jobs, as well as list the active backend daemon names. There are many actions not available directly as API endpoints which can be done using a queued job (a good example is to delete a device).
Data structures returned from the endpoints vary, as they are simply wrapping the internals of Netdisco’s web user interface and we’ve not implemented a separate, consistent, API. You can easily test endpoints in your web browser (see below) to determine the returned data structures.
Notably missing from the API are reports which are limited to admin users; patches welcome. Bug reports are welcome, too, as there might be data configurations we couldn’t test in the API development.
All endpoints are documented and can be tested in your browser under the
/swagger-ui
path of your installation (for example
http://localhost:5000/swagger-ui). There is a link to this in the user’s
menu in the Netdisco titlebar (which works for tenanices as well).
You need to log in at /swagger-ui
to test the endpoints. First click the
Authorize button at the top right and log in as any user, using the Basic
authorization form.
Then call the /login
endpont: click the General category to expand it,
click login, then Try it out, then Execute. You’ll get back a JSON
payload with an API key.
Click once again on the Authorize button at the top right, and this time enter the API key into the APIKeyHeader form and click Authorize.
The /swagger-ui
interface is now ready for use and you can try any endpoint.
The documentation shows parameters and their data types for all endpoints, and
will show the API request made as well as example code for the cURL utility.
Using the API programatically requires a single login step, which returns an API key for use with the data endpoints.
The API works fully with tenancies. However, all Authentication happens against your Default tenant and all and API keys also come from your Default tenant, as configured in database settings.
From your web client code, POST to the /login
endpoint with Accept
application/json
, and pass any user’s
username and password in an HTTP Basic Authentication header. You will get
back a JSON payload with an API key (examples are below).
Now when using any API endpoint, pass the key in the Authorization
HTTP
Header, in plain text (optionally prefixed by “Apikey
”).
You can also use the “netdisco-do getapikey -e <username>
” command at
the CLI to get an API key for any user.
All API keys are valid for the value of the api_token_lifetime
setting,
which defaults to one hour (3600 seconds).
If you log in or call getapikey
and an existing key is in place and still
valid, it is returned and the lifetime counter is reset for another
api_token_lifetime
amount.
Netdisco supports the setting no_auth
to disable authentication, and also
trust_remote_user
and trust_x_remote_user
to delegate the authentication
to another service (for example Apache proxy).
The API supports these settings, and they will always override any log-in operation the user attemps. That is, the user is not prevented from logging in to the API, however it will be the permissions of the delegated or guest user that take effect.
When testing the API with the Swagger interface when trust_x_remote_user
is
emabled, you will see an additional optional field for the X-REMOTE_USER
HTTP Header. This overrides any log in or API token provided.
The API works fully with tenancies. However, all Authentication happens against your Default tenant and all and API keys also come from your Default tenant, as configured in database settings.
You can browse the Swagger interface for a tenant by following the link in the
user menu (when viewing any tenant). API endpoints for tenants are prefixed with
/t/tenantname
as in the example below:
This is an HTTP POST action, and you must specify the Accept header
(application/json
) so that Netdisco knows you want an API key (rather than a
cookie).
use feature 'say';
use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new(max_redirects => 2);
say $ua->post('http://username:password@localhost:5000/login' =>
{Accept => 'application/json'})
->result->json
->{api_key};
# will emit something like: 9d8ff4b68dbedb804c90e9b375eea48a
import requests
r = requests.post('http://localhost:5000/login',
auth=('username', 'password'),
headers={'Accept': 'application/json'})
print( r.json()['api_key'] )
# will emit something like: 9d8ff4b68dbedb804c90e9b375eea48a
curl -X POST -H "Accept: application/json" http://localhost:5000/login -u username
Enter host password for user 'username':
{"api_key":"9d8ff4b68dbedb804c90e9b375eea48a"}
If you have the MAC address or IP address of a Node, you can find the Device Port where it’s currently connected (or was last seen).
If you have the IP, it requires two searches: the first search is needed to resolve the IP to a MAC, and the second finds the Switch Port for the MAC. For example:
① http://localhost:5000/api/v1/search/node?q=172.16.1.101
② http://localhost:5000/api/v1/search/node?q=00:03:00:11:11:01
use feature 'say';
use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new(max_redirects => 2);
$ua->on(start => sub {
my ($ua, $tx) = @_;
$tx->req->headers->header(Accept => 'application/json');
$tx->req->headers->header(Authorization => '9d8ff4b68dbedb804c90e9b375eea48a');
});
my $mac = $ua->get('http://localhost:5000/api/v1/search/node' => form =>
{q => '172.16.1.101'})
->result->json
->{macs} # the IP might be associated with multiple MAC addresses
->[0] # search returns a list (possibly empty) of matches
->{mac}; # the mac address e.g. 00:03:00:11:11:01
my $port = $ua->get('http://localhost:5000/api/v1/search/node' => form =>
{q => $mac})
->result->json
->{sightings} # the MAC might be associated with multiple ports
->[0]; # search returns a list (possibly empty) of matches
say $port->{switch}; # the device IP
say $port->{port}; # the device port
import requests
r = requests.get('http://localhost:5000/api/v1/search/node',
headers={'Accept': 'application/json',
'Authorization': '9d8ff4b68dbedb804c90e9b375eea48a'},
params={'q': '172.16.1.101'})
# the mac address e.g. 00:03:00:11:11:01
mac = r.json()['macs'][0]['mac']
r = requests.get('http://localhost:5000/api/v1/search/node',
headers={'Accept': 'application/json',
'Authorization': '9d8ff4b68dbedb804c90e9b375eea48a'},
params={'q': mac})
device = r.json()['sightings'][0]['switch']
port = r.json()['sightings'][0]['port']
curl -X POST \
-H "Accept: application/json" \
-H "Authorization: 9d8ff4b68dbedb804c90e9b375eea48a" \
"http://localhost:5000/api/v1/search/device?q=172.16.1.101"
# will emit the full JSON reply for IP search
curl -X POST \
-H "Accept: application/json" \
-H "Authorization: 9d8ff4b68dbedb804c90e9b375eea48a" \
"http://localhost:5000/api/v1/search/device?q=00:03:00:11:11:01"
# will emit the full JSON reply for MAC search
use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new(max_redirects => 2);
$ua->on(start => sub {
my ($ua, $tx) = @_;
$tx->req->headers->header(Accept => 'application/json');
$tx->req->headers->header(Authorization => '9d8ff4b68dbedb804c90e9b375eea48a');
});
my $nodes = $ua->get('http://localhost:5000/api/v1/object/vlan/123/nodes')
->result
->json;
# this is a listref (possibly empty) of the node table entries for the VLAN
# pretty print the hashref content
use Data::Printer;
p $nodes;
import json
import requests
r = requests.get('http://localhost:5000/api/v1/object/vlan/123/nodes',
headers={'Accept': 'application/json',
'Authorization': '9d8ff4b68dbedb804c90e9b375eea48a'})
# pretty print the JSON stucture
print(json.dumps(r.json(), indent=2, sort_keys=True))
use feature 'say';
use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new(max_redirects => 2);
$ua->on(start => sub {
my ($ua, $tx) = @_;
$tx->req->headers->header(Accept => 'application/json');
$tx->req->headers->header(Authorization => '9d8ff4b68dbedb804c90e9b375eea48a');
});
say $ua->get('http://localhost:5000/api/v1/search/device' => form =>
{dns => 'mydevicename.example.com'})
->result->json
->[0] # search returns a list (possibly empty) of matches
->{ip}; # the ip field of the device is the primary key
# will emit something like: 192.0.2.1
import requests
r = requests.get('http://localhost:5000/api/v1/search/device',
headers={'Accept': 'application/json',
'Authorization': '9d8ff4b68dbedb804c90e9b375eea48a'},
params={'dns': 'mydevicename.example.com'})
print(r.json()[0]['ip'])
# will emit something like: 192.0.2.1
curl -X POST \
-H "Accept: application/json" \
-H "Authorization: 9d8ff4b68dbedb804c90e9b375eea48a" \
"http://localhost:5000/api/v1/search/device?dns=mydevice.example.com"
# will emit the full JSON reply
use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new(max_redirects => 2);
$ua->on(start => sub {
my ($ua, $tx) = @_;
$tx->req->headers->header(Accept => 'application/json');
$tx->req->headers->header(Authorization => '9d8ff4b68dbedb804c90e9b375eea48a');
});
my $device = $ua->get('http://localhost:5000/api/v1/object/device/192.0.2.1')
->result
->json;
# this is a hashref of the device table entry in Netdisco
# pretty print the hashref content
use Data::Printer;
p $device;
import json
import requests
r = requests.get('http://localhost:5000/api/v1/object/device/192.0.2.1',
headers={'Accept': 'application/json',
'Authorization': '9d8ff4b68dbedb804c90e9b375eea48a'})
# pretty print the JSON stucture
print(json.dumps(r.json(), indent=2, sort_keys=True))
This is done by submitting a job named delete
using the queue management API
endpoints as documented below. If you wish to keep nodes, pass a true value to
the port
parameter (mnemonic: preserve).
[
{
"action": "delete",
"device": "192.0.2.1"
}
]
use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new(max_redirects => 2);
$ua->on(start => sub {
my ($ua, $tx) = @_;
$tx->req->headers->header(Accept => 'application/json');
$tx->req->headers->header(Authorization => '9d8ff4b68dbedb804c90e9b375eea48a');
});
my $nodes = $ua->get('http://localhost:5000/api/v1/object/device/192.0.2.1/nodes')
->result
->json;
# this is a listref (possibly empty) of the node table entries for the device
# pretty print the hashref content
use Data::Printer;
p $nodes;
import json
import requests
r = requests.get('http://localhost:5000/api/v1/object/device/192.0.2.1/nodes',
headers={'Accept': 'application/json',
'Authorization': '9d8ff4b68dbedb804c90e9b375eea48a'})
# pretty print the JSON stucture
print(json.dumps(r.json(), indent=2, sort_keys=True))
This endpoint is the equivalent of macsuck
. It queues a job for a backend
worker to associate your supplied node data with a device (or pseudo device)
and its ports. API user must be a Netdisco admin
to call this endpoint.
You can submit a "raw" mac-address table pulled from a device. Netdisco will
apply sanity checking to the content, as well as filtering nodes which are on
other devices. The body
of the request must be a JSON formatted list of your
node/port associations (see examples below).
Netdisco will ignore configured limits on the execution of macsuck
:
macsuck_no
,macsuck_only
, macsuck_unsupported
, macsuck_min_age
, and
whether the given Device reports layer 2 capability.
You can also submit the same data to netdisco-do macsuck
by passing a
filename to the -p
option (with the choice to act immediately or --enqueue
a job).
use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new(max_redirects => 2);
$ua->on(start => sub {
my ($ua, $tx) = @_;
$tx->req->headers->header(Accept => 'application/json');
$tx->req->headers->header(Authorization => '9d8ff4b68dbedb804c90e9b375eea48a');
});
my $macaddresstable = [
{ mac => '00:03:00:44:44:01', port => 'GigabitEthernet2/1', vlan => '2' },
{ mac => '00:03:00:33:33:02', port => 'TenGigabitEthernet3/1' }
];
my $resp = $ua->put('http://localhost:5000/api/v1/object/device/192.0.2.1/nodes'
=> json => $macaddresstable);
# will be 200 OK or 400 for some error
say $resp->code;
import json
import requests
macaddresstable = [
{ "mac": "00:03:00:44:44:01", "port": "GigabitEthernet2/1", "vlan": "2" },
{ "mac": "00:03:00:33:33:02", "port": "TenGigabitEthernet3/1" } ]
r = requests.put('http://localhost:5000/api/v1/object/device/192.0.2.1/nodes',
headers={'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': '9d8ff4b68dbedb804c90e9b375eea48a'},
data=json.dumps(macaddresstable))
# will be 200 OK or 400 for some error
print(r.status_code)
cat > /path/to/mac/address/table.json
[
{ "mac": "00:03:00:44:44:01", "port": "GigabitEthernet2/1", "vlan": "2" },
{ "mac": "00:03:00:33:33:02", "port": "TenGigabitEthernet3/1" }
]
^D
curl -X PUT -H "Accept: application/json" \
-H "Content-Type: application/json" \
-d "@/path/to/mac/address/table.json"
http://localhost:5000/api/v1/object/device/192.0.2.1/nodes
This endpoint is the equivalent of arpnip
. It queues a job for a backend
worker to associate your supplied MAC-IP pairs. API user must be a Netdisco
admin
to call this endpoint.
You can submit a "raw" ARP table pulled from a device. Netdisco will apply
sanity checking to the content. The body
of the request must be a JSON
formatted list of your MAC-IP pairs (see examples below).
Netdisco will ignore configured limits on the execution of arpnip
:
arpnip_no
,arpnip_only
, arpnip_min_age
, and whether the given Device
reports layer 3 capability.
Strictly speaking, any device IP can be used in the resource endpoint (path). The device IP is only used for logging. You can create a pseudo device and use that to submit any MAC-IP data into Netdisco.
You can also submit the same data to netdisco-do arpnip
by passing a
filename to the -p
option (with the choice to act immediately or --enqueue
a job).
use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new(max_redirects => 2);
$ua->on(start => sub {
my ($ua, $tx) = @_;
$tx->req->headers->header(Accept => 'application/json');
$tx->req->headers->header(Authorization => '9d8ff4b68dbedb804c90e9b375eea48a');
});
my $arpstable = [
{ mac => 'a0:00:00:00:00:11', ip => '192.168.0.11' },
{ mac => 'a0:00:00:00:00:12', ip => '192.168.0.12' }
];
my $resp = $ua->put('http://localhost:5000/api/v1/object/device/192.0.2.1/arps'
=> json => $arpstable);
# will be 200 OK or 400 for some error
say $resp->code;
import json
import requests
arpstable = [
{ mac => "a0:00:00:00:00:11", ip => "192.168.0.11" },
{ mac => "a0:00:00:00:00:12", ip => "192.168.0.12" } ]
r = requests.put('http://localhost:5000/api/v1/object/device/192.0.2.1/arps',
headers={'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': '9d8ff4b68dbedb804c90e9b375eea48a'},
data=json.dumps(arpstable))
# will be 200 OK or 400 for some error
print(r.status_code)
cat > /path/to/arp/table.json
[
{ mac => "a0:00:00:00:00:11", ip => "192.168.0.11" },
{ mac => "a0:00:00:00:00:12", ip => "192.168.0.12" }
]
^D
curl -X PUT -H "Accept: application/json" \
-H "Content-Type: application/json" \
-d "@/path/to/mac/address/table.json"
http://localhost:5000/api/v1/object/device/192.0.2.1/arps
use Mojo::UserAgent;
my $ua = Mojo::UserAgent->new(max_redirects => 2);
$ua->on(start => sub {
my ($ua, $tx) = @_;
$tx->req->headers->header(Accept => 'application/json');
$tx->req->headers->header(Authorization => '9d8ff4b68dbedb804c90e9b375eea48a');
});
my $results = $ua->get('http://localhost:5000/api/v1/report/device/portutilization')
->result
->json; # decode the JSON into a Perl data structure
# pretty print the results content
use Data::Printer;
p $results;
import json
import requests
r = requests.get('http://localhost:5000/api/v1/report/device/portutilization',
headers={'Accept': 'application/json',
'Authorization': '9d8ff4b68dbedb804c90e9b375eea48a'})
# pretty print the JSON stucture
print(json.dumps(r.json(), indent=2, sort_keys=True))
Various endpoints to manage the Job Queue, including listing the jobs, submitting jobs, deleting jobs. Because it’s possible to filter results on the backend server, you can also get a list of the active backend server names.
In general the API user must be an admin
user of Netdisco, however for a job
to update any custom field, the user may have port_control
rights instead.
The endpoints to GET a list of jobs or DELETE jobs take query string
parameters to filter: device
, port
, action
, status
, username
,
userip
, backend
.
The endpoint to GET the list of jobs also takes a limit
query string
parameter to set the maximum number of jobs returned, that defaults to 50 or
the configuration setting jobs_qdepth
.
Takes query string parameters as described above, to filter results.
Returns a list of dictionaries describing the jobs, with some sugar fields, for example:
[
{
"job": 1061,
"action": "discover",
"device": "192.0.2.1",
"subaction": null,
"port": null,
"status": "done",
"entered": "2023-04-27 16:23:13.545341",
"entered_stamp": "2023-04-27 16:23",
"started": "2023-04-27 16:23:14",
"started_stamp": "2023-04-27 16:23",
"finished": "2023-04-27 16:23:15",
"finished_stamp": "2023-04-27 16:23",
"debug": null,
"userip": "127.0.0.1",
"username": "admin",
"log": "Ended discover for 192.0.2.1",
"device_key": null
}
]
Provide in the HTTP BODY a list of dictionaries that describe the job(s). For example:
[
{
"action": "macsuck",
"device": "192.0.2.1"
},
{
"action": "discoverall"
}
]
Jobs submitted this way will override any deferral count which would normally suppress SNMP after connection failures.
A list of the possible job actions and options is available by running the
netdisco-do
CLI application without parameters and reading the documentation
shown.
Note that the extra
and subaction
parameters are synonyms for each other.
This is the same API call as submitting a job to the queue, however Netdisco makes a special case and will execute the change immediately and without queueing a job.
In the example below, we set the field "endoflife" to the value "2028-06-30".
So as you can see the action
is the custom field name prefixed by "cf_" and
the value goes in the subaction
field. If you want to set a custom field on
a port, then simply add the port
to the JSON dictionary.
Custom fields must exist in the app configuration to be accepted for update. Users with either Port Control or Admin rights can call this API endpoint.
[
{
"action": "cf_endoflife",
"device": "192.0.2.1",
"subaction": "2028-06-30"
}
]
Takes query string parameters as described above, to limit its effect. When no parameters are given to filter, it will empty the queue and any Skip List deferrals hints.
An alternative to the endpoint above (/queue/jobs
) to more easily
delete a specific device’s jobs. Also clears any Skip List deferral hints for
the device (i.e. the number of SNMP connect failures). Also takes the same
query string parameters as described above, to limit its effect.
Usually these are fully qualified domain names of the hosts running
netdisco-backend. Can be used in a filter on /queue/jobs
.