Files
lxc_manager/lxc_manager.sh
2025-12-18 08:27:56 +00:00

796 lines
22 KiB
Bash

#!/bin/bash
#############################################
# Script de Gestion LXC pour Proxmox VE
# Date: 18/12/2025
# Description: Gestion complète des conteneurs LXC
#############################################
# Sécurité stricte
set -euo pipefail
# Couleurs pour l'affichage
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m' # No Color
# Fonction pour gérer les erreurs
error_exit() {
echo -e "${RED}Erreur: $1${NC}" >&2
read -p "Appuyez sur Entrée pour continuer..."
return 1
}
# Fonction pour valider un VMID
validate_vmid() {
local vmid="$1"
if [[ ! "$vmid" =~ ^[1-9][0-9]{2,8}$ ]]; then
return 1
fi
return 0
}
# Fonction pour valider un nom d'hôte
validate_hostname() {
local hostname="$1"
if [[ ! "$hostname" =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$ ]]; then
return 1
fi
return 0
}
# Fonction pour valider une adresse IP
validate_ip() {
local ip="$1"
if [[ ! "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
return 1
fi
return 0
}
# Fonction pour valider un nombre positif
validate_positive_number() {
local num="$1"
if [[ ! "$num" =~ ^[1-9][0-9]*$ ]]; then
return 1
fi
return 0
}
# Fonction pour afficher le logo
show_logo() {
clear
echo -e "${BLUE}"
echo "╔═══════════════════════════════════════════════╗"
echo "║ GESTIONNAIRE LXC PROXMOX VE ║"
echo "║ Version 2.0 - Sécurisé ║"
echo "╚═══════════════════════════════════════════════╝"
echo -e "${NC}"
}
# Fonction pour vérifier si on est sur Proxmox
check_proxmox() {
if ! command -v pct &> /dev/null; then
echo -e "${RED}Erreur: Ce script doit être exécuté sur un serveur Proxmox VE${NC}"
exit 1
fi
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}Erreur: Ce script doit être exécuté en tant que root${NC}"
exit 1
fi
}
# Fonction pour lister les conteneurs
list_containers() {
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
echo -e "${GREEN}Liste des conteneurs LXC:${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
pct list || error_exit "Impossible de lister les conteneurs"
echo ""
}
# Fonction pour vérifier l'existence d'un conteneur
container_exists() {
local vmid="$1"
pct status "$vmid" &>/dev/null
}
# Fonction pour créer un conteneur
create_container() {
show_logo
echo -e "${GREEN}Création d'un nouveau conteneur LXC${NC}\n"
# Liste des conteneurs existants
list_containers
# Demande et validation du VMID
local vmid
while true; do
read -rp "Entrez le numéro du conteneur (VMID, 100-999999999): " vmid
if ! validate_vmid "$vmid"; then
echo -e "${RED}Erreur: VMID invalide (doit être entre 100 et 999999999)${NC}"
continue
fi
if container_exists "$vmid"; then
echo -e "${RED}Erreur: Le VMID $vmid existe déjà${NC}"
continue
fi
break
done
# Nom du conteneur avec validation
local hostname
while true; do
read -rp "Nom du conteneur (ex: web-server): " hostname
if [[ -z "$hostname" ]]; then
echo -e "${RED}Erreur: Le nom ne peut pas être vide${NC}"
continue
fi
if ! validate_hostname "$hostname"; then
echo -e "${RED}Erreur: Nom invalide (lettres, chiffres, tirets uniquement)${NC}"
continue
fi
break
done
# Mot de passe root avec validation
local password password_confirm
while true; do
read -rsp "Mot de passe root (min 8 caractères): " password
echo ""
if [[ ${#password} -lt 8 ]]; then
echo -e "${RED}Erreur: Le mot de passe doit contenir au moins 8 caractères${NC}"
continue
fi
read -rsp "Confirmez le mot de passe: " password_confirm
echo ""
if [[ "$password" != "$password_confirm" ]]; then
echo -e "${RED}Erreur: Les mots de passe ne correspondent pas${NC}"
continue
fi
break
done
# Stockage disponible
echo -e "\n${BLUE}Stockages disponibles:${NC}"
pvesm status | grep -E "^(local|local-lvm)" || pvesm status
echo ""
local storage
read -rp "Stockage pour le conteneur (ex: local-lvm): " storage
if [[ -z "$storage" ]]; then
error_exit "Le stockage ne peut pas être vide"
return 1
fi
# Vérifier que le stockage existe
if ! pvesm status | grep -q "^${storage}"; then
error_exit "Le stockage '$storage' n'existe pas"
return 1
fi
# Template disponible
echo -e "\n${BLUE}Templates disponibles:${NC}"
pveam available | grep -i "system" | head -10
echo ""
local template
read -rp "Template à utiliser (ex: debian-12-standard): " template
if [[ -z "$template" ]]; then
error_exit "Le template ne peut pas être vide"
return 1
fi
# Si le template n'est pas téléchargé
if ! pveam list "$storage" | grep -q "$template"; then
echo -e "${YELLOW}Téléchargement du template...${NC}"
pveam download "$storage" "$template" || {
error_exit "Échec du téléchargement du template"
return 1
}
fi
# Ressources avec validation
local memory
while true; do
read -rp "RAM en MB (défaut: 512): " memory
memory=${memory:-512}
if validate_positive_number "$memory"; then
break
fi
echo -e "${RED}Erreur: Valeur invalide${NC}"
done
local swap
while true; do
read -rp "Swap en MB (défaut: 512): " swap
swap=${swap:-512}
if validate_positive_number "$swap"; then
break
fi
echo -e "${RED}Erreur: Valeur invalide${NC}"
done
local disk
while true; do
read -rp "Espace disque en GB (défaut: 8): " disk
disk=${disk:-8}
if validate_positive_number "$disk"; then
break
fi
echo -e "${RED}Erreur: Valeur invalide${NC}"
done
local cores
while true; do
read -rp "Nombre de CPU cores (défaut: 1): " cores
cores=${cores:-1}
if validate_positive_number "$cores"; then
break
fi
echo -e "${RED}Erreur: Valeur invalide${NC}"
done
# Bridge réseau avec validation
local bridge
read -rp "Bridge réseau (défaut: vmbr0): " bridge
bridge=${bridge:-vmbr0}
if [[ ! "$bridge" =~ ^vmbr[0-9]+$ ]]; then
error_exit "Bridge invalide (format: vmbr0, vmbr1, etc.)"
return 1
fi
# Configuration IP
echo -e "\n${BLUE}Configuration réseau:${NC}"
echo "1) DHCP"
echo "2) IP statique"
local net_choice
read -rp "Choix: " net_choice
local net_config
if [[ "$net_choice" == "2" ]]; then
local ip gateway
while true; do
read -rp "Adresse IP/CIDR (ex: 192.168.1.100/24): " ip
if validate_ip "$ip"; then
break
fi
echo -e "${RED}Erreur: Format IP invalide${NC}"
done
read -rp "Passerelle: " gateway
net_config="name=eth0,bridge=${bridge},ip=${ip},gw=${gateway}"
else
net_config="name=eth0,bridge=${bridge},ip=dhcp"
fi
# Démarrage automatique
local autostart onboot
read -rp "Démarrage automatique? (o/n, défaut: n): " autostart
onboot="0"
[[ "$autostart" == "o" ]] && onboot="1"
# Création du conteneur
echo -e "\n${YELLOW}Création du conteneur en cours...${NC}"
if pct create "$vmid" "${storage}:vztmpl/${template}" \
--hostname "$hostname" \
--password "$password" \
--memory "$memory" \
--swap "$swap" \
--rootfs "${storage}:${disk}" \
--cores "$cores" \
--net0 "$net_config" \
--onboot "$onboot" \
--unprivileged 1; then
echo -e "${GREEN}✓ Conteneur $vmid créé avec succès!${NC}"
local start
read -rp "Démarrer le conteneur maintenant? (o/n): " start
if [[ "$start" == "o" ]]; then
pct start "$vmid" && echo -e "${GREEN}✓ Conteneur démarré${NC}"
fi
else
error_exit "Échec de la création du conteneur"
fi
read -rp "Appuyez sur Entrée pour continuer..."
}
# Fonction pour démarrer un conteneur
start_container() {
show_logo
list_containers
local vmid
read -rp "Numéro du conteneur à démarrer: " vmid
if ! validate_vmid "$vmid"; then
error_exit "VMID invalide"
return 1
fi
if ! container_exists "$vmid"; then
error_exit "Le conteneur $vmid n'existe pas"
return 1
fi
echo -e "${YELLOW}Démarrage du conteneur $vmid...${NC}"
if pct start "$vmid"; then
echo -e "${GREEN}✓ Conteneur $vmid démarré avec succès${NC}"
else
error_exit "Échec du démarrage"
fi
read -rp "Appuyez sur Entrée pour continuer..."
}
# Fonction pour arrêter un conteneur
stop_container() {
show_logo
list_containers
local vmid
read -rp "Numéro du conteneur à arrêter: " vmid
if ! validate_vmid "$vmid"; then
error_exit "VMID invalide"
return 1
fi
if ! container_exists "$vmid"; then
error_exit "Le conteneur $vmid n'existe pas"
return 1
fi
echo "1) Arrêt normal (shutdown)"
echo "2) Arrêt forcé (stop)"
local choice
read -rp "Choix: " choice
if [[ "$choice" == "1" ]]; then
echo -e "${YELLOW}Arrêt normal du conteneur $vmid...${NC}"
pct shutdown "$vmid" || error_exit "Échec de l'arrêt"
else
echo -e "${YELLOW}Arrêt forcé du conteneur $vmid...${NC}"
pct stop "$vmid" || error_exit "Échec de l'arrêt forcé"
fi
echo -e "${GREEN}✓ Conteneur $vmid arrêté${NC}"
read -rp "Appuyez sur Entrée pour continuer..."
}
# Fonction pour redémarrer un conteneur
reboot_container() {
show_logo
list_containers
local vmid
read -rp "Numéro du conteneur à redémarrer: " vmid
if ! validate_vmid "$vmid"; then
error_exit "VMID invalide"
return 1
fi
if ! container_exists "$vmid"; then
error_exit "Le conteneur $vmid n'existe pas"
return 1
fi
echo -e "${YELLOW}Redémarrage du conteneur $vmid...${NC}"
if pct reboot "$vmid"; then
echo -e "${GREEN}✓ Conteneur $vmid redémarré${NC}"
else
error_exit "Échec du redémarrage"
fi
read -rp "Appuyez sur Entrée pour continuer..."
}
# Fonction pour supprimer un conteneur
delete_container() {
show_logo
list_containers
local vmid
read -rp "Numéro du conteneur à supprimer: " vmid
if ! validate_vmid "$vmid"; then
error_exit "VMID invalide"
return 1
fi
if ! container_exists "$vmid"; then
error_exit "Le conteneur $vmid n'existe pas"
return 1
fi
echo -e "${RED}⚠ ATTENTION: Cette action est irréversible!${NC}"
local confirm
read -rp "Tapez exactement 'SUPPRIMER' pour confirmer: " confirm
if [[ "$confirm" != "SUPPRIMER" ]]; then
echo -e "${YELLOW}Suppression annulée${NC}"
read -rp "Appuyez sur Entrée pour continuer..."
return
fi
# Vérifier si le conteneur est en cours d'exécution
if pct status "$vmid" | grep -q "running"; then
echo -e "${YELLOW}Arrêt du conteneur en cours...${NC}"
pct stop "$vmid" || true
sleep 2
fi
echo -e "${YELLOW}Suppression du conteneur $vmid...${NC}"
if pct destroy "$vmid" --purge; then
echo -e "${GREEN}✓ Conteneur $vmid supprimé avec succès${NC}"
else
error_exit "Échec de la suppression"
fi
read -rp "Appuyez sur Entrée pour continuer..."
}
# Fonction pour déverrouiller un conteneur
unlock_container() {
show_logo
list_containers
local vmid
read -rp "Numéro du conteneur à déverrouiller: " vmid
if ! validate_vmid "$vmid"; then
error_exit "VMID invalide"
return 1
fi
if ! container_exists "$vmid"; then
error_exit "Le conteneur $vmid n'existe pas"
return 1
fi
echo -e "${YELLOW}Déverrouillage du conteneur $vmid...${NC}"
if pct unlock "$vmid"; then
echo -e "${GREEN}✓ Conteneur $vmid déverrouillé${NC}"
else
error_exit "Échec du déverrouillage"
fi
read -rp "Appuyez sur Entrée pour continuer..."
}
# Fonction pour backup local
backup_local() {
show_logo
list_containers
local vmid
read -rp "Numéro du conteneur à sauvegarder: " vmid
if ! validate_vmid "$vmid"; then
error_exit "VMID invalide"
return 1
fi
if ! container_exists "$vmid"; then
error_exit "Le conteneur $vmid n'existe pas"
return 1
fi
# Stockage disponible pour backup
echo -e "\n${BLUE}Stockages disponibles:${NC}"
pvesm status | grep -v "^Disk"
echo ""
local dumpdir
read -rp "Stockage pour la sauvegarde (défaut: local): " dumpdir
dumpdir=${dumpdir:-local}
# Vérifier que le stockage existe
if ! pvesm status | grep -q "^${dumpdir}"; then
error_exit "Le stockage '$dumpdir' n'existe pas"
return 1
fi
# Type de compression
echo -e "\n${BLUE}Type de compression:${NC}"
echo "1) gzip (rapide, compression moyenne)"
echo "2) lzo (très rapide, faible compression)"
echo "3) zstd (bon compromis vitesse/compression)"
local compress_choice compress
read -rp "Choix (défaut: 1): " compress_choice
case $compress_choice in
2) compress="lzo";;
3) compress="zstd";;
*) compress="gzip";;
esac
# Mode de backup
echo -e "\n${BLUE}Mode de sauvegarde:${NC}"
echo "1) Snapshot (plus rapide, nécessite ZFS/LVM)"
echo "2) Stop (arrêt du conteneur)"
echo "3) Suspend (suspension temporaire)"
local mode_choice mode
read -rp "Choix (défaut: 1): " mode_choice
case $mode_choice in
2) mode="stop";;
3) mode="suspend";;
*) mode="snapshot";;
esac
echo -e "\n${YELLOW}Création de la sauvegarde...${NC}"
if vzdump "$vmid" --storage "$dumpdir" --mode "$mode" --compress "$compress"; then
echo -e "${GREEN}✓ Sauvegarde créée avec succès${NC}"
echo -e "${BLUE}Emplacement: $dumpdir${NC}"
else
error_exit "Échec de la sauvegarde"
fi
read -rp "Appuyez sur Entrée pour continuer..."
}
# Fonction pour restaurer un backup
restore_backup() {
show_logo
echo -e "${GREEN}Restauration d'une sauvegarde${NC}\n"
# Lister les sauvegardes disponibles
echo -e "${BLUE}Sauvegardes disponibles:${NC}"
pvesm list local | grep "vzdump" || echo "Aucune sauvegarde trouvée dans 'local'"
echo ""
local backup_file
read -rp "Nom complet du fichier de sauvegarde: " backup_file
if [[ -z "$backup_file" ]]; then
error_exit "Le nom du fichier ne peut pas être vide"
return 1
fi
local new_vmid
while true; do
read -rp "Nouveau VMID pour la restauration: " new_vmid
if ! validate_vmid "$new_vmid"; then
echo -e "${RED}VMID invalide${NC}"
continue
fi
if container_exists "$new_vmid"; then
echo -e "${RED}Erreur: Le VMID $new_vmid existe déjà${NC}"
continue
fi
break
done
local storage
read -rp "Stockage pour le conteneur (défaut: local-lvm): " storage
storage=${storage:-local-lvm}
# Vérifier que le stockage existe
if ! pvesm status | grep -q "^${storage}"; then
error_exit "Le stockage '$storage' n'existe pas"
return 1
fi
echo -e "\n${YELLOW}Restauration en cours...${NC}"
if pct restore "$new_vmid" "local:backup/${backup_file}" --storage "$storage"; then
echo -e "${GREEN}✓ Sauvegarde restaurée avec succès sur VMID $new_vmid${NC}"
else
error_exit "Échec de la restauration"
fi
read -rp "Appuyez sur Entrée pour continuer..."
}
# Fonction pour afficher les informations d'un conteneur
show_container_info() {
show_logo
list_containers
local vmid
read -rp "Numéro du conteneur: " vmid
if ! validate_vmid "$vmid"; then
error_exit "VMID invalide"
return 1
fi
if ! container_exists "$vmid"; then
error_exit "Le conteneur $vmid n'existe pas"
return 1
fi
echo -e "\n${BLUE}═══════════════════════════════════════════════${NC}"
echo -e "${GREEN}Informations du conteneur $vmid:${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════${NC}\n"
echo -e "${YELLOW}Statut:${NC}"
pct status "$vmid" || true
echo ""
echo -e "${YELLOW}Configuration:${NC}"
pct config "$vmid" || true
echo ""
echo -e "${YELLOW}Utilisation des ressources:${NC}"
pct df "$vmid" 2>/dev/null || echo "Conteneur arrêté"
echo ""
read -rp "Appuyez sur Entrée pour continuer..."
}
# Fonction pour entrer dans un conteneur
enter_container() {
show_logo
list_containers
local vmid
read -rp "Numéro du conteneur: " vmid
if ! validate_vmid "$vmid"; then
error_exit "VMID invalide"
return 1
fi
if ! container_exists "$vmid"; then
error_exit "Le conteneur $vmid n'existe pas"
return 1
fi
if ! pct status "$vmid" | grep -q "running"; then
error_exit "Le conteneur doit être démarré"
return 1
fi
echo -e "${GREEN}Connexion au conteneur $vmid...${NC}"
echo -e "${YELLOW}(Tapez 'exit' pour quitter)${NC}\n"
pct enter "$vmid" || error_exit "Échec de la connexion"
}
# Fonction pour cloner un conteneur
clone_container() {
show_logo
list_containers
local source_vmid
read -rp "Numéro du conteneur source: " source_vmid
if ! validate_vmid "$source_vmid"; then
error_exit "VMID source invalide"
return 1
fi
if ! container_exists "$source_vmid"; then
error_exit "Le conteneur $source_vmid n'existe pas"
return 1
fi
local new_vmid
while true; do
read -rp "Numéro du nouveau conteneur: " new_vmid
if ! validate_vmid "$new_vmid"; then
echo -e "${RED}VMID invalide${NC}"
continue
fi
if container_exists "$new_vmid"; then
echo -e "${RED}Erreur: Le VMID $new_vmid existe déjà${NC}"
continue
fi
break
done
local new_hostname
while true; do
read -rp "Nom du nouveau conteneur: " new_hostname
if [[ -z "$new_hostname" ]]; then
echo -e "${RED}Le nom ne peut pas être vide${NC}"
continue
fi
if ! validate_hostname "$new_hostname"; then
echo -e "${RED}Nom invalide${NC}"
continue
fi
break
done
echo -e "\n${YELLOW}Clonage en cours...${NC}"
if pct clone "$source_vmid" "$new_vmid" --hostname "$new_hostname"; then
echo -e "${GREEN}✓ Conteneur cloné avec succès${NC}"
else
error_exit "Échec du clonage"
fi
read -rp "Appuyez sur Entrée pour continuer..."
}
# Menu principal
main_menu() {
while true; do
show_logo
echo -e "${GREEN}Menu Principal:${NC}\n"
echo "1) Lister les conteneurs"
echo "2) Créer un conteneur"
echo "3) Démarrer un conteneur"
echo "4) Arrêter un conteneur"
echo "5) Redémarrer un conteneur"
echo "6) Supprimer un conteneur"
echo "7) Déverrouiller un conteneur"
echo "8) Sauvegarder un conteneur"
echo "9) Restaurer une sauvegarde"
echo "10) Afficher les informations"
echo "11) Entrer dans un conteneur"
echo "12) Cloner un conteneur"
echo "0) Quitter"
echo ""
local choice
read -rp "Votre choix: " choice
case $choice in
1) show_logo; list_containers; read -rp "Appuyez sur Entrée pour continuer...";;
2) create_container || true;;
3) start_container || true;;
4) stop_container || true;;
5) reboot_container || true;;
6) delete_container || true;;
7) unlock_container || true;;
8) backup_local || true;;
9) restore_backup || true;;
10) show_container_info || true;;
11) enter_container || true;;
12) clone_container || true;;
0) echo -e "${GREEN}Au revoir!${NC}"; exit 0;;
*) echo -e "${RED}Choix invalide${NC}"; sleep 2;;
esac
done
}
# Point d'entrée principal
main() {
check_proxmox
main_menu
}
# Lancement du script
main