Support agents distants (SSH) pour !agentUPDATE/UPGRADE

- agent_update.py : _run_ssh() via sshpass, dispatche local ou SSH selon ssh_host
- agent1.py : _get_agent_git_info() transmet ssh_host/ssh_user depuis le registre
- agents_registry.json : agent2_test → ssh_host: 192.168.7.13, ssh_user: root

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 16:11:43 +00:00
parent 1565b145dc
commit 0fe1ece68d
3 changed files with 144 additions and 38 deletions
+87 -29
View File
@@ -3,16 +3,18 @@ Utilitaire : vérification et application des mises à jour git pour les agents.
Fonctions appelées directement depuis agent1.py (pas de trigger LLM).
check_update(name, install_path, branch) → rapport git fetch
do_upgrade(name, install_path, service, branch) → git pull + systemctl restart
check_update(name, install_path, branch, ssh_host, ssh_user)
do_upgrade(name, install_path, service, branch, ssh_host, ssh_user, self_upgrade)
Si ssh_host est défini, les commandes sont exécutées via SSH sur la machine distante.
"""
import subprocess
import shlex
from pathlib import Path
def _run(cmd: str, cwd: str = None, timeout: int = 30) -> tuple:
"""Lance une commande shell, retourne (stdout, stderr, returncode)."""
def _run_local(cmd: str, cwd: str = None, timeout: int = 30) -> tuple:
"""Lance une commande locale, retourne (stdout, stderr, returncode)."""
try:
result = subprocess.run(
shlex.split(cmd),
@@ -28,32 +30,74 @@ def _run(cmd: str, cwd: str = None, timeout: int = 30) -> tuple:
return "", str(e), -1
def check_update(agent_name: str, install_path: str, branch: str = "main") -> str:
def _run_ssh(cmd: str, ssh_host: str, ssh_user: str = "root",
cwd: str = None, timeout: int = 30) -> tuple:
"""Lance une commande via SSH, retourne (stdout, stderr, returncode)."""
if cwd:
cmd = "cd {} && {}".format(cwd, cmd)
ssh_cmd = "sshpass -p 'Matador3721' ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 {}@{} {}".format(
ssh_user, ssh_host, shlex.quote(cmd))
try:
result = subprocess.run(
shlex.split(ssh_cmd),
capture_output=True,
text=True,
timeout=timeout
)
return result.stdout.strip(), result.stderr.strip(), result.returncode
except subprocess.TimeoutExpired:
return "", "Timeout SSH ({} s)".format(timeout), -1
except Exception as e:
return "", str(e), -1
def _run(cmd: str, cwd: str = None, timeout: int = 30,
ssh_host: str = None, ssh_user: str = "root") -> tuple:
"""Dispatche local ou SSH selon ssh_host."""
if ssh_host:
return _run_ssh(cmd, ssh_host, ssh_user, cwd=cwd, timeout=timeout)
return _run_local(cmd, cwd=cwd, timeout=timeout)
def check_update(agent_name: str, install_path: str, branch: str = "main",
ssh_host: str = None, ssh_user: str = "root") -> str:
"""
Vérifie si une mise à jour est disponible sur le dépôt distant.
Effectue un git fetch puis compare HEAD avec origin/<branch>.
"""
path = install_path
remote = " [{}]".format(ssh_host) if ssh_host else ""
if not Path(path).is_dir():
return "[{}] Répertoire introuvable : {}".format(agent_name, path)
# Vérifier que le répertoire existe
if ssh_host:
out, _, rc = _run("test -d {}".format(install_path),
ssh_host=ssh_host, ssh_user=ssh_user)
if rc != 0:
return "[{}{}] Répertoire introuvable : {}".format(
agent_name, remote, install_path)
else:
if not Path(install_path).is_dir():
return "[{}] Répertoire introuvable : {}".format(agent_name, install_path)
# git fetch
out, err, rc = _run("git fetch origin {}".format(branch), cwd=path, timeout=20)
out, err, rc = _run("git fetch origin {}".format(branch),
cwd=install_path, timeout=20,
ssh_host=ssh_host, ssh_user=ssh_user)
if rc != 0:
return "[{}] Erreur git fetch : {}".format(agent_name, err or out)
return "[{}{}] Erreur git fetch : {}".format(agent_name, remote, err or out)
# Compter les commits disponibles
out, err, rc = _run(
"git log HEAD..origin/{} --oneline".format(branch), cwd=path)
"git log HEAD..origin/{} --oneline".format(branch),
cwd=install_path, ssh_host=ssh_host, ssh_user=ssh_user)
if rc != 0:
return "[{}] Erreur git log : {}".format(agent_name, err or out)
return "[{}{}] Erreur git log : {}".format(agent_name, remote, err or out)
commits = [l for l in out.splitlines() if l.strip()]
if not commits:
return "[{}] Déjà à jour.".format(agent_name)
return "[{}{}] Déjà à jour.".format(agent_name, remote)
lines = ["[{}] {} commit(s) disponible(s) :".format(agent_name, len(commits))]
lines = ["[{}{}] {} commit(s) disponible(s) :".format(
agent_name, remote, len(commits))]
for c in commits[:10]:
lines.append(" {}".format(c))
if len(commits) > 10:
@@ -64,38 +108,52 @@ def check_update(agent_name: str, install_path: str, branch: str = "main") -> st
def do_upgrade(agent_name: str, install_path: str,
service_name: str, branch: str = "main",
self_upgrade: bool = False) -> str:
self_upgrade: bool = False,
ssh_host: str = None, ssh_user: str = "root") -> str:
"""
Applique la mise à jour : git pull + systemctl restart.
Pour agent1 (self_upgrade=True), envoie la réponse AVANT le restart.
Pour agent1 (self_upgrade=True), retourne le message AVANT le restart.
"""
path = install_path
remote = " [{}]".format(ssh_host) if ssh_host else ""
if not Path(path).is_dir():
return "[{}] Répertoire introuvable : {}".format(agent_name, path)
# Vérifier que le répertoire existe
if ssh_host:
_, _, rc = _run("test -d {}".format(install_path),
ssh_host=ssh_host, ssh_user=ssh_user)
if rc != 0:
return "[{}{}] Répertoire introuvable : {}".format(
agent_name, remote, install_path)
else:
if not Path(install_path).is_dir():
return "[{}] Répertoire introuvable : {}".format(agent_name, install_path)
# git pull
out, err, rc = _run(
"git pull origin {}".format(branch), cwd=path, timeout=60)
"git pull origin {}".format(branch),
cwd=install_path, timeout=60,
ssh_host=ssh_host, ssh_user=ssh_user)
if rc != 0:
return "[{}] Erreur git pull : {}".format(agent_name, err or out)
return "[{}{}] Erreur git pull : {}".format(agent_name, remote, err or out)
pull_msg = out or "Déjà à jour."
if self_upgrade:
# On retourne le message AVANT le restart (agent1 l'envoie puis se redémarre)
return "[{}] {} \nRedémarrage en cours...".format(agent_name, pull_msg)
return "[{}] {}\nRedémarrage en cours...".format(agent_name, pull_msg)
# systemctl restart
_, err, rc = _run("systemctl restart {}".format(service_name), timeout=15)
_, err, rc = _run("systemctl restart {}".format(service_name),
timeout=15, ssh_host=ssh_host, ssh_user=ssh_user)
if rc != 0:
return "[{}] Pull OK mais restart échoué : {}".format(agent_name, err)
return "[{}{}] Pull OK mais restart échoué : {}".format(
agent_name, remote, err)
# Vérifier que le service est bien remonté
out, _, _ = _run("systemctl is-active {}".format(service_name), timeout=5)
out, _, _ = _run("systemctl is-active {}".format(service_name),
timeout=5, ssh_host=ssh_host, ssh_user=ssh_user)
state = out.strip()
if state == "active":
return "[{}] Mise à jour appliquée. Service actif.\n{}".format(agent_name, pull_msg)
return "[{}{}] Mise à jour appliquée. Service actif.\n{}".format(
agent_name, remote, pull_msg)
else:
return "[{}] Pull OK, service état : {}. Vérifiez les logs.".format(
agent_name, state)
return "[{}{}] Pull OK, service état : {}. Vérifiez les logs.".format(
agent_name, remote, state)