From 00dbd3eb2833a2e96387154006ea429f42254ba8 Mon Sep 17 00:00:00 2001 From: Johnny Date: Wed, 31 Dec 2025 08:43:51 +0000 Subject: [PATCH] Actualiser lxc-manager.sh --- lxc-manager.sh | 463 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 429 insertions(+), 34 deletions(-) diff --git a/lxc-manager.sh b/lxc-manager.sh index da118ce..b04e08e 100644 --- a/lxc-manager.sh +++ b/lxc-manager.sh @@ -226,17 +226,18 @@ create_container() { break done - # Stockage disponible - echo -e "\n${BLUE}Stockages disponibles:${NC}" - mapfile -t storages_list < <(pvesm status | awk 'NR>1 {print $1}') + # 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_list[@]} -eq 0 ]]; then + if [[ ${#storages_rootfs[@]} -eq 0 ]]; then error_exit "Aucun stockage disponible" return 1 fi - for i in "${!storages_list[@]}"; do - echo "$((i+1))) ${storages_list[$i]}" + 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 "" @@ -244,30 +245,54 @@ create_container() { 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 + 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_list[$((storage_choice-1))]}" + local storage="${storages_rootfs[$((storage_choice-1))]}" - # Template disponible - echo -e "\n${BLUE}Templates disponibles:${NC}" - pveam available | grep -i "system" | head -10 - echo "" + # 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}') - local template - read -rp "Template à utiliser (ex: debian-12-standard): " template + if [[ ${#template_storages[@]} -gt 0 ]]; then + template_storage="${template_storages[0]}" + fi - if [[ -z "$template" ]]; then - error_exit "Le template ne peut pas être vide" + # 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 "$storage" | grep -q "$template"; then - echo -e "${YELLOW}Téléchargement du template...${NC}" - pveam download "$storage" "$template" || { + 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 } @@ -355,6 +380,64 @@ create_container() { 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 @@ -370,7 +453,7 @@ create_container() { # Création du conteneur echo -e "\n${YELLOW}Création du conteneur en cours...${NC}" - if pct create "$vmid" "${storage}:vztmpl/${template}" \ + if pct create "$vmid" "${template_storage}:vztmpl/${template}" \ --hostname "$hostname" \ --password "$password" \ --memory "$memory" \ @@ -379,7 +462,8 @@ create_container() { --cores "$cores" \ --net0 "$net_config" \ --onboot "$onboot" \ - --unprivileged 1 \ + --unprivileged "$unprivileged" \ + $features_param \ $protection_flag; then echo -e "${GREEN}✓ Conteneur $vmid créé avec succès!${NC}" @@ -1052,6 +1136,314 @@ clone_container() { 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 @@ -1097,14 +1489,16 @@ main_menu() { 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}" + 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 "14) ${MAGENTA}Mode protection global (ACTIVÉ)${NC}" + echo -e "16) ${MAGENTA}Mode protection global (ACTIVÉ)${NC}" else - echo -e "14) ${MAGENTA}Mode protection global (DÉSACTIVÉ)${NC}" + echo -e "16) ${MAGENTA}Mode protection global (DÉSACTIVÉ)${NC}" fi echo "0) Quitter" echo "" @@ -1122,11 +1516,13 @@ main_menu() { 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;; + 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 @@ -1139,5 +1535,4 @@ main() { main_menu } -# Lancement du script -main \ No newline at end of file +# Lancement du script \ No newline at end of file