Compare commits

..

No commits in common. "main" and "v1.1.3" have entirely different histories.
main ... v1.1.3

29 changed files with 1673 additions and 5303 deletions

View File

@ -1,93 +1,3 @@
## 2025-08-06
### New version v1.1.4
### Added
- **Proxmox 9 Compatibility Preparation**
This version prepares **ProxMenux** for the upcoming **Proxmox VE 9**:
- The function to add the official Proxmox repositories now supports the new `.sources` format used in Proxmox 9, while maintaining backward compatibility with Proxmox 8.
- Banner removal is now optionally supported for Proxmox 9.
- **xshok-proxmox Detection**
Added a check to detect if the `xshok-proxmox` post-install script has already been executed.
If detected, a warning is shown to avoid conflicting adjustments:
```
It appears that you have already executed the xshok-proxmox post-install script on this system.
If you continue, some adjustments may be duplicated or conflict with those already made by xshok.
Do you want to continue anyway?
```
---
### Improved
- **Banner Removal (Proxmox 8.4.9+)**
Updated the logic for removing the subscription banner in **Proxmox 8.4.9**, due to changes in `proxmoxlib.js`.
- **LXC Disk Passthrough (Persistent UUID)**
The function to add a physical disk to an LXC container now uses **UUID-based persistent paths**.
This ensures that disks remain correctly mounted, even if the `/dev/sdX` order changes due to new hardware.
```bash
PERSISTENT_DISK=$(get_persistent_path "$DISK")
if [[ "$PERSISTENT_DISK" != "$DISK" ]] ...
```
- **System Utilities Installer**
Now checks whether APT sources are available before installing selected tools.
If a new Proxmox installation has no active repos, it will **automatically add the default sources** to avoid installation failure.
- **IOMMU Activation on ZFS Systems**
The function that enables IOMMU for passthrough now verifies existing kernel parameters to avoid duplication if the user has already configured them manually.
---
### Fixed
- Minor code cleanup and improved runtime performance across several modules.
## 2025-07-20
### Changed
- **Subscription Banner Removal (Proxmox 8.4.5+)**
Improved the `remove_subscription_banner` function to ensure compatibility with Proxmox 8.4.5, where the banner removal method was failing after clean installations.
- **Improved Log2RAM Detection**
In both the automatic and customizable post-install scripts, the logic for Log2RAM installation has been improved.
Now it correctly detects if Log2RAM is already configured and avoids triggering errors or reconfiguration.
- **Optimized Figurine Installation**
The `install_figurine` function now avoids duplicating `.bashrc` entries if the customization for the root prompt already exists.
### Added
- **New Function: Persistent Network Interface Naming**
Added a new function `setup_persistent_network` to create stable network interface names using `.link` files based on MAC addresses.
This avoids unpredictable renaming (e.g., `enp2s0` becoming `enp3s0`) when hardware changes, PCI topology shifts, or passthrough configurations are applied.
**Why use `.link` files?**
Because predictable interface names in `systemd` can change with hardware reordering or replacement. Using static `.link` files bound to MAC addresses ensures consistency, especially on systems with multiple NICs or passthrough setups.
Special thanks to [@Andres_Eduardo_Rojas_Moya] for contributing the persistent
network naming function and for the original idea.
```bash
[Match]
MACAddress=XX:XX:XX:XX:XX:XX
[Link]
Name=eth0
```
## 2025-07-01 ## 2025-07-01
### New version v1.1.3 ### New version v1.1.3

View File

@ -59,7 +59,7 @@ Then, follow the on-screen options to manage your Proxmox server efficiently.
## 📌 System Requirements ## 📌 System Requirements
🖥 **Compatible with:** 🖥 **Compatible with:**
- Proxmox VE 8.x and 9.x - Proxmox VE 8.x**
📦 **Dependencies:** 📦 **Dependencies:**
- `bash`, `curl`, `wget`, `jq`, `whiptail`, `python3-venv` (These dependencies are installed automatically during setup.) - `bash`, `curl`, `wget`, `jq`, `whiptail`, `python3-venv` (These dependencies are installed automatically during setup.)

View File

