Skip to main content

Nautobot SSoT integration for Hudu (one-way: Nautobot -> Hudu).

Project description

nautobot-ssot-hudu

A Nautobot SSoT integration that pushes data from Nautobot into Hudu.

Direction: Nautobot is the source of truth. Hudu is a Data Target — Nautobot writes Companies, Devices, and related records into Hudu.

Status: Beta. Seven entity types syncing end-to-end with full idempotency, 94 unit tests, validated against a live Hudu self-hosted instance. Not yet on PyPI.

Compatibility: Nautobot 3.0+, Python 3.10+, nautobot-ssot 4.2+, hudu-magic 0.4+.

Architecture

Built on the DiffSync library bundled with nautobot-ssot. The hudu-magic client library handles all Hudu API I/O.

Nautobot ORM ──> Nautobot DiffSync adapter ─┐
                                            ├──> diff ──> Hudu DiffSync adapter ──> hudu-magic ──> Hudu API
                                  Hudu API ─┘   (read current state)

Mapping

Nautobot Hudu Status
tenancy.Tenant Company ✅ name, description (notes)
dcim.Device Asset (per-role layouts + configurable custom field map) ✅ name + custom-field map + role-based layout selection
ipam.Prefix Network ✅ address (CIDR), name, description
ipam.IPAddress IPAddress ✅ address (host), dns_name, description
ipam.VLAN VLAN ✅ vid (1-4094), name, description
dcim.Rack RackStorage ✅ name, height (U), width (in), serial, asset_tag, description, desc_units
dcim.Device rack/position/face RackStorageItem ✅ asset placement: rack_name, start_unit, end_unit, side
dcim.Location (no API equivalent) ⚠️ Hudu does not expose Locations as a CRUD entity. Per-device location is captured via device_field_map["Location"] = "location.name".

