mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-08-13 08:22:22 +00:00
Compare commits
187 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
161c840136 | ||
|
4dd2abc202 | ||
|
cc0e9f61a7 | ||
|
21a658f1f4 | ||
|
b99f391c2a | ||
|
9abe25b91a | ||
|
2531fc6dac | ||
|
e5551cb179 | ||
|
4728b7a8b7 | ||
|
2863921e15 | ||
|
b93668edfe | ||
|
8ae91b8c31 | ||
|
894a23d701 | ||
|
3598906cbd | ||
|
75c12e2d4b | ||
|
d6e0519a3d | ||
|
41efc71626 | ||
|
6e3d97f472 | ||
|
9d58d02522 | ||
|
fe86396b21 | ||
|
97994f7632 | ||
|
33d63457b3 | ||
|
ed36d9e953 | ||
|
9a478d74d2 | ||
|
72d72544a4 | ||
|
4bbbe81182 | ||
|
a0af0c2492 | ||
|
ce7d3e4702 | ||
|
1bb4ca8541 | ||
|
ea65445772 | ||
|
972db8fcea | ||
|
a3c12631f0 | ||
|
3cadfd08d8 | ||
|
104f3de013 | ||
|
713b41bd52 | ||
|
253093fa2f | ||
|
f36af5af64 | ||
|
97b6c0e44d | ||
|
c4f6dabd4d | ||
|
d1c8aeb25d | ||
|
6e1cb2e0fe | ||
|
da9762f60e | ||
|
27affdec14 | ||
|
433a19e46a | ||
|
da9db9d3d1 | ||
|
ed4b0eba2f | ||
|
615aecf80f | ||
|
4622d1a610 | ||
|
f1b80d8f57 | ||
|
e76e303383 | ||
|
97133c3fcb | ||
|
450610b6e6 | ||
|
4dc3fd92cc | ||
|
f4b5e7c044 | ||
|
6c0b2a468d | ||
|
e33f724f1b | ||
|
b0d5562917 | ||
|
eecf7a2194 | ||
|
54fd8a0332 | ||
|
b6ca91980b | ||
|
6af7e2d749 | ||
|
86d334c204 | ||
|
585a4fa449 | ||
|
7438073e7e | ||
|
6e808ae35a | ||
|
99ec64e852 | ||
|
eeac63c0a5 | ||
|
5d5a3c3301 | ||
|
31e9730236 | ||
|
69b32a02ff | ||
|
a222df8176 | ||
|
7f4c99be60 | ||
|
ccff657a62 | ||
|
fb258499e1 | ||
|
79c6d6c742 | ||
|
80d9d5480c | ||
|
958a553922 | ||
|
a44bbc3513 | ||
|
d7f2f4a3e7 | ||
|
073566a23e | ||
|
590aecfcf1 | ||
|
77ab52310e | ||
|
2c2ccddbe4 | ||
|
87062db9d5 | ||
|
b74701dbc5 | ||
|
a88db8830b | ||
|
36cd83c796 | ||
|
a039c93c05 | ||
|
57b4ade3be | ||
|
87ce6cfa98 | ||
|
6a99c8c81d | ||
|
46e8188d5a | ||
|
3c990df1fe | ||
|
8969bc5aa6 | ||
|
45e8ca8d42 | ||
|
39930153c9 | ||
|
5890e46db3 | ||
|
c5c06a08ba | ||
|
2a0e677a89 | ||
|
8f7a968dc9 | ||
|
20cfc50448 | ||
|
4bf019ec7e | ||
|
57fe45484c | ||
|
7744f4ed76 | ||
|
a43e81e229 | ||
|
f6ad7e250b | ||
|
0f2b0482ec | ||
|
21d850d39e | ||
|
d3f2e42301 | ||
|
df68154f10 | ||
|
f8ebf03afd | ||
|
23f8b97319 | ||
|
d712054353 | ||
|
58da896b14 | ||
|
8db57bda6e | ||
|
53f29ec710 | ||
|
43a8fc0e86 | ||
|
7f2adb068e | ||
|
218ae9f9bf | ||
|
350c03874d | ||
|
575c0e5bf9 | ||
|
3a890ba2c7 | ||
|
2a345f4869 | ||
|
658ebbd84d | ||
|
0c54ade367 | ||
|
f16ba64026 | ||
|
d72127aaeb | ||
|
40e0b1291c | ||
|
0fae7f0166 | ||
|
6f916a4c32 | ||
|
41979d5389 | ||
|
0d51205bd6 | ||
|
416dd52a30 | ||
|
850e45b9a5 | ||
|
040d2ca7f6 | ||
|
e173072622 | ||
|
991dd80382 | ||
|
5fc5d02134 | ||
|
0f51256add | ||
|
a78860dbc4 | ||
|
f655a3c52d | ||
|
b69aebd5be | ||
|
769a7c391f | ||
|
9a4d55aa36 | ||
|
e776acfbab | ||
|
b4f58286b4 | ||
|
59779cc931 | ||
|
567bcecc80 | ||
|
0ca87dc29b | ||
|
6e0824e357 | ||
|
9a8a620658 | ||
|
eb1db3120d | ||
|
98a9225c32 | ||
|
1b8fb766a8 | ||
|
c28ef3ec3b | ||
|
fab3f0630c | ||
|
d2499ad157 | ||
|
724a37bbf4 | ||
|
c5f1c30b1c | ||
|
e90363df71 | ||
|
0932008619 | ||
|
a24e00ad5a | ||
|
fab938055f | ||
|
b2026b0dac | ||
|
c1c742084e | ||
|
7140f590cf | ||
|
dcc26ad666 | ||
|
70f1ecad49 | ||
|
03c7383b54 | ||
|
d3734971cc | ||
|
cff8358b3e | ||
|
42d691c4ce | ||
|
33dcbe8b5a | ||
|
980c7a4390 | ||
|
1b7f881d5a | ||
|
8635e2cf67 | ||
|
e36bc8bab2 | ||
|
693da7733f | ||
|
6b6128d92d | ||
|
64586a44b4 | ||
|
2d28ecca32 | ||
|
92f3edb337 | ||
|
32f8edecdd | ||
|
14b061cd28 | ||
|
aeb90cbdd2 | ||
|
e2a0b627b2 | ||
|
de5eb0d914 |
90
CHANGELOG.md
90
CHANGELOG.md
@ -1,3 +1,93 @@
|
||||
## 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
|
||||
|
||||
### New version v1.1.3
|
||||
|
@ -59,7 +59,7 @@ Then, follow the on-screen options to manage your Proxmox server efficiently.
|
||||
|
||||
## 📌 System Requirements
|
||||
🖥 **Compatible with:**
|
||||
- Proxmox VE 8.x**
|
||||
- Proxmox VE 8.x and 9.x
|
||||
|
||||
📦 **Dependencies:**
|
||||
- `bash`, `curl`, `wget`, `jq`, `whiptail`, `python3-venv` (These dependencies are installed automatically during setup.)
|
||||
|
@ -498,6 +498,8 @@ install_translation_version() {
|
||||
show_installation_options() {
|
||||
local current_install_type
|
||||
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_text="Choose installation type:"
|
||||
@ -516,9 +518,29 @@ show_installation_options() {
|
||||
esac
|
||||
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
|
||||
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
|
||||
msg_warn "Installation cancelled."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if [ -z "$INSTALL_TYPE" ]; then
|
||||
msg_warn "Installation cancelled."
|
||||
|
224
json/cache.json
224
json/cache.json
@ -1908,6 +1908,230 @@
|
||||
"de": "Automatisiertes Post-Installationsskript",
|
||||
"it": "Script automatico post-installazione",
|
||||
"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"
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -413,6 +413,18 @@
|
||||
"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",
|
||||
"slug": "barcode-buddy",
|
||||
@ -549,24 +561,6 @@
|
||||
],
|
||||
"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",
|
||||
"slug": "casaos",
|
||||
@ -650,6 +644,18 @@
|
||||
],
|
||||
"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",
|
||||
"slug": "cloudflare-ddns",
|
||||
@ -679,6 +685,20 @@
|
||||
],
|
||||
"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",
|
||||
"slug": "cockpit",
|
||||
@ -988,7 +1008,7 @@
|
||||
{
|
||||
"name": "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_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/docmost.sh",
|
||||
"categories": [
|
||||
@ -1618,7 +1638,8 @@
|
||||
4
|
||||
],
|
||||
"notes": [
|
||||
"Configuration settings: `/etc/headscale/config.yaml`"
|
||||
"Configuration settings: `/etc/headscale/config.yaml`",
|
||||
"Access headscale-admin UI via `http://<LXC-IP>/admin/`"
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
@ -1675,23 +1696,6 @@
|
||||
"notes": [],
|
||||
"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",
|
||||
"slug": "homeassistant",
|
||||
@ -1718,9 +1722,7 @@
|
||||
"categories": [
|
||||
24
|
||||
],
|
||||
"notes": [
|
||||
".env file location: `/opt/.env`"
|
||||
],
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
@ -1768,6 +1770,20 @@
|
||||
],
|
||||
"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",
|
||||
"slug": "host-backup",
|
||||
@ -1937,6 +1953,25 @@
|
||||
"notes": [],
|
||||
"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",
|
||||
"slug": "jellyfin",
|
||||
@ -2084,9 +2119,14 @@
|
||||
],
|
||||
"notes": [
|
||||
"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",
|
||||
@ -2222,6 +2262,21 @@
|
||||
"notes": [],
|
||||
"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",
|
||||
"slug": "linkwarden",
|
||||
@ -2384,6 +2439,18 @@
|
||||
],
|
||||
"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",
|
||||
"slug": "mediamtx",
|
||||
@ -2653,7 +2720,9 @@
|
||||
"categories": [
|
||||
16
|
||||
],
|
||||
"notes": [],
|
||||
"notes": [
|
||||
"You may need to configure the `WEBHOOK_URL` in the config file when using a domain."
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
@ -2783,7 +2852,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "NIC Offloading Fix",
|
||||
"name": "Intel e1000e 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.",
|
||||
"script": "tools/pve/nic-offloading-fix.sh",
|
||||
@ -3048,7 +3117,7 @@
|
||||
"script": "ct/onlyoffice.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/onlyoffice.sh",
|
||||
"categories": [
|
||||
9
|
||||
12
|
||||
],
|
||||
"notes": [
|
||||
"Database / RabbitMQ Credentials: `cat ~/onlyoffice.creds`"
|
||||
@ -3168,23 +3237,18 @@
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "OPNsense",
|
||||
"slug": "opnsense-vm",
|
||||
"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": "vm/opnsense-vm.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/opnsense-vm.sh",
|
||||
"name": "OTS",
|
||||
"slug": "ots",
|
||||
"desc": "One-Time-Secret sharing platform with a symmetric 256bit AES encryption in the browser.",
|
||||
"script": "ct/ots.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/ots.sh",
|
||||
"categories": [
|
||||
4,
|
||||
2
|
||||
6
|
||||
],
|
||||
"notes": [
|
||||
"It will fail with default settings if there is no vmbr0 and vmbr1 on your node. Use advanced settings in this case."
|
||||
"When it is in used external please use it behind reverse proxy or create your own certificates"
|
||||
],
|
||||
"type": "vm",
|
||||
"default_credentials": {
|
||||
"username": "root",
|
||||
"password": "opnsense"
|
||||
}
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Outline",
|
||||
@ -3259,6 +3323,21 @@
|
||||
"notes": [],
|
||||
"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": [
|
||||
"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",
|
||||
"slug": "paperless-ai",
|
||||
@ -3849,7 +3928,7 @@
|
||||
9
|
||||
],
|
||||
"notes": [
|
||||
"Create Proxmox-API-Token first: `https://github.com/rcourtman/Pulse?tab=readme-ov-file#creating-a-proxmox-api-token`",
|
||||
"Create Proxmox-API-Token first: `https://github.com/rcourtman/Pulse?tab=readme-ov-file#creating-api-token`",
|
||||
"After installation, access the web interface to configure your Proxmox connection details through the built-in setup wizard"
|
||||
],
|
||||
"type": "ct"
|
||||
@ -4057,6 +4136,18 @@
|
||||
"notes": [],
|
||||
"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",
|
||||
"slug": "scaling-governor",
|
||||
@ -4071,18 +4162,6 @@
|
||||
],
|
||||
"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",
|
||||
"slug": "seelf",
|
||||
@ -4275,6 +4354,20 @@
|
||||
"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",
|
||||
"slug": "syncthing",
|
||||
@ -4349,6 +4442,21 @@
|
||||
],
|
||||
"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",
|
||||
"slug": "technitiumdns",
|
||||
@ -4470,6 +4578,21 @@
|
||||
"notes": [],
|
||||
"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",
|
||||
"slug": "turnkey",
|
||||
@ -4539,20 +4662,6 @@
|
||||
],
|
||||
"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",
|
||||
"slug": "ubuntu2504-vm",
|
||||
@ -4733,7 +4842,9 @@
|
||||
"categories": [
|
||||
8
|
||||
],
|
||||
"notes": [],
|
||||
"notes": [
|
||||
"Included option to install VictoriaLogs."
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
fi
|
||||
load_language
|
||||
initialize_cache
|
||||
show_proxmenux_logo
|
||||
|
||||
# ==========================================================
|
||||
|
||||
|
||||
|
335
scripts/global/common-functions.sh
Normal file
335
scripts/global/common-functions.sh
Normal file
@ -0,0 +1,335 @@
|
||||
#!/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
|
||||
}
|
76
scripts/global/remove-banner-pve8.sh
Normal file
76
scripts/global/remove-banner-pve8.sh
Normal file
@ -0,0 +1,76 @@
|
||||
#!/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
|
119
scripts/global/remove-banner-pve9.sh
Normal file
119
scripts/global/remove-banner-pve9.sh
Normal file
@ -0,0 +1,119 @@
|
||||
#!/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
|
337
scripts/global/update-pve.sh
Normal file
337
scripts/global/update-pve.sh
Normal file
@ -0,0 +1,337 @@
|
||||
#!/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
|
281
scripts/global/update-pve8.sh
Normal file
281
scripts/global/update-pve8.sh
Normal file
@ -0,0 +1,281 @@
|
||||
#!/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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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-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
|
||||
|
||||
|
||||
|
||||
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
|
@ -72,8 +72,8 @@ validate_container_id() {
|
||||
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_CONTENT='SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666", TAG+="uaccess"'
|
||||
|
||||
@ -87,6 +87,22 @@ 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() {
|
||||
local DEVICE="$1"
|
||||
|
135
scripts/lcx/jd2.sh
Normal file
135
scripts/lcx/jd2.sh
Normal file
@ -0,0 +1,135 @@
|
||||
#!/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"
|
99
scripts/lcx/jd2_.sh
Normal file
99
scripts/lcx/jd2_.sh
Normal file
@ -0,0 +1,99 @@
|
||||
#!/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"
|
@ -54,7 +54,7 @@ show_menu() {
|
||||
if [[ $EXIT_STATUS -ne 0 ]]; then
|
||||
# ESC pressed or Cancel
|
||||
clear
|
||||
msg_ok "$(translate "Thank you for using ProxMenu. Goodbye!")"
|
||||
msg_ok "$(translate "Thank you for using ProxMenux. Goodbye!")"
|
||||
rm -f "$TEMP_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
@ -13,13 +13,15 @@
|
||||
# Advanced network management and troubleshooting tool for Proxmox VE.
|
||||
# Features include interface detection, bridge management, connectivity testing,
|
||||
# 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 ============================================
|
||||
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"
|
||||
BACKUP_DIR="/var/backups/proxmenux"
|
||||
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
@ -39,10 +41,19 @@ backup_network_config() {
|
||||
local timestamp=$(date +"%Y-%m-%d_%H-%M-%S")
|
||||
local backup_file="$BACKUP_DIR/interfaces_backup_$timestamp"
|
||||
cp /etc/network/interfaces "$backup_file"
|
||||
msg_ok "$(translate "Network configuration backed up")"
|
||||
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
|
||||
detect_network_method() {
|
||||
@ -100,75 +111,6 @@ 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_() {
|
||||
local route_info=""
|
||||
@ -428,6 +370,7 @@ analyze_bridge_configuration() {
|
||||
--textbox "$temp_file" 25 80
|
||||
rm -f "$temp_file"
|
||||
|
||||
|
||||
# Offer guided repair if issues found
|
||||
if [ $issues_found -gt 0 ]; then
|
||||
if dialog --backtitle "ProxMenux" --title "$(translate "Guided Repair Available")" \
|
||||
@ -437,6 +380,7 @@ analyze_bridge_configuration() {
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
guided_bridge_repair() {
|
||||
local step=1
|
||||
local total_steps=5
|
||||
@ -455,7 +399,8 @@ guided_bridge_repair() {
|
||||
|
||||
show_proxmenux_logo
|
||||
local backup_file=$(backup_network_config)
|
||||
sleep 1
|
||||
msg_ok "$(translate "Network configuration backed up")"
|
||||
sleep 2
|
||||
|
||||
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
|
||||
@ -580,6 +525,12 @@ guided_bridge_repair() {
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# ==========================================================
|
||||
|
||||
|
||||
|
||||
|
||||
analyze_network_configuration() {
|
||||
|
||||
NETWORK_METHOD=$(detect_network_method)
|
||||
@ -693,7 +644,8 @@ guided_configuration_cleanup() {
|
||||
|
||||
show_proxmenux_logo
|
||||
local backup_file=$(backup_network_config)
|
||||
sleep 1
|
||||
msg_ok "$(translate "Network configuration backed up")"
|
||||
sleep 2
|
||||
|
||||
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
|
||||
@ -787,6 +739,68 @@ 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() {
|
||||
if dialog --title "$(translate "Restart Network")" \
|
||||
@ -849,7 +863,9 @@ create_network_backup_manual() {
|
||||
echo -e
|
||||
msg_info "$(translate "Creating backup of network interfaces configuration...")"
|
||||
sleep 3
|
||||
cleanup
|
||||
backup_network_config
|
||||
msg_ok "$(translate "Network configuration backed up")"
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to continue...")"
|
||||
read -r
|
||||
@ -923,44 +939,167 @@ 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
|
||||
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)
|
||||
local menu_items=()
|
||||
|
||||
case $selection in
|
||||
1) show_interface_details ;;
|
||||
2) show_bridge_status ;;
|
||||
3) show_routing_table ;;
|
||||
4) test_connectivity ;;
|
||||
5) advanced_network_diagnostics ;;
|
||||
6) analyze_bridge_configuration ;;
|
||||
7) analyze_network_configuration ;;
|
||||
8) restart_network_service ;;
|
||||
9) show_network_config ;;
|
||||
10) create_network_backup_manual ;;
|
||||
11) restore_network_backup ;;
|
||||
0|"") exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh") ;;
|
||||
esac
|
||||
|
||||
declare -A script_commands
|
||||
local counter=1
|
||||
|
||||
for script in "${PROXMENUX_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+=("-" "───────────────────── $(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
|
||||
}
|
||||
|
||||
# ==========================================================
|
||||
show_main_menu
|
||||
|
||||
show_menu
|
||||
|
@ -68,320 +68,99 @@ register_tool() {
|
||||
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
|
||||
}
|
||||
|
||||
# ==========================================================
|
||||
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.")"
|
||||
register_tool "lvm_repair" true
|
||||
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
|
||||
|
||||
msg_ok "$(translate "LVM PV headers check completed")"
|
||||
|
||||
|
||||
check_extremeshok_warning() {
|
||||
local marker_file="/etc/extremeshok"
|
||||
|
||||
if [[ -f "$marker_file" ]]; then
|
||||
dialog --backtitle "ProxMenux" --title "xshok-proxmox Post-Install Detected" \
|
||||
--yesno "\n$(translate "It appears that you have already executed the xshok-proxmox post-install script on this system.")\n\n\
|
||||
$(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
|
||||
}
|
||||
|
||||
|
||||
# ==========================================================
|
||||
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() {
|
||||
local pve_version
|
||||
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
|
||||
|
||||
|
||||
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")"
|
||||
if [[ -z "$pve_version" ]]; then
|
||||
msg_error "Unable to detect Proxmox version."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$pve_version" -ge 9 ]]; then
|
||||
|
||||
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")"
|
||||
bash <(curl -fsSL "$REPO_URL/scripts/global/update-pve.sh")
|
||||
else
|
||||
msg_error "$(translate "Failed to remove conflicting utilities")"
|
||||
|
||||
bash <(curl -fsSL "$REPO_URL/scripts/global/update-pve8.sh")
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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"
|
||||
local pve_version
|
||||
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
|
||||
|
||||
# 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.")"
|
||||
if [[ -z "$pve_version" ]]; then
|
||||
msg_error "Unable to detect Proxmox version."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$pve_version" -ge 9 ]]; then
|
||||
if ! whiptail --title "Proxmox VE 9.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-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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ==========================================================
|
||||
|
||||
|
||||
@ -713,7 +492,7 @@ disable_rpc() {
|
||||
}
|
||||
|
||||
# ==========================================================
|
||||
customize_bashrc() {
|
||||
customize_bashrc_() {
|
||||
msg_info "$(translate "Customizing bashrc for root user...")"
|
||||
local bashrc="/root/.bashrc"
|
||||
local bash_profile="/root/.bash_profile"
|
||||
@ -746,56 +525,152 @@ EOF
|
||||
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() {
|
||||
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
|
||||
local pool disks disk byid_path dev rot
|
||||
|
||||
if [[ "$SYSTEM_DISK" == nvme* || "$(cat /sys/block/$SYSTEM_DISK/queue/rotational 2>/dev/null)" == "0" ]]; then
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
msg_info "$(translate "Installing log2ram from GitHub...")"
|
||||
if [[ -f /etc/log2ram.conf ]] && command -v log2ram >/dev/null 2>&1 && systemctl list-units --all | grep -q log2ram; then
|
||||
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
|
||||
apt-get update -qq >/dev/null 2>&1
|
||||
apt-get install -y git >/dev/null 2>&1
|
||||
apt-get update -qq >/dev/null 2>&1
|
||||
apt-get install -y git >/dev/null 2>&1
|
||||
msg_ok "$(translate "Git installed successfully")"
|
||||
fi
|
||||
|
||||
git clone https://github.com/azlux/log2ram.git /tmp/log2ram >/dev/null 2>>/tmp/log2ram_install.log
|
||||
cd /tmp/log2ram || return 1
|
||||
bash install.sh >>/tmp/log2ram_install.log 2>&1
|
||||
|
||||
if [[ -f /etc/log2ram.conf ]] && systemctl list-units --all | grep -q log2ram; then
|
||||
msg_ok "$(translate "Log2RAM installed successfully")"
|
||||
else
|
||||
msg_error "$(translate "Failed to install Log2RAM. See /tmp/log2ram_install.log")"
|
||||
|
||||
if ! git clone https://github.com/azlux/log2ram.git /tmp/log2ram >/dev/null 2>>/tmp/log2ram_install.log; then
|
||||
msg_error "$(translate "Failed to clone log2ram repository. Check /tmp/log2ram_install.log")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
cd /tmp/log2ram || {
|
||||
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")"
|
||||
else
|
||||
msg_error "$(translate "Log2RAM installation verification failed. Check /tmp/log2ram_install.log")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
# Detect RAM
|
||||
RAM_SIZE_GB=$(free -g | awk '/^Mem:/{print $2}')
|
||||
[[ -z "$RAM_SIZE_GB" || "$RAM_SIZE_GB" -eq 0 ]] && RAM_SIZE_GB=4
|
||||
|
||||
|
||||
if (( RAM_SIZE_GB <= 8 )); then
|
||||
LOG2RAM_SIZE="128M"
|
||||
CRON_HOURS=1
|
||||
@ -806,13 +681,17 @@ install_log2ram_auto() {
|
||||
LOG2RAM_SIZE="512M"
|
||||
CRON_HOURS=6
|
||||
fi
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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)")"
|
||||
|
||||
|
||||
cat << 'EOF' > /usr/local/bin/log2ram-check.sh
|
||||
#!/bin/bash
|
||||
@ -820,19 +699,72 @@ CONF_FILE="/etc/log2ram.conf"
|
||||
LIMIT_KB=$(grep '^SIZE=' "$CONF_FILE" | cut -d'=' -f2 | tr -d 'M')000
|
||||
USED_KB=$(df /var/log --output=used | tail -1)
|
||||
THRESHOLD=$(( LIMIT_KB * 90 / 100 ))
|
||||
|
||||
if (( USED_KB > THRESHOLD )); then
|
||||
/usr/sbin/log2ram write
|
||||
$(command -v log2ram) write
|
||||
fi
|
||||
EOF
|
||||
|
||||
|
||||
chmod +x /usr/local/bin/log2ram-check.sh
|
||||
echo "*/5 * * * * root /usr/local/bin/log2ram-check.sh" > /etc/cron.d/log2ram-auto-sync
|
||||
msg_ok "$(translate "Auto-sync enabled when /var/log exceeds 90% of") $LOG2RAM_SIZE"
|
||||
|
||||
|
||||
|
||||
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() {
|
||||
@ -857,6 +789,7 @@ run_complete_optimization() {
|
||||
disable_rpc
|
||||
customize_bashrc
|
||||
install_log2ram_auto
|
||||
setup_persistent_network
|
||||
|
||||
|
||||
echo -e
|
||||
@ -888,7 +821,7 @@ run_complete_optimization() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
check_extremeshok_warning
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
run_complete_optimization
|
||||
fi
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -49,7 +49,7 @@ register_tool() {
|
||||
################################################################
|
||||
|
||||
uninstall_fastfetch() {
|
||||
if ! command -v fastfetch &>/dev/null; then
|
||||
if ! command -v fastfetch &>/dev/null && [[ ! -f /usr/local/bin/fastfetch ]]; then
|
||||
msg_warn "$(translate "Fastfetch is not installed.")"
|
||||
return 0
|
||||
fi
|
||||
@ -58,7 +58,8 @@ uninstall_fastfetch() {
|
||||
rm -f /usr/local/bin/fastfetch /usr/bin/fastfetch
|
||||
rm -rf "$HOME/.config/fastfetch"
|
||||
rm -rf /usr/local/share/fastfetch
|
||||
sed -i '/fastfetch/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null
|
||||
sed -i '/fastfetch/d' "$HOME/.bashrc" "$HOME/.profile" /etc/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
|
||||
dpkg -r fastfetch &>/dev/null
|
||||
|
||||
@ -68,7 +69,7 @@ uninstall_fastfetch() {
|
||||
|
||||
################################################################
|
||||
|
||||
uninstall_figurine() {
|
||||
uninstall_figurine_() {
|
||||
if ! command -v figurine &>/dev/null; then
|
||||
msg_warn "$(translate "Figurine is not installed.")"
|
||||
return 0
|
||||
@ -83,6 +84,26 @@ uninstall_figurine() {
|
||||
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() {
|
||||
@ -445,6 +466,29 @@ uninstall_log2ram() {
|
||||
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() {
|
||||
@ -553,10 +597,14 @@ show_uninstall_menu() {
|
||||
memory_settings) desc="Memory Settings Optimization";;
|
||||
kernel_panic) desc="Kernel Panic Configuration";;
|
||||
apt_ipv4) desc="APT IPv4 Force";;
|
||||
kexec) desc="kexec for quick reboots";;
|
||||
network_optimization) desc="Network Optimizations";;
|
||||
disable_rpc) desc="RPC/rpcbind Disable";;
|
||||
bashrc_custom) desc="Bashrc Customization";;
|
||||
figurine) desc="Figurine";;
|
||||
fastfetch) desc="Fastfetch";;
|
||||
log2ram) desc="Log2ram (SSD Protection)";;
|
||||
persistent_network) desc="Setting persistent network interfaces";;
|
||||
*) desc="$tool";;
|
||||
esac
|
||||
menu_options+=("$tool" "$desc" "off")
|
||||
|
@ -1,13 +1,12 @@
|
||||
#!/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 : 1.1
|
||||
# Last Updated: 28/06/2025
|
||||
# Version : 1.2
|
||||
# Last Updated: 30/07/2025
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# This script allows users to assign physical disks to existing
|
||||
@ -17,6 +16,7 @@
|
||||
# - Identifies and displays unassigned physical disks.
|
||||
# - Allows the user to select multiple disks and attach them to a CT.
|
||||
# - Configures the selected disks for the CT and verifies the assignment.
|
||||
# - Uses persistent device paths to avoid issues with device order changes.
|
||||
# ==========================================================
|
||||
|
||||
# Configuration ============================================
|
||||
@ -32,8 +32,66 @@ fi
|
||||
load_language
|
||||
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() {
|
||||
local disk=$1
|
||||
MODEL=$(lsblk -dn -o MODEL "$disk" | xargs)
|
||||
@ -41,8 +99,10 @@ get_disk_info() {
|
||||
echo "$MODEL" "$SIZE"
|
||||
}
|
||||
|
||||
CT_LIST=$(pct list | awk 'NR>1 {print $1, $3}')
|
||||
# Ensure repositories are configured
|
||||
ensure_repositories
|
||||
|
||||
CT_LIST=$(pct list | awk 'NR>1 {print $1, $3}')
|
||||
if [ -z "$CT_LIST" ]; then
|
||||
whiptail --title "$(translate "Error")" --msgbox "$(translate "No CTs available in the system.")" 8 40
|
||||
exit 1
|
||||
@ -60,12 +120,11 @@ CTID=$(echo "$CTID" | tr -d '"')
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
echo -e
|
||||
msg_info2 "$(translate "Add Disk") Passthrough $(translate "to a LXC")"
|
||||
msg_title "$(translate "Add Disk") Passthrough $(translate "to a LXC")"
|
||||
echo -e
|
||||
msg_ok "$(translate "CT selected successfully.")"
|
||||
|
||||
CT_STATUS=$(pct status "$CTID" | awk '{print $2}')
|
||||
|
||||
if [ "$CT_STATUS" != "running" ]; then
|
||||
msg_info "$(translate "Starting CT") $CTID..."
|
||||
pct start "$CTID"
|
||||
@ -79,10 +138,10 @@ if [ "$CT_STATUS" != "running" ]; then
|
||||
fi
|
||||
|
||||
CONF_FILE="/etc/pve/lxc/$CTID.conf"
|
||||
|
||||
if grep -q '^unprivileged: 1' "$CONF_FILE"; then
|
||||
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
|
||||
|
||||
msg_info "$(translate "Stopping container") $CTID..."
|
||||
pct shutdown "$CTID" &>/dev/null
|
||||
for i in {1..10}; do
|
||||
@ -91,15 +150,18 @@ if grep -q '^unprivileged: 1' "$CONF_FILE"; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$(pct status "$CTID" | awk '{print $2}')" == "running" ]; then
|
||||
msg_error "$(translate "Failed to stop the container.")"
|
||||
exit 1
|
||||
fi
|
||||
msg_ok "$(translate "Container stopped.")"
|
||||
|
||||
cp "$CONF_FILE" "$CONF_FILE.bak"
|
||||
sed -i '/^unprivileged: 1/d' "$CONF_FILE"
|
||||
echo "unprivileged: 0" >> "$CONF_FILE"
|
||||
msg_ok "$(translate "Container successfully converted to privileged.")"
|
||||
|
||||
msg_info "$(translate "Starting container") $CTID..."
|
||||
pct start "$CTID" &>/dev/null
|
||||
sleep 2
|
||||
@ -123,7 +185,6 @@ MOUNTED_DISKS=$(lsblk -ln -o NAME,MOUNTPOINT | awk '$2!="" {print "/dev/" $1}')
|
||||
|
||||
ZFS_DISKS=""
|
||||
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
|
||||
path=""
|
||||
if [[ "$entry" == wwn-* || "$entry" == ata-* ]]; then
|
||||
@ -133,6 +194,7 @@ for entry in $ZFS_RAW; do
|
||||
elif [[ "$entry" == /dev/* ]]; then
|
||||
path="$entry"
|
||||
fi
|
||||
|
||||
if [ -n "$path" ]; then
|
||||
base_disk=$(lsblk -no PKNAME "$path" 2>/dev/null)
|
||||
if [ -n "$base_disk" ]; then
|
||||
@ -140,7 +202,6 @@ for entry in $ZFS_RAW; do
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
ZFS_DISKS=$(echo "$ZFS_DISKS" | sort -u)
|
||||
|
||||
is_disk_in_use() {
|
||||
@ -154,6 +215,7 @@ is_disk_in_use() {
|
||||
return 0
|
||||
fi
|
||||
done < <(lsblk -ln -o NAME,FSTYPE "$disk" | tail -n +2)
|
||||
|
||||
if echo "$USED_DISKS" | grep -q "$disk" || echo "$ZFS_DISKS" | grep -q "$disk"; then
|
||||
return 0
|
||||
fi
|
||||
@ -166,6 +228,7 @@ RAID_ACTIVE=$(grep -Po 'md\d+\s*:\s*active\s+raid[0-9]+' /proc/mdstat | awk '{pr
|
||||
|
||||
while read -r DISK; do
|
||||
[[ "$DISK" =~ /dev/zd ]] && continue
|
||||
|
||||
INFO=($(get_disk_info "$DISK"))
|
||||
MODEL="${INFO[@]::${#INFO[@]}-1}"
|
||||
SIZE="${INFO[-1]}"
|
||||
@ -175,7 +238,7 @@ while read -r DISK; do
|
||||
IS_RAID=false
|
||||
IS_ZFS=false
|
||||
IS_LVM=false
|
||||
|
||||
|
||||
while read -r part fstype; do
|
||||
[[ "$fstype" == "zfs_member" ]] && IS_ZFS=true
|
||||
[[ "$fstype" == "linux_raid_member" ]] && IS_RAID=true
|
||||
@ -184,15 +247,16 @@ while read -r DISK; do
|
||||
IS_MOUNTED=true
|
||||
fi
|
||||
done < <(lsblk -ln -o NAME,FSTYPE "$DISK" | tail -n +2)
|
||||
|
||||
|
||||
REAL_PATH=$(readlink -f "$DISK")
|
||||
if echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then
|
||||
IS_MOUNTED=true
|
||||
fi
|
||||
|
||||
|
||||
USED_BY=""
|
||||
REAL_PATH=$(readlink -f "$DISK")
|
||||
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
|
||||
USED_BY="⚠ $(translate "In use")"
|
||||
else
|
||||
@ -205,30 +269,38 @@ while read -r DISK; do
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
if $IS_RAID && grep -q "$DISK" <<< "$(cat /proc/mdstat)"; then
|
||||
if grep -q "active raid" /proc/mdstat; then
|
||||
SHOW_DISK=false
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if $IS_ZFS; then
|
||||
SHOW_DISK=false
|
||||
fi
|
||||
|
||||
|
||||
if $IS_MOUNTED; then
|
||||
SHOW_DISK=false
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
if $SHOW_DISK; then
|
||||
[[ -n "$USED_BY" ]] && LABEL+=" [$USED_BY]"
|
||||
[[ "$IS_RAID" == true ]] && LABEL+=" ⚠ RAID"
|
||||
[[ "$IS_LVM" == true ]] && LABEL+=" ⚠ LVM"
|
||||
[[ "$IS_ZFS" == true ]] && LABEL+=" ⚠ ZFS"
|
||||
|
||||
DESCRIPTION=$(printf "%-30s %10s%s" "$MODEL" "$SIZE" "$LABEL")
|
||||
FREE_DISKS+=("$DISK" "$DESCRIPTION" "OFF")
|
||||
fi
|
||||
@ -270,10 +342,11 @@ msg_info "$(translate "Processing selected disks...")"
|
||||
for DISK in $SELECTED; do
|
||||
DISK=$(echo "$DISK" | tr -d '"')
|
||||
DISK_INFO=$(get_disk_info "$DISK")
|
||||
|
||||
ASSIGNED_TO=""
|
||||
RUNNING_CTS=""
|
||||
RUNNING_VMS=""
|
||||
|
||||
|
||||
while read -r CT_ID CT_NAME; do
|
||||
if [[ "$CT_ID" =~ ^[0-9]+$ ]] && pct config "$CT_ID" | grep -q "$DISK"; then
|
||||
ASSIGNED_TO+="CT $CT_ID $CT_NAME\n"
|
||||
@ -283,7 +356,7 @@ for DISK in $SELECTED; do
|
||||
fi
|
||||
fi
|
||||
done < <(pct list | awk 'NR>1 {print $1, $3}')
|
||||
|
||||
|
||||
while read -r VM_ID VM_NAME; do
|
||||
if [[ "$VM_ID" =~ ^[0-9]+$ ]] && qm config "$VM_ID" | grep -q "$DISK"; then
|
||||
ASSIGNED_TO+="VM $VM_ID $VM_NAME\n"
|
||||
@ -293,12 +366,12 @@ for DISK in $SELECTED; do
|
||||
fi
|
||||
fi
|
||||
done < <(qm list | awk 'NR>1 {print $1, $2}')
|
||||
|
||||
|
||||
if [ -n "$RUNNING_CTS" ] || [ -n "$RUNNING_VMS" ]; then
|
||||
ERROR_MESSAGES+="$(translate "The disk") $DISK_INFO $(translate "is in use by the following running VM(s) or CT(s):")\\n$RUNNING_CTS$RUNNING_VMS\\n\\n"
|
||||
continue
|
||||
fi
|
||||
|
||||
|
||||
if [ -n "$ASSIGNED_TO" ]; then
|
||||
cleanup
|
||||
whiptail --title "$(translate "Disk Already Assigned")" --yesno "$(translate "The disk") $DISK_INFO $(translate "is already assigned to the following VM(s) or CT(s):")\\n$ASSIGNED_TO\\n\\n$(translate "Do you want to continue anyway?")" 15 70
|
||||
@ -309,24 +382,25 @@ for DISK in $SELECTED; do
|
||||
fi
|
||||
|
||||
cleanup
|
||||
|
||||
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
|
||||
clear
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
MOUNT_POINT=$(whiptail --title "$(translate "Mount Point")" --inputbox "$(translate "Enter the mount point for the disk (e.g., /mnt/disk_passthrough):")" 10 60 "/mnt/disk_passthrough" 3>&1 1>&2 2>&3)
|
||||
|
||||
|
||||
if [ -z "$MOUNT_POINT" ]; then
|
||||
whiptail --title "$(translate "Error")" --msgbox "$(translate "No mount point was specified.")" 8 40
|
||||
continue
|
||||
fi
|
||||
|
||||
|
||||
msg_ok "$(translate "Mount point specified: $MOUNT_POINT")"
|
||||
|
||||
|
||||
PARTITION=$(lsblk -rno NAME "$DISK" | awk -v disk="$(basename "$DISK")" '$1 != disk {print $1; exit}')
|
||||
SKIP_FORMAT=false
|
||||
|
||||
|
||||
if [ -n "$PARTITION" ]; then
|
||||
PARTITION="/dev/$PARTITION"
|
||||
CURRENT_FS=$(lsblk -no FSTYPE "$PARTITION" | xargs)
|
||||
@ -350,12 +424,14 @@ for DISK in $SELECTED; do
|
||||
if [ $? -ne 0 ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo -e "$(translate "Creating partition table and partition...")"
|
||||
parted -s "$DISK" mklabel gpt
|
||||
parted -s "$DISK" mkpart primary 0% 100%
|
||||
sleep 2
|
||||
partprobe "$DISK"
|
||||
sleep 2
|
||||
|
||||
PARTITION=$(lsblk -rno NAME "$DISK" | awk -v disk="$(basename "$DISK")" '$1 != disk {print $1; exit}')
|
||||
if [ -n "$PARTITION" ]; then
|
||||
PARTITION="/dev/$PARTITION"
|
||||
@ -365,7 +441,7 @@ for DISK in $SELECTED; do
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ "$SKIP_FORMAT" != true ]; then
|
||||
CURRENT_FS=$(lsblk -no FSTYPE "$PARTITION" | xargs)
|
||||
if [[ "$CURRENT_FS" == "ext4" || "$CURRENT_FS" == "xfs" || "$CURRENT_FS" == "btrfs" ]]; then
|
||||
@ -376,21 +452,20 @@ for DISK in $SELECTED; do
|
||||
"ext4" "$(translate "Extended Filesystem 4 (recommended)")" \
|
||||
"xfs" "$(translate "XFS Filesystem")" \
|
||||
"btrfs" "$(translate "Btrfs Filesystem")" 3>&1 1>&2 2>&3)
|
||||
|
||||
|
||||
if [ -z "$FORMAT_TYPE" ]; then
|
||||
whiptail --title "$(translate "Format Cancelled")" --msgbox "$(translate "Format operation cancelled. The disk will not be added.")" 8 60
|
||||
continue
|
||||
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
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
whiptail --title "$(translate "Format Cancelled")" --msgbox "$(translate "Format operation cancelled. The disk will not be added.")" 8 60
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ "$SKIP_FORMAT" != true ]; then
|
||||
echo -e "$(translate "Formatting partition") $PARTITION $(translate "with") $FORMAT_TYPE..."
|
||||
case "$FORMAT_TYPE" in
|
||||
@ -398,7 +473,7 @@ for DISK in $SELECTED; do
|
||||
"xfs") mkfs.xfs -f "$PARTITION" ;;
|
||||
"btrfs") mkfs.btrfs -f "$PARTITION" ;;
|
||||
esac
|
||||
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
whiptail --title "$(translate "Format Failed")" --msgbox "$(translate "Failed to format partition") $PARTITION $(translate "with") $FORMAT_TYPE.\\n\\n$(translate "The disk may be in use by the system or have hardware issues.")" 12 70
|
||||
continue
|
||||
@ -408,18 +483,18 @@ for DISK in $SELECTED; do
|
||||
sleep 2
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
INDEX=0
|
||||
while pct config "$CTID" | grep -q "mp${INDEX}:"; do
|
||||
((INDEX++))
|
||||
done
|
||||
|
||||
|
||||
# Determine the filesystem type for mount options
|
||||
CURRENT_FS=$(lsblk -no FSTYPE "$PARTITION" | xargs)
|
||||
if [[ -n "$CURRENT_FS" ]]; then
|
||||
FORMAT_TYPE="$CURRENT_FS"
|
||||
fi
|
||||
|
||||
|
||||
# Install filesystem tools in container if needed
|
||||
FS_PKG=""
|
||||
FS_BIN=""
|
||||
@ -430,7 +505,7 @@ for DISK in $SELECTED; do
|
||||
FS_PKG="btrfs-progs"
|
||||
FS_BIN="mkfs.btrfs"
|
||||
fi
|
||||
|
||||
|
||||
if [[ -n "$FS_PKG" && -n "$FS_BIN" ]]; then
|
||||
if ! pct exec "$CTID" -- sh -c "command -v $FS_BIN >/dev/null 2>&1"; then
|
||||
msg_info "$(translate "Installing required tools for $FORMAT_TYPE in CT $CTID...")"
|
||||
@ -442,34 +517,40 @@ for DISK in $SELECTED; do
|
||||
msg_ok "$(translate "Required tools for $FORMAT_TYPE installed in CT $CTID.")"
|
||||
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)
|
||||
if [ "$CURRENT_FS" == "xfs" ] || [ "$FORMAT_TYPE" == "xfs" ]; then
|
||||
|
||||
RESULT=$(pct set "$CTID" -mp${INDEX} "$PARTITION,mp=$MOUNT_POINT,backup=0,ro=0" 2>&1)
|
||||
RESULT=$(pct set "$CTID" -mp${INDEX} "$PERSISTENT_PARTITION,mp=$MOUNT_POINT,backup=0,ro=0" 2>&1)
|
||||
else
|
||||
|
||||
RESULT=$(pct set "$CTID" -mp${INDEX} "$PARTITION,mp=$MOUNT_POINT,backup=0,ro=0,acl=1" 2>&1)
|
||||
RESULT=$(pct set "$CTID" -mp${INDEX} "$PERSISTENT_PARTITION,mp=$MOUNT_POINT,backup=0,ro=0,acl=1" 2>&1)
|
||||
fi
|
||||
|
||||
|
||||
pct exec "$CTID" -- chmod -R 777 "$MOUNT_POINT" 2>/dev/null || true
|
||||
|
||||
# Adjust permissions inside the CT
|
||||
pct exec "$CTID" -- chmod -R 775 "$MOUNT_POINT" 2>/dev/null || true
|
||||
|
||||
# Show confirmation with persistent identifier
|
||||
msg_ok "$(translate "Assigned using") $PERSISTENT_PARTITION"
|
||||
##############################################################################
|
||||
|
||||
|
||||
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+="\\n$(translate "Using persistent path"): $PERSISTENT_PARTITION"
|
||||
|
||||
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$(translate "Make sure not to start CTs that share this disk at the same time to avoid data corruption.")"
|
||||
fi
|
||||
|
||||
SUCCESS_MESSAGES+="$MESSAGE\\n\\n"
|
||||
((DISKS_ADDED++))
|
||||
else
|
||||
ERROR_MESSAGES+="$(translate "Could not add disk") $DISK_INFO $(translate "to CT") $CTID.\\n$(translate "Error:") $RESULT\\n\\n"
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
msg_ok "$(translate "Disk processing completed.")"
|
||||
@ -485,5 +566,4 @@ fi
|
||||
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
|
||||
exit 0
|
||||
|
@ -41,464 +41,91 @@ initialize_cache
|
||||
|
||||
# ==========================================================
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
NECESSARY_REBOOT=1
|
||||
|
||||
apt_upgrade() {
|
||||
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 pve_version
|
||||
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
|
||||
|
||||
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")"
|
||||
if [[ -z "$pve_version" ]]; then
|
||||
msg_error "Unable to detect Proxmox version."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check connectivity
|
||||
if ! ping -c 1 download.proxmox.com >/dev/null 2>&1; then
|
||||
msg_error "$(translate "Cannot reach Proxmox repositories")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# ======================================================
|
||||
# Proxmox repository configuration
|
||||
# ======================================================
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
local temp_sources=$(mktemp)
|
||||
|
||||
if [[ "$pve_version" -ge 9 ]]; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Proxmox system update")"
|
||||
bash <(curl -fsSL "$REPO_URL/scripts/global/update-pve.sh")
|
||||
|
||||
grep -E '^[[:space:]]*#|^[[:space:]]*$' "$sources_file" > "$temp_sources"
|
||||
|
||||
|
||||
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
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Proxmox system update")"
|
||||
bash <(curl -fsSL "$REPO_URL/scripts/global/update-pve8.sh")
|
||||
|
||||
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.)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
check_reboot() {
|
||||
NECESSARY_REBOOT=0
|
||||
|
||||
# Check for reboot-required file
|
||||
|
||||
if [ -f /var/run/reboot-required ]; then
|
||||
NECESSARY_REBOOT=1
|
||||
fi
|
||||
|
||||
# Check if kernel was updated
|
||||
if grep -q "linux-image" "$log_file" 2>/dev/null; then
|
||||
NECESSARY_REBOOT=1
|
||||
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 command -v whiptail >/dev/null 2>&1; 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...")"
|
||||
apt-get -y autoremove >/dev/null 2>&1
|
||||
apt-get -y autoclean >/dev/null 2>&1
|
||||
msg_ok "$(translate "Cleanup finished")"
|
||||
|
||||
msg_success "$(translate "Press Enter to continue...")"
|
||||
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
|
||||
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...")"
|
||||
apt-get -y autoremove >/dev/null 2>&1
|
||||
apt-get -y autoclean >/dev/null 2>&1
|
||||
msg_ok "$(translate "Cleanup finished")"
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to continue...")"
|
||||
read -r
|
||||
|
||||
msg_warn "$(translate "Rebooting the system...")"
|
||||
reboot
|
||||
else
|
||||
# Fallback without whiptail
|
||||
echo "$(translate "Reboot now? (y/N): ")"
|
||||
read -r -t 30 response
|
||||
if [[ "$response" =~ ^[Yy]$ ]]; then
|
||||
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_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
|
||||
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")"
|
||||
echo -e
|
||||
msg_info2 "$(translate "You can reboot later manually.")"
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to continue...")"
|
||||
read -r
|
||||
return 0
|
||||
fi
|
||||
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_success "$(translate "All changes applied. No reboot required.")"
|
||||
echo -e
|
||||
msg_ok "$(translate "All changes applied. No reboot required.")"
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
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")
|
||||
|
||||
if [ "$upgradable" -gt 0 ]; then
|
||||
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
|
||||
|
||||
apt_upgrade
|
||||
check_reboot
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# ProxMenux - 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 : 1.0
|
||||
# Last Updated: 30/06/2025
|
||||
# Version : 1.1
|
||||
# Last Updated: 30/07/2025
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# This script provides an interactive system utilities installer with a
|
||||
@ -34,8 +33,8 @@
|
||||
# It includes built-in troubleshooting for common PATH and command availability
|
||||
# issues that may occur after package installation.
|
||||
#
|
||||
# Configuration ============================================
|
||||
|
||||
# Configuration ============================================
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
@ -44,21 +43,47 @@ VENV_PATH="/opt/googletrans-env"
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs )"
|
||||
|
||||
# ==========================================================
|
||||
|
||||
|
||||
|
||||
install_system_utils() {
|
||||
|
||||
|
||||
|
||||
command_exists() {
|
||||
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() {
|
||||
local package="$1"
|
||||
@ -66,44 +91,16 @@ install_system_utils() {
|
||||
local description="$3"
|
||||
|
||||
msg_info "$(translate "Installing") $package ($description)..."
|
||||
|
||||
|
||||
local install_success=false
|
||||
|
||||
if command_exists apt; then
|
||||
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
|
||||
if apt install -y "$package" >/dev/null 2>&1; then
|
||||
install_success=true
|
||||
fi
|
||||
|
||||
cleanup
|
||||
|
||||
|
||||
if [ "$install_success" = true ]; then
|
||||
|
||||
hash -r 2>/dev/null
|
||||
sleep 1
|
||||
|
||||
if command_exists "$command_name"; then
|
||||
msg_ok "$package $(translate "installed correctly and available")"
|
||||
return 0
|
||||
@ -117,27 +114,25 @@ install_system_utils() {
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
show_main_utilities_menu() {
|
||||
local choice
|
||||
choice=$(dialog --clear --backtitle "ProxMenux" \
|
||||
--title "$(translate "Utilities Installation Menu")" \
|
||||
--menu "$(translate "Select an option"):" 20 70 12 \
|
||||
"1" "$(translate "Custom selection")" \
|
||||
"2" "$(translate "Install ALL utilities")" \
|
||||
"3" "$(translate "Install basic utilities") (grc, htop, tree, curl, wget)" \
|
||||
"4" "$(translate "Install development tools") (git, vim, nano)" \
|
||||
"5" "$(translate "Install compression tools") (zip, unzip, rsync)" \
|
||||
"6" "$(translate "Install terminal multiplexers") (screen, tmux)" \
|
||||
"7" "$(translate "Install analysis tools") (jq, ncdu, iotop)" \
|
||||
"8" "$(translate "Install network tools") (nethogs, nmap, tcpdump, lsof)" \
|
||||
"9" "$(translate "Verify installations")" \
|
||||
"0" "$(translate "Return to main menu")" 2>&1 >/dev/tty)
|
||||
--title "$(translate "Utilities Installation Menu")" \
|
||||
--menu "$(translate "Select an option"):" 20 70 12 \
|
||||
"1" "$(translate "Custom selection")" \
|
||||
"2" "$(translate "Install ALL utilities")" \
|
||||
"3" "$(translate "Install basic utilities") (grc, htop, tree, curl, wget)" \
|
||||
"4" "$(translate "Install development tools") (git, vim, nano)" \
|
||||
"5" "$(translate "Install compression tools") (zip, unzip, rsync)" \
|
||||
"6" "$(translate "Install terminal multiplexers") (screen, tmux)" \
|
||||
"7" "$(translate "Install analysis tools") (jq, ncdu, iotop)" \
|
||||
"8" "$(translate "Install network tools") (nethogs, nmap, tcpdump, lsof)" \
|
||||
"9" "$(translate "Verify installations")" \
|
||||
"0" "$(translate "Return to main menu")" 2>&1 >/dev/tty)
|
||||
|
||||
echo "$choice"
|
||||
}
|
||||
|
||||
|
||||
show_custom_selection() {
|
||||
local utilities=(
|
||||
@ -168,28 +163,32 @@ install_system_utils() {
|
||||
|
||||
local selected
|
||||
selected=$(dialog --clear --backtitle "ProxMenux" \
|
||||
--title "$(translate "Select utilities to install")" \
|
||||
--checklist "$(translate "Use SPACE to select/deselect, ENTER to confirm")" \
|
||||
25 80 20 "${utilities[@]}" 2>&1 >/dev/tty)
|
||||
--title "$(translate "Select utilities to install")" \
|
||||
--checklist "$(translate "Use SPACE to select/deselect, ENTER to confirm")" \
|
||||
25 80 20 "${utilities[@]}" 2>&1 >/dev/tty)
|
||||
|
||||
echo "$selected"
|
||||
}
|
||||
|
||||
|
||||
install_utility_group() {
|
||||
local group_name="$1"
|
||||
shift
|
||||
local utilities=("$@")
|
||||
|
||||
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
show_proxmenux_logo
|
||||
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 success=0
|
||||
local warning=0
|
||||
|
||||
|
||||
declare -A package_to_command=(
|
||||
["mlocate"]="locate"
|
||||
["msr-tools"]="rdmsr"
|
||||
@ -201,11 +200,7 @@ install_system_utils() {
|
||||
|
||||
for util_info in "${utilities[@]}"; do
|
||||
IFS=':' read -r package command description <<< "$util_info"
|
||||
|
||||
|
||||
local verify_command="${package_to_command[$package]:-$command}"
|
||||
|
||||
|
||||
install_single_package "$package" "$verify_command" "$description"
|
||||
local install_result=$?
|
||||
|
||||
@ -214,6 +209,7 @@ install_system_utils() {
|
||||
1) failed=$((failed + 1)) ;;
|
||||
2) warning=$((warning + 1)) ;;
|
||||
esac
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo
|
||||
@ -223,34 +219,36 @@ install_system_utils() {
|
||||
[ $failed -gt 0 ] && msg_error "$(translate "Failed"): $failed"
|
||||
|
||||
dialog --clear --backtitle "ProxMenux" \
|
||||
--title "$(translate "Installation Complete")" \
|
||||
--msgbox "$(translate "Group"): $group_name\n$(translate "Successful"): $success\n$(translate "With warnings"): $warning\n$(translate "Failed"): $failed" 10 50
|
||||
--title "$(translate "Installation Complete")" \
|
||||
--msgbox "$(translate "Group"): $group_name\n$(translate "Successful"): $success\n$(translate "With warnings"): $warning\n$(translate "Failed"): $failed" 10 50
|
||||
}
|
||||
|
||||
|
||||
install_selected_utilities() {
|
||||
local selected="$1"
|
||||
|
||||
if [ -z "$selected" ]; then
|
||||
dialog --clear --backtitle "ProxMenux" \
|
||||
--title "$(translate "No Selection")" \
|
||||
--msgbox "$(translate "No utilities were selected")" 8 40
|
||||
--title "$(translate "No Selection")" \
|
||||
--msgbox "$(translate "No utilities were selected")" 8 40
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
show_proxmenux_logo
|
||||
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 success=0
|
||||
local warning=0
|
||||
|
||||
|
||||
local selected_array
|
||||
IFS=' ' read -ra selected_array <<< "$selected"
|
||||
|
||||
|
||||
declare -A package_to_command=(
|
||||
["mlocate"]="locate"
|
||||
["msr-tools"]="rdmsr"
|
||||
@ -261,13 +259,8 @@ install_system_utils() {
|
||||
)
|
||||
|
||||
for util in "${selected_array[@]}"; do
|
||||
|
||||
util=$(echo "$util" | tr -d '"')
|
||||
|
||||
|
||||
local verify_command="${package_to_command[$util]:-$util}"
|
||||
|
||||
|
||||
install_single_package "$util" "$verify_command" "$util"
|
||||
local install_result=$?
|
||||
|
||||
@ -276,14 +269,14 @@ install_system_utils() {
|
||||
1) failed=$((failed + 1)) ;;
|
||||
2) warning=$((warning + 1)) ;;
|
||||
esac
|
||||
sleep 2
|
||||
done
|
||||
|
||||
|
||||
if [ -f ~/.bashrc ]; then
|
||||
source ~/.bashrc >/dev/null 2>&1
|
||||
fi
|
||||
hash -r 2>/dev/null
|
||||
|
||||
hash -r 2>/dev/null
|
||||
echo
|
||||
msg_info2 "$(translate "Installation summary"):"
|
||||
msg_ok "$(translate "Successful"): $success"
|
||||
@ -291,10 +284,9 @@ install_system_utils() {
|
||||
[ $failed -gt 0 ] && msg_error "$(translate "Failed"): $failed"
|
||||
|
||||
dialog --clear --backtitle "ProxMenux" \
|
||||
--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
|
||||
--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
|
||||
}
|
||||
|
||||
|
||||
verify_installations() {
|
||||
clear
|
||||
@ -348,24 +340,19 @@ install_system_utils() {
|
||||
local summary="$(translate "Total"): $((available + missing))\n$(translate "Available"): $available\n$(translate "Missing"): $missing"
|
||||
|
||||
dialog --clear --backtitle "ProxMenux" \
|
||||
--title "$(translate "Utilities Verification")" \
|
||||
--msgbox "$summary$status_text" 25 80
|
||||
--title "$(translate "Utilities Verification")" \
|
||||
--msgbox "$summary$status_text" 25 80
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# Main menu loop
|
||||
while true; do
|
||||
choice=$(show_main_utilities_menu)
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
|
||||
selected=$(show_custom_selection)
|
||||
install_selected_utilities "$selected"
|
||||
;;
|
||||
2)
|
||||
|
||||
all_utils=(
|
||||
"axel:axel:Download accelerator"
|
||||
"dos2unix:dos2unix:Convert DOS/Unix text files"
|
||||
@ -392,9 +379,8 @@ install_system_utils() {
|
||||
"chntpw:chntpw:Edit Windows registry/passwords"
|
||||
)
|
||||
install_utility_group "$(translate "ALL Utilities")" "${all_utils[@]}"
|
||||
;;
|
||||
;;
|
||||
3)
|
||||
|
||||
basic_utils=(
|
||||
"grc:grc:Generic Colouriser"
|
||||
"htop:htop:Process monitor"
|
||||
@ -405,7 +391,6 @@ install_system_utils() {
|
||||
install_utility_group "$(translate "Basic Utilities")" "${basic_utils[@]}"
|
||||
;;
|
||||
4)
|
||||
|
||||
dev_utils=(
|
||||
"git:git:Version control"
|
||||
"vim:vim:Advanced editor"
|
||||
@ -414,7 +399,6 @@ install_system_utils() {
|
||||
install_utility_group "$(translate "Development Tools")" "${dev_utils[@]}"
|
||||
;;
|
||||
5)
|
||||
|
||||
compress_utils=(
|
||||
"zip:zip:ZIP compressor"
|
||||
"unzip:unzip:ZIP extractor"
|
||||
@ -423,7 +407,6 @@ install_system_utils() {
|
||||
install_utility_group "$(translate "Compression Tools")" "${compress_utils[@]}"
|
||||
;;
|
||||
6)
|
||||
|
||||
multiplex_utils=(
|
||||
"screen:screen:Terminal multiplexer"
|
||||
"tmux:tmux:Advanced multiplexer"
|
||||
@ -431,7 +414,6 @@ install_system_utils() {
|
||||
install_utility_group "$(translate "Terminal Multiplexers")" "${multiplex_utils[@]}"
|
||||
;;
|
||||
7)
|
||||
|
||||
analysis_utils=(
|
||||
"jq:jq:JSON processor"
|
||||
"ncdu:ncdu:Disk analyzer"
|
||||
@ -440,7 +422,6 @@ install_system_utils() {
|
||||
install_utility_group "$(translate "Analysis Tools")" "${analysis_utils[@]}"
|
||||
;;
|
||||
8)
|
||||
|
||||
network_utils=(
|
||||
"nethogs:nethogs:Network monitor"
|
||||
"nmap:nmap:Network scanner"
|
||||
@ -457,8 +438,8 @@ install_system_utils() {
|
||||
;;
|
||||
*)
|
||||
dialog --clear --backtitle "ProxMenux" \
|
||||
--title "$(translate "Invalid Option")" \
|
||||
--msgbox "$(translate "Please select a valid option")" 8 40
|
||||
--title "$(translate "Invalid Option")" \
|
||||
--msgbox "$(translate "Please select a valid option")" 8 40
|
||||
;;
|
||||
esac
|
||||
done
|
||||
@ -466,5 +447,4 @@ install_system_utils() {
|
||||
clear
|
||||
}
|
||||
|
||||
|
||||
install_system_utils
|
||||
install_system_utils
|
||||
|
@ -52,8 +52,7 @@ NEON_PURPLE_BLUE="\033[38;5;99m"
|
||||
WHITE="\033[38;5;15m"
|
||||
RESET="\033[0m"
|
||||
DARK_GRAY="\033[38;5;244m"
|
||||
DARK_GRAY="\033[38;5;244m"
|
||||
DARK_GRAY="\033[38;5;244m"
|
||||
ORANGE="\033[38;5;208m"
|
||||
YW="\033[33m"
|
||||
YWB="\033[1;33m"
|
||||
GN="\033[1;92m"
|
||||
@ -137,6 +136,12 @@ msg_info2() {
|
||||
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
|
||||
msg_success() {
|
||||
if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID > /dev/null; then
|
||||
@ -165,7 +170,7 @@ msg_warn() {
|
||||
fi
|
||||
printf "\e[?25h"
|
||||
local msg="$1"
|
||||
echo -e "${BFR}${TAB}${NV}${CL} ${YWB}${msg}${CL}"
|
||||
echo -e "${BFR}${TAB}${CL} ${YWB}${msg}${CL}"
|
||||
}
|
||||
|
||||
|
||||
@ -179,6 +184,12 @@ msg_ok() {
|
||||
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
|
||||
msg_error() {
|
||||
@ -249,21 +260,23 @@ translate() {
|
||||
from googletrans import Translator
|
||||
import sys, json, re
|
||||
|
||||
def translate_text(text, dest_lang):
|
||||
def translate_text(text, dest_lang, context):
|
||||
translator = Translator()
|
||||
context = '$TRANSLATION_CONTEXT'
|
||||
try:
|
||||
full_text = context + ' ' + 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'^.*?(Context:|Contexto:|Contexte:|Kontext:|Contesto:|上下文:|コンテキスト:).*?:', '', translated, flags=re.IGNORECASE | re.DOTALL).strip()
|
||||
return json.dumps({'success': True, 'text': translated})
|
||||
print(json.dumps({'success': True, 'text': translated}))
|
||||
except Exception as e:
|
||||
return json.dumps({'success': False, 'error': str(e)})
|
||||
print(json.dumps({'success': False, 'error': str(e)}))
|
||||
|
||||
print(translate_text('$text', '$dest_lang'))
|
||||
")
|
||||
translate_text(
|
||||
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
|
||||
|
||||
local translation_result=$(echo "$translated" | jq -r '.')
|
||||
|
@ -114,6 +114,7 @@ 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 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"
|
||||
"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"
|
||||
"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"
|
||||
|
@ -36,9 +36,6 @@ fi
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
|
||||
# ==========================================================
|
||||
|
||||
|
||||
@ -78,12 +75,15 @@ function run_uupdump_creator() {
|
||||
done
|
||||
|
||||
if [[ ${#MISSING[@]} -gt 0 ]]; then
|
||||
show_proxmenux_logo
|
||||
msg_info "$(translate "Installing dependencies: ${MISSING[*]}")"
|
||||
apt-get update -qq >/dev/null 2>&1
|
||||
if ! apt-get install -y "${MISSING[@]}" >/dev/null 2>&1; then
|
||||
msg_error "$(translate "Failed to install: ${MISSING[*]}")"
|
||||
sleep 2
|
||||
exit 1
|
||||
fi
|
||||
msg_ok "$(translate "All dependencies installed and verified.")"
|
||||
fi
|
||||
|
||||
for i in "${!CMDS[@]}"; do
|
||||
@ -92,17 +92,20 @@ function run_uupdump_creator() {
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#FAILED[@]} -eq 0 ]]; then
|
||||
msg_ok "$(translate "All dependencies installed and verified.")"
|
||||
else
|
||||
msg_error "$(translate "Missing commands after installation: ${FAILED[*]}")"
|
||||
if [[ ${#FAILED[@]} -gt 0 ]]; then
|
||||
show_proxmenux_logo
|
||||
msg_error "$(translate "Missing commands after installation:") ${FAILED[*]}"
|
||||
sleep 2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
|
||||
ISO_DIR=$(detect_iso_dir)
|
||||
if [[ -z "$ISO_DIR" ]]; then
|
||||
show_proxmenux_logo
|
||||
msg_error "$(translate "Could not determine a valid ISO storage directory.")"
|
||||
sleep 2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@ -110,40 +113,63 @@ fi
|
||||
mkdir -p "$ISO_DIR"
|
||||
|
||||
|
||||
DEFAULT_TMP="/root/uup-temp"
|
||||
USER_INPUT=$(dialog --inputbox "Enter temporary folder path (default: $DEFAULT_TMP):" 10 60 "$DEFAULT_TMP" 3>&1 1>&2 2>&3)
|
||||
if [[ $? -ne 0 || -z "$USER_INPUT" ]]; then
|
||||
USER_INPUT="$DEFAULT_TMP"
|
||||
|
||||
DEFAULT_BASE="/root/uup-temp"
|
||||
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 "$BASE_INPUT" ]]; then
|
||||
BASE_INPUT="$DEFAULT_BASE"
|
||||
fi
|
||||
|
||||
#
|
||||
if [[ "$USER_INPUT" == "$DEFAULT_TMP" ]]; then
|
||||
TMP_DIR="$USER_INPUT"
|
||||
CLEAN_ALL=true
|
||||
else
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
RANDOM_ID=$(head /dev/urandom | tr -dc a-z0-9 | head -c 4)
|
||||
TMP_DIR="${USER_INPUT%/}/uup-session-${TIMESTAMP}-${RANDOM_ID}"
|
||||
CLEAN_ALL=false
|
||||
fi
|
||||
|
||||
mkdir -p "$TMP_DIR" || {
|
||||
msg_error "$(translate "Failed to create temporary directory:") $TMP_DIR"
|
||||
BASE_CLEAN="$(echo "$BASE_INPUT" | sed 's:[[:space:]]*$::' | sed 's:/*$::')"
|
||||
|
||||
|
||||
if [[ ! -d "$BASE_CLEAN" ]]; then
|
||||
if ! mkdir -p "$BASE_CLEAN"; then
|
||||
show_proxmenux_logo
|
||||
msg_error "$(translate "The selected base folder does not exist and could not be created:") $BASE_CLEAN"
|
||||
sleep 2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [[ ! -w "$BASE_CLEAN" ]]; then
|
||||
show_proxmenux_logo
|
||||
msg_error "$(translate "No write permissions on:") $BASE_CLEAN"
|
||||
sleep 2
|
||||
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)
|
||||
if [[ $? -ne 0 || -z "$UUP_URL" ]]; then
|
||||
msg_warn "$(translate "Cancelled by user or empty URL.")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! "$UUP_URL" =~ id=.+\&pack=.+\&edition=.+ ]]; then
|
||||
show_proxmenux_logo
|
||||
msg_error "$(translate "The URL does not contain the required parameters (id, pack, edition).")"
|
||||
sleep 2
|
||||
return 1
|
||||
@ -155,6 +181,8 @@ LANG=$(echo "$UUP_URL" | grep -oP 'pack=\K[^&]+')
|
||||
EDITION=$(echo "$UUP_URL" | grep -oP 'edition=\K[^&]+')
|
||||
ARCH="amd64"
|
||||
|
||||
show_proxmenux_logo
|
||||
echo -e
|
||||
echo -e "\n${BGN}=============== UUP Dump Creator ===============${CL}"
|
||||
echo -e " ${BGN}🆔 ID:${CL} ${DGN}$BUILD_ID${CL}"
|
||||
echo -e " ${BGN}🌐 Language:${CL} ${DGN}$LANG${CL}"
|
||||
|
@ -1 +1 @@
|
||||
1.1.2
|
||||
1.1.4
|
Loading…
x
Reference in New Issue
Block a user