@ -498,8 +498,6 @@ install_translation_version() {
show_installation_options() { show_installation_options() {
local current_install_type local current_install_type
current_install_type=$(check_existing_installation) current_install_type=$(check_existing_installation)
local pve_version
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
local menu_title="ProxMenux Installation" local menu_title="ProxMenux Installation"
local menu_text="Choose installation type:" local menu_text="Choose installation type:"
@ -518,34 +516,11 @@ show_installation_options() {
esac esac
fi fi
INSTALL_TYPE=$(whiptail --backtitle "ProxMenux" --title "$menu_title" --menu "\n$menu_text" 14 70 2 \
"1" "Normal Version (English only)" \
"2" "Translation Version (Multi-language support)" 3>&1 1>&2 2>&3)
if [[ "$pve_version" -ge 9 ]]; then
INSTALL_TYPE=$(whiptail --backtitle "ProxMenux" --title "$menu_title" --menu "\n$menu_text" 14 70 2 \
"1" "Normal Version (English only)" 3>&1 1>&2 2>&3)
if [ -z "$INSTALL_TYPE" ]; then
show_proxmenux_logo
msg_warn "Installation cancelled."
exit 1
fi
else
INSTALL_TYPE=$(whiptail --backtitle "ProxMenux" --title "$menu_title" --menu "\n$menu_text" 14 70 2 \
"1" "Normal Version (English only)" \
"2" "Translation Version (Multi-language support)" 3>&1 1>&2 2>&3)
if [ -z "$INSTALL_TYPE" ]; then
show_proxmenux_logo
msg_warn "Installation cancelled."
exit 1
fi
fi
if [ -z "$INSTALL_TYPE" ]; then if [ -z "$INSTALL_TYPE" ]; then
show_proxmenux_logo
msg_warn "Installation cancelled." msg_warn "Installation cancelled."
exit 1 exit 1
fi fi
@ -553,14 +528,12 @@ show_installation_options() {
# For new installations, show confirmation with details # For new installations, show confirmation with details
if [ "$current_install_type" = "none" ]; then if [ "$current_install_type" = "none" ]; then
if ! show_installation_confirmation "$INSTALL_TYPE"; then if ! show_installation_confirmation "$INSTALL_TYPE"; then
show_proxmenux_logo
msg_warn "Installation cancelled." msg_warn "Installation cancelled."
exit 1 exit 1
fi fi
fi fi
if ! handle_installation_change "$current_install_type" "$INSTALL_TYPE"; then if ! handle_installation_change "$current_install_type" "$INSTALL_TYPE"; then
show_proxmenux_logo
msg_warn "Installation cancelled." msg_warn "Installation cancelled."
exit 1 exit 1
fi fi

View File

@ -1908,663 +1908,6 @@
"de": "Automatisiertes Post-Installationsskript", "de": "Automatisiertes Post-Installationsskript",
"it": "Script automatico post-installazione", "it": "Script automatico post-installazione",
"pt": "Script automatizado pós-instalação" "pt": "Script automatizado pós-instalação"
},
"Network Management - SAFE MODE": {
"es": "Gestión de red - Modo seguro",
"fr": "Gestion du réseau - Mode sans risque",
"de": "Netzwerkverwaltung - SICHERER MODUS",
"it": "Gestione rete - Modalità sicura",
"pt": "Gerenciamento de rede - Modo seguro"
},
"Show Interface Details": {
"es": "Mostrar detalles de la interfaz",
"fr": "Afficher les détails de l'interface",
"de": "Schnittstellendetails anzeigen",
"it": "Mostra dettagli dell'interfaccia",
"pt": "Mostrar detalhes da interface"
},
"Show Bridge Status": {
"es": "Mostrar estado del puente",
"fr": "Afficher l'état du pont",
"de": "Bridge-Status anzeigen",
"it": "Mostra stato del bridge",
"pt": "Mostrar status da ponte"
},
"Show Routing Table": {
"es": "Mostrar tabla de enrutamiento",
"fr": "Afficher la table de routage",
"de": "Routing-Tabelle anzeigen",
"it": "Mostra tabella di routing",
"pt": "Mostrar tabela de roteamento"
},
"Test Connectivity": {
"es": "Probar conectividad",
"fr": "Tester la connectivité",
"de": "Konnektivität testen",
"it": "Verifica la connettività",
"pt": "Testar conectividade"
},
"Advanced Diagnostics": {
"es": "Diagnóstico avanzado",
"fr": "Diagnostic avancé",
"de": "Erweiterte Diagnose",
"it": "Diagnostica avanzata",
"pt": "Diagnóstico avançado"
},
"Analyze Bridge Configuration": {
"es": "Analizar configuración del puente",
"fr": "Analyser la configuration du pont",
"de": "Bridge-Konfiguration analysieren",
"it": "Analizza configurazione bridge",
"pt": "Analisar configuração da ponte"
},
"Analyze Network Configuration": {
"es": "Analizar configuración de red",
"fr": "Analyser la configuration du réseau",
"de": "Netzwerkkonfiguration analysieren",
"it": "Analizza configurazione di rete",
"pt": "Analisar configuração de rede"
},
"Restart Network Service": {
"es": "Reiniciar servicio de red",
"fr": "Redémarrer le service réseau",
"de": "Netzwerkdienst neu starten",
"it": "Riavvia servizio di rete",
"pt": "Reiniciar serviço de rede"
},
"Show Network Config File": {
"es": "Mostrar archivo de configuración de red",
"fr": "Afficher le fichier de configuration réseau",
"de": "Netzwerkkonfigurationsdatei anzeigen",
"it": "Mostra file di configurazione di rete",
"pt": "Mostrar arquivo de configuração de rede"
},
"Create Network Backup": {
"es": "Crear copia de seguridad de red",
"fr": "Créer une sauvegarde du réseau",
"de": "Netzwerk-Backup erstellen",
"it": "Crea backup della rete",
"pt": "Criar backup de rede"
},
"Restore Network Backup": {
"es": "Restaurar copia de seguridad de red",
"fr": "Restaurer la sauvegarde du réseau",
"de": "Netzwerk-Backup wiederherstellen",
"it": "Ripristina backup della rete",
"pt": "Restaurar backup de rede"
},
"Analyzing Bridge Configuration - READ ONLY MODE": {
"es": "Analizando configuración del puente - Solo lectura",
"fr": "Analyse de la configuration du pont - Mode lecture seule",
"de": "Bridge-Konfiguration analysieren - Nur-Lese-Modus",
"it": "Analisi configurazione bridge - Solo lettura",
"pt": "Analisando configuração da ponte - Modo somente leitura"
},
"BRIDGE CONFIGURATION ANALYSIS": {
"es": "Análisis de configuración del puente",
"fr": "Analyse de la configuration du pont",
"de": "Bridge-Konfigurationsanalyse",
"it": "Analisi configurazione bridge",
"pt": "Análise de configuração da ponte"
},
"Bridge": {
"es": "Puente",
"fr": "Pont",
"de": "Bridge",
"it": "Bridge",
"pt": "Ponte"
},
"Status": {
"es": "Estado",
"fr": "Statut",
"de": "Status",
"it": "Stato",
"pt": "Status"
},
"IP": {
"es": "IP",
"fr": "IP",
"de": "IP",
"it": "IP",
"pt": "IP"
},
"Configured Ports": {
"es": "Puertos configurados",
"fr": "Ports configurés",
"de": "Konfigurierte Ports",
"it": "Porte configurate",
"pt": "Portas configuradas"
},
"Port": {
"es": "Puerto",
"fr": "Port",
"de": "Port",
"it": "Porta",
"pt": "Porta"
},
"EXISTS": {
"es": "Existe",
"fr": "Existe",
"de": "Existiert",
"it": "Esiste",
"pt": "Existe"
},
"ANALYSIS SUMMARY": {
"es": "Resumen del análisis",
"fr": "Résumé de l'analyse",
"de": "Analyse-Zusammenfassung",
"it": "Riepilogo analisi",
"pt": "Resumo da análise"
},
"Bridges analyzed": {
"es": "Puentes analizados",
"fr": "Ponts analysés",
"de": "Analysierte Bridges",
"it": "Bridge analizzati",
"pt": "Pontes analisadas"
},
"Issues found": {
"es": "Problemas encontrados",
"fr": "Problèmes trouvés",
"de": "Gefundene Probleme",
"it": "Problemi trovati",
"pt": "Problemas encontrados"
},
"Physical interfaces available": {
"es": "Interfaces físicas disponibles",
"fr": "Interfaces physiques disponibles",
"de": "Verfügbare physische Schnittstellen",
"it": "Interfacce fisiche disponibili",
"pt": "Interfaces físicas disponíveis"
},
"No bridge configuration issues found": {
"es": "No se encontraron problemas en la configuración del puente",
"fr": "Aucun problème de configuration du pont trouvé",
"de": "Keine Bridge-Konfigurationsprobleme gefunden",
"it": "Nessun problema di configurazione del bridge trovato",
"pt": "Nenhum problema encontrado na configuração da ponte"
},
"Bridge Configuration Analysis": {
"es": "Análisis de configuración del puente",
"fr": "Analyse de la configuration du pont",
"de": "Bridge-Konfigurationsanalyse",
"it": "Analisi configurazione bridge",
"pt": "Análise de configuração da ponte"
},
"Real-time network usage (iftop)": {
"es": "Uso de red en tiempo real (iftop)",
"fr": "Utilisation du réseau en temps réel (iftop)",
"de": "Echtzeit-Netzwerkauslastung (iftop)",
"it": "Utilizzo rete in tempo reale (iftop)",
"pt": "Uso de rede em tempo real (iftop)"
},
"Network monitoring tool (iptraf-ng)": {
"es": "Herramienta de monitoreo de red (iptraf-ng)",
"fr": "Outil de surveillance réseau (iptraf-ng)",
"de": "Netzwerküberwachungstool (iptraf-ng)",
"it": "Strumento di monitoraggio rete (iptraf-ng)",
"pt": "Ferramenta de monitoramento de rede (iptraf-ng)"
},
"iftop usage": {
"es": "Uso de iftop",
"fr": "Utilisation de iftop",
"de": "Verwendung von iftop",
"it": "Utilizzo di iftop",
"pt": "Uso do iftop"
},
"To exit iftop, press q": {
"es": "Para salir de iftop, pulse q",
"fr": "Pour quitter iftop, appuyez sur q",
"de": "Zum Beenden von iftop, q drücken",
"it": "Per uscire da iftop, premi q",
"pt": "Para sair do iftop, pressione q"
},
"iptraf-ng usage": {
"es": "Uso de iptraf-ng",
"fr": "Utilisation de iptraf-ng",
"de": "Verwendung von iptraf-ng",
"it": "Utilizzo di iptraf-ng",
"pt": "Uso do iptraf-ng"
},
"To exit iptraf-ng, press x": {
"es": "Para salir de iptraf-ng, pulse x",
"fr": "Pour quitter iptraf-ng, appuyez sur x",
"de": "Zum Beenden von iptraf-ng, x drücken",
"it": "Per uscire da iptraf-ng, premi x",
"pt": "Para sair do iptraf-ng, pressione x"
},
"Proxmox VE 8 to 9 Manual Upgrade Guide": {
"es": "Guía de actualización manual de Proxmox VE 8 a 9",
"fr": "Guide de mise à niveau manuelle de Proxmox VE 8 vers 9",
"de": "Manuelles Upgrade-Handbuch: Proxmox VE 8 auf 9",
"it": "Guida all'aggiornamento manuale da Proxmox VE 8 a 9",
"pt": "Guia de atualização manual do Proxmox VE 8 para 9"
},
"Source:": {
"es": "Fuente:",
"fr": "Source :",
"de": "Quelle:",
"it": "Fonte:",
"pt": "Fonte:"
},
"IMPORTANT PREREQUISITES:": {
"es": "REQUISITOS PREVIOS IMPORTANTES:",
"fr": "PRÉREQUIS IMPORTANTS :",
"de": "WICHTIGE VORAUSSETZUNGEN:",
"it": "PREREQUISITI IMPORTANTI:",
"pt": "PRÉ-REQUISITOS IMPORTANTES:"
},
"System must be updated to latest PVE 8.4+ before starting": {
"es": "El sistema debe estar actualizado a la versión PVE 8.4+ antes de comenzar",
"fr": "Le système doit être mis à jour vers PVE 8.4+ avant de commencer",
"de": "Das System muss vor dem Start auf PVE 8.4+ aktualisiert sein",
"it": "Il sistema deve essere aggiornato a PVE 8.4+ prima di iniziare",
"pt": "O sistema deve estar atualizado para PVE 8.4+ antes de começar"
},
"Use SSH or terminal access (SSH recommended)": {
"es": "Use acceso por SSH o terminal (se recomienda SSH)",
"fr": "Utilisez un accès SSH ou terminal (SSH recommandé)",
"de": "SSH- oder Terminalzugang verwenden (SSH empfohlen)",
"it": "Usa accesso SSH o terminale (consigliato SSH)",
"pt": "Use acesso por SSH ou terminal (SSH recomendado)"
},
"Use tmux or screen to avoid interruptions": {
"es": "Use tmux o screen para evitar interrupciones",
"fr": "Utilisez tmux ou screen pour éviter les interruptions",
"de": "Verwenden Sie tmux oder screen, um Unterbrechungen zu vermeiden",
"it": "Usa tmux o screen per evitare interruzioni",
"pt": "Use tmux ou screen para evitar interrupções"
},
"Have valid backups of all VMs and containers": {
"es": "Tenga copias de seguridad válidas de todas las VMs y contenedores",
"fr": "Ayez des sauvegardes valides de toutes les VM et conteneurs",
"de": "Halten Sie gültige Backups aller VMs und Container bereit",
"it": "Avere backup validi di tutte le VM e i container",
"pt": "Tenha backups válidos de todas as VMs e contêineres"
},
"At least 5GB free space on root filesystem": {
"es": "Al menos 5 GB de espacio libre en el sistema de archivos raíz",
"fr": "Au moins 5 Go despace libre sur le système de fichiers racine",
"de": "Mindestens 5 GB freier Speicherplatz auf dem Root-Dateisystem",
"it": "Almeno 5 GB di spazio libero sul filesystem root",
"pt": "Pelo menos 5 GB de espaço livre no sistema de arquivos raiz"
},
"Do not run the upgrade from the Web UI virtual console (it will disconnect)": {
"es": "No ejecute la actualización desde la consola virtual de la interfaz web (se desconectará)",
"fr": "Nexécutez pas la mise à niveau depuis la console virtuelle de linterface Web (elle se déconnectera)",
"de": "Führen Sie das Upgrade nicht über die virtuelle Konsole der Web-UI aus (die Verbindung wird getrennt)",
"it": "Non eseguire laggiornamento dalla console virtuale dellinterfaccia Web (si disconnetterà)",
"pt": "Não execute a atualização pelo console virtual da interface Web (a conexão será interrompida)"
},
"Update system to latest PVE 8.4+ (if not done already):": {
"es": "Actualizar el sistema a PVE 8.4+ (si aún no se ha hecho):",
"fr": "Mettre à jour le système vers PVE 8.4+ (si ce nest pas déjà fait) :",
"de": "System auf PVE 8.4+ aktualisieren (falls noch nicht geschehen):",
"it": "Aggiornare il sistema a PVE 8.4+ (se non già fatto):",
"pt": "Atualizar o sistema para PVE 8.4+ (se ainda não foi feito):"
},
"Or use ProxMenux update function": {
"es": "O use la función de actualización de ProxMenux",
"fr": "Ou utilisez la fonction de mise à jour de ProxMenux",
"de": "Oder verwenden Sie die Aktualisierungsfunktion von ProxMenux",
"it": "Oppure usa la funzione di aggiornamento di ProxMenux",
"pt": "Ou use a função de atualização do ProxMenux"
},
"Verify PVE version (must be 8.4.1 or newer):": {
"es": "Verificar la versión de PVE (debe ser 8.4.1 o superior):",
"fr": "Vérifier la version de PVE (doit être 8.4.1 ou plus récente) :",
"de": "PVE-Version prüfen (muss 8.4.1 oder neuer sein):",
"it": "Verificare la versione di PVE (deve essere 8.4.1 o successiva):",
"pt": "Verificar a versão do PVE (deve ser 8.4.1 ou superior):"
},
"If this node runs hyper-converged Ceph: ensure Ceph is 19.x (Squid) BEFORE upgrading PVE.": {
"es": "Si este nodo ejecuta Ceph hiperconvergente: asegúrese de que Ceph sea 19.x (Squid) ANTES de actualizar PVE.",
"fr": "Si ce nœud exécute un Ceph hyperconvergé : assurez-vous que Ceph est en 19.x (Squid) AVANT de mettre à niveau PVE.",
"de": "Wenn dieser Knoten hyperkonvergentes Ceph betreibt: Stellen Sie sicher, dass Ceph 19.x (Squid) ist, BEVOR Sie PVE aktualisieren.",
"it": "Se questo nodo esegue Ceph iperconvergente: assicurarsi che Ceph sia 19.x (Squid) PRIMA di aggiornare PVE.",
"pt": "Se este nó executa Ceph hiperconvergente: certifique-se de que o Ceph esteja em 19.x (Squid) ANTES de atualizar o PVE."
},
"If not 19.x, upgrade Ceph (Reef→Squid) first per the official guide:": {
"es": "Si no es 19.x, actualice primero Ceph (Reef→Squid) según la guía oficial:",
"fr": "Si ce nest pas 19.x, mettez dabord à niveau Ceph (Reef→Squid) selon le guide officiel :",
"de": "Wenn nicht 19.x, aktualisieren Sie Ceph zuerst (Reef→Squid) gemäß der offiziellen Anleitung:",
"it": "Se non è 19.x, aggiornare prima Ceph (Reef→Squid) secondo la guida ufficiale:",
"pt": "Se não for 19.x, atualize primeiro o Ceph (Reef→Squid) conforme o guia oficial:"
},
"Run upgrade checklist script:": {
"es": "Ejecutar el script de la lista de verificación de actualización:",
"fr": "Exécuter le script de liste de vérification de mise à niveau :",
"de": "Upgrade-Checklisten-Skript ausführen:",
"it": "Eseguire lo script della checklist di aggiornamento:",
"pt": "Executar o script da lista de verificação de atualização:"
},
"If it warns about 'systemd-boot' meta-package, remove it:": {
"es": "Si advierte sobre el metapaquete 'systemd-boot', elimínelo:",
"fr": "Sil signale le méta-paquet « systemd-boot », supprimez-le :",
"de": "Wenn vor dem Metapaket „systemd-boot“ gewarnt wird, entfernen Sie es:",
"it": "Se avvisa del metapacchetto 'systemd-boot', rimuoverlo:",
"pt": "Se alertar sobre o metapacote 'systemd-boot', remova-o:"
},
"Start terminal multiplexer (recommended):": {
"es": "Iniciar un multiplexor de terminal (recomendado):",
"fr": "Démarrer un multiplexeur de terminal (recommandé) :",
"de": "Terminal-Multiplexer starten (empfohlen):",
"it": "Avviare un multiplexer di terminale (consigliato):",
"pt": "Iniciar um multiplexador de terminal (recomendado):"
},
"# Recommended: avoids disconnection during upgrade": {
"es": "# Recomendado: evita desconexiones durante la actualización",
"fr": "# Recommandé : évite les déconnexions pendant la mise à niveau",
"de": "# Empfohlen: vermeidet Verbindungsabbrüche während des Upgrades",
"it": "# Consigliato: evita disconnessioni durante laggiornamento",
"pt": "# Recomendado: evita desconexões durante a atualização"
},
"# Alternative if you prefer screen": {
"es": "# Alternativa si prefiere screen",
"fr": "# Alternative si vous préférez screen",
"de": "# Alternative, wenn Sie screen bevorzugen",
"it": "# Alternativa se preferisci screen",
"pt": "# Alternativa se preferir screen"
},
"Update Debian repositories to Trixie:": {
"es": "Actualizar los repositorios de Debian a Trixie:",
"fr": "Mettre à jour les dépôts Debian vers Trixie :",
"de": "Debian-Repositories auf Trixie aktualisieren:",
"it": "Aggiornare i repository Debian a Trixie:",
"pt": "Atualizar os repositórios Debian para Trixie:"
},
"Update PVE enterprise repository (Only if using enterprise):": {
"es": "Actualizar el repositorio empresarial de PVE (solo si usa enterprise):",
"fr": "Mettre à jour le dépôt entreprise de PVE (uniquement si vous utilisez enterprise) :",
"de": "PVE-Enterprise-Repository aktualisieren (nur bei Verwendung von Enterprise):",
"it": "Aggiornare il repository enterprise di PVE (solo se si usa enterprise):",
"pt": "Atualizar o repositório enterprise do PVE (apenas se usar enterprise):"
},
"Skip this step if using no-subscription repository": {
"es": "Omita este paso si usa el repositorio sin suscripción",
"fr": "Ignorez cette étape si vous utilisez le dépôt sans abonnement",
"de": "Überspringen Sie diesen Schritt, wenn Sie das No-Subscription-Repository verwenden",
"it": "Saltare questo passaggio se si usa il repository senza sottoscrizione",
"pt": "Pule esta etapa se usar o repositório sem assinatura"
},
"Add new PVE 9 enterprise repository (deb822 format) (Only if using enterprise):": {
"es": "Agregar el nuevo repositorio empresarial de PVE 9 (formato deb822) (solo si usa enterprise):",
"fr": "Ajouter le nouveau dépôt entreprise de PVE 9 (format deb822) (uniquement si vous utilisez enterprise) :",
"de": "Neues PVE-9-Enterprise-Repository (deb822-Format) hinzufügen (nur bei Enterprise):",
"it": "Aggiungere il nuovo repository enterprise di PVE 9 (formato deb822) (solo se si usa enterprise):",
"pt": "Adicionar o novo repositório enterprise do PVE 9 (formato deb822) (apenas se usar enterprise):"
},
"Only if using enterprise subscription": {
"es": "Solo si utiliza suscripción empresarial",
"fr": "Uniquement si vous avez un abonnement entreprise",
"de": "Nur bei vorhandener Enterprise-Abonnement",
"it": "Solo se si dispone di una sottoscrizione enterprise",
"pt": "Apenas se você tiver assinatura enterprise"
},
"OR add new PVE 9 no-subscription repository:": {
"es": "O agregar el nuevo repositorio de PVE 9 sin suscripción:",
"fr": "OU ajouter le nouveau dépôt PVE 9 sans abonnement :",
"de": "ODER das neue PVE-9-No-Subscription-Repository hinzufügen:",
"it": "OPPURE aggiungere il nuovo repository PVE 9 senza sottoscrizione:",
"pt": "OU adicionar o novo repositório PVE 9 sem assinatura:"
},
"Only if using no-subscription repository": {
"es": "Solo si usa el repositorio sin suscripción",
"fr": "Uniquement si vous utilisez le dépôt sans abonnement",
"de": "Nur bei Verwendung des No-Subscription-Repository",
"it": "Solo se si usa il repository senza sottoscrizione",
"pt": "Apenas se usar o repositório sem assinatura"
},
"Refresh APT index and verify repositories:": {
"es": "Actualizar el índice de APT y verificar los repositorios:",
"fr": "Actualiser lindex APT et vérifier les dépôts :",
"de": "APT-Index aktualisieren und Repositories prüfen:",
"it": "Aggiornare lindice APT e verificare i repository:",
"pt": "Atualizar o índice do APT e verificar os repositórios:"
},
"Ensure there are no errors and that proxmox-ve candidate shows 9.x": {
"es": "Asegúrese de que no haya errores y de que el candidato de proxmox-ve sea 9.x",
"fr": "Assurez-vous quil ny a aucune erreur et que le candidat proxmox-ve indique 9.x",
"de": "Stellen Sie sicher, dass keine Fehler auftreten und der proxmox-ve-Kandidat 9.x anzeigt",
"it": "Assicurarsi che non vi siano errori e che il candidato di proxmox-ve sia 9.x",
"pt": "Certifique-se de que não há erros e que o candidato proxmox-ve mostre 9.x"
},
"Update Ceph repository (Only if using Ceph):": {
"es": "Actualizar el repositorio de Ceph (solo si usa Ceph):",
"fr": "Mettre à jour le dépôt Ceph (uniquement si vous utilisez Ceph) :",
"de": "Ceph-Repository aktualisieren (nur bei Verwendung von Ceph):",
"it": "Aggiornare il repository di Ceph (solo se si usa Ceph):",
"pt": "Atualizar o repositório do Ceph (apenas se usar Ceph):"
},
"Use enterprise URL if you have subscription.": {
"es": "Use la URL enterprise si dispone de suscripción.",
"fr": "Utilisez lURL enterprise si vous avez un abonnement.",
"de": "Verwenden Sie die Enterprise-URL, wenn Sie ein Abonnement haben.",
"it": "Usa lURL enterprise se hai una sottoscrizione.",
"pt": "Use a URL enterprise se tiver assinatura."
},
"Remove old repository files:": {
"es": "Eliminar los archivos de repositorio antiguos:",
"fr": "Supprimer les anciens fichiers de dépôt :",
"de": "Alte Repository-Dateien entfernen:",
"it": "Rimuovere i vecchi file di repository:",
"pt": "Remover arquivos antigos de repositório:"
},
"Also comment any remaining 'bookworm' entries in *.list if present.": {
"es": "Comente también cualquier entrada restante de bookworm en *.list si existe.",
"fr": "Commentez également toute entrée bookworm restante dans *.list si présente.",
"de": "Kommentieren Sie außerdem verbleibende bookworm-Einträge in *.list, falls vorhanden.",
"it": "Commentare anche eventuali voci bookworm rimanenti in *.list se presenti.",
"pt": "Comente também quaisquer entradas bookworm restantes em *.list, se houver."
},
"Update package index:": {
"es": "Actualizar el índice de paquetes:",
"fr": "Mettre à jour lindex des paquets :",
"de": "Paketindex aktualisieren:",
"it": "Aggiornare lindice dei pacchetti:",
"pt": "Atualizar o índice de pacotes:"
},
"Disable kernel audit messages (optional but recommended):": {
"es": "Desactivar los mensajes de auditoría del kernel (opcional pero recomendado):",
"fr": "Désactiver les messages daudit du noyau (optionnel mais recommandé) :",
"de": "Kernel-Audit-Meldungen deaktivieren (optional, aber empfohlen):",
"it": "Disabilitare i messaggi di audit del kernel (opzionale ma consigliato):",
"pt": "Desativar as mensagens de auditoria do kernel (opcional, mas recomendado):"
},
"Start the main system upgrade:": {
"es": "Iniciar la actualización principal del sistema:",
"fr": "Démarrer la mise à niveau principale du système :",
"de": "Hauptsystem-Upgrade starten:",
"it": "Avviare laggiornamento principale del sistema:",
"pt": "Iniciar a atualização principal do sistema:"
},
"This will take time. Answer prompts carefully - see notes below.": {
"es": "Esto llevará tiempo. Responda a las indicaciones con cuidado (vea las notas a continuación).",
"fr": "Cela prendra du temps. Répondez aux invites avec attention (voir les notes ci-dessous).",
"de": "Dies wird einige Zeit dauern. Beantworten Sie Rückfragen sorgfältig (siehe Hinweise unten).",
"it": "Questo richiederà tempo. Rispondi con attenzione alle richieste (vedi note sotto).",
"pt": "Isso levará tempo. Responda às solicitações com atenção (veja as notas abaixo)."
},
"UPGRADE PROMPTS - RECOMMENDED ANSWERS:": {
"es": "INDICACIONES DE ACTUALIZACIÓN - RESPUESTAS RECOMENDADAS:",
"fr": "INVITES DE MISE À NIVEAU - RÉPONSES RECOMMANDÉES :",
"de": "UPGRADE-AUFFORDERUNGEN EMPFOHLENE ANTWORTEN:",
"it": "PROMPT DI AGGIORNAMENTO - RISPOSTE CONSIGLIATE:",
"pt": "PROMPTS DE ATUALIZAÇÃO - RESPOSTAS RECOMENDADAS:"
},
"Keep current version (N)": {
"es": "Mantener la versión actual (N)",
"fr": "Conserver la version actuelle (N)",
"de": "Aktuelle Version beibehalten (N)",
"it": "Mantieni la versione corrente (N)",
"pt": "Manter a versão atual (N)"
},
"Install maintainer's version (Y)": {
"es": "Instalar la versión del mantenedor del paquete (Y)",
"fr": "Installer la version du mainteneur du paquet (Y)",
"de": "Version des Paketbetreuers installieren (Y)",
"it": "Installare la versione del maintainer del pacchetto (Y)",
"pt": "Instalar a versão do mantenedor do pacote (Y)"
},
"Keep current version (N) if modified": {
"es": "Mantener la versión actual (N) si está modificada",
"fr": "Conserver la version actuelle (N) si elle a été modifiée",
"de": "Aktuelle Version beibehalten (N), falls angepasst",
"it": "Mantieni la versione corrente (N) se modificata",
"pt": "Manter a versão atual (N) se estiver modificada"
},
"Service restarts:": {
"es": "Reinicios de servicios:",
"fr": "Redémarrages de services :",
"de": "Neustarts von Diensten:",
"it": "Riavvii dei servizi:",
"pt": "Reinicializações de serviços:"
},
"Use default (Yes)": {
"es": "Usar la opción predeterminada (Sí)",
"fr": "Utiliser loption par défaut (Oui)",
"de": "Standardoption verwenden (Ja)",
"it": "Usare lopzione predefinita (Sì)",
"pt": "Usar a opção padrão (Sim)"
},
"Press 'q' to exit": {
"es": "Presione «q» para salir",
"fr": "Appuyez sur « q » pour quitter",
"de": "Drücken Sie „q“, um zu beenden",
"it": "Premi «q» per uscire",
"pt": "Pressione «q» para sair"
},
"If booting in EFI mode with root on LVM: install GRUB for EFI": {
"es": "Si inicia en modo EFI con root en LVM: instale GRUB para EFI",
"fr": "Si vous démarrez en mode EFI avec root sur LVM : installez GRUB pour EFI",
"de": "Wenn Sie im EFI-Modus mit Root auf LVM booten: GRUB für EFI installieren",
"it": "Se lavvio è in modalità EFI con root su LVM: installare GRUB per EFI",
"pt": "Se iniciar em modo EFI com root no LVM: instale o GRUB para EFI"
},
"Per official known issues; ensures proper boot after upgrade": {
"es": "Según las incidencias conocidas oficiales, garantiza un arranque correcto tras la actualización",
"fr": "Selon les problèmes connus officiels, cela garantit un démarrage correct après la mise à niveau",
"de": "Laut den offiziellen Known Issues sorgt dies für einen ordnungsgemäßen Start nach dem Upgrade",
"it": "Secondo le note ufficiali dei problemi noti, garantisce un avvio corretto dopo laggiornamento",
"pt": "De acordo com os problemas conhecidos oficiais, garante uma inicialização correta após a atualização"
},
"Run checklist again to verify upgrade:": {
"es": "Ejecutar de nuevo la lista de verificación para comprobar la actualización:",
"fr": "Relancer la liste de vérification pour valider la mise à niveau :",
"de": "Checkliste erneut ausführen, um das Upgrade zu verifizieren:",
"it": "Eseguire nuovamente la checklist per verificare laggiornamento:",
"pt": "Executar novamente a lista de verificação para validar a atualização:"
},
"Should show fewer or no issues": {
"es": "Debería mostrar menos problemas o ninguno",
"fr": "Devrait afficher moins de problèmes, voire aucun",
"de": "Sollte weniger oder keine Probleme anzeigen",
"it": "Dovrebbe mostrare meno problemi o nessuno",
"pt": "Deve mostrar menos problemas ou nenhum"
},
"Reboot the system:": {
"es": "Reiniciar el sistema:",
"fr": "Redémarrer le système :",
"de": "System neu starten:",
"it": "Riavviare il sistema:",
"pt": "Reiniciar o sistema:"
},
"After reboot, verify PVE version:": {
"es": "Después del reinicio, verificar la versión de PVE:",
"fr": "Après le redémarrage, vérifier la version de PVE :",
"de": "Nach dem Neustart die PVE-Version prüfen:",
"it": "Dopo il riavvio, verificare la versione di PVE:",
"pt": "Após reiniciar, verificar a versão do PVE:"
},
"Should show pve-manager/9.x.x": {
"es": "Debería mostrar pve-manager/9.x.x",
"fr": "Devrait afficher pve-manager/9.x.x",
"de": "Sollte pve-manager/9.x.x anzeigen",
"it": "Dovrebbe mostrare pve-manager/9.x.x",
"pt": "Deve mostrar pve-manager/9.x.x"
},
"Optional: Modernize repository sources:": {
"es": "Opcional: Modernizar las fuentes de repositorio:",
"fr": "Optionnel : Moderniser les sources des dépôts :",
"de": "Optional: Repository-Quellen modernisieren:",
"it": "Opzionale: Modernizzare le fonti del repository:",
"pt": "Opcional: Modernizar as fontes de repositório:"
},
"Converts to deb822; keeps .list backups as .bak": {
"es": "Convierte a deb822; mantiene copias .list como .bak",
"fr": "Convertit en deb822 ; conserve les sauvegardes .list en .bak",
"de": "Konvertiert zu deb822; behält .list-Backups als .bak bei",
"it": "Converte in deb822; mantiene i backup .list come .bak",
"pt": "Converte para deb822; mantém backups .list como .bak"
},
"CLUSTER UPGRADE NOTES:": {
"es": "NOTAS DE ACTUALIZACIÓN DEL CLÚSTER:",
"fr": "NOTES DE MISE À NIVEAU DU CLUSTER :",
"de": "HINWEISE ZUM CLUSTER-UPGRADE:",
"it": "NOTE DI AGGIORNAMENTO DEL CLUSTER:",
"pt": "NOTAS DE ATUALIZAÇÃO DO CLUSTER:"
},
"Upgrade one node at a time": {
"es": "Actualizar un nodo a la vez",
"fr": "Mettre à niveau un nœud à la fois",
"de": "Jeweils nur einen Knoten aktualisieren",
"it": "Aggiornare un nodo alla volta",
"pt": "Atualizar um nó por vez"
},
"Migrate VMs away from node being upgraded": {
"es": "Migrar las VMs fuera del nodo que se está actualizando",
"fr": "Migrer les VM hors du nœud en cours de mise à niveau",
"de": "VMs vom zu aktualisierenden Knoten weg migrieren",
"it": "Migrare le VM dal nodo in aggiornamento",
"pt": "Migrar as VMs para fora do nó em atualização"
},
"Wait for each node to complete before starting next": {
"es": "Esperar a que cada nodo termine antes de empezar con el siguiente",
"fr": "Attendre que chaque nœud soit terminé avant de commencer le suivant",
"de": "Warten, bis jeder Knoten abgeschlossen ist, bevor der nächste beginnt",
"it": "Attendere che ogni nodo termini prima di iniziare il successivo",
"pt": "Aguardar cada nó concluir antes de iniciar o próximo"
},
"HA groups will be migrated to HA rules automatically": {
"es": "Los grupos de HA se migrarán automáticamente a reglas de HA",
"fr": "Les groupes HA seront migrés automatiquement vers des règles HA",
"de": "HA-Gruppen werden automatisch in HA-Regeln migriert",
"it": "I gruppi HA verranno migrati automaticamente a regole HA",
"pt": "Grupos de HA serão migrados automaticamente para regras de HA"
},
"TROUBLESHOOTING:": {
"es": "SOLUCIÓN DE PROBLEMAS:",
"fr": "DÉPANNAGE :",
"de": "FEHLERBEHEBUNG:",
"it": "RISOLUZIONE DEI PROBLEMI:",
"pt": "SOLUÇÃO DE PROBLEMAS:"
},
"If upgrade fails:": {
"es": "Si la actualización falla:",
"fr": "Si la mise à niveau échoue :",
"de": "Wenn das Upgrade fehlschlägt:",
"it": "Se laggiornamento fallisce:",
"pt": "Se a atualização falhar:"
},
"If repositories error:": {
"es": "Si hay errores de repositorios:",
"fr": "En cas derreurs de dépôts :",
"de": "Bei Repository-Fehlern:",
"it": "In caso di errori dei repository:",
"pt": "Se houver erros nos repositórios:"
},
"If 'proxmox-ve' removal warning:": {
"es": "Si aparece advertencia de eliminación de proxmox-ve:",
"fr": "Si un avertissement de suppression de proxmox-ve apparaît :",
"de": "Bei Warnung zur Entfernung von proxmox-ve:",
"it": "Se compare un avviso di rimozione di proxmox-ve:",
"pt": "Se aparecer um aviso de remoção de proxmox-ve:"
},
"Emergency recovery:": {
"es": "Recuperación de emergencia:",
"fr": "Récupération durgence :",
"de": "Notfallwiederherstellung:",
"it": "Ripristino di emergenza:",
"pt": "Recuperação de emergência:"
} }
} }

View File

@ -413,18 +413,6 @@
"password": null "password": null
} }
}, },
{
"name": "Bar-Assistant",
"slug": "bar-assistant",
"desc": "Bar Assistant is all-in-one solution for managing your home bar. Compared to other recipe management software that usually tries to be more for general use, Bar Assistant is made specifically for managing cocktail recipes. This means that there are a lot of cocktail-oriented features, like ingredient substitutes, first-class ingredients, ABV calculations, unit switching and more..",
"script": "ct/bar-assistant.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/bar-assistant.sh",
"categories": [
24
],
"notes": [],
"type": "ct"
},
{ {
"name": "Barcode Buddy", "name": "Barcode Buddy",
"slug": "barcode-buddy", "slug": "barcode-buddy",
@ -561,6 +549,24 @@
], ],
"type": "ct" "type": "ct"
}, },
{
"name": "Calibre-Web",
"slug": "calibre-web",
"desc": "Calibre-Web is a web app for browsing, reading and downloading eBooks stored in a Calibre database.",
"script": "ct/calibre-web.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/calibre-web.sh",
"categories": [
11
],
"notes": [
"Add Calibre-Web Extras via `update`"
],
"type": "ct",
"default_credentials": {
"username": "admin",
"password": "admin123"
}
},
{ {
"name": "CasaOS", "name": "CasaOS",
"slug": "casaos", "slug": "casaos",
@ -644,18 +650,6 @@
], ],
"type": "pve" "type": "pve"
}, },
{
"name": "Cleanuparr",
"slug": "cleanuparr",
"desc": "Cleanuparr is a tool for automating the cleanup of unwanted or blocked files in Sonarr, Radarr, and supported download clients like qBittorrent, Transmission, and Deluge. It removes incomplete, blocked, or malicious downloads and can trigger replacement searches to ensure your media library stays complete and up-to-date.",
"script": "ct/cleanuparr.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/cleanuparr.sh",
"categories": [
14
],
"notes": [],
"type": "ct"
},
{ {
"name": "Cloudflare-DDNS", "name": "Cloudflare-DDNS",
"slug": "cloudflare-ddns", "slug": "cloudflare-ddns",
@ -685,20 +679,6 @@
], ],
"type": "ct" "type": "ct"
}, },
{
"name": "Cloudreve",
"slug": "cloudreve",
"desc": "Cloudreve is an open-source, community-driven cloud storage system that provides file sharing, synchronization, and management features. It supports a wide range of storage backends and integrates with various notification and logging platforms.",
"script": "ct/cloudreve.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/cloudreve.sh",
"categories": [
12
],
"notes": [
"After Installation: Register your user -> Login -> Dashboard -> Accept Primary URL."
],
"type": "ct"
},
{ {
"name": "Cockpit", "name": "Cockpit",
"slug": "cockpit", "slug": "cockpit",
@ -1008,7 +988,7 @@
{ {
"name": "Docmost", "name": "Docmost",
"slug": "docmost", "slug": "docmost",
"desc": "Open-source collaborative wiki and documentation software. Create, collaborate, and share knowledge seamlessly with Docmost. Ideal for managing your wiki, knowledge-base, documentation and a lot more.", "desc": "Open-source collaborative wiki and documentation software Create, collaborate, and share knowledge seamlessly with Docmost. Ideal for managing your wiki, knowledge-base, documentation and a lot more.",
"script": "ct/docmost.sh", "script": "ct/docmost.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/docmost.sh", "script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/docmost.sh",
"categories": [ "categories": [
@ -1638,8 +1618,7 @@
4 4
], ],
"notes": [ "notes": [
"Configuration settings: `/etc/headscale/config.yaml`", "Configuration settings: `/etc/headscale/config.yaml`"
"Access headscale-admin UI via `http://<LXC-IP>/admin/`"
], ],
"type": "ct" "type": "ct"
}, },
@ -1696,6 +1675,23 @@
"notes": [], "notes": [],
"type": "ct" "type": "ct"
}, },
{
"name": "Home Assistant Core",
"slug": "homeassistant-core",
"desc": "A standalone installation of Home Assistant Core refers to a setup where the Home Assistant Core software is installed directly on a device or operating system, without the use of Docker containers. This provides a simpler, but less flexible and scalable solution, as the software is tightly coupled with the underlying system.",
"script": "ct/homeassistant-core.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/homeassistant-core.sh",
"categories": [
16
],
"notes": [
"If the LXC is created Privileged, the script will automatically set up USB passthrough.",
"Requires PVE 8.2.2 with kernel 6.8.4-3-pve or newer",
"Deprecation-Warning: This Core-based setup will be deprecated by August 2025. Use Home Assistant OS is strongly recommended to ensure long-term stability and updates.",
"config path: `/root/.homeassistant`"
],
"type": "ct"
},
{ {
"name": "Home Assistant Container", "name": "Home Assistant Container",
"slug": "homeassistant", "slug": "homeassistant",
@ -1722,7 +1718,9 @@
"categories": [ "categories": [
24 24
], ],
"notes": [], "notes": [
".env file location: `/opt/.env`"
],
"type": "ct" "type": "ct"
}, },
{ {
@ -1770,20 +1768,6 @@
], ],
"type": "ct" "type": "ct"
}, },
{
"name": "HortusFox",
"slug": "hortusfox",
"desc": "HortusFox is a collaborative plant management system for plant enthusiasts. Manage, document and track your entire plant collection \u2013 self-hosted and privacy-friendly.",
"script": "ct/hortusfox.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/hortusfox.sh",
"categories": [
24
],
"notes": [
"Login Credentials : `cat ~/hortusfox.creds`"
],
"type": "ct"
},
{ {
"name": "Proxmox VE Host Backup", "name": "Proxmox VE Host Backup",
"slug": "host-backup", "slug": "host-backup",
@ -1953,25 +1937,6 @@
"notes": [], "notes": [],
"type": "ct" "type": "ct"
}, },
{
"name": "Jeedom",
"slug": "jeedom",
"desc": "Jeedom is a home automation system that is free, open, and cloudless. It allows users to manage and automate various aspects of their homes by creating objects, installing plugins for added functionalities, and connecting to a Market account for services. It also supports direct access URLs and user management.",
"script": "ct/jeedom.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/jeedom.sh",
"categories": [
16
],
"notes": [
"WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing.",
"Only OS packages are updateable. To update Jeedom, please use the web interface."
],
"type": "ct",
"default_credentials": {
"username": "admin",
"password": "admin"
}
},
{ {
"name": "Jellyfin Media Server", "name": "Jellyfin Media Server",
"slug": "jellyfin", "slug": "jellyfin",
@ -2007,7 +1972,7 @@
"script": "ct/jenkins.sh", "script": "ct/jenkins.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/jenkins.sh", "script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/jenkins.sh",
"categories": [ "categories": [
20 22
], ],
"notes": [], "notes": [],
"type": "ct" "type": "ct"
@ -2119,14 +2084,9 @@
], ],
"notes": [ "notes": [
"First start can take a few minutes", "First start can take a few minutes",
"This script requires some extra steps after the installation, Please checkout the `https://github.com/community-scripts/ProxmoxVE/discussions/193`", "This script requires some extra steps after the installation, Please checkout the `https://github.com/community-scripts/ProxmoxVE/discussions/193`"
"When updating, if you had modified cache-ispn.xml: Re-apply your changes to the new file, otherwise leave it unchanged."
], ],
"type": "ct", "type": "ct"
"default_credentials": {
"username": "tmpadm",
"password": "admin123"
}
}, },
{ {
"name": "Kimai", "name": "Kimai",
@ -2262,21 +2222,6 @@
"notes": [], "notes": [],
"type": "ct" "type": "ct"
}, },
{
"name": "LinkStack",
"slug": "linkstack",
"desc": "LinkStack is an open-source, self-hosted alternative to Linktree, allowing users to create a customizable profile page to share multiple links, hosted on their own server.",
"script": "ct/linkstack.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/linkstack.sh",
"categories": [
9
],
"notes": [
"LinkStack can be updated via the user interface.",
"Complete setup via the web interface at http://<container-ip>/. Check installation logs: `cat ~/linkstack-install.log`"
],
"type": "ct"
},
{ {
"name": "Linkwarden", "name": "Linkwarden",
"slug": "linkwarden", "slug": "linkwarden",
@ -2439,18 +2384,6 @@
], ],
"type": "ct" "type": "ct"
}, },
{
"name": "Mealie",
"slug": "mealie",
"desc": "Mealie is a self hosted recipe manager, meal planner and shopping list with a RestAPI backend and a reactive frontend built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the URL and Mealie will automatically import the relevant data, or add a family recipe with the UI editor. Mealie also provides an API for interactions from 3rd party applications.",
"script": "ct/mealie.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/mealie.sh",
"categories": [
13
],
"notes": [],
"type": "ct"
},
{ {
"name": "MediaMTX", "name": "MediaMTX",
"slug": "mediamtx", "slug": "mediamtx",
@ -2720,9 +2653,7 @@
"categories": [ "categories": [
16 16
], ],
"notes": [ "notes": [],
"You may need to configure the `WEBHOOK_URL` in the config file when using a domain."
],
"type": "ct" "type": "ct"
}, },
{ {
@ -2852,7 +2783,7 @@
} }
}, },
{ {
"name": "Intel e1000e NIC Offloading Fix", "name": "NIC Offloading Fix",
"slug": "nic-offloading-fix", "slug": "nic-offloading-fix",
"desc": "This script automates the process of disabling network interface card (NIC) offloading features specifically for Intel e1000e network interfaces on Linux systems.", "desc": "This script automates the process of disabling network interface card (NIC) offloading features specifically for Intel e1000e network interfaces on Linux systems.",
"script": "tools/pve/nic-offloading-fix.sh", "script": "tools/pve/nic-offloading-fix.sh",
@ -3117,7 +3048,7 @@
"script": "ct/onlyoffice.sh", "script": "ct/onlyoffice.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/onlyoffice.sh", "script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/onlyoffice.sh",
"categories": [ "categories": [
12 9
], ],
"notes": [ "notes": [
"Database / RabbitMQ Credentials: `cat ~/onlyoffice.creds`" "Database / RabbitMQ Credentials: `cat ~/onlyoffice.creds`"
@ -3237,18 +3168,23 @@
"type": "ct" "type": "ct"
}, },
{ {
"name": "OTS", "name": "OPNsense",
"slug": "ots", "slug": "opnsense-vm",
"desc": "One-Time-Secret sharing platform with a symmetric 256bit AES encryption in the browser.", "desc": "OPNsense is an open-source firewall and routing platform based on FreeBSD. It provides advanced security features, including intrusion detection, VPN support, traffic shaping, and web filtering, with an intuitive web interface for easy management. Known for its reliability and regular updates, OPNsense is a popular choice for both businesses and home networks.",
"script": "ct/ots.sh", "script": "vm/opnsense-vm.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/ots.sh", "script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/opnsense-vm.sh",
"categories": [ "categories": [
6 4,
2
], ],
"notes": [ "notes": [
"When it is in used external please use it behind reverse proxy or create your own certificates" "It will fail with default settings if there is no vmbr0 and vmbr1 on your node. Use advanced settings in this case."
], ],
"type": "ct" "type": "vm",
"default_credentials": {
"username": "root",
"password": "opnsense"
}
}, },
{ {
"name": "Outline", "name": "Outline",
@ -3323,22 +3259,6 @@
"notes": [], "notes": [],
"type": "ct" "type": "ct"
}, },
{
"name": "Palmr",
"slug": "palmr",
"desc": "Palmr is a fast and secure platform for sharing files, built with performance and privacy in mind.",
"script": "ct/palmr.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/palmr.sh",
"categories": [
11
],
"notes": [
"This LXC is very memory-hungry when updating; it requires at least 6GB RAM, but RAM may be reduced to as low as 2GB when running normally",
"To use a bind mount for storage, create symlinks to your mount for both `uploads` and `temp-uploads` in `/opt/palmr_data`",
"To use Palmr with a reverse proxy, uncomment `SECURE_SITE` in `/opt/palmr/apps/server/.env`"
],
"type": "ct"
},
{ {
"name": "PaperlessAI", "name": "PaperlessAI",
"slug": "paperless-ai", "slug": "paperless-ai",
@ -3929,9 +3849,8 @@
9 9
], ],
"notes": [ "notes": [
"Create Proxmox-API-Token first: `https://github.com/rcourtman/Pulse?tab=readme-ov-file#creating-api-token`", "Create Proxmox-API-Token first: `https://github.com/rcourtman/Pulse?tab=readme-ov-file#creating-a-proxmox-api-token`",
"After installation, access the web interface to configure your Proxmox connection details through the built-in setup wizard", "After installation, access the web interface to configure your Proxmox connection details through the built-in setup wizard"
"Configure authentication in the Web UI => Settings => Security"
], ],
"type": "ct" "type": "ct"
}, },
@ -4138,18 +4057,6 @@
"notes": [], "notes": [],
"type": "ct" "type": "ct"
}, },
{
"name": "Salt",
"slug": "salt",
"desc": "SaltStack Salt is a software for automating the management and configuration of IT infrastructure and applications. It is an event-driven automation tool and framework used to deploy, configure, and manage complex IT systems. Its primary functions include configuration management, where it ensures consistent configurations and manages operating system deployment and software installation. It also automates and orchestrates routine IT processes and can create self-aware, self-healing systems.",
"script": "ct/salt.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/salt.sh",
"categories": [
19
],
"notes": [],
"type": "ct"
},
{ {
"name": "Proxmox VE CPU Scaling Governor", "name": "Proxmox VE CPU Scaling Governor",
"slug": "scaling-governor", "slug": "scaling-governor",
@ -4164,6 +4071,18 @@
], ],
"type": "pve" "type": "pve"
}, },
{
"name": "SearXNG",
"slug": "searxng",
"desc": "SearXNG is a free internet metasearch engine which aggregates results from up to 215 search services. Users are neither tracked nor profiled. Additionally, SearXNG can be used over Tor for online anonymity.",
"script": "ct/searxng.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/searxng.sh",
"categories": [
0
],
"notes": [],
"type": "ct"
},
{ {
"name": "seelf", "name": "seelf",
"slug": "seelf", "slug": "seelf",
@ -4356,20 +4275,6 @@
"password": "null" "password": "null"
} }
}, },
{
"name": "Suwayomi-Server",
"slug": "suwayomi-server",
"desc": "A free and open source manga reader server that runs extensions built for Mihon (Tachiyomi).",
"script": "ct/suwayomiserver.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/suwayomiserver.sh",
"categories": [
13
],
"notes": [
"This application can be conflicting with Kaspersky products. You maybe need to disable Kaspersky in order to use this application."
],
"type": "ct"
},
{ {
"name": "Syncthing", "name": "Syncthing",
"slug": "syncthing", "slug": "syncthing",
@ -4444,21 +4349,6 @@
], ],
"type": "ct" "type": "ct"
}, },
{
"name": "Teamspeak-Server",
"slug": "teamspeak-server",
"desc": "TeamSpeak is a voice over IP (VoIP) application, primarily used by gamers and teams to chat in real time on dedicated servers. It delivers crystal\u2011clear, low\u2011latency voice communication.",
"script": "ct/teamspeak-server.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/teamspeak-server.sh",
"categories": [
24
],
"notes": [
"Use `journalctl -u teamspeak-server.service` inside Debian LXC console to check for admin credentials!",
"Use `cat /var/log/teamspeak.err.log` inside Alpine LXC console to check for admin credentials!"
],
"type": "ct"
},
{ {
"name": "Technitium DNS", "name": "Technitium DNS",
"slug": "technitiumdns", "slug": "technitiumdns",
@ -4580,21 +4470,6 @@
"notes": [], "notes": [],
"type": "ct" "type": "ct"
}, },
{
"name": "Tududi",
"slug": "tududi",
"desc": "Self-hosted task management with functional programming architecture, hierarchical organization, and multi-language support.",
"script": "ct/tududi.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/tududi.sh",
"categories": [
12
],
"notes": [
"Create users like this: `cd /opt/tududi` => `npm run user:create <email> <password>`",
"Database location: `/opt/tududi-db`. Uploads: `/opt/tududi-uploads`"
],
"type": "ct"
},
{ {
"name": "TurnKey", "name": "TurnKey",
"slug": "turnkey", "slug": "turnkey",
@ -4664,6 +4539,20 @@
], ],
"type": "vm" "type": "vm"
}, },
{
"name": "Ubuntu 24.10",
"slug": "ubuntu2410-vm",
"desc": "Ubuntu is a distribution based on Debian, designed to have regular releases and a consistent user experience.",
"script": "vm/ubuntu2410-vm.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2410-vm.sh",
"categories": [
2
],
"notes": [
"after installation, checkout: \u00b4https://github.com/community-scripts/ProxmoxVE/discussions/272\u00b4"
],
"type": "vm"
},
{ {
"name": "Ubuntu 25.04", "name": "Ubuntu 25.04",
"slug": "ubuntu2504-vm", "slug": "ubuntu2504-vm",
@ -4844,9 +4733,7 @@
"categories": [ "categories": [
8 8
], ],
"notes": [ "notes": [],
"Included option to install VictoriaLogs."
],
"type": "ct" "type": "ct"
}, },
{ {
@ -5024,8 +4911,7 @@
4 4
], ],
"notes": [ "notes": [
"Wireguard and WGDashboard are not the same. More info: `https://docs.wgdashboard.dev/what-is-wireguard-what-is-wgdashboard.html`", "Wireguard and WGDashboard are not the same. More info: `https://docs.wgdashboard.dev/what-is-wireguard-what-is-wgdashboard.html`"
"WGDashboard installation is optional.`"
], ],
"type": "ct", "type": "ct",
"default_credentials": { "default_credentials": {

View File

@ -31,7 +31,7 @@ if [[ -f "$UTILS_FILE" ]]; then
fi fi
load_language load_language
initialize_cache initialize_cache
show_proxmenux_logo
# ========================================================== # ==========================================================

View File

@ -1,335 +0,0 @@
#!/bin/bash
# ==========================================================
# Common Functions for Proxmox VE Scripts
# ==========================================================
# Configuration
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
get_pve_info() {
local pve_full_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
local pve_major=$(echo "$pve_full_version" | cut -d. -f1)
local os_codename="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs)"
if [ -z "$os_codename" ]; then
os_codename=$(lsb_release -cs 2>/dev/null)
fi
local target_codename
if [ "$pve_major" -ge 9 ] 2>/dev/null; then
target_codename="trixie"
else
target_codename="$os_codename"
if [ -z "$target_codename" ]; then
target_codename="bookworm"
fi
fi
echo "$pve_full_version|$pve_major|$os_codename|$target_codename"
}
lvm_repair_check() {
msg_info "$(translate "Checking and repairing old LVM PV headers (if needed)...")"
if ! command -v pvs >/dev/null 2>&1; then
msg_info "$(translate "LVM tools not available, skipping LVM check")"
return
fi
pvs_output=$(LC_ALL=C pvs -v 2>&1 | grep "old PV header" || true)
if [ -z "$pvs_output" ]; then
msg_ok "$(translate "No PVs with old headers found.")"
return
fi
declare -A vg_map
while read -r line; do
pv=$(echo "$line" | grep -o '/dev/[^ ]*' || true)
if [ -n "$pv" ]; then
vg=$(pvs -o vg_name --noheadings "$pv" 2>/dev/null | awk '{print $1}' || true)
if [ -n "$vg" ]; then
vg_map["$vg"]=1
fi
fi
done <<< "$pvs_output"
for vg in "${!vg_map[@]}"; do
msg_warn "$(translate "Old PV header(s) found in VG $vg. Updating metadata...")"
vgck --updatemetadata "$vg" 2>/dev/null
vgchange -ay "$vg" 2>/dev/null
if [ $? -ne 0 ]; then
msg_warn "$(translate "Metadata update failed for VG $vg. Review manually.")"
else
msg_ok "$(translate "Metadata updated successfully for VG $vg")"
fi
done
msg_ok "$(translate "LVM PV headers check completed")"
}
cleanup_duplicate_repos_pve9() {
msg_info "$(translate "Cleaning up duplicate repositories...")"
local sources_file="/etc/apt/sources.list"
local temp_file=$(mktemp)
local cleaned_count=0
declare -A seen_repos
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then
echo "$line" >> "$temp_file"
continue
fi
if [[ "$line" =~ ^deb ]]; then
read -r _ url dist components <<< "$line"
local key="${url}_${dist}"
if [[ -v "seen_repos[$key]" ]]; then
echo "# $line" >> "$temp_file"
cleaned_count=$((cleaned_count + 1))
msg_info "$(translate "Commented duplicate: $url $dist")"
else
echo "$line" >> "$temp_file"
seen_repos[$key]="$components"
fi
else
echo "$line" >> "$temp_file"
fi
done < "$sources_file"
mv "$temp_file" "$sources_file"
chmod 644 "$sources_file"
if [ -f "/etc/apt/sources.list.d/proxmox.sources" ]; then
if grep -q "^deb.*download\.proxmox\.com" "$sources_file"; then
sed -i '/^deb.*download\.proxmox\.com/s/^/# /' "$sources_file"
cleaned_count=$((cleaned_count + 1))
fi
for list_file in /etc/apt/sources.list.d/pve-*.list; do
if [ -f "$list_file" ] && [[ "$list_file" != "/etc/apt/sources.list.d/pve-enterprise.list" ]]; then
if grep -q "^deb" "$list_file"; then
sed -i 's/^deb/# deb/g' "$list_file"
cleaned_count=$((cleaned_count + 1))
fi
fi
done
if [ -f "/etc/apt/sources.list.d/debian.sources" ]; then
if grep -q "^deb.*deb\.debian\.org" "$sources_file"; then
sed -i '/^deb.*deb\.debian\.org/s/^/# /' "$sources_file"
cleaned_count=$((cleaned_count + 1))
fi
if grep -q "^deb.*security\.debian\.org" "$sources_file"; then
sed -i '/^deb.*security\.debian\.org/s/^/# /' "$sources_file"
cleaned_count=$((cleaned_count + 1))
fi
fi
fi
if [ -f "/etc/apt/sources.list.d/proxmox.sources" ]; then
for old_file in /etc/apt/sources.list.d/pve-public-repo.list /etc/apt/sources.list.d/pve-install-repo.list; do
if [ -f "$old_file" ]; then
rm -f "$old_file"
cleaned_count=$((cleaned_count + 1))
fi
done
fi
if [ $cleaned_count -gt 0 ]; then
msg_ok "$(translate "Cleaned up $cleaned_count duplicate/old repositories")"
apt-get update > /dev/null 2>&1 || true
else
msg_ok "$(translate "No duplicate repositories found")"
fi
}
cleanup_duplicate_repos_pve9_() {
msg_info "$(translate "Cleaning up duplicate repositories...")"
local sources_file="/etc/apt/sources.list"
local temp_file=$(mktemp)
local cleaned_count=0
declare -A seen_repos
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then
echo "$line" >> "$temp_file"
continue
fi
if [[ "$line" =~ ^deb ]]; then
read -r _ url dist components <<< "$line"
local key="${url}_${dist}"
if [[ -v "seen_repos[$key]" ]]; then
echo "# $line" >> "$temp_file"
cleaned_count=$((cleaned_count + 1))
else
echo "$line" >> "$temp_file"
seen_repos[$key]="$components"
fi
else
echo "$line" >> "$temp_file"
fi
done < "$sources_file"
mv "$temp_file" "$sources_file"
chmod 644 "$sources_file"
for src in proxmox debian ceph; do
local sources_path="/etc/apt/sources.list.d/${src}.sources"
if [ -f "$sources_path" ]; then
case "$src" in
proxmox)
url_match="download.proxmox.com"
;;
debian)
url_match="deb.debian.org"
;;
ceph)
url_match="download.proxmox.com/ceph"
;;
*)
url_match=""
;;
esac
if [[ -n "$url_match" ]]; then
if grep -q "^deb.*$url_match" "$sources_file"; then
sed -i "/^deb.*$url_match/s/^/# /" "$sources_file"
cleaned_count=$((cleaned_count + 1))
fi
fi
for list_file in /etc/apt/sources.list.d/*.list; do
[[ -f "$list_file" ]] || continue
if grep -q "^deb.*$url_match" "$list_file"; then
sed -i "/^deb.*$url_match/s/^/# /" "$list_file"
cleaned_count=$((cleaned_count + 1))
fi
done
fi
done
if [ $cleaned_count -gt 0 ]; then
msg_ok "$(translate "Cleaned up $cleaned_count duplicate/old repositories")"
apt-get update > /dev/null 2>&1 || true
else
msg_ok "$(translate "No duplicate repositories found")"
fi
}
cleanup_duplicate_repos_pve8() {
msg_info "$(translate "Cleaning up duplicate repositories...")"
local cleaned_count=0
local sources_file="/etc/apt/sources.list"
if [[ -f "$sources_file" ]]; then
local temp_file
temp_file=$(mktemp)
declare -A seen_repos
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then
echo "$line" >> "$temp_file"
continue
fi
if [[ "$line" =~ ^[[:space:]]*deb ]]; then
read -r _ url dist components <<< "$line"
local key="${url}_${dist}"
if [[ -v "seen_repos[$key]" ]]; then
echo "# $line" >> "$temp_file"
cleaned_count=$((cleaned_count + 1))
else
echo "$line" >> "$temp_file"
seen_repos[$key]="$components"
fi
else
echo "$line" >> "$temp_file"
fi
done < "$sources_file"
mv "$temp_file" "$sources_file"
chmod 644 "$sources_file"
fi
local old_pve_files=(/etc/apt/sources.list.d/pve-*.list /etc/apt/sources.list.d/proxmox.list)
for file in "${old_pve_files[@]}"; do
if [[ -f "$file" ]]; then
local base_name
base_name=$(basename "$file" .list)
local sources_equiv="/etc/apt/sources.list.d/${base_name}.sources"
if [[ -f "$sources_equiv" ]] && grep -q "^Enabled: *true" "$sources_equiv"; then
msg_info "$(translate "Removing old repository file: $(basename "$file")")"
rm -f "$file"
cleaned_count=$((cleaned_count + 1))
fi
fi
done
if [ "$cleaned_count" -gt 0 ]; then
msg_ok "$(translate "Cleaned up $cleaned_count duplicate/old repositories")"
apt-get update > /dev/null 2>&1 || true
else
msg_ok "$(translate "No duplicate repositories found")"
fi
}
cleanup_duplicate_repos() {
local pve_version
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
if [[ -z "$pve_version" ]]; then
msg_error "Unable to detect Proxmox version."
return 1
fi
if [[ "$pve_version" -ge 9 ]]; then
cleanup_duplicate_repos_pve9
else
cleanup_duplicate_repos_pve8
fi
}

View File

@ -1,76 +0,0 @@
#!/bin/bash
# ==========================================================
# Remove Subscription Banner - Proxmox VE 8.4.9
# ==========================================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
register_tool() {
local tool="$1"
local state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
remove_subscription_banner_pve8() {
local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script"
local BACKUP_FILE="${JS_FILE}.bak.$(date +%F_%T)"
local pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1)
local pve_major=$(echo "$pve_version" | cut -d. -f1)
if [[ "$pve_major" -ne 8 ]]; then
msg_error "This script is only for Proxmox VE 8.x. Detected: $pve_version"
return 1
fi
msg_info "Detected Proxmox VE $pve_version - Applying safe JS patch..."
if [[ ! -f "$JS_FILE" ]]; then
msg_error "JavaScript file not found: $JS_FILE"
return 1
fi
cp "$JS_FILE" "$BACKUP_FILE"
sed -i "s/No valid subscription/Subscription active/g" "$JS_FILE"
sed -i "s/Ext.Msg.WARNING/Ext.Msg.INFO/g" "$JS_FILE"
sed -i "s/res.data.status.toLowerCase() !== 'active'/false/g" "$JS_FILE"
sed -i "s/subscriptionActive: ''/subscriptionActive: true/g" "$JS_FILE"
[[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE"
find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
[[ -f "$APT_HOOK" ]] && rm -f "$APT_HOOK"
msg_ok "Subscription banner removed successfully."
register_tool "subscription_banner" true
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
remove_subscription_banner_pve8
fi

View File

@ -1,119 +0,0 @@
#!/bin/bash
# ==========================================================
# Remove Subscription Banner - Proxmox VE 9.x ONLY
# ==========================================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# Tool registration system
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
register_tool() {
local tool="$1"
local state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
remove_subscription_banner_pve9() {
local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
local MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script"
# Verify PVE 9.x
local pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1)
local pve_major=$(echo "$pve_version" | cut -d. -f1)
if [ "$pve_major" -lt 9 ] 2>/dev/null; then
msg_error "This script is for PVE 9.x only. Detected PVE $pve_version"
return 1
fi
msg_info "Detected Proxmox VE $pve_version - Applying PVE 9.x patches"
# Verify that the file exists
if [ ! -f "$JS_FILE" ]; then
msg_error "JavaScript file not found: $JS_FILE"
return 1
fi
# Create backup of original file
local backup_file="${JS_FILE}.backup.pve9.$(date +%Y%m%d_%H%M%S)"
cp "$JS_FILE" "$backup_file"
# Clean any existing problematic APT hooks
for f in /etc/apt/apt.conf.d/*nag*; do
[[ -e "$f" ]] && rm -f "$f"
done
# Main subscription check patches for PVE 9
sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE"
sed -i "s/subscriptionActive: ''/subscriptionActive: true/g" "$JS_FILE"
sed -i "s/title: gettext('No valid subscription')/title: gettext('Community Edition')/g" "$JS_FILE"
# Additional UX improvements for PVE 9
sed -i "s/You do not have a valid subscription for this server/Community Edition - No subscription required/g" "$JS_FILE"
sed -i "s/Enterprise repository needs valid subscription/Enterprise repository configured/g" "$JS_FILE"
sed -i "s/icon: Ext\.Msg\.WARNING/icon: Ext.Msg.INFO/g" "$JS_FILE"
# Additional subscription patterns that may exist in PVE 9
sed -i "s/subscription = !(/subscription = false \&\& (/g" "$JS_FILE"
# Remove compressed/minified files to force regeneration
[[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE"
[[ -f "$MIN_JS_FILE" ]] && rm -f "$MIN_JS_FILE"
# Clear various caches
find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
# Create PVE 9.x specific APT hook
[[ -f "$APT_HOOK" ]] && rm -f "$APT_HOOK"
cat > "$APT_HOOK" << 'EOF'
DPkg::Post-Invoke {
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/res\\.data\\.status\\.toLowerCase() !== '\''active'\''/false/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscriptionActive: '\'\'\''/subscriptionActive: true/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/title: gettext('\''No valid subscription'\'')/title: gettext('\''Community Edition'\'')/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscription = !(/subscription = false \\&\\& (/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz || true";
};
EOF
chmod 644 "$APT_HOOK"
# Verify APT hook syntax
if ! apt-config dump >/dev/null 2>&1; then
msg_warn "APT hook has syntax issues, removing..."
rm -f "$APT_HOOK"
else
msg_ok "APT hook created successfully"
fi
msg_ok "Subscription banner removed successfully for Proxmox VE $pve_version"
msg_ok "Banner removal process completed"
register_tool "subscription_banner" true
}
# Execute function if called directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
remove_subscription_banner_pve9
fi

View File

@ -1,337 +0,0 @@
#!/bin/bash
# ==========================================================
# Proxmox VE Update Script
# ==========================================================
# Configuration
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
register_tool() {
local tool="$1"
local state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
download_common_functions() {
if ! source <(curl -s "$REPO_URL/scripts/global/common-functions.sh"); then
return 1
fi
}
update_pve9() {
local start_time=$(date +%s)
local log_file="/var/log/proxmox-update-$(date +%Y%m%d-%H%M%S).log"
local changes_made=false
local OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs)"
local TARGET_CODENAME="trixie"
if [ -z "$OS_CODENAME" ]; then
OS_CODENAME=$(lsb_release -cs 2>/dev/null || echo "trixie")
fi
download_common_functions
msg_info2 "$(translate "Detected: Proxmox VE 9.x (Current: $OS_CODENAME, Target: $TARGET_CODENAME)")"
echo
local available_space=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}')
if [ "$available_space" -lt 1024 ]; then
msg_error "$(translate "Insufficient disk space. Available: ${available_space}MB")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
if ! ping -c 1 download.proxmox.com >/dev/null 2>&1; then
msg_error "$(translate "Cannot reach Proxmox repositories")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
disable_sources_repo() {
local file="$1"
if [[ -f "$file" ]]; then
sed -i ':a;/^\n*$/{$d;N;ba}' "$file"
if grep -q "^Enabled:" "$file"; then
sed -i 's/^Enabled:.*$/Enabled: false/' "$file"
else
echo "Enabled: false" >> "$file"
fi
if ! grep -q "^Types: " "$file"; then
msg_warn "$(translate "Malformed .sources file detected, removing: $(basename "$file")")"
rm -f "$file"
fi
return 0
fi
return 1
}
if disable_sources_repo "/etc/apt/sources.list.d/pve-enterprise.sources"; then
msg_ok "$(translate "Enterprise Proxmox repository disabled")"
changes_made=true
fi
if disable_sources_repo "/etc/apt/sources.list.d/ceph.sources"; then
msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")"
changes_made=true
fi
for legacy_file in /etc/apt/sources.list.d/pve-public-repo.list \
/etc/apt/sources.list.d/pve-install-repo.list \
/etc/apt/sources.list.d/debian.list; do
if [[ -f "$legacy_file" ]]; then
rm -f "$legacy_file"
msg_ok "$(translate "Removed legacy repository: $(basename "$legacy_file")")"
fi
done
if [[ -f /etc/apt/sources.list.d/debian.sources ]]; then
rm -f /etc/apt/sources.list.d/debian.sources
msg_ok "$(translate "Old debian.sources file removed to prevent duplication")"
fi
msg_info "$(translate "Creating Proxmox VE 9.x no-subscription repository...")"
cat > /etc/apt/sources.list.d/proxmox.sources << EOF
Enabled: true
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: ${TARGET_CODENAME}
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
msg_ok "$(translate "Proxmox VE 9.x no-subscription repository created")"
changes_made=true
msg_info "$(translate "Creating Debian ${TARGET_CODENAME} sources file...")"
cat > /etc/apt/sources.list.d/debian.sources << EOF
Types: deb
URIs: http://deb.debian.org/debian/
Suites: ${TARGET_CODENAME} ${TARGET_CODENAME}-updates
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security/
Suites: ${TARGET_CODENAME}-security
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
EOF
msg_ok "$(translate "Debian repositories configured for $TARGET_CODENAME")"
local firmware_conf="/etc/apt/apt.conf.d/no-firmware-warnings.conf"
if [ ! -f "$firmware_conf" ]; then
msg_info "$(translate "Disabling non-free firmware warnings...")"
echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > "$firmware_conf"
msg_ok "$(translate "Non-free firmware warnings disabled")"
fi
cleanup_duplicate_repos
update_output=$(apt-get update 2>&1)
update_exit_code=$?
if [ $update_exit_code -eq 0 ]; then
msg_ok "$(translate "Package lists updated successfully")"
else
if echo "$update_output" | grep -q "NO_PUBKEY\|GPG error"; then
msg_info "$(translate "Fixing GPG key issues...")"
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $(echo "$update_output" | grep "NO_PUBKEY" | sed 's/.*NO_PUBKEY //' | head -1) 2>/dev/null
if apt-get update > "$log_file" 2>&1; then
msg_ok "$(translate "Package lists updated after GPG fix")"
else
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
return 1
fi
elif echo "$update_output" | grep -q "404\|Failed to fetch"; then
msg_warn "$(translate "Some repositories are not available, continuing with available ones...")"
else
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
echo "Error details: $update_output"
return 1
fi
fi
if apt policy 2>/dev/null | grep -q "${TARGET_CODENAME}.*pve-no-subscription"; then
msg_ok "$(translate "Proxmox VE 9.x repositories verified")"
else
msg_warn "$(translate "Proxmox VE 9.x repositories verification inconclusive, continuing...")"
fi
local current_pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
local available_pve_version=$(apt-cache policy pve-manager 2>/dev/null | grep -oP 'Candidate: \K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
local upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable")
local security_updates=$(apt list --upgradable 2>/dev/null | grep -c "security")
show_update_menu() {
local current_version="$1"
local target_version="$2"
local upgradable_count="$3"
local security_count="$4"
local menu_text="$(translate "System Update Information")\n\n"
menu_text+="$(translate "Current PVE Version"): $current_version\n"
if [ -n "$target_version" ] && [ "$target_version" != "$current_version" ]; then
menu_text+="$(translate "Available PVE Version"): $target_version\n"
fi
menu_text+="\n$(translate "Package Updates Available"): $upgradable_count\n"
menu_text+="$(translate "Security Updates"): $security_count\n\n"
if [ "$upgradable_count" -eq 0 ]; then
menu_text+="$(translate "System is already up to date")"
whiptail --title "$(translate "Update Status")" --msgbox "$menu_text" 15 70
return 2
else
menu_text+="$(translate "Do you want to proceed with the system update?")"
if whiptail --title "$(translate "Proxmox Update")" --yesno "$menu_text" 18 70; then
return 0
else
return 1
fi
fi
}
show_update_menu "$current_pve_version" "$available_pve_version" "$upgradable" "$security_updates"
MENU_RESULT=$?
if [[ $MENU_RESULT -eq 1 ]]; then
msg_info2 "$(translate "Update cancelled by user")"
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
return 0
elif [[ $MENU_RESULT -eq 2 ]]; then
msg_ok "$(translate "System is already up to date. No update needed.")"
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
return 0
fi
msg_info "$(translate "Removing conflicting utilities...")"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' purge ntp openntpd systemd-timesyncd > /dev/null 2>&1; then
msg_ok "$(translate "Conflicting utilities removed")"
else
msg_warn "$(translate "Some conflicting utilities may not have been removed")"
fi
msg_info "$(translate "Updating packages...")"
apt-get install pv -y > /dev/null 2>&1
msg_ok "$(translate "Packages updated successfully")"
tput sc
DEBIAN_FRONTEND=noninteractive apt-get -y \
-o Dpkg::Options::='--force-confdef' \
-o Dpkg::Options::='--force-confold' \
dist-upgrade 2>&1 | while IFS= read -r line; do
echo "$line" >> "$log_file"
if [[ "$line" =~ \[[#=\-]+\]\ *[0-9]{1,3}% ]]; then
continue
fi
if [[ "$line" =~ ^(Setting\ up|Unpacking|Preparing\ to\ unpack|Processing\ triggers\ for) ]]; then
package_name=$(echo "$line" | sed -E 's/.*(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ([^ :]+).*/\2/')
[ -z "$package_name" ] && package_name="$(translate "Unknown")"
row=$(( $(tput lines) - 6 ))
tput cup $row 0; printf "%s\n" "$(translate "Installing packages...")"
tput cup $((row + 1)) 0; printf "%s\n" "──────────────────────────────────────────────"
tput cup $((row + 2)) 0; printf "%s %s\n" "$(translate "Package:")" "$package_name"
tput cup $((row + 3)) 0; printf "%s\n" "Progress: [ ] 0%"
tput cup $((row + 4)) 0; printf "%s\n" "──────────────────────────────────────────────"
for i in $(seq 1 10); do
sleep 0.1
progress=$((i * 10))
tput cup $((row + 3)) 9
printf "[%-50s] %3d%%" "$(printf "#%.0s" $(seq 1 $((progress/2))))" "$progress"
done
fi
done
tput rc
tput ed
upgrade_exit_code=${PIPESTATUS[0]}
if [ $upgrade_exit_code -eq 0 ]; then
msg_ok "$(translate "System upgrade completed successfully")"
else
msg_error "$(translate "System upgrade failed. Check log: $log_file")"
return 1
fi
msg_info "$(translate "Installing essential Proxmox packages...")"
local additional_packages="zfsutils-linux proxmox-backup-restore-image chrony"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install $additional_packages >> "$log_file" 2>&1; then
msg_ok "$(translate "Essential Proxmox packages installed")"
else
msg_warn "$(translate "Some essential Proxmox packages may not have been installed")"
fi
lvm_repair_check
cleanup_duplicate_repos
msg_info "$(translate "Performing system cleanup...")"
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
msg_ok "$(translate "Cleanup finished")"
local end_time=$(date +%s)
local duration=$((end_time - start_time))
local minutes=$((duration / 60))
local seconds=$((duration % 60))
echo -e "${TAB}${BGN}$(translate "====== PVE UPDATE COMPLETED ======")${CL}"
echo -e "${TAB}${GN}⏱️ $(translate "Duration")${CL}: ${BL}${minutes}m ${seconds}s${CL}"
echo -e "${TAB}${GN}📄 $(translate "Log file")${CL}: ${BL}$log_file${CL}"
echo -e "${TAB}${GN}📦 $(translate "Packages upgraded")${CL}: ${BL}$upgradable${CL}"
echo -e "${TAB}${GN}🖥️ $(translate "Proxmox VE")${CL}: ${BL}$target_version (Debian $OS_CODENAME)${CL}"
msg_ok "$(translate "Proxmox VE 9.x configuration completed.")"
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
update_pve9
fi

