Initial commit : agent2_deploy - déploiement interactif via XMPP/SSH
- agent2_deploy.py : bot XMPP avec machine à états pour le déploiement guidé - deployer.py : logique SSH partagée (paramiko) - deploy.py : script CLI standalone (après git clone) - agents_catalog.json : catalogue des agents déployables - README.md : documentation complète Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Script de déploiement CLI standalone.
|
||||
Utilisable après un git clone, sans avoir besoin d'un agent XMPP actif.
|
||||
|
||||
Usage :
|
||||
python3 deploy.py # déploiement interactif via SSH
|
||||
python3 deploy.py --local <agent> # installe l'agent sur la machine locale
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import getpass
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
# Ajouter le répertoire courant au path pour importer deployer
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
from deployer import deploy_agent, load_catalog
|
||||
|
||||
COLORS = {
|
||||
"reset" : "\033[0m",
|
||||
"bold" : "\033[1m",
|
||||
"green" : "\033[32m",
|
||||
"yellow": "\033[33m",
|
||||
"red" : "\033[31m",
|
||||
"cyan" : "\033[36m",
|
||||
}
|
||||
|
||||
def c(color: str, text: str) -> str:
|
||||
return COLORS.get(color, "") + text + COLORS["reset"]
|
||||
|
||||
|
||||
def print_header():
|
||||
print(c("bold", "\n=== Agent Deploy — Déploiement interactif ===\n"))
|
||||
|
||||
|
||||
def choose_agent(catalog: dict) -> str:
|
||||
print(c("cyan", "Agents disponibles :"))
|
||||
agents = list(catalog.items())
|
||||
for i, (name, info) in enumerate(agents, 1):
|
||||
print(" {}. {} — {}".format(i, c("bold", name), info["description"]))
|
||||
print()
|
||||
|
||||
while True:
|
||||
choice = input("Choisissez un agent (numéro ou nom) : ").strip()
|
||||
if choice.isdigit():
|
||||
idx = int(choice) - 1
|
||||
if 0 <= idx < len(agents):
|
||||
return agents[idx][0]
|
||||
elif choice in catalog:
|
||||
return choice
|
||||
print(c("red", "Choix invalide, réessayez."))
|
||||
|
||||
|
||||
def collect_ssh_info() -> dict:
|
||||
print(c("cyan", "\nInformations SSH :"))
|
||||
host = input(" Adresse IP de la machine cible : ").strip()
|
||||
user = input(" Nom d'utilisateur SSH : ").strip()
|
||||
|
||||
print(" Authentification :")
|
||||
print(" 1. Mot de passe")
|
||||
print(" 2. Clé SSH")
|
||||
|
||||
while True:
|
||||
auth = input(" Choix (1/2) : ").strip()
|
||||
if auth == "1":
|
||||
password = getpass.getpass(" Mot de passe SSH : ")
|
||||
return {"host": host, "user": user, "auth": "password",
|
||||
"password": password, "key_path": None}
|
||||
elif auth == "2":
|
||||
key_path = input(" Chemin de la clé SSH [~/.ssh/id_rsa] : ").strip()
|
||||
if not key_path:
|
||||
key_path = str(Path.home() / ".ssh" / "id_rsa")
|
||||
return {"host": host, "user": user, "auth": "key",
|
||||
"password": None, "key_path": key_path}
|
||||
print(c("red", "Répondez 1 ou 2."))
|
||||
|
||||
|
||||
def collect_agent_config(agent_type: str, catalog: dict, host: str) -> dict:
|
||||
print(c("cyan", "\nConfiguration de l'agent :"))
|
||||
default_jid = "{}@xmpp.ovh".format(agent_type)
|
||||
xmpp_jid = input(" JID XMPP [{}] : ".format(default_jid)).strip() or default_jid
|
||||
xmpp_pass = getpass.getpass(" Mot de passe XMPP : ")
|
||||
mqtt_host = input(" Broker MQTT [{}] : ".format(host)).strip() or host
|
||||
return {"xmpp_jid": xmpp_jid, "xmpp_pass": xmpp_pass, "mqtt_host": mqtt_host}
|
||||
|
||||
|
||||
def show_summary(agent_type: str, ssh: dict, agent_cfg: dict, catalog: dict):
|
||||
info = catalog[agent_type]
|
||||
print(c("cyan", "\nRécapitulatif :"))
|
||||
print(" Agent : {}".format(c("bold", agent_type)))
|
||||
print(" Repo : {}".format(info["repo_url"]))
|
||||
print(" Machine : {}@{}".format(ssh["user"], ssh["host"]))
|
||||
print(" Auth SSH : {}".format("clé SSH" if ssh["auth"] == "key" else "mot de passe"))
|
||||
print(" XMPP JID : {}".format(agent_cfg["xmpp_jid"]))
|
||||
print(" MQTT broker: {}".format(agent_cfg["mqtt_host"]))
|
||||
print(" MQTT inbox : {}".format(info["mqtt_inbox"]))
|
||||
print()
|
||||
|
||||
|
||||
def deploy_local(agent_type: str, catalog: dict):
|
||||
"""
|
||||
Installe l'agent localement (sur la machine courante).
|
||||
Utile quand on clone le repo directement sur la machine cible.
|
||||
"""
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
info = catalog[agent_type]
|
||||
install_path = info["install_path"]
|
||||
repo_url = info["repo_url"]
|
||||
service_name = info["service_name"]
|
||||
main_script = info["main_script"]
|
||||
deps = info["dependencies"]
|
||||
|
||||
print(c("cyan", "\nDéploiement local de {} dans {}...".format(agent_type, install_path)))
|
||||
|
||||
def run(cmd, **kwargs):
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, **kwargs)
|
||||
if result.returncode != 0:
|
||||
print(c("red", " Erreur : {}".format(result.stderr.strip() or result.stdout.strip())))
|
||||
sys.exit(1)
|
||||
return result.stdout.strip()
|
||||
|
||||
# Prérequis
|
||||
print(" Installation des prérequis...")
|
||||
run("apt-get install -y -qq python3 python3-pip python3-venv git 2>&1")
|
||||
|
||||
# Clone
|
||||
print(" Clonage du dépôt...")
|
||||
if Path(install_path + "/.git").exists():
|
||||
run("git -C {} pull".format(install_path))
|
||||
else:
|
||||
run("git clone {} {}".format(repo_url, install_path))
|
||||
|
||||
# Venv
|
||||
print(" Création du venv...")
|
||||
run("python3 -m venv {}/venv".format(install_path))
|
||||
run("{}/venv/bin/pip install -q {}".format(install_path, " ".join(deps)))
|
||||
|
||||
# Config
|
||||
print(c("cyan", "\nConfiguration de l'agent :"))
|
||||
default_jid = "{}@xmpp.ovh".format(agent_type)
|
||||
xmpp_jid = input(" JID XMPP [{}] : ".format(default_jid)).strip() or default_jid
|
||||
xmpp_pass = getpass.getpass(" Mot de passe XMPP : ")
|
||||
mqtt_host = input(" Broker MQTT [localhost] : ").strip() or "localhost"
|
||||
|
||||
config = {
|
||||
"ollama_url" : "http://192.168.7.119:11434/api/chat",
|
||||
"model" : "qwen3:8b",
|
||||
"xmpp_jid" : xmpp_jid,
|
||||
"xmpp_pass" : xmpp_pass,
|
||||
"admin_jid" : "sylvain@xmpp.ovh",
|
||||
"db_path" : "{}/memory.db".format(install_path),
|
||||
"mqtt_host" : mqtt_host,
|
||||
"mqtt_port" : 1883,
|
||||
"mqtt_client_id": agent_type,
|
||||
"mqtt_inbox" : info["mqtt_inbox"],
|
||||
"mqtt_outbox" : info["mqtt_outbox"],
|
||||
}
|
||||
config_path = Path(install_path) / "config" / "config.json"
|
||||
config_path.write_text(json.dumps(config, indent=2, ensure_ascii=False))
|
||||
print(" config.json écrit.")
|
||||
|
||||
# Service systemd
|
||||
service = """[Unit]
|
||||
Description=Agent {name}
|
||||
After=network.target mosquitto.service
|
||||
Wants=mosquitto.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory={path}
|
||||
ExecStart=/usr/bin/python3 {path}/{script}
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
""".format(name=agent_type, path=install_path, script=main_script)
|
||||
|
||||
Path("/etc/systemd/system/{}.service".format(service_name)).write_text(service)
|
||||
run("systemctl daemon-reload && systemctl enable {s} && systemctl start {s}".format(s=service_name))
|
||||
|
||||
print(c("green", "\nDéploiement local terminé !"))
|
||||
print(" Service : systemctl status {}".format(service_name))
|
||||
print(" Logs : journalctl -u {} -f".format(service_name))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Déploiement interactif d'agents")
|
||||
parser.add_argument("--local", metavar="AGENT",
|
||||
help="Installer l'agent localement (sans SSH)")
|
||||
args = parser.parse_args()
|
||||
|
||||
print_header()
|
||||
catalog = load_catalog()
|
||||
|
||||
# Mode local
|
||||
if args.local:
|
||||
agent_type = args.local if args.local in catalog else None
|
||||
if not agent_type:
|
||||
print(c("red", "Agent inconnu : {}".format(args.local)))
|
||||
print("Agents disponibles : {}".format(", ".join(catalog.keys())))
|
||||
sys.exit(1)
|
||||
deploy_local(agent_type, catalog)
|
||||
return
|
||||
|
||||
# Mode SSH interactif
|
||||
agent_type = choose_agent(catalog)
|
||||
ssh = collect_ssh_info()
|
||||
agent_cfg = collect_agent_config(agent_type, catalog, ssh["host"])
|
||||
|
||||
show_summary(agent_type, ssh, agent_cfg, catalog)
|
||||
confirm = input("Confirmer le déploiement ? (oui/non) : ").strip().lower()
|
||||
if confirm not in ("oui", "o", "yes", "y"):
|
||||
print(c("yellow", "Déploiement annulé."))
|
||||
sys.exit(0)
|
||||
|
||||
print(c("cyan", "\nDéploiement en cours..."))
|
||||
|
||||
def progress(msg):
|
||||
print(c("yellow", " [{}]".format(msg)))
|
||||
|
||||
success, result = deploy_agent(
|
||||
host = ssh["host"],
|
||||
ssh_user = ssh["user"],
|
||||
agent_type = agent_type,
|
||||
xmpp_jid = agent_cfg["xmpp_jid"],
|
||||
xmpp_pass = agent_cfg["xmpp_pass"],
|
||||
mqtt_host = agent_cfg["mqtt_host"],
|
||||
progress_cb = progress,
|
||||
ssh_password= ssh["password"],
|
||||
ssh_key_path= ssh["key_path"],
|
||||
)
|
||||
|
||||
if success:
|
||||
print(c("green", "\n" + result))
|
||||
else:
|
||||
print(c("red", "\nÉchec : " + result))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user