first commit

This commit is contained in:
Bruno Charest 2025-12-04 09:04:42 -05:00
commit 21bee026ff
29 changed files with 14170 additions and 0 deletions

65
.dockerignore Normal file
View File

@ -0,0 +1,65 @@
# Git
.git
.gitignore
# Python
__pycache__
*.pyc
*.pyo
*.pyd
.Python
*.so
.eggs
*.egg-info
.pytest_cache
.mypy_cache
.tox
.nox
.coverage
htmlcov
.hypothesis
# Virtual environments
venv
.venv
env
ENV
# IDE
.idea
.vscode
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Docker
Dockerfile
docker-compose.yml
.dockerignore
# Documentation
*.md
!README.md
# Logs
*.log
logs/
# Environment files (contient des secrets)
.env
.env.*
!.env.example
# Tests
tests/
test_*.py
*_test.py
# Temporary files
tmp/
temp/
*.tmp

22
.env.example Normal file
View File

@ -0,0 +1,22 @@
# Configuration du Homelab Automation Dashboard
# Copier ce fichier en .env et adapter les valeurs
# Clé API pour l'authentification (changer en production!)
API_KEY=dev-key-12345
# Utilisateur SSH pour Ansible (doit exister sur les hôtes cibles)
SSH_USER=automation
# Répertoire contenant les clés SSH sur la machine hôte
# Sera monté en lecture seule dans le container
SSH_KEY_DIR=~/.ssh
# Répertoire des logs de tâches (fichiers markdown classés par YYYY/MM/JJ)
# Ce répertoire sera monté dans le container et contiendra l'historique
# des exécutions de tâches au format markdown
# Exemple Windows: C:\Obsidian_doc\SessionsManager\60-TACHES\LOGS
# Exemple Linux: /home/user/tasks_logs
DIR_LOGS_TASKS=./tasks_logs
# Optionnel: Chemin spécifique de la clé privée SSH
# SSH_KEY_PATH=/path/to/id_rsa

46
.gitignore vendored Normal file
View File

@ -0,0 +1,46 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Virtual environments
.env
.venv
env/
venv/
ENV/
# IDE
.idea/
.vscode/
*.swp
*.swo
# Logs de tâches (gardent la structure mais pas les fichiers md)
tasks_logs/**/*.md
tasks_logs/**/.adhoc_history.json
!tasks_logs/.gitkeep
# Docker
*.log
# OS
.DS_Store
Thumbs.db

55
Dockerfile Normal file
View File

@ -0,0 +1,55 @@
# Dockerfile pour Homelab Automation Dashboard avec Ansible
FROM python:3.11-slim
# Métadonnées
LABEL maintainer="Homelab Automation"
LABEL description="Dashboard d'automatisation Homelab avec FastAPI et Ansible"
LABEL version="1.0"
# Variables d'environnement
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV ANSIBLE_HOST_KEY_CHECKING=False
ENV ANSIBLE_RETRY_FILES_ENABLED=False
# Répertoire de travail
WORKDIR /app
# Installation des dépendances système pour Ansible et SSH
RUN apt-get update && apt-get install -y --no-install-recommends \
openssh-client \
sshpass \
ansible \
git \
curl \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# Création du répertoire SSH et configuration
RUN mkdir -p /root/.ssh && chmod 700 /root/.ssh
# Copie des requirements et installation des dépendances Python
COPY app/requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Copie du code de l'application
COPY app/ ./
# Copie de la configuration Ansible
COPY ansible/ /ansible/
# Création du répertoire pour les clés SSH (sera monté en volume)
RUN mkdir -p /app/ssh_keys
# Configuration Ansible pour utiliser le bon répertoire
ENV ANSIBLE_CONFIG=/ansible/ansible.cfg
# Exposition du port
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/api/health || exit 1
# Commande de démarrage
CMD ["python", "-m", "uvicorn", "app_optimized:app", "--host", "0.0.0.0", "--port", "8000"]

Binary file not shown.

426
README.md Normal file
View File

