Skip to content

Latest commit

 

History

History
574 lines (461 loc) · 14.3 KB

File metadata and controls

574 lines (461 loc) · 14.3 KB

Controller API Reference

Base URL: http://<host>:9090

All /api/* endpoints (except health and auth) require authentication via the mnm_token cookie.

Authentication

POST /api/auth/login

Authenticate with the admin password.

Request:

{"password": "your-admin-password"}

Response: 200 OK with Set-Cookie: mnm_token=...

{"status": "ok"}

GET /api/auth/check

Check if the current session is authenticated.

Response:

{"authenticated": true}

POST /api/auth/logout

Clear the session cookie.

Health

GET /api/health

No authentication required.

Response:

{"status": "ok"}

Status

GET /api/status

Returns health status of all MNM containers on the mnm-network.

Response:

{
  "containers": [
    {
      "name": "mnm-nautobot",
      "status": "running",
      "health": "healthy",
      "image": "mnm-nautobot:latest",
      "ports": ["8443->8080/tcp"]
    }
  ]
}

Discovery

POST /api/discover/sweep

Start a network sweep.

Request:

{
  "cidr_ranges": ["192.0.2.0/24"],
  "location_id": "uuid",
  "secrets_group_id": "uuid",
  "snmp_community": "public",
  "auto_discover_hops": 0
}

auto_discover_hops (optional, default 0): after onboarding a network device, walk its LLDP neighbors up to this many hops deep. 0 = disabled.

Response: 200 OK

{"status": "started"}

GET /api/discover/status

Poll sweep progress. Returns per-host enriched data.

Response:

{
  "running": true,
  "hosts": {
    "192.0.2.1": {
      "ip": "192.0.2.1",
      "status": "known",
      "ports_open": [22, 161, 830],
      "mac_address": "aa:bb:cc:dd:ee:ff",
      "mac_vendor": "Juniper Networks",
      "dns_name": "firewall-01.example.com",
      "snmp": {"sysName": "firewall-01", "sysDescr": "..."},
      "classification": "network_device",
      "first_seen": "2026-04-05T14:30:00Z",
      "last_seen": "2026-04-06T02:00:00Z"
    }
  },
  "summary": null
}

GET /api/discover/neighbors

Returns LLDP neighbors not matching known devices.

Response:

{
  "neighbors": [
    {
      "neighbor_name": "ap-lobby-01",
      "connected_to": "ge-0/0/5 (core-switch-01)"
    }
  ]
}

POST /api/discover/onboard

Onboard a single device (from LLDP advisory).

Request:

{
  "ip": "192.0.2.50",
  "location_id": "uuid",
  "secrets_group_id": "uuid"
}

GET /api/discover/schedule

Returns configured sweep schedules.

POST /api/discover/schedule

Save a sweep schedule.

Request:

{
  "cidr_ranges": ["192.0.2.0/24"],
  "location_id": "uuid",
  "secrets_group_id": "uuid",
  "interval_hours": 24
}

Endpoints (Infrastructure Collection)

GET /api/endpoints

Returns all collected endpoint records. Supports query parameters: ?vlan=100, ?switch=core-switch-01, ?mac_vendor=Apple, ?source=infrastructure

GET /api/endpoints/summary

Returns summary stats: total endpoints, VLANs active, vendors seen, last collection time.

Response:

{
  "total_endpoints": 147,
  "vlans_active": 12,
  "vendors_seen": 45,
  "switches": 2,
  "last_collection": "2026-04-06T14:00:00Z",
  "running": false
}

POST /api/endpoints/collect

Manually trigger an endpoint collection run.

Nodes

Nodes are onboarded infrastructure devices that MNM authenticates to and actively polls.

GET /api/nodes

List all onboarded nodes with poll health status.

Response:

{
  "nodes": [
    {
      "name": "core-switch-01",
      "id": "uuid",
      "platform": "junos",
      "primary_ip": "198.51.100.1/32",
      "role": "Network Device",
      "location": "Main Site",
      "health": "green",
      "health_label": "All polls healthy",
      "last_polled": "2026-04-09T22:05:00Z",
      "jobs": {
        "arp": {"last_success": "...", "interval_sec": 300, "enabled": true},
        "mac": {"last_success": "...", "interval_sec": 300, "enabled": true}
      },
      "interface_count": null,
      "nautobot_url": "..."
    }
  ]
}

GET /api/nodes/{node_name}

Returns details for a single node including poll status and Nautobot device data.

GET /api/nodes/macs

Returns MAC addresses belonging to onboarded nodes (used to filter endpoints page).

Response:

{"macs": ["AA:BB:CC:DD:EE:FF", "11:22:33:44:55:66"]}

GET /api/nodes/{node_name}/arp

ARP table for a specific node. Query params: ip, mac.

GET /api/nodes/{node_name}/mac-table

MAC address table for a specific node. Query params: mac, interface, vlan.

GET /api/nodes/{node_name}/lldp

LLDP neighbors for a specific node.

GET /api/nodes/{node_name}/fib

Forwarding table (FIB) entries for a specific node. Populated from SNMP route data. Query param: prefix.

GET /api/investigate

Contextual network investigation. Auto-detects query type and returns categorized results. Accepts any MAC format (colon, hyphen, Cisco dot, bare hex) and normalizes internally.

Query params:

  • q (required) — MAC address, IP, CIDR prefix, or hostname text
  • node (optional) — filter results to a specific node
  • vlan (optional) — filter to a specific VLAN (MAC search only)
  • type (optional) — force query type: mac, ip, prefix, text (auto-detected if omitted)

MAC search response:

{
  "query": "aa:bb:cc:dd:ee:ff",
  "query_type": "mac",
  "results": {
    "arp_hits": [{"node_name": "...", "ip": "198.51.100.10", "mac": "AA:BB:CC:DD:EE:FF", "interface": "ge-0/0/5"}],
    "mac_hits": [{"node_name": "...", "mac": "AA:BB:CC:DD:EE:FF", "interface": "ge-0/0/5", "vlan": 100}],
    "endpoint": {"mac": "AA:BB:CC:DD:EE:FF", "ip": "198.51.100.10", "hostname": "..."},
    "location": {"switch": "core-switch-01", "interface": "ge-0/0/5", "vlan": 100, "description": "Connected to core-switch-01 port ge-0/0/5 in VLAN 100"},
    "vm_host": {"name": "web-server-01", "vmid": 100, "node": "pve1", "status": "running", "type": "qemu"}
  }
}

IP search response:

{
  "query": "198.51.100.1",
  "query_type": "ip",
  "results": {
    "arp_hits": [{"node_name": "...", "ip": "198.51.100.1", "mac": "...", "interface": "..."}],
    "routes": [{"node_name": "...", "prefix": "198.51.100.0/24", "next_hop": "...", "protocol": "ospf"}],
    "fib": [{"node_name": "...", "prefix": "198.51.100.0/24", "next_hop": "...", "interface": "ge-0/0/0"}],
    "endpoints": [{"mac": "...", "ip": "198.51.100.1", "hostname": "..."}]
  }
}

Prefix search response includes routes, fib, gateways, and arp_hits (ARP entries within subnet).

Text search response includes lldp (matching system names) and endpoints (matching hostnames).

Nautobot Proxy

GET /api/nautobot/devices

Returns all devices from Nautobot (raw Nautobot API proxy, unfiltered).

GET /api/nautobot/devices/{id}

Returns a single device from Nautobot.

GET /api/nautobot/secrets-groups

Returns available credential sets.

GET /api/nautobot/locations

Returns available locations.

Configuration

GET /api/config

Returns controller configuration.

POST /api/config

Update controller configuration (merge).

Example: curl

# Login
curl -c cookies.txt -X POST http://localhost:9090/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"password":"test123"}'

# Check status
curl -b cookies.txt http://localhost:9090/api/status

# Start sweep
curl -b cookies.txt -X POST http://localhost:9090/api/discover/sweep \
  -H "Content-Type: application/json" \
  -d '{"cidr_ranges":["192.0.2.0/29"],"location_id":"uuid","secrets_group_id":"uuid","snmp_community":"public"}'

# Poll progress
curl -b cookies.txt http://localhost:9090/api/discover/status

Jobs

GET /api/jobs

Consolidated view of all background tasks: sweep scheduler, modular poller, Proxmox collector, database prune, and legacy endpoint collector.

Response:

{
  "jobs": [
    {
      "id": "sweep",
      "name": "Sweep Scheduler",
      "status": "idle",
      "running": false,
      "schedule_interval": "1h",
      "last_run": "2026-04-09T22:10:00Z",
      "duration_seconds": 285.3,
      "summary": {"total": 254, "alive": 10, "known": 3},
      "enabled": true
    }
  ]
}

POST /api/discover/sweep-scheduled

Re-run the first saved sweep schedule. Used by the Jobs page "Run Now" button.

Response: 200 OK

{"status": "started", "cidr_ranges": ["198.51.100.0/24"]}

Modular Polling

Per-device, per-job-type collection tracking. Replaces the monolithic endpoint collector.

GET /api/polling/status

All devices, all job types, grouped by device.

Response:

{
  "devices": [
    {
      "device_name": "core-switch-01",
      "jobs": {
        "arp": {"last_success": "2026-04-09T22:05:00Z", "interval_sec": 300, "enabled": true},
        "mac": {"last_success": "2026-04-09T22:05:06Z", "interval_sec": 300, "enabled": true},
        "dhcp": {"last_success": null, "interval_sec": 600, "enabled": true},
        "lldp": {"last_success": "2026-04-09T21:30:00Z", "interval_sec": 3600, "enabled": true}
      }
    }
  ]
}

GET /api/polling/status/{device_name}

Single device, all job types.

POST /api/polling/trigger/{device_name}

Trigger immediate poll of all enabled job types for a device. Returns 202 Accepted.

POST /api/polling/trigger/{device_name}/{job_type}

Trigger a single job type (arp, mac, dhcp, lldp, routes, or bgp). Returns 202 Accepted.

PUT /api/polling/config/{device_name}/{job_type}

Update interval or enabled flag for a specific device/job type.

Request:

{"interval_sec": 600, "enabled": false}

Response: Updated poll row.

Routes

Routing table data collected from onboarded nodes via NAPALM. Stored in the controller database (Nautobot has no native routing model).

GET /api/routes

Query collected routes with optional filters.

Query params: node_name, vrf, protocol, prefix (substring search)

Response:

{
  "routes": [
    {
      "id": 1,
      "node_name": "core-switch-01",
      "prefix": "198.51.100.0/24",
      "next_hop": "198.51.100.1",
      "protocol": "ospf",
      "vrf": "default",
      "metric": 10,
      "preference": 110,
      "outgoing_interface": "ge-0/0/0",
      "active": true,
      "collected_at": "2026-04-10T12:00:00Z"
    }
  ],
  "count": 1
}

GET /api/routes/{node_name}

All routes for a specific node. Query params: vrf, protocol.

GET /api/routes/advisories

Routes with next-hops that don't match any known IP in endpoint data or Nautobot IPAM. These are discovery candidates.

Response:

{
  "advisories": [
    {
      "node_name": "core-switch-01",
      "prefix": "203.0.113.0/24",
      "next_hop": "198.51.100.254",
      "protocol": "static",
      "vrf": "default"
    }
  ],
  "count": 1
}

BGP

BGP neighbor state collected from onboarded nodes via NAPALM.

GET /api/bgp

Query collected BGP neighbors with optional filters.

Query params: node_name, state, vrf

Response:

{
  "neighbors": [
    {
      "id": 1,
      "node_name": "core-router-01",
      "neighbor_ip": "198.51.100.2",
      "remote_asn": 65001,
      "local_asn": 65000,
      "state": "Established",
      "prefixes_received": 150,
      "prefixes_sent": 75,
      "uptime_seconds": 86400,
      "vrf": "default",
      "address_family": "ipv4 unicast",
      "collected_at": "2026-04-10T12:00:00Z"
    }
  ],
  "count": 1
}

GET /api/bgp/{node_name}

All BGP neighbors for a specific node. Query params: vrf.

Auto-Discovery

POST /api/discover/auto

Manually trigger hop-limited auto-discovery from a specific node.

Request:

{
  "node_name": "core-switch-01",
  "max_hops": 2,
  "location_id": "uuid",
  "secrets_group_id": "uuid",
  "snmp_community": "public"
}

Response:

{
  "seed_node": "core-switch-01",
  "max_hops": 2,
  "attempted": 3,
  "succeeded": 2,
  "failed": 1,
  "skipped": 0,
  "nodes": [
    {"name": "idf-switch-01", "ip": "198.51.100.10", "status": "succeeded", "hop_depth": 1, "parent": "core-switch-01"},
    {"name": "idf-switch-02", "ip": "198.51.100.11", "status": "succeeded", "hop_depth": 1, "parent": "core-switch-01"},
    {"name": "ap-closet-01", "ip": "198.51.100.20", "status": "failed", "hop_depth": 2, "parent": "idf-switch-01"}
  ]
}

GET /api/discover/auto/history

Past auto-discovery run summaries.

GET /api/discover/auto/recent

Nodes auto-discovered in the last N hours. Query param: hours (default 24). Used by the dashboard advisory card.

Onboarding Progress

GET /api/discover/onboarding

All tracked onboarding job states.

GET /api/discover/onboarding/{ip}

Detailed onboarding progress for a single host. Stages: submitting, queued, running, succeeded, failed, timeout.

Response:

{
  "ip": "198.51.100.7",
  "stage": "succeeded",
  "message": "Device onboarded (core-switch-02)",
  "job_result_id": "uuid",
  "device_id": "uuid"
}

Phase 2.7 — Endpoint correlation endpoints

These endpoints expose the MAC-keyed endpoint store backed by the mnm_controller PostgreSQL database. They return empty results if the database is unreachable; the controller will fall back to JSON for /api/config only.

GET /api/endpoints/{mac}

Return the current identity record for one MAC.

GET /api/endpoints/{mac}/history

Return all endpoint_events rows for a MAC, newest first. Response: {"mac": "...", "events": [{"event_type": "moved_port", "old_value": "ge-0/0/12", "new_value": "ge-0/0/24", "timestamp": "..."}, ...]}

GET /api/endpoints/{mac}/timeline

Return a chronological narrative for a MAC. Each entry has a human-readable text describing the event, plus the underlying event_type and timestamp. Response also includes the current endpoint record.

GET /api/endpoints/events

Recent network activity feed. Query params:

  • type (optional) — appeared, moved_port, moved_switch, ip_changed, hostname_changed
  • since (default 24h) — duration string: 1h, 24h, 7d, 30d
  • limit (default 200)

GET /api/endpoints/conflicts

Return IPs currently claimed by more than one MAC. Each entry includes the list of conflicting endpoints with their switch/port context.

# Last day of port moves
curl -b cookies.txt 'http://localhost:9090/api/endpoints/events?type=moved_port&since=24h'

# Full timeline for one endpoint
curl -b cookies.txt http://localhost:9090/api/endpoints/AA:BB:CC:DD:EE:FF/timeline