View File

@ -1,284 +0,0 @@
#!/bin/bash
# ==========================================================
# Proxmox VE 8.x Update Script
# ==========================================================
# Configuration
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
register_tool() {
local tool="$1"
local state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
download_common_functions() {
if ! source <(curl -s "$REPO_URL/scripts/global/common-functions.sh"); then
return 1
fi
}
update_pve8() {
local start_time=$(date +%s)
local log_file="/var/log/proxmox-update-$(date +%Y%m%d-%H%M%S).log"
local changes_made=false
local OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs)"
if [ -z "$OS_CODENAME" ]; then
OS_CODENAME=$(lsb_release -cs 2>/dev/null || echo "bookworm")
fi
download_common_functions
msg_info2 "$(translate "Detected: Proxmox VE 8.x (Debian $OS_CODENAME)")"
echo
local available_space=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}')
if [ "$available_space" -lt 1024 ]; then
msg_error "$(translate "Insufficient disk space. Available: ${available_space}MB")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
if ! ping -c 1 download.proxmox.com >/dev/null 2>&1; then
msg_error "$(translate "Cannot reach Proxmox repositories")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
if [ -f /etc/apt/sources.list.d/pve-enterprise.list ] && grep -q "^deb" /etc/apt/sources.list.d/pve-enterprise.list; then
sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/pve-enterprise.list
msg_ok "$(translate "Enterprise Proxmox repository disabled")"
changes_made=true
fi
if [ -f /etc/apt/sources.list.d/ceph.list ] && grep -q "^deb" /etc/apt/sources.list.d/ceph.list; then
sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/ceph.list
msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")"
changes_made=true
fi
if [ ! -f /etc/apt/sources.list.d/pve-public-repo.list ] || ! grep -q "pve-no-subscription" /etc/apt/sources.list.d/pve-public-repo.list; then
echo "deb http://download.proxmox.com/debian/pve $OS_CODENAME pve-no-subscription" > /etc/apt/sources.list.d/pve-public-repo.list
msg_ok "$(translate "Free public Proxmox repository enabled")"
changes_made=true
fi
local sources_file="/etc/apt/sources.list"
cp "$sources_file" "${sources_file}.backup.$(date +%Y%m%d_%H%M%S)"
if grep -q -E "(debian-security -security|debian main$|debian -updates)" "$sources_file"; then
sed -i '/^deb.*debian-security -security/d' "$sources_file"
sed -i '/^deb.*debian main$/d' "$sources_file"
sed -i '/^deb.*debian -updates/d' "$sources_file"
changes_made=true
msg_ok "$(translate "Malformed repository entries cleaned")"
fi
cat > "$sources_file" << EOF
# Debian $OS_CODENAME repositories
deb http://deb.debian.org/debian $OS_CODENAME main contrib non-free non-free-firmware
deb http://deb.debian.org/debian $OS_CODENAME-updates main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security $OS_CODENAME-security main contrib non-free non-free-firmware
EOF
msg_ok "$(translate "Debian repositories configured for $OS_CODENAME")"
local firmware_conf="/etc/apt/apt.conf.d/no-firmware-warnings.conf"
if [ ! -f "$firmware_conf" ]; then
echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > "$firmware_conf"
fi
cleanup_duplicate_repos
msg_info "$(translate "Updating package lists...")"
if apt-get update > "$log_file" 2>&1; then
msg_ok "$(translate "Package lists updated successfully")"
else
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
return 1
fi
local current_pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
local available_pve_version=$(apt-cache policy pve-manager 2>/dev/null | grep -oP 'Candidate: \K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
local upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable")
local security_updates=$(apt list --upgradable 2>/dev/null | grep -c "security")
show_update_menu() {
local current_version="$1"
local target_version="$2"
local upgradable_count="$3"
local security_count="$4"
local menu_text="$(translate "System Update Information")\n\n"
menu_text+="$(translate "Current PVE Version"): $current_version\n"
if [ -n "$target_version" ] && [ "$target_version" != "$current_version" ]; then
menu_text+="$(translate "Available PVE Version"): $target_version\n"
fi
menu_text+="\n$(translate "Package Updates Available"): $upgradable_count\n"
menu_text+="$(translate "Security Updates"): $security_count\n\n"
if [ "$upgradable_count" -eq 0 ]; then
menu_text+="$(translate "System is already up to date")"
whiptail --title "$(translate "Update Status")" --msgbox "$menu_text" 15 70
return 2
else
menu_text+="$(translate "Do you want to proceed with the system update?")"
if whiptail --title "$(translate "Proxmox Update")" --yesno "$menu_text" 18 70; then
return 0
else
return 1
fi
fi
}
show_update_menu "$current_pve_version" "$available_pve_version" "$upgradable" "$security_updates"
MENU_RESULT=$?
if [[ $MENU_RESULT -eq 1 ]]; then
msg_info2 "$(translate "Update cancelled by user")"
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
return 0
elif [[ $MENU_RESULT -eq 2 ]]; then
msg_ok "$(translate "System is already up to date. No update needed.")"
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
return 0
fi
local conflicting_packages=$(dpkg -l 2>/dev/null | grep -E "^ii.*(ntp|openntpd|systemd-timesyncd)" | awk '{print $2}')
if [ -n "$conflicting_packages" ]; then
msg_info "$(translate "Removing conflicting utilities...")"
DEBIAN_FRONTEND=noninteractive apt-get -y purge $conflicting_packages >> "$log_file" 2>&1
msg_ok "$(translate "Conflicting utilities removed")"
fi
export DEBIAN_FRONTEND=noninteractive
export APT_LISTCHANGES_FRONTEND=none
export NEEDRESTART_MODE=a
export UCF_FORCE_CONFOLD=1
export DPKG_OPTIONS="--force-confdef --force-confold"
msg_info "$(translate "Performing packages upgrade...")"
apt-get install pv -y > /dev/null 2>&1
total_packages=$(apt-get -s dist-upgrade | grep "^Inst" | wc -l)
msg_ok "$(translate "Packages upgrade successfull")"
if [ "$total_packages" -eq 0 ]; then
total_packages=1
fi
tput civis
tput sc
(
/usr/bin/env \
DEBIAN_FRONTEND=noninteractive \
APT_LISTCHANGES_FRONTEND=none \
NEEDRESTART_MODE=a \
UCF_FORCE_CONFOLD=1 \
apt-get -y \
-o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confold" \
dist-upgrade 2>&1 | \
while IFS= read -r line; do
if [[ "$line" =~ ^(Setting\ up|Unpacking|Preparing\ to\ unpack|Processing\ triggers\ for) ]]; then
package_name=$(echo "$line" | sed -E 's/.*(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ([^ ]+).*/\2/')
[ -z "$package_name" ] && package_name="$(translate "Unknown")"
tput rc
tput ed
row=$(( $(tput lines) - 6 ))
tput cup $row 0; echo "$(translate "Installing packages...")"
tput cup $((row + 1)) 0; echo "──────────────────────────────────────────────"
tput cup $((row + 2)) 0; echo "Package: $package_name"
tput cup $((row + 3)) 0; echo "Progress: [ ] 0%"
tput cup $((row + 4)) 0; echo "──────────────────────────────────────────────"
for i in $(seq 1 10); do
progress=$((i * 10))
tput cup $((row + 3)) 9
printf "[%-50s] %3d%%" "$(printf "#%.0s" $(seq 1 $((progress/2))))" "$progress"
done
fi
done
)
if [ $? -eq 0 ]; then
tput rc
tput ed
tput cnorm
msg_ok "$(translate "System upgrade completed")"
fi
local essential_packages=("zfsutils-linux" "proxmox-backup-restore-image" "chrony")
local missing_packages=()
for package in "${essential_packages[@]}"; do
if ! dpkg -l 2>/dev/null | grep -q "^ii $package "; then
missing_packages+=("$package")
fi
done
if [ ${#missing_packages[@]} -gt 0 ]; then
msg_info "$(translate "Installing essential Proxmox packages...")"
DEBIAN_FRONTEND=noninteractive apt-get -y install "${missing_packages[@]}" >> "$log_file" 2>&1
msg_ok "$(translate "Essential Proxmox packages installed")"
fi
lvm_repair_check
cleanup_duplicate_repos
msg_info "$(translate "Performing system cleanup...")"
apt-get -y autoremove > /dev/null 2>&1 || true
apt-get -y autoclean > /dev/null 2>&1 || true
msg_ok "$(translate "Cleanup finished")"
local end_time=$(date +%s)
local duration=$((end_time - start_time))
local minutes=$((duration / 60))
local seconds=$((duration % 60))
echo -e "${TAB}${BGN}$(translate "====== PVE UPDATE COMPLETED ======")${CL}"
echo -e "${TAB}${GN}⏱️ $(translate "Duration")${CL}: ${BL}${minutes}m ${seconds}s${CL}"
echo -e "${TAB}${GN}📄 $(translate "Log file")${CL}: ${BL}$log_file${CL}"
echo -e "${TAB}${GN}📦 $(translate "Packages upgraded")${CL}: ${BL}$upgradable${CL}"
echo -e "${TAB}${GN}🖥️ $(translate "Proxmox VE")${CL}: ${BL}$target_version (Debian $OS_CODENAME)${CL}"
msg_ok "$(translate "Proxmox VE 8 system update completed successfully")"
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
update_pve8
fi

View File

@ -72,8 +72,8 @@ validate_container_id() {
fi fi
} }
# Añadir regla udev para Coral USB para persistencia de permisos
add_udev_rule_for_coral_usb_() { add_udev_rule_for_coral_usb() {
RULE_FILE="/etc/udev/rules.d/99-coral-usb.rules" RULE_FILE="/etc/udev/rules.d/99-coral-usb.rules"
RULE_CONTENT='SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666", TAG+="uaccess"' RULE_CONTENT='SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666", TAG+="uaccess"'
@ -87,22 +87,6 @@ add_udev_rule_for_coral_usb_() {
} }
add_udev_rule_for_coral_usb() {
RULE_FILE="/etc/udev/rules.d/99-coral-usb.rules"
RULE_CONTENT='# Coral USB Accelerator
SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666", TAG+="uaccess", SYMLINK+="coral"
# Coral Dev Board / Mini PCIe
SUBSYSTEM=="usb", ATTRS{idVendor}=="1a6e", ATTRS{idProduct}=="089a", MODE="0666", TAG+="uaccess", SYMLINK+="coral"'
if [[ ! -f "$RULE_FILE" ]] || ! grep -q "18d1.*9302\|1a6e.*089a" "$RULE_FILE"; then
echo "$RULE_CONTENT" > "$RULE_FILE"
udevadm control --reload-rules && udevadm trigger
msg_ok "$(translate 'Udev rules for Coral USB devices added and rules reloaded.')"
else
msg_ok "$(translate 'Udev rules for Coral USB devices already exist.')"
fi
}
add_mount_if_needed() { add_mount_if_needed() {
local DEVICE="$1" local DEVICE="$1"

View File

@ -1,135 +0,0 @@
#!/bin/bash
# Script para instalar JDownloader en un contenedor LXC desde el host Proxmox
# Autor: MacRimi
# Mostrar lista de CTs
CT_LIST=$(pct list | awk 'NR>1 {print $1, $3}')
if [ -z "$CT_LIST" ]; then
whiptail --title "Error" --msgbox "No hay contenedores LXC disponibles en el sistema." 8 50
exit 1
fi
# Seleccionar CT
CTID=$(whiptail --title "Instalación de JDownloader" --menu "Selecciona el contenedor donde instalar JDownloader:" 20 60 10 $CT_LIST 3>&1 1>&2 2>&3)
if [ -z "$CTID" ]; then
whiptail --title "Cancelado" --msgbox "No se ha seleccionado ningún contenedor." 8 40
exit 1
fi
# Solicitar email
EMAIL=$(whiptail --title "Cuenta My JDownloader" --inputbox "Introduce tu correo electrónico para vincular JDownloader:" 10 60 3>&1 1>&2 2>&3)
if [ -z "$EMAIL" ]; then
whiptail --title "Error" --msgbox "No se ha introducido ningún correo." 8 40
exit 1
fi
# Solicitar contraseña con confirmación
while true; do
PASSWORD=$(whiptail --title "Cuenta My JDownloader" --passwordbox "Introduce tu contraseña de My JDownloader:" 10 60 3>&1 1>&2 2>&3)
[ -z "$PASSWORD" ] && whiptail --title "Error" --msgbox "No se ha introducido ninguna contraseña." 8 40 && exit 1
CONFIRM=$(whiptail --title "Confirmación de contraseña" --passwordbox "Repite tu contraseña para confirmar:" 10 60 3>&1 1>&2 2>&3)
[ "$PASSWORD" = "$CONFIRM" ] && break
whiptail --title "Error" --msgbox "Las contraseñas no coinciden. Intenta de nuevo." 8 50
done
# Confirmación final
whiptail --title "Confirmar datos" --yesno "¿Deseas continuar con los siguientes datos?\n\nCorreo: $EMAIL\nContraseña: (oculta)\n\nEsta información se usará para vincular el contenedor con tu cuenta de My.JDownloader." 14 60
[ $? -ne 0 ] && whiptail --title "Cancelado" --msgbox "Instalación cancelada por el usuario." 8 40 && exit 1
clear
echo "🔍 Detectando sistema operativo dentro del CT $CTID..."
OS_ID=$(pct exec "$CTID" -- awk -F= '/^ID=/{gsub("\"",""); print $2}' /etc/os-release)
echo "Sistema detectado: $OS_ID"
echo "🧰 Preparando entorno..."
case "$OS_ID" in
debian)
# Repositorio adicional para Java 8
pct exec "$CTID" -- wget -q http://www.mirbsd.org/~tg/Debs/sources.txt/wtf-bookworm.sources
pct exec "$CTID" -- mv wtf-bookworm.sources /etc/apt/sources.list.d/
pct exec "$CTID" -- apt update -y
pct exec "$CTID" -- apt install -y openjdk-8-jdk wget
JAVA_PATH="/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java"
;;
ubuntu)
pct exec "$CTID" -- apt update -y
pct exec "$CTID" -- apt install -y openjdk-8-jdk wget
JAVA_PATH="/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java"
;;
alpine)
pct exec "$CTID" -- apk update
pct exec "$CTID" -- apk add openjdk8 wget
JAVA_PATH="/usr/lib/jvm/java-1.8-openjdk/bin/java"
;;
*)
echo "❌ Sistema operativo no soportado: $OS_ID"
exit 1
;;
esac
# Crear carpeta de instalación
pct exec "$CTID" -- mkdir -p /opt/jdownloader
pct exec "$CTID" -- bash -c 'cd /opt/jdownloader && curl -O https://installer.jdownloader.org/JDownloader.jar'
# Crear archivo de configuración JSON para My JDownloader
pct exec "$CTID" -- bash -c "mkdir -p /opt/jdownloader/cfg && cat > /opt/jdownloader/cfg/org.jdownloader.api.myjdownloader.MyJDownloaderSettings.json" <<EOF
{
"email" : "$EMAIL",
"password" : "$PASSWORD",
"enabled" : true
}
EOF
# Crear servicio según sistema
if [[ "$OS_ID" == "alpine" ]]; then
# Servicio OpenRC para Alpine
pct exec "$CTID" -- bash -c 'cat > /etc/init.d/jdownloader <<EOF
#!/sbin/openrc-run
command="/usr/bin/java"
command_args="-jar /opt/jdownloader/JDownloader.jar -norestart"
pidfile="/var/run/jdownloader.pid"
name="JDownloader"
depend() {
need net
}
EOF'
pct exec "$CTID" -- chmod +x /etc/init.d/jdownloader
pct exec "$CTID" -- rc-update add jdownloader default
pct exec "$CTID" -- rc-service jdownloader start
else
# Servicio systemd para Debian/Ubuntu
pct exec "$CTID" -- bash -c 'cat > /etc/systemd/system/jdownloader.service <<EOF
[Unit]
Description=JDownloader
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/jdownloader
ExecStart=/usr/bin/java -jar JDownloader.jar -norestart
Restart=always
[Install]
WantedBy=multi-user.target
EOF'
pct exec "$CTID" -- systemctl daemon-reexec
pct exec "$CTID" -- systemctl daemon-reload
pct exec "$CTID" -- systemctl enable jdownloader
pct exec "$CTID" -- systemctl start jdownloader
fi
pct exec "$CTID" -- reboot
echo -e "\n\033[1;32m✅ JDownloader se ha instalado correctamente en el CT $CTID y está funcionando como servicio.\033[0m"
echo -e "\n➡ Accede a \033[1;34mhttps://my.jdownloader.org\033[0m con tu cuenta para gestionarlo.\n"

View File

@ -1,99 +0,0 @@
#!/bin/bash
# Script para instalar JDownloader en un contenedor LXC desde el host Proxmox
# Autor: MacRimi
# Mostrar lista de CTs
CT_LIST=$(pct list | awk 'NR>1 {print $1, $3}')
if [ -z "$CT_LIST" ]; then
whiptail --title "Error" --msgbox "No hay contenedores LXC disponibles en el sistema." 8 50
exit 1
fi
# Seleccionar CT
CTID=$(whiptail --title "Instalación de JDownloader" --menu "Selecciona el contenedor donde instalar JDownloader:" 20 60 10 $CT_LIST 3>&1 1>&2 2>&3)
if [ -z "$CTID" ]; then
whiptail --title "Cancelado" --msgbox "No se ha seleccionado ningún contenedor." 8 40
exit 1
fi
# Solicitar email
EMAIL=$(whiptail --title "Cuenta My JDownloader" --inputbox "Introduce tu correo electrónico para vincular JDownloader:" 10 60 3>&1 1>&2 2>&3)
if [ -z "$EMAIL" ]; then
whiptail --title "Error" --msgbox "No se ha introducido ningún correo." 8 40
exit 1
fi
# Solicitar contraseña
while true; do
PASSWORD=$(whiptail --title "Cuenta My JDownloader" --passwordbox "Introduce tu contraseña de My JDownloader:" 10 60 3>&1 1>&2 2>&3)
if [ -z "$PASSWORD" ]; then
whiptail --title "Error" --msgbox "No se ha introducido ninguna contraseña." 8 40
exit 1
fi
CONFIRM_PASSWORD=$(whiptail --title "Confirmación de contraseña" --passwordbox "Repite tu contraseña para confirmar:" 10 60 3>&1 1>&2 2>&3)
if [ "$PASSWORD" = "$CONFIRM_PASSWORD" ]; then
break
else
whiptail --title "Error" --msgbox "Las contraseñas no coinciden. Intenta de nuevo." 8 50
fi
done
# Confirmar datos
whiptail --title "Confirmar datos" --yesno "¿Deseas continuar con los siguientes datos?\n\nCorreo: $EMAIL\nContraseña: (establecida)\n\nEsta información se usará para vincular el contenedor con tu cuenta de My.JDownloader." 14 60
if [ $? -ne 0 ]; then
whiptail --title "Cancelado" --msgbox "Instalación cancelada por el usuario." 8 40
exit 1
fi
echo
echo "Instalando JDownloader en CT $CTID..."
echo
# Añadir repositorio alternativo para Java 8 y actualizar
pct exec "$CTID" -- wget -q http://www.mirbsd.org/~tg/Debs/sources.txt/wtf-bookworm.sources
pct exec "$CTID" -- mv wtf-bookworm.sources /etc/apt/sources.list.d/
pct exec "$CTID" -- apt update -y
pct exec "$CTID" -- apt install -y openjdk-8-jdk wget
# Crear carpeta y descargar JDownloader
pct exec "$CTID" -- mkdir -p /root/jdownloader
pct exec "$CTID" -- bash -c "cd /root/jdownloader && wget -q http://installer.jdownloader.org/JDownloader.jar"
# Crear archivo de configuración JSON para My JDownloader
pct exec "$CTID" -- bash -c "mkdir -p /root/jdownloader/cfg && cat > /root/jdownloader/cfg/org.jdownloader.api.myjdownloader.MyJDownloaderSettings.json" <<EOF
{
"email" : "$EMAIL",
"password" : "$PASSWORD",
"enabled" : true
}
EOF
# Crear servicio systemd
pct exec "$CTID" -- bash -c "cat > /etc/systemd/system/jdownloader.service <<EOF
[Unit]
Description=JDownloader Headless
After=network.target
[Service]
Type=simple
WorkingDirectory=/root/jdownloader
ExecStart=/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java -jar JDownloader.jar -norestart
Restart=always
User=root
[Install]
WantedBy=multi-user.target
EOF"
# Activar y arrancar servicio
pct exec "$CTID" -- systemctl daemon-reexec
pct exec "$CTID" -- systemctl daemon-reload
pct exec "$CTID" -- systemctl enable jdownloader
pct exec "$CTID" -- systemctl start jdownloader
echo -e "\n\033[1;32m✅ JDownloader se ha instalado y está funcionando como servicio en el CT $CTID.\033[0m"
echo -e "\nPuedes acceder a \033[1;34mhttps://my.jdownloader.org\033[0m con tu cuenta para gestionarlo.\n"

View File

@ -16,85 +16,27 @@ BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh" UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env" VENV_PATH="/opt/googletrans-env"
check_pve9_translation_compatibility() {
local pve_version
if command -v pveversion &>/dev/null; then
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
else
return 0
fi
if [[ -n "$pve_version" ]] && [[ "$pve_version" -ge 9 ]] && [[ -d "$VENV_PATH" ]]; then
local has_googletrans=false
local has_cache=false
if [[ -f "$VENV_PATH/bin/pip" ]]; then
if "$VENV_PATH/bin/pip" list 2>/dev/null | grep -q "googletrans"; then
has_googletrans=true
fi
fi
if [[ -f "$BASE_DIR/cache.json" ]]; then
has_cache=true
fi
if [[ "$has_googletrans" = true ]] || [[ "$has_cache" = true ]]; then
dialog --clear \
--backtitle "ProxMenux - Compatibility Required" \
--title "Translation Environment Incompatible with PVE $pve_version" \
--msgbox "NOTICE: You are running Proxmox VE $pve_version with translation components installed.\n\nTranslations are NOT supported in PVE 9+. This causes:\n• Menu loading errors\n• Translation failures\n• System instability\n\nREQUIRED ACTION:\nProxMenux will now automatically reinstall the Normal Version.\n\nThis process will:\n• Remove incompatible translation components\n• Install PVE 9+ compatible version\n• Preserve all your settings and preferences\n\nPress OK to continue with automatic reinstallation..." 20 75
bash <(curl -sSL "$REPO_URL/install_proxmenux.sh")
fi
exit
fi
}
check_pve9_translation_compatibility
# ==========================================================
if [[ -f "$UTILS_FILE" ]]; then if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE" source "$UTILS_FILE"
fi fi
load_language
initialize_cache
# ==========================================================
if ! command -v dialog &>/dev/null; then if ! command -v dialog &>/dev/null; then
apt update -qq >/dev/null 2>&1 apt update -qq >/dev/null 2>&1
apt install -y dialog >/dev/null 2>&1 apt install -y dialog >/dev/null 2>&1
fi fi
if [[ "$PROXMENUX_PVE9_WARNING_SHOWN" = "1" ]]; then
if ! load_language 2>/dev/null; then
LANGUAGE="en"
fi
else
load_language
initialize_cache
fi
# ==========================================================
show_menu() { show_menu() {
local TEMP_FILE local TEMP_FILE
TEMP_FILE=$(mktemp) TEMP_FILE=$(mktemp)
while true; do while true; do
local menu_title="Main ProxMenux"
if [[ -n "$PROXMENUX_PVE9_WARNING_SHOWN" ]]; then
menu_title="Main ProxMenux"
fi
dialog --clear \ dialog --clear \
--backtitle "ProxMenux" \ --backtitle "ProxMenux" \
--title "$(translate "$menu_title")" \ --title "$(translate "Main ProxMenux")" \
--menu "$(translate "Select an option:")" 20 70 10 \ --menu "$(translate "Select an option:")" 20 70 10 \
1 "$(translate "Settings post-install Proxmox")" \ 1 "$(translate "Settings post-install Proxmox")" \
2 "$(translate "Help and Info Commands")" \ 2 "$(translate "Help and Info Commands")" \
@ -110,8 +52,9 @@ show_menu() {
local EXIT_STATUS=$? local EXIT_STATUS=$?
if [[ $EXIT_STATUS -ne 0 ]]; then if [[ $EXIT_STATUS -ne 0 ]]; then
# ESC pressed or Cancel
clear clear
msg_ok "$(translate "Thank you for using ProxMenux. Goodbye!")" msg_ok "$(translate "Thank you for using ProxMenu. Goodbye!")"
rm -f "$TEMP_FILE" rm -f "$TEMP_FILE"
exit 0 exit 0
fi fi
@ -120,7 +63,7 @@ show_menu() {
case $OPTION in case $OPTION in
1) exec bash <(curl -s "$REPO_URL/scripts/menus/menu_post_install.sh") ;; 1) exec bash <(curl -s "$REPO_URL/scripts/menus/menu_post_install.sh") ;;
2) exec bash <(curl -s "$REPO_URL/scripts/help_info_menu.sh") ;; 2) bash <(curl -s "$REPO_URL/scripts/help_info_menu.sh") ;;
3) exec bash <(curl -s "$REPO_URL/scripts/menus/hw_grafics_menu.sh") ;; 3) exec bash <(curl -s "$REPO_URL/scripts/menus/hw_grafics_menu.sh") ;;
4) exec bash <(curl -s "$REPO_URL/scripts/menus/create_vm_menu.sh") ;; 4) exec bash <(curl -s "$REPO_URL/scripts/menus/create_vm_menu.sh") ;;
5) exec bash <(curl -s "$REPO_URL/scripts/menus/storage_menu.sh") ;; 5) exec bash <(curl -s "$REPO_URL/scripts/menus/storage_menu.sh") ;;
@ -134,4 +77,6 @@ show_menu() {
done done
} }
show_menu show_menu

