diff --git a/config/agents_registry.json b/config/agents_registry.json index ae2dd14..ab84254 100644 --- a/config/agents_registry.json +++ b/config/agents_registry.json @@ -3,18 +3,27 @@ "jid": "agent2_debian13@xmpp.ovh", "mqtt_inbox": "agents/agent2_debian13/inbox", "mqtt_outbox": "agents/agent1/inbox", - "speciality": "Administration Debian : apt, dpkg, systemd, conteneurs LXC/Docker, KVM, réseau, sécurité système" + "speciality": "Administration Debian : apt, dpkg, systemd, conteneurs LXC/Docker, KVM, réseau, sécurité système", + "work_hours": { "start": "07:00", "end": "23:00", "days": ["mon","tue","wed","thu","fri","sat","sun"], "enabled": true } }, "agent2_ansible": { "jid": "agent2_ansible@xmpp.ovh", "mqtt_inbox": "agents/agent2_ansible/inbox", "mqtt_outbox": "agents/agent1/inbox", - "speciality": "Automatisation infrastructure via Ansible : playbooks, commandes ad-hoc, déploiement multi-hôtes, gestion de configuration sur le réseau local" + "speciality": "Automatisation infrastructure via Ansible : playbooks, commandes ad-hoc, déploiement multi-hôtes, gestion de configuration sur le réseau local", + "work_hours": { "start": "07:00", "end": "23:00", "days": ["mon","tue","wed","thu","fri","sat","sun"], "enabled": true } }, "agent2_deploy": { "jid": "agent2_deploy@xmpp.ovh", "mqtt_inbox": "agents/agent2_deploy/inbox", "mqtt_outbox": "agents/agent1/inbox", - "speciality": "Déploiement d'agents : installe et configure d'autres agents sur des machines distantes ou locales via SSH" + "speciality": "Déploiement d'agents : installe et configure d'autres agents sur des machines distantes ou locales via SSH", + "work_hours": { "start": "08:00", "end": "20:00", "days": ["mon","tue","wed","thu","fri"], "enabled": true } + }, + "agent2_test": { + "jid": "agent2_test@xmpp.ovh", + "mqtt_inbox": "agents/agent2_test/inbox", + "mqtt_outbox": "agents/agent1/inbox", + "speciality": "Spécialiste Debian : apt, systemd, conteneurs LXC/Docker, KVM, réseau, sécurité" } -} \ No newline at end of file +} diff --git a/config/blackout_hours.json b/config/blackout_hours.json new file mode 100644 index 0000000..223c00e --- /dev/null +++ b/config/blackout_hours.json @@ -0,0 +1,8 @@ +[ + { + "start": "02:00", + "end": "05:00", + "label": "maintenance nuit", + "enabled": true + } +] diff --git a/config/reports_schedule.json b/config/reports_schedule.json new file mode 100644 index 0000000..4cb8288 --- /dev/null +++ b/config/reports_schedule.json @@ -0,0 +1,18 @@ +{ + "agents": { + "agent2_debian13": { + "report_time": "22:00", + "enabled": true + }, + "agent2_ansible": { + "report_time": "22:05", + "enabled": true + }, + "agent2_deploy": { + "report_time": "22:10", + "enabled": true + } + }, + "daily_report_time": "22:30", + "daily_report_enabled": true +} diff --git a/config/tasks_schedule.json b/config/tasks_schedule.json new file mode 100644 index 0000000..50ffbb9 --- /dev/null +++ b/config/tasks_schedule.json @@ -0,0 +1,3 @@ +{ + "tasks": [] +} diff --git a/skills/delegate.py b/skills/delegate.py index e0741ce..ceda4a9 100644 --- a/skills/delegate.py +++ b/skills/delegate.py @@ -2,6 +2,7 @@ Skill : DELEGATE Délègue une tâche à un agent spécialisé via MQTT et attend sa réponse. Log le résultat et notifie agent1 en cas d'erreur. +Vérifie les plages horaires (work_hours) et le blackout avant délégation. Commande : DELEGATE: | @@ -9,17 +10,18 @@ Commande : import json import time import threading +from datetime import datetime from pathlib import Path import paho.mqtt.client as mqtt SKILL_NAME = "delegate" TRIGGER = "DELEGATE:" -CONFIG_FILE = Path("/opt/agent/config/config.json") -REGISTRY_FILE = Path("/opt/agent/config/agents_registry.json") -TIMEOUT = 120 +CONFIG_FILE = Path("/opt/agent/config/config.json") +REGISTRY_FILE = Path("/opt/agent/config/agents_registry.json") +BLACKOUT_FILE = Path("/opt/agent/config/blackout_hours.json") +TIMEOUT = 120 -# Mots-clés indiquant une erreur dans la réponse d'un agent ERROR_KEYWORDS = ["erreur", "error", "timeout", "échec", "failed", "cannot", "permission denied", "command not found", "no such file", "connexion refusée"] @@ -34,6 +36,51 @@ def _is_error(result: str) -> bool: return True return any(kw in lower for kw in ERROR_KEYWORDS) +def _check_availability(agent_name: str, registry: dict) -> tuple: + """Retourne (disponible: bool, message: str).""" + now = datetime.now() + + # Vérifier blackout_hours.json + try: + blackouts = json.loads(BLACKOUT_FILE.read_text()) + for b in blackouts: + if not b.get("enabled", True): + continue + start = datetime.strptime(b["start"], "%H:%M").replace( + year=now.year, month=now.month, day=now.day) + end = datetime.strptime(b["end"], "%H:%M").replace( + year=now.year, month=now.month, day=now.day) + if start <= now <= end: + return False, "Blackout actif ({}) : {}-{}. Tâche non exécutée.".format( + b.get("label", "maintenance"), b["start"], b["end"]) + except Exception: + pass + + # Vérifier work_hours de l'agent + agent = registry.get(agent_name, {}) + wh = agent.get("work_hours") + if wh and wh.get("enabled", True): + start_str = wh.get("start", "00:00") + end_str = wh.get("end", "23:59") + start = datetime.strptime(start_str, "%H:%M").replace( + year=now.year, month=now.month, day=now.day) + end = datetime.strptime(end_str, "%H:%M").replace( + year=now.year, month=now.month, day=now.day) + + # Vérifier le jour + days = wh.get("days") + if days: + day_map = ["mon","tue","wed","thu","fri","sat","sun"] + if day_map[now.weekday()] not in days: + return False, "Agent {} ne travaille pas ce jour. Jours actifs : {}.".format( + agent_name, ", ".join(days)) + + if not (start <= now <= end): + return False, "Agent {} hors plage horaire ({}-{}).".format( + agent_name, start_str, end_str) + + return True, "" + def _notify_error(host: str, port: int, agent: str, task: str, result: str): """Publie l'erreur sur le topic d'erreurs pour que agent1 notifie l'utilisateur.""" try: @@ -62,6 +109,12 @@ def execute(args: str) -> str: available = ", ".join(registry.keys()) return "Agent inconnu : «{}». Agents disponibles : {}".format(agent_name, available) + # Vérifier disponibilité (plages horaires + blackout) + available, reason = _check_availability(agent_name, registry) + if not available: + print("[DELEGATE] {} indisponible : {}".format(agent_name, reason)) + return "[INDISPONIBLE] {} : {}".format(agent_name, reason) + agent = registry[agent_name] inbox = agent["mqtt_inbox"] outbox = agent["mqtt_outbox"] @@ -88,7 +141,7 @@ def execute(args: str) -> str: print("[DELEGATE] Tâche envoyée à {} : {}".format(agent_name, task[:80])) - start = time.time() + start = time.time() received = response_received.wait(timeout=TIMEOUT) duration = time.time() - start @@ -98,8 +151,8 @@ def execute(args: str) -> str: from skills.reporting import log_execution if received and response_container: - result = response_container[0] - status = "error" if _is_error(result) else "success" + result = response_container[0] + status = "error" if _is_error(result) else "success" log_execution("delegate", agent_name, task, status, result, duration) if status == "error":