@ -0,0 +1,426 @@
# Homelab Automation Dashboard
Une application moderne et professionnelle pour la gestion automatisée d'homelab, avec une interface utilisateur élégante et une API REST puissante.
## ✨ Fonctionnalités
### Interface Utilisateur
- **Design Moderne** : Interface élégante avec thème sombre et animations fluides
- **Tableau de Bord Temps Réel** : Métriques et statistiques en direct
- **Gestion des Hôtes** : Ajout, suppression et surveillance des serveurs
- **Gestion des Tâches** : Création et suivi des tâches automatisées
- **Logs Système** : Journalisation complète avec filtrage
- **WebSocket** : Mises à jour en temps réel sans rechargement
- **Animations** : Effets visuels professionnels avec Anime.js
### API REST
- **Endpoints Complets** : Gestion complète des hôtes, tâches et logs
- **Validation Pydantic** : Validation automatique des données
- **WebSocket Support** : Communication temps réel
- **Authentification API** : Sécurité renforcée avec clés API
- **Documentation Interactive** : Swagger UI et ReDoc
- **CORS Support** : Compatible avec les applications web modernes
### Intégration Ansible
- **Exécution de Playbooks** : Lancer des playbooks Ansible depuis le dashboard
- **Inventaire Dynamique** : Lecture automatique de l'inventaire Ansible
- **Actions Rapides** : Upgrade, Reboot, Health-check, Backup en un clic
- **Groupes Ansible** : Sélection des cibles par groupe (proxmox, lab, prod, etc.)
## 🛠️ Technologies Utilisées
### Frontend
- **HTML5 & CSS3** : Structure et styles modernes
- **Tailwind CSS** : Framework CSS utilitaire
- **Anime.js** : Bibliothèque d'animations
- **Font Awesome** : Icônes professionnelles
- **Google Fonts (Inter)** : Typographie moderne
### Backend
- **FastAPI** : Framework web Python moderne et rapide
- **Pydantic** : Validation de données
- **WebSockets** : Communication temps réel
- **Uvicorn** : Serveur ASGI performant
## 📁 Structure du Projet
```
homelab-automation-api-v2/
├── app/
│ ├── app_optimized.py # Backend FastAPI avec intégration Ansible
│ ├── index.html # Interface principale du dashboard
│ ├── main.js # Logique JavaScript (appels API)
│ └── requirements.txt # Dépendances Python
├── ansible/
│ ├── ansible.cfg # Configuration Ansible
│ ├── inventory/
│ │ └── hosts.yml # Inventaire des hôtes
│ ├── group_vars/
│ │ └── homelab.yml # Variables de groupe
│ └── playbooks/
│ ├── vm-upgrade.yml # Mise à jour des systèmes
│ ├── vm-reboot.yml # Redémarrage des hôtes
│ ├── health-check.yml # Vérification de santé
│ └── backup-config.yml # Sauvegarde de configuration
└── README.md # Documentation
```
## 🚀 Installation et Lancement
### Prérequis
- Python 3.10+ (testé avec Python 3.14)
- Ansible (pour l'exécution des playbooks)
- Navigateur moderne (Chrome, Firefox, Safari, Edge)
### Installation d'Ansible (optionnel mais recommandé)
```bash
# Sur Debian/Ubuntu
sudo apt install ansible
# Sur Windows (via pip)
pip install ansible
# Sur macOS
brew install ansible
```
### Installation
1. **Cloner le projet**
```bash
git clone <url-du-repo>
cd homelab-automation-api-v2/app
```
2. **Installer les dépendances Python**
```bash
pip install -r requirements.txt
```
3. **Lancer le serveur backend (recommandé)**
```bash
python -m uvicorn app_optimized:app --host 0.0.0.0 --port 8000 --reload
```
Ou directement via le script Python :
```bash
python app_optimized.py
```
4. **Ouvrir le dashboard frontend complet** (interface de l'image 1)
Naviguez vers :
- `http://localhost:8000/ui` → Dashboard Homelab (frontend HTML/JS)
5. **Accéder à la page API** (interface de l'image 2)
Naviguez vers :
- `http://localhost:8000` → Page d'accueil API avec liens vers la documentation
## 📖 Utilisation
### Interface Web
1. **Tableau de Bord** : Vue d'ensemble des métriques système
2. **Gestion des Hôtes** :
- Ajouter de nouveaux hôtes avec le bouton "Ajouter Host"
- Surveiller l'état des hôtes en temps réel
- Exécuter des actions (connexion, mise à jour, redémarrage)
3. **Gestion des Tâches** :
- Créer des tâches d'automatisation
- Suivre la progression en temps réel
- Voir les détails et les logs
4. **Logs Système** : Consulter l'historique des événements
### API REST
#### Authentification
Toutes les requêtes API nécessitent une clé API dans le header `X-API-Key`:
```bash
curl -H "X-API-Key: dev-key-12345" http://localhost:8000/api/hosts
```
#### Endpoints Principaux
**Hôtes**
- `GET /api/hosts` - Liste tous les hôtes
- `POST /api/hosts` - Crée un nouvel hôte
- `GET /api/hosts/{id}` - Récupère un hôte spécifique
- `DELETE /api/hosts/{id}` - Supprime un hôte
**Tâches**
- `GET /api/tasks` - Liste toutes les tâches
- `POST /api/tasks` - Crée une nouvelle tâche
- `GET /api/tasks/{id}` - Récupère une tâche spécifique
- `DELETE /api/tasks/{id}` - Supprime une tâche
**Logs**
- `GET /api/logs` - Récupère les logs récents
- `POST /api/logs` - Ajoute un nouveau log
- `DELETE /api/logs` - Efface tous les logs
**Métriques**
- `GET /api/metrics` - Métriques système
- `GET /api/health/{host}` - Health check d'un hôte
**WebSocket**
- `WS /ws` - Connexion WebSocket pour les mises à jour temps réel
**Ansible** (nouveaux endpoints)
- `GET /api/ansible/playbooks` - Liste les playbooks disponibles
- `GET /api/ansible/inventory` - Récupère l'inventaire Ansible (hôtes et groupes)
- `GET /api/ansible/groups` - Liste les groupes Ansible
- `POST /api/ansible/execute` - Exécute un playbook directement
- `POST /api/ansible/adhoc` - Exécute une commande ad-hoc sur les hôtes
- `POST /api/ansible/bootstrap` - Bootstrap un hôte pour Ansible (crée user, SSH, sudo, Python)
- `GET /api/ansible/ssh-config` - Diagnostic de la configuration SSH
#### Exemples d'utilisation Ansible
**Lister les playbooks disponibles :**
```bash
curl -H "X-API-Key: dev-key-12345" http://localhost:8000/api/ansible/playbooks
```
**Voir l'inventaire Ansible :**
```bash
curl -H "X-API-Key: dev-key-12345" http://localhost:8000/api/ansible/inventory
```
**Exécuter un playbook (ex: mise à jour sur le groupe "lab") :**
```bash
curl -X POST -H "X-API-Key: dev-key-12345" -H "Content-Type: application/json" \
-d '{"playbook": "vm-upgrade.yml", "target": "lab"}' \
http://localhost:8000/api/ansible/execute
```
**Créer une tâche Ansible via l'API tasks :**
```bash
curl -X POST -H "X-API-Key: dev-key-12345" -H "Content-Type: application/json" \
-d '{"action": "upgrade", "group": "proxmox"}' \
http://localhost:8000/api/tasks
```
**Exécuter une commande ad-hoc :**
```bash
# Vérifier l'espace disque sur tous les hôtes
curl -X POST -H "X-API-Key: dev-key-12345" -H "Content-Type: application/json" \
-d '{"target": "all", "command": "df -h /", "module": "shell"}' \
http://localhost:8000/api/ansible/adhoc
# Redémarrer un service avec sudo
curl -X POST -H "X-API-Key: dev-key-12345" -H "Content-Type: application/json" \
-d '{"target": "web-servers", "command": "systemctl restart nginx", "become": true}' \
http://localhost:8000/api/ansible/adhoc
```
### Documentation API
- **Swagger UI** : `http://localhost:8000/api/docs`
- **ReDoc** : `http://localhost:8000/api/redoc`
## 🎨 Personnalisation
### Thèmes
L'application supporte les thèmes clair et sombre. Utilisez le bouton en haut à droite pour basculer.
### Couleurs
Les couleurs principales peuvent être modifiées dans les variables CSS :
```css
:root {
--primary-bg: #0a0a0a;
--accent-color: #7c3aed;
--success-color: #10b981;
--error-color: #ef4444;
}
```
### Animations
Les animations sont gérées par Anime.js dans `main.js`. Vous pouvez ajuster :
- La durée des animations
- Les effets de transition
- Les comportements au scroll
## 🔧 Configuration
### Variables d'Environnement
Créez un fichier `.env` pour configurer l'application :
```env
API_KEY=votre-cle-api-secrete
SSH_REMOTE_USER=automation
LOGS_DIR=/var/log/homelab
ANSIBLE_DIR=/etc/ansible
```
### Base de Données
Par défaut, l'application utilise une base de données en mémoire. Pour une utilisation en production, configurez PostgreSQL ou SQLite en modifiant la classe `InMemoryDB`.
## 🚀 Déploiement
### Production
1. **Configuration de la base de données**
```python
# Remplacer InMemoryDB par une vraie base de données
```
2. **Sécurité**
- Utilisez une clé API forte
- Activez HTTPS
- Configurez les pare-feu
- Limitez les origines CORS
3. **Performance**
- Utilisez un serveur de production (Gunicorn)
- Configurez Redis pour le cache
- Activez la compression
### Docker
Le projet inclut un Dockerfile et docker-compose.yml prêts à l'emploi avec Ansible intégré.
#### Démarrage rapide avec Docker Compose
```bash
# 1. Copier le fichier d'exemple d'environnement
cp .env.example .env
# 2. Éditer .env pour configurer vos clés SSH
nano .env
# 3. Lancer le container
docker-compose up -d
# 4. Accéder au dashboard
# http://localhost:8000/ui
```
#### Configuration des clés SSH
Par défaut, docker-compose monte votre répertoire `~/.ssh` en lecture seule. Assurez-vous que :
1. Votre clé privée SSH existe : `~/.ssh/id_rsa`
2. Votre clé publique existe : `~/.ssh/id_rsa.pub`
Ou spécifiez un répertoire différent dans `.env` :
```bash
SSH_KEY_DIR=/chemin/vers/vos/cles/ssh
```
#### Variables d'environnement
| Variable | Description | Défaut |
|----------|-------------|--------|
| `API_KEY` | Clé API pour l'authentification | `dev-key-12345` |
| `SSH_USER` | Utilisateur SSH pour Ansible | `automation` |
| `SSH_KEY_DIR` | Répertoire des clés SSH sur l'hôte | `~/.ssh` |
| `SSH_KEY_PATH` | Chemin de la clé privée dans le container | `/app/ssh_keys/id_rsa` |
#### Construction manuelle de l'image
```bash
# Construire l'image
docker build -t homelab-dashboard .
```
```bash
# Exécuter le container
docker run -d \
-p 8000:8000 \
-v ~/.ssh:/app/ssh_keys:ro \
-v ./ansible/inventory:/ansible/inventory:ro \
-e API_KEY=votre-cle-api-secrete \
--name homelab-dashboard \
homelab-dashboard
```
```bash
# se connecter au container
docker exec -it homelab-dashboard /bin/bash
```
## 🔧 Bootstrap SSH
Le dashboard inclut une fonctionnalité de **Bootstrap** pour préparer automatiquement un hôte à recevoir des commandes Ansible.
### Ce que fait le Bootstrap
1. **Crée l'utilisateur d'automatisation** (par défaut: `automation`)
2. **Configure l'authentification SSH par clé** (copie votre clé publique)
3. **Installe et configure sudo** sans mot de passe pour cet utilisateur
4. **Installe Python3** (requis par Ansible)
5. **Vérifie la connexion SSH** par clé après configuration
### Systèmes supportés
- **Debian/Ubuntu** (apt)
- **Alpine Linux** (apk)
- **FreeBSD** (pkg)
### Utilisation via l'interface
1. Cliquez sur le bouton **Bootstrap** (jaune) sur la carte d'un hôte
2. Entrez le **mot de passe root** de l'hôte distant
3. Optionnel : modifiez le nom de l'utilisateur d'automatisation
4. Cliquez sur **Lancer le Bootstrap**
### Utilisation via l'API
```bash
curl -X POST http://localhost:8000/api/ansible/bootstrap \
-H "X-API-Key: dev-key-12345" \
-H "Content-Type: application/json" \
-d '{
"host": "192.168.1.100",
"root_password": "votre-mot-de-passe-root",
"automation_user": "automation"
}'
```
### Prérequis
- **sshpass** doit être installé sur la machine qui exécute le dashboard
- L'hôte cible doit accepter les connexions SSH avec mot de passe (pour la configuration initiale)
- Le compte root doit être accessible via SSH
```bash
# Installation de sshpass
# Debian/Ubuntu
apt install sshpass
# Alpine
apk add sshpass
# macOS
brew install hudochenkov/sshpass/sshpass
```
## 🤝 Contribution
Les contributions sont les bienvenues ! Veuillez :
1. Fork le projet
2. Créer une branche pour votre fonctionnalité
3. Commit vos changements
4. Push vers la branche
5. Ouvrir une Pull Request
## 📄 Licence
Ce projet est sous licence MIT. Voir le fichier LICENSE pour plus de détails.
## 🆘 Support
Pour toute question ou problème :
1. Consultez la documentation
2. Vérifiez les logs du serveur
3. Ouvrez une issue sur GitHub
4. Contactez l'équipe de développement
---
**Développé avec ❤️ pour la communauté homelab**

9
ansible/ansible.cfg Normal file
View File

@ -0,0 +1,9 @@
[defaults]
inventory = ./inventory/hosts.yml
host_key_checking = False
retry_files_enabled = False
stdout_callback = default
bin_ansible_callbacks = True
[callback_default]
result_format = yaml

View File

@ -0,0 +1,4 @@
ansible_port: 22
ansible_user: automation
ansible_ssh_private_key_file: /app/ssh_keys/id_automation_ansible

View File

@ -0,0 +1,4 @@
ansible_port: 22
ansible_user: automation
ansible_ssh_private_key_file: /app/ssh_keys/id_automation_ansible
ansible_python_interpreter: /usr/bin/python3

View File

@ -0,0 +1,4 @@
ansible_port: 22
ansible_user: automation
ansible_ssh_private_key_file: /app/ssh_keys/id_automation_ansible
ansible_python_interpreter: /usr/bin/python3

View File

@ -0,0 +1,4 @@
ansible_port: 22
ansible_user: automation
ansible_ssh_private_key_file: /app/ssh_keys/id_automation_ansible
ansible_python_interpreter: /usr/bin/python3

View File

@ -0,0 +1,4 @@
ansible_port: 22
ansible_user: automation
ansible_ssh_private_key_file: /app/ssh_keys/id_automation_ansible
ansible_python_interpreter: /usr/bin/python3

View File

@ -0,0 +1 @@
ansible_python_interpreter: /usr/local/bin/python3

View File

@ -0,0 +1,52 @@
all:
children:
env_homelab:
hosts:
ali2v.xeon.home: null
hp.nas.home: null
hp2.i7.home: null
hp3.i5.home: null
mimi.pc.home: null
orangepi.pc.home: null
raspi.4gb.home: null
raspi.8gb.home: null
env_lab:
hosts:
media.labb.home: null
dev.lab.home: null
env_prod:
hosts:
hp.truenas.home: null
ali2v.truenas.home: null
jump.point.home: null
automate.prod.home: null
dev.prod.home: null
role_proxmox:
hosts:
ali2v.xeon.home: null
hp.nas.home: null
hp2.i7.home: null
hp3.i5.home: null
mimi.pc.home: null
role_lab_servers:
hosts:
media.labb.home: null
dev.lab.home: null
role_truenas:
hosts:
hp.truenas.home: null
ali2v.truenas.home:
ansible_python_interpreter: /usr/bin/python3
role_prod_servers:
hosts:
jump.point.home: null
automate.prod.home: null
dev.prod.home: null
role_sbc:
hosts:
orangepi.pc.home: null
raspi.4gb.home: null
raspi.8gb.home: null
role_docker:
hosts:
dev.lab.home: null

View File

@ -0,0 +1,55 @@
all:
children:
env_homelab:
hosts:
ali2v.xeon.home: null
hp.nas.home: null
hp2.i7.home: null
hp3.i5.home: null
mimi.pc.home: null
orangepi.pc.home: null
raspi.4gb.home: null
raspi.8gb.home: null
env_lab:
hosts:
media.labb.home: null
toto:
ansible_host: toto.home
dev.lab.home: null
env_prod:
hosts:
hp.truenas.home: null
ali2v.truenas.home: null
jump.point.home: null
automate.prod.home: null
dev.prod.home: null
role_proxmox:
hosts:
ali2v.xeon.home: null
hp.nas.home: null
hp2.i7.home: null
hp3.i5.home: null
mimi.pc.home: null
role_lab_servers:
hosts:
media.labb.home: null
toto: null
dev.lab.home: null
role_truenas:
hosts:
hp.truenas.home: null
ali2v.truenas.home:
ansible_python_interpreter: /usr/bin/python3
role_prod_servers:
hosts:
jump.point.home: null
automate.prod.home: null
dev.prod.home: null
role_sbc:
hosts:
orangepi.pc.home: null
raspi.4gb.home: null
raspi.8gb.home: null
role_docker:
hosts:
dev.lab.home: null

View File

@ -0,0 +1,47 @@
---
- name: Backup configuration files
hosts: all
become: true
gather_facts: true
vars:
category: backup
subcategory: configuration
backup_dir: /tmp/config_backup
timestamp: "{{ ansible_date_time.iso8601_basic_short }}"
tasks:
- name: Create backup directory
ansible.builtin.file:
path: "{{ backup_dir }}"
state: directory
mode: '0755'
- name: Backup /etc directory (essential configs)
ansible.builtin.archive:
path:
- /etc/hostname
- /etc/hosts
- /etc/passwd
- /etc/group
- /etc/shadow
- /etc/sudoers
- /etc/ssh/sshd_config
dest: "{{ backup_dir }}/etc_backup_{{ timestamp }}.tar.gz"
format: gz
ignore_errors: true
- name: Backup crontabs
ansible.builtin.shell: |
crontab -l > {{ backup_dir }}/crontab_{{ timestamp }}.txt 2>/dev/null || echo "No crontab"
changed_when: false
- name: List backup files
ansible.builtin.find:
paths: "{{ backup_dir }}"
patterns: "*{{ timestamp }}*"
register: backup_files
- name: Display backup summary
ansible.builtin.debug:
msg: |
Backup completed for {{ inventory_hostname }}
Files created: {{ backup_files.files | map(attribute='path') | list }}

View File

@ -0,0 +1,229 @@
---
- name: Bootstrap host for Ansible automation
hosts: all
become: true
gather_facts: false
vars:
category: maintenance
subcategory: bootstrap
automation_user: "{{ lookup('env', 'SSH_USER') | default('automation', true) }}"
ssh_public_key_path: "/app/ssh_keys/id_rsa.pub"
tasks:
- name: Detect OS type
raw: |
if command -v apk >/dev/null 2>&1; then
echo "alpine"
elif command -v pkg >/dev/null 2>&1 && [ -f /etc/freebsd-update.conf ]; then
echo "freebsd"
elif [ -f /etc/debian_version ]; then
echo "debian"
elif [ -f /etc/redhat-release ]; then
echo "redhat"
else
echo "unknown"
fi
register: os_type_raw
changed_when: false
- name: Detect OS variant (Armbian, Raspbian, etc.)
raw: |
if [ -f /etc/armbian-release ]; then
echo "armbian"
elif [ -f /etc/rpi-issue ] || grep -qi "raspberry" /proc/cpuinfo 2>/dev/null; then
echo "raspbian"
else
echo "standard"
fi
register: os_variant_raw
changed_when: false
- name: Set OS type fact
set_fact:
os_type: "{{ os_type_raw.stdout | trim }}"
os_variant: "{{ os_variant_raw.stdout | trim }}"
- name: Display detected OS
debug:
msg: "[1/7] OS détecté: {{ os_type }} ({{ os_variant }})"
- name: Check if automation user exists
raw: id {{ automation_user }} 2>/dev/null && echo "exists" || echo "not_exists"
register: user_check
changed_when: false
- name: Display user status
debug:
msg: "[2/7] Utilisateur {{ automation_user }}: {{ 'existant' if 'exists' in user_check.stdout else 'à créer' }}"
- name: Create automation user (Debian/Ubuntu)
raw: useradd -m -s /bin/bash {{ automation_user }} || useradd -m -s /bin/sh {{ automation_user }}
when:
- "'not_exists' in user_check.stdout"
- os_type == "debian"
- name: Create automation user (Alpine)
raw: adduser -D {{ automation_user }}
when:
- "'not_exists' in user_check.stdout"
- os_type == "alpine"
- name: Create automation user (FreeBSD)
raw: pw useradd {{ automation_user }} -m -s /bin/sh
when:
- "'not_exists' in user_check.stdout"
- os_type == "freebsd"
- name: Display user creation result
debug:
msg: "[3/7] Utilisateur {{ automation_user }} configuré"
- name: Unlock user account
raw: |
if command -v passwd >/dev/null 2>&1; then
passwd -u {{ automation_user }} 2>/dev/null || true
fi
if command -v usermod >/dev/null 2>&1; then
usermod -U {{ automation_user }} 2>/dev/null || true
fi
changed_when: false
- name: Get home directory
raw: "getent passwd {{ automation_user }} | cut -d: -f6 || echo /home/{{ automation_user }}"
register: home_dir_raw
changed_when: false
- name: Set home directory fact
set_fact:
home_dir: "{{ home_dir_raw.stdout | trim }}"
- name: Create .ssh directory
raw: |
mkdir -p {{ home_dir }}/.ssh
chown {{ automation_user }}:{{ automation_user }} {{ home_dir }}/.ssh
chmod 700 {{ home_dir }}/.ssh
changed_when: false
- name: Read local SSH public key
delegate_to: localhost
become: false
slurp:
src: "{{ ssh_public_key_path }}"
register: ssh_pub_key
ignore_errors: true
- name: Try alternate SSH key paths
delegate_to: localhost
become: false
slurp:
src: "{{ item }}"
register: ssh_pub_key_alt
loop:
- "/app/ssh_keys/id_ed25519.pub"
- "/app/ssh_keys/id_automation_ansible.pub"
- "{{ lookup('env', 'HOME') }}/.ssh/id_rsa.pub"
when: ssh_pub_key.failed | default(false)
ignore_errors: true
- name: Set SSH public key content
set_fact:
ssh_key_content: "{{ (ssh_pub_key.content | b64decode | trim) if not (ssh_pub_key.failed | default(false)) else (ssh_pub_key_alt.results | selectattr('content', 'defined') | map(attribute='content') | first | b64decode | trim) }}"
when: not (ssh_pub_key.failed | default(false)) or (ssh_pub_key_alt.results | selectattr('content', 'defined') | list | length > 0)
- name: Install SSH public key
raw: |
cat > {{ home_dir }}/.ssh/authorized_keys << 'SSHKEY_EOF'
{{ ssh_key_content }}
SSHKEY_EOF
chown {{ automation_user }}:{{ automation_user }} {{ home_dir }}/.ssh/authorized_keys
chmod 600 {{ home_dir }}/.ssh/authorized_keys
when: ssh_key_content is defined
- name: Verify authorized_keys
raw: test -s {{ home_dir }}/.ssh/authorized_keys && wc -l < {{ home_dir }}/.ssh/authorized_keys || echo "0"
register: auth_keys_count
changed_when: false
- name: Display SSH key status
debug:
msg: "[4/7] Clé SSH installée ({{ auth_keys_count.stdout | trim }} clé(s))"
- name: Install sudo (Debian/Ubuntu)
raw: |
if ! command -v sudo >/dev/null 2>&1; then
apt-get update -qq && apt-get install -y sudo
fi
when: os_type == "debian"
- name: Install sudo (Alpine)
raw: |
if ! command -v sudo >/dev/null 2>&1; then
apk add --no-cache sudo
fi
when: os_type == "alpine"
- name: Install sudo (FreeBSD)
raw: |
if ! command -v sudo >/dev/null 2>&1; then
pkg install -y sudo
fi
when: os_type == "freebsd"
- name: Display sudo status
debug:
msg: "[5/7] sudo installé/vérifié"
- name: Configure sudoers
raw: |
mkdir -p /etc/sudoers.d
echo "{{ automation_user }} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/automation
chmod 440 /etc/sudoers.d/automation
changed_when: false
- name: Display sudoers status
debug:
msg: "[6/7] Sudoers configuré: /etc/sudoers.d/automation"
- name: Install Python3 (Debian/Ubuntu)
raw: |
if ! command -v python3 >/dev/null 2>&1; then
apt-get update -qq && apt-get install -y python3
fi
when: os_type == "debian"
- name: Install Python3 (Alpine)
raw: |
if ! command -v python3 >/dev/null 2>&1; then
apk add --no-cache python3
fi
when: os_type == "alpine"
- name: Install Python3 (FreeBSD)
raw: |
if ! command -v python3 >/dev/null 2>&1; then
pkg install -y python3
fi
when: os_type == "freebsd"
- name: Get Python version
raw: python3 --version 2>&1 || echo "N/A"
register: python_version
changed_when: false
- name: Display Python status
debug:
msg: "[7/7] Python3 installé: {{ python_version.stdout | trim }}"
- name: Display bootstrap summary
debug:
msg: |
═══════════════════════════════════════
Bootstrap terminé avec succès!
═══════════════════════════════════════
Host: {{ inventory_hostname }}
OS Type: {{ os_type }} ({{ os_variant }})
User: {{ automation_user }}
Home: {{ home_dir }}
SSH Keys: {{ auth_keys_count.stdout | trim }}
Python: {{ python_version.stdout | trim }}
═══════════════════════════════════════

View File

@ -0,0 +1,83 @@
---
- name: Health check on target host
hosts: all
become: false
gather_facts: false
vars:
category: monitoring
subcategory: health
tasks:
- name: Check if host is reachable (ping)
ansible.builtin.ping:
register: ping_result
- name: Gather minimal facts
ansible.builtin.setup:
gather_subset:
- '!all'
- '!min'
- distribution
ignore_errors: true
register: facts_result
- name: Get system uptime
ansible.builtin.command: uptime
register: uptime_result
changed_when: false
ignore_errors: true
- name: Get disk usage
ansible.builtin.shell: df -h / | tail -1 | awk '{print $5}'
register: disk_usage
changed_when: false
ignore_errors: true
- name: Get memory usage (Linux)
ansible.builtin.shell: |
if command -v free >/dev/null 2>&1; then
free -m | grep Mem | awk '{printf "%.1f%%", $3/$2 * 100}'
else
# Fallback for systems without free command
cat /proc/meminfo | awk '/MemTotal/{total=$2} /MemAvailable/{avail=$2} END{printf "%.1f%%", (total-avail)/total*100}'
fi
register: memory_usage
changed_when: false
ignore_errors: true
- name: Get CPU temperature (ARM/SBC)
ansible.builtin.shell: |
if [ -f /sys/class/thermal/thermal_zone0/temp ]; then
temp=$(cat /sys/class/thermal/thermal_zone0/temp)
# Use awk instead of bc for better compatibility
echo "${temp}" | awk '{printf "%.1f°C", $1/1000}'
else
echo "N/A"
fi
register: cpu_temp
changed_when: false
ignore_errors: true
- name: Get CPU load
ansible.builtin.shell: |
if [ -f /proc/loadavg ]; then
cat /proc/loadavg | awk '{print $1}'
else
uptime | awk -F'load average:' '{print $2}' | awk -F',' '{print $1}' | tr -d ' '
fi
register: cpu_load
changed_when: false
ignore_errors: true
- name: Display health status
ansible.builtin.debug:
msg: |
═══════════════════════════════════════
Host: {{ inventory_hostname }}
Status: {{ 'OK' if ping_result is success else 'UNREACHABLE' }}
═══════════════════════════════════════
Uptime: {{ uptime_result.stdout | default('N/A') }}
Disk Usage: {{ disk_usage.stdout | default('N/A') }}
Memory Usage: {{ memory_usage.stdout | default('N/A') }}
CPU Load: {{ cpu_load.stdout | default('N/A') }}
CPU Temp: {{ cpu_temp.stdout | default('N/A') }}
═══════════════════════════════════════

View File

@ -0,0 +1,15 @@
---
# mon-playbook.yml
# Créé le 03/12/2025
- name: mon playbook
hosts: all
become: yes
vars:
category: Test
subcategory: other
tasks:
- name: Exemple de tâche
ansible.builtin.debug:
msg: "Playbook mon-playbook exécuté avec succès!"

View File

@ -0,0 +1,44 @@
---
- name: Install jq on target host
hosts: all
become: true
gather_facts: true
vars:
category: maintenance
subcategory: system
tasks:
- name: Install jq
ansible.builtin.package:
name: jq
state: present
when: ansible_facts['os_family'] == 'Debian'
- name: Install jq on RedHat family
ansible.builtin.dnf:
name: jq
state: present
when: ansible_facts['os_family'] == 'RedHat'
- name: Install jq on Alpine
ansible.builtin.apk:
name: jq
state: present
when: ansible_facts['os_family'] == 'Alpine'
- name: Install jq on FreeBSD
ansible.builtin.pkg:
name: jq
state: present
when: ansible_facts['os_family'] == 'FreeBSD'
- name: Install jq on OpenBSD
ansible.builtin.pkg_add:
name: jq
state: present
when: ansible_facts['os_family'] == 'OpenBSD'
- name: Install jq on macOS
ansible.builtin.homebrew:
name: jq
state: present
when: ansible_facts['os_family'] == 'Darwin'
- name: Install jq on Windows
ansible.builtin.win_chocolatey:
name: jq
state: present
when: ansible_facts['os_family'] == 'Windows'

View File

@ -0,0 +1,23 @@
---
- name: Reboot target host
hosts: all
become: true
vars:
category: maintenance
subcategory: system
tasks:
- name: Detect distribution
ansible.builtin.setup:
gather_subset:
- os_family
- name: Reboot the machine (Linux)
ansible.builtin.reboot:
reboot_timeout: 600
when: ansible_facts['os_family'] != 'FreeBSD'
- name: Reboot the machine (FreeBSD)
ansible.builtin.reboot:
reboot_timeout: 600
boot_time_command: sysctl kern.boottime
when: ansible_facts['os_family'] == 'FreeBSD'

View File

@ -0,0 +1,34 @@
---
- name: Upgrade packages on target host
hosts: all
become: true
vars:
category: maintenance
subcategory: system
tasks:
- name: Detect distribution
ansible.builtin.setup:
gather_subset:
- os_family
- name: Upgrade on Debian/Ubuntu
ansible.builtin.apt:
update_cache: yes
upgrade: dist
when: ansible_facts['os_family'] == 'Debian'
- name: Upgrade on Alpine
ansible.builtin.shell: |
apk update && apk upgrade
when: ansible_facts['os_family'] == 'Alpine'
- name: Upgrade on RedHat family
ansible.builtin.dnf:
name: "*"
state: latest
when: ansible_facts['os_family'] == 'RedHat'
- name: Upgrade on FreeBSD
ansible.builtin.shell: |
pkg update && pkg upgrade -y
when: ansible_facts['os_family'] == 'FreeBSD'

3843
app/app_optimized.py Normal file

File diff suppressed because it is too large Load Diff

2540
app/index.html Normal file

File diff suppressed because it is too large Load Diff

6412
app/main.js Normal file

File diff suppressed because it is too large Load Diff

10
app/requirements.txt Normal file
View File

@ -0,0 +1,10 @@
fastapi>=0.115.0
uvicorn[standard]>=0.32.0
pydantic>=2.12.0
python-multipart>=0.0.9
PyYAML>=6.0.2
websockets>=14.0
aiofiles>=24.1.0
python-dotenv>=1.0.1
requests>=2.32.0
httpx>=0.28.0

58
docker-compose.yml Normal file
View File

@ -0,0 +1,58 @@
---
services:
homelab-dashboard:
build:
context: .
dockerfile: Dockerfile
container_name: homelab-automation-dashboard
restart: unless-stopped
ports:
- "8008:8000"
environment:
# Clé API pour l'authentification
- API_KEY=${API_KEY:-dev-key-12345}
# Utilisateur SSH pour Ansible
- SSH_USER=${SSH_USER:-automation}
# Chemin de la clé SSH dans le container
- SSH_KEY_PATH=/app/ssh_keys/id_rsa
# Désactiver la vérification des clés SSH (pour les tests)
- ANSIBLE_HOST_KEY_CHECKING=False
# Timeout SSH
- ANSIBLE_TIMEOUT=30
# Répertoire des logs de tâches (format YYYY/MM/JJ)
- DIR_LOGS_TASKS=/app/tasks_logs
# Ansible inventory
- ANSIBLE_INVENTORY=./ansible/inventory
# Ansible playbooks
- ANSIBLE_PLAYBOOKS=./ansible/playbooks
# Ansible group_vars
- ANSIBLE_GROUP_VARS=./ansible/inventory/group_vars
volumes:
# Monter l'inventaire Ansible (permet de modifier sans rebuild)
- ${ANSIBLE_INVENTORY:-./ansible/inventory}:/ansible/inventory
# Monter les playbooks (permet de modifier sans rebuild)
- ${ANSIBLE_PLAYBOOKS:-./ansible/playbooks}:/ansible/playbooks
# Monter les variables de groupe
- ${ANSIBLE_GROUP_VARS:-./ansible/inventory/group_vars}:/ansible/inventory/group_vars
# Monter les clés SSH depuis le host
- ${SSH_KEY_DIR:-~/.ssh}:/app/ssh_keys:ro
# Volume pour les logs (optionnel)
- homelab_logs:/app/logs
# Monter le répertoire des logs de tâches depuis le host
- ${DIR_LOGS_TASKS:-./tasks_logs}:/app/tasks_logs
networks:
- homelab-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
networks:
homelab-network:
driver: bridge
volumes:
homelab_logs:
driver: local

View File

@ -0,0 +1,79 @@
{
"hosts": {
"dev.lab.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T20:20:03.555927+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"media.labb.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T01:52:34.259129+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"ali2v.xeon.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T14:35:47.874004+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"raspi.4gb.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T16:09:22.961007+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"raspi.8gb.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T16:10:53.117121+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"orangepi.pc.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T16:11:47.008381+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"jump.point.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T18:56:57.635706+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"hp.nas.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T20:25:44.595352+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"hp2.i7.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T20:25:51.895846+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"hp3.i5.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T20:25:59.998069+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"mimi.pc.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T20:26:08.419143+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"dev.prod.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T21:02:48.893923+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"automate.prod.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T21:03:44.363353+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"ali2v.truenas.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-02T21:47:48.804941+00:00",
"details": "Bootstrap réussi via API (user: automation)"
},
"hp.truenas.home": {
"bootstrap_ok": true,
"bootstrap_date": "2025-12-03T00:43:57.196419+00:00",
"details": "Bootstrap réussi via API (user: automation)"
}
}
}

2
tasks_logs/.gitkeep Normal file
View File

@ -0,0 +1,2 @@
# Ce fichier garde le répertoire tasks_logs dans git
# Les logs de tâches (*.md) seront créés ici dans le format YYYY/MM/JJ/