View File

@ -1,82 +0,0 @@
#!/bin/bash
# ==========================================================
# ProxMenu - A menu-driven script for Proxmox VE management
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 2.0
# Last Updated: 04/04/2025
# ==========================================================
# Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
if ! command -v dialog &>/dev/null; then
apt update -qq >/dev/null 2>&1
apt install -y dialog >/dev/null 2>&1
fi
show_menu() {
local TEMP_FILE
TEMP_FILE=$(mktemp)
while true; do
dialog --clear \
--backtitle "ProxMenux" \
--title "$(translate "Main ProxMenux")" \
--menu "$(translate "Select an option:")" 20 70 10 \
1 "$(translate "Settings post-install Proxmox")" \
2 "$(translate "Help and Info Commands")" \
3 "$(translate "Hardware: GPUs and Coral-TPU")" \
4 "$(translate "Create VM from template or script")" \
5 "$(translate "Disk and Storage Manager")" \
6 "$(translate "Proxmox VE Helper Scripts")" \
7 "$(translate "Network Management")" \
8 "$(translate "Utilities and Tools")" \
9 "$(translate "Settings")" \
0 "$(translate "Exit")" 2>"$TEMP_FILE"
local EXIT_STATUS=$?
if [[ $EXIT_STATUS -ne 0 ]]; then
# ESC pressed or Cancel
clear
msg_ok "$(translate "Thank you for using ProxMenux. Goodbye!")"
rm -f "$TEMP_FILE"
exit 0
fi
OPTION=$(<"$TEMP_FILE")
case $OPTION in
1) exec bash <(curl -s "$REPO_URL/scripts/menus/menu_post_install.sh") ;;
2) bash <(curl -s "$REPO_URL/scripts/help_info_menu.sh") ;;
3) exec bash <(curl -s "$REPO_URL/scripts/menus/hw_grafics_menu.sh") ;;
4) exec bash <(curl -s "$REPO_URL/scripts/menus/create_vm_menu.sh") ;;
5) exec bash <(curl -s "$REPO_URL/scripts/menus/storage_menu.sh") ;;
6) exec bash <(curl -s "$REPO_URL/scripts/menus/menu_Helper_Scripts.sh") ;;
7) exec bash <(curl -s "$REPO_URL/scripts/menus/network_menu.sh") ;;
8) exec bash <(curl -s "$REPO_URL/scripts/menus/utilities_menu.sh") ;;
9) exec bash <(curl -s "$REPO_URL/scripts/menus/config_menu.sh") ;;
0) clear; msg_ok "$(translate "Thank you for using ProxMenu. Goodbye!")"; rm -f "$TEMP_FILE"; exit 0 ;;
*) msg_warn "$(translate "Invalid option")"; sleep 2 ;;
esac
done
}
show_menu

View File

