0851
This commit is contained in:
@@ -216,16 +216,16 @@ async def nb_problem(log_status, msg: str):
|
|||||||
|
|
||||||
|
|
||||||
def nb_lookup_device_by_mac(mac: str, log_status) -> Tuple[
|
def nb_lookup_device_by_mac(mac: str, log_status) -> Tuple[
|
||||||
Optional[str], Optional[int], Optional[int], Optional[str], Optional[Set[str]], Optional[Any], Optional[Any], Optional[Any]
|
Optional[str], Optional[int], Optional[int], Optional[str], Optional[Set[str]], Optional[Any], Optional[Any], Optional[Any], Optional[Any]
|
||||||
]:
|
]:
|
||||||
"""
|
"""
|
||||||
Resolve MAC -> (hostname, iface_id, device_id, device_status_value, tag_slugs_set, action_next, action_last, action_next_timestamp)
|
Resolve MAC -> (hostname, iface_id, device_id, device_status_value, tag_slugs_set, action_next, action_last, action_next_timestamp, action_state)
|
||||||
- Logs problems for anomalies (mac not found, unassigned, wrong type, iface fetch fail).
|
- Logs problems for anomalies (mac not found, unassigned, wrong type, iface fetch fail).
|
||||||
- If device detail fetch fails, returns host/id with status/tags/custom_fields as None.
|
- If device detail fetch fails, returns host/id with status/tags/custom_fields as None.
|
||||||
"""
|
"""
|
||||||
mac_norm = normalize_mac(mac)
|
mac_norm = normalize_mac(mac)
|
||||||
if not mac_norm:
|
if not mac_norm:
|
||||||
return None, None, None, None, None, None, None, None
|
return None, None, None, None, None, None, None, None, None
|
||||||
|
|
||||||
base = NB_URL.rstrip("/")
|
base = NB_URL.rstrip("/")
|
||||||
h = {
|
h = {
|
||||||
@@ -238,12 +238,12 @@ def nb_lookup_device_by_mac(mac: str, log_status) -> Tuple[
|
|||||||
data, code = http_get_json(f"{base}/api/dcim/mac-addresses/", params={"mac_address": mac_norm, "limit": "2"}, headers=h)
|
data, code = http_get_json(f"{base}/api/dcim/mac-addresses/", params={"mac_address": mac_norm, "limit": "2"}, headers=h)
|
||||||
if code != 200 or not data:
|
if code != 200 or not data:
|
||||||
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: mac query http={code} mac={mac_norm}"))
|
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: mac query http={code} mac={mac_norm}"))
|
||||||
return None, None, None, None, None, None, None, None
|
return None, None, None, None, None, None, None, None, None
|
||||||
|
|
||||||
results = (data or {}).get("results") or []
|
results = (data or {}).get("results") or []
|
||||||
if not results:
|
if not results:
|
||||||
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: mac not found mac={mac_norm}"))
|
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: mac not found mac={mac_norm}"))
|
||||||
return None, None, None, None, None, None, None, None
|
return None, None, None, None, None, None, None, None, None
|
||||||
|
|
||||||
rec = results[0]
|
rec = results[0]
|
||||||
aot = (rec.get("assigned_object_type") or "").strip()
|
aot = (rec.get("assigned_object_type") or "").strip()
|
||||||
@@ -254,29 +254,29 @@ def nb_lookup_device_by_mac(mac: str, log_status) -> Tuple[
|
|||||||
|
|
||||||
if not aot or aoid is None:
|
if not aot or aoid is None:
|
||||||
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: mac unassigned mac={mac_norm}"))
|
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: mac unassigned mac={mac_norm}"))
|
||||||
return None, None, None, None, None, None, None, None
|
return None, None, None, None, None, None, None, None, None
|
||||||
if aot != "dcim.interface":
|
if aot != "dcim.interface":
|
||||||
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: mac assigned to {aot} mac={mac_norm} iface_id={aoid}"))
|
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: mac assigned to {aot} mac={mac_norm} iface_id={aoid}"))
|
||||||
return None, aoid, None, None, None, None, None, None
|
return None, aoid, None, None, None, None, None, None, None
|
||||||
|
|
||||||
# Step 2: Interface -> Device (shallow)
|
# Step 2: Interface -> Device (shallow)
|
||||||
iface, code2 = http_get_json(f"{base}/api/dcim/interfaces/{aoid}/", headers=h)
|
iface, code2 = http_get_json(f"{base}/api/dcim/interfaces/{aoid}/", headers=h)
|
||||||
if code2 != 200 or not iface:
|
if code2 != 200 or not iface:
|
||||||
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: iface fetch http={code2} iface_id={aoid}"))
|
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: iface fetch http={code2} iface_id={aoid}"))
|
||||||
return None, aoid, None, None, None, None, None, None
|
return None, aoid, None, None, None, None, None, None, None
|
||||||
|
|
||||||
dev = iface.get("device") or {}
|
dev = iface.get("device") or {}
|
||||||
host = dev.get("name") or dev.get("display")
|
host = dev.get("name") or dev.get("display")
|
||||||
dev_id = dev.get("id")
|
dev_id = dev.get("id")
|
||||||
if not host or dev_id is None:
|
if not host or dev_id is None:
|
||||||
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: iface has no device iface_id={aoid}"))
|
asyncio.create_task(nb_problem(log_status, f"[{ts()}] nb: iface has no device iface_id={aoid}"))
|
||||||
return None, aoid, None, None, None, None, None, None
|
return None, aoid, None, None, None, None, None, None, None
|
||||||
|
|
||||||
# Step 3: Device detail (single fetch for status, tags, custom_fields.*)
|
# Step 3: Device detail (single fetch for status, tags, custom_fields.*)
|
||||||
device, code3 = http_get_json(f"{base}/api/dcim/devices/{dev_id}/", headers=h)
|
device, code3 = http_get_json(f"{base}/api/dcim/devices/{dev_id}/", headers=h)
|
||||||
if code3 != 200 or not device:
|
if code3 != 200 or not device:
|
||||||
# treat as "no extra info"
|
# treat as "no extra info"
|
||||||
return host, aoid, dev_id, None, None, None, None, None
|
return host, aoid, dev_id, None, None, None, None, None, None
|
||||||
|
|
||||||
status_val = ((device.get("status") or {}).get("value")) or None
|
status_val = ((device.get("status") or {}).get("value")) or None
|
||||||
tags = device.get("tags") or []
|
tags = device.get("tags") or []
|
||||||
@@ -290,8 +290,9 @@ def nb_lookup_device_by_mac(mac: str, log_status) -> Tuple[
|
|||||||
action_next = cf.get("action_next")
|
action_next = cf.get("action_next")
|
||||||
action_last = cf.get("action_last")
|
action_last = cf.get("action_last")
|
||||||
action_next_timestamp = cf.get("action_next_timestamp")
|
action_next_timestamp = cf.get("action_next_timestamp")
|
||||||
|
action_state = cf.get("action_state")
|
||||||
|
|
||||||
return host, aoid, dev_id, (status_val if isinstance(status_val, str) else None), tag_slugs, action_next, action_last, action_next_timestamp
|
return host, aoid, dev_id, (status_val if isinstance(status_val, str) else None), tag_slugs, action_next, action_last, action_next_timestamp, action_state
|
||||||
|
|
||||||
|
|
||||||
# =========================
|
# =========================
|
||||||
@@ -372,7 +373,7 @@ async def main():
|
|||||||
if product == "fox100":
|
if product == "fox100":
|
||||||
nb_start = time.perf_counter()
|
nb_start = time.perf_counter()
|
||||||
try:
|
try:
|
||||||
host, iface_id, dev_id, status_val, tag_slugs, action_next, action_last, action_next_timestamp = nb_lookup_device_by_mac(
|
host, iface_id, dev_id, status_val, tag_slugs, action_next, action_last, action_next_timestamp, action_state = nb_lookup_device_by_mac(
|
||||||
mac=mac, log_status=log_status
|
mac=mac, log_status=log_status
|
||||||
)
|
)
|
||||||
if host:
|
if host:
|
||||||
@@ -381,6 +382,27 @@ async def main():
|
|||||||
host_suffix += f" iface_id={iface_id}"
|
host_suffix += f" iface_id={iface_id}"
|
||||||
|
|
||||||
if host:
|
if host:
|
||||||
|
# Gate on action_state: allow only "" or "ready"
|
||||||
|
action_state_str = ""
|
||||||
|
try:
|
||||||
|
if action_state is None:
|
||||||
|
action_state_str = ""
|
||||||
|
elif isinstance(action_state, str):
|
||||||
|
action_state_str = action_state.strip()
|
||||||
|
else:
|
||||||
|
action_state_str = str(action_state).strip()
|
||||||
|
except Exception:
|
||||||
|
action_state_str = ""
|
||||||
|
|
||||||
|
if action_state_str not in ("", "ready"):
|
||||||
|
async with print_lock:
|
||||||
|
print(
|
||||||
|
f"[{ts()}] device is not ready because of action_state host={host} action_state={action_state_str}",
|
||||||
|
file=sys.stdout,
|
||||||
|
flush=True,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
# Determine if action_next is present (non-empty string, or any truthy value)
|
# Determine if action_next is present (non-empty string, or any truthy value)
|
||||||
has_action_next = False
|
has_action_next = False
|
||||||
action_next_str = None
|
action_next_str = None
|
||||||
@@ -477,14 +499,14 @@ async def main():
|
|||||||
if not routed:
|
if not routed:
|
||||||
await log_status(f"[{ts()}] rmq: publish immediate routed=false host={host}")
|
await log_status(f"[{ts()}] rmq: publish immediate routed=false host={host}")
|
||||||
else:
|
else:
|
||||||
# On success: set action_last and action_next_timestamp
|
# On success: set action_last and action_next_timestamp and action_state
|
||||||
try:
|
try:
|
||||||
base = NB_URL.rstrip("/")
|
base = NB_URL.rstrip("/")
|
||||||
nb_headers = {
|
nb_headers = {
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
"Authorization": f"Token {NB_TOKEN}",
|
"Authorization": f"Token {NB_TOKEN}",
|
||||||
}
|
}
|
||||||
patch_body = {"custom_fields": {"action_last": action_next_str, "action_next_timestamp": str(now_epoch)}}
|
patch_body = {"custom_fields": {"action_last": action_next_str, "action_next_timestamp": str(now_epoch), "action_state": "started"}}
|
||||||
_, pcode = http_patch_json(
|
_, pcode = http_patch_json(
|
||||||
f"{base}/api/dcim/devices/{dev_id}/",
|
f"{base}/api/dcim/devices/{dev_id}/",
|
||||||
patch_body,
|
patch_body,
|
||||||
@@ -544,7 +566,7 @@ async def main():
|
|||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
"Authorization": f"Token {NB_TOKEN}",
|
"Authorization": f"Token {NB_TOKEN}",
|
||||||
}
|
}
|
||||||
patch_body = {"custom_fields": {"action_last": action_next_str, "action_next_timestamp": str(now_epoch)}}
|
patch_body = {"custom_fields": {"action_last": action_next_str, "action_next_timestamp": str(now_epoch), "action_state": "started"}}
|
||||||
_, pcode = http_patch_json(
|
_, pcode = http_patch_json(
|
||||||
f"{base}/api/dcim/devices/{dev_id}/",
|
f"{base}/api/dcim/devices/{dev_id}/",
|
||||||
patch_body,
|
patch_body,
|
||||||
|
|||||||
Reference in New Issue
Block a user