jaque import -from nautobot reads the devices of a Nautobot and writes them as a CUE file of hosts. It runs once, when you run it, and what it writes is an ordinary config file: you commit it next to your own, and jaque loads it like any other. Nautobot knows what exists; the import puts that list into jaque, and the services that say what to watch on each device stay in your own files.

1. Running it

From a response you saved earlier:

jaque import -from nautobot -o inventory.cue response.json

Or by asking a Nautobot directly, with the token in the environment:

NAUTOBOT_TOKEN=... jaque import -from nautobot -url https://nautobot.example -o inventory.cue

Flags go before the path. Flag parsing stops at the first argument that is not a flag, so a -o after response.json is counted as a second path and the command fails. -url and a path exclude each other.

Without -o, the CUE goes to stdout. The report goes to stderr either way, so jaque import ... > inventory.cue captures only the CUE. The token is read from NAUTOBOT_TOKEN and from nowhere else: there is no flag for it, so it does not end up in a shell history or a process list. With -url and no token set, the command stops before it sends anything.

2. What is read

The import sends one GraphQL query to /api/graphql/ and maps the response it gets back. Nothing is paged: the whole device list comes in a single response.

query {
  devices {
    id
    name
    status { name }
    role { name }
    location { name parent { name parent { name parent { name parent { name } } } } }
    platform { name }
    tenant { name }
    device_type { model }
    tags { name }
    primary_ip4 { address }
    _custom_field_data
  }
}

A response that carries GraphQL errors, or that is not valid JSON, fails the import and writes nothing.

3. What becomes what

Each imported device becomes one host, named after the device, with an ICMP check on its address.

Nautobot jaque
name the host name
primary_ip4.address address, without the prefix length, and the host of the ICMP check
role label role
location label site
the top of the location chain label region, when the location has a parent
platform label platform
tenant label tenant
device_type.model label device_type
status label status
each tag label tag.<name> with the value true
_custom_field_data.jaque_parent parents, see section 5

The location chain is read up to four levels above the device's own location. A device in AMS 01, inside the region Europe, gets site: ams-01 and region: europe.

Nautobot names are free text and a label value is not, so each name is turned into a value: lowercase, with every run of characters outside a-z, 0-9, _ and . replaced by a single -, and leading and trailing separators trimmed. Core Switch becomes core-switch and AMS 01 becomes ams-01. Two different names in the same category can turn into the same value; the first is kept as it is, and each later one is listed in the report as approximated, naming both. A name that leaves nothing after the conversion is dropped and listed the same way.

4. What is left out

Only devices that are Active and have a primary IPv4 address are imported. Each device left out is listed in the report as unsupported, with the reason and the Nautobot id of the device:

  • the status is anything but Active, or the device has no status;
  • the device has no primary IPv4 address;
  • the address does not parse as an address;
  • the name is empty;
  • the name is shared by several Active devices with a primary IPv4: all of them are left out, since a host name has to be unique.

The report header says how many of the devices in the response made it into the file, the same figure the Nagios importer reports. Read the unsupported lines before deciding the inventory is complete.

5. Parents

A device whose jaque_parent custom field holds the name of another imported device gets that device as its parents. The field is a text field on the device; Nautobot cables are not read to work out a topology.

When jaque_parent names a device that is not in the import, because it is not Active or has no address, the host is emitted without a parent and the report lists it as approximated. The same happens when the field names the device itself or holds something other than text. An empty field means no parent.

6. Attaching services

The import writes hosts and labels and no services, because Nautobot does not know what should be watched on a device. The generated file is not edited. A file of your own unifies with it, and attaches services through the labels:

hosts: {
	for name, h in hosts if (*h.labels.role | "") == "core-switch" {
		(name): services: ssh: check: {type: "tcp", address: "\(h.address):22"}
	}
	"core-sw01": check: check_interval: "30s"
}

The comprehension adds an ssh service to every host whose role label is core-switch. The *... | "" default is what lets a host without that label pass: a bare h.labels.role is an error on the first host that has no role. The last line overrides one host's check interval from your layer, which is where an override belongs; a change made in the generated file is lost at the next import.

7. Keeping it current

Run the import from cron or from a CI job, write the file with -o, and commit it. When the file changes, the reload picks it up; see hot reload. A device that left Nautobot, or stopped being Active, is absent from the next file.

Rolling back is reverting that commit: jaque keeps no history of imported inventory of its own, and the diff in the repository is the record of what changed between two imports.