diff --git a/agent2_debian13.py b/agent2_ansible.py similarity index 97% rename from agent2_debian13.py rename to agent2_ansible.py index 865ec78..3137edb 100644 --- a/agent2_debian13.py +++ b/agent2_ansible.py @@ -10,11 +10,11 @@ from pathlib import Path from slixmpp import ClientXMPP import paho.mqtt.client as mqtt -sys.path.insert(0, "/opt/agent2_debian13") +sys.path.insert(0, "/opt/agent2_ansible") from skills.loader import load_skills, run_skills # ── CONFIG ─────────────────────────────────────────────────────────────── -CONFIG_DIR = Path("/opt/agent2_debian13/config") +CONFIG_DIR = Path("/opt/agent2_ansible/config") CONFIG_FILE = CONFIG_DIR / "config.json" PROMPT_FILE = CONFIG_DIR / "system_prompt.txt" @@ -125,7 +125,7 @@ class AgentBot(ClientXMPP): self.send_presence() await self.get_roster() self.send_message(mto=ADMIN_JID, - mbody="Agent2_Debian13 en ligne !", + mbody="Agent2_Ansible en ligne !", mtype='chat') async def message(self, msg): diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg new file mode 100644 index 0000000..fe9173c --- /dev/null +++ b/ansible/ansible.cfg @@ -0,0 +1,11 @@ +[defaults] +inventory = /opt/agent2_ansible/ansible/inventory +roles_path = /opt/agent2_ansible/ansible/roles +host_key_checking = False +retry_files_enabled = False +stdout_callback = yaml +timeout = 60 + +[privilege_escalation] +become = False +become_method = sudo diff --git a/ansible/inventory/hosts b/ansible/inventory/hosts new file mode 100644 index 0000000..9f979f0 --- /dev/null +++ b/ansible/inventory/hosts @@ -0,0 +1,15 @@ +# Inventaire Ansible - Réseau local +# Ajouter les hôtes selon l'infrastructure réelle + +[local] +localhost ansible_connection=local + +[servers] +# exemple : srv1 ansible_host=192.168.7.10 ansible_user=root +# exemple : srv2 ansible_host=192.168.7.11 ansible_user=debian + +[workstations] +# exemple : pc1 ansible_host=192.168.7.50 ansible_user=sylvain + +[all:vars] +ansible_python_interpreter=/usr/bin/python3 diff --git a/config/system_prompt.txt b/config/system_prompt.txt index e3b962e..f821a4b 100644 --- a/config/system_prompt.txt +++ b/config/system_prompt.txt @@ -1,35 +1,52 @@ -Tu es agent2_debian13, un agent autonome spécialisé dans l'administration de systèmes Debian. +Tu es agent2_ansible, un agent autonome spécialisé dans l'automatisation et l'orchestration d'infrastructure via Ansible. Tu travailles sous les ordres d'agent1 qui te délègue des tâches via MQTT. -Tu as accès au système réel et peux exécuter des commandes shell. +Tu gères les machines du réseau local via Ansible (playbooks, modules ad-hoc, inventaire). Tes domaines de compétence : -- Gestion des paquets : apt, dpkg, snap -- Services systemd : start, stop, enable, status, journalctl -- Conteneurs : LXC, LXD, Docker sur Debian -- Machines virtuelles : KVM/QEMU, libvirt -- Réseau Debian : interfaces, /etc/network, NetworkManager -- Sécurité : ufw, fail2ban, SSH, sudoers -- Fichiers de config système : fstab, crontab, hosts -- Surveillance : top, htop, df, du, netstat, ss +- Exécution de playbooks Ansible sur les hôtes du réseau local +- Commandes ad-hoc Ansible (ping, shell, copy, apt, service, etc.) +- Gestion de l'inventaire Ansible +- Déploiement de configurations sur plusieurs machines simultanément +- Installation et gestion de paquets à distance +- Gestion de services systemd à distance +- Copie et gestion de fichiers sur les hôtes distants +- Collecte de facts (informations système) sur les hôtes Formats de commandes disponibles : -EXEC: - → Exécuter une commande sur le système et récupérer le résultat +ANSIBLE: + → Exécute une commande ansible (ad-hoc) sur les hôtes → Exemples : - EXEC: systemctl status nginx - EXEC: apt install -y curl - EXEC: docker ps -a - EXEC: journalctl -u ssh --no-pager -n 20 + ANSIBLE: all -m ping + ANSIBLE: all -m shell -a "uptime" + ANSIBLE: webservers -m apt -a "name=nginx state=present" --become + ANSIBLE: all -m gather_facts --tree /tmp/facts + ANSIBLE: all -m service -a "name=nginx state=restarted" --become + +PLAYBOOK: [options] + → Exécute un playbook Ansible + → Les playbooks sont dans /opt/agent2_ansible/ansible/playbooks/ + → Exemples : + PLAYBOOK: site.yml + PLAYBOOK: deploy.yml --limit webservers + PLAYBOOK: update.yml --tags packages + PLAYBOOK: hardening.yml --check + +EXEC: + → Exécuter une commande locale (utile pour inspecter l'inventaire, les logs, etc.) + → Exemples : + EXEC: ansible-inventory --list + EXEC: cat /opt/agent2_ansible/ansible/inventory/hosts + EXEC: ls /opt/agent2_ansible/ansible/playbooks/ SEARCH: - → Recherche web si besoin de documentation + → Recherche web si besoin de documentation Ansible READ: → Lire une page de documentation REMEMBER: | - → Mémoriser une information + → Mémoriser une information (ex: IP d'un hôte, configuration) RECALL: → Récupérer une information mémorisée @@ -37,9 +54,10 @@ RECALL: ⚠ RÈGLES : - Tu reçois des tâches d'agent1 via MQTT et tu lui réponds via MQTT - Tu peux aussi recevoir des instructions directement de sylvain via XMPP -- Pour toute tâche système : utilise EXEC: pour l'exécuter réellement, pas juste expliquer -- Enchaîne plusieurs EXEC: si nécessaire pour accomplir une tâche complexe +- Pour toute tâche Ansible : utilise ANSIBLE: ou PLAYBOOK: pour l'exécuter réellement +- Enchaîne plusieurs commandes si nécessaire pour accomplir une tâche complexe - Vérifie toujours le résultat d'une commande avant de passer à la suivante - Si une commande échoue, analyse l'erreur et adapte-toi -- Signale à agent1 si une tâche est hors de ton domaine Debian +- Signale à agent1 si une tâche est hors de ton domaine Ansible - Réponds toujours en français avec un résumé clair de ce qui a été fait +- Pour les opérations risquées (suppression, modification critique), utilise --check d'abord diff --git a/skills/ansible_exec.py b/skills/ansible_exec.py new file mode 100644 index 0000000..62f3602 --- /dev/null +++ b/skills/ansible_exec.py @@ -0,0 +1,107 @@ +""" +Skill : ANSIBLE / PLAYBOOK +Exécute des commandes Ansible (ad-hoc ou playbooks) sur les hôtes du réseau local. + +Commandes : + ANSIBLE: + → Exécute ansible avec les arguments fournis + → Exemples : + ANSIBLE: all -m ping + ANSIBLE: all -m shell -a "uptime" + ANSIBLE: webservers -m apt -a "name=nginx state=present" --become + + PLAYBOOK: [options] + → Exécute ansible-playbook depuis le dossier playbooks/ + → Exemples : + PLAYBOOK: site.yml + PLAYBOOK: deploy.yml --limit webservers --tags packages +""" +import subprocess +from pathlib import Path + +SKILL_NAME = "ansible_exec" +TRIGGER = None +TRIGGERS = { + "ANSIBLE:": "ansible_adhoc", + "PLAYBOOK:": "ansible_playbook", +} + +ANSIBLE_DIR = Path("/opt/agent2_ansible/ansible") +PLAYBOOKS_DIR = ANSIBLE_DIR / "playbooks" +ANSIBLE_CFG = ANSIBLE_DIR / "ansible.cfg" + +TIMEOUT = 120 # secondes (les commandes réseau peuvent être longues) +MAX_CHARS = 5000 + +# Variables d'environnement pour ansible +import os +ANSIBLE_ENV = { + **os.environ, + "ANSIBLE_CONFIG": str(ANSIBLE_CFG), + "ANSIBLE_FORCE_COLOR": "0", + "ANSIBLE_NOCOLOR": "1", +} + + +def _run(cmd: list) -> str: + try: + result = subprocess.run( + cmd, + capture_output=True, + text=True, + timeout=TIMEOUT, + env=ANSIBLE_ENV, + cwd=str(ANSIBLE_DIR), + ) + stdout = result.stdout.strip() + stderr = result.stderr.strip() + + output = "" + if stdout: + output += stdout + if stderr: + output += ("\n[stderr] " + stderr) if output else "[stderr] " + stderr + if not output: + output = "(aucune sortie)" + + if len(output) > MAX_CHARS: + output = output[:MAX_CHARS] + "\n...[tronqué]" + + status = "OK" if result.returncode == 0 else "Erreur (code {})".format(result.returncode) + return "[{}] $ {}\n{}".format(status, " ".join(cmd), output) + + except subprocess.TimeoutExpired: + return "Timeout : la commande a dépassé {}s.".format(TIMEOUT) + except FileNotFoundError: + return "Erreur : ansible n'est pas installé ou introuvable dans le PATH." + except Exception as e: + return "Erreur d'exécution : {}".format(e) + + +def ansible_adhoc(args: str) -> str: + args = args.strip() + if not args: + return "Erreur : arguments vides. Exemple : ANSIBLE: all -m ping" + cmd = ["ansible"] + args.split() + return _run(cmd) + + +def ansible_playbook(args: str) -> str: + args = args.strip() + if not args: + return "Erreur : playbook non spécifié. Exemple : PLAYBOOK: site.yml" + + parts = args.split() + playbook = parts[0] + options = parts[1:] + + # Chemin absolu si non fourni + playbook_path = Path(playbook) + if not playbook_path.is_absolute(): + playbook_path = PLAYBOOKS_DIR / playbook + + if not playbook_path.exists(): + return "Erreur : playbook introuvable : {}".format(playbook_path) + + cmd = ["ansible-playbook", str(playbook_path)] + options + return _run(cmd) diff --git a/skills/memory.py b/skills/memory.py index 3f06e54..c9b6945 100644 --- a/skills/memory.py +++ b/skills/memory.py @@ -9,7 +9,7 @@ SKILL_NAME = "memory" TRIGGER = None TRIGGERS = {"REMEMBER:": "remember", "RECALL:": "recall"} -DB_PATH = Path("/opt/agent2_debian13/memory.db") +DB_PATH = Path("/opt/agent2_ansible/memory.db") def _get_conn(): conn = sqlite3.connect(DB_PATH) diff --git a/skills/prompt_memory.py b/skills/prompt_memory.py index 5f67d8c..89e21ce 100644 --- a/skills/prompt_memory.py +++ b/skills/prompt_memory.py @@ -23,7 +23,7 @@ TRIGGERS = { "PROMPT_DEL:": "prompt_del", } -DB_PATH = Path("/opt/agent2_debian13/chroma_db") +DB_PATH = Path("/opt/agent2_ansible/chroma_db") # Phase 1 : embedding factice (hash MD5 → vecteur 16 dims) # Phase 2 : remplacer par un vrai modèle (ex: sentence-transformers)