This commit is contained in:
2025-11-04 17:27:49 +02:00
parent 83b882e670
commit 3261be9bda

View File

@@ -4,23 +4,22 @@ nb_sync_one_device.py
Update a single NetBox device from Cloud by hostname — ONLY if device is online.
Flow
1) Input: hostname (positional arg).
2) NetBox: lookup device by name -> read custom_fields.cloud_id.
3) Cloud: GET /v1/devices/{cloud_id}/liveness; if isConnected == false -> exit with "device is not online".
4) Cloud: GET /v1/devices/{cloud_id} -> read firmwareVersion, ipAddress, nodeName, sectorName, smallCellName, serialNumber.
5) NetBox updates (idempotent by default):
- custom_fields.fw_version ← firmwareVersion
- custom_fields.nodeName ← nodeName
- custom_fields.sectorName ← sectorName
- custom_fields.smallCellName ← smallCellName
- serial ← serialNumber
6) IP handling (only when online):
- If Cloud ipAddress is valid (not None/""/"0.0.0.0"):
ensure eth0, ensure/create IP, MOVE if needed, set primary_ip4,
then PRUNE all other IPs on the device (default behavior).
- If Cloud ipAddress is placeholder/invalid: skip IP changes and keep NetBox IPs as-is.
7) Single-line OK/FAIL and proper exit code.
Behavior
- Liveness gate via /v1/devices/{cloud_id}/liveness (no changes if offline).
- Updates NetBox fields from Cloud:
custom_fields.fw_version ← firmwareVersion
custom_fields.nodeName ← nodeName
custom_fields.sectorName ← sectorName
custom_fields.smallCellName ← smallCellName
serial ← serialNumber
- IP handling:
If Cloud ipAddress is valid (not None/""/"0.0.0.0"):
ensure eth0, ensure/create IP, MOVE from other device if needed, set primary_ip4,
then PRUNE all other IPs on this device (default).
If Cloud ipAddress is placeholder/invalid:
skip IP changes and do not prune.
- --chatty logs step-by-step to stderr; stdout remains one-line OK/FAIL.
- NEW: Logs custom field cf_upgrade_cmd as `NB: upgrade_cmd=<value>` when --chatty.
Exit codes:
0 = success
@@ -185,12 +184,10 @@ class NetBox:
# --- Helpers for pruning ---
def list_device_ips(self, dev_id: int) -> List[Dict]:
# Try device_id filter (NetBox ≥3.6); otherwise fall back via interface scan
r = S_NB.get(self._url("/api/ipam/ip-addresses/"), headers=self._h(),
params={"device_id": dev_id, "limit": 1000}, timeout=REQ_TIMEOUT)
if r.status_code == 200:
return r.json().get("results") or []
# Fallback path
ips: List[Dict] = []
r2 = S_NB.get(self._url("/api/dcim/interfaces/"), headers=self._h(),
params={"device_id": dev_id, "limit": 1000}, timeout=REQ_TIMEOUT)
@@ -254,6 +251,11 @@ def run(hostname: str) -> None:
dev_id = dev["id"]
dev_full = nb.get_device(dev_id)
cf = dev_full.get("custom_fields") or {}
# NEW: log upgrade command if present
upgrade_cmd = cf.get("cf_upgrade_cmd")
log(f"NB: upgrade_cmd={upgrade_cmd}")
cloud_id = cf.get("cloud_id")
if cloud_id in (None, "", "null"):
die(1, f"FAIL {hostname} has no custom_fields.cloud_id in NetBox")