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