@ -13,15 +13,13 @@
# Advanced network management and troubleshooting tool for Proxmox VE. # Advanced network management and troubleshooting tool for Proxmox VE.
# Features include interface detection, bridge management, connectivity testing, # Features include interface detection, bridge management, connectivity testing,
# network diagnostics, configuration backup/restore, and automated repairs. # network diagnostics, configuration backup/restore, and automated repairs.
# Special thanks to @Andres_Eduardo_Rojas_Moya for contributing the persistent
# network naming function and for the original idea.
# Configuration ============================================ # Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux" BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh" UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env" VENV_PATH="/opt/googletrans-env"
BACKUP_DIR="/var/backups/proxmenux" BACKUP_DIR="/var/backups/proxmenux"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE" source "$UTILS_FILE"
@ -41,19 +39,10 @@ backup_network_config() {
local timestamp=$(date +"%Y-%m-%d_%H-%M-%S") local timestamp=$(date +"%Y-%m-%d_%H-%M-%S")
local backup_file="$BACKUP_DIR/interfaces_backup_$timestamp" local backup_file="$BACKUP_DIR/interfaces_backup_$timestamp"
cp /etc/network/interfaces "$backup_file" cp /etc/network/interfaces "$backup_file"
msg_ok "$(translate "Network configuration backed up")"
echo "$backup_file" echo "$backup_file"
} }
# Tool registration system
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
register_tool() {
local tool="$1"
local state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
# ========================================================== # ==========================================================
# Network Detection Functions # Network Detection Functions
detect_network_method() { detect_network_method() {
@ -111,6 +100,75 @@ get_interface_info() {
} }
# ========================================================== # ==========================================================
# Network Information Functions
show_interface_details() {
NETWORK_METHOD=$(detect_network_method)
if [[ "$NETWORK_METHOD" != "classic" ]]; then
dialog --title "Unsupported Network Stack" \
--msgbox "WARNING: This script only supports the classic Debian/Proxmox network configuration (/etc/network/interfaces).\n\nDetected: $NETWORK_METHOD.\n\nAborting for safety.\n\nPlease configure your network using your distribution's supported tools." 14 70
exit 1
fi
local interfaces=($(detect_all_interfaces))
local info_text=""
info_text+="$(translate "Network Interface Details")\n"
info_text+="$(printf '=%.0s' {1..50})\n\n"
for interface in "${interfaces[@]}"; do
local details=$(get_interface_info "$interface")
IFS='|' read -r name ip status mac <<< "$details"
info_text+="$(translate "Interface"): $name\n"
info_text+=" $(translate "IP Address"): $ip\n"
info_text+=" $(translate "Status"): $status\n"
info_text+=" $(translate "MAC Address"): $mac\n\n"
done
dialog --backtitle "ProxMenux" --title "$(translate "Interface Details")" \
--msgbox "$info_text" 20 70
}
show_bridge_status() {
NETWORK_METHOD=$(detect_network_method)
if [[ "$NETWORK_METHOD" != "classic" ]]; then
dialog --title "Unsupported Network Stack" \
--msgbox "WARNING: This script only supports the classic Debian/Proxmox network configuration (/etc/network/interfaces).\n\nDetected: $NETWORK_METHOD.\n\nAborting for safety.\n\nPlease configure your network using your distribution's supported tools." 14 70
exit 1
fi
local bridges=($(detect_bridge_interfaces))
local bridge_info=""
bridge_info+="$(translate "Bridge Configuration Status")\n"
bridge_info+="$(printf '=%.0s' {1..40})\n\n"
if [ ${#bridges[@]} -eq 0 ]; then
bridge_info+="$(translate "No bridges found")\n"
else
for bridge in "${bridges[@]}"; do
local details=$(get_interface_info "$bridge")
IFS='|' read -r name ip status mac <<< "$details"
# Get bridge ports
local ports=$(grep -A5 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep "bridge-ports" | cut -d' ' -f2-)
[ -z "$ports" ] && ports="$(translate "None")"
bridge_info+="$(translate "Bridge"): $name\n"
bridge_info+=" $(translate "IP"): $ip\n"
bridge_info+=" $(translate "Status"): $status\n"
bridge_info+=" $(translate "Ports"): $ports\n\n"
done
fi
dialog --backtitle "ProxMenux" --title "$(translate "Bridge Status")" \
--msgbox "$bridge_info" 20 70
}
show_routing_table_() { show_routing_table_() {
local route_info="" local route_info=""
@ -370,7 +428,6 @@ analyze_bridge_configuration() {
--textbox "$temp_file" 25 80 --textbox "$temp_file" 25 80
rm -f "$temp_file" rm -f "$temp_file"
# Offer guided repair if issues found # Offer guided repair if issues found
if [ $issues_found -gt 0 ]; then if [ $issues_found -gt 0 ]; then
if dialog --backtitle "ProxMenux" --title "$(translate "Guided Repair Available")" \ if dialog --backtitle "ProxMenux" --title "$(translate "Guided Repair Available")" \
@ -380,7 +437,6 @@ analyze_bridge_configuration() {
fi fi
} }
guided_bridge_repair() { guided_bridge_repair() {
local step=1 local step=1
local total_steps=5 local total_steps=5
@ -399,8 +455,7 @@ guided_bridge_repair() {
show_proxmenux_logo show_proxmenux_logo
local backup_file=$(backup_network_config) local backup_file=$(backup_network_config)
msg_ok "$(translate "Network configuration backed up")" sleep 1
sleep 2
dialog --backtitle "ProxMenux" --title "$(translate "Backup Created")" \ dialog --backtitle "ProxMenux" --title "$(translate "Backup Created")" \
--msgbox "$(translate "Safety backup created"): $backup_file\n\n$(translate "You can restore it anytime with"):\ncp $backup_file /etc/network/interfaces" 10 70 --msgbox "$(translate "Safety backup created"): $backup_file\n\n$(translate "You can restore it anytime with"):\ncp $backup_file /etc/network/interfaces" 10 70
@ -525,12 +580,6 @@ guided_bridge_repair() {
fi fi
} }
# ==========================================================
analyze_network_configuration() { analyze_network_configuration() {
NETWORK_METHOD=$(detect_network_method) NETWORK_METHOD=$(detect_network_method)
@ -644,8 +693,7 @@ guided_configuration_cleanup() {
show_proxmenux_logo show_proxmenux_logo
local backup_file=$(backup_network_config) local backup_file=$(backup_network_config)
msg_ok "$(translate "Network configuration backed up")" sleep 1
sleep 2
dialog --backtitle "ProxMenux" --title "$(translate "Backup Created")" \ dialog --backtitle "ProxMenux" --title "$(translate "Backup Created")" \
--msgbox "$(translate "Safety backup created"): $backup_file\n\n$(translate "You can restore it anytime with"):\ncp $backup_file /etc/network/interfaces" 10 70 --msgbox "$(translate "Safety backup created"): $backup_file\n\n$(translate "You can restore it anytime with"):\ncp $backup_file /etc/network/interfaces" 10 70
@ -739,68 +787,6 @@ guided_configuration_cleanup() {
} }
# ==========================================================
setup_persistent_network() {
local LINK_DIR="/etc/systemd/network"
local BACKUP_DIR="/etc/systemd/network/backup-$(date +%Y%m%d-%H%M%S)"
if ! dialog --title "$(translate "Network Interface Setup")" \
--yesno "\n$(translate "Create persistent network interface names?")" 8 60; then
return 1
fi
show_proxmenux_logo
msg_info "$(translate "Setting up persistent network interfaces")"
sleep 2
# Create directory
mkdir -p "$LINK_DIR"
# Backup existing files
if ls "$LINK_DIR"/*.link >/dev/null 2>&1; then
mkdir -p "$BACKUP_DIR"
cp "$LINK_DIR"/*.link "$BACKUP_DIR"/ 2>/dev/null || true
fi
# Process physical interfaces
local count=0
for iface in $(ls /sys/class/net/ | grep -vE "lo|docker|veth|br-|vmbr|tap|fwpr|fwln|virbr|bond|cilium|zt|wg"); do
if [[ -e "/sys/class/net/$iface/device" ]] || [[ -e "/sys/class/net/$iface/phy80211" ]]; then
local MAC=$(cat /sys/class/net/$iface/address 2>/dev/null)
if [[ "$MAC" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ ]]; then
local LINK_FILE="$LINK_DIR/10-$iface.link"
cat > "$LINK_FILE" <<EOF
[Match]
MACAddress=$MAC
[Link]
Name=$iface
EOF
chmod 644 "$LINK_FILE"
((count++))
fi
fi
done
if [[ $count -gt 0 ]]; then
msg_ok "$(translate "Created persistent names for") $count $(translate "interfaces")"
msg_ok "$(translate "Changes will apply after reboot.")"
else
msg_warn "$(translate "No physical interfaces found")"
fi
register_tool "persistent_network" true
echo -e
msg_success "$(translate "Press ENTER to continue...")"
read -r
}
# ==========================================================
restart_network_service() { restart_network_service() {
if dialog --title "$(translate "Restart Network")" \ if dialog --title "$(translate "Restart Network")" \
@ -863,9 +849,7 @@ create_network_backup_manual() {
echo -e echo -e
msg_info "$(translate "Creating backup of network interfaces configuration...")" msg_info "$(translate "Creating backup of network interfaces configuration...")"
sleep 3 sleep 3
cleanup
backup_network_config backup_network_config
msg_ok "$(translate "Network configuration backed up")"
echo -e echo -e
msg_success "$(translate "Press Enter to continue...")" msg_success "$(translate "Press Enter to continue...")"
read -r read -r
@ -939,167 +923,44 @@ restore_network_backup() {
} }
launch_iftop() {
if ! command -v iftop &>/dev/null; then
apt-get update -qq && apt-get install -y iftop &>/dev/null
fi
dialog --backtitle "ProxMenux" --title "$(translate "iftop usage")" --msgbox "\n$(translate "To exit iftop, press q")" 8 50
clear
iftop
}
launch_iptraf() {
if ! command -v iptraf-ng &>/dev/null; then
apt-get update -qq && apt-get install -y iptraf-ng &>/dev/null
fi
dialog --backtitle "ProxMenux" --title "$(translate "iptraf-ng usage")" --msgbox "\n$(translate "To exit iptraf-ng, press x")" 8 50
clear
iptraf-ng
}
# ========================================================== # ==========================================================
# Main Menu
show_main_menu() {
confirm_and_run() {
local name="$1"
local command="$2"
dialog --clear --title "$(translate "Confirmation")" \
--yesno "\n\n$(translate "Do you want to run the network script from") $name?" 10 70
response=$?
clear
if [ $response -eq 0 ]; then
eval "$command"
echo
msg_success "$(translate 'Press ENTER to continue...')"
read -r _
else
msg_warn "$(translate "Cancelled by user.")"
sleep 1
fi
}
# ==========================================================
declare -a PROXMENUX_SCRIPTS=(
"Real-time network usage (iftop)||launch_iftop"
"Network monitoring tool (iptraf-ng)||launch_iptraf"
"Show Routing Table||show_routing_table"
"Test Connectivity||test_connectivity"
"Advanced Diagnostics||advanced_network_diagnostics"
"Analyze Bridge Configuration||analyze_bridge_configuration"
"Analyze Network Configuration||analyze_network_configuration"
"Setup Persistent Network Names||setup_persistent_network"
"Restart Network Service||restart_network_service"
"Show Network Config File||show_network_config"
"Create Network Backup||create_network_backup_manual"
"Restore Network Backup||restore_network_backup"
)
declare -a COMMUNITY_SCRIPTS=(
"Disable NIC Offloading (Intel e1000e)|Helper-Scripts|confirm_and_run \"Helper-Scripts\" \"bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/nic-offloading-fix.sh)\""
)
# ==========================================================
format_menu_item() {
local description="$1"
local source="$2"
local total_width=62
local desc_length=${#description}
local source_length=${#source}
local spaces_needed=$((total_width - desc_length - source_length))
[ $spaces_needed -lt 3 ] && spaces_needed=3
local spacing=""
for ((i=0; i<spaces_needed; i++)); do
spacing+=" "
done
echo "${description}${spacing}${source}"
}
# ==========================================================
show_menu() {
while true; do while true; do
local menu_items=() local selection=$(dialog --clear \
--backtitle "ProxMenux" \
--title "$(translate "Network Management - SAFE MODE")" \
--menu "$(translate "Select an option:"):" 20 70 12 \
"1" "$(translate "Show Interface Details")" \
"2" "$(translate "Show Bridge Status")" \
"3" "$(translate "Show Routing Table")" \
"4" "$(translate "Test Connectivity")" \
"5" "$(translate "Advanced Diagnostics")" \
"6" "$(translate "Analyze Bridge Configuration")" \
"7" "$(translate "Analyze Network Configuration")" \
"8" "$(translate "Restart Network Service")" \
"9" "$(translate "Show Network Config File")" \
"10" "$(translate "Create Network Backup")" \
"11" "$(translate "Restore Network Backup")" \
"0" "$(translate "Return to Main Menu")" \
3>&1 1>&2 2>&3)
case $selection in
declare -A script_commands 1) show_interface_details ;;
local counter=1 2) show_bridge_status ;;
3) show_routing_table ;;
for script in "${PROXMENUX_SCRIPTS[@]}"; do 4) test_connectivity ;;
IFS='|' read -r name source command <<< "$script" 5) advanced_network_diagnostics ;;
local translated_name="$(translate "$name")" 6) analyze_bridge_configuration ;;
local formatted_item 7) analyze_network_configuration ;;
formatted_item=$(format_menu_item "$translated_name" "$source") 8) restart_network_service ;;
menu_items+=("$counter" "$formatted_item") 9) show_network_config ;;
script_commands["$counter"]="$command" 10) create_network_backup_manual ;;
((counter++)) 11) restore_network_backup ;;
done 0|"") exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh") ;;
esac
menu_items+=("" "")
menu_items+=("-" "───────────────────── $(translate "Community Scripts") ──────────────────────")
menu_items+=("" "")
for script in "${COMMUNITY_SCRIPTS[@]}"; do
IFS='|' read -r name source command <<< "$script"
local translated_name="$(translate "$name")"
local formatted_item
formatted_item=$(format_menu_item "$translated_name" "$source")
menu_items+=("$counter" "$formatted_item")
script_commands["$counter"]="$command"
((counter++))
done
menu_items+=("" "")
menu_items+=("0" "$(translate "Return to Main Menu")")
exec 3>&1
script_selection=$(dialog --clear \
--backtitle "ProxMenux" \
--title "$(translate "Network Management")" \
--menu "\n$(translate "Select a network management option:"):\n" \
26 78 19 \
"${menu_items[@]}" 2>&1 1>&3)
exit_status=$?
exec 3>&-
if [ $exit_status -ne 0 ] || [ "$script_selection" = "0" ]; then
exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh")
fi
if [[ "$script_selection" == "-" || "$script_selection" == "" ]]; then
continue
fi
if [[ -n "${script_commands[$script_selection]}" ]]; then
eval "${script_commands[$script_selection]}"
else
msg_error "$(translate "Invalid selection")"
sleep 1
fi
done done
} }
# ========================================================== # ==========================================================
show_main_menu
show_menu

View File

@ -68,98 +68,319 @@ register_tool() {
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
} }
# ==========================================================
lvm_repair_check() {
check_extremeshok_warning() { msg_info "$(translate "Checking and repairing old LVM PV headers (if needed)...")"
local marker_file="/etc/extremeshok" pvs_output=$(LC_ALL=C pvs -v 2>&1 | grep "old PV header")
if [ -z "$pvs_output" ]; then
if [[ -f "$marker_file" ]]; then msg_ok "$(translate "No PVs with old headers found.")"
dialog --backtitle "ProxMenux" --title "xshok-proxmox Post-Install Detected" \ register_tool "lvm_repair" true
--yesno "\n$(translate "It appears that you have already executed the xshok-proxmox post-install script on this system.")\n\n\ return
$(translate "If you continue, some adjustments may be duplicated or conflict with those already made by xshok.")\n\n\
$(translate "Do you want to continue anyway?")" 13 70
local response=$?
if [[ $response -ne 0 ]]; then
show_proxmenux_logo
msg_warn "$(translate "Action cancelled due to previous xshok-proxmox modifications.")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
exit 1
fi
fi fi
declare -A vg_map
while read -r line; do
pv=$(echo "$line" | grep -o '/dev/[^ ]*')
vg=$(pvs -o vg_name --noheadings "$pv" | awk '{print $1}')
if [ -n "$vg" ]; then
vg_map["$vg"]=1
fi
done <<< "$pvs_output"
for vg in "${!vg_map[@]}"; do
msg_warn "$(translate "Old PV header(s) found in VG $vg. Updating metadata...")"
vgck --updatemetadata "$vg"
vgchange -ay "$vg"
if [ $? -ne 0 ]; then
msg_warn "$(translate "Metadata update failed for VG $vg. Review manually.")"
else
msg_ok "$(translate "Metadata updated successfully for VG $vg")"
fi
done
msg_ok "$(translate "LVM PV headers check completed")"
} }
# ========================================================== # ==========================================================
cleanup_duplicate_repos() {
local sources_file="/etc/apt/sources.list"
local temp_file=$(mktemp)
local cleaned_count=0
declare -A seen_repos
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then
echo "$line" >> "$temp_file"
continue
fi
if [[ "$line" =~ ^deb ]]; then
read -r _ url dist components <<< "$line"
local key="${url}_${dist}"
if [[ -v "seen_repos[$key]" ]]; then
echo "# $line" >> "$temp_file"
cleaned_count=$((cleaned_count + 1))
else
echo "$line" >> "$temp_file"
seen_repos[$key]="$components"
fi
else
echo "$line" >> "$temp_file"
fi
done < "$sources_file"
mv "$temp_file" "$sources_file"
chmod 644 "$sources_file"
local pve_files=(/etc/apt/sources.list.d/*proxmox*.list /etc/apt/sources.list.d/*pve*.list)
local pve_content="deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription"
local pve_public_repo="/etc/apt/sources.list.d/pve-public-repo.list"
local pve_public_repo_exists=false
if [ -f "$pve_public_repo" ] && grep -q "^deb.*pve-no-subscription" "$pve_public_repo"; then
pve_public_repo_exists=true
fi
for file in "${pve_files[@]}"; do
if [ -f "$file" ] && grep -q "^deb.*pve-no-subscription" "$file"; then
if ! $pve_public_repo_exists && [[ "$file" == "$pve_public_repo" ]]; then
sed -i 's/^# *deb/deb/' "$file"
pve_public_repo_exists=true
elif [[ "$file" != "$pve_public_repo" ]]; then
sed -i 's/^deb/# deb/' "$file"
cleaned_count=$((cleaned_count + 1))
fi
fi
done
apt update
}
apt_upgrade() { apt_upgrade() {
local pve_version
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
if [[ -z "$pve_version" ]]; then
msg_error "Unable to detect Proxmox version." NECESSARY_REBOOT=1
if [ -f /etc/apt/sources.list.d/pve-enterprise.list ] && grep -q "^deb" /etc/apt/sources.list.d/pve-enterprise.list; then
msg_info "$(translate "Disabling enterprise Proxmox repository...")"
sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/pve-enterprise.list
msg_ok "$(translate "Enterprise Proxmox repository disabled")"
fi
if [ -f /etc/apt/sources.list.d/ceph.list ] && grep -q "^deb" /etc/apt/sources.list.d/ceph.list; then
msg_info "$(translate "Disabling enterprise Proxmox Ceph repository...")"
sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/ceph.list
msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")"
fi
if [ ! -f /etc/apt/sources.list.d/pve-public-repo.list ] || ! grep -q "pve-no-subscription" /etc/apt/sources.list.d/pve-public-repo.list; then
msg_info "$(translate "Enabling free public Proxmox repository...")"
echo "deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription" > /etc/apt/sources.list.d/pve-public-repo.list
msg_ok "$(translate "Free public Proxmox repository enabled")"
fi
sources_file="/etc/apt/sources.list"
need_update=false
sed -i 's|ftp.es.debian.org|deb.debian.org|g' "$sources_file"
if grep -q "^deb http://security.debian.org ${OS_CODENAME}-security main contrib" "$sources_file"; then
sed -i "s|^deb http://security.debian.org ${OS_CODENAME}-security main contrib|deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware|" "$sources_file"
msg_ok "$(translate "Replaced security repository with full version")"
need_update=true
fi
if ! grep -q "deb http://security.debian.org/debian-security ${OS_CODENAME}-security" "$sources_file"; then
echo "deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware" >> "$sources_file"
need_update=true
fi
if ! grep -q "deb http://deb.debian.org/debian ${OS_CODENAME} " "$sources_file"; then
echo "deb http://deb.debian.org/debian ${OS_CODENAME} main contrib non-free non-free-firmware" >> "$sources_file"
need_update=true
fi
if ! grep -q "deb http://deb.debian.org/debian ${OS_CODENAME}-updates" "$sources_file"; then
echo "deb http://deb.debian.org/debian ${OS_CODENAME}-updates main contrib non-free non-free-firmware" >> "$sources_file"
need_update=true
fi
msg_ok "$(translate "Debian repositories configured correctly")"
# ===================================================
if [ ! -f /etc/apt/apt.conf.d/no-bookworm-firmware.conf ]; then
msg_info "$(translate "Disabling non-free firmware warnings...")"
echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > /etc/apt/apt.conf.d/no-bookworm-firmware.conf
msg_ok "$(translate "Non-free firmware warnings disabled")"
fi
msg_info "$(translate "Updating package lists...")"
if apt-get update > /dev/null 2>&1; then
msg_ok "$(translate "Package lists updated")"
else
msg_error "$(translate "Failed to update package lists")"
return 1 return 1
fi fi
if [[ "$pve_version" -ge 9 ]]; then
bash <(curl -fsSL "$REPO_URL/scripts/global/update-pve.sh") msg_info "$(translate "Removing conflicting utilities...")"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' purge ntp openntpd systemd-timesyncd > /dev/null 2>&1; then
msg_ok "$(translate "Conflicting utilities removed")"
else else
msg_error "$(translate "Failed to remove conflicting utilities")"
bash <(curl -fsSL "$REPO_URL/scripts/global/update-pve8.sh")
fi fi
msg_info "$(translate "Performing packages upgrade...")"
apt-get install pv -y > /dev/null 2>&1
total_packages=$(apt-get -s dist-upgrade | grep "^Inst" | wc -l)
if [ "$total_packages" -eq 0 ]; then
total_packages=1
fi
msg_ok "$(translate "Packages upgrade successfull")"
tput civis
tput sc
(
/usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' dist-upgrade 2>&1 | \
while IFS= read -r line; do
if [[ "$line" =~ ^(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ]]; then
package_name=$(echo "$line" | sed -E 's/.*(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ([^ ]+).*/\2/')
[ -z "$package_name" ] && package_name="$(translate "Unknown")"
tput rc
tput ed
row=$(( $(tput lines) - 6 ))
tput cup $row 0; echo "$(translate "Installing packages...")"
tput cup $((row + 1)) 0; echo "──────────────────────────────────────────────"
tput cup $((row + 2)) 0; echo "Package: $package_name"
tput cup $((row + 3)) 0; echo "Progress: [ ] 0%"
tput cup $((row + 4)) 0; echo "──────────────────────────────────────────────"
for i in $(seq 1 10); do
progress=$((i * 10))
tput cup $((row + 3)) 9
printf "[%-50s] %3d%%" "$(printf "#%.0s" $(seq 1 $((progress/2))))" "$progress"
done
fi
done
)
if [ $? -eq 0 ]; then
tput rc
tput ed
msg_ok "$(translate "System upgrade completed")"
fi
msg_info "$(translate "Installing additional Proxmox packages...")"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install zfsutils-linux proxmox-backup-restore-image chrony > /dev/null 2>&1; then
msg_ok "$(translate "Additional Proxmox packages installed")"
else
msg_error "$(translate "Failed to install additional Proxmox packages")"
fi
lvm_repair_check
cleanup_duplicate_repos
msg_ok "$(translate "Proxmox update completed")"
} }
# ========================================================== # ==========================================================
remove_subscription_banner_() {
msg_info "$(translate "Removing Proxmox subscription nag banner...")"
local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script"
if [[ ! -f "$APT_HOOK" ]]; then
cat <<'EOF' > "$APT_HOOK"
DPkg::Post-Invoke { "dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'; if [ $? -eq 1 ]; then { echo 'Removing subscription nag from UI...'; sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz; }; fi"; };
EOF
remove_subscription_banner() {
local pve_version
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
if [[ -z "$pve_version" ]]; then
msg_error "Unable to detect Proxmox version."
return 1
fi fi
if [[ "$pve_version" -ge 9 ]]; then if [[ -f "$JS_FILE" ]]; then
if ! whiptail --title "Proxmox VE 9.x Subscription Banner Removal" \ sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' "$JS_FILE"
--yesno "Do you want to remove the Proxmox subscription banner from the web interface for PVE $pve_version?" 10 70; then [[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE"
msg_warn "Banner removal cancelled by user." touch "$JS_FILE"
return 1
fi
bash <(curl -fsSL "$REPO_URL/scripts/global/remove-banner-pve9.sh")
else
if ! whiptail --title "Proxmox VE 8.x Subscription Banner Removal" \
--yesno "Do you want to remove the Proxmox subscription banner from the web interface for PVE $pve_version?" 10 70; then
msg_warn "Banner removal cancelled by user."
return 1
fi
bash <(curl -fsSL "$REPO_URL/scripts/global/remove-banner-pve8.sh")
fi fi
apt --reinstall install proxmox-widget-toolkit -y > /dev/null 2>&1
msg_ok "$(translate "Subscription nag banner removed successfully")"
register_tool "subscription_banner" true
} }
remove_subscription_banner() {
local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script"
# Detect if already applied
if grep -q "NoMoreNagging" "$JS_FILE" 2>/dev/null && [[ -f "$APT_HOOK" ]]; then
# Already applied, just reapply silently
:
else
# Ask user
if ! whiptail --title "$(translate "Proxmox Subscription Banner")" \
--yesno "$(translate "Do you want to remove the Proxmox subscription banner from the web interface?")" 10 60; then
msg_warn "$(translate "Banner removal cancelled by user.")"
return 1
fi
fi
msg_info "$(translate "Removing Proxmox subscription nag banner...")"
if [[ ! -f "$APT_HOOK" ]]; then
cat <<'EOF' > "$APT_HOOK"
DPkg::Post-Invoke { "dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'; if [ $? -eq 1 ]; then { echo 'Removing subscription nag from UI...'; sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz; }; fi"; };
EOF
fi
if [[ -f "$JS_FILE" ]]; then
sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/;s/Active/NoMoreNagging/}' "$JS_FILE"
[[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE"
touch "$JS_FILE"
fi
apt --reinstall install proxmox-widget-toolkit -y > /dev/null 2>&1
msg_ok "$(translate "Subscription nag banner removed successfully")"
register_tool "subscription_banner" true
}
# ========================================================== # ==========================================================
@ -492,7 +713,7 @@ disable_rpc() {
} }
# ========================================================== # ==========================================================
customize_bashrc_() { customize_bashrc() {
msg_info "$(translate "Customizing bashrc for root user...")" msg_info "$(translate "Customizing bashrc for root user...")"
local bashrc="/root/.bashrc" local bashrc="/root/.bashrc"
local bash_profile="/root/.bash_profile" local bash_profile="/root/.bash_profile"
@ -525,149 +746,53 @@ EOF
register_tool "bashrc_custom" true register_tool "bashrc_custom" true
} }
customize_bashrc() {
msg_info "$(translate "Customizing bashrc for root user...")"
local bashrc="/root/.bashrc"
local bash_profile="/root/.bash_profile"
local marker_begin="# BEGIN PMX_CORE_BASHRC"
local marker_end="# END PMX_CORE_BASHRC"
[ -f "${bashrc}.bak" ] || cp "$bashrc" "${bashrc}.bak" > /dev/null 2>&1
if grep -q "^${marker_begin}$" "$bashrc" 2>/dev/null; then
sed -i "/^${marker_begin}$/,/^${marker_end}$/d" "$bashrc"
fi
cat >> "$bashrc" << 'EOF'
${marker_begin}
# ProxMenux core customizations
export HISTTIMEFORMAT="%d/%m/%y %T "
export PS1="\[\e[31m\][\[\e[m\]\[\e[38;5;172m\]\u\[\e[m\]@\[\e[38;5;153m\]\h\[\e[m\] \[\e[38;5;214m\]\W\[\e[m\]\[\e[31m\]]\[\e[m\]\\$ "
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias ls='ls --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
source /etc/profile.d/bash_completion.sh
${marker_end}
EOF
if ! grep -q "source /root/.bashrc" "$bash_profile" 2>/dev/null; then
echo "source /root/.bashrc" >> "$bash_profile" 2>/dev/null
fi
msg_ok "$(translate "Bashrc customization completed")"
register_tool "bashrc_custom" true
}
# ========================================================== # ==========================================================
install_log2ram_auto() { install_log2ram_auto() {
msg_info "$(translate "Checking if system disk is SSD or M.2...")"
msg_info "$(translate "Checking if system disk is SSD or M.2...")" ROOT_PART=$(lsblk -no NAME,MOUNTPOINT | grep ' /$' | awk '{print $1}')
SYSTEM_DISK=$(lsblk -no PKNAME /dev/$ROOT_PART 2>/dev/null)
SYSTEM_DISK=${SYSTEM_DISK:-sda}
local is_ssd=false if [[ "$SYSTEM_DISK" == nvme* || "$(cat /sys/block/$SYSTEM_DISK/queue/rotational 2>/dev/null)" == "0" ]]; then
local pool disks disk byid_path dev rot msg_ok "$(translate "System disk ($SYSTEM_DISK) is SSD or M.2. Proceeding with Log2RAM setup.")"
if grep -qE '^root=ZFS=' /etc/kernel/cmdline 2>/dev/null || mount | grep -q 'on / type zfs'; then
pool=$(zfs list -Ho name,mountpoint 2>/dev/null | awk '$2=="/"{print $1}' | cut -d/ -f1)
disks=$(zpool status "$pool" 2>/dev/null | awk '/ONLINE/ && $1 !~ /:|mirror|raidz|log|spare|config|NAME|rpool|state/ {print $1}' | sort -u)
is_ssd=true
for disk in $disks; do
byid_path=$(readlink -f /dev/disk/by-id/*$disk* 2>/dev/null) || continue
dev=$(basename "$byid_path" | sed -E 's|[0-9]+$||' | sed -E 's|p$||')
rot=$(cat /sys/block/$dev/queue/rotational 2>/dev/null)
[[ "$rot" != "0" ]] && is_ssd=false && break
done
else else
msg_warn "$(translate "System disk ($SYSTEM_DISK) is not SSD/M.2. Skipping Log2RAM installation.")"
ROOT_PART=$(lsblk -no NAME,MOUNTPOINT | grep ' /$' | awk '{print $1}')
SYSTEM_DISK=$(lsblk -no PKNAME /dev/$ROOT_PART 2>/dev/null)
SYSTEM_DISK=${SYSTEM_DISK:-sda}
if [[ "$SYSTEM_DISK" == nvme* || "$(cat /sys/block/$SYSTEM_DISK/queue/rotational 2>/dev/null)" == "0" ]]; then
is_ssd=true
fi
fi
if [[ "$is_ssd" == true ]]; then
msg_ok "$(translate "System disk is SSD or M.2. Proceeding with Log2RAM setup.")"
else
msg_warn "$(translate "System disk is not SSD/M.2. Skipping Log2RAM installation.")"
return 0 return 0
fi fi
# Clean up previous state
rm -rf /tmp/log2ram
rm -f /etc/systemd/system/log2ram*
rm -f /etc/systemd/system/log2ram-daily.*
rm -f /etc/cron.d/log2ram*
rm -f /usr/sbin/log2ram
rm -f /etc/log2ram.conf
rm -f /usr/local/bin/log2ram-check.sh
rm -rf /var/log.hdd
systemctl daemon-reexec >/dev/null 2>&1
systemctl daemon-reload >/dev/null 2>&1
if [[ -f /etc/log2ram.conf ]] && command -v log2ram >/dev/null 2>&1 && systemctl list-units --all | grep -q log2ram; then msg_info "$(translate "Installing log2ram from GitHub...")"
msg_ok "$(translate "Log2RAM is already installed and configured correctly.")"
register_tool "log2ram" true
return 0
fi
msg_info "$(translate "Log2RAM proceeding with installation...")"
if [[ -d /tmp/log2ram ]]; then
rm -rf /tmp/log2ram
fi
[[ -f /etc/systemd/system/log2ram.service ]] && rm -f /etc/systemd/system/log2ram*
[[ -f /etc/systemd/system/log2ram-daily.service ]] && rm -f /etc/systemd/system/log2ram-daily.*
[[ -f /etc/cron.d/log2ram ]] && rm -f /etc/cron.d/log2ram*
[[ -f /usr/sbin/log2ram ]] && rm -f /usr/sbin/log2ram
[[ -f /etc/log2ram.conf ]] && rm -f /etc/log2ram.conf
[[ -f /usr/local/bin/log2ram-check.sh ]] && rm -f /usr/local/bin/log2ram-check.sh
[[ -d /var/log.hdd ]] && rm -rf /var/log.hdd
systemctl daemon-reexec >/dev/null 2>&1 || true
systemctl daemon-reload >/dev/null 2>&1 || true
if ! command -v git >/dev/null 2>&1; then if ! command -v git >/dev/null 2>&1; then
apt-get update -qq >/dev/null 2>&1 apt-get update -qq >/dev/null 2>&1
apt-get install -y git >/dev/null 2>&1 apt-get install -y git >/dev/null 2>&1
msg_ok "$(translate "Git installed successfully")"
fi fi
if ! git clone https://github.com/azlux/log2ram.git /tmp/log2ram >/dev/null 2>>/tmp/log2ram_install.log; then git clone https://github.com/azlux/log2ram.git /tmp/log2ram >/dev/null 2>>/tmp/log2ram_install.log
msg_error "$(translate "Failed to clone log2ram repository. Check /tmp/log2ram_install.log")" cd /tmp/log2ram || return 1
return 1 bash install.sh >>/tmp/log2ram_install.log 2>&1
fi
cd /tmp/log2ram || { if [[ -f /etc/log2ram.conf ]] && systemctl list-units --all | grep -q log2ram; then
msg_error "$(translate "Failed to access log2ram directory")"
return 1
}
if ! bash install.sh >>/tmp/log2ram_install.log 2>&1; then
msg_error "$(translate "Failed to run log2ram installer. Check /tmp/log2ram_install.log")"
return 1
fi
if [[ -f /etc/log2ram.conf ]] && command -v log2ram >/dev/null 2>&1; then
msg_ok "$(translate "Log2RAM installed successfully")" msg_ok "$(translate "Log2RAM installed successfully")"
else else
msg_error "$(translate "Log2RAM installation verification failed. Check /tmp/log2ram_install.log")" msg_error "$(translate "Failed to install Log2RAM. See /tmp/log2ram_install.log")"
return 1 return 1
fi fi
# Detect RAM
RAM_SIZE_GB=$(free -g | awk '/^Mem:/{print $2}') RAM_SIZE_GB=$(free -g | awk '/^Mem:/{print $2}')
[[ -z "$RAM_SIZE_GB" || "$RAM_SIZE_GB" -eq 0 ]] && RAM_SIZE_GB=4 [[ -z "$RAM_SIZE_GB" || "$RAM_SIZE_GB" -eq 0 ]] && RAM_SIZE_GB=4
@ -684,21 +809,10 @@ install_log2ram_auto() {
msg_ok "$(translate "Detected RAM:") $RAM_SIZE_GB GB — $(translate "Log2RAM size set to:") $LOG2RAM_SIZE" msg_ok "$(translate "Detected RAM:") $RAM_SIZE_GB GB — $(translate "Log2RAM size set to:") $LOG2RAM_SIZE"
sed -i "s/^SIZE=.*/SIZE=$LOG2RAM_SIZE/" /etc/log2ram.conf sed -i "s/^SIZE=.*/SIZE=$LOG2RAM_SIZE/" /etc/log2ram.conf
LOG2RAM_BIN="$(command -v log2ram || echo /usr/local/bin/log2ram)"
rm -f /etc/cron.daily/log2ram /etc/cron.weekly/log2ram /etc/cron.monthly/log2ram 2>/dev/null || true
rm -f /etc/cron.hourly/log2ram rm -f /etc/cron.hourly/log2ram
echo "0 */$CRON_HOURS * * * root /usr/sbin/log2ram write" > /etc/cron.d/log2ram
{ msg_ok "$(translate "log2ram write scheduled every") $CRON_HOURS $(translate "hour(s)")"
echo 'MAILTO=""'
echo "0 */$CRON_HOURS * * * root $LOG2RAM_BIN write >/dev/null 2>&1"
} > /etc/cron.d/log2ram
chmod 0644 /etc/cron.d/log2ram
chown root:root /etc/cron.d/log2ram
msg_ok "$(translate "Log2RAM write scheduled every") $CRON_HOURS $(translate "hour(s)")"
cat << 'EOF' > /usr/local/bin/log2ram-check.sh cat << 'EOF' > /usr/local/bin/log2ram-check.sh
#!/bin/bash #!/bin/bash
@ -706,77 +820,19 @@ CONF_FILE="/etc/log2ram.conf"
LIMIT_KB=$(grep '^SIZE=' "$CONF_FILE" | cut -d'=' -f2 | tr -d 'M')000 LIMIT_KB=$(grep '^SIZE=' "$CONF_FILE" | cut -d'=' -f2 | tr -d 'M')000
USED_KB=$(df /var/log --output=used | tail -1) USED_KB=$(df /var/log --output=used | tail -1)
THRESHOLD=$(( LIMIT_KB * 90 / 100 )) THRESHOLD=$(( LIMIT_KB * 90 / 100 ))
if (( USED_KB > THRESHOLD )); then if (( USED_KB > THRESHOLD )); then
$(command -v log2ram) write /usr/sbin/log2ram write
fi fi
EOF EOF
chmod +x /usr/local/bin/log2ram-check.sh chmod +x /usr/local/bin/log2ram-check.sh
{ echo "*/5 * * * * root /usr/local/bin/log2ram-check.sh" > /etc/cron.d/log2ram-auto-sync
echo 'MAILTO=""'
echo "*/5 * * * * root /usr/local/bin/log2ram-check.sh >/dev/null 2>&1"
} > /etc/cron.d/log2ram-auto-sync
chmod 0644 /etc/cron.d/log2ram-auto-sync
chown root:root /etc/cron.d/log2ram-auto-sync
msg_ok "$(translate "Auto-sync enabled when /var/log exceeds 90% of") $LOG2RAM_SIZE" msg_ok "$(translate "Auto-sync enabled when /var/log exceeds 90% of") $LOG2RAM_SIZE"
register_tool "log2ram" true register_tool "log2ram" true
} }
# ==========================================================
setup_persistent_network() {
local LINK_DIR="/etc/systemd/network"
local BACKUP_DIR="/etc/systemd/network/backup-$(date +%Y%m%d-%H%M%S)"
msg_info "$(translate "Setting up persistent network interfaces")"
sleep 2
mkdir -p "$LINK_DIR"
if ls "$LINK_DIR"/*.link >/dev/null 2>&1; then
mkdir -p "$BACKUP_DIR"
cp "$LINK_DIR"/*.link "$BACKUP_DIR"/ 2>/dev/null || true
fi
local count=0
for iface in $(ls /sys/class/net/ | grep -vE "lo|docker|veth|br-|vmbr|tap|fwpr|fwln|virbr|bond|cilium|zt|wg"); do
if [[ -e "/sys/class/net/$iface/device" ]] || [[ -e "/sys/class/net/$iface/phy80211" ]]; then
local MAC=$(cat /sys/class/net/$iface/address 2>/dev/null)
if [[ "$MAC" =~ ^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$ ]]; then
local LINK_FILE="$LINK_DIR/10-$iface.link"
cat > "$LINK_FILE" <<EOF
[Match]
MACAddress=$MAC
[Link]
Name=$iface
EOF
chmod 644 "$LINK_FILE"
((count++))
fi
fi
done
if [[ $count -gt 0 ]]; then
msg_ok "$(translate "Created persistent names for") $count $(translate "interfaces")"
msg_ok "$(translate "Changes will apply after reboot.")"
else
msg_warn "$(translate "No physical interfaces found")"
fi
register_tool "persistent_network" true
}
# ========================================================== # ==========================================================
run_complete_optimization() { run_complete_optimization() {
@ -801,7 +857,6 @@ run_complete_optimization() {
disable_rpc disable_rpc
customize_bashrc customize_bashrc
install_log2ram_auto install_log2ram_auto
setup_persistent_network
echo -e echo -e
@ -833,7 +888,7 @@ run_complete_optimization() {
} }
check_extremeshok_warning
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
run_complete_optimization run_complete_optimization
fi fi

File diff suppressed because it is too large Load Diff

View File

@ -49,7 +49,7 @@ register_tool() {
################################################################ ################################################################
uninstall_fastfetch() { uninstall_fastfetch() {
if ! command -v fastfetch &>/dev/null && [[ ! -f /usr/local/bin/fastfetch ]]; then if ! command -v fastfetch &>/dev/null; then
msg_warn "$(translate "Fastfetch is not installed.")" msg_warn "$(translate "Fastfetch is not installed.")"
return 0 return 0
fi fi
@ -58,8 +58,7 @@ uninstall_fastfetch() {
rm -f /usr/local/bin/fastfetch /usr/bin/fastfetch rm -f /usr/local/bin/fastfetch /usr/bin/fastfetch
rm -rf "$HOME/.config/fastfetch" rm -rf "$HOME/.config/fastfetch"
rm -rf /usr/local/share/fastfetch rm -rf /usr/local/share/fastfetch
sed -i '/fastfetch/d' "$HOME/.bashrc" "$HOME/.profile" /etc/profile 2>/dev/null sed -i '/fastfetch/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null
sed -i '/# BEGIN FASTFETCH/,/# END FASTFETCH/d' "$HOME/.bashrc"
rm -f /etc/profile.d/fastfetch.sh /etc/update-motd.d/99-fastfetch rm -f /etc/profile.d/fastfetch.sh /etc/update-motd.d/99-fastfetch
dpkg -r fastfetch &>/dev/null dpkg -r fastfetch &>/dev/null
@ -69,7 +68,7 @@ uninstall_fastfetch() {
################################################################ ################################################################
uninstall_figurine_() { uninstall_figurine() {
if ! command -v figurine &>/dev/null; then if ! command -v figurine &>/dev/null; then
msg_warn "$(translate "Figurine is not installed.")" msg_warn "$(translate "Figurine is not installed.")"
return 0 return 0
@ -84,26 +83,6 @@ uninstall_figurine_() {
register_tool "figurine" false register_tool "figurine" false
} }
uninstall_figurine() {
if ! command -v figurine &>/dev/null; then
msg_warn "$(translate "Figurine is not installed.")"
return 0
fi
msg_info2 "$(translate "Uninstalling Figurine...")"
rm -f /usr/local/bin/figurine
rm -f /etc/profile.d/figurine.sh
sed -i '/lxcclean/d;/lxcupdate/d;/kernelclean/d;/cpugov/d;/updatecerts/d;/seqwrite/d;/seqread/d;/ranwrite/d;/ranread/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null
sed -i '/# ProxMenux Figurine aliases and tools/,+20d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null
sed -i '/# BEGIN PROXMENUX ALIASES/,/# END PROXMENUX ALIASES/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null
msg_ok "$(translate "Figurine removed from system")"
register_tool "figurine" false
}
################################################################ ################################################################
uninstall_kexec() { uninstall_kexec() {
@ -466,29 +445,6 @@ uninstall_log2ram() {
register_tool "log2ram" false register_tool "log2ram" false
} }
################################################################
uninstall_persistent_network() {
local LINK_DIR="/etc/systemd/network"
msg_info "$(translate "Removing all .link files from") $LINK_DIR"
sleep 2
if ! ls "$LINK_DIR"/*.link >/dev/null 2>&1; then
msg_warn "$(translate "No .link files found in") $LINK_DIR"
return 0
fi
rm -f "$LINK_DIR"/*.link
msg_ok "$(translate "Removed all .link files from") $LINK_DIR"
msg_info "$(translate "Interface names will return to default systemd behavior.")"
register_tool "persistent_network" false
}
################################################################ ################################################################
migrate_installed_tools() { migrate_installed_tools() {
@ -597,14 +553,10 @@ show_uninstall_menu() {
memory_settings) desc="Memory Settings Optimization";; memory_settings) desc="Memory Settings Optimization";;
kernel_panic) desc="Kernel Panic Configuration";; kernel_panic) desc="Kernel Panic Configuration";;
apt_ipv4) desc="APT IPv4 Force";; apt_ipv4) desc="APT IPv4 Force";;
kexec) desc="kexec for quick reboots";;
network_optimization) desc="Network Optimizations";; network_optimization) desc="Network Optimizations";;
disable_rpc) desc="RPC/rpcbind Disable";; disable_rpc) desc="RPC/rpcbind Disable";;
bashrc_custom) desc="Bashrc Customization";; bashrc_custom) desc="Bashrc Customization";;
figurine) desc="Figurine";;
fastfetch) desc="Fastfetch";;
log2ram) desc="Log2ram (SSD Protection)";; log2ram) desc="Log2ram (SSD Protection)";;
persistent_network) desc="Setting persistent network interfaces";;
*) desc="$tool";; *) desc="$tool";;
esac esac
menu_options+=("$tool" "$desc" "off") menu_options+=("$tool" "$desc" "off")

View File

@ -1,12 +1,13 @@
#!/bin/bash #!/bin/bash
# ========================================================== # ==========================================================
# ProxMenu - A menu-driven script for Proxmox VE management # ProxMenu - A menu-driven script for Proxmox VE management
# ========================================================== # ==========================================================
# Author : MacRimi # Author : MacRimi
# Copyright : (c) 2024 MacRimi # Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE) # License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.2 # Version : 1.1
# Last Updated: 30/07/2025 # Last Updated: 28/06/2025
# ========================================================== # ==========================================================
# Description: # Description:
# This script allows users to assign physical disks to existing # This script allows users to assign physical disks to existing
@ -16,7 +17,6 @@
# - Identifies and displays unassigned physical disks. # - Identifies and displays unassigned physical disks.
# - Allows the user to select multiple disks and attach them to a CT. # - Allows the user to select multiple disks and attach them to a CT.
# - Configures the selected disks for the CT and verifies the assignment. # - Configures the selected disks for the CT and verifies the assignment.
# - Uses persistent device paths to avoid issues with device order changes.
# ========================================================== # ==========================================================
# Configuration ============================================ # Configuration ============================================
@ -32,66 +32,8 @@ fi
load_language load_language
initialize_cache initialize_cache
# Get OS codename for repository configuration
OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs )"
# ========================================================== # ==========================================================
# Function to get persistent device path
get_persistent_path() {
local device="$1"
local persistent_path=""
# Try by-id first (most reliable)
for path in /dev/disk/by-id/*; do
if [[ -e "$path" && "$(readlink -f "$path")" == "$device" ]]; then
# Prefer ata- or scsi- over wwn- for readability
if [[ "$path" =~ ata-|scsi- ]]; then
echo "$path"
return 0
elif [[ -z "$persistent_path" ]]; then
persistent_path="$path"
fi
fi
done
# Return the first found by-id path if any
if [[ -n "$persistent_path" ]]; then
echo "$persistent_path"
return 0
fi
# Try by-path as fallback
for path in /dev/disk/by-path/*; do
if [[ -e "$path" && "$(readlink -f "$path")" == "$device" ]]; then
echo "$path"
return 0
fi
done
# Fallback to original device if no persistent path found
msg_warn "$(translate "No persistent path found for") $device, $(translate "using direct path")"
echo "$device"
}
# Function to ensure repositories are properly configured
ensure_repositories() {
local sources_file="/etc/apt/sources.list"
local need_update=false
# Only verify the main repository with contrib and non-free
if ! grep -q "deb.*${OS_CODENAME}.*main" "$sources_file"; then
echo "deb http://deb.debian.org/debian ${OS_CODENAME} main contrib non-free non-free-firmware" >> "$sources_file"
need_update=true
fi
if [ "$need_update" = true ]; then
apt update >/dev/null 2>&1
fi
return 0
}
get_disk_info() { get_disk_info() {
local disk=$1 local disk=$1
MODEL=$(lsblk -dn -o MODEL "$disk" | xargs) MODEL=$(lsblk -dn -o MODEL "$disk" | xargs)
@ -99,10 +41,8 @@ get_disk_info() {
echo "$MODEL" "$SIZE" echo "$MODEL" "$SIZE"
} }
# Ensure repositories are configured
ensure_repositories
CT_LIST=$(pct list | awk 'NR>1 {print $1, $3}') CT_LIST=$(pct list | awk 'NR>1 {print $1, $3}')
if [ -z "$CT_LIST" ]; then if [ -z "$CT_LIST" ]; then
whiptail --title "$(translate "Error")" --msgbox "$(translate "No CTs available in the system.")" 8 40 whiptail --title "$(translate "Error")" --msgbox "$(translate "No CTs available in the system.")" 8 40
exit 1 exit 1
@ -120,11 +60,12 @@ CTID=$(echo "$CTID" | tr -d '"')
clear clear
show_proxmenux_logo show_proxmenux_logo
echo -e echo -e
msg_title "$(translate "Add Disk") Passthrough $(translate "to a LXC")" msg_info2 "$(translate "Add Disk") Passthrough $(translate "to a LXC")"
echo -e echo -e
msg_ok "$(translate "CT selected successfully.")" msg_ok "$(translate "CT selected successfully.")"
CT_STATUS=$(pct status "$CTID" | awk '{print $2}') CT_STATUS=$(pct status "$CTID" | awk '{print $2}')
if [ "$CT_STATUS" != "running" ]; then if [ "$CT_STATUS" != "running" ]; then
msg_info "$(translate "Starting CT") $CTID..." msg_info "$(translate "Starting CT") $CTID..."
pct start "$CTID" pct start "$CTID"
@ -138,10 +79,10 @@ if [ "$CT_STATUS" != "running" ]; then
fi fi
CONF_FILE="/etc/pve/lxc/$CTID.conf" CONF_FILE="/etc/pve/lxc/$CTID.conf"
if grep -q '^unprivileged: 1' "$CONF_FILE"; then if grep -q '^unprivileged: 1' "$CONF_FILE"; then
if whiptail --title "$(translate "Privileged Container")" \ if whiptail --title "$(translate "Privileged Container")" \
--yesno "$(translate "The selected container is unprivileged. A privileged container is required for direct device passthrough.")\\n\\n$(translate "Do you want to convert it to a privileged container now?")" 12 70; then --yesno "$(translate "The selected container is unprivileged. A privileged container is required for direct device passthrough.")\\n\\n$(translate "Do you want to convert it to a privileged container now?")" 12 70; then
msg_info "$(translate "Stopping container") $CTID..." msg_info "$(translate "Stopping container") $CTID..."
pct shutdown "$CTID" &>/dev/null pct shutdown "$CTID" &>/dev/null
for i in {1..10}; do for i in {1..10}; do
@ -150,18 +91,15 @@ if grep -q '^unprivileged: 1' "$CONF_FILE"; then
break break
fi fi
done done
if [ "$(pct status "$CTID" | awk '{print $2}')" == "running" ]; then if [ "$(pct status "$CTID" | awk '{print $2}')" == "running" ]; then
msg_error "$(translate "Failed to stop the container.")" msg_error "$(translate "Failed to stop the container.")"
exit 1 exit 1
fi fi
msg_ok "$(translate "Container stopped.")" msg_ok "$(translate "Container stopped.")"
cp "$CONF_FILE" "$CONF_FILE.bak" cp "$CONF_FILE" "$CONF_FILE.bak"
sed -i '/^unprivileged: 1/d' "$CONF_FILE" sed -i '/^unprivileged: 1/d' "$CONF_FILE"
echo "unprivileged: 0" >> "$CONF_FILE" echo "unprivileged: 0" >> "$CONF_FILE"
msg_ok "$(translate "Container successfully converted to privileged.")" msg_ok "$(translate "Container successfully converted to privileged.")"
msg_info "$(translate "Starting container") $CTID..." msg_info "$(translate "Starting container") $CTID..."
pct start "$CTID" &>/dev/null pct start "$CTID" &>/dev/null
sleep 2 sleep 2
@ -185,6 +123,7 @@ MOUNTED_DISKS=$(lsblk -ln -o NAME,MOUNTPOINT | awk '$2!="" {print "/dev/" $1}')
ZFS_DISKS="" ZFS_DISKS=""
ZFS_RAW=$(zpool list -v -H 2>/dev/null | awk '{print $1}' | grep -v '^NAME$' | grep -v '^-' | grep -v '^mirror') ZFS_RAW=$(zpool list -v -H 2>/dev/null | awk '{print $1}' | grep -v '^NAME$' | grep -v '^-' | grep -v '^mirror')
for entry in $ZFS_RAW; do for entry in $ZFS_RAW; do
path="" path=""
if [[ "$entry" == wwn-* || "$entry" == ata-* ]]; then if [[ "$entry" == wwn-* || "$entry" == ata-* ]]; then
@ -194,7 +133,6 @@ for entry in $ZFS_RAW; do
elif [[ "$entry" == /dev/* ]]; then elif [[ "$entry" == /dev/* ]]; then
path="$entry" path="$entry"
fi fi
if [ -n "$path" ]; then if [ -n "$path" ]; then
base_disk=$(lsblk -no PKNAME "$path" 2>/dev/null) base_disk=$(lsblk -no PKNAME "$path" 2>/dev/null)
if [ -n "$base_disk" ]; then if [ -n "$base_disk" ]; then
@ -202,6 +140,7 @@ for entry in $ZFS_RAW; do
fi fi
fi fi
done done
ZFS_DISKS=$(echo "$ZFS_DISKS" | sort -u) ZFS_DISKS=$(echo "$ZFS_DISKS" | sort -u)
is_disk_in_use() { is_disk_in_use() {
@ -215,7 +154,6 @@ is_disk_in_use() {
return 0 return 0
fi fi
done < <(lsblk -ln -o NAME,FSTYPE "$disk" | tail -n +2) done < <(lsblk -ln -o NAME,FSTYPE "$disk" | tail -n +2)
if echo "$USED_DISKS" | grep -q "$disk" || echo "$ZFS_DISKS" | grep -q "$disk"; then if echo "$USED_DISKS" | grep -q "$disk" || echo "$ZFS_DISKS" | grep -q "$disk"; then
return 0 return 0
fi fi
@ -228,7 +166,6 @@ RAID_ACTIVE=$(grep -Po 'md\d+\s*:\s*active\s+raid[0-9]+' /proc/mdstat | awk '{pr
while read -r DISK; do while read -r DISK; do
[[ "$DISK" =~ /dev/zd ]] && continue [[ "$DISK" =~ /dev/zd ]] && continue
INFO=($(get_disk_info "$DISK")) INFO=($(get_disk_info "$DISK"))
MODEL="${INFO[@]::${#INFO[@]}-1}" MODEL="${INFO[@]::${#INFO[@]}-1}"
SIZE="${INFO[-1]}" SIZE="${INFO[-1]}"
@ -256,7 +193,6 @@ while read -r DISK; do
USED_BY="" USED_BY=""
REAL_PATH=$(readlink -f "$DISK") REAL_PATH=$(readlink -f "$DISK")
CONFIG_DATA=$(grep -vE '^\s*#' /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf 2>/dev/null) CONFIG_DATA=$(grep -vE '^\s*#' /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf 2>/dev/null)
if grep -Fq "$REAL_PATH" <<< "$CONFIG_DATA"; then if grep -Fq "$REAL_PATH" <<< "$CONFIG_DATA"; then
USED_BY="$(translate "In use")" USED_BY="$(translate "In use")"
else else
@ -284,15 +220,8 @@ while read -r DISK; do
SHOW_DISK=false SHOW_DISK=false
fi fi
# Check if disk is already assigned to this CT using persistent paths
if pct config "$CTID" | grep -vE '^\s*#|^description:' | grep -q "$DISK"; then if pct config "$CTID" | grep -vE '^\s*#|^description:' | grep -q "$DISK"; then
SHOW_DISK=false SHOW_DISK=false
else
# Also check persistent paths
PERSISTENT_DISK=$(get_persistent_path "$DISK")
if [[ "$PERSISTENT_DISK" != "$DISK" ]] && pct config "$CTID" | grep -vE '^\s*#|^description:' | grep -q "$PERSISTENT_DISK"; then
SHOW_DISK=false
fi
fi fi
if $SHOW_DISK; then if $SHOW_DISK; then
@ -300,7 +229,6 @@ while read -r DISK; do
[[ "$IS_RAID" == true ]] && LABEL+=" ⚠ RAID" [[ "$IS_RAID" == true ]] && LABEL+=" ⚠ RAID"
[[ "$IS_LVM" == true ]] && LABEL+=" ⚠ LVM" [[ "$IS_LVM" == true ]] && LABEL+=" ⚠ LVM"
[[ "$IS_ZFS" == true ]] && LABEL+=" ⚠ ZFS" [[ "$IS_ZFS" == true ]] && LABEL+=" ⚠ ZFS"
DESCRIPTION=$(printf "%-30s %10s%s" "$MODEL" "$SIZE" "$LABEL") DESCRIPTION=$(printf "%-30s %10s%s" "$MODEL" "$SIZE" "$LABEL")
FREE_DISKS+=("$DISK" "$DESCRIPTION" "OFF") FREE_DISKS+=("$DISK" "$DESCRIPTION" "OFF")
fi fi
@ -342,7 +270,6 @@ msg_info "$(translate "Processing selected disks...")"
for DISK in $SELECTED; do for DISK in $SELECTED; do
DISK=$(echo "$DISK" | tr -d '"') DISK=$(echo "$DISK" | tr -d '"')
DISK_INFO=$(get_disk_info "$DISK") DISK_INFO=$(get_disk_info "$DISK")
ASSIGNED_TO="" ASSIGNED_TO=""
RUNNING_CTS="" RUNNING_CTS=""
RUNNING_VMS="" RUNNING_VMS=""
@ -382,7 +309,6 @@ for DISK in $SELECTED; do
fi fi
cleanup cleanup
if lsblk "$DISK" | grep -q "raid" || grep -q "${DISK##*/}" /proc/mdstat; then if lsblk "$DISK" | grep -q "raid" || grep -q "${DISK##*/}" /proc/mdstat; then
whiptail --title "$(translate "RAID Detected")" --msgbox "$(translate "The disk") $DISK_INFO $(translate "appears to be part of a") RAID. $(translate "For security reasons, the system cannot format it.")\\n\\n$(translate "If you are sure you want to use it, please remove the") RAID metadata $(translate "or format it manually using external tools.")\\n\\n$(translate "After that, run this script again to add it.")" 18 70 whiptail --title "$(translate "RAID Detected")" --msgbox "$(translate "The disk") $DISK_INFO $(translate "appears to be part of a") RAID. $(translate "For security reasons, the system cannot format it.")\\n\\n$(translate "If you are sure you want to use it, please remove the") RAID metadata $(translate "or format it manually using external tools.")\\n\\n$(translate "After that, run this script again to add it.")" 18 70
clear clear
@ -424,14 +350,12 @@ for DISK in $SELECTED; do
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
continue continue
fi fi
echo -e "$(translate "Creating partition table and partition...")" echo -e "$(translate "Creating partition table and partition...")"
parted -s "$DISK" mklabel gpt parted -s "$DISK" mklabel gpt
parted -s "$DISK" mkpart primary 0% 100% parted -s "$DISK" mkpart primary 0% 100%
sleep 2 sleep 2
partprobe "$DISK" partprobe "$DISK"
sleep 2 sleep 2
PARTITION=$(lsblk -rno NAME "$DISK" | awk -v disk="$(basename "$DISK")" '$1 != disk {print $1; exit}') PARTITION=$(lsblk -rno NAME "$DISK" | awk -v disk="$(basename "$DISK")" '$1 != disk {print $1; exit}')
if [ -n "$PARTITION" ]; then if [ -n "$PARTITION" ]; then
PARTITION="/dev/$PARTITION" PARTITION="/dev/$PARTITION"
@ -459,6 +383,7 @@ for DISK in $SELECTED; do
fi fi
whiptail --title "$(translate "WARNING")" --yesno "$(translate "WARNING: This operation will FORMAT the disk") $DISK_INFO $(translate "with") $FORMAT_TYPE.\\n\\n$(translate "ALL DATA ON THIS DISK WILL BE PERMANENTLY LOST!")\\n\\n$(translate "Are you sure you want to continue")" 15 70 whiptail --title "$(translate "WARNING")" --yesno "$(translate "WARNING: This operation will FORMAT the disk") $DISK_INFO $(translate "with") $FORMAT_TYPE.\\n\\n$(translate "ALL DATA ON THIS DISK WILL BE PERMANENTLY LOST!")\\n\\n$(translate "Are you sure you want to continue")" 15 70
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
whiptail --title "$(translate "Format Cancelled")" --msgbox "$(translate "Format operation cancelled. The disk will not be added.")" 8 60 whiptail --title "$(translate "Format Cancelled")" --msgbox "$(translate "Format operation cancelled. The disk will not be added.")" 8 60
continue continue
@ -519,38 +444,32 @@ for DISK in $SELECTED; do
fi fi
############################################################################## ##############################################################################
# Get persistent path for the partition
PERSISTENT_PARTITION=$(get_persistent_path "$PARTITION")
# Apply passthrough with persistent path
CURRENT_FS=$(lsblk -no FSTYPE "$PARTITION" | xargs) CURRENT_FS=$(lsblk -no FSTYPE "$PARTITION" | xargs)
if [ "$CURRENT_FS" == "xfs" ] || [ "$FORMAT_TYPE" == "xfs" ]; then if [ "$CURRENT_FS" == "xfs" ] || [ "$FORMAT_TYPE" == "xfs" ]; then
RESULT=$(pct set "$CTID" -mp${INDEX} "$PERSISTENT_PARTITION,mp=$MOUNT_POINT,backup=0,ro=0" 2>&1)
RESULT=$(pct set "$CTID" -mp${INDEX} "$PARTITION,mp=$MOUNT_POINT,backup=0,ro=0" 2>&1)
else else
RESULT=$(pct set "$CTID" -mp${INDEX} "$PERSISTENT_PARTITION,mp=$MOUNT_POINT,backup=0,ro=0,acl=1" 2>&1)
RESULT=$(pct set "$CTID" -mp${INDEX} "$PARTITION,mp=$MOUNT_POINT,backup=0,ro=0,acl=1" 2>&1)
fi fi
# Adjust permissions inside the CT
pct exec "$CTID" -- chmod -R 775 "$MOUNT_POINT" 2>/dev/null || true
# Show confirmation with persistent identifier pct exec "$CTID" -- chmod -R 777 "$MOUNT_POINT" 2>/dev/null || true
msg_ok "$(translate "Assigned using") $PERSISTENT_PARTITION"
############################################################################## ##############################################################################
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
MESSAGE="$(translate "The disk") $DISK_INFO $(translate "has been successfully added to CT") $CTID $(translate "as a mount point at") $MOUNT_POINT." MESSAGE="$(translate "The disk") $DISK_INFO $(translate "has been successfully added to CT") $CTID $(translate "as a mount point at") $MOUNT_POINT."
MESSAGE+="\\n$(translate "Using persistent path"): $PERSISTENT_PARTITION"
if [ -n "$ASSIGNED_TO" ]; then if [ -n "$ASSIGNED_TO" ]; then
MESSAGE+="\\n\\n$(translate "WARNING: This disk is also assigned to the following CT(s):")\\n$ASSIGNED_TO" MESSAGE+="\\n\\n$(translate "WARNING: This disk is also assigned to the following CT(s):")\\n$ASSIGNED_TO"
MESSAGE+="\\n$(translate "Make sure not to start CTs that share this disk at the same time to avoid data corruption.")" MESSAGE+="\\n$(translate "Make sure not to start CTs that share this disk at the same time to avoid data corruption.")"
fi fi
SUCCESS_MESSAGES+="$MESSAGE\\n\\n" SUCCESS_MESSAGES+="$MESSAGE\\n\\n"
((DISKS_ADDED++)) ((DISKS_ADDED++))
else else
ERROR_MESSAGES+="$(translate "Could not add disk") $DISK_INFO $(translate "to CT") $CTID.\\n$(translate "Error:") $RESULT\\n\\n" ERROR_MESSAGES+="$(translate "Could not add disk") $DISK_INFO $(translate "to CT") $CTID.\\n$(translate "Error:") $RESULT\\n\\n"
fi fi
done done
msg_ok "$(translate "Disk processing completed.")" msg_ok "$(translate "Disk processing completed.")"
@ -566,4 +485,5 @@ fi
msg_success "$(translate "Press Enter to return to menu...")" msg_success "$(translate "Press Enter to return to menu...")"
read -r read -r
exit 0 exit 0

View File

@ -1,271 +0,0 @@
#!/bin/bash
# ==========================================================
# ProxMenux - Manual Proxmox VE 8 to 9 Upgrade Guide
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.0
# Last Updated: 13/08/2025
# ==========================================================
# Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
show_command() {
local step="$1"
local description="$2"
local command="$3"
local note="$4"
local command_extra="$5"
echo -e "${BGN}${step}.${CL} ${BL}${description}${CL}"
echo ""
echo -e "${TAB}${command}"
echo -e
[[ -n "$note" ]] && echo -e "${TAB}${DARK_GRAY}${note}${CL}"
[[ -n "$command_extra" ]] && echo -e "${TAB}${YW}${command_extra}${CL}"
echo ""
}
show_proxmox_upgrade_manual_guide() {
clear
show_proxmenux_logo
msg_title "$(translate "Proxmox VE 8 to 9 Manual Upgrade Guide")"
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "Source:")${CL} ${BL}https://pve.proxmox.com/wiki/Upgrade_from_8_to_9${CL}"
echo -e
echo -e
echo -e "${TAB}${BOLD}$(translate "IMPORTANT PREREQUISITES:")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "System must be updated to latest PVE 8.4+ before starting")${CL}"
echo -e "${TAB}${BGN}$(translate "Use SSH or terminal access (SSH recommended)")${CL}"
echo -e "${TAB}${BGN}$(translate "Use tmux or screen to avoid interruptions")${CL}"
echo -e "${TAB}${BGN}$(translate "Have valid backups of all VMs and containers")${CL}"
echo -e "${TAB}${BGN}$(translate "At least 5GB free space on root filesystem")${CL}"
echo -e "${TAB}${BGN}$(translate "Do not run the upgrade from the Web UI virtual console (it will disconnect)")${CL}"
echo -e
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
show_command "1" \
"$(translate "Update system to latest PVE 8.4+ (if not done already):")\n\n" \
"apt update && apt dist-upgrade -y" \
"$(translate "Or use ProxMenux update function")" \
"\n\n"
show_command "2" \
"$(translate "Verify PVE version (must be 8.4.1 or newer):")\n\n" \
"pveversion" \
"" \
"\n"
show_command "2.1" \
"${YW}$(translate "If this node runs hyper-converged Ceph: ensure Ceph is 19.x (Squid) BEFORE upgrading PVE.")${CL}\n\n" \
"ceph --version" \
"$(translate "If not 19.x, upgrade Ceph (Reef→Squid) first per the official guide:") ${BL}https://pve.proxmox.com/wiki/Ceph_Squid${CL}" \
"\n"
show_command "3" \
"$(translate "Run upgrade checklist script:")\n\n" \
"pve8to9 --full" \
"${YW}$(translate "If it warns about 'systemd-boot' meta-package, remove it:")${CL} apt remove systemd-boot" \
"\n"
show_command "4" \
"$(translate "Start terminal multiplexer (recommended):")\n\n" \
"tmux new-session -s upgrade ${DARK_GRAY}$(translate "# Recommended: avoids disconnection during upgrade")${CL}\n\n screen -S upgrade ${DARK_GRAY}$(translate "# Alternative if you prefer screen")${CL}" \
"" \
"\n"
show_command "5" \
"$(translate "Update Debian repositories to Trixie:")\n\n" \
"sed -i 's/bookworm/trixie/g' /etc/apt/sources.list" \
"" \
"\n"
show_command "6" \
"${YW}$(translate "Update PVE enterprise repository (Only if using enterprise):")${CL}\n\n" \
"${CUS}sed -i 's/bookworm/trixie/g' /etc/apt/sources.list.d/pve-enterprise.list${CL}" \
"$(translate "Skip this step if using no-subscription repository")" \
"\n\n"
show_command "7" \
"${YW}$(translate "Add new PVE 9 enterprise repository (deb822 format) (Only if using enterprise):")${CL}\n\n" \
"${CUS}cat > /etc/apt/sources.list.d/pve-enterprise.sources << EOF
Types: deb
URIs: https://enterprise.proxmox.com/debian/pve
Suites: trixie
Components: pve-enterprise
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF${CL}" \
"$(translate "Only if using enterprise subscription")" \
"\n\n"
show_command "8" \
"$(translate "OR add new PVE 9 no-subscription repository:")\n\n" \
"cat > /etc/apt/sources.list.d/proxmox.sources << EOF
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF" \
"$(translate "Only if using no-subscription repository")" \
"\n\n"
show_command "8.1" \
"$(translate "Refresh APT index and verify repositories:")\n\n" \
"apt update && apt policy | sed -n '1,120p'" \
"$(translate "Ensure there are no errors and that proxmox-ve candidate shows 9.x")" \
"\n"
show_command "9" \
"${YW}$(translate "Update Ceph repository (Only if using Ceph):")${CL}\n\n" \
"${CUS}cat > /etc/apt/sources.list.d/ceph.sources << EOF
Types: deb
URIs: http://download.proxmox.com/debian/ceph-squid
Suites: trixie
Components: no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF${CL}" \
"$(translate "Use enterprise URL if you have subscription.")" \
"\n\n"
show_command "10" \
"$(translate "Remove old repository files:")\n\n" \
"rm -f /etc/apt/sources.list.d/pve-enterprise.list /etc/apt/sources.list.d/ceph.list" \
"$(translate "Also comment any remaining 'bookworm' entries in *.list if present.")" \
"\n"
show_command "11" \
"$(translate "Update package index:")\n\n" \
"apt update" \
"" \
"\n"
show_command "12" \
"$(translate "Disable kernel audit messages (optional but recommended):")\n\n" \
"systemctl disable --now systemd-journald-audit.socket" \
"" \
"\n"
show_command "13" \
"$(translate "Start the main system upgrade:")\n\n" \
"apt dist-upgrade" \
"$(translate "This will take time. Answer prompts carefully - see notes below.")\n" \
"\n"
echo -e "${TAB}${BOLD}$(translate "UPGRADE PROMPTS - RECOMMENDED ANSWERS:")${CL}"
echo -e
echo -e "${TAB}${BGN}/etc/issue:${CL} ${YW}$(translate "Keep current version (N)")${CL}"
echo -e "${TAB}${BGN}/etc/lvm/lvm.conf:${CL} ${YW}$(translate "Install maintainer's version (Y)")${CL}"
echo -e "${TAB}${BGN}/etc/ssh/sshd_config:${CL} ${YW}$(translate "Install maintainer's version (Y)")${CL}"
echo -e "${TAB}${BGN}/etc/default/grub:${CL} ${YW}$(translate "Keep current version (N) if modified")${CL}"
echo -e "${TAB}${BGN}/etc/chrony/chrony.conf:${CL} ${YW}$(translate "Install maintainer's version (Y)")${CL}"
echo -e "${TAB}${BGN}$(translate "Service restarts:")${CL} ${YW}$(translate "Use default (Yes)")${CL}"
echo -e "${TAB}${BGN}apt-listchanges:${CL} ${YW}$(translate "Press 'q' to exit")${CL}"
echo -e
echo -e
echo -e
show_command "13.1" \
"${YW}$(translate "If booting in EFI mode with root on LVM: install GRUB for EFI")${CL}\n\n" \
"[ -d /sys/firmware/efi ] && apt install grub-efi-amd64" \
"$(translate "Per official known issues; ensures proper boot after upgrade")" \
"\n"
show_command "14" \
"$(translate "Run checklist again to verify upgrade:")\n\n" \
"pve8to9 --full" \
"$(translate "Should show fewer or no issues")" \
"\n"
show_command "15" \
"$(translate "Reboot the system:")\n\n" \
"reboot" \
"" \
"\n"
show_command "16" \
"$(translate "After reboot, verify PVE version:")\n\n" \
"pveversion" \
"$(translate "Should show pve-manager/9.x.x")" \
"\n"
show_command "17" \
"$(translate "Optional: Modernize repository sources:")\n\n" \
"apt modernize-sources" \
"$(translate "Converts to deb822; keeps .list backups as .bak")" \
"\n"
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
echo -e
echo -e "${TAB}${BOLD}$(translate "CLUSTER UPGRADE NOTES:")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "Upgrade one node at a time")${CL}"
echo -e "${TAB}${BGN}$(translate "Migrate VMs away from node being upgraded")${CL}"
echo -e "${TAB}${BGN}$(translate "Wait for each node to complete before starting next")${CL}"
echo -e "${TAB}${BGN}$(translate "HA groups will be migrated to HA rules automatically")${CL}"
echo -e
echo -e
echo -e
echo -e "${TAB}${BOLD}$(translate "TROUBLESHOOTING:")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "If upgrade fails:")${CL} ${YW}apt -f install${CL}"
echo -e "${TAB}${BGN}$(translate "If repositories error:")${CL} ${YW}Check /etc/apt/sources.list*${CL}"
echo -e "${TAB}${BGN}$(translate "If 'proxmox-ve' removal warning:")${CL} ${YW}Fix repository configuration (ensure PVE 9 repo active)${CL}"
echo -e "${TAB}${BGN}$(translate "Emergency recovery:")${CL} ${YW}Boot from rescue system${CL}"
echo -e
echo -e
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
echo -e
read -r
clear
exit 0
#bash <(curl -fsSL "$REPO_URL/scripts/utilities/upgrade_pve8_to_pve9.sh")
}
# Main execution
show_proxmox_upgrade_manual_guide

View File

@ -41,91 +41,464 @@ initialize_cache
# ========================================================== # ==========================================================
NECESSARY_REBOOT=1
OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs )"
if [ -z "$OS_CODENAME" ]; then
OS_CODENAME=$(lsb_release -cs 2>/dev/null || echo "bookworm")
fi
# ======================================================
# Auxiliary functions
# ======================================================
lvm_repair_check() {
msg_info "$(translate "Checking and repairing old LVM PV headers (if needed)...")"
pvs_output=$(LC_ALL=C pvs -v 2>&1 | grep "old PV header")
if [ -z "$pvs_output" ]; then
msg_ok "$(translate "No PVs with old headers found.")"
return
fi
declare -A vg_map
while read -r line; do
pv=$(echo "$line" | grep -o '/dev/[^ ]*')
vg=$(pvs -o vg_name --noheadings "$pv" | awk '{print $1}')
if [ -n "$vg" ]; then
vg_map["$vg"]=1
fi
done <<< "$pvs_output"
for vg in "${!vg_map[@]}"; do
msg_warn "$(translate "Old PV header(s) found in VG $vg. Updating metadata...")"
vgck --updatemetadata "$vg"
vgchange -ay "$vg"
if [ $? -ne 0 ]; then
msg_warn "$(translate "Metadata update failed for VG $vg. Review manually.")"
else
msg_ok "$(translate "Metadata updated successfully for VG $vg")"
fi
done
}
cleanup_duplicate_repos() {
local sources_file="/etc/apt/sources.list"
local temp_file=$(mktemp)
local cleaned_count=0
declare -A seen_repos
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then
echo "$line" >> "$temp_file"
continue
fi
if [[ "$line" =~ ^deb ]]; then
read -r _ url dist components <<< "$line"
local key="${url}_${dist}"
if [[ -v "seen_repos[$key]" ]]; then
echo "# $line" >> "$temp_file"
cleaned_count=$((cleaned_count + 1))
else
echo "$line" >> "$temp_file"
seen_repos[$key]="$components"
fi
else
echo "$line" >> "$temp_file"
fi
done < "$sources_file"
mv "$temp_file" "$sources_file"
chmod 644 "$sources_file"
local pve_files=(/etc/apt/sources.list.d/*proxmox*.list /etc/apt/sources.list.d/*pve*.list)
local pve_public_repo="/etc/apt/sources.list.d/pve-public-repo.list"
local pve_public_repo_exists=false
local pve_repo_count=0
if [ -f "$pve_public_repo" ] && grep -q "^deb.*pve-no-subscription" "$pve_public_repo"; then
pve_public_repo_exists=true
pve_repo_count=1
fi
for file in "${pve_files[@]}"; do
if [ -f "$file" ] && grep -q "^deb.*pve-no-subscription" "$file"; then
if ! $pve_public_repo_exists && [[ "$file" == "$pve_public_repo" ]]; then
sed -i 's/^# *deb/deb/' "$file"
pve_public_repo_exists=true
pve_repo_count=1
elif [[ "$file" != "$pve_public_repo" ]]; then
sed -i 's/^deb/# deb/' "$file"
cleaned_count=$((cleaned_count + 1))
fi
fi
done
if [ $cleaned_count -gt 0 ]; then
msg_ok "$(translate "Duplicate repositories cleaned: $cleaned_count")"
fi
apt update
}
apt_upgrade() { apt_upgrade() {
local pve_version local start_time=$(date +%s)
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1) local log_file="/var/log/proxmox-update-$(date +%Y%m%d-%H%M%S).log"
local changes_made=false
if [[ -z "$pve_version" ]]; then
msg_error "Unable to detect Proxmox version." clear
show_proxmenux_logo
echo -e
msg_title "$(translate "Proxmox system update")"
# ======================================================
# Basic checks
# ======================================================
# Check minimum disk space
local available_space=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}')
if [ "$available_space" -lt 1024 ]; then
msg_error "$(translate "Insufficient disk space. Available: ${available_space}MB")"
return 1 return 1
fi fi
if [[ "$pve_version" -ge 9 ]]; then # Check connectivity
show_proxmenux_logo if ! ping -c 1 download.proxmox.com >/dev/null 2>&1; then
msg_title "$(translate "Proxmox system update")" msg_error "$(translate "Cannot reach Proxmox repositories")"
bash <(curl -fsSL "$REPO_URL/scripts/global/update-pve.sh") return 1
fi
else # ======================================================
show_proxmenux_logo # Proxmox repository configuration
msg_title "$(translate "Proxmox system update")" # ======================================================
bash <(curl -fsSL "$REPO_URL/scripts/global/update-pve8.sh")
# Disable enterprise Proxmox repository
if [ -f /etc/apt/sources.list.d/pve-enterprise.list ] && grep -q "^deb" /etc/apt/sources.list.d/pve-enterprise.list; then
msg_info "$(translate "Disabling enterprise Proxmox repository...")"
sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/pve-enterprise.list
msg_ok "$(translate "Enterprise Proxmox repository disabled")"
changes_made=true
fi
# Disable enterprise Ceph repository
if [ -f /etc/apt/sources.list.d/ceph.list ] && grep -q "^deb" /etc/apt/sources.list.d/ceph.list; then
msg_info "$(translate "Disabling enterprise Ceph repository...")"
sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/ceph.list
msg_ok "$(translate "Enterprise Ceph repository disabled")"
changes_made=true
fi
# Enable free public repository
if [ ! -f /etc/apt/sources.list.d/pve-public-repo.list ] || ! grep -q "pve-no-subscription" /etc/apt/sources.list.d/pve-public-repo.list; then
msg_info "$(translate "Enabling free public Proxmox repository...")"
echo "deb http://download.proxmox.com/debian/pve ${OS_CODENAME} pve-no-subscription" > /etc/apt/sources.list.d/pve-public-repo.list
msg_ok "$(translate "Free public Proxmox repository enabled")"
changes_made=true
fi
# ======================================================
# Debian repository configuration
# ======================================================
local sources_file="/etc/apt/sources.list"
local debian_changes=false
# Clean up malformed entries first
if grep -q -E "(debian-security -security|debian main$|debian -updates)" "$sources_file"; then
msg_info "$(translate "Cleaning malformed repository entries...")"
# Remove malformed lines that cause 404 errors
sed -i '/^deb.*debian-security -security/d' "$sources_file"
sed -i '/^deb.*debian main$/d' "$sources_file"
sed -i '/^deb.*debian -updates/d' "$sources_file"
debian_changes=true
msg_ok "$(translate "Cleaning malformed repository sucefull")"
fi
# Replace old mirrors
if grep -q "ftp.es.debian.org" "$sources_file"; then
sed -i 's|ftp.es.debian.org|deb.debian.org|g' "$sources_file"
debian_changes=true
fi
# Fix incomplete security repository line
if grep -q "^deb http://security.debian.org ${OS_CODENAME}-security main contrib$" "$sources_file"; then
sed -i "s|^deb http://security.debian.org ${OS_CODENAME}-security main contrib$|deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware|" "$sources_file"
debian_changes=true
fi fi
} local temp_sources=$(mktemp)
grep -E '^[[:space:]]*#|^[[:space:]]*$' "$sources_file" > "$temp_sources"
check_reboot() {
cat >> "$temp_sources" << EOF
# Debian ${OS_CODENAME} repositories
deb http://deb.debian.org/debian ${OS_CODENAME} main contrib non-free non-free-firmware
deb http://deb.debian.org/debian ${OS_CODENAME}-updates main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security ${OS_CODENAME}-security main contrib non-free non-free-firmware
EOF
if ! cmp -s "$sources_file" "$temp_sources"; then
cp "$sources_file" "${sources_file}.backup-$(date +%Y%m%d-%H%M%S)"
mv "$temp_sources" "$sources_file"
debian_changes=true
msg_ok "$(translate "Debian repositories updated")"
else
rm "$temp_sources"
fi
if [ ! -f /etc/apt/apt.conf.d/no-bookworm-firmware.conf ]; then
echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > /etc/apt/apt.conf.d/no-bookworm-firmware.conf
debian_changes=true
fi
if [ "$debian_changes" = true ]; then
changes_made=true
fi
# ======================================================
# Clean duplicate repositories
# ======================================================
cleanup_duplicate_repos
# ======================================================
# System update
# ======================================================
# Update package lists
if [ "$changes_made" = true ]; then
msg_info "$(translate "Updating package lists...")"
else
msg_info "$(translate "Checking for available updates...")"
fi
if apt-get update > "$log_file" 2>&1; then
msg_ok "$(translate "Package lists updated")"
else
msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
echo "$(translate "Repository errors found:")"
grep -E "Err:|E:" "$log_file" | head -5
return 1
fi
# Remove conflicting packages
local conflicting_packages=$(dpkg -l 2>/dev/null | grep -E "^ii.*(ntp|openntpd|systemd-timesyncd)" | awk '{print $2}')
if [ -n "$conflicting_packages" ]; then
msg_info "$(translate "Removing conflicting packages...")"
DEBIAN_FRONTEND=noninteractive apt-get -y purge $conflicting_packages >> "$log_file" 2>&1
msg_ok "$(translate "Conflicting packages removed")"
fi
# Show update information
local upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable")
if [ "$upgradable" -gt 0 ]; then
# Show with dialog if available
if command -v whiptail >/dev/null 2>&1; then
if whiptail --title "$(translate "Proxmox Update")" \
--yesno "$(translate "Found $upgradable packages to upgrade.\n\nProceed with system update?")" 10 60; then
msg_info "$(translate "Performing system upgrade. This process may take several minutes...")"
else
msg_info2 "$(translate "Update cancelled by user")"
return 0
fi
fi
else
msg_success "$(translate "System is already up to date")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 0
fi
# Perform update
# msg_info "$(translate "Performing system upgrade...")"
# echo "$(translate "This process may take several minutes...")"
# Update with logging
if DEBIAN_FRONTEND=noninteractive apt-get -y \
-o Dpkg::Options::='--force-confdef' \
-o Dpkg::Options::='--force-confold' \
dist-upgrade >> "$log_file" 2>&1; then
msg_ok "$(translate "System upgrade completed successfully")"
else
msg_error "$(translate "System upgrade failed. Check log: $log_file")"
return 1
fi
# Install essential Proxmox packages if missing
local essential_packages=("zfsutils-linux" "proxmox-backup-restore-image" "chrony")
local missing_packages=()
for package in "${essential_packages[@]}"; do
if ! dpkg -l 2>/dev/null | grep -q "^ii $package "; then
missing_packages+=("$package")
fi
done
if [ ${#missing_packages[@]} -gt 0 ]; then
msg_info "$(translate "Installing essential Proxmox packages...")"
DEBIAN_FRONTEND=noninteractive apt-get -y install "${missing_packages[@]}" >> "$log_file" 2>&1
msg_ok "$(translate "Essential Proxmox packages installed")"
fi
# Check LVM
lvm_repair_check
# ======================================================
# Final summary - BEFORE reboot logic
# ======================================================
local end_time=$(date +%s)
local duration=$((end_time - start_time))
local minutes=$((duration / 60))
local seconds=$((duration % 60))
echo ""
echo "$(translate "=== UPDATE COMPLETED ===")"
echo "$(translate "Duration"): ${minutes}m ${seconds}s"
echo "$(translate "Log file"): $log_file"
echo "$(translate "Packages upgraded"): $upgradable"
echo ""
msg_success "$(translate "Proxmox system update completed successfully")"
# ======================================================
# Reboot logic - After summary
# ======================================================
# Check if reboot is needed (kernel updates, system packages, etc.)
NECESSARY_REBOOT=0 NECESSARY_REBOOT=0
# Check for reboot-required file
if [ -f /var/run/reboot-required ]; then if [ -f /var/run/reboot-required ]; then
NECESSARY_REBOOT=1 NECESSARY_REBOOT=1
fi fi
# Check if kernel was updated
if grep -q "linux-image" "$log_file" 2>/dev/null; then if grep -q "linux-image" "$log_file" 2>/dev/null; then
NECESSARY_REBOOT=1 NECESSARY_REBOOT=1
fi fi
# For system updates, it's generally safer to reboot
if [ "$upgradable" -gt 0 ]; then
NECESSARY_REBOOT=1
fi
if [[ "$NECESSARY_REBOOT" -eq 1 ]]; then if [[ "$NECESSARY_REBOOT" -eq 1 ]]; then
if whiptail --title "$(translate "Reboot Required")" \ if command -v whiptail >/dev/null 2>&1; then
--yesno "$(translate "Some changes require a reboot to take effect. Do you want to restart now?")" 10 60; then if whiptail --title "$(translate "Reboot Required")" \
--yesno "$(translate "Some changes require a reboot to take effect. Do you want to restart now?")" 10 60; then
msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" msg_info "$(translate "Removing no longer required packages and purging old cached updates...")"
apt-get -y autoremove >/dev/null 2>&1 apt-get -y autoremove >/dev/null 2>&1
apt-get -y autoclean >/dev/null 2>&1 apt-get -y autoclean >/dev/null 2>&1
msg_ok "$(translate "Cleanup finished")" msg_ok "$(translate "Cleanup finished")"
echo -e
msg_success "$(translate "Press Enter to continue...")"
read -r
msg_warn "$(translate "Rebooting the system...")" msg_success "$(translate "Press Enter to continue...")"
reboot read -r
msg_warn "$(translate "Rebooting the system...")"
reboot
else
msg_info "$(translate "Removing no longer required packages and purging old cached updates...")"
apt-get -y autoremove >/dev/null 2>&1
apt-get -y autoclean >/dev/null 2>&1
msg_ok "$(translate "Cleanup finished")"
msg_info2 "$(translate "You can reboot later manually.")"
msg_success "$(translate "Press Enter to continue...")"
read -r
return 0
fi
else else
msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" # Fallback without whiptail
apt-get -y autoremove >/dev/null 2>&1 echo "$(translate "Reboot now? (y/N): ")"
apt-get -y autoclean >/dev/null 2>&1 read -r -t 30 response
msg_ok "$(translate "Cleanup finished")" if [[ "$response" =~ ^[Yy]$ ]]; then
echo -e msg_info "$(translate "Removing no longer required packages and purging old cached updates...")"
msg_info2 "$(translate "You can reboot later manually.")" apt-get -y autoremove >/dev/null 2>&1
echo -e apt-get -y autoclean >/dev/null 2>&1
msg_success "$(translate "Press Enter to continue...")" msg_ok "$(translate "Cleanup finished")"
read -r
return 0 msg_warn "$(translate "Rebooting the system...")"
sleep 3
reboot
else
msg_info "$(translate "Removing no longer required packages and purging old cached updates...")"
apt-get -y autoremove >/dev/null 2>&1
apt-get -y autoclean >/dev/null 2>&1
msg_ok "$(translate "Cleanup finished")"
msg_info2 "$(translate "You can reboot later manually.")"
return 0
fi
fi fi
else else
msg_info "$(translate "Removing no longer required packages and purging old cached updates...")" msg_info "$(translate "Removing no longer required packages and purging old cached updates...")"
apt-get -y autoremove >/dev/null 2>&1 apt-get -y autoremove >/dev/null 2>&1
apt-get -y autoclean >/dev/null 2>&1 apt-get -y autoclean >/dev/null 2>&1
msg_ok "$(translate "Cleanup finished")" msg_ok "$(translate "Cleanup finished")"
echo -e
msg_ok "$(translate "All changes applied. No reboot required.")" msg_success "$(translate "All changes applied. No reboot required.")"
echo -e
msg_success "$(translate "Press Enter to return to menu...")" msg_success "$(translate "Press Enter to return to menu...")"
read -r read -r
fi fi
return 0
} }
# Function to show available update information
check_updates_available() {
msg_info "$(translate "Checking for available updates...")"
apt-get update >/dev/null 2>&1
local upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable")
local security_updates=$(apt list --upgradable 2>/dev/null | grep -c "security")
apt_upgrade if [ "$upgradable" -gt 0 ]; then
check_reboot echo "$(translate "Updates available"): $upgradable"
echo "$(translate "Security updates"): $security_updates"
cleanup
if command -v whiptail >/dev/null 2>&1; then
whiptail --title "$(translate "Updates Available")" \
--msgbox "$(translate "Updates available: $upgradable\nSecurity updates: $security_updates\n\nUse the update option to proceed.")" 12 60
fi
else
msg_ok "$(translate "System is up to date")"
fi
}
# Execute function based on parameter
case "${1:-}" in
"check")
check_updates_available
;;
"")
apt_upgrade
;;
*)
echo "$(translate "Usage: $0 [check]")"
echo "$(translate " check - Check for available updates")"
echo "$(translate " (no args) - Perform full system update")"
;;
esac

View File

@ -1,12 +1,13 @@
#!/bin/bash #!/bin/bash
# ========================================================== # ==========================================================
# ProxMenux - A menu-driven script for Proxmox VE management # ProxMenux - A menu-driven script for Proxmox VE management
# ========================================================== # ==========================================================
# Author : MacRimi # Author : MacRimi
# Copyright : (c) 2024 MacRimi # Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE) # License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.1 # Version : 1.0
# Last Updated: 30/07/2025 # Last Updated: 30/06/2025
# ========================================================== # ==========================================================
# Description: # Description:
# This script provides an interactive system utilities installer with a # This script provides an interactive system utilities installer with a
@ -33,8 +34,8 @@
# It includes built-in troubleshooting for common PATH and command availability # It includes built-in troubleshooting for common PATH and command availability
# issues that may occur after package installation. # issues that may occur after package installation.
# #
# Configuration ============================================ # Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux" BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh" UTILS_FILE="$BASE_DIR/utils.sh"
@ -43,64 +44,66 @@ VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE" source "$UTILS_FILE"
fi fi
load_language load_language
initialize_cache initialize_cache
OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs )"
# ========================================================== # ==========================================================
install_system_utils() { install_system_utils() {
command_exists() { command_exists() {
command -v "$1" >/dev/null 2>&1 command -v "$1" >/dev/null 2>&1
} }
ensure_repositories() {
local sources_file="/etc/apt/sources.list"
local need_update=false
if [[ ! -f "$sources_file" ]]; then
msg_warn "$(translate "sources.list not found, creating default Debian repository...")"
cat > "$sources_file" << EOF
# Default Debian ${OS_CODENAME} repository
deb http://deb.debian.org/debian ${OS_CODENAME} main contrib non-free non-free-firmware
EOF
need_update=true
else
if ! grep -q "deb.*${OS_CODENAME}.*main" "$sources_file"; then
echo "deb http://deb.debian.org/debian ${OS_CODENAME} main contrib non-free non-free-firmware" >> "$sources_file"
need_update=true
fi
fi
if [[ "$need_update" == true ]] || ! apt list --installed >/dev/null 2>&1; then
msg_info "$(translate "Updating APT package lists...")"
apt-get update -o Acquire::AllowInsecureRepositories=true >/dev/null 2>&1
fi
return 0
}
install_single_package() { install_single_package() {
local package="$1" local package="$1"
local command_name="${2:-$package}" local command_name="${2:-$package}"
local description="$3" local description="$3"
msg_info "$(translate "Installing") $package ($description)..." msg_info "$(translate "Installing") $package ($description)..."
local install_success=false local install_success=false
if apt install -y "$package" >/dev/null 2>&1; then if command_exists apt; then
install_success=true if apt update >/dev/null 2>&1 && apt install -y "$package" >/dev/null 2>&1; then
install_success=true
fi
elif command_exists yum; then
if yum install -y "$package" >/dev/null 2>&1; then
install_success=true
fi
elif command_exists dnf; then
if dnf install -y "$package" >/dev/null 2>&1; then
install_success=true
fi
elif command_exists pacman; then
if pacman -S --noconfirm "$package" >/dev/null 2>&1; then
install_success=true
fi
elif command_exists zypper; then
if zypper install -y "$package" >/dev/null 2>&1; then
install_success=true
fi
else
cleanup
msg_error "$(translate "No compatible package manager detected")"
return 1
fi fi
cleanup cleanup
if [ "$install_success" = true ]; then if [ "$install_success" = true ]; then
hash -r 2>/dev/null hash -r 2>/dev/null
sleep 1 sleep 1
if command_exists "$command_name"; then if command_exists "$command_name"; then
msg_ok "$package $(translate "installed correctly and available")" msg_ok "$package $(translate "installed correctly and available")"
return 0 return 0
@ -115,25 +118,27 @@ EOF
fi fi
} }
show_main_utilities_menu() { show_main_utilities_menu() {
local choice local choice
choice=$(dialog --clear --backtitle "ProxMenux" \ choice=$(dialog --clear --backtitle "ProxMenux" \
--title "$(translate "Utilities Installation Menu")" \ --title "$(translate "Utilities Installation Menu")" \
--menu "$(translate "Select an option"):" 20 70 12 \ --menu "$(translate "Select an option"):" 20 70 12 \
"1" "$(translate "Custom selection")" \ "1" "$(translate "Custom selection")" \
"2" "$(translate "Install ALL utilities")" \ "2" "$(translate "Install ALL utilities")" \
"3" "$(translate "Install basic utilities") (grc, htop, tree, curl, wget)" \ "3" "$(translate "Install basic utilities") (grc, htop, tree, curl, wget)" \
"4" "$(translate "Install development tools") (git, vim, nano)" \ "4" "$(translate "Install development tools") (git, vim, nano)" \
"5" "$(translate "Install compression tools") (zip, unzip, rsync)" \ "5" "$(translate "Install compression tools") (zip, unzip, rsync)" \
"6" "$(translate "Install terminal multiplexers") (screen, tmux)" \ "6" "$(translate "Install terminal multiplexers") (screen, tmux)" \
"7" "$(translate "Install analysis tools") (jq, ncdu, iotop)" \ "7" "$(translate "Install analysis tools") (jq, ncdu, iotop)" \
"8" "$(translate "Install network tools") (nethogs, nmap, tcpdump, lsof)" \ "8" "$(translate "Install network tools") (nethogs, nmap, tcpdump, lsof)" \
"9" "$(translate "Verify installations")" \ "9" "$(translate "Verify installations")" \
"0" "$(translate "Return to main menu")" 2>&1 >/dev/tty) "0" "$(translate "Return to main menu")" 2>&1 >/dev/tty)
echo "$choice" echo "$choice"
} }
show_custom_selection() { show_custom_selection() {
local utilities=( local utilities=(
"axel" "$(translate "Download accelerator")" "OFF" "axel" "$(translate "Download accelerator")" "OFF"
@ -163,13 +168,14 @@ EOF
local selected local selected
selected=$(dialog --clear --backtitle "ProxMenux" \ selected=$(dialog --clear --backtitle "ProxMenux" \
--title "$(translate "Select utilities to install")" \ --title "$(translate "Select utilities to install")" \
--checklist "$(translate "Use SPACE to select/deselect, ENTER to confirm")" \ --checklist "$(translate "Use SPACE to select/deselect, ENTER to confirm")" \
25 80 20 "${utilities[@]}" 2>&1 >/dev/tty) 25 80 20 "${utilities[@]}" 2>&1 >/dev/tty)
echo "$selected" echo "$selected"
} }
install_utility_group() { install_utility_group() {
local group_name="$1" local group_name="$1"
shift shift
@ -179,16 +185,11 @@ EOF
show_proxmenux_logo show_proxmenux_logo
msg_title "$(translate "Installing group"): $group_name" msg_title "$(translate "Installing group"): $group_name"
if ! ensure_repositories; then
msg_error "$(translate "Failed to configure repositories. Installation aborted.")"
return 1
fi
local failed=0 local failed=0
local success=0 local success=0
local warning=0 local warning=0
declare -A package_to_command=( declare -A package_to_command=(
["mlocate"]="locate" ["mlocate"]="locate"
["msr-tools"]="rdmsr" ["msr-tools"]="rdmsr"
@ -200,7 +201,11 @@ EOF
for util_info in "${utilities[@]}"; do for util_info in "${utilities[@]}"; do
IFS=':' read -r package command description <<< "$util_info" IFS=':' read -r package command description <<< "$util_info"
local verify_command="${package_to_command[$package]:-$command}" local verify_command="${package_to_command[$package]:-$command}"
install_single_package "$package" "$verify_command" "$description" install_single_package "$package" "$verify_command" "$description"
local install_result=$? local install_result=$?
@ -209,7 +214,6 @@ EOF
1) failed=$((failed + 1)) ;; 1) failed=$((failed + 1)) ;;
2) warning=$((warning + 1)) ;; 2) warning=$((warning + 1)) ;;
esac esac
sleep 2
done done
echo echo
@ -219,17 +223,18 @@ EOF
[ $failed -gt 0 ] && msg_error "$(translate "Failed"): $failed" [ $failed -gt 0 ] && msg_error "$(translate "Failed"): $failed"
dialog --clear --backtitle "ProxMenux" \ dialog --clear --backtitle "ProxMenux" \
--title "$(translate "Installation Complete")" \ --title "$(translate "Installation Complete")" \
--msgbox "$(translate "Group"): $group_name\n$(translate "Successful"): $success\n$(translate "With warnings"): $warning\n$(translate "Failed"): $failed" 10 50 --msgbox "$(translate "Group"): $group_name\n$(translate "Successful"): $success\n$(translate "With warnings"): $warning\n$(translate "Failed"): $failed" 10 50
} }
install_selected_utilities() { install_selected_utilities() {
local selected="$1" local selected="$1"
if [ -z "$selected" ]; then if [ -z "$selected" ]; then
dialog --clear --backtitle "ProxMenux" \ dialog --clear --backtitle "ProxMenux" \
--title "$(translate "No Selection")" \ --title "$(translate "No Selection")" \
--msgbox "$(translate "No utilities were selected")" 8 40 --msgbox "$(translate "No utilities were selected")" 8 40
return return
fi fi
@ -237,18 +242,15 @@ EOF
show_proxmenux_logo show_proxmenux_logo
msg_title "$(translate "Installing selected utilities")" msg_title "$(translate "Installing selected utilities")"
if ! ensure_repositories; then
msg_error "$(translate "Failed to configure repositories. Installation aborted.")"
return 1
fi
local failed=0 local failed=0
local success=0 local success=0
local warning=0 local warning=0
local selected_array local selected_array
IFS=' ' read -ra selected_array <<< "$selected" IFS=' ' read -ra selected_array <<< "$selected"
declare -A package_to_command=( declare -A package_to_command=(
["mlocate"]="locate" ["mlocate"]="locate"
["msr-tools"]="rdmsr" ["msr-tools"]="rdmsr"
@ -259,8 +261,13 @@ EOF
) )
for util in "${selected_array[@]}"; do for util in "${selected_array[@]}"; do
util=$(echo "$util" | tr -d '"') util=$(echo "$util" | tr -d '"')
local verify_command="${package_to_command[$util]:-$util}" local verify_command="${package_to_command[$util]:-$util}"
install_single_package "$util" "$verify_command" "$util" install_single_package "$util" "$verify_command" "$util"
local install_result=$? local install_result=$?
@ -269,14 +276,14 @@ EOF
1) failed=$((failed + 1)) ;; 1) failed=$((failed + 1)) ;;
2) warning=$((warning + 1)) ;; 2) warning=$((warning + 1)) ;;
esac esac
sleep 2
done done
if [ -f ~/.bashrc ]; then if [ -f ~/.bashrc ]; then
source ~/.bashrc >/dev/null 2>&1 source ~/.bashrc >/dev/null 2>&1
fi fi
hash -r 2>/dev/null hash -r 2>/dev/null
echo echo
msg_info2 "$(translate "Installation summary"):" msg_info2 "$(translate "Installation summary"):"
msg_ok "$(translate "Successful"): $success" msg_ok "$(translate "Successful"): $success"
@ -284,10 +291,11 @@ EOF
[ $failed -gt 0 ] && msg_error "$(translate "Failed"): $failed" [ $failed -gt 0 ] && msg_error "$(translate "Failed"): $failed"
dialog --clear --backtitle "ProxMenux" \ dialog --clear --backtitle "ProxMenux" \
--title "$(translate "Installation Complete")" \ --title "$(translate "Installation Complete")" \
--msgbox "$(translate "Selected utilities installation completed")\n$(translate "Successful"): $success\n$(translate "With warnings"): $warning\n$(translate "Failed"): $failed" 12 60 --msgbox "$(translate "Selected utilities installation completed")\n$(translate "Successful"): $success\n$(translate "With warnings"): $warning\n$(translate "Failed"): $failed" 12 60
} }
verify_installations() { verify_installations() {
clear clear
show_proxmenux_logo show_proxmenux_logo
@ -340,19 +348,24 @@ EOF
local summary="$(translate "Total"): $((available + missing))\n$(translate "Available"): $available\n$(translate "Missing"): $missing" local summary="$(translate "Total"): $((available + missing))\n$(translate "Available"): $available\n$(translate "Missing"): $missing"
dialog --clear --backtitle "ProxMenux" \ dialog --clear --backtitle "ProxMenux" \
--title "$(translate "Utilities Verification")" \ --title "$(translate "Utilities Verification")" \
--msgbox "$summary$status_text" 25 80 --msgbox "$summary$status_text" 25 80
} }
# Main menu loop
while true; do while true; do
choice=$(show_main_utilities_menu) choice=$(show_main_utilities_menu)
case $choice in case $choice in
1) 1)
selected=$(show_custom_selection) selected=$(show_custom_selection)
install_selected_utilities "$selected" install_selected_utilities "$selected"
;; ;;
2) 2)
all_utils=( all_utils=(
"axel:axel:Download accelerator" "axel:axel:Download accelerator"
"dos2unix:dos2unix:Convert DOS/Unix text files" "dos2unix:dos2unix:Convert DOS/Unix text files"
@ -381,6 +394,7 @@ EOF
install_utility_group "$(translate "ALL Utilities")" "${all_utils[@]}" install_utility_group "$(translate "ALL Utilities")" "${all_utils[@]}"
;; ;;
3) 3)
basic_utils=( basic_utils=(
"grc:grc:Generic Colouriser" "grc:grc:Generic Colouriser"
"htop:htop:Process monitor" "htop:htop:Process monitor"
@ -391,6 +405,7 @@ EOF
install_utility_group "$(translate "Basic Utilities")" "${basic_utils[@]}" install_utility_group "$(translate "Basic Utilities")" "${basic_utils[@]}"
;; ;;
4) 4)
dev_utils=( dev_utils=(
"git:git:Version control" "git:git:Version control"
"vim:vim:Advanced editor" "vim:vim:Advanced editor"
@ -399,6 +414,7 @@ EOF
install_utility_group "$(translate "Development Tools")" "${dev_utils[@]}" install_utility_group "$(translate "Development Tools")" "${dev_utils[@]}"
;; ;;
5) 5)
compress_utils=( compress_utils=(
"zip:zip:ZIP compressor" "zip:zip:ZIP compressor"
"unzip:unzip:ZIP extractor" "unzip:unzip:ZIP extractor"
@ -407,6 +423,7 @@ EOF
install_utility_group "$(translate "Compression Tools")" "${compress_utils[@]}" install_utility_group "$(translate "Compression Tools")" "${compress_utils[@]}"
;; ;;
6) 6)
multiplex_utils=( multiplex_utils=(
"screen:screen:Terminal multiplexer" "screen:screen:Terminal multiplexer"
"tmux:tmux:Advanced multiplexer" "tmux:tmux:Advanced multiplexer"
@ -414,6 +431,7 @@ EOF
install_utility_group "$(translate "Terminal Multiplexers")" "${multiplex_utils[@]}" install_utility_group "$(translate "Terminal Multiplexers")" "${multiplex_utils[@]}"
;; ;;
7) 7)
analysis_utils=( analysis_utils=(
"jq:jq:JSON processor" "jq:jq:JSON processor"
"ncdu:ncdu:Disk analyzer" "ncdu:ncdu:Disk analyzer"
@ -422,6 +440,7 @@ EOF
install_utility_group "$(translate "Analysis Tools")" "${analysis_utils[@]}" install_utility_group "$(translate "Analysis Tools")" "${analysis_utils[@]}"
;; ;;
8) 8)
network_utils=( network_utils=(
"nethogs:nethogs:Network monitor" "nethogs:nethogs:Network monitor"
"nmap:nmap:Network scanner" "nmap:nmap:Network scanner"
@ -438,8 +457,8 @@ EOF
;; ;;
*) *)
dialog --clear --backtitle "ProxMenux" \ dialog --clear --backtitle "ProxMenux" \
--title "$(translate "Invalid Option")" \ --title "$(translate "Invalid Option")" \
--msgbox "$(translate "Please select a valid option")" 8 40 --msgbox "$(translate "Please select a valid option")" 8 40
;; ;;
esac esac
done done
@ -447,4 +466,5 @@ EOF
clear clear
} }
install_system_utils install_system_utils

View File

@ -1,877 +0,0 @@
#!/bin/bash
# ==========================================================
# ProxMenuX - Upgrade PVE 8 → 9 (Simplified, per official guide)
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.0
# Last Updated: 14/08/2025
# ==========================================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
LOG="/var/log/pve8-a-pve9-$(date +%Y%m%d-%H%M%S).log"
: > "$LOG"
REPO_MODE="no-subscription"
DISABLE_AUDIT="1"
ASSUME_YES="0"
CEPH_ENFORCE="auto"
for arg in "$@"; do
case "$arg" in
--enterprise) REPO_MODE="enterprise" ;;
--no-subscription) REPO_MODE="no-subscription" ;;
--skip-audit-disable) DISABLE_AUDIT="0" ;;
--assume-yes|-y) ASSUME_YES="1" ;;
--ignore-ceph-check) CEPH_ENFORCE="skip" ;;
--warn-ceph-check) CEPH_ENFORCE="warn" ;;
*) ;;
esac
done
# ==========================================================
run_manual_guide() {
local url="$REPO_URL/scripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh"
if command -v curl >/dev/null 2>&1; then
bash <(curl -fsSL "$url")
else
bash <(wget -qO- "$url")
fi
}
ask_run_mode() {
if [[ "${ASSUME_YES:-0}" == "1" ]]; then
msg_ok "$(translate "Run mode: Unattended")"
export DEBIAN_FRONTEND=noninteractive
export APT_LISTCHANGES_FRONTEND=none
return 0
fi
while true; do
if command -v dialog >/dev/null 2>&1; then
local title text choice status
title="$(translate "Select run mode")"; [[ -z "$title" ]] && title="Select run mode"
text="$(translate "Choose how to perform the upgrade:")"; [[ -z "$text" ]] && text="Choose how to perform the upgrade:"
title=${title//$'\r'/}; title=${title//$'\n'/' '}
text=${text//$'\r'/}; text=${text//$'\n'/' '}
choice=$(
dialog --backtitle "ProxMenux" \
--title "$title" \
--menu "$text" 17 78 3 \
1 "$(translate "Automatic/Unattended")" \
2 "$(translate "Interactive (guided, prompts visible)")" \
3 "$(translate "Manual upgrade guide step by step")" \
3>&1 1>&2 2>&3
); status=$?
if [[ $status -ne 0 ]]; then
show_proxmenux_logo || true
msg_warn "$(translate "Action canceled by user")"
exit 0
fi
case "$choice" in
1)
ASSUME_YES="1"
show_proxmenux_logo || true
msg_title "$(translate "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)")"
msg_info2 "$(translate "Run mode selected: Unattended")"
export DEBIAN_FRONTEND=noninteractive
export APT_LISTCHANGES_FRONTEND=none
break
;;
3)
run_manual_guide
continue
;;
*)
show_proxmenux_logo || true
msg_title "$(translate "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)")"
msg_info2 "$(translate "Run mode selected: Interactive")"
break
;;
esac
elif command -v whiptail >/dev/null 2>&1; then
local choice
if ! choice=$(
whiptail --title "$(translate "Select run mode")" \
--menu "$(translate "Choose how to perform the upgrade:")" 17 78 3 \
"1" "$(translate "Automatic/Unattended")" \
"2" "$(translate "Interactive (guided, prompts visible)")" \
"3" "$(translate "Manual upgrade guide step by step")" \
3>&1 1>&2 2>&3
); then
show_proxmenux_logo || true
msg_warn "$(translate "Action canceled by user")"
exit 0
fi
case "$choice" in
1)
ASSUME_YES="1"
show_proxmenux_logo || true
msg_title "$(translate "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)")"
msg_info2 "$(translate "Run mode selected: Unattended")"
export DEBIAN_FRONTEND=noninteractive
export APT_LISTCHANGES_FRONTEND=none
break
;;
3)
run_manual_guide
continue
;;
*)
show_proxmenux_logo || true
msg_title "$(translate "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)")"
msg_info2 "$(translate "Run mode selected: Interactive")"
break
;;
esac
else
printf "%s" "$(translate "Select run mode: [1]unattended / [2]interactive / [3]manual guide? (default: 2): ")"
read -r ans
case "$ans" in
1)
ASSUME_YES="1"
show_proxmenux_logo || true
msg_title "$(translate "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)")"
msg_info2 "$(translate "Run mode selected: Unattended")"
export DEBIAN_FRONTEND=noninteractive
export APT_LISTCHANGES_FRONTEND=none
break
;;
3)
run_manual_guide
continue
;;
*)
show_proxmenux_logo || true
msg_title "$(translate "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)")"
msg_info2 "$(translate "Run mode selected: Interactive")"
break
;;
esac
fi
done
}
# ==========================================================
confirm() {
local prompt="$1"
if [[ "$ASSUME_YES" == "1" ]]; then
return 0
fi
if command -v whiptail >/dev/null 2>&1; then
whiptail --title "$(translate "Confirmation")" --yesno "$prompt" 14 80
else
echo -n "$(translate "$prompt") [y/N]: "
read -r ans
[[ "$ans" =~ ^[Yy]$ ]]
fi
}
ask_choice_auto_or_manual() {
if [[ "$ASSUME_YES" == "1" ]]; then
echo "auto"; return 0
fi
if command -v whiptail >/dev/null 2>&1; then
if whiptail --title "$(translate "Cluster upgrade mode")" --yesno "$(translate "Pending upgrades detected on a clustered node.\n\nTo proceed safely, update this node to the latest Proxmox VE 8.x before switching to Trixie/PVE 9.\n\nSelect Yes for AUTOMATIC upgrade (recommended), or No for MANUAL instructions.")" 16 78; then
echo "auto"
else
echo "manual"
fi
else
echo -n "$(translate "Pending upgrades detected on a clustered node. Perform AUTOMATIC upgrade now? (y = automatic, n = manual): ")"
read -r ans
[[ "$ans" =~ ^[Yy]$ ]] && echo "auto" || echo "manual"
fi
}
run_step() {
local pre="$1"; local ok="$2"; local cmd="$3"
echo -ne "${TAB}${YW}$(translate "$pre")${CL}"
if bash -lc "set -o pipefail; $cmd 2>&1 | tee -a \"$LOG\""; then
echo -e "${BFR}${TAB}${CM}${GN}$(translate "$ok")${CL}"
return 0
else
echo -e "${BFR}${TAB}${RD}[ERROR] $(translate "Failed. See log:") $LOG${CL}"
exit 1
fi
}
run_step_interactive() {
local pre="$1"; local ok="$2"; shift 2
echo -ne "${TAB}${YW}$(translate "$pre")${CL}"
if "$@"; then
echo -e "${BFR}${TAB}${CM}${GN}$(translate "$ok")${CL}"
return 0
else
echo -e "${BFR}${TAB}${RD}[ERROR] $(translate "Failed. See log:") $LOG${CL}"
exit 1
fi
}
append_step() {
local pre="$1"; local ok="$2"; local cmd="$3"
echo -ne "${TAB}${YW}$(translate "$pre")${CL}"
if bash -lc "set -o pipefail; $cmd 2>&1 | tee -a \"$LOG\""; then
echo -e "${BFR}${TAB}${CM}${GN}$(translate "$ok")${CL}"
else
echo -e "${BFR}${TAB}${CM}${GN}$(translate "$ok")${CL}"
fi
}
file_contains() {
local file="$1"
local pattern="$2"
grep -qE "$pattern" "$file" 2>/dev/null
}
is_cluster_node() {
if command -v pvecm >/dev/null 2>&1 && pvecm status >> "$LOG" 2>&1; then
return 0
fi
return 1
}
is_hyperconverged_ceph_node() {
if command -v pveceph >/dev/null 2>&1; then
if pveceph status >> "$LOG" 2>&1; then
return 0
fi
fi
if systemctl list-units --type=service --all 2>/dev/null | grep -Eq 'ceph-(mon|osd|mgr)@'; then
return 0
fi
if ls /var/lib/ceph/osd/ceph-* >/dev/null 2>&1; then
return 0
fi
if [[ -f /etc/pve/ceph.conf ]]; then
return 0
fi
return 1
}
# ==========================================================
create_pve_repo_enterprise_if_missing() {
local f="/etc/apt/sources.list.d/pve-enterprise.sources"
[[ -f "$f" ]] && return 0
cat > "$f" << 'EOF'
Types: deb
URIs: https://enterprise.proxmox.com/debian/pve
Suites: trixie
Components: pve-enterprise
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
chmod 644 "$f"
}
create_pve_repo_nosub_if_missing() {
local f="/etc/apt/sources.list.d/proxmox.sources"
[[ -f "$f" ]] && return 0
cat > "$f" << 'EOF'
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
chmod 644 "$f"
}
create_ceph_repo_enterprise_if_missing() {
local f="/etc/apt/sources.list.d/ceph.sources"
[[ -f "$f" ]] && return 0
cat > "$f" << 'EOF'
Types: deb
URIs: https://enterprise.proxmox.com/debian/ceph-squid
Suites: trixie
Components: enterprise
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
chmod 644 "$f"
}
create_ceph_repo_nosub_if_missing() {
local f="/etc/apt/sources.list.d/ceph.sources"
[[ -f "$f" ]] && return 0
cat > "$f" << 'EOF'
Types: deb
URIs: http://download.proxmox.com/debian/ceph-squid
Suites: trixie
Components: no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
chmod 644 "$f"
}
disable_enterprise_repo_if_present() {
local s="/etc/apt/sources.list.d/pve-enterprise.sources"
local l="/etc/apt/sources.list.d/pve-enterprise.list"
if [[ -f "$s" ]]; then
if grep -qi '^Enabled:' "$s"; then
sed -i 's/^Enabled:.*/Enabled: false/i' "$s"
else
echo "Enabled: false" >> "$s"
fi
fi
if [[ -f "$l" ]]; then
sed -i 's/^[[:space:]]*deb/# deb/' "$l"
fi
}
comment_legacy_pve8_lists() {
for f in /etc/apt/sources.list.d/pve-public-repo.list /etc/apt/sources.list.d/pve-install-repo.list; do
[[ -f "$f" ]] || continue
sed -i 's/^[[:space:]]*deb/# deb/' "$f" || true
done
}
comment_legacy_ceph_list() {
local f="/etc/apt/sources.list.d/ceph.list"
[[ -f "$f" ]] || return 0
sed -i 's/^[[:space:]]*deb/# deb/' "$f" || true
}
apt_update_with_repo_fallback() {
local output status
output="$(apt-get update >/dev/null 2>&1 | tee -a "$LOG")"; status=${PIPESTATUS[0]}
if [[ $status -eq 0 ]]; then
msg_ok2 "$(translate "APT indexes updated for Trixie")"
return 0
fi
if [[ "$REPO_MODE" == "enterprise" ]] && { echo "$output" | grep -qE '401[[:space:]]+Unauthorized' || echo "$output" | grep -qi 'enterprise\.proxmox\.com'; }; then
if [[ "$ASSUME_YES" == "1" ]] || confirm "$(translate "Enterprise repository returned 401 Unauthorized (no valid subscription). Switch to the no-subscription repository and retry?")"; then
disable_enterprise_repo_if_present
create_pve_repo_nosub_if_missing
if [[ -f /etc/apt/sources.list.d/ceph.sources ]]; then
create_ceph_repo_nosub_if_missing
fi
REPO_MODE="no-subscription"
msg_ok2 "$(translate "Repositories switched to no-subscription")"
if apt-get update >> "$LOG" >/dev/null 2>&1; then
msg_ok2 "$(translate "APT indexes updated for Trixie (no-subscription)")"
return 0
else
echo -e "${BFR}${TAB}${RD}[ERROR] $(translate "APT index update failed after switching to no-subscription. See log:") $LOG${CL}"
exit 1
fi
else
echo -e "${BFR}${TAB}${RD}[ERROR] $(translate "Enterprise repository unauthorized and fallback declined by user") $LOG${CL}"
exit 1
fi
fi
echo -e "${BFR}${TAB}${RD}[ERROR] $(translate "APT index update failed. See log:") $LOG${CL}"
exit 1
}
# ==========================================================
proxmox_repo_candidate_ok() {
local cand
cand="$(apt-cache policy proxmox-ve 2>/dev/null | awk -F': ' '/Candidate:/{print $2}')"
[[ -n "$cand" && "$cand" != "(none)" && "$cand" =~ ^9 ]]
}
simulate_would_remove_proxmox_ve() {
apt-get -s dist-upgrade >/dev/null 2>&1 | grep -Eq 'Remv[[:space:]]+proxmox-ve|The following packages will be REMOVED:.*proxmox-ve'
}
guard_against_proxmox_ve_removal() {
msg_info "$(translate "Validating Proxmox 9 repositories (checking 'proxmox-ve' candidate)...")"
if proxmox_repo_candidate_ok; then
msg_ok "$(translate "Proxmox repository OK (candidate is 9.x)")"
else
if [[ "$REPO_MODE" == "enterprise" ]]; then
msg_warn "$(translate "Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...")"
disable_enterprise_repo_if_present
create_pve_repo_nosub_if_missing
REPO_MODE="no-subscription"
apt_update_with_repo_fallback
if proxmox_repo_candidate_ok; then
msg_ok "$(translate "Proxmox repository fixed (no-subscription, candidate is 9.x)")"
else
msg_error "$(translate "Could not find a valid 'proxmox-ve' 9.x candidate after switching to no-subscription. Please verify your repository configuration and network, then retry.")"
exit 1
fi
else
msg_error "$(translate "Invalid 'proxmox-ve' candidate (not 9.x or none). Please verify your repository configuration and network, then retry.")"
exit 1
fi
fi
msg_info "$(translate "Running pre-upgrade simulation to verify 'proxmox-ve' will remain installed...")"
if simulate_would_remove_proxmox_ve; then
msg_error "$(translate "Pre-upgrade check FAILED: the simulation shows that 'proxmox-ve' would be REMOVED.\n This indicates a repository or dependency issue and upgrading now could break your Proxmox installation.")"
echo "---- $(translate "Recommended diagnostic commands") ----"
echo "apt-cache policy proxmox-ve"
echo "apt policy | sed -n '1,120p'"
echo "cat /etc/apt/sources.list"
echo "ls -l /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources"
exit 1
else
msg_ok "$(translate "Pre-upgrade simulation passed: 'proxmox-ve' will be kept or upgraded safely.")"
fi
}
# ==========================================================
check_not_web_terminal() {
[[ "${PVE_ALLOW_WEBTERM:-0}" = "1" ]] && return 0
[[ -n "${SSH_CONNECTION:-}" ]] && return 0
local pid ppid comm args i
pid=$$
show_web_terminal_block_msg() {
local title msg_line1 msg_line2 msg
title="$(translate "Unsupported Terminal")"
msg_line1="$(translate "This script cannot be executed from the Proxmox web terminal.")"
msg_line2="$(translate "Please use an SSH session (Linux, macOS, Windows/PuTTY) or a physical console to perform the upgrade.")"
msg="${msg_line1}"$'\n\n'"${msg_line2}"
whiptail --title "$title" --msgbox "$msg" 12 72
}
for i in {1..12}; do
ppid=$(ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' ')
[[ -z "$ppid" || "$ppid" -le 1 ]] && break
comm=$(ps -o comm= -p "$ppid" 2>/dev/null || true)
args=$(ps -o args= -p "$ppid" 2>/dev/null || true)
if echo "$comm $args" | grep -Eqi 'termproxy|vncshell'; then
show_web_terminal_block_msg
msg_warn "$(translate "Upgrade canceled by user")"
exit 1
fi
pid="$ppid"
done
}
ask_run_mode
check_not_web_terminal
export NCURSES_NO_UTF8_ACS=1
show_upgrade_confirm() {
local title
title="$(translate "Upgrade to Proxmox VE 9")"
local intro req l1 l2 l3 l4 cont
intro="$(translate "This will upgrade this node to Proxmox VE 9 on Debian Trixie.")"
req="$(translate "REQUIREMENTS:")"
l1="$(translate "Valid backups for all VMs/CTs")"
l2="$(translate "Run from console, or SSH inside tmux/screen")"
l3="$(translate "Migrate away any guests that must keep running")"
l4=""
cont="$(translate "Continue?")"
local bullet="•"
if ! locale charmap 2>/dev/null | grep -qi 'utf-8'; then
bullet="-"
fi
local msg
msg="${intro}"$'\n\n'"${req}"$'\n'
msg+="${bullet} ${l1}"$'\n'
msg+="${bullet} ${l2}"$'\n'
msg+="${bullet} ${l3}"
[[ -n "$l4" ]] && msg+=$'\n'"${bullet} ${l4}"
msg+=$'\n\n'"${cont}"
local cols width height
cols=$(tput cols 2>/dev/null || echo 80)
width=$(( cols < 78 ? (cols>40?cols-2:40) : 78 ))
height=18
if whiptail --title "$title" --yesno "$msg" "$height" "$width"; then
return 0
else
msg_warn "$(translate "Upgrade canceled by user")"
exit 0
fi
}
show_upgrade_confirm
# ---------------------------
# Step 1
# ---------------------------
apt_upgrade() {
local pve_version
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
if [[ -z "$pve_version" ]]; then
msg_error "Unable to detect Proxmox version."
return 1
fi
if [[ "$pve_version" -ge 9 ]]; then
msg_info2 "$(translate "Proxmox 9 system update allready")"
msg_success "$(translate "Press Enter to return to menu...")"
read -r
exit 1
else
bash <(curl -fsSL "$REPO_URL/scripts/global/update-pve8.sh")
hash -r
fi
}
apt_upgrade
run_pve8to9_check() {
local tmp
tmp="$(mktemp)"
echo -e
set -o pipefail
pve8to9 --full 2>&1 | tee -a "$LOG" | tee "$tmp"
local rc=${PIPESTATUS[0]}
local fails warns
fails=$(grep -c 'FAIL:' "$tmp" || true)
warns=$(grep -c 'WARN:' "$tmp" || true)
if (( fails > 0 )); then
echo -e
echo -e "${BFR}${RD}[ERROR] $(translate "Pre-check found") $fails $(translate "blocking issue(s).")\n$(translate "Please resolve the problem(s) as described above, then re-run the upgrade script.")${CL}"
echo -e
if grep -q 'systemd-boot meta-package installed' "$tmp"; then
echo -e
echo -e "$(translate "Fix: remove the conflicting meta-package:") apt remove systemd-boot"
echo -e
fi
msg_success "$(translate "Press Enter to exit and repair")"
read -r
rm -f "$tmp"
exit 1
fi
echo -e
msg_ok "$(translate "Checklist pre-check finished. Warnings:") $warns"
rm -f "$tmp"
return $rc
}
run_pve8to9_check
# ---------------------------
# Step 2
# ---------------------------
run_step \
"" \
"Connectivity to Proxmox repository OK" \
"ping -c1 -W2 download.proxmox.com >/dev/null"
msg_info "$(translate "Checking free space in /var/cache/apt/archives...")"
FREE_MB=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}')
if [[ "${FREE_MB:-0}" -lt 1024 ]]; then
msg_error "$(translate "Insufficient space:") ${FREE_MB}MB $(translate "(need ≥ 1024MB)")"
exit 1
else
msg_ok "$(translate "Free space OK:") ${FREE_MB}MB"
fi
if [[ "$DISABLE_AUDIT" == "1" ]]; then
append_step \
"" \
"Audit socket disabled or not required" \
"systemctl disable --now systemd-journald-audit.socket >/dev/null 2>&1 || true"
fi
# ---------------------------
# Step 3
# ---------------------------
if command -v ceph >/dev/null 2>&1; then
if [[ "$CEPH_ENFORCE" == "skip" ]]; then
msg_ok2 "$(translate "Ceph check skipped by user flag (--ignore-ceph-check)")"
else
if is_hyperconverged_ceph_node; then
msg_info "$(translate "Ceph detected as hyper-converged on this node. Checking version (require 19.x Squid)...")"
CEPH_V=$(ceph --version 2>/dev/null | head -1 || true)
if echo "$CEPH_V" | grep -Eq 'ceph.*(version 19|squid)'; then
msg_ok "$(translate "Ceph version OK:") $CEPH_V"
else
if [[ "$CEPH_ENFORCE" == "warn" ]]; then
msg_warn "$(translate "Ceph is not 19.x (Squid). Proceeding due to --warn-ceph-check. Detected:") ${CEPH_V:-N/A}"
else
msg_error "$(translate "Ceph is not 19.x (Squid). Upgrade Ceph first. Detected:") ${CEPH_V:-N/A}"
exit 1
fi
fi
else
CEPH_V=$(ceph --version 2>/dev/null | head -1 || true)
echo -e "${BFR}${TAB}${CM}${GN}${CEPH_V:-N/A}${CL}"
fi
fi
fi
# ---------------------------
# Step 4
# ---------------------------
OS_FILE="/etc/apt/sources.list"
if [[ -f "$OS_FILE" ]]; then
msg_info "$(translate "Updating Debian Bookworm → Trixie in sources.list...")"
if sed -i 's/bookworm/trixie/g' "$OS_FILE"; then
msg_ok "$(translate "sources.list updated to Trixie")"
else
msg_ok "$(translate "sources.list update skipped (no change)")"
fi
else
msg_ok "$(translate "No main sources.list present (skipped)")"
fi
PVE_ENT_LIST="/etc/apt/sources.list.d/pve-enterprise.list"
msg_info "$(translate "Updating pve-enterprise.list (if present) to Trixie...")"
if [[ -f "$PVE_ENT_LIST" ]]; then
if sed -i 's/bookworm/trixie/g' "$PVE_ENT_LIST"; then
msg_ok "$(translate "pve-enterprise.list updated to Trixie")"
else
msg_ok "$(translate "pve-enterprise.list update skipped (no change)")"
fi
else
msg_ok "$(translate "No pve-enterprise.list present (skipped)")"
fi
msg_info "$(translate "Commenting any residual Bookworm lines in *.list...")"
for f in /etc/apt/sources.list.d/*.list; do
[[ -f "$f" ]] || continue
sed -i '/bookworm/s/^/# /' "$f" || true
done
sed -i '/bookworm/s/^/# /' "$OS_FILE" 2>/dev/null || true
msg_ok "$(translate "Residual Bookworm entries commented where applicable")"
# ---------------------------
# Step 5
# ---------------------------
if [[ "$REPO_MODE" == "enterprise" ]]; then
msg_info "$(translate "Ensuring pve-enterprise.sources (PVE 9, deb822) is present...")"
create_pve_repo_enterprise_if_missing
msg_ok "$(translate "Enterprise repository present")"
else
msg_info "$(translate "Ensuring proxmox.sources (PVE 9, no-subscription, deb822) is present...")"
create_pve_repo_nosub_if_missing
msg_ok "$(translate "No-subscription repository present")"
fi
msg_info "$(translate "Commenting legacy PVE 8 repository .list files (if any)...")"
comment_legacy_pve8_lists
msg_ok "$(translate "Legacy PVE 8 .list files commented or not present")"
# ---------------------------
# Step 6
# ---------------------------
if command -v ceph >/dev/null 2>&1; then
if [[ "$REPO_MODE" == "enterprise" ]]; then
msg_info "$(translate "Ensuring ceph.sources (enterprise, Trixie) is present...")"
create_ceph_repo_enterprise_if_missing
msg_ok "$(translate "Ceph enterprise repository present")"
else
msg_info "$(translate "Ensuring ceph.sources (no-subscription, Trixie) is present...")"
create_ceph_repo_nosub_if_missing
msg_ok "$(translate "Ceph no-subscription repository present")"
fi
msg_info "$(translate "Commenting legacy ceph.list (if present)...")"
comment_legacy_ceph_list
msg_ok "$(translate "Legacy ceph.list commented or not present")"
fi
# ---------------------------
# Step 7
# ---------------------------
apt_update_with_repo_fallback
run_step \
"" \
"Repository verification completed (see log)" \
"apt policy >/dev/null 2>&1"
# ---------------------------
# Step 8
# ---------------------------
guard_against_proxmox_ve_removal
disable_translation_post_upgrade() {
translate() { echo "$1"; }
}
disable_translation_post_upgrade
# ==========================================================
# ---------------------------
# Step 9
# ---------------------------
if [[ "$ASSUME_YES" == "1" ]]; then
run_step \
"" \
"System upgraded to Trixie/PVE 9" \
"DEBIAN_FRONTEND=noninteractive APT_LISTCHANGES_FRONTEND=none apt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade"
else
run_step_interactive \
"" \
"System upgraded to Trixie/PVE 9" \
bash -lc "apt-get dist-upgrade"
fi
# ==========================================================
# ---------------------------
# Step 10
# ---------------------------
if [[ -d /sys/firmware/efi ]]; then
if [[ "$ASSUME_YES" == "1" ]]; then
append_step \
"Installing grub-efi-amd64" \
"grub-efi-amd64 installed or already present" \
"apt-get install -y grub-efi-amd64 >/dev/null 2>&1"
else
run_step \
"Installing grub-efi-amd64" \
"grub-efi-amd64 installed or already present" \
"apt-get install grub-efi-amd64 >/dev/null 2>&1"
fi
fi
if command -v pve8to9 >/dev/null 2>&1; then
sleep 2
append_step \
"" \
"Checklist post-upgrade finished" \
"pve8to9 --full"
fi
echo
echo
echo
echo "════════════════════════════════════════════════════════════════"
echo " UPGRADE TO PVE 9 COMPLETED (reboot required) "
echo "════════════════════════════════════════════════════════════════"
echo
echo
echo
msg_success "$(translate "Press Enter to continue...")"
read -r
if confirm "It is RECOMMENDED to reboot now to load the new kernel and services.\n\nReboot now?"; then
echo -e
msg_warn "$(translate "Rebooting the system...")"
echo -e
reboot
else
msg_info2 "$(translate "You can reboot later manually.")"
echo -e
msg_success "$(translate "Press Enter to exit")"
read -r
fi
exit 0

View File

@ -52,7 +52,8 @@ NEON_PURPLE_BLUE="\033[38;5;99m"
WHITE="\033[38;5;15m" WHITE="\033[38;5;15m"
RESET="\033[0m" RESET="\033[0m"
DARK_GRAY="\033[38;5;244m" DARK_GRAY="\033[38;5;244m"
ORANGE="\033[38;5;208m" DARK_GRAY="\033[38;5;244m"
DARK_GRAY="\033[38;5;244m"
YW="\033[33m" YW="\033[33m"
YWB="\033[1;33m" YWB="\033[1;33m"
GN="\033[1;92m" GN="\033[1;92m"
@ -136,12 +137,6 @@ msg_info2() {
echo -e "${TAB}${BOLD}${YW}${HOLD}${msg}${CL}" echo -e "${TAB}${BOLD}${YW}${HOLD}${msg}${CL}"
} }
# Display info message with spinner
msg_info3() {
local msg="$1"
echo -ne "${TAB}${YW}${HOLD}${msg}${CL}"
}
# Display success message # Display success message
msg_success() { msg_success() {
if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID > /dev/null; then if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID > /dev/null; then
@ -170,7 +165,7 @@ msg_warn() {
fi fi
printf "\e[?25h" printf "\e[?25h"
local msg="$1" local msg="$1"
echo -e "${BFR}${TAB}${CL} ${YWB}${msg}${CL}" echo -e "${BFR}${TAB}${NV}${CL} ${YWB}${msg}${CL}"
} }
@ -184,12 +179,6 @@ msg_ok() {
echo -e "${BFR}${TAB}${CM}${GN}${msg}${CL}" echo -e "${BFR}${TAB}${CM}${GN}${msg}${CL}"
} }
msg_ok2() {
printf "\e[?25h"
local msg="$1"
echo -e "${BFR}${TAB}${CM}${GN}${msg}${CL}"
}
# Display error message # Display error message
msg_error() { msg_error() {
@ -260,23 +249,21 @@ translate() {
from googletrans import Translator from googletrans import Translator
import sys, json, re import sys, json, re
def translate_text(text, dest_lang, context): def translate_text(text, dest_lang):
translator = Translator() translator = Translator()
context = '$TRANSLATION_CONTEXT'
try: try:
full_text = context + ' ' + text full_text = context + ' ' + text
result = translator.translate(full_text, dest=dest_lang).text result = translator.translate(full_text, dest=dest_lang).text
# Remove context and any leading/trailing whitespace
translated = re.sub(r'^.*?(Translate:|Traducir:|Traduire:|Übersetzen:|Tradurre:|Traduzir:|翻译:|翻訳:)', '', result, flags=re.IGNORECASE | re.DOTALL).strip() translated = re.sub(r'^.*?(Translate:|Traducir:|Traduire:|Übersetzen:|Tradurre:|Traduzir:|翻译:|翻訳:)', '', result, flags=re.IGNORECASE | re.DOTALL).strip()
translated = re.sub(r'^.*?(Context:|Contexto:|Contexte:|Kontext:|Contesto:|上下文:|コンテキスト:).*?:', '', translated, flags=re.IGNORECASE | re.DOTALL).strip() translated = re.sub(r'^.*?(Context:|Contexto:|Contexte:|Kontext:|Contesto:|上下文:|コンテキスト:).*?:', '', translated, flags=re.IGNORECASE | re.DOTALL).strip()
print(json.dumps({'success': True, 'text': translated})) return json.dumps({'success': True, 'text': translated})
except Exception as e: except Exception as e:
print(json.dumps({'success': False, 'error': str(e)})) return json.dumps({'success': False, 'error': str(e)})
translate_text( print(translate_text('$text', '$dest_lang'))
json.loads(sys.argv[1]), ")
sys.argv[2],
json.loads(sys.argv[3])
)
" "$(jq -Rn --arg t "$text" '$t')" "$dest_lang" "$(jq -Rn --arg ctx "$TRANSLATION_CONTEXT" '$ctx')")
deactivate deactivate
local translation_result=$(echo "$translated" | jq -r '.') local translation_result=$(echo "$translated" | jq -r '.')

View File

@ -114,7 +114,6 @@ function select_linux_iso_official() {
"Debian 12 Netinst|CLI|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.10.0-amd64-netinst.iso" "Debian 12 Netinst|CLI|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.10.0-amd64-netinst.iso"
"Debian 11 Netinst|CLI|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-cd/debian-11.11.0-amd64-netinst.iso" "Debian 11 Netinst|CLI|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-cd/debian-11.11.0-amd64-netinst.iso"
"Fedora Workstation 42|Desktop|ProxMenux|https://download.fedoraproject.org/pub/fedora/linux/releases/42/Workstation/x86_64/iso/Fedora-Workstation-Live-42-1.1.x86_64.iso" "Fedora Workstation 42|Desktop|ProxMenux|https://download.fedoraproject.org/pub/fedora/linux/releases/42/Workstation/x86_64/iso/Fedora-Workstation-Live-42-1.1.x86_64.iso"
"Arch Linux|CLI|ProxMenux|https://geo.mirror.pkgbuild.com/iso/2025.07.01/archlinux-2025.07.01-x86_64.iso"
"Rocky Linux 9.5|Desktop|ProxMenux|https://download.rockylinux.org/pub/rocky/9/isos/x86_64/Rocky-9.5-x86_64-dvd.iso" "Rocky Linux 9.5|Desktop|ProxMenux|https://download.rockylinux.org/pub/rocky/9/isos/x86_64/Rocky-9.5-x86_64-dvd.iso"
"Linux Mint 22.1|Desktop|ProxMenux|https://mirrors.edge.kernel.org/linuxmint/stable/22.1/linuxmint-22.1-cinnamon-64bit.iso" "Linux Mint 22.1|Desktop|ProxMenux|https://mirrors.edge.kernel.org/linuxmint/stable/22.1/linuxmint-22.1-cinnamon-64bit.iso"
"openSUSE Leap 15.6|Desktop|ProxMenux|https://download.opensuse.org/distribution/leap/15.6/iso/openSUSE-Leap-15.6-DVD-x86_64-Media.iso" "openSUSE Leap 15.6|Desktop|ProxMenux|https://download.opensuse.org/distribution/leap/15.6/iso/openSUSE-Leap-15.6-DVD-x86_64-Media.iso"

View File

@ -36,6 +36,9 @@ fi
load_language load_language
initialize_cache initialize_cache
clear
show_proxmenux_logo
# ========================================================== # ==========================================================
@ -75,15 +78,12 @@ function run_uupdump_creator() {
done done
if [[ ${#MISSING[@]} -gt 0 ]]; then if [[ ${#MISSING[@]} -gt 0 ]]; then
show_proxmenux_logo
msg_info "$(translate "Installing dependencies: ${MISSING[*]}")" msg_info "$(translate "Installing dependencies: ${MISSING[*]}")"
apt-get update -qq >/dev/null 2>&1 apt-get update -qq >/dev/null 2>&1
if ! apt-get install -y "${MISSING[@]}" >/dev/null 2>&1; then if ! apt-get install -y "${MISSING[@]}" >/dev/null 2>&1; then
msg_error "$(translate "Failed to install: ${MISSING[*]}")" msg_error "$(translate "Failed to install: ${MISSING[*]}")"
sleep 2
exit 1 exit 1
fi fi
msg_ok "$(translate "All dependencies installed and verified.")"
fi fi
for i in "${!CMDS[@]}"; do for i in "${!CMDS[@]}"; do
@ -92,20 +92,17 @@ function run_uupdump_creator() {
fi fi
done done
if [[ ${#FAILED[@]} -gt 0 ]]; then if [[ ${#FAILED[@]} -eq 0 ]]; then
show_proxmenux_logo msg_ok "$(translate "All dependencies installed and verified.")"
msg_error "$(translate "Missing commands after installation:") ${FAILED[*]}" else
sleep 2 msg_error "$(translate "Missing commands after installation: ${FAILED[*]}")"
exit 1 exit 1
fi fi
ISO_DIR=$(detect_iso_dir) ISO_DIR=$(detect_iso_dir)
if [[ -z "$ISO_DIR" ]]; then if [[ -z "$ISO_DIR" ]]; then
show_proxmenux_logo
msg_error "$(translate "Could not determine a valid ISO storage directory.")" msg_error "$(translate "Could not determine a valid ISO storage directory.")"
sleep 2
exit 1 exit 1
fi fi
@ -113,63 +110,40 @@ fi
mkdir -p "$ISO_DIR" mkdir -p "$ISO_DIR"
DEFAULT_TMP="/root/uup-temp"
DEFAULT_BASE="/root/uup-temp" USER_INPUT=$(dialog --inputbox "Enter temporary folder path (default: $DEFAULT_TMP):" 10 60 "$DEFAULT_TMP" 3>&1 1>&2 2>&3)
BASE_INPUT=$(dialog --clear --inputbox "$(translate "Enter base folder for temporary files and converter (default:") $DEFAULT_BASE):" 10 60 "$DEFAULT_BASE" 3>&1 1>&2 2>&3) if [[ $? -ne 0 || -z "$USER_INPUT" ]]; then
USER_INPUT="$DEFAULT_TMP"
if [[ $? -ne 0 || -z "$BASE_INPUT" ]]; then
BASE_INPUT="$DEFAULT_BASE"
fi fi
#
BASE_CLEAN="$(echo "$BASE_INPUT" | sed 's:[[:space:]]*$::' | sed 's:/*$::')" if [[ "$USER_INPUT" == "$DEFAULT_TMP" ]]; then
TMP_DIR="$USER_INPUT"
CLEAN_ALL=true
if [[ ! -d "$BASE_CLEAN" ]]; then else
if ! mkdir -p "$BASE_CLEAN"; then TIMESTAMP=$(date +%Y%m%d_%H%M%S)
show_proxmenux_logo RANDOM_ID=$(head /dev/urandom | tr -dc a-z0-9 | head -c 4)
msg_error "$(translate "The selected base folder does not exist and could not be created:") $BASE_CLEAN" TMP_DIR="${USER_INPUT%/}/uup-session-${TIMESTAMP}-${RANDOM_ID}"
sleep 2 CLEAN_ALL=false
exit 1
fi
fi fi
if [[ ! -w "$BASE_CLEAN" ]]; then
show_proxmenux_logo mkdir -p "$TMP_DIR" || {
msg_error "$(translate "No write permissions on:") $BASE_CLEAN" msg_error "$(translate "Failed to create temporary directory:") $TMP_DIR"
sleep 2
exit 1 exit 1
fi }
TMP_DIR="$BASE_CLEAN/uup-temp"
OUT_DIR="$ISO_DIR"
CONVERTER="$BASE_CLEAN/uup-converter"
if ! mkdir -p "$TMP_DIR"; then
show_proxmenux_logo
msg_error "$(translate "Could not create temporary directory:") $TMP_DIR"
sleep 2
exit 1
fi
if ! mkdir -p "$CONVERTER"; then
show_proxmenux_logo
msg_error "$(translate "Could not create converter directory:") $CONVERTER"
sleep 2
exit 1
fi
cd "$TMP_DIR" || { msg_error "$(translate "Failed to access:") $TMP_DIR"; exit 1; }
OUT_DIR=$(detect_iso_dir)
[[ -z "$OUT_DIR" ]] && msg_error "$(translate "Could not determine a valid ISO directory.")" && exit 1
mkdir -p "$OUT_DIR"
UUP_URL=$(whiptail --inputbox "$(translate "Paste the UUP Dump URL here")" 10 90 3>&1 1>&2 2>&3) UUP_URL=$(whiptail --inputbox "$(translate "Paste the UUP Dump URL here")" 10 90 3>&1 1>&2 2>&3)
if [[ $? -ne 0 || -z "$UUP_URL" ]]; then if [[ $? -ne 0 || -z "$UUP_URL" ]]; then
msg_warn "$(translate "Cancelled by user or empty URL.")"
return 1 return 1
fi fi
if [[ ! "$UUP_URL" =~ id=.+\&pack=.+\&edition=.+ ]]; then if [[ ! "$UUP_URL" =~ id=.+\&pack=.+\&edition=.+ ]]; then
show_proxmenux_logo
msg_error "$(translate "The URL does not contain the required parameters (id, pack, edition).")" msg_error "$(translate "The URL does not contain the required parameters (id, pack, edition).")"
sleep 2 sleep 2
return 1 return 1
@ -181,8 +155,6 @@ LANG=$(echo "$UUP_URL" | grep -oP 'pack=\K[^&]+')
EDITION=$(echo "$UUP_URL" | grep -oP 'edition=\K[^&]+') EDITION=$(echo "$UUP_URL" | grep -oP 'edition=\K[^&]+')
ARCH="amd64" ARCH="amd64"
show_proxmenux_logo
echo -e
echo -e "\n${BGN}=============== UUP Dump Creator ===============${CL}" echo -e "\n${BGN}=============== UUP Dump Creator ===============${CL}"
echo -e " ${BGN}🆔 ID:${CL} ${DGN}$BUILD_ID${CL}" echo -e " ${BGN}🆔 ID:${CL} ${DGN}$BUILD_ID${CL}"
echo -e " ${BGN}🌐 Language:${CL} ${DGN}$LANG${CL}" echo -e " ${BGN}🌐 Language:${CL} ${DGN}$LANG${CL}"

View File

@ -1 +1 @@
1.1.4 1.1.2