NetAuto Academy

Capstone

From Source of Truth to an Actionable Problem

Advanced14 min read

What you'll learn

  • The one scenario this entire module builds toward: an NTP server migration
  • NetBox-sourced inventory feeding directly into NAPALM state collection
  • Classifying a real fleet as compliant vs. non-compliant against one requirement
  • What this stage does NOT re-teach, and why
🎓 Teach This Lesson

Listen to a guided explanation of this lesson.

Spoken narration isn't supported in this browser.

One scenario, five lessons, one system

The Capstone in one sentence

The organization is migrating its NTP configuration to a new server, 10.10.10.2. This module builds one coherent, cumulative system that detects which devices haven't migrated, generates and Git-evidences the exact fix, rolls it out through a canary gate, and produces one auditable report -- across five lessons, ending in one complete script. No new automation framework appears anywhere in this module.

Quick checkConcept check
How many separate projects does this module build?

What this stage integrates

  1. NetBox-shaped source of truthNetBox / Source of Truth, Real-world Projects
  2. NetBoxInventory2 -> dynamic Nornir inventoryuse_platform_napalm_driver, unchanged
  3. napalm_get(get_config) across the fleetMulti-vendor Automation, NAPALM
  4. Classify: compliant vs. non-compliantthis stage's one new output

Not re-taught here

How NetBoxInventory2 resolves platforms, how nornir_napalm selects a driver, and how NAPALM's mock driver serves fixture data are all assumed knowledge from NetBox / Source of Truth, Multi-vendor Automation, and Real-world Projects' own Multi-Vendor Configuration Compliance Report. Nothing below re-explains them.

Quick checkScenario
This lesson's fleet-loading and compliance-check code looks almost identical to Real-world Projects' Multi-Vendor Configuration Compliance Report. Is that a problem?

The fleet and the requirement

Four devices, three vendor platforms, one requirement: ntp server 10.10.10.2 must be present in each device's running configuration.

mock_netbox_server.py (excerpt)
DEVICES = [
  {"id": 101, "name": "RTR1", "platform": {"id": 1, "slug": "cisco_ios", "name": "Cisco IOS"}, "site": {"id": 1, "slug": "hq"}, "primary_ip": None},
  {"id": 102, "name": "RTR2", "platform": {"id": 1, "slug": "cisco_ios", "name": "Cisco IOS"}, "site": {"id": 1, "slug": "hq"}, "primary_ip": None},
  {"id": 103, "name": "SW-EOS1", "platform": {"id": 2, "slug": "arista_eos", "name": "Arista EOS"}, "site": {"id": 1, "slug": "hq"}, "primary_ip": None},
  {"id": 104, "name": "SW-JUNOS1", "platform": {"id": 3, "slug": "junos", "name": "Juniper Junos"}, "site": {"id": 1, "slug": "hq"}, "primary_ip": None},
]

Same freshly-built, stdlib-only NetBox-shaped mock pattern as Real-world Projects (two endpoints: /api/dcim/platforms/, /api/dcim/devices/). Three vendors across four devices -- rebuilt fresh for this module, not reused from any earlier phase's file.

The code

capstone_lib.py (Lesson 1 portion)
import os
from nornir import InitNornir
from nornir.core.inventory import ConnectionOptions
from nornir_napalm.plugins.tasks import napalm_get

NEW_NTP_SERVER = "10.10.10.2"
REQUIRED_NTP_LINE = f"ntp server {NEW_NTP_SERVER}"


def load_fleet_from_netbox(netbox_url, fixtures_dir):
  nr = InitNornir(
      inventory={
          "plugin": "NetBoxInventory2",
          "options": {
              "nb_url": netbox_url,
              "nb_token": "fake-token-for-mock-server",
              "use_platform_napalm_driver": True,
              "group_file": "inventory/groups.yaml",
              "defaults_file": "inventory/defaults.yaml",
          },
      },
  )
  for host in nr.inventory.hosts.values():
      host.connection_options["napalm"] = ConnectionOptions(
          extras={"optional_args": {"path": os.path.join(fixtures_dir, host.name)}}
      )
  return nr


def _pull_config(task):
  task.run(task=napalm_get, getters=["get_config"])


def detect_noncompliance(nr):
  agg = nr.run(task=_pull_config)
  compliant = []
  non_compliant = []
  for hostname, multi_result in agg.items():
      running_config = multi_result[1].result["get_config"]["running"]
      host = nr.inventory.hosts[hostname]
      vendor = host.data.get("platform", {}).get("slug", "unknown")
      if REQUIRED_NTP_LINE in running_config:
          compliant.append({"host": hostname, "vendor": vendor})
      else:
          non_compliant.append({"host": hostname, "vendor": vendor})
  return {"compliant": compliant, "non_compliant": non_compliant}

Real, verified code. load_fleet_from_netbox() reuses NetBoxInventory2 exactly as configured in Real-world Projects; detect_noncompliance() reuses the same get_config()-substring compliance check, this time producing a result the next lesson consumes directly.

Quick checkIdentify the correct code
Why does load_fleet_from_netbox() override each host's connection_options after loading the inventory, rather than relying on NetBox's data directly for the NAPALM connection?

Real, verified output

terminal output — real, verified run
--- Detection (fleet loaded from NetBox) ---
compliant: [{'host': 'SW-JUNOS1', 'vendor': 'junos'}]
non_compliant: [
{'host': 'RTR1', 'vendor': 'cisco_ios'},
{'host': 'RTR2', 'vendor': 'cisco_ios'},
{'host': 'SW-EOS1', 'vendor': 'arista_eos'}
]

Real, verified. SW-JUNOS1 already carries "ntp server 10.10.10.2" and is correctly classified compliant; the other three devices, across two other vendors, are correctly detected as non-compliant -- both outcomes genuinely produced by the same run, not asserted separately.

Quick checkPredict output
SW-JUNOS1 never appears again in any later lesson's canary, expansion, or rollback data. Why?

What this does and doesn't prove

Never claimed here

This ran against a freshly-built, stdlib-only NetBox-shaped mock and NAPALM's real mock driver -- never real NetBox, never a real device. The detection logic itself is genuinely verified; whether any real fleet's NTP configuration matches this result is a separate, untested question.

Quick checkScenario
How should a learner honestly describe what this lesson's real run proves?

Key takeaways

  • One scenario drives the entire module: migrating the fleet's NTP configuration to 10.10.10.2.
  • load_fleet_from_netbox() and detect_noncompliance() reuse already-verified mechanisms from NetBox / Source of Truth, Multi-vendor Automation, and Real-world Projects -- nothing here is a new automation primitive.
  • Real, verified run: 1 compliant device and 3 non-compliant devices across three vendor platforms, both outcomes genuinely produced by the same execution.
  • The detection result is not a final report here -- it is the input the next four lessons build on.
  • Mock NetBox and mock NAPALM driver only -- never claimed as evidence about real infrastructure.

Done with this lesson?

Next lesson

Generating Remediation from Detected State