API - netdisco/netdisco GitHub Wiki

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).

Overview of the API

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.

Endpoint Documentation and Testing

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.

Client Authentication

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.

Access when using Delegated or Disabled Authentication

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.

Tenancy Support

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:

Client Code Examples

Login

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).

Perl

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

Python

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

curl -X POST -H "Accept: application/json" http://localhost:5000/login -u username
Enter host password for user 'username':
{"api_key":"9d8ff4b68dbedb804c90e9b375eea48a"}

Find a Node on a Device Port

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

Perl

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

Python

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

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

Get all Nodes in a VLAN

Perl

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;

Python

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))

Search for a Device

Perl

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

Python

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

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

Get Device details

Perl

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;

Python

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))

Delete a Device

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"
  }
]

Get all Nodes on a Device

Perl

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;

Python

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))

Submit Nodes (MAC address table) on a Device

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).

Perl

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;

Python

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)

cURL

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

Submit MAC-IP associations (ARP table)

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).

Perl

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;

Python

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)

cURL

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

Run a Report

Perl

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;

Python

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))

Job Queue Management

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.

List the jobs in the queue

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
  }
]

Submit one or more jobs to the queue

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.

Update a Custom Field

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"
  }
]

Empty the queue

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.

Remove jobs for a specific device

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.

Get the names of the running backend daemons

Usually these are fully qualified domain names of the hosts running netdisco-backend. Can be used in a filter on /queue/jobs.

⚠️ **GitHub.com Fallback** ⚠️