1539 lines
49 KiB
Bash
1539 lines
49 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 pour le rootfs
|
|
echo -e "\n${BLUE}Stockages disponibles pour le conteneur:${NC}"
|
|
mapfile -t storages_rootfs < <(pvesm status | awk 'NR>1 {print $1}')
|
|
|
|
if [[ ${#storages_rootfs[@]} -eq 0 ]]; then
|
|
error_exit "Aucun stockage disponible"
|
|
return 1
|
|
fi
|
|
|
|
for i in "${!storages_rootfs[@]}"; do
|
|
local storage_info=$(pvesm status | grep "^${storages_rootfs[$i]}" | awk '{print $1" ("$3")"}')
|
|
echo "$((i+1))) $storage_info"
|
|
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_rootfs[@]} ]]; then
|
|
error_exit "Choix invalide"
|
|
return 1
|
|
fi
|
|
|
|
local storage="${storages_rootfs[$((storage_choice-1))]}"
|
|
|
|
# Trouver un stockage pour les templates (doit supporter les templates)
|
|
local template_storage="local"
|
|
mapfile -t template_storages < <(pvesm status -content vztmpl 2>/dev/null | awk 'NR>1 {print $1}')
|
|
|
|
if [[ ${#template_storages[@]} -gt 0 ]]; then
|
|
template_storage="${template_storages[0]}"
|
|
fi
|
|
|
|
# Template disponible avec numérotation
|
|
echo -e "\n${BLUE}Templates disponibles sur '$template_storage':${NC}"
|
|
mapfile -t templates < <(pveam available | grep -i "system" | awk '{print $2}' | head -20)
|
|
|
|
if [[ ${#templates[@]} -eq 0 ]]; then
|
|
error_exit "Aucun template disponible"
|
|
return 1
|
|
fi
|
|
|
|
for i in "${!templates[@]}"; do
|
|
local template_name="${templates[$i]}"
|
|
# Extraire l'OS et la version
|
|
local os_name=$(echo "$template_name" | cut -d'-' -f1)
|
|
local os_version=$(echo "$template_name" | cut -d'-' -f2 | cut -d'_' -f1)
|
|
echo "$((i+1))) $os_name $os_version - $template_name"
|
|
done
|
|
echo ""
|
|
|
|
local template_choice
|
|
read -rp "Choisir le template (défaut: 1): " template_choice
|
|
template_choice=${template_choice:-1}
|
|
|
|
if [[ ! "$template_choice" =~ ^[0-9]+$ ]] || [[ $template_choice -lt 1 ]] || [[ $template_choice -gt ${#templates[@]} ]]; then
|
|
error_exit "Choix invalide"
|
|
return 1
|
|
fi
|
|
|
|
local template="${templates[$((template_choice-1))]}"
|
|
|
|
# Si le template n'est pas téléchargé
|
|
if ! pveam list "$template_storage" | grep -q "$template"; then
|
|
echo -e "${YELLOW}Téléchargement du template sur '$template_storage'...${NC}"
|
|
pveam download "$template_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
|
|
|
|
# Mode privilégié ou non
|
|
echo -e "\n${BLUE}Mode du conteneur:${NC}"
|
|
echo "1) Unprivileged (recommandé, plus sécurisé)"
|
|
echo "2) Privileged (accès root complet)"
|
|
|
|
local priv_choice unprivileged
|
|
read -rp "Choix (défaut: 1): " priv_choice
|
|
priv_choice=${priv_choice:-1}
|
|
|
|
if [[ "$priv_choice" == "2" ]]; then
|
|
unprivileged="0"
|
|
echo -e "${YELLOW}⚠ Mode privileged sélectionné${NC}"
|
|
else
|
|
unprivileged="1"
|
|
fi
|
|
|
|
# Fonctionnalités avancées (nesting, fuse, etc.)
|
|
echo -e "\n${BLUE}Fonctionnalités avancées:${NC}"
|
|
|
|
local features=""
|
|
|
|
read -rp "Activer nesting (Docker dans LXC)? (o/n, défaut: n): " enable_nesting
|
|
if [[ "$enable_nesting" == "o" ]]; then
|
|
features="${features}nesting=1,"
|
|
fi
|
|
|
|
read -rp "Activer FUSE (systèmes de fichiers utilisateur)? (o/n, défaut: n): " enable_fuse
|
|
if [[ "$enable_fuse" == "o" ]]; then
|
|
features="${features}fuse=1,"
|
|
fi
|
|
|
|
local mount_list=""
|
|
read -rp "Activer NFS (montage NFS)? (o/n, défaut: n): " enable_nfs
|
|
if [[ "$enable_nfs" == "o" ]]; then
|
|
mount_list="${mount_list}nfs;"
|
|
fi
|
|
|
|
read -rp "Activer SMB/CIFS (montage Windows)? (o/n, défaut: n): " enable_cifs
|
|
if [[ "$enable_cifs" == "o" ]]; then
|
|
mount_list="${mount_list}cifs;"
|
|
fi
|
|
|
|
# Retirer le séparateur final
|
|
mount_list="${mount_list%;}"
|
|
|
|
# Ajouter mount aux features si nécessaire
|
|
if [[ -n "$mount_list" ]]; then
|
|
features="${features}mount=${mount_list},"
|
|
fi
|
|
|
|
# Retirer la virgule finale
|
|
features="${features%,}"
|
|
|
|
local features_param=""
|
|
if [[ -n "$features" ]]; then
|
|
features_param="--features $features"
|
|
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" "${template_storage}:vztmpl/${template}" \
|
|
--hostname "$hostname" \
|
|
--password "$password" \
|
|
--memory "$memory" \
|
|
--swap "$swap" \
|
|
--rootfs "${storage}:${disk}" \
|
|
--cores "$cores" \
|
|
--net0 "$net_config" \
|
|
--onboot "$onboot" \
|
|
--unprivileged "$unprivileged" \
|
|
$features_param \
|
|
$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 modifier les options d'un conteneur
|
|
modify_container_options() {
|
|
show_logo
|
|
list_containers
|
|
|
|
local vmid
|
|
read -rp "Numéro du conteneur à modifier: " 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 le conteneur est arrêté
|
|
if pct status "$vmid" | grep -q "running"; then
|
|
echo -e "${YELLOW}⚠ Le conteneur est en cours d'exécution${NC}"
|
|
echo -e "${YELLOW}Certaines modifications nécessitent un redémarrage${NC}\n"
|
|
fi
|
|
|
|
# Afficher les options actuelles
|
|
echo -e "\n${BLUE}Configuration actuelle:${NC}"
|
|
local current_config=$(pct config "$vmid" | grep -E "^(features|mount):")
|
|
echo -e "${GREEN}${current_config:-Aucune fonctionnalité activée}${NC}"
|
|
echo ""
|
|
|
|
# Extraire les options actuelles depuis features
|
|
local current_features=$(pct config "$vmid" | grep "^features:" | cut -d' ' -f2-)
|
|
|
|
local current_nesting="0"
|
|
local current_fuse="0"
|
|
local current_nfs="0"
|
|
local current_cifs="0"
|
|
|
|
[[ "$current_features" =~ nesting=1 ]] && current_nesting="1"
|
|
[[ "$current_features" =~ fuse=1 ]] && current_fuse="1"
|
|
[[ "$current_features" =~ mount=.*nfs ]] && current_nfs="1"
|
|
[[ "$current_features" =~ mount=.*cifs ]] && current_cifs="1"
|
|
|
|
echo -e "${BLUE}Modification des fonctionnalités avancées:${NC}\n"
|
|
|
|
# Nesting
|
|
echo -e "Nesting (Docker dans LXC) - Actuellement: $([ "$current_nesting" == "1" ] && echo -e "${GREEN}activé${NC}" || echo -e "${YELLOW}désactivé${NC}")"
|
|
read -rp "Modifier? (o/activer, n/désactiver, Entrée/garder): " mod_nesting
|
|
case $mod_nesting in
|
|
o) current_nesting="1";;
|
|
n) current_nesting="0";;
|
|
esac
|
|
|
|
# FUSE
|
|
echo -e "FUSE (systèmes de fichiers utilisateur) - Actuellement: $([ "$current_fuse" == "1" ] && echo -e "${GREEN}activé${NC}" || echo -e "${YELLOW}désactivé${NC}")"
|
|
read -rp "Modifier? (o/activer, n/désactiver, Entrée/garder): " mod_fuse
|
|
case $mod_fuse in
|
|
o) current_fuse="1";;
|
|
n) current_fuse="0";;
|
|
esac
|
|
|
|
# NFS
|
|
echo -e "NFS (montage NFS) - Actuellement: $([ "$current_nfs" == "1" ] && echo -e "${GREEN}activé${NC}" || echo -e "${YELLOW}désactivé${NC}")"
|
|
read -rp "Modifier? (o/activer, n/désactiver, Entrée/garder): " mod_nfs
|
|
case $mod_nfs in
|
|
o) current_nfs="1";;
|
|
n) current_nfs="0";;
|
|
esac
|
|
|
|
# CIFS
|
|
echo -e "SMB/CIFS (montage Windows) - Actuellement: $([ "$current_cifs" == "1" ] && echo -e "${GREEN}activé${NC}" || echo -e "${YELLOW}désactivé${NC}")"
|
|
read -rp "Modifier? (o/activer, n/désactiver, Entrée/garder): " mod_cifs
|
|
case $mod_cifs in
|
|
o) current_cifs="1";;
|
|
n) current_cifs="0";;
|
|
esac
|
|
|
|
# Construire la chaîne de fonctionnalités complète
|
|
local new_features=""
|
|
|
|
# Ajouter nesting et fuse
|
|
[[ "$current_nesting" == "1" ]] && new_features="${new_features}nesting=1,"
|
|
[[ "$current_fuse" == "1" ]] && new_features="${new_features}fuse=1,"
|
|
|
|
# Ajouter mount si nécessaire
|
|
local mount_list=""
|
|
[[ "$current_nfs" == "1" ]] && mount_list="${mount_list}nfs;"
|
|
[[ "$current_cifs" == "1" ]] && mount_list="${mount_list}cifs;"
|
|
mount_list="${mount_list%;}"
|
|
|
|
if [[ -n "$mount_list" ]]; then
|
|
new_features="${new_features}mount=${mount_list},"
|
|
fi
|
|
|
|
# Retirer la virgule finale
|
|
new_features="${new_features%,}"
|
|
|
|
# Appliquer les modifications
|
|
echo -e "\n${YELLOW}Application des modifications...${NC}"
|
|
|
|
if [[ -n "$new_features" ]]; then
|
|
if pct set "$vmid" --features "$new_features"; then
|
|
echo -e "${GREEN}✓ Configuration mise à jour${NC}"
|
|
echo -e "${BLUE}Nouvelles options: $new_features${NC}"
|
|
else
|
|
error_exit "Échec de la mise à jour"
|
|
return 1
|
|
fi
|
|
else
|
|
if pct set "$vmid" --delete features 2>/dev/null; then
|
|
echo -e "${GREEN}✓ Toutes les fonctionnalités ont été désactivées${NC}"
|
|
else
|
|
echo -e "${YELLOW}Note: Aucune fonctionnalité à désactiver${NC}"
|
|
fi
|
|
fi
|
|
|
|
# Demander si un redémarrage est nécessaire
|
|
if pct status "$vmid" | grep -q "running"; then
|
|
echo -e "\n${YELLOW}Le conteneur doit être redémarré pour que les changements prennent effet${NC}"
|
|
read -rp "Redémarrer maintenant? (o/n): " restart_choice
|
|
if [[ "$restart_choice" == "o" ]]; then
|
|
pct reboot "$vmid" && echo -e "${GREEN}✓ Conteneur redémarré${NC}"
|
|
fi
|
|
fi
|
|
|
|
read -rp "Appuyez sur Entrée pour continuer..."
|
|
}
|
|
|
|
# Fonction pour supprimer des sauvegardes
|
|
delete_backups() {
|
|
show_logo
|
|
echo -e "${GREEN}Suppression de sauvegardes${NC}\n"
|
|
|
|
# Lister tous les conteneurs
|
|
list_containers
|
|
|
|
local vmid
|
|
read -rp "Numéro du conteneur (ou Entrée pour toutes les sauvegardes): " vmid
|
|
|
|
if [[ -n "$vmid" ]] && ! validate_vmid "$vmid"; then
|
|
error_exit "VMID invalide"
|
|
return 1
|
|
fi
|
|
|
|
# Lister tous les stockages disponibles
|
|
echo -e "\n${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
|
|
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
|
|
echo -e "\n${BLUE}Sauvegardes disponibles sur '$selected_storage':${NC}"
|
|
|
|
local filter=""
|
|
if [[ -n "$vmid" ]]; then
|
|
filter="vzdump-lxc-${vmid}-"
|
|
echo -e "${YELLOW}Filtrage pour le conteneur $vmid${NC}\n"
|
|
fi
|
|
|
|
mapfile -t backups < <(pvesm list "$selected_storage" | grep "vzdump" | grep "$filter" | awk '{print $1}' | sed "s/^${selected_storage}://")
|
|
|
|
if [[ ${#backups[@]} -eq 0 ]]; then
|
|
error_exit "Aucune sauvegarde trouvée"
|
|
return 1
|
|
fi
|
|
|
|
# Afficher les sauvegardes avec numéros
|
|
for i in "${!backups[@]}"; do
|
|
local backup_name="${backups[$i]}"
|
|
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 "")
|
|
local size_backup=$(pvesm list "$selected_storage" | grep "$filename" | awk '{print $3}')
|
|
|
|
echo -e "$((i+1))) ${MAGENTA}VMID:${NC} $vmid_backup ${MAGENTA}Date:${NC} $date_backup ${MAGENTA}Taille:${NC} $size_backup"
|
|
echo -e " ${BLUE}Fichier:${NC} $filename"
|
|
echo ""
|
|
done
|
|
|
|
# Sélection des sauvegardes à supprimer
|
|
echo -e "${YELLOW}Options de sélection:${NC}"
|
|
echo "- Un numéro: 3"
|
|
echo "- Plusieurs numéros séparés par des espaces: 1 3 5"
|
|
echo "- Une plage: 1-3"
|
|
echo "- Combinaison: 1 3-5 7"
|
|
echo "- 'all' pour tout supprimer"
|
|
echo ""
|
|
|
|
local selection
|
|
read -rp "Sélection: " selection
|
|
|
|
if [[ -z "$selection" ]]; then
|
|
echo -e "${YELLOW}Aucune sélection, opération annulée${NC}"
|
|
read -rp "Appuyez sur Entrée pour continuer..."
|
|
return
|
|
fi
|
|
|
|
# Parser la sélection
|
|
local -a selected_indices=()
|
|
|
|
if [[ "$selection" == "all" ]]; then
|
|
for i in "${!backups[@]}"; do
|
|
selected_indices+=($i)
|
|
done
|
|
else
|
|
for part in $selection; do
|
|
if [[ "$part" =~ ^([0-9]+)-([0-9]+)$ ]]; then
|
|
# Plage
|
|
local start=${BASH_REMATCH[1]}
|
|
local end=${BASH_REMATCH[2]}
|
|
for ((j=start; j<=end; j++)); do
|
|
if [[ $j -ge 1 ]] && [[ $j -le ${#backups[@]} ]]; then
|
|
selected_indices+=($((j-1)))
|
|
fi
|
|
done
|
|
elif [[ "$part" =~ ^[0-9]+$ ]]; then
|
|
# Numéro simple
|
|
if [[ $part -ge 1 ]] && [[ $part -le ${#backups[@]} ]]; then
|
|
selected_indices+=($((part-1)))
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [[ ${#selected_indices[@]} -eq 0 ]]; then
|
|
error_exit "Aucune sauvegarde valide sélectionnée"
|
|
return 1
|
|
fi
|
|
|
|
# Afficher les sauvegardes sélectionnées
|
|
echo -e "\n${RED}⚠ Sauvegardes sélectionnées pour suppression:${NC}"
|
|
local total_size=0
|
|
for idx in "${selected_indices[@]}"; do
|
|
local backup_name="${backups[$idx]}"
|
|
local filename=$(basename "$backup_name")
|
|
echo -e "${RED} - $filename${NC}"
|
|
done
|
|
echo ""
|
|
|
|
# Confirmation
|
|
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
|
|
|
|
# Suppression des sauvegardes
|
|
echo -e "\n${YELLOW}Suppression en cours...${NC}\n"
|
|
local success_count=0
|
|
local fail_count=0
|
|
|
|
for idx in "${selected_indices[@]}"; do
|
|
local backup_path="${backups[$idx]}"
|
|
local filename=$(basename "$backup_path")
|
|
|
|
echo -e "${BLUE}Suppression de: $filename${NC}"
|
|
|
|
if pvesm free "${selected_storage}:${backup_path}"; then
|
|
echo -e "${GREEN}✓ Supprimé${NC}\n"
|
|
((success_count++))
|
|
else
|
|
echo -e "${RED}✗ Échec${NC}\n"
|
|
((fail_count++))
|
|
fi
|
|
done
|
|
|
|
# Résumé
|
|
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
|
echo -e "${GREEN}✓ Supprimées avec succès: $success_count${NC}"
|
|
if [[ $fail_count -gt 0 ]]; then
|
|
echo -e "${RED}✗ Échecs: $fail_count${NC}"
|
|
fi
|
|
echo -e "${BLUE}═══════════════════════════════════════════════${NC}"
|
|
|
|
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) Supprimer des sauvegardes"
|
|
echo "11) Afficher les informations"
|
|
echo "12) Entrer dans un conteneur"
|
|
echo "13) Cloner un conteneur"
|
|
echo -e "14) ${MAGENTA}Modifier les options (nesting/fuse/nfs/cifs)${NC}"
|
|
echo -e "15) ${MAGENTA}Gérer la protection d'un conteneur${NC}"
|
|
if [[ "$PROTECTION_ENABLED" == true ]]; then
|
|
echo -e "16) ${MAGENTA}Mode protection global (ACTIVÉ)${NC}"
|
|
else
|
|
echo -e "16) ${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) delete_backups || true;;
|
|
11) show_container_info || true;;
|
|
12) enter_container || true;;
|
|
13) clone_container || true;;
|
|
14) modify_container_options || true;;
|
|
15) toggle_protection || true;;
|
|
16) 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 |