first commit
This commit is contained in:
commit
21bee026ff
65
.dockerignore
Normal file
65
.dockerignore
Normal 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
22
.env.example
Normal 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
46
.gitignore
vendored
Normal 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
55
Dockerfile
Normal 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"]
|
||||||
BIN
OKComputer_Optimiser UI_UX.zip
Normal file
BIN
OKComputer_Optimiser UI_UX.zip
Normal file
Binary file not shown.
426
README.md
Normal file
426
README.md
Normal 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
9
ansible/ansible.cfg
Normal 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
|
||||||
4
ansible/inventory/group_vars/env_homelab.yml
Normal file
4
ansible/inventory/group_vars/env_homelab.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
ansible_port: 22
|
||||||
|
ansible_user: automation
|
||||||
|
ansible_ssh_private_key_file: /app/ssh_keys/id_automation_ansible
|
||||||
|
|
||||||
4
ansible/inventory/group_vars/env_lab.yml
Normal file
4
ansible/inventory/group_vars/env_lab.yml
Normal 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
|
||||||
4
ansible/inventory/group_vars/env_prod.yml
Normal file
4
ansible/inventory/group_vars/env_prod.yml
Normal 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
|
||||||
4
ansible/inventory/group_vars/role_proxmox.yml
Normal file
4
ansible/inventory/group_vars/role_proxmox.yml
Normal 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
|
||||||
4
ansible/inventory/group_vars/role_sbc.yml
Normal file
4
ansible/inventory/group_vars/role_sbc.yml
Normal 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
|
||||||
1
ansible/inventory/group_vars/role_truenas.yml
Normal file
1
ansible/inventory/group_vars/role_truenas.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
ansible_python_interpreter: /usr/local/bin/python3
|
||||||
52
ansible/inventory/hosts.yml
Normal file
52
ansible/inventory/hosts.yml
Normal 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
|
||||||
55
ansible/inventory/hosts.yml.bak
Normal file
55
ansible/inventory/hosts.yml.bak
Normal 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
|
||||||
47
ansible/playbooks/backup-config.yml
Normal file
47
ansible/playbooks/backup-config.yml
Normal 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 }}
|
||||||
229
ansible/playbooks/bootstrap-host.yml
Normal file
229
ansible/playbooks/bootstrap-host.yml
Normal 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 }}
|
||||||
|
═══════════════════════════════════════
|
||||||
83
ansible/playbooks/health-check.yml
Normal file
83
ansible/playbooks/health-check.yml
Normal 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') }}
|
||||||
|
═══════════════════════════════════════
|
||||||
15
ansible/playbooks/mon-playbook.yml
Normal file
15
ansible/playbooks/mon-playbook.yml
Normal 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!"
|
||||||
44
ansible/playbooks/vm-install-jq.yml
Normal file
44
ansible/playbooks/vm-install-jq.yml
Normal 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'
|
||||||
23
ansible/playbooks/vm-reboot.yml
Normal file
23
ansible/playbooks/vm-reboot.yml
Normal 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'
|
||||||
34
ansible/playbooks/vm-upgrade.yml
Normal file
34
ansible/playbooks/vm-upgrade.yml
Normal 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
3843
app/app_optimized.py
Normal file
File diff suppressed because it is too large
Load Diff
2540
app/index.html
Normal file
2540
app/index.html
Normal file
File diff suppressed because it is too large
Load Diff
6412
app/main.js
Normal file
6412
app/main.js
Normal file
File diff suppressed because it is too large
Load Diff
10
app/requirements.txt
Normal file
10
app/requirements.txt
Normal 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
58
docker-compose.yml
Normal 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
|
||||||
79
tasks_logs/.bootstrap_status.json
Normal file
79
tasks_logs/.bootstrap_status.json
Normal 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
2
tasks_logs/.gitkeep
Normal 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/
|
||||||
Loading…
x
Reference in New Issue
Block a user