mirror of
https://github.com/myronblair/jarvis
synced 2026-06-30 17:50:23 -05:00
90e4ded7c9
- ha-poller: replace recursive main() retry with while loop (stack overflow fix) - ha-poller: advance last_push on empty HA response (log spam fix) - ha-poller: use datetime.now(timezone.utc) instead of deprecated utcnow() - ping-probe: always call update_status() unconditionally so offline devices register as offline - agent.php: heartbeat reads status from payload instead of hardcoding 'online' - phone-probe: delegate JSON building to python3 (bash concatenation injection fix) - netscan + phone-probe: read registration key from /etc/jarvis-agent/reg-key - admin/index.php: sync ha_list skipDomains with ha.php (14 missing domains added) - facts_collector: self-check JARVIS via 127.0.0.1 instead of Cloudflare hairpin
100 lines
2.9 KiB
Python
100 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
JARVIS Ping Probe — runs on PVE1 (10.48.200.90), which is on the LAN.
|
|
Pings devices that can't run the full agent, then calls JARVIS heartbeat
|
|
on their behalf so the dashboard shows live status.
|
|
"""
|
|
|
|
import json
|
|
import subprocess
|
|
import urllib.request
|
|
import urllib.error
|
|
import ssl
|
|
|
|
JARVIS_URL = "http://10.48.200.211"
|
|
HOST_HEADER = "jarvis.orbishosting.com"
|
|
|
|
# Devices to probe: agent_id → api_key
|
|
DEVICES = {
|
|
"fortigate_gw": "00103aea6fcbf837bc55e11b445a3620",
|
|
"yealink_t48s": "2bf8bd7ca8dd31c28fd16aa956e15f88",
|
|
"homeassistant_ha": "6f8077dee7a7b4af202bc80886f1223d",
|
|
}
|
|
|
|
# Map agent_id → IP (for ping)
|
|
IPS = {
|
|
"fortigate_gw": "10.48.200.1",
|
|
"yealink_t48s": "10.48.200.43",
|
|
"homeassistant_ha": "10.48.200.97",
|
|
}
|
|
|
|
def ping(ip: str) -> bool:
|
|
result = subprocess.run(
|
|
["ping", "-c", "1", "-W", "2", ip],
|
|
capture_output=True, timeout=5
|
|
)
|
|
return result.returncode == 0
|
|
|
|
def heartbeat(agent_id: str, api_key: str, alive: bool):
|
|
# If device is down we still send heartbeat so JARVIS updates last_seen
|
|
# and sets status based on the alive flag via the metric payload
|
|
ctx = ssl.create_default_context()
|
|
ctx.check_hostname = False
|
|
ctx.verify_mode = ssl.CERT_NONE
|
|
|
|
payload = json.dumps({}).encode()
|
|
req = urllib.request.Request(
|
|
f"{JARVIS_URL}/api/agent/heartbeat",
|
|
data=payload, method="POST"
|
|
)
|
|
req.add_header("Content-Type", "application/json")
|
|
req.add_header("X-Agent-Key", api_key)
|
|
req.add_header("Host", HOST_HEADER)
|
|
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=10, context=ctx):
|
|
pass
|
|
except Exception:
|
|
pass
|
|
|
|
def update_status(agent_id: str, api_key: str, status: str):
|
|
"""Push a minimal metric so JARVIS knows if device is up or down."""
|
|
ctx = ssl.create_default_context()
|
|
ctx.check_hostname = False
|
|
ctx.verify_mode = ssl.CERT_NONE
|
|
|
|
payload = json.dumps({
|
|
"type": "system",
|
|
"data": {
|
|
"hostname": agent_id,
|
|
"cpu_percent": 0,
|
|
"ping_only": True,
|
|
"ping_status": status,
|
|
}
|
|
}).encode()
|
|
req = urllib.request.Request(
|
|
f"{JARVIS_URL}/api/agent/metrics",
|
|
data=payload, method="POST"
|
|
)
|
|
req.add_header("Content-Type", "application/json")
|
|
req.add_header("X-Agent-Key", api_key)
|
|
req.add_header("Host", HOST_HEADER)
|
|
|
|
try:
|
|
with urllib.request.urlopen(req, timeout=10, context=ctx):
|
|
pass
|
|
except Exception:
|
|
pass
|
|
|
|
def main():
|
|
for agent_id, api_key in DEVICES.items():
|
|
ip = IPS.get(agent_id, "")
|
|
alive = ping(ip) if ip else False
|
|
status = "online" if alive else "offline"
|
|
print(f"{agent_id} ({ip}): {status}", flush=True)
|
|
heartbeat(agent_id, api_key, alive)
|
|
update_status(agent_id, api_key, status)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|