Skip to content

v3.x summary — Device sync, VLAN sub-interfaces, Static Routes

This page summarizes the capability arc from v3.0 → v3.4.1. For the per-release detail (every hotfix, regression guard, and PII audit finding), see the canonical CHANGELOG.md in the repo.

What v3.x adds

The v2.x line stabilized bidirectional sync for firewall objects + wireless config + NAT VIPs + policies. The v3.x line expands the project's surface beyond config-objects to DCIM: the FortiGate itself, its interfaces (physical + aggregate + VLAN sub-interfaces), its IP assignments, and its static routes.

Job count: 5 → 7

v3.x added 2 new Jobs:

Job Direction First shipped
FortiGate → Nautobot (device + interfaces) pull v3.0 (2026.05.18.11)
Nautobot → FortiGate (device + interfaces) push v3.3+v3.4 (2026.05.19.0/.1)

Plus the existing 5 (firewall pull/push, wireless pull/push, live status) — total 7 Jobs registered.

New capabilities by release

v3.0 — Device + Interface sync (pull)

The FortiGate appears as a dcim.Device with its operator-meaningful interfaces (physical / aggregate / hard-switch / switch) and IP assignments synced from system.interface. Operator pre-creates the DeviceType + Role + Location + Status; the sync attaches.

v3.1 — VLAN sub-interfaces + Static Routes (pull)

VLAN sub-interfaces (FortiOS type: vlan with a real vlanid and interface parent — not the auto-quarantine wqtn.* ones) sync as dcim.Interface records with parent_interface resolution.

Static routes (FortiOS cmdb/router/static) sync to a new app-owned Django model: FortinetStaticRoute, anchored to the FortiGate's dcim.Device. Browse them at /plugins/ssot-fortinet/static-routes/.

Why an app-owned model: Nautobot core doesn't have a first-class Route model. The FortinetStaticRoute lives in this app's namespace and references the synced Device, the next-hop IP, the destination (prefix or named AddressObject), and the FortiOS metadata (distance, priority, blackhole, etc.).

v3.2 — FortiOS shape coverage from Kevin's prod sync

Real production FortiOS data revealed shape variations our dev-only testing hadn't surfaced:

  • MAC / dynamic / geography address types — synced as placeholder FQDNs (e.g. vpn_users.dynamic.fortios.invalid) with the actual FortiOS value annotated in the description. Operators can see them in Nautobot's UI without losing the FortiOS-side context.
  • dstaddr shape varies by FortiOS version — plain string on 7.0.x, list-of-dict on 7.2+. Normalized in the pull adapter.
  • DHCP-acquired default routes appear in the routing table but NOT in cmdb/router/static — our static-route sync deliberately covers only operator config-intent (the cmdb view), not the active RIB.

v3.3 — VLAN sub-interface PUSH

Operators can now author VLAN sub-interfaces in Nautobot's UI and push them to FortiGate. New "Nautobot → FortiGate (device + interfaces)" Job.

Caveat: FortiOS silently truncates interface names to 15 characters. Keep your VLAN sub-interface names short or the round-trip drifts.

v3.4 — Static route PUSH

Authoring static routes in Nautobot now propagates to FortiOS via the same push Job. Includes blackhole routes, named-AddressObject destinations (with the allow-routing: enable flag set automatically), and explicit ECMP via priority / distance.

v3.4.1 — Module-import smoke-test framework

Tiny tests that import every shipped module catch the bug class where a top-level statement raises (e.g. v3.3's NavMenuGroup type error that broke ALL 7 Jobs on import). Runs in <1s; catches the kind of issue that would otherwise need a full Nautobot startup to surface.

Same-day hotfixes captured in CHANGELOG

The 2026.05.18.13.1 through .6 series (5 same-day fixes) are documented in CHANGELOG.md. Highlights:

  • .1 — NavMenuGroup type error fix (regression on Nautobot 3.x nav API)
  • .2 — blackhole field treated as string, not bool (would have flipped every non-blackhole route to blackhole)
  • .3 / .4 — PII audit findings + scrub
  • .5 — Device.serial extraction (closes v3.0's carryover bug)
  • .6 — named-AddressObject route resolution (closes v3.1 deferral)

Live-validated against real hardware

Every v3.x capability has at least one e2e test in development/scripts/. The dev stack runs against a FortiWiFi-61E, and Kevin's production environment (a FortiGate-100F cluster) surfaced the v3.2 shape variations. Both targets converge on the same Nautobot schema.

Upgrade path

pip install --upgrade nautobot-ssot-fortinet
nautobot-server collectstatic --no-input
sudo systemctl restart nautobot nautobot-worker

If you were on a v2.x release, run the pull Jobs once after upgrade — the v2.5 /32 ipmask normalization migrates pre-v2.5 records to the new classification on first pull.

What's queued for v3.5+

  • Push direction for the FortinetStaticRoute UI workflow (operators editing routes in Nautobot and pushing) — most of v3.4 is the underlying machinery; v3.5 adds the UI-driven workflow polish
  • VPN sync (Nautobot has vpn.VPN / vpn.VPNTunnel models, FortiOS has phase1/phase2 data)
  • firewall.schedule — currently we hardcode schedule: "always" on policy push; time-based rules need this

See the maintainer's notes (CLAUDE.md in the repo) for the full queue.