Metadata-Version: 2.4
Name: netbox-kentik
Version: 0.0.2
Summary: Netbox plugin to display Kentik links and state inside the UI.
Author: Kentik Technologies, Inc
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/kentik/netbox-kentik/
Project-URL: Bug Tracker, https://github.com/kentik/netbox-kentik/issues/
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.12.0
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Pygments>=2.16
Dynamic: license-file

# Kentik plugin for NetBox

A [NetBox](https://github.com/netbox-community/netbox) plugin that brings [Kentik](https://www.kentik.com/)
network observability into the NetBox UI — reconciling your NetBox inventory against Kentik's,
surfacing Kentik device and site metadata inline, and deep-linking to the Kentik portal.

[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

## Screenshots

<!--
  Suggested captures, mirroring the layout other NetBox plugins use:
  1. Reconciliation → Devices tab, showing the match graphs and the three status badges
  2. The Kentik JSON modal with syntax highlighting
  3. Device page Kentik tab (device details, plan, labels)
  4. Site page Kentik tab
  5. Kentik → Status page
  6. Kentik match column on the core device list
-->

## Features

### Reconciliation

A background job matches NetBox devices and sites to their Kentik counterparts using a pluggable matching policy, then stores the result in Redis for the UI to read.

- **Three-way reconciliation view** (**Kentik → Reconciliation**) with separate Devices and Sites tabs.
  Every row is classified as:
  - `matched` — present in both NetBox and Kentik
  - `netbox_only` — in NetBox, no Kentik counterpart
  - `kentik_only` — in Kentik, no NetBox counterpart
- **Match ratio graphs** in each tab's toolbar showing Kentik-side and NetBox-side coverage
- **Kentik JSON viewer** — Kentik API JSON for data and site objects are easily viewable within NetBox.

### Inline Kentik context on NetBox objects

- **Kentik tab on device pages** showing the Kentik device record: ID, name, alias, type,
  description, role, manufacturer, site ID/name; the associated Kentik **plan** (ID, name,
  active, BGP, description); and all Kentik **labels** rendered with their configured colours.
- **Kentik tab on site pages** showing: ID, title, type, postal address, GPS coordinates,
  site market, PeeringDB site mappings, and address classification (infrastructure /
  user access / other networks).
- Both tabs appear **only when the object is matched**, so unmatched objects stay uncluttered.
- **Kentik match column** added to NetBox's own Device and Site list views, so you can see
  reconciliation state without leaving your usual workflows.

### Portal deep links

A **Kentik** button group is added to several NetBox object pages, linking straight into the
relevant Kentik portal view:

- **Device pages** — alerts, interfaces, traffic, and telemetry
- **Site pages** — alerts, devices, and traffic
- **ASN pages** — traffic and PeeringDB

Unmatched objects show a disabled button with an explanatory tooltip instead.

### Operations

- **Status dashboard** (**Kentik → Status**) covering API sync state (last run, expiry, TTL,
  device and site counts) and reconciliation state, plus **Sync now** / **Reconcile now** buttons.
- **Config tab** shows how the plugin is configured, helpful for debugging.
- **Dashboard widget** — "Kentik Sync Status" for the NetBox home dashboard.
- **CLI management commands** for operators (see [CLI](#cli-management-commands)).
- **REST endpoints** exposing cache freshness and match counts (see [REST API](#rest-api)).

## Compatibility

See the [compatibility matrix](COMPATIBILITY.md) for the full history.

- Python 3.12+
- Redis (already required by NetBox — used here for the Kentik cache)
- A running NetBox RQ worker for scheduled jobs

## Installation

Kentik inventory is cached rather than stored in database tables, but the plugin includes an unmanaged permissions model. After enabling the plugin, run `python /opt/netbox/netbox/manage.py migrate` in NetBox's virtual environment before restarting the services so its object type and permissions are registered.

1. Install the package into NetBox's virtual environment:

   ```bash
   source /opt/netbox/venv/bin/activate
   pip install netbox-kentik
   ```

2. If running within Docker add it to `local_requirements.txt` so it survives NetBox upgrades:

   ```bash
   echo netbox-kentik >> /opt/netbox/local_requirements.txt
   ```

3. Enable and configure it in `/opt/netbox/netbox/netbox/configuration.py`:

   ```python
   PLUGINS = [
       "netbox_kentik",
   ]

   PLUGINS_CONFIG = {
       "netbox_kentik": {
           "api_auth_email": "you@example.com",
           "api_auth_token": "<kentik-api-token>",
           "environment": "us",
       },
   }
   ```

4. Restart NetBox and its worker:

   ```bash
   sudo systemctl restart netbox netbox-rq
   ```

## Configuration

| Setting                  | Required | Default                                     | Description                               |
| ------------------------ | -------- |---------------------------------------------|-------------------------------------------|
| `api_auth_email`         | yes      |                                             | Kentik API auth email.                    |
| `api_auth_token`         | yes      |                                             | Kentik API token.                         |
| `environment`            | yes      |                                             | Kentik environment: `us`, `eu`, or `custom`. |
| `grpc_base_url`          | only when `environment` is `custom` |                  | Base URL of the Kentik gRPC API.          |
| `portal_base_url`        | only when `environment` is `custom` |                  | Base URL of the Kentik portal.            |
| `api_timeout`            | no       | `30`                                        | Per-request timeout, in seconds.          |
| `cache_seed`             | no       | `0`                                         | Bump to force-invalidate the cache key.   |
| `device_matching_policy` | no       | `"netbox_kentik.policies.device.NamePolicy"` | Device matching policy: a dotted path, or an ordered list of them. |
| `site_matching_policy`   | no       | `"netbox_kentik.policies.site.NamePolicy"`   | Site matching policy: a dotted path, or an ordered list of them. |

Use `us` or `eu` for Kentik's hosted regions. Use `custom` for an on-premise or otherwise
non-standard deployment and supply both `grpc_base_url` and `portal_base_url` — the plugin
refuses to start if either is missing.

NetBox's `HTTP_PROXIES` setting is honoured for all outbound Kentik API calls.

### Matching policies

A policy decides which NetBox object corresponds to which Kentik object. Devices and sites
are matched by separate policies, configured independently. Device matching always runs first,
so a site policy can build on the device matches.

Device policies (`device_matching_policy`), in `netbox_kentik.policies.device`:

| Policy | Behaviour |
| ------ | --------- |
| `device.NamePolicy` | Case-insensitive exact match of NetBox device `name` ↔ Kentik `deviceName`. |

Site policies (`site_matching_policy`), in `netbox_kentik.policies.site`:

| Policy | Behaviour |
| ------ | --------- |
| `site.NamePolicy` | Case-insensitive exact match of NetBox site `name` ↔ Kentik site `title`. |
| `site.ByDevicePolicy` | Matches a NetBox site to the Kentik site its matched devices are assigned to, when they all agree. Matched devices without a Kentik site are ignored; at least one must have one. A site whose devices disagree is left unmatched, as is every NetBox site pointing at a Kentik site another NetBox site also points at. |

Either setting accepts an ordered list. Each policy only considers objects still unmatched by
the policies before it, so later entries act as fallbacks. For example, to match sites via
their devices and fall back to names for sites without matched devices:

```python
"site_matching_policy": [
    "netbox_kentik.policies.site.ByDevicePolicy",
    "netbox_kentik.policies.site.NamePolicy",
],
```

Matching is strictly 1:1 in both directions. If a second candidate would claim an
already-matched object, the policy logs a warning and skips it, so a duplicate name can never
produce two matches.

Each match records which policy made it and why, e.g. `site.ByDevicePolicy` with
`3 matched devices in this Kentik site`. This is shown on the device and site **Kentik** tabs, in
the optional *Matched by* and *Match reason* columns of the reconciliation tables, and as
per-policy match counts on the status page. Built-in policies are shown without the
`netbox_kentik.policies.` prefix; custom policies keep their full dotted path.

## How it works

Two independent jobs feed a Redis cache, which everything in the UI reads from:

```mermaid
flowchart LR
    K([Kentik API]) -->|"ApiSyncJob<br/>every 30 min"| R1[("Redis<br/>full device &amp; site records")]
    NB[("NetBox database<br/>devices &amp; sites")] --> RJ{{"ReconcileJob<br/>every 5 min"}}
    R1 --> RJ
    RJ --> R2[("Redis<br/>match maps<br/>per-object records<br/>totals")]
    R2 --> UI["Reconciliation view<br/>Kentik device &amp; site tabs<br/>portal buttons<br/>list columns"]
```

The two jobs are deliberately separate: syncing is rate-limit sensitive and touches the Kentik API,
whereas reconciliation is purely local and cheap, so it can run far more often.

Nothing is written to the NetBox database — all Kentik state lives in Redis with a **6 hour TTL**.
Cache keys are namespaced and self-invalidating:

```
netbox_kentik.v<netbox-version>.v<plugin-version>.<cache_seed>.<key>
```

## Permissions

The plugin adds two permissions, assigned through a NetBox **object permission**
(**Admin → Permissions**) on the object type **Kentik Reconciliation** (grouped under **Kentik integration for NetBox**):

| Permission | Object permission action | Grants |
| ---------- | ------------------------ | ------ |
| `netbox_kentik.view_reconciliation` | **View** | The Kentik menu, the Status and Reconciliation pages, and `GET /api/plugins/kentik/status/`. |
| `netbox_kentik.run_reconciliation` | additional action `run` | The **Sync now** and **Reconcile now** buttons on the Status page. |

Superusers have both. The Kentik tabs on devices and sites only need `dcim.view_device` / `dcim.view_site`.
The object type has no database table, so leave the permission's **Constraints** empty.

## Background jobs

Both jobs are registered as NetBox **system jobs**, so they appear under
**Operations → Jobs** with full logs and require no manual scheduling.

| Job | Interval | What it does |
| --- | -------- | ------------ |
| `Kentik API Sync Job` | every 30 minutes | Fetches all devices and sites from the Kentik API into Redis. |
| `Kentik Reconcile Job` | every 5 minutes | Runs the matching policy over the cached Kentik data and the NetBox database. |

In addition, the plugin listens for NetBox signals and re-enqueues a reconciliation whenever a
**Device** or **Site** is created, updated, or deleted — so the reconciliation view reflects
inventory changes without waiting for the next scheduled run.

## CLI (management commands)

Useful for operators, first-run bootstrapping, and automation.

```bash
# Queue the jobs, returning immediately (needs a running RQ worker)
python manage.py kentik_api_sync
python manage.py kentik_reconcile

# Run inline in this process and block until finished (no worker needed)
python manage.py kentik_api_sync --wait
python manage.py kentik_reconcile --wait
```

## REST API

The plugin currently exposes a single read-only status endpoint:

```
GET /api/plugins/kentik/status/
```

```json
{
  "plugin_version": "0.0.2",
  "device_matching_policies": ["netbox_kentik.policies.device.NamePolicy"],
  "site_matching_policies": ["netbox_kentik.policies.site.NamePolicy"],
  "api_sync": {
    "last_run": "2026-07-29T22:09:22Z",
    "expires_at": "2026-07-30T04:09:22Z"
  },
  "reconciliation": {
    "last_run": "2026-07-29T22:09:25Z",
    "last_trigger": "cli",
    "results": [
      {
        "object_type": "dcim.device",
        "netbox_total": 3,
        "netbox_matched": 2,
        "kentik_total": 398,
        "kentik_matched": 2
      },
      {
        "object_type": "dcim.site",
        "netbox_total": 4,
        "netbox_matched": 3,
        "kentik_total": 140,
        "kentik_matched": 3
      }
    ]
  }
}
```

## Troubleshooting

**"Reconciliation has not been run yet"** — either no RQ worker is running, or the API sync has
never succeeded. Check **Operations → Jobs** for failures, then run
`python manage.py kentik_api_sync --wait` to sync synchronously.

**Match counts look wrong** — the reconciliation view is only as current as the last job run.
Force a refresh via the UI or `python manage.py kentik_reconcile --wait`.

## Contributing

Bug reports and feature requests are welcome via
[GitHub issues](https://github.com/kentik/netbox-kentik/issues). Please report suspected security
vulnerabilities privately to support@kentik.com rather than in a public issue.

## About

Maintained by [Kentik](https://www.kentik.com/). See the
[Kentik knowledge base](https://kb.kentik.com/docs/netbox) for product documentation.
