mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-10-02 16:16:19 +00:00
Compare commits
347 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5cb9e13ca7 | ||
|
0187010f94 | ||
|
2c2ed21e59 | ||
|
f8b2ccec40 | ||
|
e858dc582d | ||
|
dd737f4b46 | ||
|
f0bc238b6d | ||
|
af55424850 | ||
|
902534baff | ||
|
6daa630040 | ||
|
0b2b86673b | ||
|
6aa5b58208 | ||
|
4430201cd2 | ||
|
7c7963a83e | ||
|
e2202cd2d8 | ||
|
a931be83bc | ||
|
7350bea345 | ||
|
9b1e39dbb4 | ||
|
15cd118845 | ||
|
d58dff047c | ||
|
a2f83c896c | ||
|
6ef77c731c | ||
|
29b0f61958 | ||
|
e944b2ecdd | ||
|
41819c46a3 | ||
|
13f391a6f0 | ||
|
85a3d44f2c | ||
|
0792392058 | ||
|
ff5083ada0 | ||
|
62841677bc | ||
|
1761cf53a2 | ||
|
a771efc5fa | ||
|
ed049da76a | ||
|
5d1d357a2e | ||
|
30d0706a1c | ||
|
e9667e1266 | ||
|
73109483e7 | ||
|
a9c1acf204 | ||
|
81c4f5814c | ||
|
c595f6d781 | ||
|
24bb6b1d3d | ||
|
49eeb6020d | ||
|
7c272bd2a2 | ||
|
cfbd865937 | ||
|
fe472f33ef | ||
|
8d6b3d650f | ||
|
3b0d5b5eb7 | ||
|
875e8a99bd | ||
|
6c19d81844 | ||
|
ba535a931f | ||
|
45dca5218d | ||
|
da3cb9971b | ||
|
b39270dc1e | ||
|
ae8a7d0de9 | ||
|
2d501415bf | ||
|
da639ccaac | ||
|
a352770e2d | ||
|
e3e1899466 | ||
|
e67288e623 | ||
|
4019e49b07 | ||
|
cd8711f3bc | ||
|
0d119379de | ||
|
aa2b6ff112 | ||
|
3482f7dc98 | ||
|
16c321f114 | ||
|
a81e7f3c44 | ||
|
d7cc001521 | ||
|
eb11962231 | ||
|
9f73b8f159 | ||
|
873a4abe24 | ||
|
56bc584f5e | ||
|
2a9f2f3c2e | ||
|
ee719cdd39 | ||
|
a571b57b30 | ||
|
5ee7a23bea | ||
|
fe159ea195 | ||
|
8fcdf6176b | ||
|
715166bbca | ||
|
1d58072c70 | ||
|
d667cde699 | ||
|
4cd8889c38 | ||
|
93896f6fb7 | ||
|
3b3f0387bb | ||
|
2875c9af95 | ||
|
93ef1bfccc | ||
|
a886af1d87 | ||
|
d731ff3ae6 | ||
|
d44864637d | ||
|
674ee34ec6 | ||
|
a93eeda243 | ||
|
80fd92e2a1 | ||
|
d4ff2da473 | ||
|
9b7b271580 | ||
|
e1b340966a | ||
|
7ec4c331af | ||
|
3102d596ee | ||
|
af56dc546e | ||
|
15d47499fa | ||
|
53a34d0470 | ||
|
3ee675cefe | ||
|
d98c7bdc03 | ||
|
bb4f1ebed6 | ||
|
c8f73ea23b | ||
|
8292b12787 | ||
|
0f518e3c35 | ||
|
1c2f67d43d | ||
|
a5560a3123 | ||
|
1332096360 | ||
|
80381a6375 | ||
|
acf92bd005 | ||
|
da4f8a3a19 | ||
|
3a332192e3 | ||
|
1fdb1d87cc | ||
|
b99aa55d7a | ||
|
de20da2dad | ||
|
9444f0a68b | ||
|
48fd223a28 | ||
|
0845efe419 | ||
|
57b7ba91bc | ||
|
97af8a4892 | ||
|
d6f237e289 | ||
|
aba7109b35 | ||
|
d3ec71052e | ||
|
1be63f396b | ||
|
9308742146 | ||
|
b32241082d | ||
|
1f8504d685 | ||
|
97c5c48150 | ||
|
afe84dc46a | ||
|
ffafd42f03 | ||
|
7dca715c91 | ||
|
7695e1d8dd | ||
|
84b86d1db7 | ||
|
bae3ef6460 | ||
|
97c6ec8875 | ||
|
d33128dc26 | ||
|
10bdecabb6 | ||
|
de88f530c8 | ||
|
fb511b7596 | ||
|
322665ce91 | ||
|
baeca1fcfb | ||
|
095b98c36a | ||
|
29bb7e7608 | ||
|
e3d137efba | ||
|
207e915393 | ||
|
614e629a2b | ||
|
f35de5c749 | ||
|
c1623bd4df | ||
|
8690da5017 | ||
|
696adcdc24 | ||
|
2756bd06c1 | ||
|
4893f6ea00 | ||
|
35a7348197 | ||
|
cdd6333d0a | ||
|
54399b5b5d | ||
|
f6b192cc1e | ||
|
cd231b90d8 | ||
|
87fe788358 | ||
|
3e9bd21ea8 | ||
|
b6d4029797 | ||
|
ec65e96148 | ||
|
926f1f971f | ||
|
5d69fad73f | ||
|
a796761023 | ||
|
5d1338e485 | ||
|
ce25a167f1 | ||
|
1c44969580 | ||
|
b6e04e3ede | ||
|
84c26be703 | ||
|
d201160722 | ||
|
e112361b43 | ||
|
3e69795c9d | ||
|
b11baf2e5d | ||
|
233770b553 | ||
|
187db73798 | ||
|
0e3fc6f682 | ||
|
d11e3a4ac4 | ||
|
d3b4ca3e66 | ||
|
f37fbbfb8b | ||
|
52b7aac424 | ||
|
d42f3f8f0c | ||
|
91b5c7c9bc | ||
|
48feebc092 | ||
|
14e2d66d96 | ||
|
10d844a195 | ||
|
bbf91ae5d6 | ||
|
cb82eda49a | ||
|
bc1dbb1c27 | ||
|
9496a7f1ce | ||
|
7241fa31b4 | ||
|
fed7216436 | ||
|
ffe7d7c4c6 | ||
|
f430ac8d6c | ||
|
70dfd7c9a3 | ||
|
ed3140932b | ||
|
3cd2bd6ce8 | ||
|
982bf45fc4 | ||
|
aaba8569fc | ||
|
4111e15eb9 | ||
|
2012478f26 | ||
|
88869d3239 | ||
|
f3c2549b18 | ||
|
57e3b839d0 | ||
|
faf3f43413 | ||
|
52e5bb3386 | ||
|
89405f6670 | ||
|
73111c4139 | ||
|
04e9c5db8c | ||
|
69278902de | ||
|
efa95b0858 | ||
|
660128cd5c | ||
|
ef1e052e47 | ||
|
0b346bc343 | ||
|
2272eaf833 | ||
|
4adee98bce | ||
|
cbdb2c0705 | ||
|
4f438aabbf | ||
|
b6ccc06963 | ||
|
5b89a15bfc | ||
|
5596ae551d | ||
|
1360df592a | ||
|
13684ff83c | ||
|
ae88f7870e | ||
|
810b6da60c | ||
|
7bdf3e08f9 | ||
|
fdad2a087f | ||
|
c437a8c426 | ||
|
ef861e6d1d | ||
|
928a008688 | ||
|
638a124adb | ||
|
c2a63ae9bb | ||
|
28cf31e6e7 | ||
|
3cf416167d | ||
|
ebf03923a0 | ||
|
82797d2421 | ||
|
52b6be946c | ||
|
dc46724d7b | ||
|
ed7d43b6a9 | ||
|
6f3fc51278 | ||
|
a446acc282 | ||
|
d987d639ab | ||
|
e7e180e468 | ||
|
76770f82cd | ||
|
4079d4fd7c | ||
|
ac48178369 | ||
|
c2e9f038ee | ||
|
70220d9829 | ||
|
b9a1f378ec | ||
|
f6bc090a98 | ||
|
be519f3932 | ||
|
0a46f77555 | ||
|
0e6cc0c7e5 | ||
|
11cd425162 | ||
|
aa269688d6 | ||
|
4c9e94768e | ||
|
581157fa82 | ||
|
e748e479cc | ||
|
5c9e4eea1e | ||
|
0c1189b233 | ||
|
5ec9b82b4a | ||
|
c84ec533da | ||
|
fb80c6ad7a | ||
|
2e3bfff6a4 | ||
|
e96ce30891 | ||
|
10de5b2e5f | ||
|
1966081239 | ||
|
b48d806d53 | ||
|
97784d74e7 | ||
|
c42e92b07d | ||
|
2c52943b54 | ||
|
4ccb1902cb | ||
|
349b0572cd | ||
|
87fae8a9eb | ||
|
a77a097f47 | ||
|
a84d81143e | ||
|
d9cee50ef3 | ||
|
0fc414e5e9 | ||
|
e18f20ce4c | ||
|
c12af4060c | ||
|
9992ea0dee | ||
|
b8310f1c5d | ||
|
78f66af702 | ||
|
4d7564094e | ||
|
370f4694d1 | ||
|
fc7c740691 | ||
|
8e1f955519 | ||
|
aa3d16d981 | ||
|
5447f0e4df | ||
|
97294df208 | ||
|
c6f53629da | ||
|
fcba907658 | ||
|
f481df7b8d | ||
|
81079a35d9 | ||
|
bda344c382 | ||
|
c4ebc396af | ||
|
4cdbf1231b | ||
|
92db58a9f6 | ||
|
5f07f47308 | ||
|
2132ae79a6 | ||
|
bda7834a4f | ||
|
7693f313c4 | ||
|
d2200a64e0 | ||
|
a5c46ab837 | ||
|
a389282e23 | ||
|
1f90b5b739 | ||
|
2c59500046 | ||
|
a59a056e12 | ||
|
235364013b | ||
|
1049ac6eac | ||
|
04dc7af25c | ||
|
f62ea3ad04 | ||
|
14c75f2cd9 | ||
|
9ae68b9653 | ||
|
b7ab4c4568 | ||
|
7d0b3a0c87 | ||
|
0d38f7f290 | ||
|
12e5ef4231 | ||
|
f3aa1f7414 | ||
|
f2eaec6e02 | ||
|
0654a3ed55 | ||
|
9a27138d96 | ||
|
b3c9f71c02 | ||
|
e9c9b957db | ||
|
29cdf6fa48 | ||
|
8466a8e21e | ||
|
1523b6b8a8 | ||
|
33205e1008 | ||
|
537af385f8 | ||
|
7259b0a850 | ||
|
11fbfda6bf | ||
|
4f0353d0fb | ||
|
a605d68d73 | ||
|
237b7fbf1b | ||
|
4a7e21f6b4 | ||
|
b7017573b8 | ||
|
a98b087c5d | ||
|
161c840136 | ||
|
4dd2abc202 | ||
|
cc0e9f61a7 | ||
|
21a658f1f4 | ||
|
b99f391c2a | ||
|
9abe25b91a | ||
|
2531fc6dac | ||
|
e5551cb179 | ||
|
4728b7a8b7 | ||
|
2863921e15 | ||
|
b93668edfe |
101
CHANGELOG.md
101
CHANGELOG.md
@@ -1,3 +1,104 @@
|
||||
## 2025-01-10
|
||||
|
||||
### New version v1.1.6
|
||||
|
||||

|
||||
|
||||
### Added
|
||||
|
||||
- **New Menu: Mount and Share Manager**
|
||||
Introduced a comprehensive new menu for managing shared resources between Proxmox host and LXC containers:
|
||||
|
||||
**Host Configuration Options:**
|
||||
1. **Configure NFS Shared on Host** - Add, view, and remove NFS shared resources on the Proxmox server with automatic export management
|
||||
2. **Configure Samba Shared on Host** - Add, view, and remove Samba/CIFS shared resources on the Proxmox server with share configuration
|
||||
3. **Configure Local Shared on Host** - Create and manage local shared directories with proper permissions on the Proxmox host
|
||||
|
||||
**LXC Integration Options:**
|
||||
4. **Configure LXC Mount Points (Host ↔ Container)** - **Core feature** that enables mounting host directories into LXC containers with automatic permission handling. Includes the ability to **view existing mount points** for each container in a clear, organized way and **remove mount points** with proper verification that the process completed successfully. Especially optimized for **unprivileged containers** where UID/GID mapping is critical.
|
||||
5. **Configure NFS Client in LXC** - Set up NFS client inside privileged containers
|
||||
6. **Configure Samba Client in LXC** - Set up Samba client inside privileged containers
|
||||
7. **Configure NFS Server in LXC** - Install NFS server inside privileged containers
|
||||
8. **Configure Samba Server in LXC** - Install Samba server inside privileged containers
|
||||
|
||||
**Documentation & Support:**
|
||||
- **Help & Info (commands)** - Comprehensive guides with step-by-step manual instructions for all sharing scenarios
|
||||
|
||||
The entire system is built around the **LXC Mount Points** functionality, which automatically detects filesystem types, handles permission mapping between host and container users, and provides seamless integration for both privileged and unprivileged containers.
|
||||
|
||||
---
|
||||
|
||||
### Improved
|
||||
|
||||
- **Log2RAM Auto-Detection Enhancement**
|
||||
In the automatic post-install script, the Log2RAM installation function now prompts the user when automatic disk ssd/m2 detection fails.
|
||||
This ensures Log2RAM can still be installed on systems where automatic disk detection doesn't work properly.
|
||||
|
||||
---
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Proxmox Update Repository Verification**
|
||||
Fixed an issue in the Proxmox update function where empty repository source files would cause errors during conflict verification. The function now properly handles empty `/etc/apt/sources.list.d/` files without throwing false warnings.
|
||||
|
||||
Thanks to **@JF_Car** for reporting this issue.
|
||||
|
||||
---
|
||||
|
||||
### Acknowledgments
|
||||
|
||||
Special thanks to **@JF_Car**, **@ghosthvj**, and **@jonatanc** for their testing, valuable feedback, and suggestions that helped refine the shared resources functionality and improve the overall user experience.
|
||||
|
||||
|
||||
|
||||
## 2025-08-20
|
||||
|
||||
### New version v1.1.5
|
||||
|
||||
### Added
|
||||
|
||||
- **New Script: Upgrade PVE 8 to PVE 9**
|
||||
Added a full upgrade tool located under `Utilities and Tools`. It provides:
|
||||
1. **Automatic upgrade** from PVE 8 to 9
|
||||
2. **Interactive upgrade** with step-by-step confirmations
|
||||
3. **Check-only mode** using `check-pve8to9`
|
||||
4. **Manual instructions** shown in order for users who prefer to upgrade manually
|
||||
|
||||
- **New Tools in System Utilities**
|
||||
- [`s-tui`](https://github.com/amanusk/s-tui): Terminal-based CPU monitoring with graphs
|
||||
- [`intel-gpu-tools`](https://gitlab.freedesktop.org/drm/igt-gpu-tools): Useful for Intel GPU diagnostics
|
||||
|
||||
---
|
||||
|
||||
### Improved
|
||||
|
||||
- **APT Upgrade Handling**
|
||||
The PVE upgrade function now blocks the process if any package prompts for manual confirmation. This avoids partial upgrades and ensures consistency.
|
||||
|
||||
- **Network Optimization (sysctl)**
|
||||
- Obsolete kernel parameters removed (e.g., `tcp_tw_recycle`, `nf_conntrack_helper`) to prevent warnings in **Proxmox 9 / kernel 6.14**
|
||||
- Now generates only valid, up-to-date sysctl parameters
|
||||
|
||||
- **AMD CPU Patch Handling**
|
||||
- Now applies correct `idle=nomwait` and KVM options (`ignore_msrs=1`, `report_ignored_msrs=0`)
|
||||
- Expected warning is now documented and safely handled for stability with Ryzen/EPYC
|
||||
|
||||
- **Timezone & NTP Fixes**
|
||||
- Automatically detects timezone using public IP geolocation
|
||||
- Falls back to UTC if detection fails
|
||||
- Restarts Postfix after timezone set → resolves `/var/spool/postfix/etc/localtime` mismatch warning
|
||||
|
||||
- **Repository & Package Installer Logic**
|
||||
- Now verifies that working repositories exist before installing any package
|
||||
- If none are available, adds a fallback **Debian stable** repository
|
||||
- Replaces deprecated `mlocate` with `plocate` (compatible with Debian 13 and Proxmox 9)
|
||||
|
||||
- **Improved Logs and User Feedback**
|
||||
- Actions that fail now provide precise messages (instead of falsely marking as success)
|
||||
- Helps users clearly understand what's been applied or skipped
|
||||
|
||||
|
||||
|
||||
## 2025-08-06
|
||||
|
||||
### New version v1.1.4
|
||||
|
@@ -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.)
|
||||
|
@@ -106,12 +106,12 @@ uninstall_proxmenu() {
|
||||
local force_clean="$2"
|
||||
|
||||
if [ "$force_clean" != "force" ]; then
|
||||
if ! whiptail --title "Uninstall ProxMenu" --yesno "Are you sure you want to uninstall ProxMenu?" 10 60; then
|
||||
if ! whiptail --title "Uninstall ProxMenux" --yesno "Are you sure you want to uninstall ProxMenux?" 10 60; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Uninstalling ProxMenu..."
|
||||
echo "Uninstalling ProxMenux..."
|
||||
|
||||
if [ -f "$VENV_PATH/bin/activate" ]; then
|
||||
echo "Removing googletrans and virtual environment..."
|
||||
@@ -151,7 +151,7 @@ uninstall_proxmenu() {
|
||||
sed -i '/This system is optimised by: ProxMenux/d' /etc/motd
|
||||
fi
|
||||
|
||||
echo "ProxMenu has been uninstalled."
|
||||
echo "ProxMenux has been uninstalled."
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ show_progress() {
|
||||
local total="$2"
|
||||
local message="$3"
|
||||
|
||||
echo -e "\n${BOLD}${BL}${TAB}Installing ProxMenu: Step $step of $total${CL}"
|
||||
echo -e "\n${BOLD}${BL}${TAB}Installing ProxMenux: Step $step of $total${CL}"
|
||||
echo
|
||||
msg_info2 "$message"
|
||||
}
|
||||
@@ -526,6 +526,7 @@ show_installation_options() {
|
||||
"1" "Normal Version (English only)" 3>&1 1>&2 2>&3)
|
||||
|
||||
if [ -z "$INSTALL_TYPE" ]; then
|
||||
show_proxmenux_logo
|
||||
msg_warn "Installation cancelled."
|
||||
exit 1
|
||||
fi
|
||||
@@ -535,6 +536,7 @@ show_installation_options() {
|
||||
"2" "Translation Version (Multi-language support)" 3>&1 1>&2 2>&3)
|
||||
|
||||
if [ -z "$INSTALL_TYPE" ]; then
|
||||
show_proxmenux_logo
|
||||
msg_warn "Installation cancelled."
|
||||
exit 1
|
||||
fi
|
||||
@@ -543,6 +545,7 @@ show_installation_options() {
|
||||
|
||||
|
||||
if [ -z "$INSTALL_TYPE" ]; then
|
||||
show_proxmenux_logo
|
||||
msg_warn "Installation cancelled."
|
||||
exit 1
|
||||
fi
|
||||
@@ -550,12 +553,14 @@ show_installation_options() {
|
||||
# For new installations, show confirmation with details
|
||||
if [ "$current_install_type" = "none" ]; then
|
||||
if ! show_installation_confirmation "$INSTALL_TYPE"; then
|
||||
show_proxmenux_logo
|
||||
msg_warn "Installation cancelled."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! handle_installation_change "$current_install_type" "$INSTALL_TYPE"; then
|
||||
show_proxmenux_logo
|
||||
msg_warn "Installation cancelled."
|
||||
exit 1
|
||||
fi
|
||||
|
435
json/cache.json
435
json/cache.json
@@ -2132,6 +2132,439 @@
|
||||
"de": "Zum Beenden von iptraf-ng, x drücken",
|
||||
"it": "Per uscire da iptraf-ng, premi x",
|
||||
"pt": "Para sair do iptraf-ng, pressione x"
|
||||
},
|
||||
"Proxmox VE 8 to 9 Manual Upgrade Guide": {
|
||||
"es": "Guía de actualización manual de Proxmox VE 8 a 9",
|
||||
"fr": "Guide de mise à niveau manuelle de Proxmox VE 8 vers 9",
|
||||
"de": "Manuelles Upgrade-Handbuch: Proxmox VE 8 auf 9",
|
||||
"it": "Guida all'aggiornamento manuale da Proxmox VE 8 a 9",
|
||||
"pt": "Guia de atualização manual do Proxmox VE 8 para 9"
|
||||
},
|
||||
"Source:": {
|
||||
"es": "Fuente:",
|
||||
"fr": "Source :",
|
||||
"de": "Quelle:",
|
||||
"it": "Fonte:",
|
||||
"pt": "Fonte:"
|
||||
},
|
||||
"IMPORTANT PREREQUISITES:": {
|
||||
"es": "REQUISITOS PREVIOS IMPORTANTES:",
|
||||
"fr": "PRÉREQUIS IMPORTANTS :",
|
||||
"de": "WICHTIGE VORAUSSETZUNGEN:",
|
||||
"it": "PREREQUISITI IMPORTANTI:",
|
||||
"pt": "PRÉ-REQUISITOS IMPORTANTES:"
|
||||
},
|
||||
"System must be updated to latest PVE 8.4+ before starting": {
|
||||
"es": "El sistema debe estar actualizado a la versión PVE 8.4+ antes de comenzar",
|
||||
"fr": "Le système doit être mis à jour vers PVE 8.4+ avant de commencer",
|
||||
"de": "Das System muss vor dem Start auf PVE 8.4+ aktualisiert sein",
|
||||
"it": "Il sistema deve essere aggiornato a PVE 8.4+ prima di iniziare",
|
||||
"pt": "O sistema deve estar atualizado para PVE 8.4+ antes de começar"
|
||||
},
|
||||
"Use SSH or terminal access (SSH recommended)": {
|
||||
"es": "Use acceso por SSH o terminal (se recomienda SSH)",
|
||||
"fr": "Utilisez un accès SSH ou terminal (SSH recommandé)",
|
||||
"de": "SSH- oder Terminalzugang verwenden (SSH empfohlen)",
|
||||
"it": "Usa accesso SSH o terminale (consigliato SSH)",
|
||||
"pt": "Use acesso por SSH ou terminal (SSH recomendado)"
|
||||
},
|
||||
"Use tmux or screen to avoid interruptions": {
|
||||
"es": "Use tmux o screen para evitar interrupciones",
|
||||
"fr": "Utilisez tmux ou screen pour éviter les interruptions",
|
||||
"de": "Verwenden Sie tmux oder screen, um Unterbrechungen zu vermeiden",
|
||||
"it": "Usa tmux o screen per evitare interruzioni",
|
||||
"pt": "Use tmux ou screen para evitar interrupções"
|
||||
},
|
||||
"Have valid backups of all VMs and containers": {
|
||||
"es": "Tenga copias de seguridad válidas de todas las VMs y contenedores",
|
||||
"fr": "Ayez des sauvegardes valides de toutes les VM et conteneurs",
|
||||
"de": "Halten Sie gültige Backups aller VMs und Container bereit",
|
||||
"it": "Avere backup validi di tutte le VM e i container",
|
||||
"pt": "Tenha backups válidos de todas as VMs e contêineres"
|
||||
},
|
||||
"At least 5GB free space on root filesystem": {
|
||||
"es": "Al menos 5 GB de espacio libre en el sistema de archivos raíz",
|
||||
"fr": "Au moins 5 Go d’espace libre sur le système de fichiers racine",
|
||||
"de": "Mindestens 5 GB freier Speicherplatz auf dem Root-Dateisystem",
|
||||
"it": "Almeno 5 GB di spazio libero sul filesystem root",
|
||||
"pt": "Pelo menos 5 GB de espaço livre no sistema de arquivos raiz"
|
||||
},
|
||||
"Do not run the upgrade from the Web UI virtual console (it will disconnect)": {
|
||||
"es": "No ejecute la actualización desde la consola virtual de la interfaz web (se desconectará)",
|
||||
"fr": "N’exécutez pas la mise à niveau depuis la console virtuelle de l’interface Web (elle se déconnectera)",
|
||||
"de": "Führen Sie das Upgrade nicht über die virtuelle Konsole der Web-UI aus (die Verbindung wird getrennt)",
|
||||
"it": "Non eseguire l’aggiornamento dalla console virtuale dell’interfaccia Web (si disconnetterà)",
|
||||
"pt": "Não execute a atualização pelo console virtual da interface Web (a conexão será interrompida)"
|
||||
},
|
||||
"Update system to latest PVE 8.4+ (if not done already):": {
|
||||
"es": "Actualizar el sistema a PVE 8.4+ (si aún no se ha hecho):",
|
||||
"fr": "Mettre à jour le système vers PVE 8.4+ (si ce n’est pas déjà fait) :",
|
||||
"de": "System auf PVE 8.4+ aktualisieren (falls noch nicht geschehen):",
|
||||
"it": "Aggiornare il sistema a PVE 8.4+ (se non già fatto):",
|
||||
"pt": "Atualizar o sistema para PVE 8.4+ (se ainda não foi feito):"
|
||||
},
|
||||
"Or use ProxMenux update function": {
|
||||
"es": "O use la función de actualización de ProxMenux",
|
||||
"fr": "Ou utilisez la fonction de mise à jour de ProxMenux",
|
||||
"de": "Oder verwenden Sie die Aktualisierungsfunktion von ProxMenux",
|
||||
"it": "Oppure usa la funzione di aggiornamento di ProxMenux",
|
||||
"pt": "Ou use a função de atualização do ProxMenux"
|
||||
},
|
||||
"Verify PVE version (must be 8.4.1 or newer):": {
|
||||
"es": "Verificar la versión de PVE (debe ser 8.4.1 o superior):",
|
||||
"fr": "Vérifier la version de PVE (doit être 8.4.1 ou plus récente) :",
|
||||
"de": "PVE-Version prüfen (muss 8.4.1 oder neuer sein):",
|
||||
"it": "Verificare la versione di PVE (deve essere 8.4.1 o successiva):",
|
||||
"pt": "Verificar a versão do PVE (deve ser 8.4.1 ou superior):"
|
||||
},
|
||||
"If this node runs hyper-converged Ceph: ensure Ceph is 19.x (Squid) BEFORE upgrading PVE.": {
|
||||
"es": "Si este nodo ejecuta Ceph hiperconvergente: asegúrese de que Ceph sea 19.x (Squid) ANTES de actualizar PVE.",
|
||||
"fr": "Si ce nœud exécute un Ceph hyperconvergé : assurez-vous que Ceph est en 19.x (Squid) AVANT de mettre à niveau PVE.",
|
||||
"de": "Wenn dieser Knoten hyperkonvergentes Ceph betreibt: Stellen Sie sicher, dass Ceph 19.x (Squid) ist, BEVOR Sie PVE aktualisieren.",
|
||||
"it": "Se questo nodo esegue Ceph iperconvergente: assicurarsi che Ceph sia 19.x (Squid) PRIMA di aggiornare PVE.",
|
||||
"pt": "Se este nó executa Ceph hiperconvergente: certifique-se de que o Ceph esteja em 19.x (Squid) ANTES de atualizar o PVE."
|
||||
},
|
||||
"If not 19.x, upgrade Ceph (Reef→Squid) first per the official guide:": {
|
||||
"es": "Si no es 19.x, actualice primero Ceph (Reef→Squid) según la guía oficial:",
|
||||
"fr": "Si ce n’est pas 19.x, mettez d’abord à niveau Ceph (Reef→Squid) selon le guide officiel :",
|
||||
"de": "Wenn nicht 19.x, aktualisieren Sie Ceph zuerst (Reef→Squid) gemäß der offiziellen Anleitung:",
|
||||
"it": "Se non è 19.x, aggiornare prima Ceph (Reef→Squid) secondo la guida ufficiale:",
|
||||
"pt": "Se não for 19.x, atualize primeiro o Ceph (Reef→Squid) conforme o guia oficial:"
|
||||
},
|
||||
"Run upgrade checklist script:": {
|
||||
"es": "Ejecutar el script de la lista de verificación de actualización:",
|
||||
"fr": "Exécuter le script de liste de vérification de mise à niveau :",
|
||||
"de": "Upgrade-Checklisten-Skript ausführen:",
|
||||
"it": "Eseguire lo script della checklist di aggiornamento:",
|
||||
"pt": "Executar o script da lista de verificação de atualização:"
|
||||
},
|
||||
"If it warns about 'systemd-boot' meta-package, remove it:": {
|
||||
"es": "Si advierte sobre el metapaquete 'systemd-boot', elimínelo:",
|
||||
"fr": "S’il signale le méta-paquet « systemd-boot », supprimez-le :",
|
||||
"de": "Wenn vor dem Metapaket „systemd-boot“ gewarnt wird, entfernen Sie es:",
|
||||
"it": "Se avvisa del metapacchetto 'systemd-boot', rimuoverlo:",
|
||||
"pt": "Se alertar sobre o metapacote 'systemd-boot', remova-o:"
|
||||
},
|
||||
"Start terminal multiplexer (recommended):": {
|
||||
"es": "Iniciar un multiplexor de terminal (recomendado):",
|
||||
"fr": "Démarrer un multiplexeur de terminal (recommandé) :",
|
||||
"de": "Terminal-Multiplexer starten (empfohlen):",
|
||||
"it": "Avviare un multiplexer di terminale (consigliato):",
|
||||
"pt": "Iniciar um multiplexador de terminal (recomendado):"
|
||||
},
|
||||
"# Recommended: avoids disconnection during upgrade": {
|
||||
"es": "# Recomendado: evita desconexiones durante la actualización",
|
||||
"fr": "# Recommandé : évite les déconnexions pendant la mise à niveau",
|
||||
"de": "# Empfohlen: vermeidet Verbindungsabbrüche während des Upgrades",
|
||||
"it": "# Consigliato: evita disconnessioni durante l’aggiornamento",
|
||||
"pt": "# Recomendado: evita desconexões durante a atualização"
|
||||
},
|
||||
"# Alternative if you prefer screen": {
|
||||
"es": "# Alternativa si prefiere screen",
|
||||
"fr": "# Alternative si vous préférez screen",
|
||||
"de": "# Alternative, wenn Sie screen bevorzugen",
|
||||
"it": "# Alternativa se preferisci screen",
|
||||
"pt": "# Alternativa se preferir screen"
|
||||
},
|
||||
"Update Debian repositories to Trixie:": {
|
||||
"es": "Actualizar los repositorios de Debian a Trixie:",
|
||||
"fr": "Mettre à jour les dépôts Debian vers Trixie :",
|
||||
"de": "Debian-Repositories auf Trixie aktualisieren:",
|
||||
"it": "Aggiornare i repository Debian a Trixie:",
|
||||
"pt": "Atualizar os repositórios Debian para Trixie:"
|
||||
},
|
||||
"Update PVE enterprise repository (Only if using enterprise):": {
|
||||
"es": "Actualizar el repositorio empresarial de PVE (solo si usa enterprise):",
|
||||
"fr": "Mettre à jour le dépôt entreprise de PVE (uniquement si vous utilisez enterprise) :",
|
||||
"de": "PVE-Enterprise-Repository aktualisieren (nur bei Verwendung von Enterprise):",
|
||||
"it": "Aggiornare il repository enterprise di PVE (solo se si usa enterprise):",
|
||||
"pt": "Atualizar o repositório enterprise do PVE (apenas se usar enterprise):"
|
||||
},
|
||||
"Skip this step if using no-subscription repository": {
|
||||
"es": "Omita este paso si usa el repositorio sin suscripción",
|
||||
"fr": "Ignorez cette étape si vous utilisez le dépôt sans abonnement",
|
||||
"de": "Überspringen Sie diesen Schritt, wenn Sie das No-Subscription-Repository verwenden",
|
||||
"it": "Saltare questo passaggio se si usa il repository senza sottoscrizione",
|
||||
"pt": "Pule esta etapa se usar o repositório sem assinatura"
|
||||
},
|
||||
"Add new PVE 9 enterprise repository (deb822 format) (Only if using enterprise):": {
|
||||
"es": "Agregar el nuevo repositorio empresarial de PVE 9 (formato deb822) (solo si usa enterprise):",
|
||||
"fr": "Ajouter le nouveau dépôt entreprise de PVE 9 (format deb822) (uniquement si vous utilisez enterprise) :",
|
||||
"de": "Neues PVE-9-Enterprise-Repository (deb822-Format) hinzufügen (nur bei Enterprise):",
|
||||
"it": "Aggiungere il nuovo repository enterprise di PVE 9 (formato deb822) (solo se si usa enterprise):",
|
||||
"pt": "Adicionar o novo repositório enterprise do PVE 9 (formato deb822) (apenas se usar enterprise):"
|
||||
},
|
||||
"Only if using enterprise subscription": {
|
||||
"es": "Solo si utiliza suscripción empresarial",
|
||||
"fr": "Uniquement si vous avez un abonnement entreprise",
|
||||
"de": "Nur bei vorhandener Enterprise-Abonnement",
|
||||
"it": "Solo se si dispone di una sottoscrizione enterprise",
|
||||
"pt": "Apenas se você tiver assinatura enterprise"
|
||||
},
|
||||
"OR add new PVE 9 no-subscription repository:": {
|
||||
"es": "O agregar el nuevo repositorio de PVE 9 sin suscripción:",
|
||||
"fr": "OU ajouter le nouveau dépôt PVE 9 sans abonnement :",
|
||||
"de": "ODER das neue PVE-9-No-Subscription-Repository hinzufügen:",
|
||||
"it": "OPPURE aggiungere il nuovo repository PVE 9 senza sottoscrizione:",
|
||||
"pt": "OU adicionar o novo repositório PVE 9 sem assinatura:"
|
||||
},
|
||||
"Only if using no-subscription repository": {
|
||||
"es": "Solo si usa el repositorio sin suscripción",
|
||||
"fr": "Uniquement si vous utilisez le dépôt sans abonnement",
|
||||
"de": "Nur bei Verwendung des No-Subscription-Repository",
|
||||
"it": "Solo se si usa il repository senza sottoscrizione",
|
||||
"pt": "Apenas se usar o repositório sem assinatura"
|
||||
},
|
||||
"Refresh APT index and verify repositories:": {
|
||||
"es": "Actualizar el índice de APT y verificar los repositorios:",
|
||||
"fr": "Actualiser l’index APT et vérifier les dépôts :",
|
||||
"de": "APT-Index aktualisieren und Repositories prüfen:",
|
||||
"it": "Aggiornare l’indice APT e verificare i repository:",
|
||||
"pt": "Atualizar o índice do APT e verificar os repositórios:"
|
||||
},
|
||||
"Ensure there are no errors and that proxmox-ve candidate shows 9.x": {
|
||||
"es": "Asegúrese de que no haya errores y de que el candidato de proxmox-ve sea 9.x",
|
||||
"fr": "Assurez-vous qu’il n’y a aucune erreur et que le candidat proxmox-ve indique 9.x",
|
||||
"de": "Stellen Sie sicher, dass keine Fehler auftreten und der proxmox-ve-Kandidat 9.x anzeigt",
|
||||
"it": "Assicurarsi che non vi siano errori e che il candidato di proxmox-ve sia 9.x",
|
||||
"pt": "Certifique-se de que não há erros e que o candidato proxmox-ve mostre 9.x"
|
||||
},
|
||||
"Update Ceph repository (Only if using Ceph):": {
|
||||
"es": "Actualizar el repositorio de Ceph (solo si usa Ceph):",
|
||||
"fr": "Mettre à jour le dépôt Ceph (uniquement si vous utilisez Ceph) :",
|
||||
"de": "Ceph-Repository aktualisieren (nur bei Verwendung von Ceph):",
|
||||
"it": "Aggiornare il repository di Ceph (solo se si usa Ceph):",
|
||||
"pt": "Atualizar o repositório do Ceph (apenas se usar Ceph):"
|
||||
},
|
||||
"Use enterprise URL if you have subscription.": {
|
||||
"es": "Use la URL enterprise si dispone de suscripción.",
|
||||
"fr": "Utilisez l’URL enterprise si vous avez un abonnement.",
|
||||
"de": "Verwenden Sie die Enterprise-URL, wenn Sie ein Abonnement haben.",
|
||||
"it": "Usa l’URL enterprise se hai una sottoscrizione.",
|
||||
"pt": "Use a URL enterprise se tiver assinatura."
|
||||
},
|
||||
"Remove old repository files:": {
|
||||
"es": "Eliminar los archivos de repositorio antiguos:",
|
||||
"fr": "Supprimer les anciens fichiers de dépôt :",
|
||||
"de": "Alte Repository-Dateien entfernen:",
|
||||
"it": "Rimuovere i vecchi file di repository:",
|
||||
"pt": "Remover arquivos antigos de repositório:"
|
||||
},
|
||||
"Also comment any remaining 'bookworm' entries in *.list if present.": {
|
||||
"es": "Comente también cualquier entrada restante de ‘bookworm’ en *.list si existe.",
|
||||
"fr": "Commentez également toute entrée ‘bookworm’ restante dans *.list si présente.",
|
||||
"de": "Kommentieren Sie außerdem verbleibende ‚bookworm‘-Einträge in *.list, falls vorhanden.",
|
||||
"it": "Commentare anche eventuali voci ‘bookworm’ rimanenti in *.list se presenti.",
|
||||
"pt": "Comente também quaisquer entradas ‘bookworm’ restantes em *.list, se houver."
|
||||
},
|
||||
"Update package index:": {
|
||||
"es": "Actualizar el índice de paquetes:",
|
||||
"fr": "Mettre à jour l’index des paquets :",
|
||||
"de": "Paketindex aktualisieren:",
|
||||
"it": "Aggiornare l’indice dei pacchetti:",
|
||||
"pt": "Atualizar o índice de pacotes:"
|
||||
},
|
||||
"Disable kernel audit messages (optional but recommended):": {
|
||||
"es": "Desactivar los mensajes de auditoría del kernel (opcional pero recomendado):",
|
||||
"fr": "Désactiver les messages d’audit du noyau (optionnel mais recommandé) :",
|
||||
"de": "Kernel-Audit-Meldungen deaktivieren (optional, aber empfohlen):",
|
||||
"it": "Disabilitare i messaggi di audit del kernel (opzionale ma consigliato):",
|
||||
"pt": "Desativar as mensagens de auditoria do kernel (opcional, mas recomendado):"
|
||||
},
|
||||
"Start the main system upgrade:": {
|
||||
"es": "Iniciar la actualización principal del sistema:",
|
||||
"fr": "Démarrer la mise à niveau principale du système :",
|
||||
"de": "Hauptsystem-Upgrade starten:",
|
||||
"it": "Avviare l’aggiornamento principale del sistema:",
|
||||
"pt": "Iniciar a atualização principal do sistema:"
|
||||
},
|
||||
"This will take time. Answer prompts carefully - see notes below.": {
|
||||
"es": "Esto llevará tiempo. Responda a las indicaciones con cuidado (vea las notas a continuación).",
|
||||
"fr": "Cela prendra du temps. Répondez aux invites avec attention (voir les notes ci-dessous).",
|
||||
"de": "Dies wird einige Zeit dauern. Beantworten Sie Rückfragen sorgfältig (siehe Hinweise unten).",
|
||||
"it": "Questo richiederà tempo. Rispondi con attenzione alle richieste (vedi note sotto).",
|
||||
"pt": "Isso levará tempo. Responda às solicitações com atenção (veja as notas abaixo)."
|
||||
},
|
||||
"UPGRADE PROMPTS - RECOMMENDED ANSWERS:": {
|
||||
"es": "INDICACIONES DE ACTUALIZACIÓN - RESPUESTAS RECOMENDADAS:",
|
||||
"fr": "INVITES DE MISE À NIVEAU - RÉPONSES RECOMMANDÉES :",
|
||||
"de": "UPGRADE-AUFFORDERUNGEN – EMPFOHLENE ANTWORTEN:",
|
||||
"it": "PROMPT DI AGGIORNAMENTO - RISPOSTE CONSIGLIATE:",
|
||||
"pt": "PROMPTS DE ATUALIZAÇÃO - RESPOSTAS RECOMENDADAS:"
|
||||
},
|
||||
"Keep current version (N)": {
|
||||
"es": "Mantener la versión actual (N)",
|
||||
"fr": "Conserver la version actuelle (N)",
|
||||
"de": "Aktuelle Version beibehalten (N)",
|
||||
"it": "Mantieni la versione corrente (N)",
|
||||
"pt": "Manter a versão atual (N)"
|
||||
},
|
||||
"Install maintainer's version (Y)": {
|
||||
"es": "Instalar la versión del mantenedor del paquete (Y)",
|
||||
"fr": "Installer la version du mainteneur du paquet (Y)",
|
||||
"de": "Version des Paketbetreuers installieren (Y)",
|
||||
"it": "Installare la versione del maintainer del pacchetto (Y)",
|
||||
"pt": "Instalar a versão do mantenedor do pacote (Y)"
|
||||
},
|
||||
"Keep current version (N) if modified": {
|
||||
"es": "Mantener la versión actual (N) si está modificada",
|
||||
"fr": "Conserver la version actuelle (N) si elle a été modifiée",
|
||||
"de": "Aktuelle Version beibehalten (N), falls angepasst",
|
||||
"it": "Mantieni la versione corrente (N) se modificata",
|
||||
"pt": "Manter a versão atual (N) se estiver modificada"
|
||||
},
|
||||
"Service restarts:": {
|
||||
"es": "Reinicios de servicios:",
|
||||
"fr": "Redémarrages de services :",
|
||||
"de": "Neustarts von Diensten:",
|
||||
"it": "Riavvii dei servizi:",
|
||||
"pt": "Reinicializações de serviços:"
|
||||
},
|
||||
"Use default (Yes)": {
|
||||
"es": "Usar la opción predeterminada (Sí)",
|
||||
"fr": "Utiliser l’option par défaut (Oui)",
|
||||
"de": "Standardoption verwenden (Ja)",
|
||||
"it": "Usare l’opzione predefinita (Sì)",
|
||||
"pt": "Usar a opção padrão (Sim)"
|
||||
},
|
||||
"Press 'q' to exit": {
|
||||
"es": "Presione «q» para salir",
|
||||
"fr": "Appuyez sur « q » pour quitter",
|
||||
"de": "Drücken Sie „q“, um zu beenden",
|
||||
"it": "Premi «q» per uscire",
|
||||
"pt": "Pressione «q» para sair"
|
||||
},
|
||||
"If booting in EFI mode with root on LVM: install GRUB for EFI": {
|
||||
"es": "Si inicia en modo EFI con root en LVM: instale GRUB para EFI",
|
||||
"fr": "Si vous démarrez en mode EFI avec root sur LVM : installez GRUB pour EFI",
|
||||
"de": "Wenn Sie im EFI-Modus mit Root auf LVM booten: GRUB für EFI installieren",
|
||||
"it": "Se l’avvio è in modalità EFI con root su LVM: installare GRUB per EFI",
|
||||
"pt": "Se iniciar em modo EFI com root no LVM: instale o GRUB para EFI"
|
||||
},
|
||||
"Per official known issues; ensures proper boot after upgrade": {
|
||||
"es": "Según las incidencias conocidas oficiales, garantiza un arranque correcto tras la actualización",
|
||||
"fr": "Selon les problèmes connus officiels, cela garantit un démarrage correct après la mise à niveau",
|
||||
"de": "Laut den offiziellen Known Issues sorgt dies für einen ordnungsgemäßen Start nach dem Upgrade",
|
||||
"it": "Secondo le note ufficiali dei problemi noti, garantisce un avvio corretto dopo l’aggiornamento",
|
||||
"pt": "De acordo com os problemas conhecidos oficiais, garante uma inicialização correta após a atualização"
|
||||
},
|
||||
"Run checklist again to verify upgrade:": {
|
||||
"es": "Ejecutar de nuevo la lista de verificación para comprobar la actualización:",
|
||||
"fr": "Relancer la liste de vérification pour valider la mise à niveau :",
|
||||
"de": "Checkliste erneut ausführen, um das Upgrade zu verifizieren:",
|
||||
"it": "Eseguire nuovamente la checklist per verificare l’aggiornamento:",
|
||||
"pt": "Executar novamente a lista de verificação para validar a atualização:"
|
||||
},
|
||||
"Should show fewer or no issues": {
|
||||
"es": "Debería mostrar menos problemas o ninguno",
|
||||
"fr": "Devrait afficher moins de problèmes, voire aucun",
|
||||
"de": "Sollte weniger oder keine Probleme anzeigen",
|
||||
"it": "Dovrebbe mostrare meno problemi o nessuno",
|
||||
"pt": "Deve mostrar menos problemas ou nenhum"
|
||||
},
|
||||
"Reboot the system:": {
|
||||
"es": "Reiniciar el sistema:",
|
||||
"fr": "Redémarrer le système :",
|
||||
"de": "System neu starten:",
|
||||
"it": "Riavviare il sistema:",
|
||||
"pt": "Reiniciar o sistema:"
|
||||
},
|
||||
"After reboot, verify PVE version:": {
|
||||
"es": "Después del reinicio, verificar la versión de PVE:",
|
||||
"fr": "Après le redémarrage, vérifier la version de PVE :",
|
||||
"de": "Nach dem Neustart die PVE-Version prüfen:",
|
||||
"it": "Dopo il riavvio, verificare la versione di PVE:",
|
||||
"pt": "Após reiniciar, verificar a versão do PVE:"
|
||||
},
|
||||
"Should show pve-manager/9.x.x": {
|
||||
"es": "Debería mostrar pve-manager/9.x.x",
|
||||
"fr": "Devrait afficher pve-manager/9.x.x",
|
||||
"de": "Sollte pve-manager/9.x.x anzeigen",
|
||||
"it": "Dovrebbe mostrare pve-manager/9.x.x",
|
||||
"pt": "Deve mostrar pve-manager/9.x.x"
|
||||
},
|
||||
"Optional: Modernize repository sources:": {
|
||||
"es": "Opcional: Modernizar las fuentes de repositorio:",
|
||||
"fr": "Optionnel : Moderniser les sources des dépôts :",
|
||||
"de": "Optional: Repository-Quellen modernisieren:",
|
||||
"it": "Opzionale: Modernizzare le fonti del repository:",
|
||||
"pt": "Opcional: Modernizar as fontes de repositório:"
|
||||
},
|
||||
"Converts to deb822; keeps .list backups as .bak": {
|
||||
"es": "Convierte a deb822; mantiene copias .list como .bak",
|
||||
"fr": "Convertit en deb822 ; conserve les sauvegardes .list en .bak",
|
||||
"de": "Konvertiert zu deb822; behält .list-Backups als .bak bei",
|
||||
"it": "Converte in deb822; mantiene i backup .list come .bak",
|
||||
"pt": "Converte para deb822; mantém backups .list como .bak"
|
||||
},
|
||||
"CLUSTER UPGRADE NOTES:": {
|
||||
"es": "NOTAS DE ACTUALIZACIÓN DEL CLÚSTER:",
|
||||
"fr": "NOTES DE MISE À NIVEAU DU CLUSTER :",
|
||||
"de": "HINWEISE ZUM CLUSTER-UPGRADE:",
|
||||
"it": "NOTE DI AGGIORNAMENTO DEL CLUSTER:",
|
||||
"pt": "NOTAS DE ATUALIZAÇÃO DO CLUSTER:"
|
||||
},
|
||||
"Upgrade one node at a time": {
|
||||
"es": "Actualizar un nodo a la vez",
|
||||
"fr": "Mettre à niveau un nœud à la fois",
|
||||
"de": "Jeweils nur einen Knoten aktualisieren",
|
||||
"it": "Aggiornare un nodo alla volta",
|
||||
"pt": "Atualizar um nó por vez"
|
||||
},
|
||||
"Migrate VMs away from node being upgraded": {
|
||||
"es": "Migrar las VMs fuera del nodo que se está actualizando",
|
||||
"fr": "Migrer les VM hors du nœud en cours de mise à niveau",
|
||||
"de": "VMs vom zu aktualisierenden Knoten weg migrieren",
|
||||
"it": "Migrare le VM dal nodo in aggiornamento",
|
||||
"pt": "Migrar as VMs para fora do nó em atualização"
|
||||
},
|
||||
"Wait for each node to complete before starting next": {
|
||||
"es": "Esperar a que cada nodo termine antes de empezar con el siguiente",
|
||||
"fr": "Attendre que chaque nœud soit terminé avant de commencer le suivant",
|
||||
"de": "Warten, bis jeder Knoten abgeschlossen ist, bevor der nächste beginnt",
|
||||
"it": "Attendere che ogni nodo termini prima di iniziare il successivo",
|
||||
"pt": "Aguardar cada nó concluir antes de iniciar o próximo"
|
||||
},
|
||||
"HA groups will be migrated to HA rules automatically": {
|
||||
"es": "Los grupos de HA se migrarán automáticamente a reglas de HA",
|
||||
"fr": "Les groupes HA seront migrés automatiquement vers des règles HA",
|
||||
"de": "HA-Gruppen werden automatisch in HA-Regeln migriert",
|
||||
"it": "I gruppi HA verranno migrati automaticamente a regole HA",
|
||||
"pt": "Grupos de HA serão migrados automaticamente para regras de HA"
|
||||
},
|
||||
"TROUBLESHOOTING:": {
|
||||
"es": "SOLUCIÓN DE PROBLEMAS:",
|
||||
"fr": "DÉPANNAGE :",
|
||||
"de": "FEHLERBEHEBUNG:",
|
||||
"it": "RISOLUZIONE DEI PROBLEMI:",
|
||||
"pt": "SOLUÇÃO DE PROBLEMAS:"
|
||||
},
|
||||
"If upgrade fails:": {
|
||||
"es": "Si la actualización falla:",
|
||||
"fr": "Si la mise à niveau échoue :",
|
||||
"de": "Wenn das Upgrade fehlschlägt:",
|
||||
"it": "Se l’aggiornamento fallisce:",
|
||||
"pt": "Se a atualização falhar:"
|
||||
},
|
||||
"If repositories error:": {
|
||||
"es": "Si hay errores de repositorios:",
|
||||
"fr": "En cas d’erreurs de dépôts :",
|
||||
"de": "Bei Repository-Fehlern:",
|
||||
"it": "In caso di errori dei repository:",
|
||||
"pt": "Se houver erros nos repositórios:"
|
||||
},
|
||||
"If 'proxmox-ve' removal warning:": {
|
||||
"es": "Si aparece advertencia de eliminación de ‘proxmox-ve’:",
|
||||
"fr": "Si un avertissement de suppression de ‘proxmox-ve’ apparaît :",
|
||||
"de": "Bei Warnung zur Entfernung von ‚proxmox-ve‘:",
|
||||
"it": "Se compare un avviso di rimozione di ‘proxmox-ve’:",
|
||||
"pt": "Se aparecer um aviso de remoção de ‘proxmox-ve’:"
|
||||
},
|
||||
"Emergency recovery:": {
|
||||
"es": "Recuperación de emergencia:",
|
||||
"fr": "Récupération d’urgence :",
|
||||
"de": "Notfallwiederherstellung:",
|
||||
"it": "Ripristino di emergenza:",
|
||||
"pt": "Recuperação de emergência:"
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -365,6 +365,22 @@
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Autocaliweb",
|
||||
"slug": "autocaliweb",
|
||||
"desc": "A modern web management system for eBooks, eComics and PDFs",
|
||||
"script": "ct/autocaliweb.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/autocaliweb.sh",
|
||||
"categories": [
|
||||
13
|
||||
],
|
||||
"notes": [],
|
||||
"type": "ct",
|
||||
"default_credentials": {
|
||||
"username": "admin",
|
||||
"password": "admin123"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Baby Buddy",
|
||||
"slug": "babybuddy",
|
||||
@@ -498,7 +514,9 @@
|
||||
"categories": [
|
||||
13
|
||||
],
|
||||
"notes": [],
|
||||
"notes": [
|
||||
"Starting Booklore (Web UI) may take up to 2 minutes after a restart or fresh installation."
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
@@ -773,6 +791,20 @@
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Copyparty",
|
||||
"slug": "copyparty",
|
||||
"desc": "Copyparty is a lightweight, portable HTTP file server with a browser-based interface. It supports drag-and-drop uploads, downloads, deduplication, media playback, and advanced search, making it ideal for quickly sharing and managing files.",
|
||||
"script": "tools/addon/copyparty.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/copyparty.sh",
|
||||
"categories": [
|
||||
11
|
||||
],
|
||||
"notes": [
|
||||
"Execute within the Proxmox shell or in LXC"
|
||||
],
|
||||
"type": "addon"
|
||||
},
|
||||
{
|
||||
"name": "Cosmos",
|
||||
"slug": "cosmos",
|
||||
@@ -899,6 +931,22 @@
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Debian 13",
|
||||
"slug": "debian-13-vm",
|
||||
"desc": "Debian 13 (Trixie) Linux is a distribution that emphasizes free software. It supports many hardware platforms",
|
||||
"script": "vm/debian-13-vm.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-13-vm.sh",
|
||||
"categories": [
|
||||
2
|
||||
],
|
||||
"notes": [
|
||||
"VM has no root password set. To login type in user `root` and just press enter",
|
||||
"After installation, checkout: \u00b4https://github.com/community-scripts/ProxmoxVE/discussions/836\u00b4 for useful Debian commands",
|
||||
"If you use Cloud-init, checkout after installation: \u00b4https://github.com/community-scripts/ProxmoxVE/discussions/272\u00b4"
|
||||
],
|
||||
"type": "vm"
|
||||
},
|
||||
{
|
||||
"name": "Debian 12",
|
||||
"slug": "debian-vm",
|
||||
@@ -1008,7 +1056,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": [
|
||||
@@ -1274,7 +1322,9 @@
|
||||
"categories": [
|
||||
14
|
||||
],
|
||||
"notes": [],
|
||||
"notes": [
|
||||
"Flaresolverr is pinned to Version 3.3.25 because they add an breaking python package which doesn't work with debian 12.`"
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
@@ -1461,8 +1511,7 @@
|
||||
9
|
||||
],
|
||||
"notes": [
|
||||
"Execute within an existing LXC Console",
|
||||
"WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing."
|
||||
"Execute within an existing LXC Console (Debian / Ubuntu / Alpine supported)"
|
||||
],
|
||||
"type": "addon"
|
||||
},
|
||||
@@ -1643,6 +1692,21 @@
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Healthchecks",
|
||||
"slug": "healthchecks",
|
||||
"desc": "Healthchecks is a cron job monitoring service. It listens for HTTP requests and email messages (\"pings\") from your cron jobs and scheduled tasks (\"checks\"). When a ping does not arrive on time, Healthchecks sends out alerts. Healthchecks comes with a web dashboard, API, 25+ integrations for delivering notifications, monthly email reports, WebAuthn 2FA support, team management features: projects, team members, read-only access.",
|
||||
"script": "ct/healthchecks.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/healthchecks.sh",
|
||||
"categories": [
|
||||
9
|
||||
],
|
||||
"notes": [
|
||||
"if you change your LXC-IP, you need to update /etc/caddy/Caddyfile & /opt/healthchecks/hc/local_settings.py",
|
||||
"Show credentials: `cat ~/healthchecks.creds`"
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Heimdall Dashboard",
|
||||
"slug": "heimdall-dashboard",
|
||||
@@ -1696,23 +1760,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",
|
||||
@@ -1787,6 +1834,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",
|
||||
@@ -1848,6 +1909,7 @@
|
||||
13
|
||||
],
|
||||
"notes": [
|
||||
"Please be aware that Immich releases are pinned to specific versions until compatibility has been confirmed by the Community Scripts maintainers; as a result, the version installed by the helper script may not be the most current version of Immich",
|
||||
"During installation, you will be prompted with the option to install Intel OpenVINO for hardware-accelerated machine-learning. If you opt in, increase your LXC RAM after installation, as OpenVINO is memory-intensive",
|
||||
"HW-accelerated video transcoding is supported, but must be enabled in Immich Settings",
|
||||
"To change upload location, edit 'IMMICH_MEDIA_LOCATION' in `/opt/immich/.env`, and create the symlink 'upload' in /opt/immich/app & /opt/immich/app/machine-learning to your new upload location",
|
||||
@@ -2010,7 +2072,7 @@
|
||||
"script": "ct/jenkins.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/jenkins.sh",
|
||||
"categories": [
|
||||
22
|
||||
20
|
||||
],
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
@@ -2065,7 +2127,7 @@
|
||||
"notes": [
|
||||
"WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing.",
|
||||
"Kasm needs swap (on Proxmox host) and activated FUSE to be installed successfully!",
|
||||
"Show password: `cat ~/kasm.creds`"
|
||||
"Show credentials: `cat ~/kasm.creds`"
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
@@ -2122,7 +2184,6 @@
|
||||
],
|
||||
"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`",
|
||||
"When updating, if you had modified cache-ispn.xml: Re-apply your changes to the new file, otherwise leave it unchanged."
|
||||
],
|
||||
"type": "ct",
|
||||
@@ -2198,7 +2259,7 @@
|
||||
3
|
||||
],
|
||||
"notes": [
|
||||
"After the initial installation: Enter your desired admin user and password and then click on Sign Up"
|
||||
"For admin username and password type `cat ~/komodo.creds` inside LXC."
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
@@ -2226,6 +2287,18 @@
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Leantime",
|
||||
"slug": "leantime",
|
||||
"desc": "Leantime is a goals focused project management system for non-project managers. Building with ADHD, Autism, and dyslexia in mind. ",
|
||||
"script": "ct/leantime.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/leantime.sh",
|
||||
"categories": [
|
||||
12
|
||||
],
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Librespeed Rust",
|
||||
"slug": "librespeed-rust",
|
||||
@@ -2304,6 +2377,24 @@
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "LiteLLM",
|
||||
"slug": "litellm",
|
||||
"desc": "LLM proxy to call 100+ LLMs in a unified interface & track spend, set budgets per virtual key/user",
|
||||
"script": "ct/litellm.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/litellm.sh",
|
||||
"categories": [
|
||||
20
|
||||
],
|
||||
"notes": [
|
||||
"Update master key in the config file"
|
||||
],
|
||||
"type": "ct",
|
||||
"default_credentials": {
|
||||
"username": "admin",
|
||||
"password": "sk-1234"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "lldap",
|
||||
"slug": "lldap",
|
||||
@@ -2454,6 +2545,26 @@
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "MediaManager",
|
||||
"slug": "mediamanager",
|
||||
"desc": "A modern selfhosted media management system for your media library",
|
||||
"script": "ct/mediamanager.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/mediamanager.sh",
|
||||
"categories": [
|
||||
14,
|
||||
13
|
||||
],
|
||||
"notes": [
|
||||
"During the installation, provide the email address of the first admin user",
|
||||
"You're probably going to want to use a bind mount for the media directories"
|
||||
],
|
||||
"type": "ct",
|
||||
"default_credentials": {
|
||||
"username": "<email address>",
|
||||
"password": "admin"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "MediaMTX",
|
||||
"slug": "mediamtx",
|
||||
@@ -2939,11 +3050,11 @@
|
||||
"notes": [
|
||||
"This uses Docker under the hood, as this can not easily be installed bare-metal. ",
|
||||
"The initial starting process can be take 1-2min. ",
|
||||
"Application credentials: `cat /opt/.npm_pwd`"
|
||||
"Application credentials: `cat /opt/.npm_pwd` - if file not exist in LXC check docker logs for password with `docker logs npmplus`"
|
||||
],
|
||||
"type": "ct",
|
||||
"default_credentials": {
|
||||
"username": "root",
|
||||
"username": "admin@example.org",
|
||||
"password": null
|
||||
}
|
||||
},
|
||||
@@ -3207,7 +3318,9 @@
|
||||
4,
|
||||
2
|
||||
],
|
||||
"notes": [],
|
||||
"notes": [
|
||||
"If you use VLANs (default LAN is set to VLAN 999), make sure the Proxmox Linux Bridge is configured as VLAN-aware, otherwise the VM may fail to start."
|
||||
],
|
||||
"type": "vm"
|
||||
},
|
||||
{
|
||||
@@ -3326,6 +3439,22 @@
|
||||
"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": [
|
||||
"This LXC is very memory-hungry when updating; it requires at least 6GB RAM, but RAM may be reduced to as low as 2GB when running normally",
|
||||
"To use a bind mount for storage, create symlinks to your mount for both `uploads` and `temp-uploads` in `/opt/palmr_data`",
|
||||
"To use Palmr with a reverse proxy, uncomment `SECURE_SITE` in `/opt/palmr/apps/server/.env`"
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "PaperlessAI",
|
||||
"slug": "paperless-ai",
|
||||
@@ -3362,7 +3491,7 @@
|
||||
12
|
||||
],
|
||||
"notes": [
|
||||
"Show Login Credentials, type `update` in the LXC console",
|
||||
"Show Login Credentials, type `cat ~/paperless-ngx.creds` in the LXC console",
|
||||
"Script installs English as default OCR language. To install additional languages, use `apt-get install tesseract-ocr-[lang]`, where [lang] is the language code (e.g. `apt-get install tesseract-ocr-deu`)."
|
||||
],
|
||||
"type": "ct"
|
||||
@@ -3414,6 +3543,23 @@
|
||||
],
|
||||
"type": "pve"
|
||||
},
|
||||
{
|
||||
"name": "PBS 4 Upgrade",
|
||||
"slug": "pbs4-upgrade",
|
||||
"desc": "This script guides you through upgrading Proxmox Backup Server from version 3.x (Debian 12 Bookworm) to version 4.0 (Debian 13 Trixie). It adjusts the Debian base sources, configures PBS 4 repositories in deb822 format, updates enterprise/no-subscription/test repos, runs a full system upgrade, and finalizes with a reboot.",
|
||||
"script": "tools/pve/pbs4-upgrade.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/pbs4-upgrade.sh",
|
||||
"categories": [
|
||||
1
|
||||
],
|
||||
"notes": [
|
||||
"Execute this script directly on the PBS 3.x host as root.",
|
||||
"Ensure you have a verified backup of /etc/proxmox-backup before starting.",
|
||||
"Do not run this on an already upgraded PBS 4.x system.",
|
||||
"A reboot is strongly recommended after upgrade to activate the new kernel and services."
|
||||
],
|
||||
"type": "pve"
|
||||
},
|
||||
{
|
||||
"name": "PeaNUT",
|
||||
"slug": "peanut",
|
||||
@@ -3823,7 +3969,7 @@
|
||||
],
|
||||
"notes": [
|
||||
"Set a root password if using autologin. This will be the PBS password. `passwd root`",
|
||||
"Advanced Install is only possible without root password and root SSH access, you can configure this after installation."
|
||||
"Advanced Install is only possible with disabled IPV6! Otherwise the installation sometimes stuck."
|
||||
],
|
||||
"type": "ct",
|
||||
"default_credentials": {
|
||||
@@ -3917,7 +4063,8 @@
|
||||
],
|
||||
"notes": [
|
||||
"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"
|
||||
"After installation, access the web interface to configure your Proxmox connection details through the built-in setup wizard",
|
||||
"Configure authentication in the Web UI => Settings => Security"
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
@@ -3963,7 +4110,11 @@
|
||||
18
|
||||
],
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
"type": "ct",
|
||||
"default_credentials": {
|
||||
"username": "proxmox",
|
||||
"password": "proxmox"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Radarr",
|
||||
@@ -4069,6 +4220,32 @@
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Redlib",
|
||||
"slug": "alpine-redlib",
|
||||
"desc": "An alternative private front-end to Reddit. Redlib hopes to provide an easier way to browse Reddit, without the ads, trackers, and bloat.",
|
||||
"script": "ct/alpine-redlib.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/alpine-redlib.sh",
|
||||
"categories": [
|
||||
10
|
||||
],
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Resilio Sync",
|
||||
"slug": "resiliosync",
|
||||
"desc": "Fast, reliable, and simple file sync and share solution, powered by P2P technology. Sync files across all your devices without storing them in the cloud.",
|
||||
"script": "ct/resilio-sync.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/resilio-sync.sh",
|
||||
"categories": [
|
||||
11
|
||||
],
|
||||
"notes": [
|
||||
"After free registration, you will receive a license keyfile to your email address. Upload it into any LXC directory and select on first run."
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "RevealJS",
|
||||
"slug": "revealjs",
|
||||
@@ -4108,7 +4285,8 @@
|
||||
],
|
||||
"notes": [
|
||||
"Check our configuration guide for help: `https://github.com/community-scripts/ProxmoxVE/discussions/2388`",
|
||||
"Login credentials: `cat ~/rustdesk.creds`"
|
||||
"To set admin password on Debian, type `cd /var/lib/rustdesk-api && rustdesk-api reset-admin-pwd <yournewpasswordhere>` inside LXC.",
|
||||
"To see admin password on Alpine, type `cat ~/rustdesk.creds` inside LXC."
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
@@ -4150,6 +4328,18 @@
|
||||
],
|
||||
"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",
|
||||
@@ -4356,6 +4546,23 @@
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Swizzin",
|
||||
"slug": "swizzin",
|
||||
"desc": "Swizzin is a light-weight, modular, and user-friendly seedbox solution for Debian-based servers. It allows for the easy installation and management of a wide variety of applications commonly used for torrenting and media management, such as rTorrent, Sonarr, Radarr, and Plex, all accessible through a command-line utility or a web-based dashboard.",
|
||||
"script": "ct/swizzin.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/swizzin.sh",
|
||||
"categories": [
|
||||
13
|
||||
],
|
||||
"notes": [
|
||||
"Installation might take a long time if choosing to install many apps. Be patient.",
|
||||
"Swizzin is a management suite, not a single application. Use the 'box' command inside the container to install/manage individual apps like rTorrent, Sonarr, etc. A full list can be found in documentation.",
|
||||
"It is very recommended to install at least the 'panel' for web access, and 'nginx' for easy access to other apps.",
|
||||
"WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing."
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Syncthing",
|
||||
"slug": "syncthing",
|
||||
@@ -4454,9 +4661,7 @@
|
||||
"categories": [
|
||||
5
|
||||
],
|
||||
"notes": [
|
||||
"WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing."
|
||||
],
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
@@ -4527,6 +4732,20 @@
|
||||
"notes": [],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Tracktor",
|
||||
"slug": "tracktor",
|
||||
"desc": "Tracktor is an open-source web application for comprehensive vehicle management.\nEasily track fuel consumption, maintenance, insurance, and regulatory documents for all your vehicles in one place.",
|
||||
"script": "ct/tracktor.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/tracktor.sh",
|
||||
"categories": [
|
||||
9
|
||||
],
|
||||
"notes": [
|
||||
"Please check and update the '/opt/tracktor/app/backend/.env' file if using behind reverse proxy."
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "Traefik",
|
||||
"slug": "traefik",
|
||||
@@ -4596,6 +4815,21 @@
|
||||
],
|
||||
"type": "turnkey"
|
||||
},
|
||||
{
|
||||
"name": "twingate-connector",
|
||||
"slug": "twingate-connector",
|
||||
"desc": "Twingate Connectors are lightweight software components that establish secure, least-privileged access between private network resources and authorized users without exposing the network to the internet. They act as outbound-only bridges between your protected resources and the Twingate infrastructure, ensuring zero-trust access without the need for a VPN.",
|
||||
"script": "ct/twingate-connector.sh",
|
||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/twingate-connector.sh",
|
||||
"categories": [
|
||||
4
|
||||
],
|
||||
"notes": [
|
||||
"You can get your Twingate access or refresh tokens from the Twingate Admin Console. `https://auth.twingate.com/signup-v2`",
|
||||
"If you need to update your access or refresh tokens, they can be found in /etc/twingate/connector.conf"
|
||||
],
|
||||
"type": "ct"
|
||||
},
|
||||
{
|
||||
"name": "TypeSense",
|
||||
"slug": "typesense",
|
||||
@@ -5010,7 +5244,8 @@
|
||||
4
|
||||
],
|
||||
"notes": [
|
||||
"Wireguard and WGDashboard are not the same. More info: `https://docs.wgdashboard.dev/what-is-wireguard-what-is-wgdashboard.html`"
|
||||
"Wireguard and WGDashboard are not the same. More info: `https://docs.wgdashboard.dev/what-is-wireguard-what-is-wgdashboard.html`",
|
||||
"WGDashboard installation is optional.`"
|
||||
],
|
||||
"type": "ct",
|
||||
"default_credentials": {
|
||||
|
1294
scripts/backup_restore/backup_host4.sh
Normal file
1294
scripts/backup_restore/backup_host4.sh
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
|
||||
# Version : 1.0
|
||||
# Last Updated: 28/01/2025
|
||||
# Version : 1.1
|
||||
# Last Updated: 17/08/2025
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# This script automates the process of enabling and configuring Intel Integrated GPU (iGPU) support in Proxmox VE LXC containers.
|
||||
@@ -32,7 +32,7 @@ initialize_cache
|
||||
|
||||
|
||||
|
||||
# Select LXC container
|
||||
|
||||
select_container() {
|
||||
|
||||
CONTAINERS=$(pct list | awk 'NR>1 {print $1, $3}' | xargs -n2)
|
||||
@@ -59,14 +59,14 @@ select_container() {
|
||||
|
||||
|
||||
|
||||
# Validate that the selected container is valid
|
||||
|
||||
validate_container_id() {
|
||||
if [ -z "$CONTAINER_ID" ]; then
|
||||
msg_error "$(translate 'Container ID not defined. Make sure to select a container first.')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if the container is running and stop it before configuration
|
||||
|
||||
if pct status "$CONTAINER_ID" | grep -q "running"; then
|
||||
msg_info "$(translate 'Stopping the container before applying configuration...')"
|
||||
pct stop "$CONTAINER_ID"
|
||||
@@ -76,77 +76,103 @@ validate_container_id() {
|
||||
|
||||
|
||||
|
||||
# Configure LXC for Coral TPU and iGPU
|
||||
configure_lxc_for_igpu() {
|
||||
validate_container_id
|
||||
|
||||
CONFIG_FILE="/etc/pve/lxc/${CONTAINER_ID}.conf"
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
msg_error "$(translate 'Configuration file for container') $CONTAINER_ID $(translate 'not found.')"
|
||||
exit 1
|
||||
[[ -f "$CONFIG_FILE" ]] || { msg_error "$(translate 'Configuration file for container') $CONTAINER_ID $(translate 'not found.')"; exit 1; }
|
||||
|
||||
|
||||
if [[ ! -d /dev/dri ]]; then
|
||||
modprobe i915 2>/dev/null || true
|
||||
for _ in {1..5}; do
|
||||
[[ -d /dev/dri ]] && break
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
||||
if grep -q "^unprivileged: 1" "$CONFIG_FILE"; then
|
||||
msg_info "$(translate 'The container is unprivileged. Changing to privileged...')"
|
||||
sed -i "s/^unprivileged: 1/unprivileged: 0/" "$CONFIG_FILE"
|
||||
STORAGE_TYPE=$(pct config "$CONTAINER_ID" | grep "^rootfs:" | awk -F, '{print $2}' | cut -d'=' -f2)
|
||||
if [[ "$STORAGE_TYPE" == "dir" ]]; then
|
||||
STORAGE_PATH=$(pct config "$CONTAINER_ID" | grep "^rootfs:" | awk '{print $2}' | cut -d',' -f1)
|
||||
chown -R root:root "$STORAGE_PATH"
|
||||
fi
|
||||
msg_ok "$(translate 'Container changed to privileged.')"
|
||||
CT_TYPE=$(pct config "$CONTAINER_ID" | awk '/^unprivileged:/ {print $2}')
|
||||
[[ -z "$CT_TYPE" ]] && CT_TYPE="0"
|
||||
|
||||
msg_info "$(translate 'Configuring Intel iGPU passthrough for container...')"
|
||||
|
||||
for rn in /dev/dri/renderD*; do
|
||||
[[ -e "$rn" ]] || continue
|
||||
chmod 660 "$rn" 2>/dev/null || true
|
||||
chgrp render "$rn" 2>/dev/null || true
|
||||
done
|
||||
|
||||
mapfile -t RENDER_NODES < <(find /dev/dri -maxdepth 1 -type c -name 'renderD*' 2>/dev/null || true)
|
||||
mapfile -t CARD_NODES < <(find /dev/dri -maxdepth 1 -type c -name 'card*' 2>/dev/null || true)
|
||||
FB_NODE=""
|
||||
[[ -e /dev/fb0 ]] && FB_NODE="/dev/fb0"
|
||||
|
||||
if [[ ${#RENDER_NODES[@]} -eq 0 && ${#CARD_NODES[@]} -eq 0 && -z "$FB_NODE" ]]; then
|
||||
msg_warn "$(translate 'No VA-API devices found on host (/dev/dri*, /dev/fb0). Is i915 loaded?')"
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
if grep -q "^lxc.apparmor.profile" "$CONFIG_FILE"; then
|
||||
msg_info "$(translate 'Disabling AppArmor profile to avoid conflicts...')"
|
||||
sed -i "/^lxc.apparmor.profile/d" "$CONFIG_FILE"
|
||||
msg_ok "$(translate 'AppArmor profile removed.')"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Configure iGPU
|
||||
if ! grep -q "features: nesting=1" "$CONFIG_FILE"; then
|
||||
if grep -q '^features:' "$CONFIG_FILE"; then
|
||||
grep -Eq '^features:.*(^|,)\s*nesting=1(\s|,|$)' "$CONFIG_FILE" || sed -i 's/^features:\s*/&nesting=1, /' "$CONFIG_FILE"
|
||||
else
|
||||
echo "features: nesting=1" >> "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
if ! grep -q "c 226:0 rwm" "$CONFIG_FILE"; then
|
||||
echo "lxc.cgroup2.devices.allow: c 226:0 rwm # iGPU" >> "$CONFIG_FILE"
|
||||
echo "lxc.cgroup2.devices.allow: c 226:128 rwm # iGPU" >> "$CONFIG_FILE"
|
||||
|
||||
|
||||
if [[ "$CT_TYPE" == "0" ]]; then
|
||||
|
||||
sed -i '/^lxc\.cgroup2\.devices\.allow:\s*c\s*226:/d' "$CONFIG_FILE"
|
||||
sed -i '\|^lxc\.mount\.entry:\s*/dev/dri|d' "$CONFIG_FILE"
|
||||
sed -i '\|^lxc\.mount\.entry:\s*/dev/fb0|d' "$CONFIG_FILE"
|
||||
|
||||
echo "lxc.cgroup2.devices.allow: c 226:* rwm" >> "$CONFIG_FILE"
|
||||
echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >> "$CONFIG_FILE"
|
||||
echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >> "$CONFIG_FILE"
|
||||
[[ -n "$FB_NODE" ]] && echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >> "$CONFIG_FILE"
|
||||
|
||||
|
||||
else
|
||||
sed -i '/^dev[0-9]\+:/d' "$CONFIG_FILE"
|
||||
|
||||
idx=0
|
||||
for c in "${CARD_NODES[@]}"; do
|
||||
echo "dev${idx}: $c,gid=44" >> "$CONFIG_FILE"
|
||||
idx=$((idx+1))
|
||||
done
|
||||
for r in "${RENDER_NODES[@]}"; do
|
||||
echo "dev${idx}: $r,gid=104" >> "$CONFIG_FILE"
|
||||
idx=$((idx+1))
|
||||
done
|
||||
|
||||
fi
|
||||
msg_ok "$(translate 'iGPU configuration added to container') $CONTAINER_ID."
|
||||
|
||||
if ! grep -q "c 29:0 rwm # Framebuffer" "$CONFIG_FILE"; then
|
||||
echo "lxc.cgroup2.devices.allow: c 29:0 rwm # Framebuffer" >> "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
if ! grep -q "lxc.mount.entry: /dev/fb0" "$CONFIG_FILE"; then
|
||||
echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >> "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
|
||||
msg_ok "$(translate 'Coral TPU and iGPU configuration added to container') $CONTAINER_ID."
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Install iGPU drivers in the container
|
||||
|
||||
|
||||
install_igpu_in_container() {
|
||||
|
||||
msg_info2 "$(translate 'Installing iGPU drivers inside the container...')"
|
||||
tput sc
|
||||
LOG_FILE=$(mktemp)
|
||||
|
||||
msg_info "$(translate 'Installing iGPU drivers...')"
|
||||
|
||||
pct start "$CONTAINER_ID" >/dev/null 2>&1
|
||||
|
||||
script -q -c "pct exec \"$CONTAINER_ID\" -- bash -c '
|
||||
set -e
|
||||
getent group video >/dev/null || groupadd -g 44 video
|
||||
getent group render >/dev/null || groupadd -g 104 render
|
||||
usermod -aG video,render root || true
|
||||
|
||||
apt-get update >/dev/null 2>&1
|
||||
apt-get install -y va-driver-all ocl-icd-libopencl1 intel-opencl-icd vainfo intel-gpu-tools
|
||||
chgrp video /dev/dri && chmod 755 /dev/dri
|
||||
adduser root video && adduser root render
|
||||
|
||||
chgrp video /dev/dri 2>/dev/null || true
|
||||
chmod 755 /dev/dri 2>/dev/null || true
|
||||
'" "$LOG_FILE"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
@@ -167,6 +193,7 @@ install_igpu_in_container() {
|
||||
|
||||
select_container
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Add HW iGPU acceleration to an LXC")"
|
||||
configure_lxc_for_igpu
|
||||
install_igpu_in_container
|
||||
|
||||
|
@@ -187,7 +187,12 @@ is_disk_in_use() {
|
||||
|
||||
FREE_DISKS=()
|
||||
|
||||
LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -n1 readlink -f | sort -u)
|
||||
LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -r -n1 readlink -f | sort -u)
|
||||
|
||||
if [[ -n "$LVM_DEVICES" ]] && echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then
|
||||
IS_MOUNTED=true
|
||||
fi
|
||||
|
||||
RAID_ACTIVE=$(grep -Po 'md\d+\s*:\s*active\s+raid[0-9]+' /proc/mdstat | awk '{print $1}' | sort -u)
|
||||
|
||||
while read -r DISK; do
|
||||
|
@@ -92,6 +92,9 @@ cleanup_duplicate_repos_pve9() {
|
||||
local cleaned_count=0
|
||||
declare -A seen_repos
|
||||
|
||||
if [ ! -s "$sources_file" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# Remove Subscription Banner - Proxmox VE 9.x ONLY
|
||||
# Remove Subscription Banner - Proxmox VE 9.x
|
||||
# ==========================================================
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
@@ -15,7 +15,7 @@ fi
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
# Tool registration system
|
||||
|
||||
ensure_tools_json() {
|
||||
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
|
||||
}
|
||||
@@ -33,7 +33,7 @@ remove_subscription_banner_pve9() {
|
||||
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)
|
||||
|
||||
@@ -44,45 +44,49 @@ remove_subscription_banner_pve9() {
|
||||
|
||||
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
|
||||
|
||||
[[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE"
|
||||
[[ -f "$MIN_JS_FILE" ]] && rm -f "$MIN_JS_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
|
||||
find /var/cache/nginx/ -type f -delete 2>/dev/null || true
|
||||
|
||||
|
||||
# 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"
|
||||
if grep -q "res\.data\.status\.toLowerCase() !== 'active'" "$JS_FILE"; then
|
||||
msg_warn "Some patches may not have applied correctly, retrying..."
|
||||
sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE"
|
||||
fi
|
||||
|
||||
# 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 {
|
||||
@@ -96,7 +100,7 @@ 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"
|
||||
@@ -105,15 +109,16 @@ EOF
|
||||
fi
|
||||
|
||||
|
||||
msg_ok "Subscription banner removed successfully for Proxmox VE $pve_version"
|
||||
msg_ok "Banner removal process completed"
|
||||
|
||||
systemctl reload nginx 2>/dev/null || true
|
||||
|
||||
msg_ok "Subscription banner removed successfully for Proxmox VE $pve_version"
|
||||
msg_ok "Banner removal process completed - refresh your browser to see changes"
|
||||
|
||||
register_tool "subscription_banner" true
|
||||
}
|
||||
|
||||
|
||||
# Execute function if called directly
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
remove_subscription_banner_pve9
|
||||
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
|
901
scripts/global/share-common.func
Normal file
901
scripts/global/share-common.func
Normal file
@@ -0,0 +1,901 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==========================================================
|
||||
# ProxMenux - Global Share Functions (reusable)
|
||||
# File: scripts/global/share_common.func
|
||||
# ==========================================================
|
||||
|
||||
|
||||
|
||||
if [[ -n "${__PROXMENUX_SHARE_COMMON__}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
__PROXMENUX_SHARE_COMMON__=1
|
||||
|
||||
|
||||
: "${PROXMENUX_DEFAULT_SHARE_GROUP:=sharedfiles}"
|
||||
|
||||
|
||||
: "${PROXMENUX_SHARE_MAP_DB:=/usr/local/share/proxmenux/share-map.db}"
|
||||
|
||||
|
||||
mkdir -p "$(dirname "$PROXMENUX_SHARE_MAP_DB")" 2>/dev/null || true
|
||||
touch "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null || true
|
||||
|
||||
|
||||
pmx_share_map_get() {
|
||||
|
||||
local key="$1"
|
||||
awk -F'=' -v k="$key" '$1==k {print $2}' "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null | tail -n1
|
||||
}
|
||||
|
||||
|
||||
pmx_share_map_set() {
|
||||
|
||||
local key="$1" val="$2"
|
||||
|
||||
sed -i "\|^${key}=|d" "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null || true
|
||||
echo "${key}=${val}" >> "$PROXMENUX_SHARE_MAP_DB"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pmx_choose_or_create_group() {
|
||||
local default_group="${1:-$PROXMENUX_DEFAULT_SHARE_GROUP}"
|
||||
local choice group_name groups menu_args gid_min
|
||||
|
||||
|
||||
gid_min="$(awk '/^\s*GID_MIN\s+[0-9]+/ {print $2}' /etc/login.defs 2>/dev/null | tail -n1)"
|
||||
[[ -z "$gid_min" ]] && gid_min=1000
|
||||
|
||||
choice=$(whiptail --title "$(translate "Shared Group")" \
|
||||
--menu "$(translate "Choose a group policy for this shared directory:")" 18 78 6 \
|
||||
"1" "$(translate "Use default group:") $default_group $(translate "(recommended)")" \
|
||||
"2" "$(translate "Create a new group for isolation")" \
|
||||
"3" "$(translate "Select an existing group")" \
|
||||
3>&1 1>&2 2>&3) || { echo ""; return 1; }
|
||||
|
||||
case "$choice" in
|
||||
1)
|
||||
|
||||
pmx_ensure_host_group "$default_group" >/dev/null || { echo ""; return 1; }
|
||||
echo "$default_group"
|
||||
;;
|
||||
|
||||
2)
|
||||
group_name=$(whiptail --inputbox "$(translate "Enter new group name:")" 10 70 "sharedfiles-project" \
|
||||
--title "$(translate "New Group")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
|
||||
|
||||
if [[ -z "$group_name" ]]; then
|
||||
msg_error "$(translate "Group name cannot be empty.")"
|
||||
echo ""; return 1
|
||||
fi
|
||||
|
||||
if ! [[ "$group_name" =~ ^[a-zA-Z_][a-zA-Z0-9_-]*$ ]]; then
|
||||
msg_error "$(translate "Invalid group name. Use letters, digits, underscore or hyphen, and start with a letter or underscore.")"
|
||||
echo ""; return 1
|
||||
fi
|
||||
|
||||
pmx_ensure_host_group "$group_name" >/dev/null || { echo ""; return 1; }
|
||||
echo "$group_name"
|
||||
;;
|
||||
|
||||
3)
|
||||
|
||||
groups=$(getent group | awk -F: -v MIN="$gid_min" '
|
||||
$3 >= MIN && $1 != "nogroup" && $1 !~ /^pve/ {print $0}
|
||||
' | sort -t: -k1,1)
|
||||
|
||||
if [[ -z "$groups" ]]; then
|
||||
whiptail --title "$(translate "Groups")" --msgbox "$(translate "No user groups found.")" 8 60
|
||||
echo ""; return 1
|
||||
fi
|
||||
|
||||
menu_args=()
|
||||
while IFS=: read -r gname _ gid members; do
|
||||
menu_args+=("$gname" "GID=$gid")
|
||||
done <<< "$groups"
|
||||
|
||||
group_name=$(whiptail --title "$(translate "Existing Groups")" \
|
||||
--menu "$(translate "Select an existing group:")" 20 70 12 \
|
||||
"${menu_args[@]}" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
|
||||
|
||||
|
||||
pmx_ensure_host_group "$group_name" >/dev/null || { echo ""; return 1; }
|
||||
echo "$group_name"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo ""; return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pmx_ensure_host_group() {
|
||||
local group_name="$1"
|
||||
local suggested_gid="${2:-}"
|
||||
local base_gid=101000
|
||||
local new_gid gid
|
||||
|
||||
|
||||
if getent group "$group_name" >/dev/null 2>&1; then
|
||||
gid="$(getent group "$group_name" | cut -d: -f3)"
|
||||
echo "$gid"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -n "$suggested_gid" ]]; then
|
||||
|
||||
if getent group "$suggested_gid" >/dev/null 2>&1; then
|
||||
msg_error "$(translate "GID already in use:") $suggested_gid"
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
if ! groupadd -g "$suggested_gid" "$group_name" >/dev/null 2>&1; then
|
||||
msg_error "$(translate "Failed to create group:") $group_name"
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
msg_ok "$(translate "Group created:") $group_name"
|
||||
else
|
||||
|
||||
new_gid="$base_gid"
|
||||
while getent group "$new_gid" >/dev/null 2>&1; do
|
||||
new_gid=$((new_gid+1))
|
||||
done
|
||||
if ! groupadd -g "$new_gid" "$group_name" >/dev/null 2>&1; then
|
||||
msg_error "$(translate "Failed to create group:") $group_name"
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
msg_ok "$(translate "Group created:") $group_name"
|
||||
fi
|
||||
|
||||
gid="$(getent group "$group_name" | cut -d: -f3)"
|
||||
if [[ -z "$gid" ]]; then
|
||||
msg_error "$(translate "Failed to resolve group GID for") $group_name"
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$gid"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pmx_prepare_host_shared_dir() {
|
||||
|
||||
local dir="$1" group_name="$2"
|
||||
[[ -z "$dir" || -z "$group_name" ]] && { msg_error "$(translate "Internal error: missing arguments in pmx_prepare_host_shared_dir")"; return 1; }
|
||||
|
||||
if [[ ! -d "$dir" ]]; then
|
||||
if mkdir -p "$dir" 2>/dev/null; then
|
||||
msg_ok "$(translate "Created directory on host:") $dir"
|
||||
else
|
||||
msg_error "$(translate "Failed to create directory on host:") $dir"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
chown -R root:"$group_name" "$dir" 2>/dev/null || true
|
||||
chmod -R 2775 "$dir" 2>/dev/null || true
|
||||
|
||||
if command -v setfacl >/dev/null 2>&1; then
|
||||
setfacl -R -m d:g:"$group_name":rwx -m d:o::rx -m g:"$group_name":rwx "$dir" 2>/dev/null || true
|
||||
msg_ok "$(translate "Default ACLs applied for group inheritance.")"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pmx_select_host_mount_point() {
|
||||
local title="${1:-$(translate "Select Mount Point")}"
|
||||
local default_path="${2:-/mnt/shared}"
|
||||
local context="${3:-local}"
|
||||
local choice folder_name result existing_dirs mount_point
|
||||
|
||||
while true; do
|
||||
choice=$(whiptail --title "$title" --menu "$(translate "Where do you want the host folder?")" 16 76 3 \
|
||||
"1" "$(translate "Create new folder in /mnt")" \
|
||||
"2" "$(translate "Enter custom pathr")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
|
||||
|
||||
case "$choice" in
|
||||
1)
|
||||
folder_name=$(whiptail --inputbox "$(translate "Enter folder name for /mnt:")" 10 70 "$(basename "$default_path")" --title "$(translate "Folder Name")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
|
||||
[[ -z "$folder_name" ]] && continue
|
||||
mount_point="/mnt/$folder_name"
|
||||
echo "$mount_point"; return 0
|
||||
;;
|
||||
|
||||
2)
|
||||
result=$(whiptail --inputbox "$(translate "Enter full path:")" 10 80 "$default_path" --title "$(translate "Custom Path")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
|
||||
[[ -z "$result" ]] && continue
|
||||
echo "$result"; return 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
select_host_directory_() {
|
||||
local method choice result
|
||||
|
||||
method=$(whiptail --title "$(translate "Select Host Directory")" --menu "$(translate "How do you want to select the HOST folder to mount?")" 15 70 4 \
|
||||
"mnt" "$(translate "Select from /mnt directories")" \
|
||||
"manual" "$(translate "Enter path manually")" 3>&1 1>&2 2>&3) || return 1
|
||||
|
||||
case "$method" in
|
||||
mnt|srv|media)
|
||||
local base_path="/$method"
|
||||
local host_dirs=("$base_path"/*)
|
||||
local options=()
|
||||
|
||||
for dir in "${host_dirs[@]}"; do
|
||||
if [[ -d "$dir" ]]; then
|
||||
options+=("$dir" "$(basename "$dir")")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#options[@]} -eq 0 ]]; then
|
||||
msg_error "$(translate "No directories found in") $base_path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
result=$(whiptail --title "$(translate "Select Host Folder")" \
|
||||
--menu "$(translate "Select the folder to mount:")" 20 80 10 "${options[@]}" 3>&1 1>&2 2>&3)
|
||||
;;
|
||||
manual)
|
||||
|
||||
result=$(whiptail --title "$(translate "Enter Path")" \
|
||||
--inputbox "$(translate "Enter the full path to the host folder:")" 10 70 "/mnt/" 3>&1 1>&2 2>&3)
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -z "$result" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$result" ]]; then
|
||||
msg_error "$(translate "The selected path is not a valid directory:") $result"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
select_host_directory__() {
|
||||
local method result
|
||||
|
||||
method=$(whiptail --title "$(translate "Select Host Directory")" \
|
||||
--menu "$(translate "How do you want to select the HOST folder to mount?")" 15 70 4 \
|
||||
"mnt" "$(translate "Select from /mnt directories")" \
|
||||
"manual" "$(translate "Enter path manually")" \
|
||||
3>&1 1>&2 2>&3) || return 1
|
||||
|
||||
case "$method" in
|
||||
mnt|srv|media)
|
||||
local base_path="/$method"
|
||||
local host_dirs=("$base_path"/*)
|
||||
local options=()
|
||||
|
||||
for dir in "${host_dirs[@]}"; do
|
||||
[[ -d "$dir" ]] && options+=("$dir" "$(basename "$dir")")
|
||||
done
|
||||
|
||||
if [[ ${#options[@]} -eq 0 ]]; then
|
||||
msg_error "$(translate "No directories found in") $base_path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
result=$(whiptail --title "$(translate "Select Host Folder")" \
|
||||
--menu "$(translate "Select the folder to mount:")" 20 80 10 \
|
||||
"${options[@]}" 3>&1 1>&2 2>&3) || return 1
|
||||
;;
|
||||
manual)
|
||||
result=$(whiptail --title "$(translate "Enter Path")" \
|
||||
--inputbox "$(translate "Enter the full path to the host folder:")" \
|
||||
10 70 "/mnt/" 3>&1 1>&2 2>&3) || return 1
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
[[ -z "$result" ]] && return 1
|
||||
[[ ! -d "$result" ]] && {
|
||||
msg_error "$(translate "The selected path is not a valid directory:") $result"
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "$result"
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
select_host_directory() {
|
||||
local method result
|
||||
|
||||
method=$(whiptail --title "$(translate "Select Host Directory")" \
|
||||
--menu "$(translate "How do you want to select the HOST folder to mount?")" 15 70 4 \
|
||||
"mnt" "$(translate "Select from /mnt directories")" \
|
||||
"manual" "$(translate "Enter path manually")" \
|
||||
3>&1 1>&2 2>&3) || return 1
|
||||
|
||||
case "$method" in
|
||||
mnt|srv|media)
|
||||
local base_path="/$method"
|
||||
local host_dirs=("$base_path"/*)
|
||||
local options=()
|
||||
|
||||
for dir in "${host_dirs[@]}"; do
|
||||
[[ -d "$dir" ]] && options+=("$dir" "$(basename "$dir")")
|
||||
done
|
||||
|
||||
if [[ ${#options[@]} -eq 0 ]]; then
|
||||
msg_error "$(translate "No directories found in") $base_path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
result=$(whiptail --title "$(translate "Select Host Folder")" \
|
||||
--menu "$(translate "Select the folder to mount:")" 20 80 10 \
|
||||
"${options[@]}" 3>&1 1>&2 2>&3) || return 1
|
||||
;;
|
||||
manual)
|
||||
result=$(whiptail --title "$(translate "Enter Path")" \
|
||||
--inputbox "$(translate "Enter the full path to the host folder:")" \
|
||||
10 70 "/mnt/" 3>&1 1>&2 2>&3) || return 1
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
[[ -z "$result" ]] && return 1
|
||||
[[ ! -d "$result" ]] && {
|
||||
msg_error "$(translate "The selected path is not a valid directory:") $result"
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "$result"
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
select_lxc_container() {
|
||||
local ct_list ctid ct_status
|
||||
|
||||
ct_list=$(pct list | awk 'NR>1 {print $1, $2, $3}')
|
||||
if [[ -z "$ct_list" ]]; then
|
||||
dialog --title "$(translate "Error")" \
|
||||
--msgbox "$(translate "No LXC containers available")" 8 50
|
||||
return 1
|
||||
fi
|
||||
|
||||
local options=()
|
||||
while read -r id name status; do
|
||||
if [[ -n "$id" ]]; then
|
||||
options+=("$id" "$name ($status)")
|
||||
fi
|
||||
done <<< "$ct_list"
|
||||
|
||||
ctid=$(dialog --title "$(translate "Select LXC Container")" \
|
||||
--menu "\n$(translate "Select container:")" 25 80 15 \
|
||||
"${options[@]}" 3>&1 1>&2 2>&3)
|
||||
|
||||
if [[ -z "$ctid" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$ctid"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
select_container_mount_point_() {
|
||||
local ctid="$1"
|
||||
local host_dir="$2"
|
||||
local choice mount_point existing_dirs options
|
||||
|
||||
while true; do
|
||||
choice=$(whiptail --title "$(translate "Configure Mount Point inside LXC")" \
|
||||
--menu "$(translate "Where to mount inside container?")" 18 70 5 \
|
||||
"1" "$(translate "Create new directory in /mnt")" \
|
||||
"2" "$(translate "Enter path manually")" \
|
||||
"3" "$(translate "Cancel")" 3>&1 1>&2 2>&3) || return 1
|
||||
|
||||
case "$choice" in
|
||||
1)
|
||||
mount_point=$(whiptail --inputbox "$(translate "Enter folder name for /mnt:")" 10 60 "shared" 3>&1 1>&2 2>&3) || continue
|
||||
[[ -z "$mount_point" ]] && continue
|
||||
mount_point="/mnt/$mount_point"
|
||||
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
|
||||
;;
|
||||
|
||||
2)
|
||||
mount_point=$(whiptail --inputbox "$(translate "Enter full path:")" 10 70 "/mnt/shared" 3>&1 1>&2 2>&3) || continue
|
||||
[[ -z "$mount_point" ]] && continue
|
||||
mount_point="/mnt/$mount_point"
|
||||
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
|
||||
;;
|
||||
|
||||
3)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if pct exec "$ctid" -- test -d "$mount_point" 2>/dev/null; then
|
||||
echo "$mount_point"
|
||||
return 0
|
||||
else
|
||||
whiptail --msgbox "$(translate "Could not create or access directory:") $mount_point" 8 70
|
||||
continue
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
select_container_mount_point() {
|
||||
local ctid="$1"
|
||||
local host_dir="$2"
|
||||
local choice mount_point base_name
|
||||
|
||||
base_name=$(basename "$host_dir")
|
||||
|
||||
while true; do
|
||||
choice=$(whiptail --title "$(translate "Configure Mount Point inside LXC")" \
|
||||
--menu "$(translate "Where to mount inside container?")" 18 70 5 \
|
||||
"1" "$(translate "Create new directory in /mnt")" \
|
||||
"2" "$(translate "Enter path manually")" \
|
||||
"3" "$(translate "Cancel")" 3>&1 1>&2 2>&3) || return 1
|
||||
|
||||
case "$choice" in
|
||||
1)
|
||||
mount_point=$(whiptail --inputbox "$(translate "Enter folder name for /mnt:")" \
|
||||
10 60 "$base_name" 3>&1 1>&2 2>&3) || continue
|
||||
[[ -z "$mount_point" ]] && continue
|
||||
mount_point="/mnt/$mount_point"
|
||||
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
|
||||
;;
|
||||
|
||||
2)
|
||||
mount_point=$(whiptail --inputbox "$(translate "Enter full path:")" \
|
||||
10 70 "/mnt/$base_name" 3>&1 1>&2 2>&3) || continue
|
||||
[[ -z "$mount_point" ]] && continue
|
||||
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
|
||||
;;
|
||||
|
||||
3)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if pct exec "$ctid" -- test -d "$mount_point" 2>/dev/null; then
|
||||
echo "$mount_point"
|
||||
return 0
|
||||
else
|
||||
whiptail --msgbox "$(translate "Could not create or access directory:") $mount_point" 8 70
|
||||
continue
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ==========================================================
|
||||
# CLIENT MOUNT FUNCTIONS (NFS/SAMBA COMMON)
|
||||
# ==========================================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Check if container is privileged (required for client mounts)
|
||||
select_privileged_lxc() {
|
||||
# === Select CT ===
|
||||
local ct_list ctid ct_status conf unpriv
|
||||
|
||||
ct_list=$(pct list | awk 'NR>1 {print $1, $3}')
|
||||
if [[ -z "$ct_list" ]]; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "Error")" \
|
||||
--msgbox "$(translate "No CTs available in the system.")" 8 50
|
||||
return 1
|
||||
fi
|
||||
|
||||
ctid=$(dialog --backtitle "ProxMenux" --title "$(translate "Select CT")" \
|
||||
--menu "$(translate "Select the CT to manage NFS/Samba client:")" 20 70 12 \
|
||||
$ct_list 3>&1 1>&2 2>&3)
|
||||
|
||||
if [[ -z "$ctid" ]]; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "Error")" \
|
||||
--msgbox "$(translate "No CT was selected.")" 8 50
|
||||
return 1
|
||||
fi
|
||||
|
||||
# === Start CT if not running ===
|
||||
ct_status=$(pct status "$ctid" | awk '{print $2}')
|
||||
if [[ "$ct_status" != "running" ]]; then
|
||||
show_proxmenux_logo
|
||||
echo -e
|
||||
msg_info "$(translate "Starting CT") $ctid..."
|
||||
pct start "$ctid"
|
||||
sleep 2
|
||||
if [[ "$(pct status "$ctid" | awk '{print $2}')" != "running" ]]; then
|
||||
msg_error "$(translate "Failed to start the CT.")"
|
||||
echo -e ""
|
||||
msg_success "$(translate 'Press Enter to continue...')"
|
||||
read -r
|
||||
return 1
|
||||
fi
|
||||
msg_ok "$(translate "CT started successfully.")"
|
||||
fi
|
||||
|
||||
# === Check privileged/unprivileged ===
|
||||
conf="/etc/pve/lxc/${ctid}.conf"
|
||||
unpriv=$(awk '/^unprivileged:/ {print $2}' "$conf" 2>/dev/null)
|
||||
|
||||
if [[ "$unpriv" == "1" ]]; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "Privileged Container Required")" \
|
||||
--msgbox "\n$(translate "Network share mounting (NFS/Samba) requires a PRIVILEGED container.")\n\n$(translate "Selected container") $ctid $(translate "is UNPRIVILEGED.")\n\n$(translate "For unprivileged containers, use instead:")\n • $(translate "Configure LXC mount points")\n • $(translate "Mount shares on HOST first")\n • $(translate "Then bind-mount to container")" 15 75
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Export CTID if all good
|
||||
echo "$ctid"
|
||||
CTID="$ctid"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Common mount point selection for containers
|
||||
pmx_select_container_mount_point() {
|
||||
local ctid="$1"
|
||||
local share_name="${2:-shared}"
|
||||
|
||||
while true; do
|
||||
local choice=$(whiptail --title "$(translate "Select Mount Point")" --menu "$(translate "Where do you want to mount inside container?")" 15 70 3 \
|
||||
"existing" "$(translate "Select from existing folders in /mnt")" \
|
||||
"new" "$(translate "Create new folder in /mnt")" \
|
||||
"custom" "$(translate "Enter custom path")" 3>&1 1>&2 2>&3)
|
||||
|
||||
case "$choice" in
|
||||
existing)
|
||||
local existing_dirs=$(pct exec "$ctid" -- find /mnt -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort)
|
||||
if [[ -z "$existing_dirs" ]]; then
|
||||
whiptail --title "$(translate "No Folders")" --msgbox "$(translate "No folders found in /mnt. Please create a new folder.")" 8 60
|
||||
continue
|
||||
fi
|
||||
|
||||
local options=()
|
||||
while IFS= read -r dir; do
|
||||
if [[ -n "$dir" ]]; then
|
||||
local name=$(basename "$dir")
|
||||
if pct exec "$ctid" -- [ "$(ls -A "$dir" 2>/dev/null | wc -l)" -eq 0 ]; then
|
||||
local status="$(translate "Empty")"
|
||||
else
|
||||
local status="$(translate "Contains files")"
|
||||
fi
|
||||
options+=("$dir" "$name ($status)")
|
||||
fi
|
||||
done <<< "$existing_dirs"
|
||||
|
||||
local mount_point=$(whiptail --title "$(translate "Select Existing Folder")" --menu "$(translate "Choose a folder to mount:")" 20 80 10 "${options[@]}" 3>&1 1>&2 2>&3)
|
||||
|
||||
if [[ -n "$mount_point" ]]; then
|
||||
if pct exec "$ctid" -- [ "$(ls -A "$mount_point" 2>/dev/null | wc -l)" -gt 0 ]; then
|
||||
local file_count=$(pct exec "$ctid" -- ls -A "$mount_point" 2>/dev/null | wc -l || true)
|
||||
if ! whiptail --yesno "$(translate "WARNING: The selected directory is not empty!")\n\n$(translate "Directory:"): $mount_point\n$(translate "Contains:"): $file_count $(translate "files/folders")\n\n$(translate "Mounting here will hide existing files until unmounted.")\n\n$(translate "Do you want to continue?")" 14 70 --title "$(translate "Directory Not Empty")"; then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
echo "$mount_point"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
new)
|
||||
local folder_name=$(whiptail --inputbox "$(translate "Enter new folder name:")" 10 60 "$share_name" --title "$(translate "New Folder in /mnt")" 3>&1 1>&2 2>&3)
|
||||
if [[ -n "$folder_name" ]]; then
|
||||
local mount_point="/mnt/$folder_name"
|
||||
echo "$mount_point"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
custom)
|
||||
local mount_point=$(whiptail --inputbox "$(translate "Enter full path for mount point:")" 10 70 "/mnt/${share_name}" --title "$(translate "Custom Path")" 3>&1 1>&2 2>&3)
|
||||
if [[ -n "$mount_point" ]]; then
|
||||
echo "$mount_point"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Common server discovery function
|
||||
pmx_discover_network_servers() {
|
||||
local service_type="$1" # "NFS" or "Samba"
|
||||
local port="$2" # "2049" for NFS, "139,445" for Samba
|
||||
|
||||
local host_ip=$(hostname -I | awk '{print $1}')
|
||||
local network=$(echo "$host_ip" | cut -d. -f1-3).0/24
|
||||
|
||||
# Install nmap if needed
|
||||
if ! which nmap >/dev/null 2>&1; then
|
||||
apt-get install -y nmap &>/dev/null
|
||||
fi
|
||||
|
||||
local servers
|
||||
if [[ "$service_type" == "Samba" ]]; then
|
||||
servers=$(nmap -p 139,445 --open "$network" 2>/dev/null | grep -B 4 -E "(139|445)/tcp open" | grep "Nmap scan report" | awk '{print $5}' | sort -u || true)
|
||||
else
|
||||
servers=$(nmap -p 2049 --open "$network" 2>/dev/null | grep -B 4 "2049/tcp open" | grep "Nmap scan report" | awk '{print $5}' | sort -u || true)
|
||||
fi
|
||||
|
||||
if [[ -z "$servers" ]]; then
|
||||
whiptail --title "$(translate "No Servers Found")" --msgbox "$(translate "No") $service_type $(translate "servers found on the network.")\n\n$(translate "You can add servers manually.")" 10 60
|
||||
return 1
|
||||
fi
|
||||
|
||||
local options=()
|
||||
while IFS= read -r server; do
|
||||
if [[ -n "$server" ]]; then
|
||||
if [[ "$service_type" == "Samba" ]]; then
|
||||
# Try to get NetBIOS name for Samba
|
||||
local nb_name=$(nmblookup -A "$server" 2>/dev/null | awk '/<00> -.*B <ACTIVE>/ {print $1; exit}')
|
||||
if [[ -z "$nb_name" || "$nb_name" == "$server" || "$nb_name" == "address" || "$nb_name" == "-" ]]; then
|
||||
nb_name="Unknown"
|
||||
fi
|
||||
options+=("$server" "$nb_name ($server)")
|
||||
else
|
||||
# For NFS, show export count
|
||||
local exports_count=$(showmount -e "$server" 2>/dev/null | tail -n +2 | wc -l || echo "0")
|
||||
options+=("$server" "NFS Server ($exports_count exports)")
|
||||
fi
|
||||
fi
|
||||
done <<< "$servers"
|
||||
|
||||
if [[ ${#options[@]} -eq 0 ]]; then
|
||||
whiptail --title "$(translate "No Valid Servers")" --msgbox "$(translate "No accessible") $service_type $(translate "servers found.")" 8 50
|
||||
return 1
|
||||
fi
|
||||
|
||||
local selected_server=$(whiptail --title "$(translate "Select") $service_type $(translate "Server")" --menu "$(translate "Choose a server:")" 20 80 10 "${options[@]}" 3>&1 1>&2 2>&3)
|
||||
|
||||
if [[ -n "$selected_server" ]]; then
|
||||
echo "$selected_server"
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Common server selection function
|
||||
pmx_select_server() {
|
||||
local service_type="$1" # "NFS" or "Samba"
|
||||
local port="$2" # "2049" for NFS, "139,445" for Samba
|
||||
|
||||
local method=$(whiptail --title "$(translate "$service_type Server Selection")" --menu "$(translate "How do you want to select the") $service_type $(translate "server?")" 15 70 3 \
|
||||
"auto" "$(translate "Auto-discover servers on network")" \
|
||||
"manual" "$(translate "Enter server IP/hostname manually")" \
|
||||
"recent" "$(translate "Select from recent servers")" 3>&1 1>&2 2>&3)
|
||||
|
||||
local result_code=$?
|
||||
if [[ $result_code -ne 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
case "$method" in
|
||||
auto)
|
||||
local discovered_server
|
||||
discovered_server=$(pmx_discover_network_servers "$service_type" "$port")
|
||||
local discover_result=$?
|
||||
if [[ $discover_result -eq 0 && -n "$discovered_server" ]]; then
|
||||
echo "$discovered_server"
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
manual)
|
||||
local server=$(whiptail --inputbox "$(translate "Enter") $service_type $(translate "server IP or hostname:")" 10 60 --title "$(translate "$service_type Server")" 3>&1 1>&2 2>&3)
|
||||
local input_result=$?
|
||||
if [[ $input_result -eq 0 && -n "$server" ]]; then
|
||||
echo "$server"
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
recent)
|
||||
local fs_type
|
||||
if [[ "$service_type" == "NFS" ]]; then
|
||||
fs_type="nfs"
|
||||
else
|
||||
fs_type="cifs"
|
||||
fi
|
||||
|
||||
# Fix the recent servers detection for NFS
|
||||
local recent
|
||||
if [[ "$service_type" == "NFS" ]]; then
|
||||
recent=$(grep "$fs_type" /etc/fstab 2>/dev/null | awk '{print $1}' | cut -d: -f1 | sort -u || true)
|
||||
else
|
||||
recent=$(grep "$fs_type" /etc/fstab 2>/dev/null | awk '{print $1}' | cut -d/ -f3 | sort -u || true)
|
||||
fi
|
||||
|
||||
if [[ -z "$recent" ]]; then
|
||||
whiptail --title "$(translate "No Recent Servers")" --msgbox "\n$(translate "No recent") $service_type $(translate "servers found.")" 8 50
|
||||
return 1
|
||||
fi
|
||||
|
||||
local options=()
|
||||
while IFS= read -r server; do
|
||||
[[ -n "$server" ]] && options+=("$server" "$(translate "Recent") $service_type $(translate "server")")
|
||||
done <<< "$recent"
|
||||
|
||||
local selected_server=$(whiptail --title "$(translate "Recent") $service_type $(translate "Servers")" --menu "$(translate "Choose a recent server:")" 20 70 10 "${options[@]}" 3>&1 1>&2 2>&3)
|
||||
local select_result=$?
|
||||
if [[ $select_result -eq 0 && -n "$selected_server" ]]; then
|
||||
echo "$selected_server"
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Common mount options configuration
|
||||
pmx_configure_mount_options() {
|
||||
local service_type="$1" # "NFS" or "CIFS"
|
||||
|
||||
local mount_type
|
||||
if [[ "$service_type" == "NFS" ]]; then
|
||||
mount_type=$(whiptail --title "$(translate "Mount Options")" --menu "$(translate "Select mount configuration:")" 15 70 4 \
|
||||
"default" "$(translate "Default options")" \
|
||||
"readonly" "$(translate "Read-only mount")" \
|
||||
"performance" "$(translate "Performance optimized")" \
|
||||
"custom" "$(translate "Custom options")" 3>&1 1>&2 2>&3)
|
||||
|
||||
case "$mount_type" in
|
||||
default)
|
||||
echo "rw,hard,intr,rsize=8192,wsize=8192,timeo=14"
|
||||
;;
|
||||
readonly)
|
||||
echo "ro,hard,intr,rsize=8192,timeo=14"
|
||||
;;
|
||||
performance)
|
||||
echo "rw,hard,intr,rsize=1048576,wsize=1048576,timeo=14,retrans=2"
|
||||
;;
|
||||
custom)
|
||||
local options=$(whiptail --inputbox "$(translate "Enter custom mount options:")" 10 70 "rw,hard,intr" --title "$(translate "Custom Options")" 3>&1 1>&2 2>&3)
|
||||
echo "${options:-rw,hard,intr}"
|
||||
;;
|
||||
*)
|
||||
echo "rw,hard,intr,rsize=8192,wsize=8192,timeo=14"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
# CIFS options
|
||||
mount_type=$(whiptail --title "$(translate "Mount Options")" --menu "$(translate "Select mount configuration:")" 15 70 4 \
|
||||
"default" "$(translate "Default options")" \
|
||||
"readonly" "$(translate "Read-only mount")" \
|
||||
"performance" "$(translate "Performance optimized")" \
|
||||
"custom" "$(translate "Custom options")" 3>&1 1>&2 2>&3)
|
||||
|
||||
case "$mount_type" in
|
||||
default)
|
||||
echo "rw,file_mode=0664,dir_mode=0775,iocharset=utf8"
|
||||
;;
|
||||
readonly)
|
||||
echo "ro,file_mode=0444,dir_mode=0555,iocharset=utf8"
|
||||
;;
|
||||
performance)
|
||||
echo "rw,file_mode=0664,dir_mode=0775,iocharset=utf8,cache=strict,rsize=1048576,wsize=1048576"
|
||||
;;
|
||||
custom)
|
||||
local options=$(whiptail --inputbox "$(translate "Enter custom mount options:")" 10 70 "rw,file_mode=0664,dir_mode=0775" --title "$(translate "Custom Options")" 3>&1 1>&2 2>&3)
|
||||
echo "${options:-rw,file_mode=0664,dir_mode=0775}"
|
||||
;;
|
||||
*)
|
||||
echo "rw,file_mode=0664,dir_mode=0775,iocharset=utf8"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
# Common permanent mount question
|
||||
pmx_ask_permanent_mount() {
|
||||
if whiptail --yesno "$(translate "Do you want to make this mount permanent?")\n\n$(translate "This will add the mount to /etc/fstab so it persists after reboot.")" 10 70 --title "$(translate "Permanent Mount")"; then
|
||||
echo "true"
|
||||
else
|
||||
echo "false"
|
||||
fi
|
||||
}
|
@@ -240,14 +240,16 @@ EOF
|
||||
return 0
|
||||
fi
|
||||
|
||||
msg_info "$(translate "Removing conflicting utilities...")"
|
||||
msg_info "$(translate "Cleaning up unused time synchronization services...")"
|
||||
|
||||
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")"
|
||||
msg_ok "$(translate "Old time services removed successfully")"
|
||||
else
|
||||
msg_warn "$(translate "Some conflicting utilities may not have been removed")"
|
||||
msg_warn "$(translate "Some old time services could not be removed (not installed)")"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
msg_info "$(translate "Updating packages...")"
|
||||
apt-get install pv -y > /dev/null 2>&1
|
||||
msg_ok "$(translate "Packages updated successfully")"
|
||||
@@ -330,6 +332,7 @@ EOF
|
||||
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
|
||||
|
@@ -179,9 +179,11 @@ EOF
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
export APT_LISTCHANGES_FRONTEND=none
|
||||
export NEEDRESTART_MODE=a
|
||||
export UCF_FORCE_CONFOLD=1
|
||||
export DPKG_OPTIONS="--force-confdef --force-confold"
|
||||
|
||||
msg_info "$(translate "Performing packages upgrade...")"
|
||||
apt-get install pv -y > /dev/null 2>&1
|
||||
@@ -195,22 +197,24 @@ EOF
|
||||
tput civis
|
||||
tput sc
|
||||
|
||||
|
||||
(
|
||||
/usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' dist-upgrade 2>&1 | \
|
||||
/usr/bin/env \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
APT_LISTCHANGES_FRONTEND=none \
|
||||
NEEDRESTART_MODE=a \
|
||||
UCF_FORCE_CONFOLD=1 \
|
||||
apt-get -y \
|
||||
-o Dpkg::Options::="--force-confdef" \
|
||||
-o Dpkg::Options::="--force-confold" \
|
||||
dist-upgrade 2>&1 | \
|
||||
while IFS= read -r line; do
|
||||
if [[ "$line" =~ ^(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ]]; then
|
||||
|
||||
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 "──────────────────────────────────────────────"
|
||||
@@ -218,12 +222,10 @@ EOF
|
||||
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
|
||||
@@ -232,6 +234,7 @@ EOF
|
||||
if [ $? -eq 0 ]; then
|
||||
tput rc
|
||||
tput ed
|
||||
tput cnorm
|
||||
msg_ok "$(translate "System upgrade completed")"
|
||||
fi
|
||||
|
||||
|
@@ -197,13 +197,12 @@ show_vm_ct_commands() {
|
||||
|
||||
echo -e "\n${YELLOW}$(translate 'Listing relevant CT users and their mapped UID/GID on host...')${NC}\n"
|
||||
|
||||
# Obtener el shift de UID del CT (por defecto 100000 si no está configurado)
|
||||
|
||||
UID_SHIFT=$(grep "^lxc.idmap" /etc/pve/lxc/"$id".conf | grep 'u 0' | awk '{print $5}')
|
||||
UID_SHIFT=${UID_SHIFT:-100000}
|
||||
|
||||
# Obtener todos los usuarios y filtrar solo root o UID >= 1000
|
||||
pct exec "$id" -- getent passwd | while IFS=: read -r username _ uid gid _ home _; do
|
||||
if [ "$uid" -eq 0 ] || [ "$uid" -ge 1000 ]; then
|
||||
if [ "$uid" -eq 0 ] || [ "$uid" -eq 65534 ] || [ "$uid" -ge 30 ]; then
|
||||
real_uid=$((UID_SHIFT + uid))
|
||||
real_gid=$((UID_SHIFT + gid))
|
||||
echo -e "${GREEN}$(translate 'User')${NC}: $username"
|
||||
|
284
scripts/lxc/lxc-manual-guide.sh
Normal file
284
scripts/lxc/lxc-manual-guide.sh
Normal file
@@ -0,0 +1,284 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# ProxMenux - Manual LXC Conversion Guide
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
|
||||
# Version : 1.0
|
||||
# Last Updated: 19/08/2025
|
||||
# ==========================================================
|
||||
|
||||
# Configuration ============================================
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
VENV_PATH="/opt/googletrans-env"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
load_language
|
||||
initialize_cache
|
||||
# ==========================================================
|
||||
|
||||
show_command() {
|
||||
local step="$1"
|
||||
local description="$2"
|
||||
local command="$3"
|
||||
local note="$4"
|
||||
local command_extra="$5"
|
||||
|
||||
echo -e "${BGN}${step}.${CL} ${BL}${description}${CL}"
|
||||
echo ""
|
||||
echo -e "${TAB}${command}"
|
||||
echo -e
|
||||
[[ -n "$note" ]] && echo -e "${TAB}${DARK_GRAY}${note}${CL}"
|
||||
[[ -n "$command_extra" ]] && echo -e "${TAB}${YW}${command_extra}${CL}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
show_privileged_to_unprivileged_guide() {
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Manual Guide: Convert LXC Privileged to Unprivileged")"
|
||||
|
||||
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}$(translate "Source:")${CL} ${BL}https://forum.proxmox.com/threads/converting-between-privileged-and-unprivileged-containers.97243/${CL}"
|
||||
echo -e
|
||||
echo -e
|
||||
echo -e "${TAB}${BOLD}$(translate "IMPORTANT PREREQUISITES:")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}• $(translate "Container must be stopped before conversion")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "Create a backup of your container before proceeding")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "This process changes file ownership inside the container")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "Process may take several minutes depending on container size")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "Works with LVM, ZFS, and BTRFS storage types")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
|
||||
echo -e
|
||||
|
||||
show_command "1" \
|
||||
"$(translate "List all containers to identify the privileged one:")" \
|
||||
"pct list" \
|
||||
"$(translate "Look for containers without 'unprivileged: 1' in their config")"
|
||||
|
||||
show_command "2" \
|
||||
"$(translate "Stop the container if it's running:")" \
|
||||
"pct stop <container-id>" \
|
||||
"$(translate "Replace <container-id> with your actual container ID")" \
|
||||
"$(translate "Example: pct stop 114")"
|
||||
|
||||
show_command "3" \
|
||||
"$(translate "Create a backup of the container configuration:")" \
|
||||
"cp /etc/pve/lxc/<container-id>.conf /etc/pve/lxc/<container-id>.conf.bak" \
|
||||
"$(translate "This creates a backup in case you need to revert changes")" \
|
||||
"$(translate "Example: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak")"
|
||||
|
||||
show_command "4" \
|
||||
"$(translate "Get the container's storage information:")" \
|
||||
"grep '^rootfs:' /etc/pve/lxc/<container-id>.conf" \
|
||||
"$(translate "This shows the storage type and disk identifier")" \
|
||||
"$(translate "Example output: rootfs: local-lvm:vm-114-disk-0,size=8G")"
|
||||
|
||||
show_command "5" \
|
||||
"$(translate "Get the actual disk path:")" \
|
||||
"pvesm path <storage-identifier>" \
|
||||
"$(translate "Replace <storage-identifier> with the value from step 4")" \
|
||||
"$(translate "Example: pvesm path local-lvm:vm-114-disk-0")"
|
||||
|
||||
echo -e "${TAB}${BOLD}$(translate "STEP 6: Choose commands based on your storage type")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}$(translate "If pvesm path returned a DIRECTORY (ZFS/BTRFS):")${CL}"
|
||||
echo -e "${TAB}${YW}$(translate "Example: /rpool/data/subvol-114-disk-0")${CL}"
|
||||
echo -e
|
||||
|
||||
show_command "6a" \
|
||||
"$(translate "For ZFS/BTRFS - Set the mount path:")" \
|
||||
"MOUNT_PATH=\"/rpool/data/subvol-<container-id>-disk-0\"" \
|
||||
"$(translate "Replace with your actual path from step 5")" \
|
||||
"$(translate "Example: MOUNT_PATH=\"/rpool/data/subvol-114-disk-0\"")"
|
||||
|
||||
echo -e "${TAB}${BGN}$(translate "If pvesm path returned a DEVICE (LVM):")${CL}"
|
||||
echo -e "${TAB}${YW}$(translate "Example: /dev/pve/vm-114-disk-0")${CL}"
|
||||
echo -e
|
||||
|
||||
show_command "6b" \
|
||||
"$(translate "For LVM - Create mount directory and mount:")" \
|
||||
"mkdir -p /tmp/lxc_convert_<container-id>\nmount -o loop /dev/path/to/disk /tmp/lxc_convert_<container-id>\nMOUNT_PATH=\"/tmp/lxc_convert_<container-id>\"" \
|
||||
"$(translate "Replace paths with your actual values from step 5")" \
|
||||
"$(translate "Example: mkdir -p /tmp/lxc_convert_114")"
|
||||
|
||||
show_command "7" \
|
||||
"$(translate "Convert file ownership (this takes time):")" \
|
||||
"find \"\$MOUNT_PATH\" -type f | while read file; do\n if [ -e \"\$file\" ]; then\n CURRENT_UID=\$(stat -c '%u' \"\$file\")\n CURRENT_GID=\$(stat -c '%g' \"\$file\")\n NEW_UID=\$((100000 + CURRENT_UID))\n NEW_GID=\$((100000 + CURRENT_GID))\n chown \"\$NEW_UID:\$NEW_GID\" \"\$file\"\n fi\ndone" \
|
||||
"$(translate "This converts all file UIDs/GIDs by adding 100000")" \
|
||||
"$(translate "Process may take several minutes for large containers")"
|
||||
|
||||
show_command "8" \
|
||||
"$(translate "Convert directory ownership:")" \
|
||||
"find \"\$MOUNT_PATH\" -type d | while read dir; do\n if [ -e \"\$dir\" ]; then\n CURRENT_UID=\$(stat -c '%u' \"\$dir\")\n CURRENT_GID=\$(stat -c '%g' \"\$dir\")\n NEW_UID=\$((100000 + CURRENT_UID))\n NEW_GID=\$((100000 + CURRENT_GID))\n chown \"\$NEW_UID:\$NEW_GID\" \"\$dir\"\n fi\ndone" \
|
||||
"$(translate "This converts all directory UIDs/GIDs by adding 100000")"
|
||||
|
||||
echo -e "${TAB}${BOLD}$(translate "STEP 9: Cleanup (LVM only)")${CL}"
|
||||
echo -e "${TAB}${YW}$(translate "Only run this if you used LVM (step 6b):")${CL}"
|
||||
echo -e
|
||||
|
||||
show_command "9" \
|
||||
"$(translate "Unmount and cleanup (LVM only):")" \
|
||||
"umount /tmp/lxc_convert_<container-id>\nrmdir /tmp/lxc_convert_<container-id>" \
|
||||
"$(translate "Only needed if you mounted the filesystem in step 6b")" \
|
||||
"$(translate "Skip this step for ZFS/BTRFS")"
|
||||
|
||||
show_command "10" \
|
||||
"$(translate "Add unprivileged flag to container configuration:")" \
|
||||
"echo 'unprivileged: 1' >> /etc/pve/lxc/<container-id>.conf" \
|
||||
"$(translate "This marks the container as unprivileged")"
|
||||
|
||||
show_command "11" \
|
||||
"$(translate "Start the converted container:")" \
|
||||
"pct start <container-id>" \
|
||||
"$(translate "The container should now start as unprivileged")"
|
||||
|
||||
show_command "12" \
|
||||
"$(translate "Verify the conversion:")" \
|
||||
"pct config <container-id> | grep unprivileged" \
|
||||
"$(translate "Should show 'unprivileged: 1'")"
|
||||
|
||||
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BOLD}$(translate "STORAGE TYPE IDENTIFICATION:")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}• $(translate "LVM:")${CL} ${YW}pvesm path returns /dev/xxx (block device)${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "ZFS:")${CL} ${YW}pvesm path returns /rpool/xxx (directory)${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "BTRFS:")${CL} ${YW}pvesm path returns directory path${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BOLD}$(translate "TROUBLESHOOTING:")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}$(translate "If mount fails (LVM):")${CL} ${YW}Check that the container is stopped and disk path is correct${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "If path not accessible (ZFS/BTRFS):")${CL} ${YW}Verify the dataset/subvolume exists and is mounted${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "If container won't start:")${CL} ${YW}Check /var/log/pve/tasks/ for detailed error messages${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "To revert changes:")${CL} ${YW}cp /etc/pve/lxc/<container-id>.conf.bak /etc/pve/lxc/<container-id>.conf${CL}"
|
||||
echo -e
|
||||
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
echo -e
|
||||
read -r
|
||||
}
|
||||
|
||||
show_unprivileged_to_privileged_guide() {
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Manual Guide: Convert LXC Unprivileged to Privileged")"
|
||||
|
||||
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${RD}$(translate "SECURITY WARNING:")${CL} ${YW}$(translate "Privileged containers have full root access to the host system!")${CL}"
|
||||
echo -e "${TAB}${YW}$(translate "Only convert to privileged if absolutely necessary for your use case.")${CL}"
|
||||
echo -e
|
||||
echo -e
|
||||
echo -e "${TAB}${BOLD}$(translate "IMPORTANT PREREQUISITES:")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}• $(translate "Container must be stopped before conversion")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "Create a backup of your container before proceeding")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "Understand the security implications of privileged containers")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "This is a simple configuration change")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
|
||||
echo -e
|
||||
|
||||
|
||||
show_command "1" \
|
||||
"$(translate "List all containers to identify the unprivileged one:")" \
|
||||
"pct list" \
|
||||
"$(translate "Look for containers with 'unprivileged: 1' in their config")"
|
||||
|
||||
show_command "2" \
|
||||
"$(translate "Check if container is unprivileged:")" \
|
||||
"pct config <container-id> | grep unprivileged" \
|
||||
"$(translate "Should show 'unprivileged: 1' if it's unprivileged")" \
|
||||
"$(translate "Example: pct config 110 | grep unprivileged")"
|
||||
|
||||
show_command "3" \
|
||||
"$(translate "Stop the container if it's running:")" \
|
||||
"pct stop <container-id>" \
|
||||
"$(translate "Replace <container-id> with your actual container ID")" \
|
||||
"$(translate "Example: pct stop 110")"
|
||||
|
||||
show_command "4" \
|
||||
"$(translate "Create a backup of the container configuration:")" \
|
||||
"cp /etc/pve/lxc/<container-id>.conf /etc/pve/lxc/<container-id>.conf.bak" \
|
||||
"$(translate "This creates a backup in case you need to revert changes")" \
|
||||
"$(translate "Example: cp /etc/pve/lxc/110.conf /etc/pve/lxc/110.conf.bak")"
|
||||
|
||||
show_command "5" \
|
||||
"$(translate "Remove the unprivileged flag from configuration:")" \
|
||||
"sed -i '/^unprivileged: 1/d' /etc/pve/lxc/<container-id>.conf" \
|
||||
"$(translate "This removes the 'unprivileged: 1' line from the config")" \
|
||||
"$(translate "Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf")"
|
||||
|
||||
show_command "6" \
|
||||
"$(translate "Add explicit privileged flag (optional but recommended):")" \
|
||||
"echo 'unprivileged: 0' >> /etc/pve/lxc/<container-id>.conf" \
|
||||
"$(translate "This explicitly marks the container as privileged")"
|
||||
|
||||
show_command "7" \
|
||||
"$(translate "Start the converted container:")" \
|
||||
"pct start <container-id>" \
|
||||
"$(translate "The container should now start as privileged")"
|
||||
|
||||
show_command "8" \
|
||||
"$(translate "Verify the conversion:")" \
|
||||
"pct config <container-id> | grep unprivileged" \
|
||||
"$(translate "Should show 'unprivileged: 0' or no unprivileged line")"
|
||||
|
||||
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
|
||||
echo -e
|
||||
echo -e
|
||||
echo -e "${TAB}${BOLD}$(translate "SECURITY CONSIDERATIONS:")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${RD}• $(translate "Privileged containers can access host devices directly")${CL}"
|
||||
echo -e "${TAB}${RD}• $(translate "Root inside container = root on host system")${CL}"
|
||||
echo -e "${TAB}${RD}• $(translate "Use only when unprivileged containers cannot meet your needs")${CL}"
|
||||
echo -e "${TAB}${RD}• $(translate "Consider security implications for production environments")${CL}"
|
||||
echo -e
|
||||
echo -e
|
||||
|
||||
echo -e "${TAB}${BOLD}$(translate "TROUBLESHOOTING:")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}$(translate "If container won't start:")${CL} ${YW}Check /var/log/pve/tasks/ for detailed error messages${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "To revert changes:")${CL} ${YW}cp /etc/pve/lxc/<container-id>.conf.bak /etc/pve/lxc/<container-id>.conf${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "If config issues occur:")${CL} ${YW}Manually edit /etc/pve/lxc/<container-id>.conf${CL}"
|
||||
echo -e
|
||||
echo -e
|
||||
|
||||
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
echo -e
|
||||
read -r
|
||||
}
|
||||
|
||||
show_lxc_conversion_manual_menu() {
|
||||
while true; do
|
||||
CHOICE=$(dialog --title "$(translate "LXC Conversion Manual Guides")" \
|
||||
--menu "$(translate "Select conversion guide:")" 18 70 10 \
|
||||
"1" "$(translate "Convert Privileged to Unprivileged")" \
|
||||
"2" "$(translate "Convert Unprivileged to Privileged")" \
|
||||
"3" "$(translate "Return to Main Menu")" \
|
||||
3>&1 1>&2 2>&3)
|
||||
|
||||
case $CHOICE in
|
||||
1) show_privileged_to_unprivileged_guide ;;
|
||||
2) show_unprivileged_to_privileged_guide ;;
|
||||
3) return ;;
|
||||
*) return ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Main execution
|
||||
show_lxc_conversion_manual_menu
|
271
scripts/lxc/lxc-privileged-to-unprivileged.sh
Normal file
271
scripts/lxc/lxc-privileged-to-unprivileged.sh
Normal file
@@ -0,0 +1,271 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# ProxMenu - LXC Privileged to Unprivileged Converter
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
|
||||
# Version : 1.1
|
||||
# Last Updated: 19/08/2025
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# This script converts a privileged LXC container to an unprivileged one
|
||||
# using the direct conversion method (mount and chown).
|
||||
# ==========================================================
|
||||
|
||||
# Configuration ============================================
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
VENV_PATH="/opt/googletrans-env"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
# ==========================================================
|
||||
|
||||
select_privileged_container() {
|
||||
|
||||
CONTAINERS=$(pct list | awk 'NR>1 {print $1, $3}' | while read id name; do
|
||||
if pct config "$id" | grep -q "^unprivileged: 0" || ! pct config "$id" | grep -q "^unprivileged:"; then
|
||||
echo "$id" "$name"
|
||||
fi
|
||||
done | xargs -n2)
|
||||
|
||||
if [ -z "$CONTAINERS" ]; then
|
||||
msg_error "$(translate 'No privileged containers available in Proxmox.')"
|
||||
exit 1
|
||||
fi
|
||||
cleanup
|
||||
CONTAINER_ID=$(whiptail --title "$(translate 'Select Privileged Container')" \
|
||||
--menu "$(translate 'Select the privileged LXC container to convert:')" 20 70 10 $CONTAINERS 3>&1 1>&2 2>&3)
|
||||
|
||||
if [ -z "$CONTAINER_ID" ]; then
|
||||
msg_error "$(translate 'No container selected. Exiting.')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msg_ok "$(translate 'Privileged container selected:') $CONTAINER_ID"
|
||||
}
|
||||
|
||||
validate_container_id() {
|
||||
if [ -z "$CONTAINER_ID" ]; then
|
||||
msg_error "$(translate 'Container ID not defined. Make sure to select a container first.')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if pct config "$CONTAINER_ID" | grep -q "^unprivileged: 1"; then
|
||||
msg_error "$(translate 'Container') $CONTAINER_ID $(translate 'is already unprivileged.')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if pct status "$CONTAINER_ID" | grep -q "running"; then
|
||||
msg_info "$(translate 'Stopping the container before conversion...')"
|
||||
pct stop "$CONTAINER_ID"
|
||||
msg_ok "$(translate 'Container stopped.')"
|
||||
fi
|
||||
}
|
||||
|
||||
show_backup_warning() {
|
||||
local message="$(translate 'It is strongly recommended to create a backup of your container before proceeding with the conversion.')"
|
||||
message="$message\n\n$(translate 'Do you want to continue with the conversion now, or exit to create a backup first?')"
|
||||
message="$message\n\n$(translate 'Continue: Proceed with conversion')"
|
||||
message="$message\n$(translate 'Exit: Stop to create backup manually')"
|
||||
|
||||
if whiptail --title "$(translate 'Backup Recommendation')" \
|
||||
--yes-button "$(translate 'Continue')" \
|
||||
--no-button "$(translate 'Exit')" \
|
||||
--yesno "$message" 18 80; then
|
||||
return 0
|
||||
else
|
||||
msg_info2 "$(translate 'User chose to exit for manual backup creation.')"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
convert_direct_method() {
|
||||
msg_info2 "$(translate 'Starting direct conversion of container') $CONTAINER_ID..."
|
||||
|
||||
TEMP_DIR="/tmp/lxc_convert_$CONTAINER_ID"
|
||||
mkdir -p "$TEMP_DIR"
|
||||
|
||||
|
||||
ROOTFS_CONFIG=$(pct config "$CONTAINER_ID" | grep "^rootfs:")
|
||||
if [ -z "$ROOTFS_CONFIG" ]; then
|
||||
msg_error "$(translate 'Could not find rootfs configuration for container.')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
STORAGE_DISK=$(echo "$ROOTFS_CONFIG" | awk '{print $2}' | cut -d, -f1)
|
||||
|
||||
msg_ok "$(translate 'Storage disk identifier:') $STORAGE_DISK"
|
||||
|
||||
|
||||
DISK_PATH=$(pvesm path "$STORAGE_DISK" 2>/dev/null)
|
||||
|
||||
if [ -n "$DISK_PATH" ] && [ -e "$DISK_PATH" ]; then
|
||||
msg_ok "$(translate 'Disk path resolved via pvesm:') $DISK_PATH"
|
||||
else
|
||||
|
||||
STORAGE_NAME=$(echo "$STORAGE_DISK" | cut -d: -f1)
|
||||
DISK_NAME=$(echo "$STORAGE_DISK" | cut -d: -f2)
|
||||
|
||||
msg_info2 "$(translate 'pvesm path failed, trying manual detection...')"
|
||||
msg_info2 "$(translate 'Storage:') $STORAGE_NAME, $(translate 'Disk:') $DISK_NAME"
|
||||
|
||||
|
||||
for vg in pve $(vgs --noheadings -o vg_name 2>/dev/null | tr -d ' '); do
|
||||
if [ -e "/dev/$vg/$DISK_NAME" ]; then
|
||||
DISK_PATH="/dev/$vg/$DISK_NAME"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
if [ -z "$DISK_PATH" ] || [ ! -e "$DISK_PATH" ]; then
|
||||
ZFS_PATH="/dev/zvol/$STORAGE_NAME/$DISK_NAME"
|
||||
if [ -e "$ZFS_PATH" ]; then
|
||||
DISK_PATH="$ZFS_PATH"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ -z "$DISK_PATH" ] || [ ! -e "$DISK_PATH" ]; then
|
||||
msg_error "$(translate 'Could not determine disk path for:') $STORAGE_DISK"
|
||||
msg_error "$(translate 'Tried pvesm path and manual detection methods')"
|
||||
msg_info2 "$(translate 'Available storage information:')"
|
||||
pvesm status 2>/dev/null || msg_error "$(translate 'pvesm status failed')"
|
||||
rmdir "$TEMP_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
msg_ok "$(translate 'Mounting container filesystem')"
|
||||
if ! mount "$DISK_PATH" "$TEMP_DIR" 2>/dev/null; then
|
||||
|
||||
if ! mount -o loop "$DISK_PATH" "$TEMP_DIR" 2>/dev/null; then
|
||||
msg_error "$(translate 'Failed to mount container filesystem.')"
|
||||
msg_error "$(translate 'Disk path:') $DISK_PATH"
|
||||
msg_success "$(translate "Press Enter to return")"
|
||||
read -r
|
||||
rmdir "$TEMP_DIR"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
msg_info "$(translate 'Converting file ownership (this may take several minutes)...')"
|
||||
|
||||
|
||||
find "$TEMP_DIR" -type f -print0 | while IFS= read -r -d '' S; do
|
||||
|
||||
if [ ! -e "$S" ] || [ ! -r "$S" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
|
||||
if STAT_OUTPUT=$(stat -c "%u %g" "$S" 2>/dev/null); then
|
||||
U=$(echo "$STAT_OUTPUT" | awk '{print $1}')
|
||||
G=$(echo "$STAT_OUTPUT" | awk '{print $2}')
|
||||
F=100000
|
||||
|
||||
|
||||
NEW_UID=$((F + U))
|
||||
NEW_GID=$((F + G))
|
||||
|
||||
|
||||
if ! chown "$NEW_UID:$NEW_GID" "$S" 2>/dev/null; then
|
||||
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
find "$TEMP_DIR" -type d -print0 | while IFS= read -r -d '' S; do
|
||||
|
||||
if [ ! -e "$S" ] || [ ! -r "$S" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
|
||||
if STAT_OUTPUT=$(stat -c "%u %g" "$S" 2>/dev/null); then
|
||||
U=$(echo "$STAT_OUTPUT" | awk '{print $1}')
|
||||
G=$(echo "$STAT_OUTPUT" | awk '{print $2}')
|
||||
F=100000
|
||||
|
||||
|
||||
NEW_UID=$((F + U))
|
||||
NEW_GID=$((F + G))
|
||||
|
||||
|
||||
if ! chown "$NEW_UID:$NEW_GID" "$S" 2>/dev/null; then
|
||||
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
[ -e "$TEMP_DIR/var/spool/postfix/dev/-random" ] && rm -f "$TEMP_DIR/var/spool/postfix/dev/-random"
|
||||
[ -e "$TEMP_DIR/var/spool/postfix/dev/-urandom" ] && rm -f "$TEMP_DIR/var/spool/postfix/dev/-urandom"
|
||||
|
||||
|
||||
[ -e "$TEMP_DIR/usr/bin/sudo" ] && chmod u+s "$TEMP_DIR/usr/bin/sudo"
|
||||
|
||||
umount "$TEMP_DIR"
|
||||
rmdir "$TEMP_DIR"
|
||||
|
||||
|
||||
CONFIG_FILE="/etc/pve/lxc/$CONTAINER_ID.conf"
|
||||
if ! grep -q "^unprivileged:" "$CONFIG_FILE"; then
|
||||
echo "unprivileged: 1" >> "$CONFIG_FILE"
|
||||
else
|
||||
sed -i 's/^unprivileged:.*/unprivileged: 1/' "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
msg_ok "$(translate 'Direct conversion completed for container') $CONTAINER_ID"
|
||||
|
||||
echo -e
|
||||
msg_success "Press Enter to continue..."
|
||||
read -r
|
||||
}
|
||||
|
||||
cleanup_and_finalize() {
|
||||
|
||||
if whiptail --yesno "$(translate 'Do you want to start the converted unprivileged container') $CONTAINER_ID $(translate 'now?')" 10 60; then
|
||||
msg_info2 "$(translate 'Starting unprivileged container...')"
|
||||
pct start "$CONTAINER_ID"
|
||||
msg_ok "$(translate 'Unprivileged container') $CONTAINER_ID $(translate 'started successfully.')"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "LXC Privileged to Unprivileged conversion")"
|
||||
msg_info "$(translate 'Starting LXC Privileged to Unprivileged conversion process...')"
|
||||
|
||||
select_privileged_container
|
||||
validate_container_id
|
||||
show_backup_warning
|
||||
|
||||
convert_direct_method
|
||||
cleanup_and_finalize
|
||||
|
||||
msg_ok "$(translate 'Converted container ID:') $CONTAINER_ID"
|
||||
msg_ok "$(translate 'LXC conversion from privileged to unprivileged completed successfully!')"
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
exit 0
|
||||
}
|
||||
|
||||
|
||||
main
|
144
scripts/lxc/lxc-unprivileged-to-privileged.sh
Normal file
144
scripts/lxc/lxc-unprivileged-to-privileged.sh
Normal file
@@ -0,0 +1,144 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# ProxMenu - LXC Unprivileged to Privileged Converter
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
|
||||
# Version : 2.0
|
||||
# Last Updated: 19/08/2025
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# This script converts an unprivileged LXC container to a privileged one
|
||||
# by directly modifying the configuration file.
|
||||
# WARNING: This reduces security. Use only when necessary.
|
||||
# ==========================================================
|
||||
|
||||
# Configuration ============================================
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
VENV_PATH="/opt/googletrans-env"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
# ==========================================================
|
||||
|
||||
|
||||
|
||||
select_unprivileged_container() {
|
||||
|
||||
CONTAINERS=$(pct list | awk 'NR>1 {print $1, $3}' | while read id name; do
|
||||
if pct config "$id" | grep -q "^unprivileged: 1"; then
|
||||
echo "$id" "$name"
|
||||
fi
|
||||
done | xargs -n2)
|
||||
|
||||
if [ -z "$CONTAINERS" ]; then
|
||||
msg_error "$(translate 'No unprivileged containers available in Proxmox.')"
|
||||
exit 1
|
||||
fi
|
||||
cleanup
|
||||
CONTAINER_ID=$(whiptail --title "$(translate 'Select Unprivileged Container')" \
|
||||
--menu "$(translate 'Select the unprivileged LXC container to convert:')" 20 70 10 $CONTAINERS 3>&1 1>&2 2>&3)
|
||||
|
||||
if [ -z "$CONTAINER_ID" ]; then
|
||||
msg_error "$(translate 'No container selected. Exiting.')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msg_ok "$(translate 'Unprivileged container selected:') $CONTAINER_ID"
|
||||
}
|
||||
|
||||
show_backup_warning() {
|
||||
if ! whiptail --title "$(translate 'Backup Recommendation')" \
|
||||
--yes-button "$(translate 'Continue')" \
|
||||
--no-button "$(translate 'Exit')" \
|
||||
--yesno "$(translate 'It is recommended to create a backup before continuing.')" \
|
||||
12 70; then
|
||||
msg_info "$(translate 'Operation cancelled by user to create backup.')"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
convert_to_privileged() {
|
||||
CONF_FILE="/etc/pve/lxc/$CONTAINER_ID.conf"
|
||||
|
||||
CONTAINER_STATUS=$(pct status "$CONTAINER_ID" | awk '{print $2}')
|
||||
|
||||
if [ "$CONTAINER_STATUS" == "running" ]; then
|
||||
msg_info "$(translate 'Stopping container') $CONTAINER_ID..."
|
||||
pct shutdown "$CONTAINER_ID"
|
||||
|
||||
# Wait for container to stop
|
||||
for i in {1..10}; do
|
||||
sleep 1
|
||||
if [ "$(pct status "$CONTAINER_ID" | awk '{print $2}')" != "running" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Verify container stopped
|
||||
if [ "$(pct status "$CONTAINER_ID" | awk '{print $2}')" == "running" ]; then
|
||||
msg_error "$(translate 'Failed to stop the container.')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msg_ok "$(translate 'Container stopped.')"
|
||||
else
|
||||
msg_ok "$(translate 'Container is already stopped.')"
|
||||
fi
|
||||
|
||||
msg_info "$(translate 'Creating backup of configuration file...')"
|
||||
cp "$CONF_FILE" "$CONF_FILE.bak"
|
||||
msg_ok "$(translate 'Configuration backup created:') $CONF_FILE.bak"
|
||||
|
||||
msg_info "$(translate 'Converting container to privileged...')"
|
||||
sed -i '/^unprivileged: 1/d' "$CONF_FILE"
|
||||
echo "unprivileged: 0" >> "$CONF_FILE"
|
||||
|
||||
msg_ok "$(translate 'Container successfully converted to privileged.')"
|
||||
|
||||
echo -e
|
||||
msg_success "Press Enter to continue..."
|
||||
read -r
|
||||
}
|
||||
|
||||
finalize_conversion() {
|
||||
|
||||
if whiptail --yesno "$(translate 'Do you want to start the privileged container') $CONTAINER_ID $(translate 'now?')" 10 60; then
|
||||
msg_info "$(translate 'Starting privileged container...')"
|
||||
pct start "$CONTAINER_ID"
|
||||
msg_ok "$(translate 'Privileged container') $CONTAINER_ID $(translate 'started successfully.')"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "LXC Unprivileged to Privileged conversion")"
|
||||
msg_info "$(translate 'Starting LXC Unprivileged to Privileged conversion process...')"
|
||||
|
||||
|
||||
select_unprivileged_container
|
||||
show_backup_warning
|
||||
convert_to_privileged
|
||||
finalize_conversion
|
||||
|
||||
msg_ok "$(translate 'LXC conversion from unprivileged to privileged completed successfully!')"
|
||||
msg_ok "$(translate 'Converted container ID:') $CONTAINER_ID"
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Execute main function
|
||||
main
|
104
scripts/menus/lxc_menu.sh
Normal file
104
scripts/menus/lxc_menu.sh
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# ProxMenu - LXC Conversion Management Menu
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
|
||||
# Version : 1.0
|
||||
# Last Updated: 19/08/2025
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# This script provides a menu interface for LXC container privilege conversions.
|
||||
# Allows converting between privileged and unprivileged containers safely.
|
||||
# ==========================================================
|
||||
|
||||
# Configuration ============================================
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
VENV_PATH="/opt/googletrans-env"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
# ==========================================================
|
||||
|
||||
show_main_menu() {
|
||||
CHOICE=$(dialog --backtitle "ProxMenux" --title "$(translate 'LXC Management')" \
|
||||
--menu "$(translate 'Select conversion option:')" 20 70 10 \
|
||||
"1" "$(translate 'Convert Privileged to Unprivileged')" \
|
||||
"2" "$(translate 'Convert Unprivileged to Privileged')" \
|
||||
"3" "$(translate 'Show Container Privilege Status')" \
|
||||
"4" "$(translate "Help & Info (commands)")" \
|
||||
"5" "$(translate 'Exit')" 3>&1 1>&2 2>&3)
|
||||
|
||||
case $CHOICE in
|
||||
1)
|
||||
bash <(curl -s "$REPO_URL/scripts/lxc/lxc-privileged-to-unprivileged.sh")
|
||||
;;
|
||||
2)
|
||||
bash <(curl -s "$REPO_URL/scripts/lxc/lxc-unprivileged-to-privileged.sh")
|
||||
;;
|
||||
3)
|
||||
show_container_status
|
||||
;;
|
||||
4)
|
||||
bash <(curl -s "$REPO_URL/scripts/lxc/lxc-conversion-manual-guide.sh")
|
||||
;;
|
||||
5)
|
||||
exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh")
|
||||
;;
|
||||
*)
|
||||
exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh")
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
|
||||
show_container_status() {
|
||||
msg_info "$(translate 'Gathering container privilege information...')"
|
||||
|
||||
|
||||
TEMP_FILE=$(mktemp)
|
||||
|
||||
echo "$(translate 'LXC Container Privilege Status')" > "$TEMP_FILE"
|
||||
echo "=================================" >> "$TEMP_FILE"
|
||||
echo "" >> "$TEMP_FILE"
|
||||
|
||||
|
||||
pct list | awk 'NR>1 {print $1, $3}' | while read id name; do
|
||||
if pct config "$id" | grep -q "^unprivileged: 1"; then
|
||||
status="$(translate 'Unprivileged')"
|
||||
else
|
||||
status="$(translate 'Privileged')"
|
||||
fi
|
||||
|
||||
running_status=$(pct status "$id" | grep -q "running" && echo "$(translate 'Running')" || echo "$(translate 'Stopped')")
|
||||
|
||||
printf "ID: %-4s | %-20s | %-12s | %s\n" "$id" "$name" "$status" "$running_status" >> "$TEMP_FILE"
|
||||
done
|
||||
|
||||
echo "" >> "$TEMP_FILE"
|
||||
echo "$(translate 'Legend:')" >> "$TEMP_FILE"
|
||||
echo "$(translate 'Privileged: Full host access (less secure)')" >> "$TEMP_FILE"
|
||||
echo "$(translate 'Unprivileged: Limited access (more secure)')" >> "$TEMP_FILE"
|
||||
|
||||
cleanup
|
||||
dialog --title "$(translate 'Container Status')" --textbox "$TEMP_FILE" 25 80
|
||||
|
||||
|
||||
rm -f "$TEMP_FILE"
|
||||
|
||||
|
||||
show_main_menu
|
||||
}
|
||||
|
||||
|
||||
|
||||
show_main_menu
|
@@ -16,27 +16,87 @@ BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
VENV_PATH="/opt/googletrans-env"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
# ==========================================================
|
||||
|
||||
if ! command -v dialog &>/dev/null; then
|
||||
apt update -qq >/dev/null 2>&1
|
||||
apt install -y dialog >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
|
||||
check_pve9_translation_compatibility() {
|
||||
local pve_version
|
||||
|
||||
if command -v pveversion &>/dev/null; then
|
||||
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -n "$pve_version" ]] && [[ "$pve_version" -ge 9 ]] && [[ -d "$VENV_PATH" ]]; then
|
||||
|
||||
local has_googletrans=false
|
||||
local has_cache=false
|
||||
|
||||
if [[ -f "$VENV_PATH/bin/pip" ]]; then
|
||||
if "$VENV_PATH/bin/pip" list 2>/dev/null | grep -q "googletrans"; then
|
||||
has_googletrans=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "$BASE_DIR/cache.json" ]]; then
|
||||
has_cache=true
|
||||
fi
|
||||
|
||||
if [[ "$has_googletrans" = true ]] || [[ "$has_cache" = true ]]; then
|
||||
|
||||
dialog --clear \
|
||||
--backtitle "ProxMenux - Compatibility Required" \
|
||||
--title "Translation Environment Incompatible with PVE $pve_version" \
|
||||
--msgbox "NOTICE: You are running Proxmox VE $pve_version with translation components installed.\n\nTranslations are NOT supported in PVE 9+. This causes:\n• Menu loading errors\n• Translation failures\n• System instability\n\nREQUIRED ACTION:\nProxMenux will now automatically reinstall the Normal Version.\n\nThis process will:\n• Remove incompatible translation components\n• Install PVE 9+ compatible version\n• Preserve all your settings and preferences\n\nPress OK to continue with automatic reinstallation..." 20 75
|
||||
|
||||
bash <(curl -sSL "$REPO_URL/install_proxmenux.sh")
|
||||
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
check_pve9_translation_compatibility
|
||||
|
||||
# ==========================================================
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
|
||||
if [[ "$PROXMENUX_PVE9_WARNING_SHOWN" = "1" ]]; then
|
||||
|
||||
if ! load_language 2>/dev/null; then
|
||||
LANGUAGE="en"
|
||||
fi
|
||||
|
||||
else
|
||||
load_language
|
||||
initialize_cache
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
|
||||
show_menu() {
|
||||
local TEMP_FILE
|
||||
TEMP_FILE=$(mktemp)
|
||||
|
||||
while true; do
|
||||
|
||||
local menu_title="Main ProxMenux"
|
||||
if [[ -n "$PROXMENUX_PVE9_WARNING_SHOWN" ]]; then
|
||||
menu_title="Main ProxMenux"
|
||||
fi
|
||||
|
||||
dialog --clear \
|
||||
--backtitle "ProxMenux" \
|
||||
--title "$(translate "Main ProxMenux")" \
|
||||
--title "$(translate "$menu_title")" \
|
||||
--menu "$(translate "Select an option:")" 20 70 10 \
|
||||
1 "$(translate "Settings post-install Proxmox")" \
|
||||
2 "$(translate "Help and Info Commands")" \
|
||||
@@ -52,7 +112,6 @@ show_menu() {
|
||||
local EXIT_STATUS=$?
|
||||
|
||||
if [[ $EXIT_STATUS -ne 0 ]]; then
|
||||
# ESC pressed or Cancel
|
||||
clear
|
||||
msg_ok "$(translate "Thank you for using ProxMenux. Goodbye!")"
|
||||
rm -f "$TEMP_FILE"
|
||||
@@ -77,6 +136,4 @@ show_menu() {
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
show_menu
|
||||
|
82
scripts/menus/main_menu_.sh
Normal file
82
scripts/menus/main_menu_.sh
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# ProxMenu - A menu-driven script for Proxmox VE management
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
|
||||
# Version : 2.0
|
||||
# Last Updated: 04/04/2025
|
||||
# ==========================================================
|
||||
|
||||
# Configuration ============================================
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
VENV_PATH="/opt/googletrans-env"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
# ==========================================================
|
||||
|
||||
if ! command -v dialog &>/dev/null; then
|
||||
apt update -qq >/dev/null 2>&1
|
||||
apt install -y dialog >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
show_menu() {
|
||||
local TEMP_FILE
|
||||
TEMP_FILE=$(mktemp)
|
||||
|
||||
while true; do
|
||||
dialog --clear \
|
||||
--backtitle "ProxMenux" \
|
||||
--title "$(translate "Main ProxMenux")" \
|
||||
--menu "$(translate "Select an option:")" 20 70 10 \
|
||||
1 "$(translate "Settings post-install Proxmox")" \
|
||||
2 "$(translate "Help and Info Commands")" \
|
||||
3 "$(translate "Hardware: GPUs and Coral-TPU")" \
|
||||
4 "$(translate "Create VM from template or script")" \
|
||||
5 "$(translate "Disk and Storage Manager")" \
|
||||
6 "$(translate "Proxmox VE Helper Scripts")" \
|
||||
7 "$(translate "Network Management")" \
|
||||
8 "$(translate "Utilities and Tools")" \
|
||||
9 "$(translate "Settings")" \
|
||||
0 "$(translate "Exit")" 2>"$TEMP_FILE"
|
||||
|
||||
local EXIT_STATUS=$?
|
||||
|
||||
if [[ $EXIT_STATUS -ne 0 ]]; then
|
||||
# ESC pressed or Cancel
|
||||
clear
|
||||
msg_ok "$(translate "Thank you for using ProxMenux. Goodbye!")"
|
||||
rm -f "$TEMP_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
OPTION=$(<"$TEMP_FILE")
|
||||
|
||||
case $OPTION in
|
||||
1) exec bash <(curl -s "$REPO_URL/scripts/menus/menu_post_install.sh") ;;
|
||||
2) bash <(curl -s "$REPO_URL/scripts/help_info_menu.sh") ;;
|
||||
3) exec bash <(curl -s "$REPO_URL/scripts/menus/hw_grafics_menu.sh") ;;
|
||||
4) exec bash <(curl -s "$REPO_URL/scripts/menus/create_vm_menu.sh") ;;
|
||||
5) exec bash <(curl -s "$REPO_URL/scripts/menus/storage_menu.sh") ;;
|
||||
6) exec bash <(curl -s "$REPO_URL/scripts/menus/menu_Helper_Scripts.sh") ;;
|
||||
7) exec bash <(curl -s "$REPO_URL/scripts/menus/network_menu.sh") ;;
|
||||
8) exec bash <(curl -s "$REPO_URL/scripts/menus/utilities_menu.sh") ;;
|
||||
9) exec bash <(curl -s "$REPO_URL/scripts/menus/config_menu.sh") ;;
|
||||
0) clear; msg_ok "$(translate "Thank you for using ProxMenu. Goodbye!")"; rm -f "$TEMP_FILE"; exit 0 ;;
|
||||
*) msg_warn "$(translate "Invalid option")"; sleep 2 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
show_menu
|
88
scripts/menus/share_menu.sh
Normal file
88
scripts/menus/share_menu.sh
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# ProxMenux - Network Storage Manager Menu
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : MIT
|
||||
# Version : 1.2
|
||||
# Last Updated: $(date +%d/%m/%Y)
|
||||
# ==========================================================
|
||||
|
||||
# Configuration
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
VENV_PATH="/opt/googletrans-env"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
# ==========================================================
|
||||
|
||||
while true; do
|
||||
OPTION=$(dialog --colors --backtitle "ProxMenux" \
|
||||
--title "$(translate "Mount and Share Manager")" \
|
||||
--menu "\n$(translate "Select an option:")" 25 80 15 \
|
||||
"" "\Z4──────────────────────── $(translate "HOST") ─────────────────────────\Zn" \
|
||||
"1" "$(translate "Configure NFS shared on Host")" \
|
||||
"2" "$(translate "Configure Samba shared on Host")" \
|
||||
"3" "$(translate "Configure Local Shared on Host")" \
|
||||
"" "\Z4──────────────────────── $(translate "LXC") ─────────────────────────\Zn" \
|
||||
"4" "$(translate "Configure LXC Mount Points (Host ↔ Container)")" \
|
||||
"" "" \
|
||||
"5" "$(translate "Configure NFS Client in LXC (only privileged)")" \
|
||||
"6" "$(translate "Configure Samba Client in LXC (only privileged)")" \
|
||||
"7" "$(translate "Configure NFS Server in LXC (only privileged)")" \
|
||||
"8" "$(translate "configure Samba Server in LXC (only privileged)")" \
|
||||
"" "" \
|
||||
"h" "$(translate "Help & Info (commands)")" \
|
||||
"0" "$(translate "Return to Main Menu")" \
|
||||
2>&1 >/dev/tty
|
||||
) || { exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh"); }
|
||||
|
||||
case "$OPTION" in
|
||||
|
||||
lxctitle|hosttitle)
|
||||
continue
|
||||
;;
|
||||
|
||||
1)
|
||||
bash <(curl -s "$REPO_URL/scripts/share/nfs_host.sh")
|
||||
;;
|
||||
2)
|
||||
bash <(curl -s "$REPO_URL/scripts/share/samba_host.sh")
|
||||
;;
|
||||
3)
|
||||
bash <(curl -s "$REPO_URL/scripts/share/local-shared-manager.sh")
|
||||
;;
|
||||
4)
|
||||
bash <(curl -s "$REPO_URL/scripts/share/lxc-mount-manager_minimal.sh")
|
||||
;;
|
||||
5)
|
||||
bash <(curl -s "$REPO_URL/scripts/share/nfs_client.sh")
|
||||
;;
|
||||
6)
|
||||
bash <(curl -s "$REPO_URL/scripts/share/samba_client.sh")
|
||||
;;
|
||||
7)
|
||||
bash <(curl -s "$REPO_URL/scripts/share/nfs_lxc_server.sh")
|
||||
;;
|
||||
8)
|
||||
bash <(curl -s "$REPO_URL/scripts/share/samba_lxc_server.sh")
|
||||
;;
|
||||
h)
|
||||
bash <(curl -s "$REPO_URL/scripts/share/commands_share.sh")
|
||||
;;
|
||||
0)
|
||||
exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh")
|
||||
;;
|
||||
*)
|
||||
exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh")
|
||||
;;
|
||||
esac
|
||||
done
|
@@ -36,15 +36,12 @@ while true; do
|
||||
|
||||
case $OPTION in
|
||||
1)
|
||||
clear
|
||||
bash <(curl -s "$REPO_URL/scripts/storage/disk-passthrough.sh")
|
||||
;;
|
||||
2)
|
||||
clear
|
||||
bash <(curl -s "$REPO_URL/scripts/storage/disk-passthrough_ct.sh")
|
||||
;;
|
||||
3)
|
||||
clear
|
||||
bash <(curl -s "$REPO_URL/scripts/storage/import-disk-image.sh")
|
||||
;;
|
||||
4)
|
||||
|
@@ -26,11 +26,12 @@ initialize_cache
|
||||
|
||||
while true; do
|
||||
OPTION=$(dialog --clear --backtitle "ProxMenux" --title "$(translate "Utilities Menu")" \
|
||||
--menu "\n$(translate "Select an option:")" 20 70 8 \
|
||||
--menu "$(translate "Select an option:")" 20 70 8 \
|
||||
"1" "$(translate "UUp Dump ISO creator Custom")" \
|
||||
"2" "$(translate "System Utilities Installer")" \
|
||||
"3" "$(translate "Proxmox System Update")" \
|
||||
"4" "$(translate "Return to Main Menu")" \
|
||||
"4" "$(translate "Upgrade PVE 8 to PVE 9")" \
|
||||
"5" "$(translate "Return to Main Menu")" \
|
||||
2>&1 >/dev/tty)
|
||||
|
||||
case $OPTION in
|
||||
@@ -69,7 +70,13 @@ initialize_cache
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
4) exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh") ;;
|
||||
4)
|
||||
bash <(curl -s "$REPO_URL/scripts/utilities/upgrade_pve8_to_pve9.sh")
|
||||
if [ $? -ne 0 ]; then
|
||||
return
|
||||
fi
|
||||
;;
|
||||
5) exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh") ;;
|
||||
*) exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh") ;;
|
||||
esac
|
||||
done
|
@@ -164,15 +164,17 @@ remove_subscription_banner() {
|
||||
# ==========================================================
|
||||
|
||||
|
||||
configure_time_sync() {
|
||||
msg_info "$(translate "Configuring system time settings...")"
|
||||
configure_time_sync_() {
|
||||
msg_info2 "$(translate "Configuring system time settings...")"
|
||||
|
||||
|
||||
# Get public IP address
|
||||
this_ip=$(dig +short myip.opendns.com @resolver1.opendns.com)
|
||||
if [ -z "$this_ip" ]; then
|
||||
msg_warn "$(translate "Failed to obtain public IP address")"
|
||||
timezone="UTC"
|
||||
else
|
||||
|
||||
# Get timezone based on IP
|
||||
timezone=$(curl -s "https://ipapi.co/${this_ip}/timezone")
|
||||
if [ -z "$timezone" ]; then
|
||||
msg_warn "$(translate "Failed to determine timezone from IP address")"
|
||||
@@ -182,16 +184,64 @@ configure_time_sync() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set the timezone
|
||||
if timedatectl set-timezone "$timezone"; then
|
||||
msg_ok "$(translate "Timezone set to $timezone")"
|
||||
else
|
||||
msg_error "$(translate "Failed to set timezone to $timezone")"
|
||||
fi
|
||||
|
||||
# Configure time synchronization
|
||||
msg_info "$(translate "Enabling automatic time synchronization...")"
|
||||
if timedatectl set-ntp true; then
|
||||
msg_ok "$(translate "Time settings configured - Timezone:") $timezone"
|
||||
systemctl restart postfix 2>/dev/null || true
|
||||
msg_ok "$(translate "Automatic time synchronization enabled")"
|
||||
register_tool "time_sync" true
|
||||
else
|
||||
msg_error "$(translate "Failed to enable automatic time synchronization")"
|
||||
fi
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
configure_time_sync() {
|
||||
msg_info2 "$(translate "Configuring system time settings...")"
|
||||
|
||||
this_ip=$(dig +short myip.opendns.com @resolver1.opendns.com 2>/dev/null)
|
||||
if [ -z "$this_ip" ]; then
|
||||
msg_warn "$(translate "Failed to obtain public IP address - keeping current timezone settings")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
timezone=$(curl -s --connect-timeout 10 "https://ipapi.co/${this_ip}/timezone" 2>/dev/null)
|
||||
if [ -z "$timezone" ] || [ "$timezone" = "undefined" ]; then
|
||||
msg_warn "$(translate "Failed to determine timezone from IP address - keeping current timezone settings")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
msg_ok "$(translate "Found timezone $timezone for IP $this_ip")"
|
||||
|
||||
if timedatectl set-timezone "$timezone"; then
|
||||
msg_ok "$(translate "Timezone set to $timezone")"
|
||||
|
||||
if timedatectl set-ntp true; then
|
||||
msg_ok "$(translate "Time settings configured - Timezone:") $timezone"
|
||||
register_tool "time_sync" true
|
||||
|
||||
systemctl restart postfix 2>/dev/null || true
|
||||
else
|
||||
msg_warn "$(translate "Failed to enable automatic time synchronization")"
|
||||
fi
|
||||
else
|
||||
msg_warn "$(translate "Failed to set timezone - keeping current settings")"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# ==========================================================
|
||||
skip_apt_languages() {
|
||||
msg_info "$(translate "Configuring APT to skip downloading additional languages...")"
|
||||
@@ -228,11 +278,10 @@ optimize_journald() {
|
||||
[Journal]
|
||||
Storage=persistent
|
||||
SplitMode=none
|
||||
RateLimitInterval=0
|
||||
RateLimitIntervalSec=0
|
||||
RateLimitBurst=0
|
||||
RateLimitIntervalSec=30s
|
||||
RateLimitBurst=1000
|
||||
ForwardToSyslog=no
|
||||
ForwardToWall=yes
|
||||
ForwardToWall=no
|
||||
Seal=no
|
||||
Compress=yes
|
||||
SystemMaxUse=64M
|
||||
@@ -418,59 +467,59 @@ force_apt_ipv4() {
|
||||
}
|
||||
|
||||
# ==========================================================
|
||||
|
||||
apply_network_optimizations() {
|
||||
msg_info "$(translate "Optimizing network settings...")"
|
||||
NECESSARY_REBOOT=1
|
||||
|
||||
cat <<EOF > /etc/sysctl.d/99-network.conf
|
||||
cat <<'EOF' > /etc/sysctl.d/99-network.conf
|
||||
# ==========================================================
|
||||
# ProxMenux - Network tuning (PVE 9 compatible)
|
||||
# ==========================================================
|
||||
|
||||
# Core buffers & queues
|
||||
net.core.netdev_max_backlog = 8192
|
||||
net.core.optmem_max=8192
|
||||
net.core.rmem_max = 16777216
|
||||
net.core.somaxconn=8151
|
||||
net.core.wmem_max = 16777216
|
||||
net.core.somaxconn = 8192
|
||||
|
||||
# IPv4
|
||||
net.ipv4.conf.all.accept_redirects = 0
|
||||
net.ipv4.conf.all.accept_source_route = 0
|
||||
net.ipv4.conf.all.log_martians = 0
|
||||
net.ipv4.conf.all.rp_filter = 1
|
||||
net.ipv4.conf.all.secure_redirects = 0
|
||||
net.ipv4.conf.all.send_redirects = 0
|
||||
net.ipv4.conf.all.log_martians = 1
|
||||
|
||||
net.ipv4.conf.default.accept_redirects = 0
|
||||
net.ipv4.conf.default.accept_source_route = 0
|
||||
net.ipv4.conf.default.log_martians = 0
|
||||
net.ipv4.conf.default.rp_filter = 1
|
||||
net.ipv4.conf.default.secure_redirects = 0
|
||||
net.ipv4.conf.default.send_redirects = 0
|
||||
net.ipv4.conf.default.log_martians = 1
|
||||
|
||||
# rp_filter: loose multi-homed/bridges
|
||||
net.ipv4.conf.all.rp_filter = 2
|
||||
net.ipv4.conf.default.rp_filter = 2
|
||||
|
||||
# ICMP
|
||||
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
||||
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
||||
|
||||
# TCP/IP
|
||||
net.ipv4.ip_local_port_range = 1024 65535
|
||||
net.ipv4.tcp_base_mss = 1024
|
||||
net.ipv4.tcp_challenge_ack_limit = 999999999
|
||||
net.ipv4.tcp_fin_timeout=10
|
||||
net.ipv4.tcp_keepalive_intvl=30
|
||||
net.ipv4.tcp_keepalive_probes=3
|
||||
net.ipv4.tcp_keepalive_time=240
|
||||
net.ipv4.tcp_limit_output_bytes=65536
|
||||
net.ipv4.tcp_max_syn_backlog=8192
|
||||
net.ipv4.tcp_max_tw_buckets = 1440000
|
||||
net.ipv4.tcp_mtu_probing = 1
|
||||
net.ipv4.tcp_rfc1337 = 1
|
||||
net.ipv4.tcp_rmem=8192 87380 16777216
|
||||
net.ipv4.tcp_sack = 1
|
||||
net.ipv4.tcp_slow_start_after_idle=0
|
||||
net.ipv4.tcp_syn_retries=3
|
||||
net.ipv4.tcp_synack_retries = 2
|
||||
net.ipv4.tcp_tw_recycle = 0
|
||||
net.ipv4.tcp_tw_reuse = 0
|
||||
net.ipv4.tcp_rmem = 8192 87380 16777216
|
||||
net.ipv4.tcp_wmem = 8192 65536 16777216
|
||||
net.netfilter.nf_conntrack_generic_timeout = 60
|
||||
net.netfilter.nf_conntrack_helper=0
|
||||
net.netfilter.nf_conntrack_max = 524288
|
||||
net.netfilter.nf_conntrack_tcp_timeout_established = 28800
|
||||
|
||||
# Unix sockets
|
||||
net.unix.max_dgram_qlen = 4096
|
||||
EOF
|
||||
|
||||
|
||||
sysctl --system > /dev/null 2>&1
|
||||
|
||||
|
||||
local interfaces_file="/etc/network/interfaces"
|
||||
if ! grep -q 'source /etc/network/interfaces.d/*' "$interfaces_file"; then
|
||||
echo "source /etc/network/interfaces.d/*" >> "$interfaces_file"
|
||||
@@ -480,6 +529,7 @@ EOF
|
||||
register_tool "network_optimization" true
|
||||
}
|
||||
|
||||
|
||||
# ==========================================================
|
||||
disable_rpc() {
|
||||
msg_info "$(translate "Disabling portmapper/rpcbind for security...")"
|
||||
@@ -596,7 +646,8 @@ install_log2ram_auto() {
|
||||
else
|
||||
|
||||
ROOT_PART=$(lsblk -no NAME,MOUNTPOINT | grep ' /$' | awk '{print $1}')
|
||||
SYSTEM_DISK=$(lsblk -no PKNAME /dev/$ROOT_PART 2>/dev/null)
|
||||
#SYSTEM_DISK=$(lsblk -no PKNAME /dev/$ROOT_PART 2>/dev/null)
|
||||
SYSTEM_DISK=$(lsblk -no PKNAME /dev/$ROOT_PART 2>/dev/null | grep -E '^[a-z]+' | head -n1)
|
||||
SYSTEM_DISK=${SYSTEM_DISK:-sda}
|
||||
if [[ "$SYSTEM_DISK" == nvme* || "$(cat /sys/block/$SYSTEM_DISK/queue/rotational 2>/dev/null)" == "0" ]]; then
|
||||
is_ssd=true
|
||||
@@ -606,9 +657,14 @@ install_log2ram_auto() {
|
||||
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.")"
|
||||
if whiptail --yesno "$(translate "Do you want to install Log2RAM anyway to reduce log write load?")" \
|
||||
10 70 --title "Log2RAM"; then
|
||||
:
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if [[ -f /etc/log2ram.conf ]] && command -v log2ram >/dev/null 2>&1 && systemctl list-units --all | grep -q log2ram; then
|
||||
@@ -641,7 +697,7 @@ install_log2ram_auto() {
|
||||
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
|
||||
msg_ok "$(translate "Git installed successfully")"
|
||||
#msg_ok "$(translate "Git installed successfully")"
|
||||
fi
|
||||
|
||||
if ! git clone https://github.com/azlux/log2ram.git /tmp/log2ram >/dev/null 2>>/tmp/log2ram_install.log; then
|
||||
@@ -686,11 +742,18 @@ install_log2ram_auto() {
|
||||
|
||||
|
||||
sed -i "s/^SIZE=.*/SIZE=$LOG2RAM_SIZE/" /etc/log2ram.conf
|
||||
|
||||
|
||||
LOG2RAM_BIN="$(command -v log2ram || echo /usr/local/bin/log2ram)"
|
||||
rm -f /etc/cron.daily/log2ram /etc/cron.weekly/log2ram /etc/cron.monthly/log2ram 2>/dev/null || true
|
||||
rm -f /etc/cron.hourly/log2ram
|
||||
echo "0 */$CRON_HOURS * * * root /usr/sbin/log2ram write" > /etc/cron.d/log2ram
|
||||
msg_ok "$(translate "log2ram write scheduled every") $CRON_HOURS $(translate "hour(s)")"
|
||||
|
||||
{
|
||||
echo 'MAILTO=""'
|
||||
echo "0 */$CRON_HOURS * * * root $LOG2RAM_BIN write >/dev/null 2>&1"
|
||||
} > /etc/cron.d/log2ram
|
||||
|
||||
chmod 0644 /etc/cron.d/log2ram
|
||||
chown root:root /etc/cron.d/log2ram
|
||||
msg_ok "$(translate "Log2RAM write scheduled every") $CRON_HOURS $(translate "hour(s)")"
|
||||
|
||||
|
||||
cat << 'EOF' > /usr/local/bin/log2ram-check.sh
|
||||
@@ -706,7 +769,12 @@ 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
|
||||
{
|
||||
echo 'MAILTO=""'
|
||||
echo "*/5 * * * * root /usr/local/bin/log2ram-check.sh >/dev/null 2>&1"
|
||||
} > /etc/cron.d/log2ram-auto-sync
|
||||
chmod 0644 /etc/cron.d/log2ram-auto-sync
|
||||
chown root:root /etc/cron.d/log2ram-auto-sync
|
||||
msg_ok "$(translate "Auto-sync enabled when /var/log exceeds 90% of") $LOG2RAM_SIZE"
|
||||
|
||||
register_tool "log2ram" true
|
||||
@@ -775,8 +843,9 @@ run_complete_optimization() {
|
||||
ensure_tools_json
|
||||
|
||||
apt_upgrade
|
||||
cleanup
|
||||
remove_subscription_banner
|
||||
configure_time_sync
|
||||
#configure_time_sync
|
||||
skip_apt_languages
|
||||
optimize_journald
|
||||
optimize_logrotate
|
||||
@@ -786,7 +855,7 @@ run_complete_optimization() {
|
||||
configure_kernel_panic
|
||||
force_apt_ipv4
|
||||
apply_network_optimizations
|
||||
disable_rpc
|
||||
#disable_rpc
|
||||
customize_bashrc
|
||||
install_log2ram_auto
|
||||
setup_persistent_network
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -489,6 +489,93 @@ uninstall_persistent_network() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
uninstall_amd_fixes() {
|
||||
msg_info2 "$(translate "Reverting AMD (Ryzen/EPYC) fixes...")"
|
||||
NECESSARY_REBOOT=1
|
||||
|
||||
|
||||
if grep -q "root=ZFS=" /proc/cmdline 2>/dev/null; then
|
||||
|
||||
cmdline_file="/etc/kernel/cmdline"
|
||||
if [[ -f "$cmdline_file" ]] && grep -q "idle=nomwait" "$cmdline_file"; then
|
||||
cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)" || {
|
||||
msg_error "$(translate "Failed to backup $cmdline_file")"
|
||||
return 1
|
||||
}
|
||||
|
||||
sed -i 's/\bidle=nomwait\b//g; s/[[:space:]]\+/ /g; s/^ //; s/ $//' "$cmdline_file"
|
||||
|
||||
if command -v proxmox-boot-tool >/dev/null 2>&1; then
|
||||
proxmox-boot-tool refresh >/dev/null 2>&1 || {
|
||||
msg_error "$(translate "Failed to refresh boot configuration")"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
msg_ok "$(translate "Removed idle=nomwait from /etc/kernel/cmdline (ZFS)")"
|
||||
fi
|
||||
else
|
||||
|
||||
grub_file="/etc/default/grub"
|
||||
if [[ -f "$grub_file" ]] && grep -q 'GRUB_CMDLINE_LINUX_DEFAULT=' "$grub_file"; then
|
||||
if grep -q "idle=nomwait" "$grub_file"; then
|
||||
cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)" || {
|
||||
msg_error "$(translate "Failed to backup $grub_file")"
|
||||
return 1
|
||||
}
|
||||
|
||||
sed -i -E 's/(GRUB_CMDLINE_LINUX_DEFAULT=")/\1/; s/\bidle=nomwait\b//g' "$grub_file"
|
||||
|
||||
awk -F\" '
|
||||
$1=="GRUB_CMDLINE_LINUX_DEFAULT=" {
|
||||
gsub(/[[:space:]]+/," ",$2); sub(/^ /,"",$2); sub(/ $/,"",$2)
|
||||
}1
|
||||
' OFS="\"" "$grub_file" > "${grub_file}.tmp" && mv "${grub_file}.tmp" "$grub_file"
|
||||
|
||||
update-grub >/dev/null 2>&1 || {
|
||||
msg_error "$(translate "Failed to update GRUB configuration")"
|
||||
return 1
|
||||
}
|
||||
msg_ok "$(translate "Removed idle=nomwait from GRUB configuration")"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
kvm_conf="/etc/modprobe.d/kvm.conf"
|
||||
if [[ -f "$kvm_conf" ]]; then
|
||||
if grep -Eq '(ignore_msrs|report_ignored_msrs)' "$kvm_conf"; then
|
||||
cp "$kvm_conf" "${kvm_conf}.bak.$(date +%Y%m%d_%H%M%S)" || {
|
||||
msg_error "$(translate "Failed to backup $kvm_conf")"
|
||||
return 1
|
||||
}
|
||||
sed -i -E '/ignore_msrs|report_ignored_msrs/d' "$kvm_conf"
|
||||
|
||||
if [[ ! -s "$kvm_conf" ]]; then
|
||||
rm -f "$kvm_conf"
|
||||
msg_ok "$(translate "Removed empty KVM configuration file")"
|
||||
else
|
||||
msg_ok "$(translate "Removed KVM MSR options from configuration")"
|
||||
fi
|
||||
|
||||
update-initramfs -u -k all >/dev/null 2>&1 || true
|
||||
else
|
||||
msg_ok "$(translate "KVM MSR options not present, nothing to revert")"
|
||||
fi
|
||||
fi
|
||||
|
||||
msg_success "$(translate "AMD fixes have been successfully reverted")"
|
||||
register_tool "amd_fixes" false
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
################################################################
|
||||
|
||||
migrate_installed_tools() {
|
||||
@@ -604,6 +691,7 @@ show_uninstall_menu() {
|
||||
figurine) desc="Figurine";;
|
||||
fastfetch) desc="Fastfetch";;
|
||||
log2ram) desc="Log2ram (SSD Protection)";;
|
||||
amd_fixes) desc="AMD CPU (Ryzen/EPYC) fixes";;
|
||||
persistent_network) desc="Setting persistent network interfaces";;
|
||||
*) desc="$tool";;
|
||||
esac
|
||||
|
472
scripts/share/commands_share.sh
Normal file
472
scripts/share/commands_share.sh
Normal file
@@ -0,0 +1,472 @@
|
||||
#!/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.5
|
||||
# Last Updated: 04/08/2025
|
||||
# ==========================================================
|
||||
|
||||
# Configuration ============================================
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
VENV_PATH="/opt/googletrans-env"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
load_language
|
||||
initialize_cache
|
||||
# ==========================================================
|
||||
|
||||
show_command() {
|
||||
local step="$1"
|
||||
local description="$2"
|
||||
local command="$3"
|
||||
local note="$4"
|
||||
local command_extra="$5"
|
||||
|
||||
echo -e "${BGN}${step}.${CL} ${BL}${description}${CL}"
|
||||
echo ""
|
||||
echo -e "${TAB}${command}"
|
||||
echo -e
|
||||
[[ -n "$note" ]] && echo -e "${TAB}${DARK_GRAY}${note}${CL}"
|
||||
[[ -n "$command_extra" ]] && echo -e "${TAB}${YW}${command_extra}${CL}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
show_how_to_enter_lxc() {
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "How to Access an LXC Terminal from Proxmox Host")"
|
||||
|
||||
msg_info2 "$(translate "Use these commands on your Proxmox host to access an LXC container's terminal:")"
|
||||
echo -e
|
||||
|
||||
show_command "1" \
|
||||
"$(translate "Get a list of all your containers:")" \
|
||||
"pct list" \
|
||||
"" \
|
||||
""
|
||||
|
||||
show_command "2" \
|
||||
"$(translate "Enter the container's terminal")" \
|
||||
"pct enter ${CUS}<container-id>${CL}" \
|
||||
"$(translate "Replace <container-id> with the actual ID.")"\
|
||||
"$(translate "For example: pct enter 101")"
|
||||
|
||||
show_command "3" \
|
||||
"$(translate "To exit the container's terminal, press:")" \
|
||||
"CTRL + D" \
|
||||
"" \
|
||||
""
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
show_host_mount_resources_help() {
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Mount Remote Resources on Proxmox Host")"
|
||||
|
||||
msg_info2 "$(translate "How to mount NFS and Samba shares directly on the Proxmox host. Proxmox already has the necessary tools installed.")"
|
||||
echo -e
|
||||
|
||||
echo -e "${BOLD}${BL}=== MOUNT NFS SHARE ===${CL}"
|
||||
echo -e
|
||||
|
||||
show_command "1" \
|
||||
"$(translate "Create mount point:")" \
|
||||
"mkdir -p ${CUS}/mnt/nfs_share${CL}" \
|
||||
"$(translate "Replace with your preferred path.")" \
|
||||
""
|
||||
|
||||
show_command "2" \
|
||||
"$(translate "Mount NFS share:")" \
|
||||
"mount -t nfs ${CUS}192.168.1.100${CL}:${CUS}/path/to/share${CL} ${CUS}/mnt/nfs_share${CL}" \
|
||||
"$(translate "Replace IP and paths with your values.")" \
|
||||
""
|
||||
|
||||
show_command "3" \
|
||||
"$(translate "Make permanent (optional):")" \
|
||||
"echo '${CUS}192.168.1.100${CL}:${CUS}/path/to/share${CL} ${CUS}/mnt/nfs_share${CL} nfs4 rw,hard,intr,_netdev,rsize=1048576,wsize=1048576,timeo=600,retrans=2 0 0' >> /etc/fstab" \
|
||||
"$(translate "_netdev waits for network before mounting.")" \
|
||||
""
|
||||
|
||||
echo -e "${BOLD}${BL}=== MOUNT SAMBA SHARE ===${CL}"
|
||||
echo -e
|
||||
|
||||
show_command "4" \
|
||||
"$(translate "Create mount point:")" \
|
||||
"mkdir -p ${CUS}/mnt/samba_share${CL}" \
|
||||
"$(translate "Replace with your preferred path.")" \
|
||||
""
|
||||
|
||||
show_command "5" \
|
||||
"$(translate "Mount Samba share:")" \
|
||||
"mount -t cifs //${CUS}192.168.1.100${CL}/${CUS}sharename${CL} ${CUS}/mnt/samba_share${CL} -o username=${CUS}user${CL}" \
|
||||
"$(translate "You will be prompted for password. Replace IP, share and user.")" \
|
||||
""
|
||||
|
||||
show_command "6" \
|
||||
"$(translate "Make permanent (optional):")" \
|
||||
"echo '//${CUS}192.168.1.100${CL}/${CUS}sharename${CL} ${CUS}/mnt/samba_share${CL} cifs username=${CUS}user${CL},password=${CUS}pass${CL},_netdev 0 0' >> /etc/fstab" \
|
||||
"$(translate "Replace with your credentials.")" \
|
||||
""
|
||||
|
||||
echo -e "${BOLD}${BL}=== CREATE LOCAL DIRECTORY ===${CL}"
|
||||
echo -e
|
||||
|
||||
show_command "7" \
|
||||
"$(translate "Create directory:")" \
|
||||
"mkdir -p ${CUS}/mnt/local_share${CL}" \
|
||||
"$(translate "Creates a local directory on Proxmox host.")" \
|
||||
""
|
||||
|
||||
show_command "8" \
|
||||
"$(translate "Set permissions:")" \
|
||||
"chmod 755 ${CUS}/mnt/local_share${CL}" \
|
||||
"$(translate "Sets basic read/write permissions.")" \
|
||||
""
|
||||
|
||||
show_command "9" \
|
||||
"$(translate "Verify mounts:")" \
|
||||
"df -h" \
|
||||
"$(translate "Shows all mounted filesystems.")" \
|
||||
""
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
show_host_to_lxc_mount_help() {
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Mount Host Directory to LXC Container")"
|
||||
|
||||
msg_info2 "$(translate "How to mount a Proxmox host directory into an LXC container. Execute these commands on the Proxmox host.")"
|
||||
echo -e
|
||||
|
||||
show_command "1" \
|
||||
"$(translate "Add mount point to container:")" \
|
||||
"pct set ${CUS}<container-id>${CL} -mp0 ${CUS}/host/directory${CL},mp=${CUS}/container/path${CL},backup=0,shared=1" \
|
||||
"$(translate "Replace container-id, host directory and container path.")" \
|
||||
"$(translate "Example: pct set 101 -mp0 /mnt/shared,mp=/mnt/shared,,backup=0,shared=1")"
|
||||
|
||||
show_command "2" \
|
||||
"$(translate "Restart container:")" \
|
||||
"pct reboot ${CUS}<container-id>${CL}" \
|
||||
"$(translate "Required to activate the mount point.")" \
|
||||
""
|
||||
|
||||
show_command "3" \
|
||||
"$(translate "Verify mount inside container:")" \
|
||||
"pct enter ${CUS}<container-id>${CL}
|
||||
df -h | grep ${CUS}/container/path${CL}" \
|
||||
"$(translate "Check if the directory is mounted.")" \
|
||||
""
|
||||
|
||||
show_command "4" \
|
||||
"$(translate "Remove mount point (if needed):")" \
|
||||
"pct set ${CUS}<container-id>${CL} --delete mp0" \
|
||||
"$(translate "Removes the mount point. Use mp1, mp2, etc. for other mounts.")" \
|
||||
""
|
||||
|
||||
echo -e "${BOR}"
|
||||
echo -e "${BOLD}$(translate "Notes:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Mount indices:")${CL} ${BL}Use mp0, mp1, mp2, etc. for multiple mounts${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Permissions:")${CL} ${BL}May need adjustment depending on directory type${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Container types:")${CL} ${BL}Works with both privileged and unprivileged containers${CL}"
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
show_nfs_server_help() {
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "NFS Server Installation")"
|
||||
|
||||
msg_info2 "$(translate "How to install and configure an NFS server in an LXC container.")"
|
||||
echo -e
|
||||
|
||||
show_command "1" \
|
||||
"$(translate "Update and install packages:")" \
|
||||
"apt-get update && apt-get install -y nfs-kernel-server" \
|
||||
"" \
|
||||
""
|
||||
|
||||
show_command "2" \
|
||||
"$(translate "Create export directory:")" \
|
||||
"mkdir -p ${CUS}/mnt/nfs_export${CL}" \
|
||||
"$(translate "Replace with your preferred path.")" \
|
||||
""
|
||||
|
||||
show_command "3" \
|
||||
"$(translate "Set directory permissions:")" \
|
||||
"chmod 755 ${CUS}/mnt/nfs_export${CL}" \
|
||||
"" \
|
||||
""
|
||||
|
||||
show_command "4.1" \
|
||||
"$(translate "Configure exports (safe root_squash):")" \
|
||||
"echo '${CUS}/mnt/nfs_export${CL} ${CUS}192.168.1.0/24${CL}(rw,sync,no_subtree_check,root_squash)' >> /etc/exports" \
|
||||
"$(translate "Replace directory path and network range.")" \
|
||||
""
|
||||
|
||||
show_command "4.2" \
|
||||
"$(translate "Or Configure exports (map all users):")" \
|
||||
"echo '${CUS}/mnt/nfs_export${CL} ${CUS}192.168.1.0/24${CL}(rw,sync,no_subtree_check,all_squash,anonuid=0,anongid=0)' >> /etc/exports" \
|
||||
"$(translate "Replace directory path and network range.")" \
|
||||
""
|
||||
|
||||
|
||||
show_command "5" \
|
||||
"$(translate "Apply configuration:")" \
|
||||
"exportfs -ra" \
|
||||
"" \
|
||||
""
|
||||
|
||||
show_command "6" \
|
||||
"$(translate "Start and enable service:")" \
|
||||
"systemctl restart nfs-kernel-server
|
||||
systemctl enable nfs-kernel-server" \
|
||||
"" \
|
||||
""
|
||||
|
||||
show_command "7" \
|
||||
"$(translate "Verify exports:")" \
|
||||
"showmount -e localhost" \
|
||||
"$(translate "Shows available NFS exports.")" \
|
||||
""
|
||||
|
||||
echo -e "${BOR}"
|
||||
echo -e "${BOLD}$(translate "Export Options:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "rw:")${CL} ${BL}Read-write access${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "sync:")${CL} ${BL}Synchronous writes${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "no_subtree_check:")${CL} ${BL}Improves performance${CL}"
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
show_samba_server_help() {
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Samba Server Installation")"
|
||||
|
||||
msg_info2 "$(translate "How to install and configure a Samba server in an LXC container.")"
|
||||
echo -e
|
||||
|
||||
show_command "1" \
|
||||
"$(translate "Update and install packages:")" \
|
||||
"apt-get update && apt-get install -y samba" \
|
||||
"" \
|
||||
""
|
||||
|
||||
show_command "2" \
|
||||
"$(translate "Create share directory:")" \
|
||||
"mkdir -p ${CUS}/mnt/samba_share${CL}" \
|
||||
"$(translate "Replace with your preferred path.")" \
|
||||
""
|
||||
|
||||
show_command "3" \
|
||||
"$(translate "Set directory permissions:")" \
|
||||
"chmod 755 ${CUS}/mnt/samba_share${CL}" \
|
||||
"" \
|
||||
""
|
||||
|
||||
show_command "4" \
|
||||
"$(translate "Create Samba user:")" \
|
||||
"adduser ${CUS}sambauser${CL}
|
||||
smbpasswd -a ${CUS}sambauser${CL}" \
|
||||
"$(translate "Replace with your username. You'll be prompted for password.")" \
|
||||
""
|
||||
|
||||
show_command "5" \
|
||||
"$(translate "Configure share:")" \
|
||||
"cat >> /etc/samba/smb.conf << EOF
|
||||
[shared]
|
||||
comment = Shared folder
|
||||
path = ${CUS}/mnt/samba_share${CL}
|
||||
read only = no
|
||||
browseable = yes
|
||||
valid users = ${CUS}sambauser${CL}
|
||||
EOF" \
|
||||
"$(translate "Replace path and username.")" \
|
||||
""
|
||||
|
||||
show_command "6" \
|
||||
"$(translate "Restart and enable service:")" \
|
||||
"systemctl restart smbd
|
||||
systemctl enable smbd" \
|
||||
"" \
|
||||
""
|
||||
|
||||
show_command "7" \
|
||||
"$(translate "Test configuration:")" \
|
||||
"smbclient -L localhost -U ${CUS}sambauser${CL}" \
|
||||
"$(translate "Lists available shares. You'll be prompted for password.")" \
|
||||
""
|
||||
|
||||
echo -e "${BOR}"
|
||||
echo -e "${BOLD}$(translate "Connection Examples:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Windows:")${CL} ${YW}\\\\<server-ip>\\shared${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Linux:")${CL} ${YW}smbclient //server-ip/shared -U sambauser${CL}"
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
show_nfs_client_help() {
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "NFS Client Configuration")"
|
||||
|
||||
msg_info2 "$(translate "How to configure an NFS client in an LXC container.")"
|
||||
echo -e
|
||||
|
||||
show_command "1" \
|
||||
"$(translate "Update and install packages:")" \
|
||||
"apt-get update && apt-get install -y nfs-common" \
|
||||
"" \
|
||||
""
|
||||
|
||||
show_command "2" \
|
||||
"$(translate "Create mount point:")" \
|
||||
"mkdir -p ${CUS}/mnt/nfsmount${CL}" \
|
||||
"$(translate "Replace with your preferred path.")" \
|
||||
""
|
||||
|
||||
show_command "3" \
|
||||
"$(translate "Mount NFS share:")" \
|
||||
"mount -t nfs ${CUS}192.168.1.100${CL}:${CUS}/mnt/nfs_export${CL} ${CUS}/mnt/nfsmount${CL}" \
|
||||
"$(translate "Replace server IP and paths.")" \
|
||||
""
|
||||
|
||||
show_command "4" \
|
||||
"$(translate "Test access:")" \
|
||||
"ls -la ${CUS}/mnt/nfsmount${CL}" \
|
||||
"$(translate "Verify you can access the mounted share.")" \
|
||||
""
|
||||
|
||||
show_command "5" \
|
||||
"$(translate "Make permanent (optional):")" \
|
||||
"echo '${CUS}192.168.1.100${CL}:${CUS}/path/to/share${CL} ${CUS}/mnt/nfs_share${CL} nfs4 rw,hard,intr,_netdev,rsize=1048576,wsize=1048576,timeo=600,retrans=2 0 0' >> /etc/fstab" \
|
||||
"$(translate "Replace with your server IP and paths.")" \
|
||||
""
|
||||
|
||||
show_command "6" \
|
||||
"$(translate "Verify mount:")" \
|
||||
"df -h | grep nfs" \
|
||||
"$(translate "Shows NFS mounts.")" \
|
||||
""
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
show_samba_client_help() {
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Samba Client Configuration")"
|
||||
|
||||
msg_info2 "$(translate "How to configure a Samba client in an LXC container.")"
|
||||
echo -e
|
||||
|
||||
show_command "1" \
|
||||
"$(translate "Update and install packages:")" \
|
||||
"apt-get update && apt-get install -y cifs-utils" \
|
||||
"" \
|
||||
""
|
||||
|
||||
show_command "2" \
|
||||
"$(translate "Create mount point:")" \
|
||||
"mkdir -p ${CUS}/mnt/sambamount${CL}" \
|
||||
"$(translate "Replace with your preferred path.")" \
|
||||
""
|
||||
|
||||
show_command "3" \
|
||||
"$(translate "Mount Samba share:")" \
|
||||
"mount -t cifs //${CUS}192.168.1.100${CL}/${CUS}shared${CL} ${CUS}/mnt/sambamount${CL} -o username=${CUS}sambauser${CL}" \
|
||||
"$(translate "Replace server IP, share name and username. You'll be prompted for password.")" \
|
||||
""
|
||||
|
||||
show_command "4" \
|
||||
"$(translate "Test access:")" \
|
||||
"ls -la ${CUS}/mnt/sambamount${CL}" \
|
||||
"$(translate "Verify you can access the mounted share.")" \
|
||||
""
|
||||
|
||||
show_command "5" \
|
||||
"$(translate "Create credentials file (optional):")" \
|
||||
"cat > /etc/samba/credentials << EOF
|
||||
username=${CUS}sambauser${CL}
|
||||
password=${CUS}your_password${CL}
|
||||
EOF
|
||||
chmod 600 /etc/samba/credentials" \
|
||||
"$(translate "Secure way to store credentials.")" \
|
||||
""
|
||||
|
||||
show_command "6" \
|
||||
"$(translate "Mount with credentials file:")" \
|
||||
"mount -t cifs //${CUS}192.168.1.100${CL}/${CUS}shared${CL} ${CUS}/mnt/sambamount${CL} -o credentials=/etc/samba/credentials" \
|
||||
"$(translate "No password prompt needed.")" \
|
||||
""
|
||||
|
||||
show_command "7" \
|
||||
"$(translate "Make permanent (optional):")" \
|
||||
"echo '//${CUS}192.168.1.100${CL}/${CUS}shared${CL} ${CUS}/mnt/sambamount${CL} cifs credentials=/etc/samba/credentials,_netdev 0 0' >> /etc/fstab" \
|
||||
"$(translate "Replace with your values.")" \
|
||||
""
|
||||
|
||||
show_command "8" \
|
||||
"$(translate "Verify mount:")" \
|
||||
"df -h | grep cifs" \
|
||||
"$(translate "Shows CIFS/Samba mounts.")" \
|
||||
""
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
show_help_menu() {
|
||||
while true; do
|
||||
CHOICE=$(dialog --title "$(translate "Help & Information")" \
|
||||
--menu "$(translate "Select help topic:")" 24 80 14 \
|
||||
"0" "$(translate "How to Access an LXC Terminal")" \
|
||||
"1" "$(translate "Mount Remote Resources on Proxmox Host")" \
|
||||
"2" "$(translate "Mount Host Directory to LXC Container")" \
|
||||
"3" "$(translate "NFS Server Installation")" \
|
||||
"4" "$(translate "Samba Server Installation")" \
|
||||
"5" "$(translate "NFS Client Configuration")" \
|
||||
"6" "$(translate "Samba Client Configuration")" \
|
||||
"7" "$(translate "Return to Main Menu")" \
|
||||
3>&1 1>&2 2>&3)
|
||||
|
||||
case $CHOICE in
|
||||
0) show_how_to_enter_lxc ;;
|
||||
1) show_host_mount_resources_help ;;
|
||||
2) show_host_to_lxc_mount_help ;;
|
||||
3) show_nfs_server_help ;;
|
||||
4) show_samba_server_help ;;
|
||||
5) show_nfs_client_help ;;
|
||||
6) show_samba_client_help ;;
|
||||
7) return ;;
|
||||
*) return ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
show_help_menu
|
271
scripts/share/group_manager.sh
Normal file
271
scripts/share/group_manager.sh
Normal file
@@ -0,0 +1,271 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==========================================================
|
||||
# ProxMenux - Shared Groups Manager
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Description : Manage host groups for shared directories
|
||||
# ==========================================================
|
||||
|
||||
# Configuration
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
|
||||
pmx_list_groups() {
|
||||
local groups
|
||||
groups=$(getent group | awk -F: '$3 >= 1000 && $1 != "nogroup" && $1 !~ /^pve/ {print $1 ":" $3}')
|
||||
if [[ -z "$groups" ]]; then
|
||||
whiptail --title "$(translate "Groups")" --msgbox "$(translate "No user groups found.")" 8 60
|
||||
return
|
||||
fi
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Existing Groups")"
|
||||
echo "$groups" | column -t -s: | while read -r name gid; do
|
||||
members=$(getent group "$name" | awk -F: '{print $4}')
|
||||
echo -e " • ${BL}$name${CL} (GID: $gid) -> ${YW}${members:-no members}${CL}"
|
||||
done
|
||||
echo ""
|
||||
msg_success "$(translate "Press Enter to continue...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
|
||||
pmx_create_group() {
|
||||
group_name=$(dialog --inputbox "$(translate "Enter new group name:")" 10 60 "sharedfiles-new" \
|
||||
--title "$(translate "New Group")" 3>&1 1>&2 2>&3) || return
|
||||
[[ -z "$group_name" ]] && return
|
||||
|
||||
if getent group "$group_name" >/dev/null; then
|
||||
dialog --title "$(translate "Error")" --msgbox "$(translate "Group already exists.")" 8 50
|
||||
return
|
||||
fi
|
||||
|
||||
if groupadd "$group_name"; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Create Group")"
|
||||
msg_ok "$(translate "Group created successfully:") $group_name"
|
||||
else
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Create Group")"
|
||||
msg_error "$(translate "Failed to create group.")"
|
||||
fi
|
||||
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to continue...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
|
||||
pmx_edit_group() {
|
||||
local groups group_name action
|
||||
|
||||
|
||||
groups=$(getent group | awk -F: '$3 >= 1000 && $1 != "nogroup" && $1 !~ /^pve/ {print $1}')
|
||||
|
||||
if [[ -z "$groups" ]]; then
|
||||
dialog --title "$(translate "Error")" --msgbox "$(translate "No groups available to edit.")" 8 50
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
local menu_options=""
|
||||
while read -r group; do
|
||||
if [[ -n "$group" ]]; then
|
||||
local gid=$(getent group "$group" | cut -d: -f3)
|
||||
menu_options="$menu_options $group \"GID:$gid\""
|
||||
fi
|
||||
done <<< "$groups"
|
||||
|
||||
|
||||
group_name=$(eval "dialog --title \"$(translate "Edit Group")\" --menu \
|
||||
\"$(translate "Select a group:")\" 20 60 10 \
|
||||
$menu_options 3>&1 1>&2 2>&3")
|
||||
|
||||
if [[ -z "$group_name" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
action=$(dialog --title "$(translate "Edit Group")" --menu \
|
||||
"$(translate "What do you want to edit in group:") $group_name" 15 60 3 \
|
||||
"rename" "$(translate "Rename group")" \
|
||||
"gid" "$(translate "Change GID")" \
|
||||
"users" "$(translate "Add/Remove users")" 3>&1 1>&2 2>&3)
|
||||
|
||||
if [[ -z "$action" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
case "$action" in
|
||||
rename)
|
||||
new_name=$(dialog --inputbox "$(translate "Enter new group name:")" 10 60 \
|
||||
"$group_name" --title "$(translate "Rename Group")" 3>&1 1>&2 2>&3)
|
||||
if [[ -n "$new_name" && "$new_name" != "$group_name" ]]; then
|
||||
if groupmod -n "$new_name" "$group_name" 2>/dev/null; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Rename Group")"
|
||||
msg_ok "$(translate "Group renamed to:") $new_name"
|
||||
else
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Rename Group")"
|
||||
msg_error "$(translate "Failed to rename group")"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
gid)
|
||||
current_gid=$(getent group "$group_name" | cut -d: -f3)
|
||||
new_gid=$(dialog --inputbox "$(translate "Enter new GID:")" 10 60 \
|
||||
"$current_gid" --title "$(translate "Change GID")" 3>&1 1>&2 2>&3)
|
||||
if [[ -n "$new_gid" && "$new_gid" != "$current_gid" ]]; then
|
||||
if groupmod -g "$new_gid" "$group_name" 2>/dev/null; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Change GID")"
|
||||
msg_ok "$(translate "GID changed to:") $new_gid"
|
||||
else
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Change GID")"
|
||||
msg_error "$(translate "Failed to change GID")"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
users)
|
||||
user_action=$(dialog --title "$(translate "User Management")" --menu \
|
||||
"$(translate "Choose an action for group:") $group_name" 15 60 2 \
|
||||
"add" "$(translate "Add user to group")" \
|
||||
"remove" "$(translate "Remove user from group")" 3>&1 1>&2 2>&3)
|
||||
|
||||
case "$user_action" in
|
||||
add)
|
||||
username=$(dialog --inputbox "$(translate "Enter username to add:")" 10 60 \
|
||||
--title "$(translate "Add User")" 3>&1 1>&2 2>&3)
|
||||
if [[ -n "$username" ]]; then
|
||||
if id "$username" >/dev/null 2>&1; then
|
||||
if usermod -aG "$group_name" "$username" 2>/dev/null; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Add User")"
|
||||
msg_ok "$(translate "User added:") $username"
|
||||
else
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Add User")"
|
||||
msg_error "$(translate "Failed to add user")"
|
||||
fi
|
||||
else
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Add User")"
|
||||
msg_error "$(translate "User does not exist:") $username"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
remove)
|
||||
members=$(getent group "$group_name" | awk -F: '{print $4}' | tr ',' ' ')
|
||||
if [[ -z "$members" ]]; then
|
||||
dialog --title "$(translate "Info")" --msgbox "$(translate "No users in this group.")" 8 50
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
local user_options=""
|
||||
for user in $members; do
|
||||
user_options="$user_options $user \"\""
|
||||
done
|
||||
|
||||
username=$(eval "dialog --title \"$(translate "Remove User")\" --menu \
|
||||
\"$(translate "Select user to remove:")\" 15 60 5 \
|
||||
$user_options 3>&1 1>&2 2>&3")
|
||||
|
||||
if [[ -n "$username" ]]; then
|
||||
if gpasswd -d "$username" "$group_name" 2>/dev/null; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Remove User")"
|
||||
msg_ok "$(translate "User removed:") $username"
|
||||
else
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Remove User")"
|
||||
msg_error "$(translate "Failed to remove user")"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to continue...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
|
||||
pmx_delete_group() {
|
||||
local groups group_name menu_options
|
||||
groups=$(getent group | awk -F: '$3 >= 1000 && $1 != "nogroup" && $1 !~ /^pve/ {print $1}')
|
||||
|
||||
if [[ -z "$groups" ]]; then
|
||||
dialog --title "$(translate "Error")" --msgbox "$(translate "No groups available to delete.")" 8 50
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
menu_options=""
|
||||
while read -r group; do
|
||||
if [[ -n "$group" ]]; then
|
||||
menu_options="$menu_options $group \"\""
|
||||
fi
|
||||
done <<< "$groups"
|
||||
|
||||
group_name=$(eval "dialog --title \"$(translate "Delete Group")\" --menu \
|
||||
\"$(translate "Select a group to delete:")\" 20 60 10 \
|
||||
$menu_options 3>&1 1>&2 2>&3") || return
|
||||
|
||||
if dialog --yesno "$(translate "Are you sure you want to delete group:") $group_name ?" 10 60; then
|
||||
if groupdel "$group_name" 2>/dev/null; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Deleting Groups")"
|
||||
msg_ok "$(translate "Group deleted:") $group_name"
|
||||
else
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Deleting Groups")"
|
||||
msg_ok "$(translate "Group deleted:") $group_name"
|
||||
msg_error "$(translate "Failed to delete group")"
|
||||
fi
|
||||
fi
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to continue...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
pmx_manage_groups() {
|
||||
while true; do
|
||||
CHOICE=$(dialog --title "$(translate "Shared Groups Manager")" \
|
||||
--menu "$(translate "Select an option:")" 20 70 10 \
|
||||
"list" "$(translate "View existing groups")" \
|
||||
"create" "$(translate "Create new group")" \
|
||||
"edit" "$(translate "Edit existing group")" \
|
||||
"delete" "$(translate "Delete a group")" \
|
||||
"exit" "$(translate "Exit")" \
|
||||
3>&1 1>&2 2>&3) || return 0
|
||||
|
||||
case "$CHOICE" in
|
||||
list) pmx_list_groups ;;
|
||||
create) pmx_create_group ;;
|
||||
edit) pmx_edit_group ;;
|
||||
delete) pmx_delete_group ;;
|
||||
exit) return 0 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
pmx_manage_groups
|
1292
scripts/share/guia.md
Normal file
1292
scripts/share/guia.md
Normal file
File diff suppressed because it is too large
Load Diff
93
scripts/share/local-shared-manager.sh
Normal file
93
scripts/share/local-shared-manager.sh
Normal file
@@ -0,0 +1,93 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# ProxMenux - Local Shared Directory Manager
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : MIT
|
||||
# Version : 1.0
|
||||
# Last Updated: $(date +%d/%m/%Y)
|
||||
# ==========================================================
|
||||
|
||||
# Configuration
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
|
||||
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
|
||||
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
|
||||
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
|
||||
SHARE_COMMON_LOADED=false
|
||||
else
|
||||
SHARE_COMMON_LOADED=true
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
# ==========================================================
|
||||
|
||||
|
||||
|
||||
|
||||
create_shared_directory() {
|
||||
SHARED_DIR=$(pmx_select_host_mount_point "$(translate "Select Shared Directory Location")" "/mnt/shared")
|
||||
[[ -z "$SHARED_DIR" ]] && return
|
||||
|
||||
|
||||
if [[ -d "$SHARED_DIR" ]]; then
|
||||
if ! whiptail --yesno "$(translate "Directory already exists. Continue with permission setup?")" 10 70 --title "$(translate "Directory Exists")"; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
SHARE_GROUP=$(pmx_choose_or_create_group "sharedfiles") || return 1
|
||||
SHARE_GID=$(pmx_ensure_host_group "$SHARE_GROUP" 101000) || return 1
|
||||
|
||||
|
||||
if command -v setfacl >/dev/null 2>&1; then
|
||||
setfacl -k /mnt 2>/dev/null || true
|
||||
setfacl -b /mnt 2>/dev/null || true
|
||||
fi
|
||||
chmod 755 /mnt 2>/dev/null || true
|
||||
|
||||
|
||||
pmx_prepare_host_shared_dir "$SHARED_DIR" "$SHARE_GROUP" || return 1
|
||||
|
||||
|
||||
if command -v setfacl >/dev/null 2>&1; then
|
||||
setfacl -b -R "$SHARED_DIR" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
|
||||
chown root:"$SHARE_GROUP" "$SHARED_DIR"
|
||||
chmod 2775 "$SHARED_DIR"
|
||||
|
||||
pmx_share_map_set "$SHARED_DIR" "$SHARE_GROUP"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Create Shared Directory")"
|
||||
|
||||
echo -e ""
|
||||
echo -e "${TAB}${BOLD}$(translate "Shared Directory Created:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Directory:")${CL} ${BL}$SHARED_DIR${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Group:")${CL} ${BL}$SHARE_GROUP (GID: $SHARE_GID)${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Permissions:")${CL} ${BL}2775 (rwxrwsr-x)${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Owner:")${CL} ${BL}root:$SHARE_GROUP${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "ACL Status:")${CL} ${BL}$(translate "Cleaned and set for POSIX inheritance")${CL}"
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
create_shared_directory
|
480
scripts/share/lxc-mount-manager.sh
Normal file
480
scripts/share/lxc-mount-manager.sh
Normal file
@@ -0,0 +1,480 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# ProxMenux - LXC Mount Manager
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : MIT
|
||||
# Version : 3.1-enhanced
|
||||
# Last Updated: $(date +%d/%m/%Y)
|
||||
# ==========================================================
|
||||
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
source "$BASE_DIR/utils.sh"
|
||||
|
||||
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
|
||||
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
|
||||
SHARE_COMMON_LOADED=false
|
||||
else
|
||||
SHARE_COMMON_LOADED=true
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
# ==========================================================
|
||||
|
||||
get_container_uid_shift() {
|
||||
local ctid="$1"
|
||||
local conf="/etc/pve/lxc/${ctid}.conf"
|
||||
local uid_shift
|
||||
|
||||
if [[ ! -f "$conf" ]]; then
|
||||
echo "100000"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local unpriv
|
||||
unpriv=$(grep "^unprivileged:" "$conf" | awk '{print $2}')
|
||||
|
||||
if [[ "$unpriv" == "1" ]]; then
|
||||
uid_shift=$(grep "^lxc.idmap" "$conf" | grep 'u 0' | awk '{print $5}' | head -1)
|
||||
echo "${uid_shift:-100000}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "0"
|
||||
return 0
|
||||
}
|
||||
|
||||
setup_container_access() {
|
||||
local ctid="$1" group_name="$2" host_gid="$3" host_dir="$4"
|
||||
local uid_shift mapped_gid
|
||||
|
||||
if [[ ! "$ctid" =~ ^[0-9]+$ ]]; then
|
||||
msg_error "$(translate 'Invalid container ID format:') $ctid"
|
||||
return 1
|
||||
fi
|
||||
|
||||
uid_shift=$(get_container_uid_shift "$ctid")
|
||||
|
||||
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# CONTAINER TYPE DETECTION AND STRATEGY
|
||||
# ===================================================================
|
||||
|
||||
if [[ "$uid_shift" -eq 0 ]]; then
|
||||
msg_ok "$(translate "PRIVILEGED container detected - using direct UID/GID mapping")"
|
||||
mapped_gid="$host_gid"
|
||||
container_type="privileged"
|
||||
else
|
||||
msg_ok "$(translate "UNPRIVILEGED container detected - using mapped UID/GID")"
|
||||
mapped_gid=$((uid_shift + host_gid))
|
||||
container_type="unprivileged"
|
||||
msg_ok "UID shift: $uid_shift, Host GID: $host_gid → Container GID: $mapped_gid"
|
||||
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# STEP 1: ACL TOOLS (only for unprivileged containers)
|
||||
# ===================================================================
|
||||
|
||||
if [[ "$container_type" == "unprivileged" ]]; then
|
||||
if ! command -v setfacl >/dev/null 2>&1; then
|
||||
msg_info "$(translate "Installing ACL tools (REQUIRED for unprivileged containers)...")"
|
||||
apt-get update >/dev/null 2>&1
|
||||
apt-get install -y acl >/dev/null 2>&1
|
||||
if command -v setfacl >/dev/null 2>&1; then
|
||||
msg_ok "$(translate "ACL tools installed successfully")"
|
||||
else
|
||||
msg_error "$(translate "Failed to install ACL tools - permissions may not work correctly")"
|
||||
fi
|
||||
else
|
||||
msg_ok "$(translate "ACL tools already available")"
|
||||
fi
|
||||
else
|
||||
msg_ok "$(translate "Privileged container - ACL tools not required (using POSIX permissions)")"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# STEP 2: CONTAINER GROUP CONFIGURATION
|
||||
# ===================================================================
|
||||
|
||||
msg_info "$(translate "Configuring container group with") $container_type $(translate "strategy...")"
|
||||
|
||||
pct exec "$ctid" -- sh -c "
|
||||
# Remove existing group if GID is wrong
|
||||
if getent group $group_name >/dev/null 2>&1; then
|
||||
current_gid=\$(getent group $group_name | cut -d: -f3)
|
||||
if [ \"\$current_gid\" != \"$mapped_gid\" ]; then
|
||||
groupdel $group_name 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create group with correct GID
|
||||
groupadd -g $mapped_gid $group_name 2>/dev/null || true
|
||||
" 2>/dev/null
|
||||
|
||||
msg_ok "$(translate "Container group configured:") $group_name (GID: $mapped_gid)"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# STEP 3: USER PROCESSING (different strategies)
|
||||
# ===================================================================
|
||||
|
||||
local container_users
|
||||
container_users=$(pct exec "$ctid" -- getent passwd | awk -F: '{print $1 ":" $3}' 2>/dev/null)
|
||||
|
||||
local users_added=0
|
||||
local acls_applied=0
|
||||
|
||||
if [[ "$container_type" == "privileged" ]]; then
|
||||
|
||||
|
||||
msg_ok "$(translate "Privileged container:") $users_added $(translate "users added to group (no ACLs needed)")"
|
||||
|
||||
else
|
||||
|
||||
msg_info "$(translate "Using UNPRIVILEGED strategy: mapped UIDs + ACL permissions")"
|
||||
|
||||
while IFS=: read -r username ct_uid; do
|
||||
if [[ -n "$username" && "$ct_uid" =~ ^[0-9]+$ ]]; then
|
||||
local host_uid=$((uid_shift + ct_uid))
|
||||
|
||||
if pct exec "$ctid" -- usermod -aG "$group_name" "$username" 2>/dev/null; then
|
||||
users_added=$((users_added + 1))
|
||||
|
||||
if command -v setfacl >/dev/null 2>&1; then
|
||||
setfacl -m u:$host_uid:rwx "$host_dir" 2>/dev/null
|
||||
setfacl -m d:u:$host_uid:rwx "$host_dir" 2>/dev/null
|
||||
acls_applied=$((acls_applied + 1))
|
||||
fi
|
||||
|
||||
case "$username" in
|
||||
root|www-data|ncp|nobody|ubuntu|debian)
|
||||
msg_ok "$(translate "Configured user:") $username (CT_UID:$ct_uid → HOST_UID:$host_uid)"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
done <<< "$container_users"
|
||||
|
||||
msg_ok "$(translate "Unprivileged container:") $users_added $(translate "users added,") $acls_applied $(translate "ACL entries applied")"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# STEP 4: DIRECTORY PERMISSIONS
|
||||
# ===================================================================
|
||||
msg_info "$(translate "Setting optimal directory permissions...")"
|
||||
|
||||
chmod 2775 "$host_dir" 2>/dev/null || true
|
||||
chgrp "$group_name" "$host_dir" 2>/dev/null || true
|
||||
|
||||
msg_ok "$(translate "Host directory permissions:") 2775 root:$group_name"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# STEP 5: VERIFICATION
|
||||
# ===================================================================
|
||||
msg_info "$(translate "Verifying configuration...")"
|
||||
|
||||
if [[ "$container_type" == "unprivileged" ]] && command -v getfacl >/dev/null 2>&1; then
|
||||
local acl_count=$(getfacl "$host_dir" 2>/dev/null | grep "^user:" | grep -v "^user::" | wc -l)
|
||||
msg_ok "$(translate "ACL entries configured:") $acl_count"
|
||||
|
||||
# Show sample ACL entries
|
||||
if [[ $acl_count -gt 0 ]]; then
|
||||
echo -e "${TAB}${BGN}$(translate " ACL entries:")${CL}"
|
||||
getfacl "$host_dir" 2>/dev/null | grep "^user:" | grep -v "^user::" | head -3 | while read acl_line; do
|
||||
echo -e "${TAB} ${BL}$acl_line${CL}"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
local test_users=("www-data" "root" "ncp" "nobody")
|
||||
local successful_tests=0
|
||||
|
||||
for test_user in "${test_users[@]}"; do
|
||||
if pct exec "$ctid" -- id "$test_user" >/dev/null 2>&1; then
|
||||
if pct exec "$ctid" -- su -s /bin/bash "$test_user" -c "ls '$4' >/dev/null 2>&1" 2>/dev/null; then
|
||||
successful_tests=$((successful_tests + 1))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $successful_tests -gt 0 ]]; then
|
||||
msg_ok "$(translate "Access verification:") $successful_tests $(translate "users can access mount point")"
|
||||
fi
|
||||
|
||||
|
||||
if [[ "$container_type" == "privileged" ]]; then
|
||||
msg_ok "$(translate "PRIVILEGED container configuration completed - using direct POSIX permissions")"
|
||||
else
|
||||
msg_ok "$(translate "UNPRIVILEGED container configuration completed - using ACL permissions")"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
get_next_mp_index() {
|
||||
local ctid="$1"
|
||||
local conf="/etc/pve/lxc/${ctid}.conf"
|
||||
|
||||
if [[ ! "$ctid" =~ ^[0-9]+$ ]] || [[ ! -f "$conf" ]]; then
|
||||
echo "0"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local used idx next=0
|
||||
used=$(awk -F: '/^mp[0-9]+:/ {print $1}' "$conf" | sed 's/mp//' | sort -n)
|
||||
for idx in $used; do
|
||||
[[ "$idx" -ge "$next" ]] && next=$((idx+1))
|
||||
done
|
||||
echo "$next"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
add_bind_mount() {
|
||||
local ctid="$1" host_path="$2" ct_path="$3"
|
||||
local mpidx result
|
||||
|
||||
if [[ ! "$ctid" =~ ^[0-9]+$ ]]; then
|
||||
msg_error "$(translate 'Invalid container ID format:') $ctid"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ -z "$ctid" || -z "$host_path" || -z "$ct_path" ]]; then
|
||||
msg_error "$(translate "Missing arguments")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if pct config "$ctid" | grep -q "$host_path"; then
|
||||
echo -e
|
||||
msg_warn "$(translate "Directory already mounted in container configuration.")"
|
||||
echo -e ""
|
||||
msg_success "$(translate 'Press Enter to return to menu...')"
|
||||
read -r
|
||||
return 1
|
||||
fi
|
||||
|
||||
mpidx=$(get_next_mp_index "$ctid")
|
||||
|
||||
result=$(pct set "$ctid" -mp${mpidx} "$host_path,mp=$ct_path,shared=1,backup=0,acl=1" 2>&1)
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
msg_ok "$(translate "Successfully mounted:") $host_path → $ct_path"
|
||||
return 0
|
||||
else
|
||||
msg_error "$(translate "Error mounting folder:") $result"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
mount_host_directory_to_lxc() {
|
||||
|
||||
# Step 1: Select container
|
||||
local container_id
|
||||
container_id=$(select_lxc_container)
|
||||
if [[ $? -ne 0 || -z "$container_id" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate 'Mount Host Directory to LXC Container')"
|
||||
|
||||
# Step 1.1: Ensure running
|
||||
ct_status=$(pct status "$container_id" | awk '{print $2}')
|
||||
if [[ "$ct_status" != "running" ]]; then
|
||||
|
||||
msg_info "$(translate "Starting container") $container_id..."
|
||||
if pct start "$container_id"; then
|
||||
sleep 3
|
||||
msg_ok "$(translate "Container started")"
|
||||
else
|
||||
msg_error "$(translate "Failed to start container")"
|
||||
echo -e ""
|
||||
msg_success "$(translate 'Press Enter to continue...')"
|
||||
read -r
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
msg_ok "$(translate 'Container selected and running')"
|
||||
sleep 2
|
||||
|
||||
# Step 2: Select host directory
|
||||
local host_dir
|
||||
host_dir=$(select_host_directory)
|
||||
if [[ -z "$host_dir" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
msg_ok "$(translate 'Host directory selected')"
|
||||
|
||||
# Step 3: Setup group
|
||||
local group_name="sharedfiles"
|
||||
local group_gid
|
||||
group_gid=$(pmx_ensure_host_group "$group_name")
|
||||
if [[ -z "$group_gid" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Set basic permissions
|
||||
chown -R root:"$group_name" "$host_dir" 2>/dev/null || true
|
||||
chmod -R 2775 "$host_dir" 2>/dev/null || true
|
||||
|
||||
msg_ok "$(translate 'Host group configured')"
|
||||
|
||||
# Step 4: Select container mount point
|
||||
|
||||
local ct_mount_point
|
||||
ct_mount_point=$(select_container_mount_point "$container_id" "$host_dir")
|
||||
if [[ -z "$ct_mount_point" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Step 5: Confirmation
|
||||
local uid_shift container_type
|
||||
uid_shift=$(get_container_uid_shift "$container_id")
|
||||
if [[ "$uid_shift" -eq 0 ]]; then
|
||||
container_type="$(translate 'Privileged')"
|
||||
else
|
||||
container_type="$(translate 'Unprivileged')"
|
||||
fi
|
||||
|
||||
local confirm_msg="$(translate "Mount Configuration:")
|
||||
|
||||
$(translate "Container ID:"): $container_id ($container_type)
|
||||
$(translate "Host Directory:"): $host_dir
|
||||
$(translate "Container Mount Point:"): $ct_mount_point
|
||||
$(translate "Shared Group:"): $group_name (GID: $group_gid)
|
||||
|
||||
$(translate "Proceed?")"
|
||||
|
||||
if ! whiptail --title "$(translate "Confirm Mount")" --yesno "$confirm_msg" 16 70; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Step 6: Add mount
|
||||
if ! add_bind_mount "$container_id" "$host_dir" "$ct_mount_point"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Step 7: Setup access (handles both privileged and unprivileged)
|
||||
setup_container_access "$container_id" "$group_name" "$group_gid" "$host_dir"
|
||||
|
||||
# Step 8: Final setup
|
||||
pct exec "$container_id" -- chgrp "$group_name" "$ct_mount_point" 2>/dev/null || true
|
||||
pct exec "$container_id" -- chmod 2775 "$ct_mount_point" 2>/dev/null || true
|
||||
|
||||
# Step 9: Summary
|
||||
echo -e ""
|
||||
echo -e "${TAB}${BOLD}$(translate 'Mount Added Successfully:')${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate 'Container:')${CL} ${BL}$container_id ($container_type)${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate 'Host Directory:')${CL} ${BL}$host_dir${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate 'Mount Point:')${CL} ${BL}$ct_mount_point${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate 'Group:')${CL} ${BL}$group_name (GID: $group_gid)${CL}"
|
||||
|
||||
if [[ "$uid_shift" -eq 0 ]]; then
|
||||
echo -e "${TAB}${BGN}$(translate 'Permission Strategy:')${CL} ${BL}POSIX (direct mapping)${CL}"
|
||||
else
|
||||
echo -e "${TAB}${BGN}$(translate 'Permission Strategy:')${CL} ${BL}ACL (mapped UIDs)${CL}"
|
||||
fi
|
||||
|
||||
echo -e ""
|
||||
if whiptail --yesno "$(translate "Restart container to activate mount?")" 8 60; then
|
||||
msg_info "$(translate 'Restarting container...')"
|
||||
if pct reboot "$container_id"; then
|
||||
sleep 5
|
||||
msg_ok "$(translate 'Container restarted successfully')"
|
||||
|
||||
echo -e
|
||||
echo -e "${TAB}${BOLD}$(translate 'Testing access and read/write:')${CL}"
|
||||
test_user=$(pct exec "$container_id" -- sh -c "id -u ncp >/dev/null 2>&1 && echo ncp || echo www-data")
|
||||
|
||||
if pct exec "$container_id" -- su -s /bin/bash $test_user -c "touch $ct_mount_point/test_access.txt" 2>/dev/null; then
|
||||
msg_ok "$(translate "Mount access and read/write successful (tested as $test_user)")"
|
||||
rm -f "$host_dir/test_access.txt" 2>/dev/null || true
|
||||
else
|
||||
msg_warn "$(translate "⚠ Access test failed - check permissions (user: $test_user)")"
|
||||
fi
|
||||
|
||||
else
|
||||
msg_warn "$(translate 'Failed to restart - restart manually')"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate 'Press Enter to continue...')"
|
||||
read -r
|
||||
}
|
||||
|
||||
# Main menu
|
||||
main_menu() {
|
||||
while true; do
|
||||
choice=$(dialog --title "$(translate 'LXC Mount Manager')" \
|
||||
--menu "\n$(translate 'Choose an option:')" 25 80 15 \
|
||||
"1" "$(translate 'Mount Host Directory to LXC')" \
|
||||
"2" "$(translate 'View Mount Points')" \
|
||||
"3" "$(translate 'Remove Mount Point')" \
|
||||
"4" "$(translate 'Exit')" 3>&1 1>&2 2>&3)
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
mount_host_directory_to_lxc
|
||||
;;
|
||||
2)
|
||||
msg_info2 "$(translate 'Feature coming soon...')"
|
||||
read -p "$(translate 'Press Enter to continue...')"
|
||||
;;
|
||||
3)
|
||||
msg_info2 "$(translate 'Feature coming soon...')"
|
||||
read -p "$(translate 'Press Enter to continue...')"
|
||||
;;
|
||||
4|"")
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
#main_menu
|
||||
mount_host_directory_to_lxc
|
1103
scripts/share/lxc-mount-manager_minimal.sh
Normal file
1103
scripts/share/lxc-mount-manager_minimal.sh
Normal file
File diff suppressed because it is too large
Load Diff
687
scripts/share/nfs_client.sh
Normal file
687
scripts/share/nfs_client.sh
Normal file
@@ -0,0 +1,687 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# ProxMenu CT - NFS Client Manager for Proxmox LXC
|
||||
# ==========================================================
|
||||
# Based on ProxMenux by MacRimi
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# This script allows you to manage NFS client mounts inside Proxmox CTs:
|
||||
# - Mount NFS shares (temporary and permanent)
|
||||
# - View current mounts
|
||||
# - Unmount and remove NFS shares
|
||||
# - Auto-discover NFS servers
|
||||
# ==========================================================
|
||||
|
||||
# Configuration
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
# Load shared functions
|
||||
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
|
||||
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
|
||||
msg_error "$(translate "Could not load shared functions. Script cannot continue.")"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
|
||||
select_privileged_lxc
|
||||
|
||||
|
||||
install_nfs_client() {
|
||||
|
||||
if pct exec "$CTID" -- dpkg -s nfs-common &>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Installing NFS Client in LXC")"
|
||||
|
||||
msg_info "$(translate "Installing NFS client packages...")"
|
||||
if ! pct exec "$CTID" -- apt-get update >/dev/null 2>&1; then
|
||||
msg_error "$(translate "Failed to update package list.")"
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! pct exec "$CTID" -- apt-get install -y nfs-common >/dev/null 2>&1; then
|
||||
msg_error "$(translate "Failed to install NFS client packages.")"
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! pct exec "$CTID" -- which showmount >/dev/null 2>&1; then
|
||||
msg_error "$(translate "showmount command not found after installation.")"
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
return 1
|
||||
fi
|
||||
if ! pct exec "$CTID" -- which mount.nfs >/dev/null 2>&1; then
|
||||
msg_error "$(translate "mount.nfs command not found after installation.")"
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
return 1
|
||||
fi
|
||||
|
||||
msg_ok "$(translate "NFS client installed successfully.")"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
discover_nfs_servers() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Mount NFS Client in LXC")"
|
||||
msg_info "$(translate "Scanning network for NFS servers...")"
|
||||
|
||||
|
||||
|
||||
HOST_IP=$(hostname -I | awk '{print $1}')
|
||||
NETWORK=$(echo "$HOST_IP" | cut -d. -f1-3).0/24
|
||||
|
||||
|
||||
if ! which nmap >/dev/null 2>&1; then
|
||||
apt-get install -y nmap &>/dev/null
|
||||
fi
|
||||
|
||||
|
||||
SERVERS=$(nmap -p 2049 --open "$NETWORK" 2>/dev/null | grep -B 4 "2049/tcp open" | grep "Nmap scan report" | awk '{print $5}' | sort -u || true)
|
||||
|
||||
if [[ -z "$SERVERS" ]]; then
|
||||
cleanup
|
||||
whiptail --title "$(translate "No Servers Found")" --msgbox "$(translate "No NFS servers found on the network.")\n\n$(translate "You can add servers manually.")" 10 60
|
||||
return 1
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while IFS= read -r server; do
|
||||
if [[ -n "$server" ]]; then
|
||||
EXPORTS_COUNT=$(showmount -e "$server" 2>/dev/null | tail -n +2 | wc -l || echo "0")
|
||||
SERVER_INFO="NFS Server ($EXPORTS_COUNT exports)"
|
||||
OPTIONS+=("$server" "$SERVER_INFO")
|
||||
fi
|
||||
done <<< "$SERVERS"
|
||||
|
||||
if [[ ${#OPTIONS[@]} -eq 0 ]]; then
|
||||
cleanup
|
||||
whiptail --title "$(translate "No Valid Servers")" --msgbox "$(translate "No accessible NFS servers found.")" 8 50
|
||||
return 1
|
||||
fi
|
||||
msg_ok "$(translate "NFS servers detected")"
|
||||
NFS_SERVER=$(whiptail --title "$(translate "Select NFS Server")" --menu "$(translate "Choose an NFS server:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
[[ -n "$NFS_SERVER" ]] && return 0 || return 1
|
||||
}
|
||||
|
||||
select_nfs_server() {
|
||||
METHOD=$(whiptail --backtitle "ProxMenux" --title "$(translate "NFS Server Selection")" --menu "$(translate "How do you want to select the NFS server?")" 15 70 3 \
|
||||
"auto" "$(translate "Auto-discover servers on network")" \
|
||||
"manual" "$(translate "Enter server IP/hostname manually")" 3>&1 1>&2 2>&3)
|
||||
case "$METHOD" in
|
||||
auto)
|
||||
discover_nfs_servers || return 1
|
||||
;;
|
||||
manual)
|
||||
NFS_SERVER=$(whiptail --inputbox "$(translate "Enter NFS server IP or hostname:")" 10 60 --title "$(translate "NFS Server")" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$NFS_SERVER" ]] && return 1
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
select_nfs_export() {
|
||||
|
||||
if ! pct exec "$CTID" -- which showmount >/dev/null 2>&1; then
|
||||
whiptail --title "$(translate "NFS Client Error")" \
|
||||
--msgbox "$(translate "showmount command is not working properly.")\n\n$(translate "Please check the installation.")" \
|
||||
10 60
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! pct exec "$CTID" -- ping -c 1 -W 3 "$NFS_SERVER" >/dev/null 2>&1; then
|
||||
whiptail --title "$(translate "Connection Error")" \
|
||||
--msgbox "$(translate "Cannot reach server") $NFS_SERVER\n\n$(translate "Please check:")\n• $(translate "Server IP/hostname is correct")\n• $(translate "Network connectivity")\n• $(translate "Server is online")" \
|
||||
12 70
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
if ! pct exec "$CTID" -- nc -z -w 3 "$NFS_SERVER" 2049 2>/dev/null; then
|
||||
whiptail --title "$(translate "NFS Port Error")" \
|
||||
--msgbox "$(translate "NFS port (2049) is not accessible on") $NFS_SERVER\n\n$(translate "Please check:")\n• $(translate "NFS server is running")\n• $(translate "Firewall settings")\n• $(translate "NFS service is enabled")" \
|
||||
12 70
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
EXPORTS_OUTPUT=$(pct exec "$CTID" -- showmount -e "$NFS_SERVER" 2>&1)
|
||||
EXPORTS_RESULT=$?
|
||||
|
||||
if [[ $EXPORTS_RESULT -ne 0 ]]; then
|
||||
ERROR_MSG=$(echo "$EXPORTS_OUTPUT" | grep -i "error\|failed\|denied" | head -1)
|
||||
|
||||
|
||||
if echo "$EXPORTS_OUTPUT" | grep -qi "connection refused\|network unreachable"; then
|
||||
whiptail --title "$(translate "Network Error")" \
|
||||
--msgbox "$(translate "Network connection failed to") $NFS_SERVER\n\n$(translate "Error:"): $ERROR_MSG\n\n$(translate "Please check:")\n• $(translate "Server is running")\n• $(translate "Network connectivity")\n• $(translate "Firewall settings")" \
|
||||
14 80
|
||||
else
|
||||
whiptail --title "$(translate "NFS Error")" \
|
||||
--msgbox "$(translate "Failed to connect to") $NFS_SERVER\n\n$(translate "Error:"): $ERROR_MSG" \
|
||||
12 80
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
||||
EXPORTS=$(echo "$EXPORTS_OUTPUT" | tail -n +2 | awk '{print $1}' | grep -v "^$")
|
||||
|
||||
if [[ -z "$EXPORTS" ]]; then
|
||||
whiptail --title "$(translate "No Exports Found")" \
|
||||
--msgbox "$(translate "No exports found on server") $NFS_SERVER\n\n$(translate "Server response:")\n$(echo "$EXPORTS_OUTPUT" | head -10)\n\n$(translate "You can enter the export path manually.")" \
|
||||
16 80
|
||||
|
||||
NFS_EXPORT=$(whiptail --inputbox "$(translate "Enter NFS export path (e.g., /mnt/shared):")" 10 60 --title "$(translate "Export Path")" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$NFS_EXPORT" ]] && return 1
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Build options for whiptail
|
||||
OPTIONS=()
|
||||
while IFS= read -r export_line; do
|
||||
if [[ -n "$export_line" ]]; then
|
||||
EXPORT_PATH=$(echo "$export_line" | awk '{print $1}')
|
||||
# Get allowed clients if available
|
||||
CLIENTS=$(echo "$EXPORTS_OUTPUT" | grep "^$EXPORT_PATH" | awk '{for(i=2;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/[[:space:]]*$//')
|
||||
if [[ -n "$CLIENTS" ]]; then
|
||||
OPTIONS+=("$EXPORT_PATH" "$CLIENTS")
|
||||
else
|
||||
OPTIONS+=("$EXPORT_PATH" "$(translate "NFS export")")
|
||||
fi
|
||||
fi
|
||||
done <<< "$EXPORTS"
|
||||
|
||||
if [[ ${#OPTIONS[@]} -eq 0 ]]; then
|
||||
whiptail --title "$(translate "No Available Exports")" \
|
||||
--msgbox "$(translate "No accessible exports found.")\n\n$(translate "You can enter the export path manually.")" \
|
||||
10 70
|
||||
|
||||
NFS_EXPORT=$(whiptail --inputbox "$(translate "Enter NFS export path (e.g., /mnt/shared):")" 10 60 --title "$(translate "Export Path")" 3>&1 1>&2 2>&3)
|
||||
[[ -n "$NFS_EXPORT" ]] && return 0 || return 1
|
||||
fi
|
||||
|
||||
NFS_EXPORT=$(whiptail --title "$(translate "Select NFS Export")" --menu "$(translate "Choose an export to mount:")" 20 70 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
[[ -n "$NFS_EXPORT" ]] && return 0 || return 1
|
||||
}
|
||||
|
||||
select_mount_point() {
|
||||
while true; do
|
||||
METHOD=$(whiptail --title "$(translate "Select Mount Point")" --menu "$(translate "Where do you want to mount the NFS export?")" 15 70 3 \
|
||||
"1" "$(translate "Create new folder in /mnt")" \
|
||||
"2" "$(translate "Select from existing folders in /mnt")" \
|
||||
"3" "$(translate "Enter custom path")" 3>&1 1>&2 2>&3)
|
||||
|
||||
case "$METHOD" in
|
||||
1)
|
||||
# Create default name from server and export
|
||||
EXPORT_NAME=$(basename "$NFS_EXPORT")
|
||||
DEFAULT_NAME="nfs_${NFS_SERVER}_${EXPORT_NAME}"
|
||||
FOLDER_NAME=$(whiptail --inputbox "$(translate "Enter new folder name:")" 10 60 "$DEFAULT_NAME" --title "$(translate "New Folder in /mnt")" 3>&1 1>&2 2>&3)
|
||||
if [[ -n "$FOLDER_NAME" ]]; then
|
||||
MOUNT_POINT="/mnt/$FOLDER_NAME"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
2)
|
||||
DIRS=$(pct exec "$CTID" -- find /mnt -maxdepth 1 -mindepth 1 -type d 2>/dev/null)
|
||||
if [[ -z "$DIRS" ]]; then
|
||||
whiptail --title "$(translate "No Folders")" --msgbox "$(translate "No folders found in /mnt. Please create a new folder.")" 8 60
|
||||
continue
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while IFS= read -r dir; do
|
||||
if [[ -n "$dir" ]]; then
|
||||
name=$(basename "$dir")
|
||||
if pct exec "$CTID" -- [ "$(ls -A "$dir" 2>/dev/null | wc -l)" -eq 0 ]; then
|
||||
status="$(translate "Empty")"
|
||||
else
|
||||
status="$(translate "Contains files")"
|
||||
fi
|
||||
OPTIONS+=("$dir" "$name ($status)")
|
||||
fi
|
||||
done <<< "$DIRS"
|
||||
|
||||
MOUNT_POINT=$(whiptail --title "$(translate "Select Existing Folder")" --menu "$(translate "Choose a folder to mount the export:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
|
||||
if [[ -n "$MOUNT_POINT" ]]; then
|
||||
if pct exec "$CTID" -- [ "$(ls -A "$MOUNT_POINT" 2>/dev/null | wc -l)" -gt 0 ]; then
|
||||
FILE_COUNT=$(pct exec "$CTID" -- ls -A "$MOUNT_POINT" 2>/dev/null | wc -l)
|
||||
if ! whiptail --yesno "$(translate "WARNING: The selected directory is not empty!")\n\n$(translate "Directory:"): $MOUNT_POINT\n$(translate "Contains:"): $FILE_COUNT $(translate "files/folders")\n\n$(translate "Mounting here will hide existing files until unmounted.")\n\n$(translate "Do you want to continue?")" 14 70 --title "$(translate "Directory Not Empty")"; then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
3)
|
||||
MOUNT_POINT=$(whiptail --inputbox "$(translate "Enter full path for mount point:")" 10 70 "/mnt/nfs_share" --title "$(translate "Custom Path")" 3>&1 1>&2 2>&3)
|
||||
if [[ -n "$MOUNT_POINT" ]]; then
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
configure_mount_options() {
|
||||
MOUNT_TYPE=$(whiptail --title "$(translate "Mount Options")" --menu "$(translate "Select mount configuration:")" 15 70 4 \
|
||||
"1" "$(translate "Default options read/write")" \
|
||||
"2" "$(translate "Read-only mount")" \
|
||||
"3" "$(translate "Custom options")" 3>&1 1>&2 2>&3)
|
||||
|
||||
case "$MOUNT_TYPE" in
|
||||
1)
|
||||
MOUNT_OPTIONS="rw,hard,rsize=1048576,wsize=1048576,timeo=600,retrans=2"
|
||||
;;
|
||||
2)
|
||||
MOUNT_OPTIONS="ro,hard,rsize=1048576,wsize=1048576,timeo=600,retrans=2"
|
||||
;;
|
||||
3)
|
||||
MOUNT_OPTIONS=$(whiptail --inputbox "$(translate "Enter custom mount options:")" \
|
||||
10 70 "rw,hard,rsize=1048576,wsize=1048576,timeo=600,retrans=2" \
|
||||
--title "$(translate "Custom Options")" 3>&1 1>&2 2>&3)
|
||||
[[ $? -ne 0 ]] && return 1
|
||||
[[ -z "$MOUNT_OPTIONS" ]] && MOUNT_OPTIONS="rw,hard"
|
||||
;;
|
||||
*)
|
||||
MOUNT_OPTIONS="rw,hard,rsize=65536,wsize=65536,timeo=600,retrans=2"
|
||||
;;
|
||||
esac
|
||||
|
||||
if whiptail --yesno "$(translate "Do you want to make this mount permanent?")\n\n$(translate "This will add the mount to /etc/fstab so it persists after reboot.")" 10 70 --title "$(translate "Permanent Mount")"; then
|
||||
PERMANENT_MOUNT=true
|
||||
else
|
||||
PERMANENT_MOUNT=false
|
||||
fi
|
||||
}
|
||||
|
||||
validate_export_exists() {
|
||||
local server="$1"
|
||||
local export="$2"
|
||||
|
||||
|
||||
|
||||
VALIDATION_OUTPUT=$(pct exec "$CTID" -- showmount -e "$server" 2>/dev/null | grep "^$export[[:space:]]")
|
||||
|
||||
if [[ -n "$VALIDATION_OUTPUT" ]]; then
|
||||
|
||||
return 0
|
||||
else
|
||||
show_proxmenux_logo
|
||||
echo -e
|
||||
msg_error "$(translate "Export not found on server:") $export"
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
mount_nfs_share() {
|
||||
# Step 0: Install NFS client first
|
||||
install_nfs_client || return
|
||||
|
||||
# Step 1: Select server
|
||||
select_nfs_server || return
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Mount NFS Share on Host")"
|
||||
msg_ok "$(translate "NFS server Selected")"
|
||||
|
||||
# Step 2: Select export
|
||||
select_nfs_export || return
|
||||
msg_ok "$(translate "NFS export Selected")"
|
||||
|
||||
# Step 2.5: Validate export exists
|
||||
if ! validate_export_exists "$NFS_SERVER" "$NFS_EXPORT"; then
|
||||
echo -e ""
|
||||
msg_error "$(translate "Cannot proceed with invalid export path.")"
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
return
|
||||
fi
|
||||
|
||||
# Step 3: Select mount point
|
||||
select_mount_point || return
|
||||
|
||||
# Step 4: Configure mount options
|
||||
configure_mount_options || return
|
||||
|
||||
|
||||
|
||||
|
||||
if ! pct exec "$CTID" -- test -d "$MOUNT_POINT"; then
|
||||
if pct exec "$CTID" -- mkdir -p "$MOUNT_POINT"; then
|
||||
msg_ok "$(translate "Mount point created.")"
|
||||
else
|
||||
msg_error "$(translate "Failed to create mount point.")"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if pct exec "$CTID" -- mount | grep -q "$MOUNT_POINT"; then
|
||||
msg_warn "$(translate "Something is already mounted at") $MOUNT_POINT"
|
||||
if ! whiptail --yesno "$(translate "Do you want to unmount it first?")" 8 60 --title "$(translate "Already Mounted")"; then
|
||||
return
|
||||
fi
|
||||
pct exec "$CTID" -- umount "$MOUNT_POINT" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Build mount command
|
||||
NFS_PATH="$NFS_SERVER:$NFS_EXPORT"
|
||||
|
||||
msg_info "$(translate "Testing NFS connection...")"
|
||||
if pct exec "$CTID" -- mount -t nfs -o "$MOUNT_OPTIONS" "$NFS_PATH" "$MOUNT_POINT"; then
|
||||
msg_ok "$(translate "NFS share mounted successfully!")"
|
||||
|
||||
# Test write access
|
||||
if pct exec "$CTID" -- touch "$MOUNT_POINT/.test_write" 2>/dev/null; then
|
||||
pct exec "$CTID" -- rm "$MOUNT_POINT/.test_write" 2>/dev/null
|
||||
msg_ok "$(translate "Write access confirmed.")"
|
||||
else
|
||||
msg_warn "$(translate "Read-only access (or no write permissions).")"
|
||||
fi
|
||||
|
||||
# Add to fstab if permanent
|
||||
if [[ "$PERMANENT_MOUNT" == "true" ]]; then
|
||||
pct exec "$CTID" -- sed -i "\|$MOUNT_POINT|d" /etc/fstab
|
||||
FSTAB_ENTRY="$NFS_PATH $MOUNT_POINT nfs $MOUNT_OPTIONS 0 0"
|
||||
pct exec "$CTID" -- bash -c "echo '$FSTAB_ENTRY' >> /etc/fstab"
|
||||
msg_ok "$(translate "Added to /etc/fstab for permanent mounting.")"
|
||||
fi
|
||||
|
||||
# Show mount information
|
||||
echo -e ""
|
||||
echo -e "${TAB}${BOLD}$(translate "Mount Information:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Server:")${CL} ${BL}$NFS_SERVER${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Export:")${CL} ${BL}$NFS_EXPORT${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Mount Point:")${CL} ${BL}$MOUNT_POINT${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Options:")${CL} ${BL}$MOUNT_OPTIONS${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Permanent:")${CL} ${BL}$PERMANENT_MOUNT${CL}"
|
||||
|
||||
else
|
||||
msg_error "$(translate "Failed to mount NFS share.")"
|
||||
echo -e "${TAB}$(translate "Please check:")"
|
||||
echo -e "${TAB}• $(translate "Server is accessible:"): $NFS_SERVER"
|
||||
echo -e "${TAB}• $(translate "Export exists:"): $NFS_EXPORT"
|
||||
echo -e "${TAB}• $(translate "Network connectivity")"
|
||||
echo -e "${TAB}• $(translate "NFS server is running")"
|
||||
echo -e "${TAB}• $(translate "Export permissions allow access")"
|
||||
fi
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
view_nfs_mounts() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Current NFS Mounts")"
|
||||
|
||||
echo -e "$(translate "NFS mounts in CT") $CTID:"
|
||||
echo "=================================="
|
||||
|
||||
# Show currently mounted NFS shares - VERSIÓN CORREGIDA
|
||||
CURRENT_MOUNTS=$(pct exec "$CTID" -- mount | grep -E "type nfs|:.*on.*nfs" 2>/dev/null || true)
|
||||
if [[ -n "$CURRENT_MOUNTS" ]]; then
|
||||
echo -e "${BOLD}$(translate "Currently Mounted:")${CL}"
|
||||
echo "$CURRENT_MOUNTS"
|
||||
echo ""
|
||||
else
|
||||
# Verificar si hay montajes NFS en fstab que estén activos
|
||||
ACTIVE_NFS_MOUNTS=$(pct exec "$CTID" -- grep "nfs" /etc/fstab 2>/dev/null | grep -v "^#" | while read -r line; do
|
||||
MOUNT_POINT=$(echo "$line" | awk '{print $2}')
|
||||
if pct exec "$CTID" -- mount | grep -q "$MOUNT_POINT"; then
|
||||
echo "$MOUNT_POINT"
|
||||
fi
|
||||
done)
|
||||
|
||||
if [[ -n "$ACTIVE_NFS_MOUNTS" ]]; then
|
||||
echo -e "${BOLD}$(translate "Currently Mounted:")${CL}"
|
||||
while IFS= read -r mount_point; do
|
||||
if [[ -n "$mount_point" ]]; then
|
||||
MOUNT_INFO=$(pct exec "$CTID" -- mount | grep "$mount_point")
|
||||
echo "$MOUNT_INFO"
|
||||
fi
|
||||
done <<< "$ACTIVE_NFS_MOUNTS"
|
||||
echo ""
|
||||
else
|
||||
echo "$(translate "No NFS shares currently mounted.")"
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
|
||||
# Show fstab entries
|
||||
FSTAB_NFS=$(pct exec "$CTID" -- grep "nfs" /etc/fstab 2>/dev/null || true)
|
||||
if [[ -n "$FSTAB_NFS" ]]; then
|
||||
echo -e "${BOLD}$(translate "Permanent Mounts (fstab):")${CL}"
|
||||
echo "$FSTAB_NFS"
|
||||
echo ""
|
||||
|
||||
echo -e "${TAB}${BOLD}$(translate "Mount Details:")${CL}"
|
||||
while IFS= read -r fstab_line; do
|
||||
if [[ -n "$fstab_line" && ! "$fstab_line" =~ ^# ]]; then
|
||||
NFS_PATH=$(echo "$fstab_line" | awk '{print $1}')
|
||||
MOUNT_POINT=$(echo "$fstab_line" | awk '{print $2}')
|
||||
OPTIONS=$(echo "$fstab_line" | awk '{print $4}')
|
||||
|
||||
# Extract server and export from NFS path
|
||||
SERVER=$(echo "$NFS_PATH" | cut -d: -f1)
|
||||
EXPORT=$(echo "$NFS_PATH" | cut -d: -f2)
|
||||
|
||||
echo -e "${TAB}${BGN}$(translate "Server:")${CL} ${BL}$SERVER${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Export:")${CL} ${BL}$EXPORT${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Mount Point:")${CL} ${BL}$MOUNT_POINT${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Options:")${CL} ${BL}$OPTIONS${CL}"
|
||||
|
||||
# Check if currently mounted
|
||||
if pct exec "$CTID" -- mount | grep -q "$MOUNT_POINT"; then
|
||||
echo -e "${TAB}${BGN}$(translate "Status:")${CL} ${GN}$(translate "Mounted")${CL}"
|
||||
else
|
||||
echo -e "${TAB}${BGN}$(translate "Status:")${CL} ${RD}$(translate "Not Mounted")${CL}"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
done <<< "$FSTAB_NFS"
|
||||
else
|
||||
echo "$(translate "No permanent NFS mounts configured.")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
|
||||
unmount_nfs_share() {
|
||||
# Get current NFS mounts
|
||||
MOUNTS=$(pct exec "$CTID" -- mount | grep -E "type nfs|:.*on.*nfs" | awk '{print $3}' | sort -u || true)
|
||||
FSTAB_MOUNTS=$(pct exec "$CTID" -- grep -E "nfs" /etc/fstab 2>/dev/null | grep -v "^#" | awk '{print $2}' | sort -u || true)
|
||||
|
||||
# Combine and deduplicate
|
||||
ALL_MOUNTS=$(echo -e "$MOUNTS\n$FSTAB_MOUNTS" | sort -u | grep -v "^$" || true)
|
||||
|
||||
if [[ -z "$ALL_MOUNTS" ]]; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "No Mounts")" --msgbox "\n$(translate "No NFS mounts found.")" 8 50
|
||||
return
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while IFS= read -r mount_point; do
|
||||
[[ -n "$mount_point" ]] && OPTIONS+=("$mount_point" "")
|
||||
done <<< "$ALL_MOUNTS"
|
||||
|
||||
SELECTED_MOUNT=$(dialog --backtitle "ProxMenux" --title "$(translate "Unmount NFS Share")" --menu "$(translate "Select mount point to unmount:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$SELECTED_MOUNT" ]] && return
|
||||
|
||||
if whiptail --yesno "$(translate "Are you sure you want to unmount this NFS share?")\n\n$(translate "Mount Point:"): $SELECTED_MOUNT\n\n$(translate "This will remove the mount from /etc/fstab.")" 12 80 --title "$(translate "Confirm Unmount")"; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Unmount NFS Share")"
|
||||
|
||||
# Remove from fstab
|
||||
pct exec "$CTID" -- sed -i "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
|
||||
msg_ok "$(translate "Removed from /etc/fstab.")"
|
||||
|
||||
echo -e ""
|
||||
msg_ok "$(translate "NFS share unmount successfully. Reboot LXC required to take effect.")"
|
||||
fi
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
test_nfs_connectivity() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Test NFS Connectivity")"
|
||||
|
||||
echo -e "$(translate "NFS Client Status in CT") $CTID:"
|
||||
echo "=================================="
|
||||
|
||||
# Check if NFS client is installed
|
||||
if pct exec "$CTID" -- dpkg -s nfs-common &>/dev/null; then
|
||||
echo "$(translate "NFS Client: INSTALLED")"
|
||||
|
||||
# Check showmount
|
||||
if pct exec "$CTID" -- which showmount >/dev/null 2>&1; then
|
||||
echo "$(translate "NFS Client Tools: AVAILABLE")"
|
||||
else
|
||||
echo "$(translate "NFS Client Tools: NOT AVAILABLE")"
|
||||
fi
|
||||
|
||||
# Check rpcbind service
|
||||
if pct exec "$CTID" -- systemctl is-active --quiet rpcbind 2>/dev/null; then
|
||||
echo "$(translate "RPC Bind Service: RUNNING")"
|
||||
else
|
||||
echo "$(translate "RPC Bind Service: STOPPED")"
|
||||
msg_warn "$(translate "Starting rpcbind service...")"
|
||||
pct exec "$CTID" -- systemctl start rpcbind 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "$(translate "Current NFS mounts:")"
|
||||
CURRENT_MOUNTS=$(pct exec "$CTID" -- mount | grep -E "type nfs|:.*on.*nfs" 2>/dev/null || true)
|
||||
if [[ -n "$CURRENT_MOUNTS" ]]; then
|
||||
echo "$CURRENT_MOUNTS"
|
||||
else
|
||||
# Check for active NFS mounts from fstab
|
||||
ACTIVE_NFS_MOUNTS=$(pct exec "$CTID" -- grep "nfs" /etc/fstab 2>/dev/null | grep -v "^#" | while read -r line; do
|
||||
MOUNT_POINT=$(echo "$line" | awk '{print $2}')
|
||||
if pct exec "$CTID" -- mount | grep -q "$MOUNT_POINT"; then
|
||||
pct exec "$CTID" -- mount | grep "$MOUNT_POINT"
|
||||
fi
|
||||
done)
|
||||
|
||||
if [[ -n "$ACTIVE_NFS_MOUNTS" ]]; then
|
||||
echo "$ACTIVE_NFS_MOUNTS"
|
||||
else
|
||||
echo "$(translate "No NFS mounts active.")"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "$(translate "Testing network connectivity...")"
|
||||
|
||||
# Test connectivity to known NFS servers from fstab
|
||||
FSTAB_SERVERS=$(pct exec "$CTID" -- grep "nfs" /etc/fstab 2>/dev/null | awk '{print $1}' | cut -d: -f1 | sort -u || true)
|
||||
if [[ -n "$FSTAB_SERVERS" ]]; then
|
||||
while IFS= read -r server; do
|
||||
if [[ -n "$server" ]]; then
|
||||
echo -n "$(translate "Testing") $server: "
|
||||
if pct exec "$CTID" -- ping -c 1 -W 2 "$server" >/dev/null 2>&1; then
|
||||
echo -e "\033[1;92m$(translate "Reachable")\033[0m"
|
||||
|
||||
# Test NFS port
|
||||
echo -n " $(translate "NFS port 2049"): "
|
||||
if pct exec "$CTID" -- nc -z -w 2 "$server" 2049 2>/dev/null; then
|
||||
echo -e "\033[1;92m$(translate "Open")\033[0m"
|
||||
else
|
||||
echo -e "\033[1;91m$(translate "Closed")\033[0m"
|
||||
fi
|
||||
|
||||
# Try to list exports
|
||||
echo -n " $(translate "Export list test"): "
|
||||
if pct exec "$CTID" -- showmount -e "$server" >/dev/null 2>&1; then
|
||||
echo -e "\033[1;92m$(translate "Available")\033[0m"
|
||||
else
|
||||
echo -e "\033[1;91m$(translate "Failed")\033[0m"
|
||||
fi
|
||||
else
|
||||
echo -e "\033[1;91m$(translate "Unreachable")\033[0m"
|
||||
fi
|
||||
fi
|
||||
done <<< "$FSTAB_SERVERS"
|
||||
else
|
||||
echo "$(translate "No NFS servers configured to test.")"
|
||||
fi
|
||||
|
||||
else
|
||||
echo "$(translate "NFS Client: NOT INSTALLED")"
|
||||
echo ""
|
||||
echo "$(translate "Run 'Mount NFS Share' to install NFS client automatically.")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
# === Main Menu ===
|
||||
while true; do
|
||||
CHOICE=$(dialog --backtitle "ProxMenux" --title "$(translate "NFS Client Manager - CT") $CTID" \
|
||||
--menu "$(translate "Choose an option:")" 20 70 12 \
|
||||
"1" "$(translate "Mount NFS Share")" \
|
||||
"2" "$(translate "View Current Mounts")" \
|
||||
"3" "$(translate "Unmount NFS Share")" \
|
||||
"4" "$(translate "Test NFS Connectivity")" \
|
||||
"5" "$(translate "Exit")" \
|
||||
3>&1 1>&2 2>&3)
|
||||
|
||||
RETVAL=$?
|
||||
if [[ $RETVAL -ne 0 ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case $CHOICE in
|
||||
1) mount_nfs_share ;;
|
||||
2) view_nfs_mounts ;;
|
||||
3) unmount_nfs_share ;;
|
||||
4) test_nfs_connectivity ;;
|
||||
5) exit 0 ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
done
|
852
scripts/share/nfs_host.sh
Normal file
852
scripts/share/nfs_host.sh
Normal file
@@ -0,0 +1,852 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# ProxMenu Host - NFS Host Manager for Proxmox Host
|
||||
# ==========================================================
|
||||
# Based on ProxMenux by MacRimi
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# This script allows you to manage NFS client mounts on Proxmox Host:
|
||||
# - Mount external NFS shares on the host
|
||||
# - Configure permanent mounts
|
||||
# - Auto-discover NFS servers
|
||||
# - Integrate with Proxmox storage system
|
||||
# ==========================================================
|
||||
|
||||
# Configuration
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
VENV_PATH="/opt/googletrans-env"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
# Load common share functions
|
||||
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
|
||||
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
|
||||
msg_warn "$(translate "Could not load shared functions. Using fallback methods.")"
|
||||
SHARE_COMMON_LOADED=false
|
||||
else
|
||||
SHARE_COMMON_LOADED=true
|
||||
fi
|
||||
|
||||
|
||||
|
||||
discover_nfs_servers() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Mount NFS Share on Host")"
|
||||
msg_info "$(translate "Scanning network for NFS servers...")"
|
||||
|
||||
HOST_IP=$(hostname -I | awk '{print $1}')
|
||||
NETWORK=$(echo "$HOST_IP" | cut -d. -f1-3).0/24
|
||||
|
||||
if ! which nmap >/dev/null 2>&1; then
|
||||
apt-get install -y nmap &>/dev/null
|
||||
fi
|
||||
|
||||
SERVERS=$(nmap -p 2049 --open "$NETWORK" 2>/dev/null | grep -B 4 "2049/tcp open" | grep "Nmap scan report" | awk '{print $5}' | sort -u || true)
|
||||
|
||||
if [[ -z "$SERVERS" ]]; then
|
||||
cleanup
|
||||
dialog --clear --title "$(translate "No Servers Found")" --msgbox "$(translate "No NFS servers found on the network.")\n\n$(translate "You can add servers manually.")" 10 60
|
||||
return 1
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while IFS= read -r server; do
|
||||
if [[ -n "$server" ]]; then
|
||||
EXPORTS_COUNT=$(showmount -e "$server" 2>/dev/null | tail -n +2 | wc -l || echo "0")
|
||||
SERVER_INFO="NFS Server ($EXPORTS_COUNT exports)"
|
||||
OPTIONS+=("$server" "$SERVER_INFO")
|
||||
fi
|
||||
done <<< "$SERVERS"
|
||||
|
||||
if [[ ${#OPTIONS[@]} -eq 0 ]]; then
|
||||
cleanup
|
||||
dialog --clear --title "$(translate "No Valid Servers")" --msgbox "$(translate "No accessible NFS servers found.")" 8 50
|
||||
return 1
|
||||
fi
|
||||
cleanup
|
||||
NFS_SERVER=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select NFS Server")" --menu "$(translate "Choose an NFS server:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
[[ -n "$NFS_SERVER" ]] && return 0 || return 1
|
||||
}
|
||||
|
||||
select_nfs_server() {
|
||||
METHOD=$(dialog --backtitle "ProxMenux" --title "$(translate "NFS Server Selection")" --menu "$(translate "How do you want to select the NFS server?")" 15 70 3 \
|
||||
"auto" "$(translate "Auto-discover servers on network")" \
|
||||
"manual" "$(translate "Enter server IP/hostname manually")" \
|
||||
"recent" "$(translate "Select from recent servers")" 3>&1 1>&2 2>&3)
|
||||
|
||||
case "$METHOD" in
|
||||
auto)
|
||||
discover_nfs_servers || return 1
|
||||
;;
|
||||
manual)
|
||||
clear
|
||||
NFS_SERVER=$(whiptail --inputbox "$(translate "Enter NFS server IP or hostname:")" 10 60 --title "$(translate "NFS Server")" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$NFS_SERVER" ]] && return 1
|
||||
;;
|
||||
recent)
|
||||
clear
|
||||
RECENT=$(grep "nfs" /etc/fstab 2>/dev/null | awk '{print $1}' | cut -d: -f1 | sort -u || true)
|
||||
if [[ -z "$RECENT" ]]; then
|
||||
dialog --title "$(translate "No Recent Servers")" --msgbox "\n$(translate "No recent NFS servers found.")" 8 50
|
||||
return 1
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while IFS= read -r server; do
|
||||
[[ -n "$server" ]] && OPTIONS+=("$server" "$(translate "Recent NFS server")")
|
||||
done <<< "$RECENT"
|
||||
|
||||
NFS_SERVER=$(whiptail --title "$(translate "Recent NFS Servers")" --menu "$(translate "Choose a recent server:")" 20 70 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
[[ -n "$NFS_SERVER" ]] && return 0 || return 1
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
select_nfs_export() {
|
||||
if ! which showmount >/dev/null 2>&1; then
|
||||
whiptail --title "$(translate "NFS Client Error")" \
|
||||
--msgbox "$(translate "showmount command is not working properly.")\n\n$(translate "Please check the installation.")" \
|
||||
10 60
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! ping -c 1 -W 3 "$NFS_SERVER" >/dev/null 2>&1; then
|
||||
whiptail --title "$(translate "Connection Error")" \
|
||||
--msgbox "$(translate "Cannot reach server") $NFS_SERVER\n\n$(translate "Please check:")\n• $(translate "Server IP/hostname is correct")\n• $(translate "Network connectivity")\n• $(translate "Server is online")" \
|
||||
12 70
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! nc -z -w 3 "$NFS_SERVER" 2049 2>/dev/null; then
|
||||
whiptail --title "$(translate "NFS Port Error")" \
|
||||
--msgbox "$(translate "NFS port (2049) is not accessible on") $NFS_SERVER\n\n$(translate "Please check:")\n• $(translate "NFS server is running")\n• $(translate "Firewall settings")\n• $(translate "NFS service is enabled")" \
|
||||
12 70
|
||||
return 1
|
||||
fi
|
||||
|
||||
EXPORTS_OUTPUT=$(showmount -e "$NFS_SERVER" 2>&1)
|
||||
EXPORTS_RESULT=$?
|
||||
|
||||
if [[ $EXPORTS_RESULT -ne 0 ]]; then
|
||||
ERROR_MSG=$(echo "$EXPORTS_OUTPUT" | grep -i "error\|failed\|denied" | head -1)
|
||||
|
||||
if echo "$EXPORTS_OUTPUT" | grep -qi "connection refused\|network unreachable"; then
|
||||
whiptail --title "$(translate "Network Error")" \
|
||||
--msgbox "$(translate "Network connection failed to") $NFS_SERVER\n\n$(translate "Error:"): $ERROR_MSG\n\n$(translate "Please check:")\n• $(translate "Server is running")\n• $(translate "Network connectivity")\n• $(translate "Firewall settings")" \
|
||||
14 80
|
||||
else
|
||||
whiptail --title "$(translate "NFS Error")" \
|
||||
--msgbox "$(translate "Failed to connect to") $NFS_SERVER\n\n$(translate "Error:"): $ERROR_MSG" \
|
||||
12 80
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
EXPORTS=$(echo "$EXPORTS_OUTPUT" | tail -n +2 | awk '{print $1}' | grep -v "^$")
|
||||
|
||||
if [[ -z "$EXPORTS" ]]; then
|
||||
whiptail --title "$(translate "No Exports Found")" \
|
||||
--msgbox "$(translate "No exports found on server") $NFS_SERVER\n\n$(translate "Server response:")\n$(echo "$EXPORTS_OUTPUT" | head -10)\n\n$(translate "You can enter the export path manually.")" \
|
||||
16 80
|
||||
|
||||
NFS_EXPORT=$(whiptail --inputbox "$(translate "Enter NFS export path (e.g., /mnt/shared):")" 10 60 --title "$(translate "Export Path")" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$NFS_EXPORT" ]] && return 1
|
||||
return 0
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while IFS= read -r export_line; do
|
||||
if [[ -n "$export_line" ]]; then
|
||||
EXPORT_PATH=$(echo "$export_line" | awk '{print $1}')
|
||||
CLIENTS=$(echo "$EXPORTS_OUTPUT" | grep "^$EXPORT_PATH" | awk '{for(i=2;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/[[:space:]]*$//')
|
||||
if [[ -n "$CLIENTS" ]]; then
|
||||
OPTIONS+=("$EXPORT_PATH" "$CLIENTS")
|
||||
else
|
||||
OPTIONS+=("$EXPORT_PATH" "$(translate "NFS export")")
|
||||
fi
|
||||
fi
|
||||
done <<< "$EXPORTS"
|
||||
|
||||
if [[ ${#OPTIONS[@]} -eq 0 ]]; then
|
||||
whiptail --title "$(translate "No Available Exports")" \
|
||||
--msgbox "$(translate "No accessible exports found.")\n\n$(translate "You can enter the export path manually.")" \
|
||||
10 70
|
||||
|
||||
NFS_EXPORT=$(whiptail --inputbox "$(translate "Enter NFS export path (e.g., /mnt/shared):")" 10 60 --title "$(translate "Export Path")" 3>&1 1>&2 2>&3)
|
||||
[[ -n "$NFS_EXPORT" ]] && return 0 || return 1
|
||||
fi
|
||||
|
||||
NFS_EXPORT=$(whiptail --title "$(translate "Select NFS Export")" --menu "$(translate "Choose an export to mount:")" 20 70 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
[[ -n "$NFS_EXPORT" ]] && return 0 || return 1
|
||||
}
|
||||
|
||||
|
||||
select_host_mount_point() {
|
||||
local export_name=$(basename "$NFS_EXPORT")
|
||||
local default_path="/mnt/shared_nfs_${export_name}"
|
||||
|
||||
MOUNT_POINT=$(pmx_select_host_mount_point "$(translate "NFS Mount Point")" "$default_path")
|
||||
[[ -n "$MOUNT_POINT" ]] && return 0 || return 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
configure_host_mount_options() {
|
||||
MOUNT_TYPE=$(whiptail --title "$(translate "Mount Options")" --menu "$(translate "Select mount configuration:")" 15 70 4 \
|
||||
"1" "$(translate "Default options read/write")" \
|
||||
"2" "$(translate "Read-only mount")" \
|
||||
"3" "$(translate "Enter custom options")" 3>&1 1>&2 2>&3)
|
||||
|
||||
[[ $? -ne 0 ]] && return 1
|
||||
|
||||
case "$MOUNT_TYPE" in
|
||||
1)
|
||||
MOUNT_OPTIONS="rw,hard,nofail,rsize=131072,wsize=131072,timeo=600,retrans=2"
|
||||
;;
|
||||
2)
|
||||
MOUNT_OPTIONS="ro,hard,nofail,rsize=131072,wsize=131072,timeo=600,retrans=2"
|
||||
;;
|
||||
3)
|
||||
|
||||
MOUNT_OPTIONS=$(whiptail --inputbox "$(translate "Enter custom mount options:")" \
|
||||
10 70 "rw,hard,nofail,rsize=131072,wsize=131072,timeo=600,retrans=2" \
|
||||
--title "$(translate "Custom Options")" 3>&1 1>&2 2>&3)
|
||||
[[ $? -ne 0 ]] && return 1
|
||||
[[ -z "$MOUNT_OPTIONS" ]] && MOUNT_OPTIONS="rw,hard,nofail"
|
||||
;;
|
||||
*)
|
||||
MOUNT_OPTIONS="rw,hard,nofail,rsize=131072,wsize=131072,timeo=600,retrans=2"
|
||||
;;
|
||||
esac
|
||||
|
||||
if whiptail --yesno "$(translate "Do you want to make this mount permanent?")\n\n$(translate "This will add the mount to /etc/fstab so it persists after reboot.")" 10 70 --title "$(translate "Permanent Mount")"; then
|
||||
PERMANENT_MOUNT=true
|
||||
else
|
||||
if [[ $? -eq 1 ]]; then
|
||||
PERMANENT_MOUNT=false
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
TEMP_MOUNT="/tmp/nfs_test_$$"
|
||||
mkdir -p "$TEMP_MOUNT" 2>/dev/null
|
||||
|
||||
NFS_PATH="$NFS_SERVER:$NFS_EXPORT"
|
||||
if timeout 10 mount -t nfs -o ro,soft,timeo=5 "$NFS_PATH" "$TEMP_MOUNT" 2>/dev/null; then
|
||||
umount "$TEMP_MOUNT" 2>/dev/null || true
|
||||
rmdir "$TEMP_MOUNT" 2>/dev/null || true
|
||||
msg_ok "$(translate "NFS export is accessible")"
|
||||
|
||||
|
||||
if whiptail --yesno "$(translate "Do you want to add this as Proxmox storage?")\n\n$(translate "This will make the NFS share available as storage in Proxmox web interface.")" 10 70 --title "$(translate "Proxmox Storage")"; then
|
||||
PROXMOX_STORAGE=true
|
||||
|
||||
STORAGE_ID=$(whiptail --inputbox "$(translate "Enter storage ID for Proxmox:")" 10 60 "nfs-$(echo $NFS_SERVER | tr '.' '-')" --title "$(translate "Storage ID")" 3>&1 1>&2 2>&3)
|
||||
STORAGE_ID_RESULT=$?
|
||||
|
||||
if [[ $STORAGE_ID_RESULT -ne 0 ]]; then
|
||||
if whiptail --yesno "$(translate "Storage ID input was cancelled.")\n\n$(translate "Do you want to continue without Proxmox storage integration?")" 10 70 --title "$(translate "Continue Without Storage")"; then
|
||||
PROXMOX_STORAGE=false
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
[[ -z "$STORAGE_ID" ]] && STORAGE_ID="nfs-$(echo $NFS_SERVER | tr '.' '-')"
|
||||
fi
|
||||
else
|
||||
DIALOG_RESULT=$?
|
||||
if [[ $DIALOG_RESULT -eq 1 ]]; then
|
||||
PROXMOX_STORAGE=false
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
|
||||
rmdir "$TEMP_MOUNT" 2>/dev/null || true
|
||||
msg_warn "$(translate "NFS export accessibility test failed")"
|
||||
|
||||
if whiptail --yesno "$(translate "The NFS export could not be validated for accessibility.")\n\n$(translate "This might be due to:")\n• $(translate "Network connectivity issues")\n• $(translate "Export permission restrictions")\n• $(translate "Firewall blocking access")\n\n$(translate "Do you want to continue mounting anyway?")\n$(translate "(Proxmox storage integration will be skipped)")" 16 80 --title "$(translate "Export Validation Failed")"; then
|
||||
PROXMOX_STORAGE=false
|
||||
msg_info2 "$(translate "Continuing without Proxmox storage integration due to accessibility issues.")"
|
||||
sleep 2
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
validate_host_export_exists() {
|
||||
local server="$1"
|
||||
local export="$2"
|
||||
|
||||
VALIDATION_OUTPUT=$(showmount -e "$server" 2>/dev/null | grep "^$export[[:space:]]")
|
||||
|
||||
if [[ -n "$VALIDATION_OUTPUT" ]]; then
|
||||
return 0
|
||||
else
|
||||
show_proxmenux_logo
|
||||
echo -e
|
||||
msg_error "$(translate "Export not found on server:") $export"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
add_proxmox_nfs_storage() {
|
||||
local storage_id="$1"
|
||||
local server="$2"
|
||||
local export="$3"
|
||||
local content="${4:-backup,iso,vztmpl}"
|
||||
|
||||
msg_info "$(translate "Starting Proxmox storage integration...")"
|
||||
|
||||
if ! command -v pvesm >/dev/null 2>&1; then
|
||||
show_proxmenux_logo
|
||||
msg_error "$(translate "pvesm command not found. This should not happen on Proxmox.")"
|
||||
echo "Press Enter to continue..."
|
||||
read -r
|
||||
return 1
|
||||
fi
|
||||
|
||||
msg_ok "$(translate "pvesm command found")"
|
||||
|
||||
# Check if storage ID already exists
|
||||
if pvesm status "$storage_id" >/dev/null 2>&1; then
|
||||
msg_warn "$(translate "Storage ID already exists:") $storage_id"
|
||||
if ! whiptail --yesno "$(translate "Storage ID already exists. Do you want to remove and recreate it?")" 8 60 --title "$(translate "Storage Exists")"; then
|
||||
return 0
|
||||
fi
|
||||
pvesm remove "$storage_id" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
msg_ok "$(translate "Storage ID is available")"
|
||||
|
||||
|
||||
# Let Proxmox handle NFS version negotiation automatically
|
||||
if pvesm_output=$(pvesm add nfs "$storage_id" \
|
||||
--server "$server" \
|
||||
--export "$export" \
|
||||
--content "$content" 2>&1); then
|
||||
|
||||
msg_ok "$(translate "NFS storage added successfully!")"
|
||||
|
||||
# Get the actual NFS version that Proxmox negotiated
|
||||
local nfs_version="Auto-negotiated"
|
||||
if pvesm config "$storage_id" 2>/dev/null | grep -q "options.*vers="; then
|
||||
nfs_version="v$(pvesm config "$storage_id" | grep "options" | grep -o "vers=[0-9.]*" | cut -d= -f2)"
|
||||
fi
|
||||
|
||||
echo -e ""
|
||||
echo -e "${TAB}${BGN}$(translate "Storage ID:")${CL} ${BL}$storage_id${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Server:")${CL} ${BL}$server${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Export:")${CL} ${BL}$export${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Content Types:")${CL} ${BL}$content${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "NFS Version:")${CL} ${BL}$nfs_version${CL}"
|
||||
echo -e ""
|
||||
msg_ok "$(translate "Storage is now available in Proxmox web interface under Datacenter > Storage")"
|
||||
return 0
|
||||
else
|
||||
msg_error "$(translate "Failed to add NFS storage to Proxmox.")"
|
||||
echo "$(translate "Error details:"): $pvesm_output"
|
||||
msg_warn "$(translate "The NFS share is still mounted, but not added as Proxmox storage.")"
|
||||
msg_info2 "$(translate "You can add it manually through:")"
|
||||
echo -e "${TAB}• $(translate "Proxmox web interface: Datacenter > Storage > Add > NFS")"
|
||||
echo -e "${TAB}• $(translate "Command line:"): pvesm add nfs $storage_id --server $server --export $export --content backup,iso,vztmpl"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_host_directory() {
|
||||
local mount_point="$1"
|
||||
|
||||
if [[ "$SHARE_COMMON_LOADED" == "true" ]]; then
|
||||
# Use common functions for advanced directory preparation
|
||||
local group_name
|
||||
group_name=$(pmx_choose_or_create_group "sharedfiles")
|
||||
if [[ -n "$group_name" ]]; then
|
||||
local host_gid
|
||||
host_gid=$(pmx_ensure_host_group "$group_name")
|
||||
if [[ -n "$host_gid" ]]; then
|
||||
pmx_prepare_host_shared_dir "$mount_point" "$group_name"
|
||||
pmx_share_map_set "$mount_point" "$group_name"
|
||||
msg_ok "$(translate "Directory prepared with shared group:") $group_name (GID: $host_gid)"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
msg_warn "$(translate "Failed to use shared functions, using basic directory creation.")"
|
||||
fi
|
||||
|
||||
# Fallback: basic directory creation
|
||||
if ! test -d "$mount_point"; then
|
||||
if mkdir -p "$mount_point"; then
|
||||
msg_ok "$(translate "Mount point created on host.")"
|
||||
return 0
|
||||
else
|
||||
msg_error "$(translate "Failed to create mount point on host.")"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
mount_host_nfs_share() {
|
||||
if ! which showmount >/dev/null 2>&1; then
|
||||
msg_error "$(translate "NFS client tools not found. Please check Proxmox installation.")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Step 1:
|
||||
select_nfs_server || return
|
||||
|
||||
# Step 2:
|
||||
select_nfs_export || return
|
||||
|
||||
# Step 2.5:
|
||||
if ! validate_host_export_exists "$NFS_SERVER" "$NFS_EXPORT"; then
|
||||
echo -e ""
|
||||
msg_error "$(translate "Cannot proceed with invalid export path.")"
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
return
|
||||
fi
|
||||
|
||||
# Step 3:
|
||||
select_host_mount_point || return
|
||||
|
||||
# Step 4:
|
||||
configure_host_mount_options || return
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Mount NFS Share on Host")"
|
||||
msg_ok "$(translate "NFS server selected")"
|
||||
|
||||
prepare_host_directory "$MOUNT_POINT" || return 1
|
||||
|
||||
if mount | grep -q "$MOUNT_POINT"; then
|
||||
msg_warn "$(translate "Something is already mounted at") $MOUNT_POINT"
|
||||
if ! whiptail --yesno "$(translate "Do you want to unmount it first?")" 8 60 --title "$(translate "Already Mounted")"; then
|
||||
return
|
||||
fi
|
||||
umount "$MOUNT_POINT" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
NFS_PATH="$NFS_SERVER:$NFS_EXPORT"
|
||||
|
||||
if mount -t nfs -o "$MOUNT_OPTIONS" "$NFS_PATH" "$MOUNT_POINT" > /dev/null 2>&1; then
|
||||
msg_ok "$(translate "NFS share mounted successfully on host!")"
|
||||
|
||||
if touch "$MOUNT_POINT/.test_write" 2>/dev/null; then
|
||||
rm "$MOUNT_POINT/.test_write" 2>/dev/null
|
||||
msg_ok "$(translate "Write access confirmed.")"
|
||||
else
|
||||
msg_warn "$(translate "Read-only access (or no write permissions).")"
|
||||
fi
|
||||
|
||||
if [[ "$PERMANENT_MOUNT" == "true" ]]; then
|
||||
sed -i "\|$MOUNT_POINT|d" /etc/fstab
|
||||
FSTAB_ENTRY="$NFS_PATH $MOUNT_POINT nfs $MOUNT_OPTIONS 0 0"
|
||||
echo "$FSTAB_ENTRY" >> /etc/fstab
|
||||
msg_ok "$(translate "Added to /etc/fstab for permanent mounting.")"
|
||||
|
||||
msg_info "$(translate "Reloading systemd configuration...")"
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
msg_ok "$(translate "Systemd configuration reloaded.")"
|
||||
fi
|
||||
|
||||
if [[ "$PROXMOX_STORAGE" == "true" ]]; then
|
||||
add_proxmox_nfs_storage "$STORAGE_ID" "$NFS_SERVER" "$NFS_EXPORT" "$MOUNT_CONTENT"
|
||||
fi
|
||||
|
||||
echo -e ""
|
||||
echo -e "${TAB}${BOLD}$(translate "Host Mount Information:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Server:")${CL} ${BL}$NFS_SERVER${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Export:")${CL} ${BL}$NFS_EXPORT${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Host Mount Point:")${CL} ${BL}$MOUNT_POINT${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Options:")${CL} ${BL}$MOUNT_OPTIONS${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Permanent:")${CL} ${BL}$PERMANENT_MOUNT${CL}"
|
||||
if [[ "$PROXMOX_STORAGE" == "true" ]]; then
|
||||
echo -e "${TAB}${BGN}$(translate "Proxmox Storage ID:")${CL} ${BL}$STORAGE_ID${CL}"
|
||||
fi
|
||||
|
||||
else
|
||||
msg_error "$(translate "Failed to mount NFS share on host.")"
|
||||
echo -e "${TAB}$(translate "Please check:")"
|
||||
echo -e "${TAB}• $(translate "Server is accessible:"): $NFS_SERVER"
|
||||
echo -e "${TAB}• $(translate "Export exists:"): $NFS_EXPORT"
|
||||
echo -e "${TAB}• $(translate "Network connectivity")"
|
||||
echo -e "${TAB}• $(translate "NFS server is running")"
|
||||
echo -e "${TAB}• $(translate "Export permissions allow access")"
|
||||
fi
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
view_host_nfs_mounts() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Current NFS Mounts on Host")"
|
||||
|
||||
echo -e "$(translate "NFS mounts on Proxmox host:"):"
|
||||
echo "=================================="
|
||||
|
||||
CURRENT_MOUNTS=$(mount | grep -E "type nfs|:.*on.*nfs" 2>/dev/null || true)
|
||||
if [[ -n "$CURRENT_MOUNTS" ]]; then
|
||||
echo -e "${BOLD}$(translate "Currently Mounted:")${CL}"
|
||||
echo "$CURRENT_MOUNTS"
|
||||
echo ""
|
||||
else
|
||||
echo "$(translate "No NFS shares currently mounted on host.")"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
FSTAB_NFS=$(grep "nfs" /etc/fstab 2>/dev/null || true)
|
||||
if [[ -n "$FSTAB_NFS" ]]; then
|
||||
echo -e "${BOLD}$(translate "Permanent Mounts (fstab):")${CL}"
|
||||
echo "$FSTAB_NFS"
|
||||
echo ""
|
||||
|
||||
echo -e "${TAB}${BOLD}$(translate "Mount Details:")${CL}"
|
||||
while IFS= read -r fstab_line; do
|
||||
if [[ -n "$fstab_line" && ! "$fstab_line" =~ ^# ]]; then
|
||||
NFS_PATH=$(echo "$fstab_line" | awk '{print $1}')
|
||||
MOUNT_POINT=$(echo "$fstab_line" | awk '{print $2}')
|
||||
OPTIONS=$(echo "$fstab_line" | awk '{print $4}')
|
||||
|
||||
SERVER=$(echo "$NFS_PATH" | cut -d: -f1)
|
||||
EXPORT=$(echo "$NFS_PATH" | cut -d: -f2)
|
||||
|
||||
echo -e "${TAB}${BGN}$(translate "Server:")${CL} ${BL}$SERVER${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Export:")${CL} ${BL}$EXPORT${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Host Mount Point:")${CL} ${BL}$MOUNT_POINT${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Options:")${CL} ${BL}$OPTIONS${CL}"
|
||||
|
||||
if mount | grep -q "$MOUNT_POINT"; then
|
||||
echo -e "${TAB}${BGN}$(translate "Status:")${CL} ${GN}$(translate "Mounted")${CL}"
|
||||
else
|
||||
echo -e "${TAB}${BGN}$(translate "Status:")${CL} ${RD}$(translate "Not Mounted")${CL}"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
done <<< "$FSTAB_NFS"
|
||||
else
|
||||
echo "$(translate "No NFS mounts found in fstab.")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "$(translate "Proxmox NFS Storage Status:")"
|
||||
if which pvesm >/dev/null 2>&1; then
|
||||
NFS_STORAGES=$(pvesm status 2>/dev/null | grep "nfs" || true)
|
||||
if [[ -n "$NFS_STORAGES" ]]; then
|
||||
echo "$NFS_STORAGES"
|
||||
else
|
||||
echo "$(translate "No NFS storage configured in Proxmox.")"
|
||||
fi
|
||||
else
|
||||
echo "$(translate "pvesm command not available.")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
unmount_host_nfs_share() {
|
||||
MOUNTS=$(mount | grep -E "type nfs|:.*on.*nfs" | awk '{print $3}' | sort -u || true)
|
||||
FSTAB_MOUNTS=$(grep -E "nfs" /etc/fstab 2>/dev/null | grep -v "^#" | awk '{print $2}' | sort -u || true)
|
||||
|
||||
ALL_MOUNTS=$(echo -e "$MOUNTS\n$FSTAB_MOUNTS" | sort -u | grep -v "^$" || true)
|
||||
|
||||
if [[ -z "$ALL_MOUNTS" ]]; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "No Mounts")" --msgbox "\n$(translate "No NFS mounts found on host.")" 8 50
|
||||
return
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while IFS= read -r mount_point; do
|
||||
if [[ -n "$mount_point" ]]; then
|
||||
NFS_PATH=$(mount | grep "$mount_point" | awk '{print $1}' || grep "$mount_point" /etc/fstab | awk '{print $1}' || echo "Unknown")
|
||||
SERVER=$(echo "$NFS_PATH" | cut -d: -f1)
|
||||
EXPORT=$(echo "$NFS_PATH" | cut -d: -f2)
|
||||
OPTIONS+=("$mount_point" "$SERVER:$EXPORT")
|
||||
fi
|
||||
done <<< "$ALL_MOUNTS"
|
||||
|
||||
SELECTED_MOUNT=$(dialog --backtitle "ProxMenux" --title "$(translate "Unmount NFS Share")" --menu "$(translate "Select mount point to unmount:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$SELECTED_MOUNT" ]] && return
|
||||
|
||||
NFS_PATH=$(mount | grep "$SELECTED_MOUNT" | awk '{print $1}' || grep "$SELECTED_MOUNT" /etc/fstab | awk '{print $1}' || echo "Unknown")
|
||||
SERVER=$(echo "$NFS_PATH" | cut -d: -f1)
|
||||
EXPORT=$(echo "$NFS_PATH" | cut -d: -f2)
|
||||
|
||||
PROXMOX_STORAGE=""
|
||||
if which pvesm >/dev/null 2>&1; then
|
||||
NFS_STORAGES=$(pvesm status 2>/dev/null | grep "nfs" | awk '{print $1}' || true)
|
||||
while IFS= read -r storage_id; do
|
||||
if [[ -n "$storage_id" ]]; then
|
||||
STORAGE_INFO=$(pvesm config "$storage_id" 2>/dev/null || true)
|
||||
STORAGE_SERVER=$(echo "$STORAGE_INFO" | grep "server" | awk '{print $2}')
|
||||
STORAGE_EXPORT=$(echo "$STORAGE_INFO" | grep "export" | awk '{print $2}')
|
||||
if [[ "$STORAGE_SERVER" == "$SERVER" && "$STORAGE_EXPORT" == "$EXPORT" ]]; then
|
||||
PROXMOX_STORAGE="$storage_id"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done <<< "$NFS_STORAGES"
|
||||
fi
|
||||
|
||||
CONFIRMATION_MSG="$(translate "Are you sure you want to unmount this NFS share?")\n\n$(translate "Mount Point:"): $SELECTED_MOUNT\n$(translate "Server:"): $SERVER\n$(translate "Export:"): $EXPORT\n\n$(translate "This will:")\n• $(translate "Unmount the NFS share")\n• $(translate "Remove from /etc/fstab")"
|
||||
|
||||
if [[ -n "$PROXMOX_STORAGE" ]]; then
|
||||
CONFIRMATION_MSG="$CONFIRMATION_MSG\n• $(translate "Remove Proxmox storage:"): $PROXMOX_STORAGE"
|
||||
fi
|
||||
|
||||
CONFIRMATION_MSG="$CONFIRMATION_MSG\n• $(translate "Remove mount point directory")"
|
||||
|
||||
if whiptail --yesno "$CONFIRMATION_MSG" 16 80 --title "$(translate "Confirm Unmount")"; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Unmount NFS Share from Host")"
|
||||
|
||||
if [[ -n "$PROXMOX_STORAGE" ]]; then
|
||||
if pvesm remove "$PROXMOX_STORAGE" 2>/dev/null; then
|
||||
msg_ok "$(translate "Proxmox storage removed successfully.")"
|
||||
else
|
||||
msg_warn "$(translate "Failed to remove Proxmox storage, continuing with unmount...")"
|
||||
fi
|
||||
fi
|
||||
|
||||
if mount | grep -q "$SELECTED_MOUNT"; then
|
||||
if umount "$SELECTED_MOUNT"; then
|
||||
msg_ok "$(translate "Successfully unmounted.")"
|
||||
else
|
||||
msg_warn "$(translate "Failed to unmount. Trying force unmount...")"
|
||||
if umount -f "$SELECTED_MOUNT" 2>/dev/null; then
|
||||
msg_ok "$(translate "Force unmount successful.")"
|
||||
else
|
||||
msg_error "$(translate "Failed to unmount. Mount point may be busy.")"
|
||||
echo -e "${TAB}$(translate "Try closing any applications using the mount point.")"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
msg_info "$(translate "Removing from /etc/fstab...")"
|
||||
sed -i "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
|
||||
msg_ok "$(translate "Removed from /etc/fstab.")"
|
||||
|
||||
echo -e ""
|
||||
msg_ok "$(translate "NFS share unmounted successfully from host!")"
|
||||
|
||||
if [[ -n "$PROXMOX_STORAGE" ]]; then
|
||||
echo -e "${TAB}${BGN}$(translate "Proxmox storage removed:")${CL} ${BL}$PROXMOX_STORAGE${CL}"
|
||||
fi
|
||||
echo -e "${TAB}${BGN}$(translate "Mount point unmounted:")${CL} ${BL}$SELECTED_MOUNT${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Removed from fstab:")${CL} ${BL}Yes${CL}"
|
||||
fi
|
||||
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
manage_proxmox_storage() {
|
||||
if ! command -v pvesm >/dev/null 2>&1; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "Error")" --msgbox "\n$(translate "pvesm command not found. This should not happen on Proxmox.")" 8 60
|
||||
return
|
||||
fi
|
||||
|
||||
NFS_STORAGES=$(pvesm status 2>/dev/null | awk '$2 == "nfs" {print $1}')
|
||||
if [[ -z "$NFS_STORAGES" ]]; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "No NFS Storage")" --msgbox "\n$(translate "No NFS storage found in Proxmox.")" 8 60
|
||||
return
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while IFS= read -r storage_id; do
|
||||
if [[ -n "$storage_id" ]]; then
|
||||
STORAGE_INFO=$(pvesm config "$storage_id" 2>/dev/null || true)
|
||||
SERVER=$(echo "$STORAGE_INFO" | grep "server" | awk '{print $2}')
|
||||
EXPORT=$(echo "$STORAGE_INFO" | grep "export" | awk '{print $2}')
|
||||
|
||||
if [[ -n "$SERVER" && -n "$EXPORT" ]]; then
|
||||
OPTIONS+=("$storage_id" "$SERVER:$EXPORT")
|
||||
else
|
||||
OPTIONS+=("$storage_id" "$(translate "NFS Storage")")
|
||||
fi
|
||||
fi
|
||||
done <<< "$NFS_STORAGES"
|
||||
|
||||
SELECTED_STORAGE=$(dialog --backtitle "ProxMenux" --title "$(translate "Manage Proxmox NFS Storage")" --menu "$(translate "Select storage to manage:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$SELECTED_STORAGE" ]] && return
|
||||
|
||||
STORAGE_INFO=$(pvesm config "$SELECTED_STORAGE" 2>/dev/null || true)
|
||||
SERVER=$(echo "$STORAGE_INFO" | grep "server" | awk '{print $2}')
|
||||
EXPORT=$(echo "$STORAGE_INFO" | grep "export" | awk '{print $2}')
|
||||
CONTENT=$(echo "$STORAGE_INFO" | grep "content" | awk '{print $2}')
|
||||
|
||||
FSTAB_NFS=$(grep "nfs" /etc/fstab 2>/dev/null || true)
|
||||
if [[ -n "$FSTAB_NFS" ]]; then
|
||||
while IFS= read -r fstab_line; do
|
||||
if [[ -n "$fstab_line" && ! "$fstab_line" =~ ^# ]]; then
|
||||
NFS_PATH=$(echo "$fstab_line" | awk '{print $1}')
|
||||
MOUNT_POINT=$(echo "$fstab_line" | awk '{print $2}')
|
||||
OPTIONS=$(echo "$fstab_line" | awk '{print $4}')
|
||||
|
||||
SERVER=$(echo "$NFS_PATH" | cut -d: -f1)
|
||||
EXPORT=$(echo "$NFS_PATH" | cut -d: -f2)
|
||||
fi
|
||||
done <<< "$FSTAB_NFS"
|
||||
fi
|
||||
|
||||
if whiptail --yesno "$(translate "Are you sure you want to REMOVE storage") $SELECTED_STORAGE?\n\n$(translate "Server:"): $SERVER\n$(translate "Export:"): $EXPORT\n\n$(translate "WARNING: This will permanently remove the storage from Proxmox configuration.")\n$(translate "The NFS mount on the host will NOT be affected.")" 14 80 --title "$(translate "Remove Storage")"; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Remove Storage")"
|
||||
|
||||
if pvesm remove "$SELECTED_STORAGE" 2>/dev/null; then
|
||||
msg_ok "$(translate "Storage removed successfully from Proxmox.")"
|
||||
echo -e ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
else
|
||||
msg_error "$(translate "Failed to remove storage.")"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
test_host_nfs_connectivity() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Test NFS Connectivity on Host")"
|
||||
|
||||
echo -e "$(translate "NFS Client Status on Proxmox Host:"):"
|
||||
echo "=================================="
|
||||
|
||||
if which showmount >/dev/null 2>&1; then
|
||||
echo "$(translate "NFS Client Tools: AVAILABLE")"
|
||||
|
||||
if systemctl is-active --quiet rpcbind 2>/dev/null; then
|
||||
echo "$(translate "RPC Bind Service: RUNNING")"
|
||||
else
|
||||
echo "$(translate "RPC Bind Service: STOPPED")"
|
||||
msg_warn "$(translate "Starting rpcbind service...")"
|
||||
systemctl start rpcbind 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "$(translate "Current NFS mounts on host:")"
|
||||
CURRENT_MOUNTS=$(mount | grep -E "type nfs|:.*on.*nfs" 2>/dev/null || true)
|
||||
if [[ -n "$CURRENT_MOUNTS" ]]; then
|
||||
echo "$CURRENT_MOUNTS"
|
||||
else
|
||||
echo "$(translate "No NFS mounts active on host.")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "$(translate "Testing network connectivity...")"
|
||||
|
||||
FSTAB_SERVERS=$(grep "nfs" /etc/fstab 2>/dev/null | awk '{print $1}' | cut -d: -f1 | sort -u || true)
|
||||
if [[ -n "$FSTAB_SERVERS" ]]; then
|
||||
while IFS= read -r server; do
|
||||
if [[ -n "$server" ]]; then
|
||||
echo -n "$(translate "Testing") $server: "
|
||||
if ping -c 1 -W 2 "$server" >/dev/null 2>&1; then
|
||||
echo -e "${GN}$(translate "Reachable")${CL}"
|
||||
|
||||
echo -n " $(translate "NFS port 2049"): "
|
||||
if nc -z -w 2 "$server" 2049 2>/dev/null; then
|
||||
echo -e "${GN}$(translate "Open")${CL}"
|
||||
else
|
||||
echo -e "${RD}$(translate "Closed")${CL}"
|
||||
fi
|
||||
|
||||
echo -n " $(translate "Export list test"): "
|
||||
if showmount -e "$server" >/dev/null 2>&1; then
|
||||
echo -e "${GN}$(translate "Available")${CL}"
|
||||
else
|
||||
echo -e "${RD}$(translate "Failed")${CL}"
|
||||
fi
|
||||
else
|
||||
echo -e "${RD}$(translate "Unreachable")${CL}"
|
||||
fi
|
||||
fi
|
||||
done <<< "$FSTAB_SERVERS"
|
||||
else
|
||||
echo "$(translate "No NFS servers configured to test.")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "$(translate "Proxmox NFS Storage Status:")"
|
||||
if which pvesm >/dev/null 2>&1; then
|
||||
NFS_STORAGES=$(pvesm status 2>/dev/null | grep "nfs" || true)
|
||||
if [[ -n "$NFS_STORAGES" ]]; then
|
||||
echo "$NFS_STORAGES"
|
||||
else
|
||||
echo "$(translate "No NFS storage configured in Proxmox.")"
|
||||
fi
|
||||
else
|
||||
echo "$(translate "pvesm command not available.")"
|
||||
fi
|
||||
|
||||
else
|
||||
echo "$(translate "NFS Client Tools: NOT AVAILABLE")"
|
||||
echo ""
|
||||
echo "$(translate "This is unusual for Proxmox. NFS client tools should be installed.")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "$(translate "ProxMenux Extensions:")"
|
||||
if [[ "$SHARE_COMMON_LOADED" == "true" ]]; then
|
||||
echo "$(translate "Shared Functions: LOADED")"
|
||||
if [[ -f "$PROXMENUX_SHARE_MAP_DB" ]]; then
|
||||
MAPPED_DIRS=$(wc -l < "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null || echo "0")
|
||||
echo "$(translate "Mapped directories:"): $MAPPED_DIRS"
|
||||
fi
|
||||
else
|
||||
echo "$(translate "Shared Functions: NOT LOADED (using fallback methods)")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
# === Main Menu ===
|
||||
while true; do
|
||||
CHOICE=$(dialog --backtitle "ProxMenux" --title "$(translate "NFS Host Manager - Proxmox Host")" \
|
||||
--menu "$(translate "Choose an option:")" 22 80 14 \
|
||||
"1" "$(translate "Mount NFS Share on Host")" \
|
||||
"2" "$(translate "View Current Host NFS Mounts")" \
|
||||
"3" "$(translate "Unmount NFS Share from Host")" \
|
||||
"4" "$(translate "Remove Proxmox NFS Storage")" \
|
||||
"5" "$(translate "Test NFS Connectivity")" \
|
||||
"6" "$(translate "Exit")" \
|
||||
3>&1 1>&2 2>&3)
|
||||
|
||||
RETVAL=$?
|
||||
if [[ $RETVAL -ne 0 ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case $CHOICE in
|
||||
1) mount_host_nfs_share ;;
|
||||
2) view_host_nfs_mounts ;;
|
||||
3) unmount_host_nfs_share ;;
|
||||
4) manage_proxmox_storage ;;
|
||||
5) test_host_nfs_connectivity ;;
|
||||
6) exit 0 ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
done
|
572
scripts/share/nfs_lxc_server.sh
Normal file
572
scripts/share/nfs_lxc_server.sh
Normal file
@@ -0,0 +1,572 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# ProxMenu CT - NFS Manager for Proxmox LXC (Simple + Universal)
|
||||
# ==========================================================
|
||||
# Based on ProxMenux by MacRimi
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# This script allows you to manage NFS shares inside Proxmox CTs:
|
||||
# - Create NFS exports with universal sharedfiles group
|
||||
# - View configured exports
|
||||
# - Delete existing exports
|
||||
# - Check NFS service status
|
||||
# ==========================================================
|
||||
|
||||
# Configuration
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
# Load shared functions
|
||||
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
|
||||
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
|
||||
msg_error "$(translate "Could not load shared functions. Script cannot continue.")"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
|
||||
select_privileged_lxc
|
||||
|
||||
|
||||
|
||||
|
||||
setup_universal_sharedfiles_group() {
|
||||
local ctid="$1"
|
||||
|
||||
msg_info "$(translate "Setting sharedfiles group with UID remapping...")"
|
||||
|
||||
if ! pct exec "$ctid" -- getent group sharedfiles >/dev/null 2>&1; then
|
||||
pct exec "$ctid" -- groupadd -g 101000 sharedfiles
|
||||
msg_ok "$(translate "Created sharedfiles group (GID: 101000)")"
|
||||
else
|
||||
local current_gid=$(pct exec "$ctid" -- getent group sharedfiles | cut -d: -f3)
|
||||
if [[ "$current_gid" != "101000" ]]; then
|
||||
pct exec "$ctid" -- groupmod -g 101000 sharedfiles
|
||||
msg_ok "$(translate "Updated sharedfiles group to GID: 101000")"
|
||||
else
|
||||
msg_ok "$(translate "Sharedfiles group already exists (GID: 101000)")"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
local lxc_users=$(pct exec "$ctid" -- awk -F: '$3 >= 1000 && $3 < 65534 {print $1 ":" $3}' /etc/passwd)
|
||||
|
||||
|
||||
if [[ -n "$lxc_users" ]]; then
|
||||
msg_info "$(translate "Adding existing users to sharedfiles group...")"
|
||||
while IFS=: read -r username uid; do
|
||||
if [[ -n "$username" ]]; then
|
||||
pct exec "$ctid" -- usermod -a -G sharedfiles "$username" 2>/dev/null || true
|
||||
msg_ok "$(translate "Added user") $username (UID: $uid) $(translate "to sharedfiles group")"
|
||||
fi
|
||||
done <<< "$lxc_users"
|
||||
fi
|
||||
|
||||
|
||||
msg_info "$(translate "Creating UID remapping for unprivileged container compatibility...")"
|
||||
local remapped_count=0
|
||||
|
||||
if [[ -n "$lxc_users" ]]; then
|
||||
while IFS=: read -r username uid; do
|
||||
if [[ -n "$uid" ]]; then
|
||||
local remapped_uid=$((uid + 100000))
|
||||
local remapped_username="remap_${uid}"
|
||||
|
||||
|
||||
if ! pct exec "$ctid" -- id "$remapped_username" >/dev/null 2>&1; then
|
||||
pct exec "$ctid" -- useradd -u "$remapped_uid" -g sharedfiles -s /bin/false -M "$remapped_username" 2>/dev/null || true
|
||||
msg_ok "$(translate "Created remapped user") $remapped_username (UID: $remapped_uid)"
|
||||
((remapped_count++))
|
||||
else
|
||||
|
||||
pct exec "$ctid" -- usermod -g sharedfiles "$remapped_username" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
done <<< "$lxc_users"
|
||||
fi
|
||||
|
||||
|
||||
local common_uids=(33 1000 1001 1002)
|
||||
for base_uid in "${common_uids[@]}"; do
|
||||
local remapped_uid=$((base_uid + 100000))
|
||||
local remapped_username="remap_${base_uid}"
|
||||
|
||||
if ! pct exec "$ctid" -- id "$remapped_username" >/dev/null 2>&1; then
|
||||
pct exec "$ctid" -- useradd -u "$remapped_uid" -g sharedfiles -s /bin/false -M "$remapped_username" 2>/dev/null || true
|
||||
msg_ok "$(translate "Created common remapped user") $remapped_username (UID: $remapped_uid)"
|
||||
((remapped_count++))
|
||||
fi
|
||||
done
|
||||
|
||||
msg_ok "$(translate "Universal sharedfiles group configured with") $remapped_count $(translate "remapped users")"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
select_mount_point() {
|
||||
while true; do
|
||||
METHOD=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Folder")" \
|
||||
--menu "$(translate "How do you want to select the folder to export?")" 15 60 5 \
|
||||
"auto" "$(translate "Select from folders inside /mnt")" \
|
||||
"manual" "$(translate "Enter path manually")" \
|
||||
3>&1 1>&2 2>&3)
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
case "$METHOD" in
|
||||
auto)
|
||||
DIRS=$(pct exec "$CTID" -- find /mnt -maxdepth 1 -mindepth 1 -type d 2>/dev/null)
|
||||
if [[ -z "$DIRS" ]]; then
|
||||
whiptail --title "$(translate "No Folders")" --msgbox "$(translate "No folders found inside /mnt in the CT.")" 8 60
|
||||
continue
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while IFS= read -r dir; do
|
||||
name=$(basename "$dir")
|
||||
OPTIONS+=("$dir" "$name")
|
||||
done <<< "$DIRS"
|
||||
|
||||
MOUNT_POINT=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Folder")" \
|
||||
--menu "$(translate "Choose a folder to export:")" 20 60 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
[[ -n "$MOUNT_POINT" ]] && return 0
|
||||
;;
|
||||
manual)
|
||||
CT_NAME=$(pct config "$CTID" | awk -F: '/hostname/ {print $2}' | xargs)
|
||||
DEFAULT_MOUNT_POINT="/mnt/${CT_NAME}_nfs"
|
||||
MOUNT_POINT=$(whiptail --title "$(translate "Mount Point")" \
|
||||
--inputbox "$(translate "Enter the mount point for the NFS export (e.g., /mnt/mynfs):")" \
|
||||
10 70 "$DEFAULT_MOUNT_POINT" 3>&1 1>&2 2>&3)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$MOUNT_POINT" ]]; then
|
||||
whiptail --title "$(translate "Error")" \
|
||||
--msgbox "$(translate "No mount point was specified.")" 8 50
|
||||
continue
|
||||
fi
|
||||
pct exec "$CTID" -- mkdir -p "$MOUNT_POINT" 2>/dev/null
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
get_network_config() {
|
||||
NETWORK=$(whiptail --backtitle "ProxMenux" --title "$(translate "Network Configuration")" --menu "\n$(translate "Select network access level:")" 15 70 4 \
|
||||
"1" "$(translate "Local network only (192.168.0.0/16)")" \
|
||||
"2" "$(translate "Specific subnet (enter manually)")" \
|
||||
"3" "$(translate "Specific host (enter IP)")" 3>&1 1>&2 2>&3)
|
||||
|
||||
case "$NETWORK" in
|
||||
1)
|
||||
NETWORK_RANGE="192.168.0.0/16"
|
||||
;;
|
||||
2)
|
||||
clear
|
||||
NETWORK_RANGE=$(whiptail --inputbox "$(translate "Enter subnet (e.g., 192.168.0.0/24):")" 10 60 "192.168.0.0/24" --title "$(translate "Subnet")" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$NETWORK_RANGE" ]] && return 1
|
||||
;;
|
||||
3)
|
||||
dialog
|
||||
NETWORK_RANGE=$(whiptail --inputbox "$(translate "Enter host IP (e.g., 192.168.0.100):")" 10 60 --title "$(translate "Host IP")" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$NETWORK_RANGE" ]] && return 1
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
select_export_options() {
|
||||
EXPORT_OPTIONS=$(whiptail --title "$(translate "Export Options")" --menu \
|
||||
"\n$(translate "Select export permissions:")" 15 70 3 \
|
||||
"1" "$(translate "Read-Write (recommended)")" \
|
||||
"2" "$(translate "Read-Only")" \
|
||||
"3" "$(translate "Custom options")" 3>&1 1>&2 2>&3)
|
||||
|
||||
case "$EXPORT_OPTIONS" in
|
||||
1)
|
||||
OPTIONS="rw,sync,no_subtree_check,all_squash,anonuid=0,anongid=101000"
|
||||
;;
|
||||
2)
|
||||
OPTIONS="ro,sync,no_subtree_check,all_squash,anonuid=0,anongid=101000"
|
||||
;;
|
||||
3)
|
||||
OPTIONS=$(whiptail --inputbox "$(translate "Enter custom NFS options:")" \
|
||||
10 70 "rw,sync,no_subtree_check,all_squash,anonuid=0,anongid=101000" \
|
||||
--title "$(translate "Custom Options")" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$OPTIONS" ]] && OPTIONS="rw,sync,no_subtree_check,all_squash,anonuid=0,anongid=101000"
|
||||
;;
|
||||
*)
|
||||
OPTIONS="rw,sync,no_subtree_check,all_squash,anonuid=0,anongid=101000"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
create_nfs_export() {
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Create LXC server NFS")"
|
||||
sleep 2
|
||||
|
||||
|
||||
select_mount_point || return
|
||||
get_network_config || return
|
||||
select_export_options || return
|
||||
|
||||
|
||||
msg_ok "$(translate "Directory successfully.")"
|
||||
|
||||
|
||||
if ! pct exec "$CTID" -- dpkg -s nfs-kernel-server &>/dev/null; then
|
||||
msg_info "$(translate "Installing NFS server packages inside the CT...")"
|
||||
pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y nfs-kernel-server nfs-common rpcbind"
|
||||
pct exec "$CTID" -- systemctl enable --now rpcbind nfs-kernel-server
|
||||
msg_ok "$(translate "NFS server installed successfully.")"
|
||||
else
|
||||
msg_ok "$(translate "NFS server is already installed.")"
|
||||
fi
|
||||
|
||||
|
||||
setup_universal_sharedfiles_group "$CTID"
|
||||
|
||||
|
||||
msg_info "$(translate "Setting directory ownership and permissions...")"
|
||||
pct exec "$CTID" -- chown root:sharedfiles "$MOUNT_POINT"
|
||||
pct exec "$CTID" -- chmod 2775 "$MOUNT_POINT"
|
||||
msg_ok "$(translate "Directory configured with sharedfiles group ownership")"
|
||||
|
||||
|
||||
|
||||
EXPORT_LINE="$MOUNT_POINT $NETWORK_RANGE($OPTIONS)"
|
||||
|
||||
|
||||
if pct exec "$CTID" -- grep -q "^$MOUNT_POINT " /etc/exports; then
|
||||
if dialog --yesno "$(translate "Do you want to update the existing export?")" \
|
||||
10 60 --title "$(translate "Update Export")"; then
|
||||
pct exec "$CTID" -- sed -i "\|^$MOUNT_POINT |d" /etc/exports
|
||||
pct exec "$CTID" -- bash -c "echo '$EXPORT_LINE' >> /etc/exports"
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Create LXC server NFS")"
|
||||
msg_ok "$(translate "Directory successfully.")"
|
||||
msg_ok "$(translate "Export updated successfully.")"
|
||||
msg_ok "$(translate "NFS server is already installed.")"
|
||||
msg_ok "$(translate "Directory configured with sharedfiles group ownership")"
|
||||
|
||||
fi
|
||||
else
|
||||
pct exec "$CTID" -- bash -c "echo '$EXPORT_LINE' >> /etc/exports"
|
||||
msg_ok "$(translate "Export added successfully.")"
|
||||
fi
|
||||
|
||||
|
||||
pct exec "$CTID" -- systemctl restart rpcbind nfs-kernel-server
|
||||
pct exec "$CTID" -- exportfs -ra
|
||||
|
||||
|
||||
CT_IP=$(pct exec "$CTID" -- hostname -I | awk '{print $1}')
|
||||
|
||||
echo -e ""
|
||||
msg_ok "$(translate "NFS export created successfully!")"
|
||||
echo -e ""
|
||||
echo -e "${TAB}${BOLD}$(translate "Connection details:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Server IP:")${CL} ${BL}$CT_IP${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Export path:")${CL} ${BL}$MOUNT_POINT${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Mount options:")${CL} ${BL}$OPTIONS${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Network access:")${CL} ${BL}$NETWORK_RANGE${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "NFS Version:")${CL} ${BL}Auto-negotiation (NFSv3/NFSv4)${CL}"
|
||||
echo -e ""
|
||||
|
||||
echo -e "${TAB}${BOLD}$(translate "Mount Examples:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Auto-negotiate:")${CL} ${BL}mount -t nfs $CT_IP:$MOUNT_POINT /mnt/nfs${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Force NFSv4:")${CL} ${BL}mount -t nfs4 $CT_IP:$MOUNT_POINT /mnt/nfs${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Force NFSv3:")${CL} ${BL}mount -t nfs -o vers=3 $CT_IP:$MOUNT_POINT /mnt/nfs${CL}"
|
||||
echo ""
|
||||
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
view_exports() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "View Current Exports")"
|
||||
|
||||
echo -e "$(translate "Current NFS exports in CT") $CTID:"
|
||||
echo "=================================="
|
||||
|
||||
if pct exec "$CTID" -- test -f /etc/exports; then
|
||||
EXPORTS=$(pct exec "$CTID" -- cat /etc/exports | grep -v '^#' | grep -v '^$')
|
||||
if [[ -n "$EXPORTS" ]]; then
|
||||
echo "$EXPORTS"
|
||||
echo ""
|
||||
echo "$(translate "Active exports:")"
|
||||
pct exec "$CTID" -- showmount -e localhost 2>/dev/null || echo "$(translate "No active exports or showmount not available")"
|
||||
|
||||
|
||||
echo ""
|
||||
echo "$(translate "Universal Group Configuration:")"
|
||||
echo "=================================="
|
||||
if pct exec "$CTID" -- getent group sharedfiles >/dev/null 2>&1; then
|
||||
local group_members=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f4)
|
||||
local sharedfiles_gid=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f3)
|
||||
echo "$(translate "Shared group: sharedfiles (GID:") $sharedfiles_gid)"
|
||||
|
||||
local member_count=$(echo "$group_members" | tr ',' '\n' | wc -l)
|
||||
echo "$(translate "Total members:") $member_count $(translate "users")"
|
||||
|
||||
|
||||
local remapped_users=$(pct exec "$CTID" -- getent passwd | grep "^remap_" | wc -l)
|
||||
if [[ "$remapped_users" -gt 0 ]]; then
|
||||
echo "$(translate "Remapped users:") $remapped_users $(translate "users (for unprivileged compatibility)")"
|
||||
fi
|
||||
|
||||
echo "$(translate "Universal compatibility: ENABLED")"
|
||||
echo "$(translate "NFS Version: Auto-negotiation (NFSv3/NFSv4)")"
|
||||
else
|
||||
echo "$(translate "Universal group: NOT CONFIGURED")"
|
||||
fi
|
||||
|
||||
CT_IP=$(pct exec "$CTID" -- hostname -I | awk '{print $1}')
|
||||
|
||||
echo ""
|
||||
echo "=================================="
|
||||
echo -e "${TAB}${BOLD}$(translate "Connection Details:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Server IP:")${CL} ${BL}$CT_IP${CL}"
|
||||
while IFS= read -r export_line; do
|
||||
if [[ -n "$export_line" ]]; then
|
||||
EXPORT_PATH=$(echo "$export_line" | awk '{print $1}')
|
||||
echo -e "${TAB}${BGN}$(translate "Export path:")${CL} ${BL}$EXPORT_PATH${CL}"
|
||||
echo ""
|
||||
fi
|
||||
done <<< "$EXPORTS"
|
||||
|
||||
else
|
||||
echo "$(translate "No exports configured.")"
|
||||
fi
|
||||
else
|
||||
echo "$(translate "/etc/exports file does not exist.")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
delete_export() {
|
||||
if ! pct exec "$CTID" -- test -f /etc/exports; then
|
||||
dialog --title "$(translate "Error")" --msgbox "\n$(translate "No exports file found.")" 8 50
|
||||
return
|
||||
fi
|
||||
|
||||
EXPORTS=$(pct exec "$CTID" -- awk '!/^#|^$/ {print NR, $0}' /etc/exports)
|
||||
if [[ -z "$EXPORTS" ]]; then
|
||||
dialog --title "$(translate "No Exports")" --msgbox "$(translate "No exports found in /etc/exports.")" 8 60
|
||||
return
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
NUM=$(echo "$line" | awk '{print $1}')
|
||||
EXPORT_LINE=$(echo "$line" | cut -d' ' -f2-)
|
||||
EXPORT_PATH=$(echo "$EXPORT_LINE" | awk '{print $1}')
|
||||
EXPORT_CLIENT=$(echo "$EXPORT_LINE" | awk '{print $2}' | cut -d'(' -f1)
|
||||
[[ -z "$EXPORT_PATH" || -z "$EXPORT_CLIENT" ]] && continue
|
||||
OPTIONS+=("$NUM" "$EXPORT_PATH $EXPORT_CLIENT")
|
||||
done <<< "$EXPORTS"
|
||||
|
||||
SELECTED_NUM=$(dialog --title "$(translate "Delete Export")" --menu "$(translate "Select an export to delete:")" 20 70 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
[ -z "$SELECTED_NUM" ] && return
|
||||
|
||||
EXPORT_LINE=$(echo "$EXPORTS" | awk -v num="$SELECTED_NUM" '$1 == num {$1=""; print substr($0,2)}')
|
||||
|
||||
if whiptail --yesno "$(translate "Are you sure you want to delete this export?")\n\n$EXPORT_LINE" 10 70 --title "$(translate "Confirm Deletion")"; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Delete Export")"
|
||||
pct exec "$CTID" -- sed -i "${SELECTED_NUM}d" /etc/exports
|
||||
pct exec "$CTID" -- exportfs -ra
|
||||
pct exec "$CTID" -- systemctl restart nfs-kernel-server
|
||||
msg_ok "$(translate "Export deleted and NFS service restarted.")"
|
||||
fi
|
||||
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
check_nfs_status() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Check NFS Status")"
|
||||
echo -e "$(translate "NFS Service Status in CT") $CTID:"
|
||||
echo "=================================="
|
||||
|
||||
if pct exec "$CTID" -- dpkg -s nfs-kernel-server &>/dev/null; then
|
||||
echo "$(translate "NFS Server: INSTALLED")"
|
||||
|
||||
if pct exec "$CTID" -- systemctl is-active --quiet nfs-kernel-server; then
|
||||
echo "$(translate "NFS Service: RUNNING")"
|
||||
else
|
||||
echo "$(translate "NFS Service: STOPPED")"
|
||||
fi
|
||||
|
||||
if pct exec "$CTID" -- systemctl is-active --quiet rpcbind; then
|
||||
echo "$(translate "RPC Bind Service: RUNNING")"
|
||||
else
|
||||
echo "$(translate "RPC Bind Service: STOPPED")"
|
||||
fi
|
||||
|
||||
|
||||
echo ""
|
||||
echo "$(translate "NFS Version Configuration:")"
|
||||
echo "$(translate "Version: Auto-negotiation (NFSv3/NFSv4)")"
|
||||
echo "$(translate "Client determines best version to use")"
|
||||
|
||||
|
||||
echo ""
|
||||
echo "$(translate "Universal Group Configuration:")"
|
||||
if pct exec "$CTID" -- getent group sharedfiles >/dev/null 2>&1; then
|
||||
echo "$(translate "Shared group: CONFIGURED")"
|
||||
local group_members=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f4)
|
||||
local sharedfiles_gid=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f3)
|
||||
echo "$(translate "Group GID:") $sharedfiles_gid"
|
||||
|
||||
local member_count=$(echo "$group_members" | tr ',' '\n' | wc -l)
|
||||
echo "$(translate "Total members:") $member_count $(translate "users")"
|
||||
|
||||
local remapped_users=$(pct exec "$CTID" -- getent passwd | grep "^remap_" | wc -l)
|
||||
echo "$(translate "Remapped users:") $remapped_users $(translate "users")"
|
||||
|
||||
echo "$(translate "Universal compatibility: ENABLED")"
|
||||
else
|
||||
echo "$(translate "Universal group: NOT CONFIGURED")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "$(translate "Listening ports:")"
|
||||
pct exec "$CTID" -- ss -tlnp | grep -E ':(111|2049|20048)' || echo "$(translate "No NFS ports found")"
|
||||
|
||||
else
|
||||
echo "$(translate "NFS Server: NOT INSTALLED")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
uninstall_nfs() {
|
||||
if ! pct exec "$CTID" -- dpkg -s nfs-kernel-server &>/dev/null; then
|
||||
dialog --title "$(translate "NFS Not Installed")" --msgbox "\n$(translate "NFS server is not installed in this CT.")" 8 60
|
||||
return
|
||||
fi
|
||||
|
||||
if ! whiptail --title "$(translate "Uninstall NFS Server")" \
|
||||
--yesno "$(translate "WARNING: This will completely remove NFS server from the CT.")\n\n$(translate "This action will:")\n$(translate "• Stop all NFS services")\n$(translate "• Remove all exports")\n$(translate "• Uninstall NFS packages")\n$(translate "• Remove universal sharedfiles group")\n$(translate "• Clean up remapped users")\n\n$(translate "Are you sure you want to continue?")" \
|
||||
18 70; then
|
||||
return
|
||||
fi
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Uninstall NFS Server")"
|
||||
|
||||
msg_info "$(translate "Stopping NFS services...")"
|
||||
pct exec "$CTID" -- systemctl stop nfs-kernel-server 2>/dev/null || true
|
||||
pct exec "$CTID" -- systemctl stop rpcbind 2>/dev/null || true
|
||||
pct exec "$CTID" -- systemctl disable nfs-kernel-server 2>/dev/null || true
|
||||
pct exec "$CTID" -- systemctl disable rpcbind 2>/dev/null || true
|
||||
msg_ok "$(translate "NFS services stopped and disabled.")"
|
||||
|
||||
if pct exec "$CTID" -- test -f /etc/exports; then
|
||||
pct exec "$CTID" -- truncate -s 0 /etc/exports
|
||||
msg_ok "$(translate "Exports cleared.")"
|
||||
fi
|
||||
|
||||
|
||||
msg_info "$(translate "Removing remapped users...")"
|
||||
local remapped_users=$(pct exec "$CTID" -- getent passwd | grep "^remap_" | cut -d: -f1)
|
||||
if [[ -n "$remapped_users" ]]; then
|
||||
while IFS= read -r username; do
|
||||
if [[ -n "$username" ]]; then
|
||||
pct exec "$CTID" -- userdel "$username" 2>/dev/null || true
|
||||
msg_ok "$(translate "Removed remapped user:") $username"
|
||||
fi
|
||||
done <<< "$remapped_users"
|
||||
fi
|
||||
|
||||
|
||||
if pct exec "$CTID" -- getent group sharedfiles >/dev/null 2>&1; then
|
||||
local regular_members=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f4 | tr ',' '\n' | grep -v "^remap_" | wc -l)
|
||||
if [[ "$regular_members" -eq 0 ]]; then
|
||||
pct exec "$CTID" -- groupdel sharedfiles 2>/dev/null || true
|
||||
msg_ok "$(translate "Removed sharedfiles group.")"
|
||||
else
|
||||
msg_warn "$(translate "Kept sharedfiles group (has regular users assigned).")"
|
||||
fi
|
||||
fi
|
||||
|
||||
pct exec "$CTID" -- apt-get remove --purge -y nfs-kernel-server nfs-common 2>/dev/null || true
|
||||
pct exec "$CTID" -- apt-get autoremove -y 2>/dev/null || true
|
||||
msg_ok "$(translate "NFS packages removed.")"
|
||||
|
||||
msg_info "$(translate "Cleaning up remaining processes...")"
|
||||
pct exec "$CTID" -- pkill -f nfs 2>/dev/null || true
|
||||
pct exec "$CTID" -- pkill -f rpc 2>/dev/null || true
|
||||
sleep 2
|
||||
msg_ok "$(translate "Universal NFS server has been completely uninstalled!")"
|
||||
echo -e ""
|
||||
echo -e "${TAB}${BOLD}$(translate "Uninstallation Summary:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Services:")${CL} ${BL}$(translate "Stopped and disabled")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Packages:")${CL} ${BL}$(translate "Removed")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Exports:")${CL} ${BL}$(translate "Cleared")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Universal Group:")${CL} ${BL}$(translate "Cleaned up")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Remapped Users:")${CL} ${BL}$(translate "Removed")${CL}"
|
||||
echo -e
|
||||
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
# === Main Menu ===
|
||||
while true; do
|
||||
CHOICE=$(dialog --title "$(translate "NFS LXC Manager - CT") $CTID" --menu "$(translate "Choose an option:")" 20 70 12 \
|
||||
"1" "$(translate "Create Universal NFS Export")" \
|
||||
"2" "$(translate "View Current Exports")" \
|
||||
"3" "$(translate "Delete Export")" \
|
||||
"4" "$(translate "Check NFS Status")" \
|
||||
"5" "$(translate "Uninstall NFS Server")" \
|
||||
"6" "$(translate "Exit")" 3>&1 1>&2 2>&3)
|
||||
|
||||
case $CHOICE in
|
||||
1) create_nfs_export ;;
|
||||
2) view_exports ;;
|
||||
3) delete_export ;;
|
||||
4) check_nfs_status ;;
|
||||
5) uninstall_nfs ;;
|
||||
6) exit 0 ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
done
|
1085
scripts/share/samba_client.sh
Normal file
1085
scripts/share/samba_client.sh
Normal file
File diff suppressed because it is too large
Load Diff
1266
scripts/share/samba_host.sh
Normal file
1266
scripts/share/samba_host.sh
Normal file
File diff suppressed because it is too large
Load Diff
576
scripts/share/samba_lxc_server.sh
Normal file
576
scripts/share/samba_lxc_server.sh
Normal file
@@ -0,0 +1,576 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# ProxMenu CT - Samba Manager for Proxmox LXC
|
||||
# ==========================================================
|
||||
# Based on ProxMenux by MacRimi
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# This script allows you to manage Samba shares inside Proxmox CTs:
|
||||
# - Create shared folders
|
||||
# - View configured shares
|
||||
# - Delete existing shares
|
||||
# - Check Samba service status
|
||||
# ==========================================================
|
||||
|
||||
# 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"
|
||||
CREDENTIALS_DIR="/etc/samba/credentials"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
|
||||
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
|
||||
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
|
||||
msg_error "$(translate "Could not load shared functions. Script cannot continue.")"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
|
||||
select_privileged_lxc
|
||||
|
||||
|
||||
select_mount_point() {
|
||||
while true; do
|
||||
METHOD=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Folder")" \
|
||||
--menu "$(translate "How do you want to select the folder to share?")" 15 60 5 \
|
||||
"auto" "$(translate "Select from folders inside /mnt")" \
|
||||
"manual" "$(translate "Enter path manually")" \
|
||||
3>&1 1>&2 2>&3)
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
case "$METHOD" in
|
||||
auto)
|
||||
DIRS=$(pct exec "$CTID" -- find /mnt -maxdepth 1 -mindepth 1 -type d 2>/dev/null)
|
||||
if [[ -z "$DIRS" ]]; then
|
||||
whiptail --title "$(translate "No Folders")" --msgbox "$(translate "No folders found inside /mnt in the CT.")" 8 60
|
||||
continue
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while IFS= read -r dir; do
|
||||
name=$(basename "$dir")
|
||||
OPTIONS+=("$dir" "$name")
|
||||
done <<< "$DIRS"
|
||||
|
||||
MOUNT_POINT=$(whiptail --title "$(translate "Select Folder")" \
|
||||
--menu "$(translate "Choose a folder to share:")" 20 60 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
[[ -n "$MOUNT_POINT" ]] && return 0
|
||||
;;
|
||||
manual)
|
||||
CT_NAME=$(pct config "$CTID" | awk -F: '/hostname/ {print $2}' | xargs)
|
||||
DEFAULT_MOUNT_POINT="/mnt/${CT_NAME}_share"
|
||||
MOUNT_POINT=$(whiptail --title "$(translate "Mount Point")" \
|
||||
--inputbox "$(translate "Enter the mount point for the shared folder (e.g., /mnt/myshare):")" \
|
||||
10 70 "$DEFAULT_MOUNT_POINT" 3>&1 1>&2 2>&3)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
clear
|
||||
if [[ -z "$MOUNT_POINT" ]]; then
|
||||
whiptail --title "$(translate "Error")" --msgbox "\n$(translate "No mount point was specified.")" 8 50
|
||||
continue
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
create_share() {
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Create Samba server service")"
|
||||
sleep 2
|
||||
|
||||
select_mount_point || return
|
||||
|
||||
|
||||
if ! pct exec "$CTID" -- test -d "$MOUNT_POINT"; then
|
||||
if whiptail --yesno "$(translate "The directory does not exist in the CT.")\n\n$MOUNT_POINT\n\n$(translate "Do you want to create it?")" 12 70 --title "$(translate "Create Directory")"; then
|
||||
pct exec "$CTID" -- mkdir -p "$MOUNT_POINT"
|
||||
msg_ok "$(translate "Directory created successfully.")"
|
||||
else
|
||||
msg_error "$(translate "Directory does not exist and was not created.")"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if pct exec "$CTID" -- dpkg -s samba &>/dev/null; then
|
||||
SAMBA_INSTALLED=true
|
||||
else
|
||||
SAMBA_INSTALLED=false
|
||||
fi
|
||||
|
||||
|
||||
if [ "$SAMBA_INSTALLED" = false ]; then
|
||||
echo -e "${TAB}$(translate "Installing Samba server packages inside the CT...")"
|
||||
pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y samba samba-common-bin acl"
|
||||
|
||||
USERNAME=$(whiptail --inputbox "$(translate "Enter the Samba username:")" 10 60 "proxmenux" --title "$(translate "Samba User")" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$USERNAME" ]] && msg_error "$(translate "No username provided.")" && return
|
||||
|
||||
while true; do
|
||||
PASSWORD1=$(whiptail --passwordbox "$(translate "Enter the password for Samba user:")" 10 60 --title "$(translate "Samba Password")" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$PASSWORD1" ]] && msg_error "$(translate "No password provided.")" && return
|
||||
PASSWORD2=$(whiptail --passwordbox "$(translate "Confirm the password:")" 10 60 --title "$(translate "Confirm Password")" 3>&1 1>&2 2>&3)
|
||||
[[ -z "$PASSWORD2" ]] && msg_error "$(translate "Password confirmation is required.")" && return
|
||||
|
||||
if [[ "$PASSWORD1" != "$PASSWORD2" ]]; then
|
||||
whiptail --title "$(translate "Password Mismatch")" --msgbox "$(translate "The passwords do not match. Please try again.")" 10 60
|
||||
else
|
||||
PASSWORD="$PASSWORD1"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if ! pct exec "$CTID" -- id "$USERNAME" &>/dev/null; then
|
||||
pct exec "$CTID" -- adduser --disabled-password --gecos "" "$USERNAME"
|
||||
fi
|
||||
pct exec "$CTID" -- bash -c "echo -e '$PASSWORD\n$PASSWORD' | smbpasswd -a '$USERNAME'"
|
||||
|
||||
msg_ok "$(translate "Samba server installed successfully.")"
|
||||
else
|
||||
USERNAME=$(pct exec "$CTID" -- pdbedit -L | awk -F: '{print $1}' | head -n1)
|
||||
msg_ok "$(translate "Samba server is already installed.")"
|
||||
echo -e "$(translate "Detected existing Samba user:") $USERNAME"
|
||||
fi
|
||||
|
||||
|
||||
IS_MOUNTED=$(pct exec "$CTID" -- mount | grep "$MOUNT_POINT" || true)
|
||||
if [[ -n "$IS_MOUNTED" ]]; then
|
||||
msg_info "$(translate "Detected a mounted directory from host. Setting up shared group...")"
|
||||
|
||||
SHARE_GID=999
|
||||
GROUP_EXISTS=$(pct exec "$CTID" -- getent group sharedfiles || true)
|
||||
GID_IN_USE=$(pct exec "$CTID" -- getent group "$SHARE_GID" | cut -d: -f1 || true)
|
||||
|
||||
if [[ -z "$GROUP_EXISTS" ]]; then
|
||||
if [[ -z "$GID_IN_USE" ]]; then
|
||||
pct exec "$CTID" -- groupadd -g "$SHARE_GID" sharedfiles
|
||||
msg_ok "$(translate "Group 'sharedfiles' created with GID $SHARE_GID")"
|
||||
else
|
||||
pct exec "$CTID" -- groupadd sharedfiles
|
||||
msg_warn "$(translate "GID $SHARE_GID already in use. Group 'sharedfiles' created with dynamic GID.")"
|
||||
fi
|
||||
else
|
||||
msg_ok "$(translate "Group 'sharedfiles' already exists inside the CT")"
|
||||
fi
|
||||
|
||||
if pct exec "$CTID" -- getent group sharedfiles >/dev/null; then
|
||||
pct exec "$CTID" -- usermod -aG sharedfiles "$USERNAME"
|
||||
pct exec "$CTID" -- chown root:sharedfiles "$MOUNT_POINT"
|
||||
pct exec "$CTID" -- chmod 2775 "$MOUNT_POINT"
|
||||
else
|
||||
msg_error "$(translate "Group 'sharedfiles' was not created successfully. Skipping chown/usermod.")"
|
||||
fi
|
||||
|
||||
HAS_ACCESS=$(pct exec "$CTID" -- su -s /bin/bash -c "test -w '$MOUNT_POINT' && echo yes || echo no" "$USERNAME" 2>/dev/null)
|
||||
if [ "$HAS_ACCESS" = "no" ]; then
|
||||
pct exec "$CTID" -- setfacl -R -m "u:$USERNAME:rwx" "$MOUNT_POINT"
|
||||
msg_warn "$(translate "ACL permissions applied to allow write access for user:") $USERNAME"
|
||||
else
|
||||
msg_ok "$(translate "Write access confirmed for user:") $USERNAME"
|
||||
fi
|
||||
else
|
||||
msg_ok "$(translate "No shared mount detected. Applying standard local access.")"
|
||||
pct exec "$CTID" -- chown -R "$USERNAME:$USERNAME" "$MOUNT_POINT"
|
||||
pct exec "$CTID" -- chmod -R 755 "$MOUNT_POINT"
|
||||
|
||||
HAS_ACCESS=$(pct exec "$CTID" -- su -s /bin/bash -c "test -w '$MOUNT_POINT' && echo yes || echo no" "$USERNAME" 2>/dev/null)
|
||||
if [ "$HAS_ACCESS" = "no" ]; then
|
||||
pct exec "$CTID" -- setfacl -R -m "u:$USERNAME:rwx" "$MOUNT_POINT"
|
||||
msg_warn "$(translate "ACL permissions applied for local access for user:") $USERNAME"
|
||||
else
|
||||
msg_ok "$(translate "Write access confirmed for user:") $USERNAME"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
SHARE_OPTIONS=$(whiptail --title "$(translate "Share Options")" --menu "$(translate "Select share permissions:")" 15 70 3 \
|
||||
"rw" "$(translate "Read-Write access")" \
|
||||
"ro" "$(translate "Read-Only access")" \
|
||||
"custom" "$(translate "Custom options")" 3>&1 1>&2 2>&3)
|
||||
|
||||
SHARE_NAME=$(basename "$MOUNT_POINT")
|
||||
|
||||
case "$SHARE_OPTIONS" in
|
||||
rw)
|
||||
CONFIG=$(cat <<EOF
|
||||
[$SHARE_NAME]
|
||||
comment = Shared folder for $USERNAME
|
||||
path = $MOUNT_POINT
|
||||
read only = no
|
||||
writable = yes
|
||||
browseable = yes
|
||||
guest ok = no
|
||||
valid users = $USERNAME
|
||||
force group = sharedfiles
|
||||
create mask = 0664
|
||||
directory mask = 2775
|
||||
force create mode = 0664
|
||||
force directory mode = 2775
|
||||
veto files = /lost+found/
|
||||
EOF
|
||||
)
|
||||
;;
|
||||
ro)
|
||||
CONFIG=$(cat <<EOF
|
||||
[$SHARE_NAME]
|
||||
comment = Read-only shared folder for $USERNAME
|
||||
path = $MOUNT_POINT
|
||||
read only = yes
|
||||
writable = no
|
||||
browseable = yes
|
||||
guest ok = no
|
||||
valid users = $USERNAME
|
||||
force group = sharedfiles
|
||||
veto files = /lost+found/
|
||||
EOF
|
||||
)
|
||||
;;
|
||||
custom)
|
||||
CUSTOM_CONFIG=$(whiptail --inputbox "$(translate "Enter custom Samba configuration for this share:")" 15 80 "read only = no\nwritable = yes\nbrowseable = yes\nguest ok = no" --title "$(translate "Custom Configuration")" 3>&1 1>&2 2>&3)
|
||||
CONFIG=$(cat <<EOF
|
||||
[$SHARE_NAME]
|
||||
comment = Custom shared folder for $USERNAME
|
||||
path = $MOUNT_POINT
|
||||
valid users = $USERNAME
|
||||
force group = sharedfiles
|
||||
$CUSTOM_CONFIG
|
||||
veto files = /lost+found/
|
||||
EOF
|
||||
)
|
||||
;;
|
||||
*)
|
||||
CONFIG=$(cat <<EOF
|
||||
[$SHARE_NAME]
|
||||
comment = Shared folder for $USERNAME
|
||||
path = $MOUNT_POINT
|
||||
read only = no
|
||||
writable = yes
|
||||
browseable = yes
|
||||
guest ok = no
|
||||
valid users = $USERNAME
|
||||
force group = sharedfiles
|
||||
create mask = 0664
|
||||
directory mask = 2775
|
||||
force create mode = 0664
|
||||
force directory mode = 2775
|
||||
veto files = /lost+found/
|
||||
EOF
|
||||
)
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
if pct exec "$CTID" -- grep -q "\[$SHARE_NAME\]" /etc/samba/smb.conf; then
|
||||
msg_warn "$(translate "The share already exists in smb.conf:") [$SHARE_NAME]"
|
||||
if whiptail --yesno "$(translate "Do you want to update the existing share?")" 10 60 --title "$(translate "Update Share")"; then
|
||||
|
||||
pct exec "$CTID" -- sed -i "/^\[$SHARE_NAME\]/,/^$/d" /etc/samba/smb.conf
|
||||
pct exec "$CTID" -- bash -c "echo '$CONFIG' >> /etc/samba/smb.conf"
|
||||
msg_ok "$(translate "Share updated successfully.")"
|
||||
else
|
||||
return
|
||||
fi
|
||||
else
|
||||
msg_ok "$(translate "Adding new share to smb.conf...")"
|
||||
pct exec "$CTID" -- bash -c "echo '$CONFIG' >> /etc/samba/smb.conf"
|
||||
msg_ok "$(translate "Share added successfully.")"
|
||||
fi
|
||||
|
||||
|
||||
pct exec "$CTID" -- systemctl restart smbd.service
|
||||
|
||||
|
||||
CT_IP=$(pct exec "$CTID" -- hostname -I | awk '{print $1}')
|
||||
|
||||
echo -e ""
|
||||
msg_ok "$(translate "Samba share created successfully!")"
|
||||
echo -e ""
|
||||
echo -e "${TAB}${BOLD}$(translate "Connection details:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Server IP:")${CL} ${BL}$CT_IP${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Share name:")${CL} ${BL}$SHARE_NAME${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Share path:")${CL} ${BL}$MOUNT_POINT${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Username:")${CL} ${BL}$USERNAME${CL}"
|
||||
echo -e
|
||||
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
view_shares() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "View Current Shares")"
|
||||
|
||||
echo -e "$(translate "Current Samba shares in CT") $CTID:"
|
||||
echo "=================================="
|
||||
|
||||
if pct exec "$CTID" -- test -f /etc/samba/smb.conf; then
|
||||
|
||||
SHARES=$(pct exec "$CTID" -- awk '/^\[.*\]/ && !/^\[global\]/ && !/^\[homes\]/ && !/^\[printers\]/ {print $0}' /etc/samba/smb.conf)
|
||||
if [[ -n "$SHARES" ]]; then
|
||||
|
||||
while IFS= read -r share_line; do
|
||||
if [[ -n "$share_line" ]]; then
|
||||
SHARE_NAME=$(echo "$share_line" | sed 's/\[//g' | sed 's/\]//g')
|
||||
SHARE_PATH=$(pct exec "$CTID" -- awk "/^\[$SHARE_NAME\]/,/^$/ {if(/path =/) print \$3}" /etc/samba/smb.conf)
|
||||
echo "$share_line -> $SHARE_PATH"
|
||||
fi
|
||||
done <<< "$SHARES"
|
||||
|
||||
|
||||
CT_IP=$(pct exec "$CTID" -- hostname -I | awk '{print $1}')
|
||||
USERNAME=$(pct exec "$CTID" -- pdbedit -L | awk -F: '{print $1}' | head -n1)
|
||||
|
||||
echo ""
|
||||
echo "=================================="
|
||||
echo -e "${TAB}${BOLD}$(translate "Connection Details:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Server IP:")${CL} ${BL}$CT_IP${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Username:")${CL} ${BL}$USERNAME${CL}"
|
||||
echo ""
|
||||
|
||||
echo -e "${TAB}${BOLD}$(translate "Available Shares:")${CL}"
|
||||
while IFS= read -r share_line; do
|
||||
if [[ -n "$share_line" ]]; then
|
||||
SHARE_NAME=$(echo "$share_line" | sed 's/\[//g' | sed 's/\]//g')
|
||||
SHARE_PATH=$(pct exec "$CTID" -- awk "/^\[$SHARE_NAME\]/,/^$/ {if(/path =/) print \$3}" /etc/samba/smb.conf)
|
||||
echo -e "${TAB}${BGN}$(translate "Share name:")${CL} ${BL}$SHARE_NAME${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Share path:")${CL} ${BL}$SHARE_PATH${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Windows path:")${CL} ${YW}\\\\$CT_IP\\$SHARE_NAME${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Linux/Mac path:")${CL} ${YW}smb://$CT_IP/$SHARE_NAME${CL}"
|
||||
echo ""
|
||||
fi
|
||||
done <<< "$SHARES"
|
||||
|
||||
else
|
||||
echo "$(translate "No shares configured.")"
|
||||
fi
|
||||
else
|
||||
echo "$(translate "/etc/samba/smb.conf file does not exist.")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
delete_share() {
|
||||
if ! pct exec "$CTID" -- test -f /etc/samba/smb.conf; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "Error")" --msgbox "\n$(translate "No smb.conf file found.")" 8 50
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
SHARES=$(pct exec "$CTID" -- awk '/^\[.*\]/ && !/^\[global\]/ && !/^\[homes\]/ && !/^\[printers\]/ {gsub(/\[|\]/, ""); print NR, $0}' /etc/samba/smb.conf)
|
||||
|
||||
if [[ -z "$SHARES" ]]; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "No Shares")" --msgbox "\n$(translate "No shares found in smb.conf.")" 8 60
|
||||
return
|
||||
fi
|
||||
|
||||
OPTIONS=()
|
||||
while read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
NUM=$(echo "$line" | awk '{print $1}')
|
||||
SHARE_NAME=$(echo "$line" | awk '{print $2}')
|
||||
SHARE_PATH=$(pct exec "$CTID" -- awk "/^\[$SHARE_NAME\]/,/^$/ {if(/path =/) print \$3}" /etc/samba/smb.conf)
|
||||
[[ -z "$SHARE_NAME" ]] && continue
|
||||
OPTIONS+=("$SHARE_NAME" "$SHARE_NAME -> $SHARE_PATH")
|
||||
done <<< "$SHARES"
|
||||
|
||||
SELECTED_SHARE=$(dialog --backtitle "ProxMenux" --title "$(translate "Delete Share")" --menu "$(translate "Select a share to delete:")" 20 70 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
[ -z "$SELECTED_SHARE" ] && return
|
||||
|
||||
SHARE_PATH=$(pct exec "$CTID" -- awk "/^\[$SELECTED_SHARE\]/,/^$/ {if(/path =/) print \$3}" /etc/samba/smb.conf)
|
||||
if whiptail --yesno "$(translate "Are you sure you want to delete this share?")\n\n$(translate "Share name:"): $SELECTED_SHARE\n$(translate "Share path:"): $SHARE_PATH" 12 70 --title "$(translate "Confirm Deletion")"; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Delete Share")"
|
||||
|
||||
|
||||
pct exec "$CTID" -- sed -i "/^\[$SELECTED_SHARE\]/,/^$/d" /etc/samba/smb.conf
|
||||
pct exec "$CTID" -- systemctl restart smbd.service
|
||||
msg_ok "$(translate "Share deleted and Samba service restarted.")"
|
||||
fi
|
||||
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
check_samba_status() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Check Samba Status")"
|
||||
|
||||
echo -e "$(translate "Samba Service Status in CT") $CTID:"
|
||||
echo "=================================="
|
||||
|
||||
|
||||
if pct exec "$CTID" -- dpkg -s samba &>/dev/null; then
|
||||
echo "$(translate "Samba Server: INSTALLED")"
|
||||
|
||||
|
||||
if pct exec "$CTID" -- systemctl is-active --quiet smbd; then
|
||||
echo "$(translate "Samba Service: RUNNING")"
|
||||
else
|
||||
echo "$(translate "Samba Service: STOPPED")"
|
||||
fi
|
||||
|
||||
|
||||
if pct exec "$CTID" -- systemctl is-active --quiet nmbd; then
|
||||
echo "$(translate "NetBIOS Service: RUNNING")"
|
||||
else
|
||||
echo "$(translate "NetBIOS Service: STOPPED")"
|
||||
fi
|
||||
|
||||
|
||||
echo ""
|
||||
echo "$(translate "Listening ports:")"
|
||||
pct exec "$CTID" -- ss -tlnp | grep -E ':(139|445)' || echo "$(translate "No Samba ports found")"
|
||||
|
||||
|
||||
echo ""
|
||||
echo "$(translate "Samba users:")"
|
||||
pct exec "$CTID" -- pdbedit -L 2>/dev/null || echo "$(translate "No Samba users found")"
|
||||
|
||||
else
|
||||
echo "$(translate "Samba Server: NOT INSTALLED")"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
|
||||
uninstall_samba() {
|
||||
|
||||
if ! pct exec "$CTID" -- dpkg -s samba &>/dev/null; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "Samba Not Installed")" --msgbox "\n$(translate "Samba server is not installed in this CT.")" 8 60
|
||||
return
|
||||
fi
|
||||
|
||||
if ! whiptail --title "$(translate "Uninstall Samba Server")" \
|
||||
--yesno "$(translate "WARNING: This will completely remove Samba server from the CT.")\n\n$(translate "This action will:")\n$(translate "• Stop all Samba services")\n$(translate "• Remove all shares")\n$(translate "• Remove all Samba users")\n$(translate "• Uninstall Samba packages")\n$(translate "• Remove Samba groups")\n\n$(translate "Are you sure you want to continue?")" \
|
||||
18 70; then
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Uninstall Samba Server")"
|
||||
|
||||
|
||||
msg_info "$(translate "Stopping Samba services...")"
|
||||
pct exec "$CTID" -- systemctl stop smbd 2>/dev/null || true
|
||||
pct exec "$CTID" -- systemctl stop nmbd 2>/dev/null || true
|
||||
pct exec "$CTID" -- systemctl disable smbd 2>/dev/null || true
|
||||
pct exec "$CTID" -- systemctl disable nmbd 2>/dev/null || true
|
||||
msg_ok "$(translate "Samba services stopped and disabled.")"
|
||||
|
||||
|
||||
if pct exec "$CTID" -- test -f /etc/samba/smb.conf; then
|
||||
pct exec "$CTID" -- cp /etc/samba/smb.conf /etc/samba/smb.conf.backup.$(date +%Y%m%d_%H%M%S)
|
||||
msg_ok "$(translate "Samba configuration backed up.")"
|
||||
fi
|
||||
|
||||
|
||||
SAMBA_USERS=$(pct exec "$CTID" -- pdbedit -L 2>/dev/null | awk -F: '{print $1}' || true)
|
||||
if [[ -n "$SAMBA_USERS" ]]; then
|
||||
while IFS= read -r user; do
|
||||
if [[ -n "$user" ]]; then
|
||||
pct exec "$CTID" -- smbpasswd -x "$user" 2>/dev/null || true
|
||||
fi
|
||||
done <<< "$SAMBA_USERS"
|
||||
msg_ok "$(translate "Samba users removed.")"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
pct exec "$CTID" -- apt-get remove --purge -y samba samba-common-bin samba-common 2>/dev/null || true
|
||||
pct exec "$CTID" -- apt-get autoremove -y 2>/dev/null || true
|
||||
msg_ok "$(translate "Samba packages removed.")"
|
||||
|
||||
|
||||
|
||||
if pct exec "$CTID" -- getent group sharedfiles >/dev/null 2>&1; then
|
||||
|
||||
GROUP_USERS=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f4)
|
||||
if [[ -z "$GROUP_USERS" ]]; then
|
||||
pct exec "$CTID" -- groupdel sharedfiles 2>/dev/null || true
|
||||
msg_ok "$(translate "Samba group removed.")"
|
||||
else
|
||||
msg_warn "$(translate "Samba group kept (has users assigned).")"
|
||||
fi
|
||||
fi
|
||||
|
||||
msg_info "$(translate "Cleaning up Samba directories...")"
|
||||
pct exec "$CTID" -- pkill -f smbd 2>/dev/null || true
|
||||
pct exec "$CTID" -- pkill -f nmbd 2>/dev/null || true
|
||||
|
||||
pct exec "$CTID" -- rm -rf /var/lib/samba 2>/dev/null || true
|
||||
pct exec "$CTID" -- rm -rf /var/cache/samba 2>/dev/null || true
|
||||
|
||||
sleep 2
|
||||
msg_ok "$(translate "Samba server has been completely uninstalled!")"
|
||||
echo -e ""
|
||||
echo -e "${TAB}${BOLD}$(translate "Uninstallation Summary:")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Services:")${CL} ${BL}$(translate "Stopped and disabled")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Packages:")${CL} ${BL}$(translate "Removed")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Users:")${CL} ${BL}$(translate "Removed")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Configuration:")${CL} ${BL}$(translate "Backed up and cleared")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Groups:")${CL} ${BL}$(translate "Cleaned up")${CL}"
|
||||
echo -e
|
||||
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
}
|
||||
|
||||
|
||||
# === Main Menu ===
|
||||
while true; do
|
||||
CHOICE=$(dialog --backtitle "ProxMenux" --title "$(translate "Samba Manager - CT") $CTID" --menu "$(translate "Choose an option:")" 20 70 12 \
|
||||
"1" "$(translate "Create Samba server service")" \
|
||||
"2" "$(translate "View Current Shares")" \
|
||||
"3" "$(translate "Delete Share")" \
|
||||
"4" "$(translate "Check Samba Status")" \
|
||||
"5" "$(translate "Uninstall Samba Server")" \
|
||||
"6" "$(translate "Exit")" 3>&1 1>&2 2>&3)
|
||||
|
||||
case $CHOICE in
|
||||
1) create_share ;;
|
||||
2) view_shares ;;
|
||||
3) delete_share ;;
|
||||
4) check_samba_status ;;
|
||||
5) uninstall_samba ;;
|
||||
6) exit 0 ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
done
|
@@ -223,7 +223,12 @@ is_disk_in_use() {
|
||||
}
|
||||
|
||||
FREE_DISKS=()
|
||||
LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -n1 readlink -f | sort -u)
|
||||
LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -r -n1 readlink -f | sort -u)
|
||||
|
||||
if [[ -n "$LVM_DEVICES" ]] && echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then
|
||||
IS_MOUNTED=true
|
||||
fi
|
||||
|
||||
RAID_ACTIVE=$(grep -Po 'md\d+\s*:\s*active\s+raid[0-9]+' /proc/mdstat | awk '{print $1}' | sort -u)
|
||||
|
||||
while read -r DISK; do
|
||||
|
275
scripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh
Normal file
275
scripts/utilities/proxmox-upgrade-pve8-to-pve9-manual-guide.sh
Normal file
@@ -0,0 +1,275 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# ProxMenux - Manual Proxmox VE 8 to 9 Upgrade Guide
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
|
||||
# Version : 1.0
|
||||
# Last Updated: 13/08/2025
|
||||
# ==========================================================
|
||||
|
||||
# Configuration ============================================
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
VENV_PATH="/opt/googletrans-env"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
load_language
|
||||
initialize_cache
|
||||
# ==========================================================
|
||||
|
||||
show_command() {
|
||||
local step="$1"
|
||||
local description="$2"
|
||||
local command="$3"
|
||||
local note="$4"
|
||||
local command_extra="$5"
|
||||
|
||||
echo -e "${BGN}${step}.${CL} ${BL}${description}${CL}"
|
||||
echo ""
|
||||
echo -e "${TAB}${command}"
|
||||
echo -e
|
||||
[[ -n "$note" ]] && echo -e "${TAB}${DARK_GRAY}${note}${CL}"
|
||||
[[ -n "$command_extra" ]] && echo -e "${TAB}${YW}${command_extra}${CL}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
show_proxmox_upgrade_manual_guide() {
|
||||
clear
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Proxmox VE 8 to 9 Manual Upgrade Guide")"
|
||||
|
||||
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}$(translate "Source:")${CL} ${BL}https://pve.proxmox.com/wiki/Upgrade_from_8_to_9${CL}"
|
||||
echo -e
|
||||
echo -e
|
||||
echo -e "${TAB}${BOLD}$(translate "IMPORTANT PREREQUISITES:")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}• $(translate "System must be updated to latest PVE 8.4+ before starting")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "Use SSH or terminal access (SSH recommended)")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "Use tmux or screen to avoid interruptions")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "Have valid backups of all VMs and containers")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "At least 5GB free space on root filesystem")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "Do not run the upgrade from the Web UI virtual console (it will disconnect)")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
|
||||
echo -e
|
||||
|
||||
|
||||
show_command "1" \
|
||||
"$(translate "Update system to latest PVE 8.4+ (if not done already):")\n\n" \
|
||||
"apt update && apt dist-upgrade -y" \
|
||||
"$(translate "Or use ProxMenux update function")" \
|
||||
"\n\n"
|
||||
|
||||
|
||||
show_command "2" \
|
||||
"$(translate "Verify PVE version (must be 8.4.1 or newer):")\n\n" \
|
||||
"pveversion" \
|
||||
"" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "2.1" \
|
||||
"${YW}$(translate "If this node runs hyper-converged Ceph: ensure Ceph is 19.x (Squid) BEFORE upgrading PVE.")${CL}\n\n" \
|
||||
"ceph --version" \
|
||||
"$(translate "If not 19.x, upgrade Ceph (Reef→Squid) first per the official guide:") ${BL}https://pve.proxmox.com/wiki/Ceph_Squid${CL}" \
|
||||
"\n"
|
||||
|
||||
|
||||
|
||||
show_command "3" \
|
||||
"$(translate "Run upgrade checklist script:")\n\n" \
|
||||
"pve8to9 --full" \
|
||||
"${YW}$(translate "If it warns about 'systemd-boot' meta-package, remove it:")${CL} apt remove systemd-boot" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "4" \
|
||||
"$(translate "Start terminal multiplexer (recommended):")\n\n" \
|
||||
"tmux new-session -s upgrade ${DARK_GRAY}$(translate "# Recommended: avoids disconnection during upgrade")${CL}\n\n screen -S upgrade ${DARK_GRAY}$(translate "# Alternative if you prefer screen")${CL}" \
|
||||
"" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "5" \
|
||||
"$(translate "Update Debian repositories to Trixie:")\n\n" \
|
||||
"sed -i 's/bookworm/trixie/g' /etc/apt/sources.list" \
|
||||
"" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "6" \
|
||||
"${YW}$(translate "Update PVE enterprise repository (Only if using enterprise):")${CL}\n\n" \
|
||||
"${CUS}sed -i 's/bookworm/trixie/g' /etc/apt/sources.list.d/pve-enterprise.list${CL}" \
|
||||
"$(translate "Skip this step if using no-subscription repository")" \
|
||||
"\n\n"
|
||||
|
||||
|
||||
show_command "7" \
|
||||
"${YW}$(translate "Add new PVE 9 enterprise repository (deb822 format) (Only if using enterprise):")${CL}\n\n" \
|
||||
"${CUS}cat > /etc/apt/sources.list.d/pve-enterprise.sources << EOF
|
||||
Types: deb
|
||||
URIs: https://enterprise.proxmox.com/debian/pve
|
||||
Suites: trixie
|
||||
Components: pve-enterprise
|
||||
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
EOF${CL}" \
|
||||
"$(translate "Only if using enterprise subscription")" \
|
||||
"\n\n"
|
||||
|
||||
|
||||
show_command "8" \
|
||||
"$(translate "OR add new PVE 9 no-subscription repository:")\n\n" \
|
||||
"cat > /etc/apt/sources.list.d/proxmox.sources << EOF
|
||||
Types: deb
|
||||
URIs: http://download.proxmox.com/debian/pve
|
||||
Suites: trixie
|
||||
Components: pve-no-subscription
|
||||
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
EOF" \
|
||||
"$(translate "Only if using no-subscription repository")" \
|
||||
"\n\n"
|
||||
|
||||
|
||||
show_command "8.1" \
|
||||
"$(translate "Refresh APT index and verify repositories:")\n\n" \
|
||||
"apt update && apt policy | sed -n '1,120p'" \
|
||||
"$(translate "Ensure there are no errors and that proxmox-ve candidate shows 9.x")" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "9" \
|
||||
"${YW}$(translate "Update Ceph repository (Only if using Ceph):")${CL}\n\n" \
|
||||
"${CUS}cat > /etc/apt/sources.list.d/ceph.sources << EOF
|
||||
Types: deb
|
||||
URIs: http://download.proxmox.com/debian/ceph-squid
|
||||
Suites: trixie
|
||||
Components: no-subscription
|
||||
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
EOF${CL}" \
|
||||
"$(translate "Use enterprise URL if you have subscription.")" \
|
||||
"\n\n"
|
||||
|
||||
|
||||
show_command "10" \
|
||||
"$(translate "Remove old repository files:")\n\n" \
|
||||
"rm -f /etc/apt/sources.list.d/pve-enterprise.list /etc/apt/sources.list.d/ceph.list" \
|
||||
"$(translate "Also comment any remaining 'bookworm' entries in *.list if present.")" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "11" \
|
||||
"$(translate "Update package index:")\n\n" \
|
||||
"apt update" \
|
||||
"" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "12" \
|
||||
"$(translate "Disable kernel audit messages (optional but recommended):")\n\n" \
|
||||
"systemctl disable --now systemd-journald-audit.socket" \
|
||||
"" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "13" \
|
||||
"$(translate "Start the main system upgrade:")\n\n" \
|
||||
"apt dist-upgrade" \
|
||||
"$(translate "This will take time. Answer prompts carefully - see notes below.")\n" \
|
||||
"\n"
|
||||
|
||||
|
||||
echo -e "${TAB}${BOLD}$(translate "UPGRADE PROMPTS - RECOMMENDED ANSWERS:")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}/etc/issue:${CL} ${YW}$(translate "Keep current version (N)")${CL}"
|
||||
echo -e "${TAB}${BGN}/etc/lvm/lvm.conf:${CL} ${YW}$(translate "Install maintainer's version (Y)")${CL}"
|
||||
echo -e "${TAB}${BGN}/etc/ssh/sshd_config:${CL} ${YW}$(translate "Install maintainer's version (Y)")${CL}"
|
||||
echo -e "${TAB}${BGN}/etc/default/grub:${CL} ${YW}$(translate "Keep current version (N) if modified")${CL}"
|
||||
echo -e "${TAB}${BGN}/etc/chrony/chrony.conf:${CL} ${YW}$(translate "Install maintainer's version (Y)")${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Service restarts:")${CL} ${YW}$(translate "Use default (Yes)")${CL}"
|
||||
echo -e "${TAB}${BGN}apt-listchanges:${CL} ${YW}$(translate "Press 'q' to exit")${CL}"
|
||||
echo -e
|
||||
echo -e
|
||||
echo -e
|
||||
|
||||
|
||||
show_command "13.1" \
|
||||
"${YW}$(translate "If booting in EFI mode with root on LVM: install GRUB for EFI")${CL}\n\n" \
|
||||
"[ -d /sys/firmware/efi ] && apt install grub-efi-amd64" \
|
||||
"$(translate "Per official known issues; ensures proper boot after upgrade")" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "14" \
|
||||
"$(translate "Run checklist again to verify upgrade:")\n\n" \
|
||||
"pve8to9 --full" \
|
||||
"$(translate "Should show fewer or no issues")" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "15" \
|
||||
"$(translate "Reboot the system:")\n\n" \
|
||||
"reboot" \
|
||||
"" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "16" \
|
||||
"$(translate "After reboot, verify PVE version:")\n\n" \
|
||||
"pveversion" \
|
||||
"$(translate "Should show pve-manager/9.x.x")" \
|
||||
"\n"
|
||||
|
||||
|
||||
show_command "17" \
|
||||
"$(translate "Optional: Modernize repository sources:")\n\n" \
|
||||
"apt modernize-sources" \
|
||||
"$(translate "Converts to deb822; keeps .list backups as .bak")" \
|
||||
"\n"
|
||||
|
||||
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
|
||||
echo -e
|
||||
echo -e
|
||||
echo -e "${TAB}${BOLD}$(translate "CLUSTER UPGRADE NOTES:")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}• $(translate "Upgrade one node at a time")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "Migrate VMs away from node being upgraded")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "Wait for each node to complete before starting next")${CL}"
|
||||
echo -e "${TAB}${BGN}• $(translate "HA groups will be migrated to HA rules automatically")${CL}"
|
||||
echo -e
|
||||
echo -e
|
||||
|
||||
echo -e
|
||||
echo -e "${TAB}${BOLD}$(translate "TROUBLESHOOTING:")${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${BGN}$(translate "If GUI does not load:")${CL} ${YW}Check with 'systemctl status pveproxy' and restart with 'systemctl restart pveproxy'${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "If ZFS errors occur:")${CL} ${YW}Ensure the 'zfsutils-linux' package is up to date${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "If network does not work:")${CL} ${YW}Check /etc/network/interfaces and ensure 'ifupdown2' is installed${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "If upgrade fails:")${CL} ${YW}apt -f install${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "If repositories error:")${CL} ${YW}Check /etc/apt/sources.list*${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "If 'proxmox-ve' removal warning:")${CL} ${YW}Fix repository configuration (ensure PVE 9 repo active)${CL}"
|
||||
echo -e "${TAB}${BGN}$(translate "Emergency recovery:")${CL} ${YW}Boot from rescue system${CL}"
|
||||
echo -e
|
||||
echo -e
|
||||
|
||||
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
echo -e
|
||||
read -r
|
||||
clear
|
||||
exit 0
|
||||
#bash <(curl -fsSL "$REPO_URL/scripts/utilities/upgrade_pve8_to_pve9.sh")
|
||||
|
||||
}
|
||||
|
||||
|
||||
# Main execution
|
||||
show_proxmox_upgrade_manual_guide
|
180
scripts/utilities/pve8to9_check.sh
Normal file
180
scripts/utilities/pve8to9_check.sh
Normal file
@@ -0,0 +1,180 @@
|
||||
#!/bin/bash
|
||||
# ==========================================================
|
||||
# ProxMenuX - Upgrade PVE 8 → 9 (Simplified, per official guide)
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
|
||||
# Version : 1.0
|
||||
# Last Updated: 14/08/2025
|
||||
# ==========================================================
|
||||
|
||||
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
|
||||
# ==========================================================
|
||||
|
||||
LOG="/var/log/pve8-a-pve9-$(date +%Y%m%d-%H%M%S).log"
|
||||
: > "$LOG"
|
||||
|
||||
disable_translation_post_upgrade() {
|
||||
translate() { echo "$1"; }
|
||||
}
|
||||
disable_translation_post_upgrade
|
||||
|
||||
if [[ "$pve_version" -ge 9 ]]; then
|
||||
disable_translation_post_upgrade
|
||||
fi
|
||||
|
||||
|
||||
|
||||
run_pve8to9_check2() {
|
||||
local tmp
|
||||
tmp="$(mktemp)"
|
||||
echo -e
|
||||
set -o pipefail
|
||||
pve8to9 --full 2>&1 | tee -a "$LOG" | tee "$tmp"
|
||||
local rc=${PIPESTATUS[0]}
|
||||
|
||||
local fails warns
|
||||
fails=$(grep -c 'FAIL:' "$tmp" || true)
|
||||
warns=$(grep -c 'WARN:' "$tmp" || true)
|
||||
|
||||
|
||||
if (( fails > 0 )); then
|
||||
echo -e
|
||||
echo -e "${BFR}${RD}[ERROR] $(translate "Pre-check found") $fails $(translate "blocking issue(s).")\n$(translate "Please resolve the problem(s) as described above, then re-run the upgrade script.")${CL}"
|
||||
echo -e
|
||||
|
||||
local repair_commands=()
|
||||
local repair_descriptions=()
|
||||
|
||||
# Error 1: systemd-boot meta-package
|
||||
if grep -q 'systemd-boot meta-package installed' "$tmp"; then
|
||||
repair_commands+=("apt install systemd-boot-efi systemd-boot-tools -y && apt remove systemd-boot -y")
|
||||
repair_descriptions+=("$(translate "Fix systemd-boot meta-package conflict")")
|
||||
echo -e "${YW}$(translate "Fix systemd-boot:") ${CL}apt install systemd-boot-efi systemd-boot-tools -y && apt remove systemd-boot -y"
|
||||
fi
|
||||
|
||||
|
||||
# Error 2: Ceph version incompatible
|
||||
if grep -q -E '(ceph.*version|ceph.*incompatible)' "$tmp"; then
|
||||
repair_commands+=("ceph versions && pveceph upgrade")
|
||||
repair_descriptions+=("$(translate "Upgrade Ceph to compatible version")")
|
||||
echo -e "${YW}$(translate "Fix Ceph version:") ${CL}ceph versions && pveceph upgrade"
|
||||
fi
|
||||
|
||||
# Error 3: Repository configuration issues
|
||||
if grep -q -E '(repository.*issue|repo.*problem|sources.*error)' "$tmp"; then
|
||||
repair_commands+=("cleanup_duplicate_repos && configure_repositories")
|
||||
repair_descriptions+=("$(translate "Fix repository configuration")")
|
||||
echo -e "${YW}$(translate "Fix repositories:") ${CL}cleanup_duplicate_repos && configure_repositories"
|
||||
fi
|
||||
|
||||
# Error 4: Package conflicts
|
||||
if grep -q -E '(package.*conflict|dependency.*problem)' "$tmp"; then
|
||||
repair_commands+=("apt update && apt autoremove -y && apt autoclean")
|
||||
repair_descriptions+=("$(translate "Resolve package conflicts")")
|
||||
echo -e "${YW}$(translate "Fix package conflicts:") ${CL}apt update && apt autoremove -y && apt autoclean"
|
||||
fi
|
||||
|
||||
# Error 5: Disk space issues
|
||||
if grep -q -E '(disk.*space|storage.*full|no.*space)' "$tmp"; then
|
||||
repair_commands+=("apt clean && apt autoremove -y && journalctl --vacuum-time=7d")
|
||||
repair_descriptions+=("$(translate "Free up disk space")")
|
||||
echo -e "${YW}$(translate "Fix disk space:") ${CL}apt clean && apt autoremove -y && journalctl --vacuum-time=7d"
|
||||
fi
|
||||
|
||||
# Error 6: Network/DNS issues
|
||||
if grep -q -E '(network.*error|dns.*problem|connection.*failed)' "$tmp"; then
|
||||
repair_commands+=("systemctl restart networking && systemctl restart systemd-resolved")
|
||||
repair_descriptions+=("$(translate "Fix network connectivity")")
|
||||
echo -e "${YW}$(translate "Fix network:") ${CL}systemctl restart networking && systemctl restart systemd-resolved"
|
||||
fi
|
||||
|
||||
echo -e
|
||||
|
||||
|
||||
if [[ ${#repair_commands[@]} -gt 0 ]]; then
|
||||
echo -e "${BFR}${CY}$(translate "Repair Options:")${CL}"
|
||||
echo -e "${TAB}${GN}1.${CL} $(translate "Try automatic repair of detected issues")"
|
||||
echo -e "${TAB}${GN}2.${CL} $(translate "Show manual repair commands")"
|
||||
echo -e
|
||||
echo -n "$(translate "Select option [1-2] (default: 2): ")"
|
||||
read -r repair_choice
|
||||
|
||||
case "$repair_choice" in
|
||||
1)
|
||||
echo -e
|
||||
msg_info2 "$(translate "Attempting automatic repair...")"
|
||||
local repair_success=0
|
||||
for i in "${!repair_commands[@]}"; do
|
||||
echo -e "${TAB}${YW}$(translate "Executing:") ${repair_descriptions[$i]}${CL}"
|
||||
if eval "${repair_commands[$i]}"; then
|
||||
msg_ok "${repair_descriptions[$i]} - $(translate "Success")"
|
||||
else
|
||||
msg_error "${repair_descriptions[$i]} - $(translate "Failed")"
|
||||
repair_success=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $repair_success -eq 0 ]]; then
|
||||
echo -e
|
||||
msg_info2 "$(translate "Re-running pre-check after repairs...")"
|
||||
sleep 2
|
||||
run_pve8to9_check
|
||||
return $?
|
||||
else
|
||||
echo -e
|
||||
msg_error "$(translate "Some repairs failed. Please fix manually and re-run the script.")"
|
||||
fi
|
||||
;;
|
||||
2)
|
||||
echo -e
|
||||
echo -e "$(translate "${BFR}${CY}Manual Repair Commands:${CL}")"
|
||||
for i in "${!repair_commands[@]}"; do
|
||||
echo -e "${TAB}${BL}# ${repair_descriptions[$i]}${CL}"
|
||||
echo -e
|
||||
echo -e "${TAB}${repair_commands[$i]}"
|
||||
echo -e
|
||||
done
|
||||
echo -e
|
||||
msg_info2 "$(translate "Once finished, re-run the script 'PVE 8 to 9 check' to verify that all issues.")"
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to exit the script after reading instructions...")"
|
||||
read -r
|
||||
rm -f "$tmp"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
msg_success "$(translate "Press Enter to continue")"
|
||||
read -r
|
||||
fi
|
||||
|
||||
echo -e
|
||||
msg_ok "$(translate "Checklist post-upgrade finished. Warnings:") $warns"
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to continue")"
|
||||
read -r
|
||||
rm -f "$tmp"
|
||||
return $rc
|
||||
|
||||
}
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Run PVE 8 to 9 check")"
|
||||
run_pve8to9_check2
|
||||
|
||||
|
@@ -56,7 +56,7 @@ install_system_utils() {
|
||||
}
|
||||
|
||||
|
||||
ensure_repositories() {
|
||||
ensure_repositories_() {
|
||||
local sources_file="/etc/apt/sources.list"
|
||||
local need_update=false
|
||||
|
||||
@@ -85,6 +85,98 @@ EOF
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ensure_repositories() {
|
||||
local pve_version need_update=false
|
||||
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 >= 9 )); then
|
||||
# ===== PVE 9 (Debian 13 - trixie) =====
|
||||
# proxmox.sources (no-subscription) ─ create if missing
|
||||
if [[ ! -f /etc/apt/sources.list.d/proxmox.sources ]]; then
|
||||
cat > /etc/apt/sources.list.d/proxmox.sources <<'EOF'
|
||||
Enabled: true
|
||||
Types: deb
|
||||
URIs: http://download.proxmox.com/debian/pve
|
||||
Suites: trixie
|
||||
Components: pve-no-subscription
|
||||
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
EOF
|
||||
need_update=true
|
||||
fi
|
||||
|
||||
# debian.sources ─ create if missing
|
||||
if [[ ! -f /etc/apt/sources.list.d/debian.sources ]]; then
|
||||
cat > /etc/apt/sources.list.d/debian.sources <<'EOF'
|
||||
Types: deb
|
||||
URIs: http://deb.debian.org/debian/
|
||||
Suites: trixie trixie-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: trixie-security
|
||||
Components: main contrib non-free-firmware
|
||||
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
|
||||
EOF
|
||||
need_update=true
|
||||
fi
|
||||
|
||||
# apt-get update only if needed or lists are empty
|
||||
if [[ "$need_update" == true ]] || [[ ! -d /var/lib/apt/lists || -z "$(ls -A /var/lib/apt/lists 2>/dev/null)" ]]; then
|
||||
msg_info "$(translate "Updating APT package lists...")"
|
||||
apt-get update >/dev/null 2>&1 || apt-get update
|
||||
fi
|
||||
|
||||
|
||||
else
|
||||
# ===== PVE 8 (Debian 12 - bookworm) =====
|
||||
local sources_file="/etc/apt/sources.list"
|
||||
|
||||
# Debian base (create or append minimal lines if missing)
|
||||
if ! grep -qE 'deb .* bookworm .* main' "$sources_file" 2>/dev/null; then
|
||||
{
|
||||
echo "deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware"
|
||||
echo "deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware"
|
||||
echo "deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware"
|
||||
} >> "$sources_file"
|
||||
need_update=true
|
||||
fi
|
||||
|
||||
# Proxmox no-subscription list (classic) if missing
|
||||
if [[ ! -f /etc/apt/sources.list.d/pve-no-subscription.list ]]; then
|
||||
echo "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" \
|
||||
> /etc/apt/sources.list.d/pve-no-subscription.list
|
||||
need_update=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# apt-get update only if needed or lists are empty
|
||||
if [[ "$need_update" == true ]] || [[ ! -d /var/lib/apt/lists || -z "$(ls -A /var/lib/apt/lists 2>/dev/null)" ]]; then
|
||||
msg_info "$(translate "Updating APT package lists...")"
|
||||
apt-get update >/dev/null 2>&1 || apt-get update
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
install_single_package() {
|
||||
local package="$1"
|
||||
local command_name="${2:-$package}"
|
||||
@@ -143,10 +235,12 @@ EOF
|
||||
"btop" "$(translate "Modern resource monitor")" "OFF"
|
||||
"iftop" "$(translate "Real-time network usage")" "OFF"
|
||||
"iotop" "$(translate "Monitor disk I/O usage")" "OFF"
|
||||
"iperf3" "$(translate "Network performance testing")" "OFF"
|
||||
#"iperf3" "$(translate "Network performance testing")" "OFF"
|
||||
"intel-gpu-tools" "$(translate "tools for the Intel graphics driver")" "OFF"
|
||||
"s-tui" "$(translate "Stress-Terminal UI")" "OFF"
|
||||
"ipset" "$(translate "Manage IP sets")" "OFF"
|
||||
"iptraf-ng" "$(translate "Network monitoring tool")" "OFF"
|
||||
"mlocate" "$(translate "Locate files quickly")" "OFF"
|
||||
"plocate" "$(translate "Locate files quickly")" "OFF"
|
||||
"msr-tools" "$(translate "Access CPU MSRs")" "OFF"
|
||||
"net-tools" "$(translate "Legacy networking tools")" "OFF"
|
||||
"sshpass" "$(translate "Non-interactive SSH login")" "OFF"
|
||||
@@ -190,7 +284,7 @@ EOF
|
||||
local warning=0
|
||||
|
||||
declare -A package_to_command=(
|
||||
["mlocate"]="locate"
|
||||
["plocate"]="locate"
|
||||
["msr-tools"]="rdmsr"
|
||||
["net-tools"]="netstat"
|
||||
["libguestfs-tools"]="virt-filesystems"
|
||||
@@ -302,10 +396,12 @@ EOF
|
||||
"btop:Modern resource monitor"
|
||||
"iftop:Real-time network usage"
|
||||
"iotop:Monitor disk I/O usage"
|
||||
"iperf3:Network performance testing"
|
||||
#"iperf3:Network performance testing"
|
||||
"intel-gpu-tools:tools for the Intel graphics driver"
|
||||
"s-tui:Stress-Terminal UI"
|
||||
"ipset:Manage IP sets"
|
||||
"iptraf-ng:Network monitoring tool"
|
||||
"locate:Locate files quickly"
|
||||
"plocate:Locate files quickly"
|
||||
"rdmsr:Access CPU MSRs"
|
||||
"netstat:Legacy networking tools"
|
||||
"sshpass:Non-interactive SSH login"
|
||||
@@ -361,10 +457,12 @@ EOF
|
||||
"btop:btop:Modern resource monitor"
|
||||
"iftop:iftop:Real-time network usage"
|
||||
"iotop:iotop:Monitor disk I/O usage"
|
||||
"iperf3:iperf3:Network performance testing"
|
||||
#"iperf3:iperf3:Network performance testing"
|
||||
"intel-gpu-tools:tools for the Intel graphics driver"
|
||||
"s-tui:Stress-Terminal UI"
|
||||
"ipset:ipset:Manage IP sets"
|
||||
"iptraf-ng:iptraf-ng:Network monitoring tool"
|
||||
"mlocate:locate:Locate files quickly"
|
||||
"plocate:locate:Locate files quickly"
|
||||
"msr-tools:rdmsr:Access CPU MSRs"
|
||||
"net-tools:netstat:Legacy networking tools"
|
||||
"sshpass:sshpass:Non-interactive SSH login"
|
||||
|
1128
scripts/utilities/upgrade_pve8_to_pve9.sh
Normal file
1128
scripts/utilities/upgrade_pve8_to_pve9.sh
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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() {
|
||||
|
@@ -109,8 +109,10 @@ function select_linux_iso_official() {
|
||||
"Ubuntu 24.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/24.04/ubuntu-24.04.2-live-server-amd64.iso"
|
||||
"Ubuntu 22.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso"
|
||||
"Ubuntu 20.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/20.04/ubuntu-20.04.6-live-server-amd64.iso"
|
||||
"Debian 13|Desktop|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-13.0.0-amd64-DVD-1.iso"
|
||||
"Debian 12|Desktop|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-12.10.0-amd64-DVD-1.iso"
|
||||
"Debian 11|Desktop|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-dvd/debian-11.11.0-amd64-DVD-1.iso"
|
||||
"Debian 13 Netinst|CLI|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-13.0.0-amd64-netinst.iso"
|
||||
"Debian 12 Netinst|CLI|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.10.0-amd64-netinst.iso"
|
||||
"Debian 11 Netinst|CLI|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-cd/debian-11.11.0-amd64-netinst.iso"
|
||||
"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"
|
||||
@@ -165,12 +167,13 @@ function select_linux_iso_official() {
|
||||
function select_linux_cloudinit() {
|
||||
local CLOUDINIT_OPTIONS=(
|
||||
"1" "Arch Linux (Cloud-Init automated) │ Helper Scripts"
|
||||
"2" "Debian 12 (Cloud-Init automated) │ Helper Scripts"
|
||||
"3" "Ubuntu 22.04 (Cloud-Init automated) │ Helper Scripts"
|
||||
"4" "Ubuntu 24.04 (Cloud-Init automated) │ Helper Scripts"
|
||||
"5" "Ubuntu 24.10 (Cloud-Init automated) │ Helper Scripts"
|
||||
"6" "Ubuntu 25.04 (Cloud-Init automated) │ Helper Scripts"
|
||||
"7" "$(translate "Return to Main Menu")"
|
||||
"2" "Debian 13 (Cloud-Init automated) │ Helper Scripts"
|
||||
"3" "Debian 12 (Cloud-Init automated) │ Helper Scripts"
|
||||
"4" "Ubuntu 22.04 (Cloud-Init automated) │ Helper Scripts"
|
||||
"5" "Ubuntu 24.04 (Cloud-Init automated) │ Helper Scripts"
|
||||
"6" "Ubuntu 24.10 (Cloud-Init automated) │ Helper Scripts"
|
||||
"7" "Ubuntu 25.04 (Cloud-Init automated) │ Helper Scripts"
|
||||
"8" "$(translate "Return to Main Menu")"
|
||||
)
|
||||
|
||||
local script_selection
|
||||
@@ -185,24 +188,27 @@ function select_linux_cloudinit() {
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/archlinux-vm.sh")
|
||||
;;
|
||||
2)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh")
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-13-vm.sh")
|
||||
;;
|
||||
3)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2204-vm.sh")
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh")
|
||||
;;
|
||||
4)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2404-vm.sh")
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2204-vm.sh")
|
||||
;;
|
||||
5)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2410-vm.sh")
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2404-vm.sh")
|
||||
;;
|
||||
6)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2410-vm.sh")
|
||||
;;
|
||||
7)
|
||||
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2504-vm.sh")
|
||||
echo -e
|
||||
echo -e "after installation, checkout:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/272"
|
||||
echo -e
|
||||
;;
|
||||
7)
|
||||
8)
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
@@ -46,7 +46,7 @@ function select_nas_iso() {
|
||||
"4" "OpenMediaVault VM (Debian based)"
|
||||
"5" "XigmaNAS VM (FreeBSD based)"
|
||||
"6" "Rockstor VM (openSUSE based)"
|
||||
"7" "ZimaOS VM (R0GGER proxmox-zimaos)"
|
||||
"7" "ZimaOS VM (Proxmox-zimaos)"
|
||||
"8" "Umbrel OS VM (Helper Scripts)"
|
||||
"9" "$(translate "Return to Main Menu")"
|
||||
)
|
||||
@@ -54,7 +54,7 @@ function select_nas_iso() {
|
||||
local NAS_TYPE
|
||||
NAS_TYPE=$(dialog --backtitle "ProxMenux" \
|
||||
--title "$(translate "NAS Systems")" \
|
||||
--menu "\n$(translate "Select the NAS system to install:")" 18 70 10 \
|
||||
--menu "\n$(translate "Select the NAS system to install:")" 20 70 10 \
|
||||
"${NAS_OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||
|
||||
|
||||
@@ -103,18 +103,9 @@ function select_nas_iso() {
|
||||
HN="Rockstor"
|
||||
;;
|
||||
7)
|
||||
HN="ZimaOS-VM"
|
||||
if ! confirm_vm_creation; then
|
||||
return 1
|
||||
fi
|
||||
bash -c "$(wget -qLO - https://raw.githubusercontent.com/R0GGER/proxmox-zimaos/refs/heads/main/zimaos_zimacube.sh)"
|
||||
echo -e
|
||||
bash <(curl -s "$REPO_URL/scripts/vm/zimaos.sh")
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
|
||||
whiptail --title "Proxmox VE - ZimaOS" \
|
||||
--msgbox "$(translate "ZimaOS installer script by R0GGER\n\nVisit the GitHub repo to learn more, contribute, or support the project:\n\nhttps://github.com/R0GGER/proxmox-zimaos")" 15 70
|
||||
|
||||
return 1
|
||||
;;
|
||||
8)
|
||||
|
1207
scripts/vm/zimaos.sh
Normal file
1207
scripts/vm/zimaos.sh
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
1.1.4
|
||||
1.1.5
|
BIN
web/public/share/main-menu.png
Normal file
BIN
web/public/share/main-menu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
Reference in New Issue
Block a user