#!/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