Skip to main content

Example: network audit

This article refers to SDK version v0.5.0. The current SDK version is N/A.

Overview

network_audit.py reports a node's network configuration: physical interfaces, hostname, VPN and Proxy state, IPTables, and NTP servers, in one shot, across as many nodes as you point it at. It's useful for a pre-deployment check, a support ticket, or spotting drift across a fleet, for example a node with a custom proxy or firewall rule nobody remembers setting.

The one write this script is willing to make is adding an NTP server, dry-run by default like every other script in this section that can modify data. It deliberately never touches physical interfaces, Proxy, Standalone Mode, VPN, or IPTables, since a bad change to any of those can strand a node's connectivity, this script only reports their current state.

Get the script

Download network_audit.py from the SDK's examples/ directory. If you haven't installed the SDK yet, see Getting started.

Set up credentials

The script reads the four Barbara API Credentials from environment variables, or from a .env file placed next to it (loaded automatically, or pointed at with --env-file). See Set up credentials in the Hello World walkthrough for the full picture.

Walk through the code

all_nodes() pages through every node in the company, the same helper used by Check Barbara Core updates; it only runs when no specific nodes are named on the command line.

decode_ntp_servers() reads NTP servers straight from a node's raw document, since there's no resource method to list them, only create/update/delete:

def decode_ntp_servers(node: Node) -> list[dict[str, Any]]:
servers = node.raw.get("deviceConfig", {}).get("ntpServers", [])
result = []
for entry in servers:
encoded = entry.get("current", {}).get("update", {}).get("config", {}).get("server", "")
result.append({
"id": entry.get("_id"),
"server": from_base64(encoded) if encoded else "",
"systemServer": entry.get("systemServer", False),
})
return result

systemServer: True marks one of Barbara's own default NTP servers rather than one you added, never create/update/delete those. This mirrors the pattern Read a node's info and telemetry uses for other fields the SDK doesn't wrap in a typed model, read the raw payload directly.

audit_node() is the core of the script, combining client.nodes.network end to end:

def audit_node(client: BarbaraClient, node: Node) -> dict[str, Any]:
network = client.nodes.network.get(node.id)
return {
"nodeId": node.id,
"nodeName": node.node_name,
"hostname": client.nodes.network.get_hostname(node.id),
"interfaces": [
{"name": iface.name, "type": iface.type, "active": iface.active}
for iface in network.interfaces
],
"vpn": network.vpn,
"proxy": [entry.get("status") for entry in network.proxy],
"iptables": client.nodes.network.get_iptables(node.id),
"ntpServers": decode_ntp_servers(node),
}

get_hostname and get_iptables each have no dedicated GET endpoint on the wire, the SDK reads both back from the node's own document instead, the same design as decode_ntp_servers above applied inside the resource itself. network.get(node_id) is the one call that covers physical interfaces, VPN, and Proxy state in a single request, the same snapshot Panel's Networking card shows.

main() follows the same fail-fast login and dry-run/--apply shape as every other script in this section. Adding an NTP server is the only branch that writes anything:

if args.add_ntp:
if not args.apply:
result["addNtp"] = {"server": args.add_ntp, "status": "dry-run"}
else:
client.nodes.network.create_ntp_server(node.id, args.add_ntp)
result["addNtp"] = {"server": args.add_ntp, "status": "added"}

Run it

# Read-only: audit every node in your company.
python network_audit.py

# Only these nodes.
python network_audit.py node-01 node-02

# Add an NTP server (dry-run first, then for real).
python network_audit.py node-01 --add-ntp time.google.com
python network_audit.py node-01 --add-ntp time.google.com --apply

Try it

Run it against one node and compare the ntpServers list to what Panel's Networking card shows, systemServer: true entries are Barbara's own defaults. Note that hostname and iptables have no dedicated read endpoint at all on the wire, and NTP servers don't even have that: they're read straight from node.raw["deviceConfig"]["ntpServers"], no resource method wraps the list at all.

Extend it

  • Add a removal counterpart. --remove-ntp <id> using client.nodes.network.delete_ntp_server, the id comes from this same script's ntpServers output.
  • Add a write for a riskier setting, carefully. If you extend this to touch physical interfaces, Proxy, Standalone Mode, VPN, or IPTables, keep the same dry-run/--apply gate this script uses for NTP, and consider requiring rollback=True (Panel's own default for these calls) rather than exposing it as a flag a caller could turn off by accident.

Summary

You audited a fleet's network configuration in one shot, physical interfaces, hostname, VPN, Proxy, IPTables, and NTP, and made the one network write this script is willing to make, safely, behind a dry-run gate.

Auditing configuration this way surfaces drift, a node with the wrong NTP server or a missing proxy, before it turns into a support ticket, across as many nodes as you point the script at in one run.

For the fuller picture of every write client.nodes.network exposes, beyond this script's deliberately read-only scope, see Node networking. For the full method list, see client.nodes.network in the Reference.