lxc-manager/lxc-manager.sh

1143 lines
35 KiB
Bash

#!/bin/bash
#############################################
# Script de Gestion LXC pour Proxmox VE
# Auteur: Généré par Claude
# 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 MAGENTA='\033[0;35m'
readonly NC='\033[0m' # No Color
# Variable globale pour la protection
PROTECTION_ENABLED=true
# 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 vérifier si un conteneur a la protection activée
check_container_protection() {
local vmid="$1"
local protection_status
protection_status=$(pct config "$vmid" | grep "^protection:" | awk '{print $2}')
if [[ "$protection_status" == "1" ]]; then
return 0 # Protection activée
else
return 1 # Protection désactivée
fi
}
# Fonction pour activer/désactiver la protection d'un conteneur
toggle_protection() {
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}État actuel de la protection:${NC}"
if check_container_protection "$vmid"; then
echo -e "${GREEN}✓ Protection activée${NC}"
echo ""
read -rp "Désactiver la protection? (o/n): " choice
if [[ "$choice" == "o" ]]; then
pct set "$vmid" --protection 0
echo -e "${YELLOW}✓ Protection désactivée pour le conteneur $vmid${NC}"
fi
else
echo -e "${YELLOW}✗ Protection désactivée${NC}"
echo ""
read -rp "Activer la protection? (o/n): " choice
if [[ "$choice" == "o" ]]; then
pct set "$vmid" --protection 1
echo -e "${GREEN}✓ Protection activée pour le conteneur $vmid${NC}"
fi
fi
read -rp "Appuyez sur Entrée pour continuer..."
}
# Fonction pour afficher le logo
show_logo() {
clear
echo -e "${BLUE}"
echo "╔═══════════════════════════════════════════════╗"
echo "║ GESTIONNAIRE LXC PROXMOX VE ║"
echo "║ Version 2.1 - Sécurisé ║"
echo "╚═══════════════════════════════════════════════╝"
echo -e "${NC}"
if [[ "$PROTECTION_ENABLED" == true ]]; then
echo -e "${GREEN}🔒 Mode protection: ACTIVÉ${NC}\n"
else
echo -e "${YELLOW}🔓 Mode protection: DÉSACTIVÉ${NC}\n"
fi
}
# 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}"
mapfile -t storages_list < <(pvesm status | awk 'NR>1 {print $1}')
if [[ ${#storages_list[@]} -eq 0 ]]; then
error_exit "Aucun stockage disponible"
return 1
fi
for i in "${!storages_list[@]}"; do
echo "$((i+1))) ${storages_list[$i]}"
done
echo ""
local storage_choice
read -rp "Choisir le stockage pour le conteneur (défaut: 1): " storage_choice
storage_choice=${storage_choice:-1}
if [[ ! "$storage_choice" =~ ^[0-9]+$ ]] || [[ $storage_choice -lt 1 ]] || [[ $storage_choice -gt ${#storages_list[@]} ]]; then
error_exit "Choix invalide"
return 1
fi
local storage="${storages_list[$((storage_choice-1))]}"
# 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"
# Protection
local protection
read -rp "Activer la protection contre la suppression? (o/n, défaut: n): " protection
local protection_flag=""
[[ "$protection" == "o" ]] && protection_flag="--protection 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 \
$protection_flag; 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
# Vérifier si la protection est activée
if check_container_protection "$vmid"; then
echo -e "${RED}⚠ ATTENTION: Ce conteneur a la protection activée!${NC}"
echo -e "${YELLOW}La protection empêche la suppression accidentelle.${NC}\n"
read -rp "Désactiver la protection et continuer? (o/n): " disable_protection
if [[ "$disable_protection" == "o" ]]; then
pct set "$vmid" --protection 0
echo -e "${YELLOW}✓ Protection désactivée${NC}\n"
else
echo -e "${YELLOW}Suppression annulée${NC}"
read -rp "Appuyez sur Entrée pour continuer..."
return
fi
fi
echo -e "${RED}⚠ ATTENTION: Cette action est irréversible!${NC}"
local confirm
if [[ "$PROTECTION_ENABLED" == true ]]; then
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
else
read -rp "Confirmer la suppression? (o/n): " confirm
if [[ "$confirm" != "o" ]]; then
echo -e "${YELLOW}Suppression annulée${NC}"
read -rp "Appuyez sur Entrée pour continuer..."
return
fi
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 pour les sauvegardes:${NC}"
# Filtrer uniquement les stockages avec content type "backup" ou "vztmpl,backup"
mapfile -t storages_backup < <(pvesm status -content backup 2>/dev/null | awk 'NR>1 {print $1}')
if [[ ${#storages_backup[@]} -eq 0 ]]; then
# Fallback: essayer de lister tous les stockages actifs
echo -e "${YELLOW}Aucun stockage spécifique pour backup trouvé, affichage de tous les stockages...${NC}"
mapfile -t storages_backup < <(pvesm status | awk 'NR>1 && $2=="active" {print $1}')
if [[ ${#storages_backup[@]} -eq 0 ]]; then
error_exit "Aucun stockage disponible"
return 1
fi
fi
for i in "${!storages_backup[@]}"; do
# Afficher les informations du stockage
local storage_info=$(pvesm status | grep "^${storages_backup[$i]}" | awk '{print $1" ("$3")"}')
echo "$((i+1))) $storage_info"
done
echo ""
echo -e "${YELLOW}Note: Seuls les stockages configurés pour les backups sont affichés${NC}"
echo ""
local dumpdir_choice
read -rp "Choisir le stockage pour la sauvegarde (défaut: 1): " dumpdir_choice
dumpdir_choice=${dumpdir_choice:-1}
if [[ ! "$dumpdir_choice" =~ ^[0-9]+$ ]] || [[ $dumpdir_choice -lt 1 ]] || [[ $dumpdir_choice -gt ${#storages_backup[@]} ]]; then
error_exit "Choix invalide"
return 1
fi
local dumpdir="${storages_backup[$((dumpdir_choice-1))]}"
# Vérifier que le stockage accepte bien les backups
echo -e "${BLUE}Vérification du stockage...${NC}"
if ! pvesm status -content backup 2>/dev/null | grep -q "^${dumpdir}"; then
echo -e "${YELLOW}⚠ Avertissement: Ce stockage pourrait ne pas être configuré pour les backups${NC}"
read -rp "Continuer quand même? (o/n): " continue_backup
if [[ "$continue_backup" != "o" ]]; then
echo -e "${YELLOW}Sauvegarde annulée${NC}"
read -rp "Appuyez sur Entrée pour continuer..."
return
fi
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 tous les stockages disponibles
echo -e "${BLUE}Stockages disponibles:${NC}"
mapfile -t storages < <(pvesm status | awk 'NR>1 {print $1}')
if [[ ${#storages[@]} -eq 0 ]]; then
error_exit "Aucun stockage disponible"
return 1
fi
for i in "${!storages[@]}"; do
echo "$((i+1))) ${storages[$i]}"
done
echo ""
# Sélection du stockage source
local storage_choice
read -rp "Choisir le stockage contenant les sauvegardes (défaut: 1): " storage_choice
storage_choice=${storage_choice:-1}
if [[ ! "$storage_choice" =~ ^[0-9]+$ ]] || [[ $storage_choice -lt 1 ]] || [[ $storage_choice -gt ${#storages[@]} ]]; then
error_exit "Choix invalide"
return 1
fi
local selected_storage="${storages[$((storage_choice-1))]}"
# Lister les sauvegardes disponibles sur ce stockage
echo -e "\n${BLUE}Sauvegardes disponibles sur '$selected_storage':${NC}"
mapfile -t backups < <(pvesm list "$selected_storage" | grep "vzdump" | awk '{print $1}' | sed "s/^${selected_storage}://")
if [[ ${#backups[@]} -eq 0 ]]; then
error_exit "Aucune sauvegarde trouvée sur '$selected_storage'"
return 1
fi
# Afficher les sauvegardes avec numéros
for i in "${!backups[@]}"; do
local backup_name="${backups[$i]}"
# Extraire les informations du nom de fichier (sans le préfixe storage:)
local filename=$(basename "$backup_name")
local vmid_backup=$(echo "$filename" | grep -oP 'vzdump-lxc-\K[0-9]+' || echo "N/A")
local date_backup=$(echo "$filename" | grep -oP '\d{4}_\d{2}_\d{2}-\d{2}_\d{2}_\d{2}' || echo "")
echo -e "$((i+1))) ${MAGENTA}VMID:${NC} $vmid_backup ${MAGENTA}Date:${NC} $date_backup"
echo -e " ${BLUE}Fichier:${NC} $filename"
echo ""
done
# Sélection de la sauvegarde
local backup_choice
read -rp "Choisir la sauvegarde à restaurer (1-${#backups[@]}): " backup_choice
if [[ ! "$backup_choice" =~ ^[0-9]+$ ]] || [[ $backup_choice -lt 1 ]] || [[ $backup_choice -gt ${#backups[@]} ]]; then
error_exit "Choix invalide"
return 1
fi
local selected_backup="${backups[$((backup_choice-1))]}"
echo -e "\n${GREEN}Sauvegarde sélectionnée:${NC} $selected_backup"
# Nouveau VMID
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
# Stockage pour le conteneur restauré
echo -e "\n${BLUE}Stockages disponibles pour la restauration:${NC}"
for i in "${!storages[@]}"; do
echo "$((i+1))) ${storages[$i]}"
done
echo ""
local restore_storage_choice
read -rp "Choisir le stockage de destination (défaut: 1): " restore_storage_choice
restore_storage_choice=${restore_storage_choice:-1}
if [[ ! "$restore_storage_choice" =~ ^[0-9]+$ ]] || [[ $restore_storage_choice -lt 1 ]] || [[ $restore_storage_choice -gt ${#storages[@]} ]]; then
error_exit "Choix invalide"
return 1
fi
local restore_storage="${storages[$((restore_storage_choice-1))]}"
echo -e "\n${YELLOW}Restauration en cours...${NC}"
echo -e "${BLUE}Source:${NC} ${selected_storage}:${selected_backup}"
echo -e "${BLUE}Destination:${NC} VMID $new_vmid sur $restore_storage"
echo ""
if pct restore "$new_vmid" "${selected_storage}:${selected_backup}" --storage "$restore_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}Protection:${NC}"
if check_container_protection "$vmid"; then
echo -e "${GREEN}✓ Activée${NC}"
else
echo -e "${YELLOW}✗ Désactivée${NC}"
fi
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
# Vérifier si le conteneur est en cours d'exécution
local is_running=false
if pct status "$source_vmid" | grep -q "running"; then
is_running=true
echo -e "\n${YELLOW}⚠ Le conteneur source est en cours d'exécution${NC}"
echo -e "${BLUE}Options disponibles:${NC}"
echo "1) Arrêter le conteneur avant le clonage (recommandé)"
echo "2) Créer un snapshot et cloner (plus rapide, nécessite ZFS/LVM)"
echo "3) Annuler"
local clone_choice
read -rp "Choix: " clone_choice
case $clone_choice in
1)
echo -e "\n${YELLOW}Arrêt du conteneur...${NC}"
if ! pct shutdown "$source_vmid"; then
echo -e "${YELLOW}Arrêt normal échoué, arrêt forcé...${NC}"
pct stop "$source_vmid" || {
error_exit "Impossible d'arrêter le conteneur"
return 1
}
fi
sleep 2
;;
2)
echo -e "\n${YELLOW}Le clonage avec snapshot sera tenté...${NC}"
;;
3)
echo -e "${YELLOW}Clonage annulé${NC}"
read -rp "Appuyez sur Entrée pour continuer..."
return
;;
*)
error_exit "Choix invalide"
return 1
;;
esac
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
# Stockage pour le clone
echo -e "\n${BLUE}Stockages disponibles pour le clone:${NC}"
mapfile -t storages_clone < <(pvesm status | awk 'NR>1 {print $1}')
if [[ ${#storages_clone[@]} -eq 0 ]]; then
error_exit "Aucun stockage disponible"
if [[ "$is_running" == true && "$clone_choice" == "1" ]]; then
pct start "$source_vmid" || true
fi
return 1
fi
for i in "${!storages_clone[@]}"; do
echo "$((i+1))) ${storages_clone[$i]}"
done
echo "0) Même stockage que la source"
echo ""
local target_storage_choice
read -rp "Choix (défaut: 0): " target_storage_choice
target_storage_choice=${target_storage_choice:-0}
local storage_param=""
if [[ "$target_storage_choice" != "0" ]]; then
if [[ ! "$target_storage_choice" =~ ^[0-9]+$ ]] || [[ $target_storage_choice -lt 1 ]] || [[ $target_storage_choice -gt ${#storages_clone[@]} ]]; then
error_exit "Choix invalide"
if [[ "$is_running" == true && "$clone_choice" == "1" ]]; then
pct start "$source_vmid" || true
fi
return 1
fi
local target_storage="${storages_clone[$((target_storage_choice-1))]}"
storage_param="--storage $target_storage"
fi
echo -e "\n${YELLOW}Clonage en cours...${NC}"
# Tenter le clonage avec snapshot si le conteneur est en cours d'exécution
local clone_cmd="pct clone $source_vmid $new_vmid --hostname $new_hostname"
if [[ -n "$storage_param" ]]; then
clone_cmd="$clone_cmd $storage_param"
fi
# Si le conteneur est en cours d'exécution et qu'on a choisi l'option snapshot
if [[ "$is_running" == true && "$clone_choice" == "2" ]]; then
clone_cmd="$clone_cmd --snapname clone_snapshot"
fi
if eval "$clone_cmd"; then
echo -e "${GREEN}✓ Conteneur cloné avec succès${NC}"
# Redémarrer le conteneur source si on l'avait arrêté
if [[ "$is_running" == true && "$clone_choice" == "1" ]]; then
echo -e "\n${YELLOW}Redémarrage du conteneur source...${NC}"
pct start "$source_vmid" && echo -e "${GREEN}✓ Conteneur source redémarré${NC}"
fi
else
echo -e "${RED}✗ Échec du clonage${NC}"
# Redémarrer le conteneur source si on l'avait arrêté
if [[ "$is_running" == true && "$clone_choice" == "1" ]]; then
echo -e "\n${YELLOW}Redémarrage du conteneur source...${NC}"
pct start "$source_vmid" && echo -e "${GREEN}✓ Conteneur source redémarré${NC}"
fi
error_exit "Le clonage a échoué"
fi
read -rp "Appuyez sur Entrée pour continuer..."
}
# Fonction pour basculer le mode protection global
toggle_global_protection() {
show_logo
if [[ "$PROTECTION_ENABLED" == true ]]; then
echo -e "${GREEN}Mode protection actuellement: ACTIVÉ${NC}"
echo -e "\n${YELLOW}Les confirmations de sécurité sont requises pour les opérations critiques.${NC}"
echo ""
read -rp "Désactiver le mode protection? (o/n): " choice
if [[ "$choice" == "o" ]]; then
PROTECTION_ENABLED=false
echo -e "${YELLOW}✓ Mode protection DÉSACTIVÉ${NC}"
echo -e "${RED}⚠ Attention: Les opérations destructives ne nécessiteront plus de confirmation renforcée${NC}"
fi
else
echo -e "${YELLOW}Mode protection actuellement: DÉSACTIVÉ${NC}"
echo -e "\n${RED}⚠ Les opérations critiques peuvent être exécutées sans confirmation renforcée.${NC}"
echo ""
read -rp "Activer le mode protection? (o/n): " choice
if [[ "$choice" == "o" ]]; then
PROTECTION_ENABLED=true
echo -e "${GREEN}✓ Mode protection ACTIVÉ${NC}"
echo -e "${GREEN}Les opérations critiques nécessiteront des confirmations de sécurité${NC}"
fi
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 -e "13) ${MAGENTA}Gérer la protection d'un conteneur${NC}"
if [[ "$PROTECTION_ENABLED" == true ]]; then
echo -e "14) ${MAGENTA}Mode protection global (ACTIVÉ)${NC}"
else
echo -e "14) ${MAGENTA}Mode protection global (DÉSACTIVÉ)${NC}"
fi
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;;
13) toggle_protection || true;;
14) toggle_global_protection || 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