Cross-entity linkages (set automatically when both sides are synced):

  • IPAddress → Asset (Hudu IP page shows the device it's assigned to, via Nautobot IPAddress.interface_assignments → device)
  • Network → VLAN (Hudu Network page shows its VLAN, via Nautobot Prefix.vlan)

Identity model:

Entity Identifier Notes
Company (name,) Both sides enforce uniqueness on name
Device (company_name, name) Hudu Asset names unique only within a company
Network (company_name, address) Networks are scoped per-company
IPAddress (company_name, address) address is the host, no mask
VLAN (company_name, vid) 802.1Q tag, two companies can each own VLAN 100
Rack (company_name, name) Same scoping as Device
RackItem (company_name, asset_name) One rack item per asset max

We deliberately use human-readable identifiers (names/vids) across the diff boundary, not Hudu primary keys. If a Hudu record is deleted and recreated, its pk changes but the identifier stays the same — the next sync rebinds by name rather than diffing as "needs update."

Empty-string normalization: Both adapters coerce empty string "" to None when loading. Hudu stores unset fields as null; Nautobot CharField defaults are "". Without coercion every sync would emit spurious updates for blank fields.

Device custom field mapping

Operators choose which Nautobot Device attributes populate which Hudu custom-layout fields via PLUGINS_CONFIG:

PLUGINS_CONFIG = {
    "nautobot_ssot_hudu": {
        "instance_url": "https://acme.huducloud.com",
        "secret_group_name": "Hudu Credentials",
        "asset_layouts": {
            # Default Hudu asset_layout_id for Devices whose role isn't
            # explicitly mapped below. Unset/None → those devices skip.
            "device": 7,
            # Optional per-Nautobot-Role overrides. Keys are role names;
            # values are Hudu asset_layout_ids. Useful for MSPs documenting
            # heterogeneous fleets across multiple Hudu layouts.
            "device_by_role": {
                "router": 8,
                "switch": 9,
                "firewall": 10,
            },
        },
        # Hudu field label -> Nautobot Device attribute path (dotted)
        "device_field_map": {
            "Hostname": "name",
            "Management IP": "primary_ip4.host",
            "Model": "device_type.model",
            "Serial": "serial",
            "Status": "status.name",
            "Location": "location.name",
        },
    }
}

The Hudu asset_layout must already have custom fields with matching labels. Field-resolution uses safe None-propagation: a Device without a primary_ip4 yields None for "Management IP" rather than raising AttributeError.

Limitations

  • Locations don't sync as a separate entity. Hudu's REST API doesn't expose Locations as a manageable resource (verified empirically — /api/v1/locations returns 404, no Locations admin page). Per-device location is captured via the device_field_map["Location"] = "location.name" entry, which appears as a custom-field string on each synced Asset.
  • Layout migration isn't supported. Hudu's API can't move an existing Asset between asset_layouts. If a Nautobot Device's role is reassigned and the new role maps to a different Hudu layout, the diff surfaces it but the writeback logs a warning and skips. Operator must delete + recreate manually.
  • hudu-magic library quirks are documented in development/hudu/HUDU_API_QUIRKS.md — several endpoints reject the lib's auto-paginated ?page=1 parameter, and several update operations are missing from the lib's resource registry. The plugin works around all of them.

Hudu prep

Before the first sync, the operator must:

  1. Create the asset_layout(s) that Devices will live in. Hudu doesn't ship a built-in "Network Device" layout — each operator defines their own. Each layout's custom fields must have labels matching the keys in device_field_map (e.g. "Hostname", "Management IP", "Model", "Serial", "Status", "Location").
  2. Generate an API key in Hudu (Admin → API Keys → New API Key). Scope: Full access. The "Delete data" permission is required if you plan to use hard_delete=True; "View passwords" and "Export data" can stay off — the plugin doesn't read passwords or export bulk data.
  3. Note the layout IDs — visible in Hudu's Admin → Asset Layouts list. You'll wire them into PLUGINS_CONFIG.asset_layouts.device (and optionally device_by_role).

Run-time options

Exposed as Job parameters in the Nautobot UI:

  • dryrun (framework-provided) — calculate the diff but don't write. Default: True. This is the canonical control; we don't redeclare it.
  • hard_delete — when an entity exists in Hudu but no longer in Nautobot, archive it (default, recoverable via Hudu UI) or hard-delete (irreversible).

Development

For static checks:

uv sync --extra dev
uv run pytest
uv run ruff check

For end-to-end testing against a real Nautobot instance, see development/README.md — a self-contained 4-container stack (postgres + redis + nautobot-web + nautobot-worker) with the plugin source bind-mounted for hot-reload and a seed script that creates synthetic Tenants + a Hudu SecretsGroup.

cd development/
cp .env.example .env  &&  $EDITOR .env
make build && make up
make seed

License

Private / proprietary. Not yet published.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nautobot_ssot_hudu-2026.5.4.tar.gz (272.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nautobot_ssot_hudu-2026.5.4-py3-none-any.whl (27.0 kB view details)

Uploaded Python 3

File details

Details for the file nautobot_ssot_hudu-2026.5.4.tar.gz.

File metadata

  • Download URL: nautobot_ssot_hudu-2026.5.4.tar.gz
  • Upload date:
  • Size: 272.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for nautobot_ssot_hudu-2026.5.4.tar.gz
Algorithm Hash digest
SHA256 c6934cff5939d1b384e4c0596c323d34257e34afc899432a202b9e294c66bdbf
MD5 658a7b32be61e8cb54dfe8654152c874
BLAKE2b-256 3976405ec2be693e6e7db5067283e7a7c80b7e5a2b57f0a01a0979346638367a

See more details on using hashes here.

File details

Details for the file nautobot_ssot_hudu-2026.5.4-py3-none-any.whl.

File metadata

  • Download URL: nautobot_ssot_hudu-2026.5.4-py3-none-any.whl
  • Upload date:
  • Size: 27.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"EndeavourOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for nautobot_ssot_hudu-2026.5.4-py3-none-any.whl
Algorithm Hash digest
SHA256 9604a09209f476e9253ef6b7d39c6d81b812b5489b13e6363de4ac1b574bfdb9
MD5 abd37861f622038707d88d3f8c0e1b6a
BLAKE2b-256 eb3910bffe2ba5d8bb2ab973e36c292307ff7b342c20ae9b4b8ba64bd86d91a3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page