> Section: [4. Configuration](https://jaque.sh/docs/config/cue-in-practice.md)
> Next: config/timeperiods
> Index: https://jaque.sh/llms.txt


`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:

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

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

```sh
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.

```graphql
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](https://jaque.sh/docs/nagios-compat/importer.md)
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:

```cue
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](hot-reload.md). 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.
