mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-10-26 17:41:58 -06:00
Merge pull request #67 from bunny-lab-io/codex/add-logging-for-silent-updates
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import os
|
||||
import re
|
||||
import asyncio
|
||||
import datetime
|
||||
import tempfile
|
||||
import uuid
|
||||
import time
|
||||
@@ -39,6 +40,42 @@ def _find_borealis_root() -> Optional[str]:
|
||||
return None
|
||||
|
||||
|
||||
def _agent_logs_root() -> str:
|
||||
root = _find_borealis_root() or _project_root()
|
||||
return os.path.abspath(os.path.join(root, 'Logs', 'Agent'))
|
||||
|
||||
|
||||
def _rotate_daily(path: str):
|
||||
try:
|
||||
if os.path.isfile(path):
|
||||
mtime = os.path.getmtime(path)
|
||||
dt = datetime.datetime.fromtimestamp(mtime)
|
||||
today = datetime.datetime.now().date()
|
||||
if dt.date() != today:
|
||||
base, ext = os.path.splitext(path)
|
||||
suffix = dt.strftime('%Y-%m-%d')
|
||||
newp = f"{base}.{suffix}{ext}"
|
||||
try:
|
||||
os.replace(path, newp)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _write_updater_log(message: str):
|
||||
try:
|
||||
log_dir = _agent_logs_root()
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
path = os.path.join(log_dir, 'updater.log')
|
||||
_rotate_daily(path)
|
||||
ts = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
with open(path, 'a', encoding='utf-8') as fh:
|
||||
fh.write(f'[{ts}] {message}\n')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _launch_silent_update_task():
|
||||
if os.name != 'nt':
|
||||
raise RuntimeError('Silent update is supported on Windows hosts only.')
|
||||
@@ -86,6 +123,7 @@ def _launch_silent_update_task():
|
||||
if proc.returncode != 0:
|
||||
stderr = proc.stderr or proc.stdout or 'scheduled task registration failed'
|
||||
raise RuntimeError(stderr.strip())
|
||||
return task_name
|
||||
|
||||
def _canonical_env_key(name: str) -> str:
|
||||
cleaned = re.sub(r"[^A-Za-z0-9_]", "_", (name or "").strip())
|
||||
@@ -341,7 +379,25 @@ class Role:
|
||||
return
|
||||
|
||||
loop = asyncio.get_running_loop()
|
||||
await loop.run_in_executor(None, _launch_silent_update_task)
|
||||
request_id = (details.get('request_id') or '').strip()
|
||||
req_disp = request_id or 'n/a'
|
||||
host_disp = hostname or 'unknown'
|
||||
_write_updater_log(f"Silent update request received for host '{host_disp}' (request_id={req_disp})")
|
||||
try:
|
||||
task_name = await loop.run_in_executor(None, _launch_silent_update_task)
|
||||
except Exception as exc:
|
||||
_write_updater_log(
|
||||
f"Silent update launch failed for host '{host_disp}' (request_id={req_disp}): {exc}"
|
||||
)
|
||||
try:
|
||||
details['_silent_update_error_logged'] = True
|
||||
except Exception:
|
||||
pass
|
||||
raise
|
||||
|
||||
_write_updater_log(
|
||||
f"Silent update scheduled via task '{task_name}' for host '{host_disp}' (request_id={req_disp})"
|
||||
)
|
||||
|
||||
try:
|
||||
await sio.emit(
|
||||
@@ -355,6 +411,16 @@ class Role:
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
if not isinstance(details, dict) or not details.get('_silent_update_error_logged'):
|
||||
try:
|
||||
request_id = (details.get('request_id') or '').strip()
|
||||
req_disp = request_id or 'n/a'
|
||||
host_disp = hostname or 'unknown'
|
||||
_write_updater_log(
|
||||
f"Silent update encountered error on host '{host_disp}' (request_id={req_disp}): {e}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
await sio.emit(
|
||||
'agent_silent_update_status',
|
||||
|
||||
@@ -3150,15 +3150,34 @@ def agent_silent_update():
|
||||
return jsonify({"error": "No valid hostnames provided"}), 400
|
||||
|
||||
request_id = uuid.uuid4().hex
|
||||
results: List[Dict[str, str]] = []
|
||||
now_ts = int(time.time())
|
||||
results: List[Dict[str, Any]] = []
|
||||
|
||||
# Map hostname -> connected agent_id(s) so we can target specific rooms.
|
||||
host_to_agents: Dict[str, List[str]] = {}
|
||||
for agent_id, info in (registered_agents or {}).items():
|
||||
try:
|
||||
hostname = str(info.get("hostname") or "").strip().lower()
|
||||
except Exception:
|
||||
hostname = ""
|
||||
if not hostname:
|
||||
continue
|
||||
host_to_agents.setdefault(hostname, []).append(agent_id)
|
||||
|
||||
for host in hostnames:
|
||||
payload = {
|
||||
"target_hostname": host,
|
||||
"request_id": request_id,
|
||||
"requested_at": int(time.time()),
|
||||
"requested_at": now_ts,
|
||||
}
|
||||
socketio.emit("agent_silent_update", payload)
|
||||
results.append({"hostname": host, "status": "queued"})
|
||||
target_agents = host_to_agents.get(host.strip().lower(), [])
|
||||
if target_agents:
|
||||
for agent_id in target_agents:
|
||||
socketio.emit("agent_silent_update", payload, room=agent_id)
|
||||
else:
|
||||
# Fallback broadcast for legacy agents or if hostname lookup failed.
|
||||
socketio.emit("agent_silent_update", payload)
|
||||
results.append({"hostname": host, "status": "queued", "agent_ids": target_agents})
|
||||
|
||||
_write_service_log(
|
||||
"server",
|
||||
|
||||
Reference in New Issue
Block a user