Batch 1 : configs plages horaires + blackout + check disponibilité dans delegate
- config/blackout_hours.json : plage maintenance nuit (02:00-05:00) - config/reports_schedule.json : horaires sollicitation rapports par agent - config/tasks_schedule.json : tâches planifiées (vide pour l'instant) - agents_registry.json : ajout work_hours par agent - delegate.py : vérification blackout + work_hours avant délégation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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é"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"start": "02:00",
|
||||
"end": "05:00",
|
||||
"label": "maintenance nuit",
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"tasks": []
|
||||
}
|
||||
+60
-7
@@ -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: <agent_name> | <tâche>
|
||||
@@ -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":
|
||||
|
||||
Reference in New Issue
Block a user