mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-09-15 03:06:45 +00:00
The change journal exists so a sysadmin sees what ProxMenux changed on the host — its own configuration, packages and services — not how it uses the host or configures a guest. Several scripts recorded operations that are neither: disk passthrough to a VM, container conversions, VM import/export, mounting a share into an LXC. Those are restored to their original, uninstrumented form. Scripts that operate on the host while also installing a package now record only the package: format-disk keeps its exFAT-tools install, the UUP ISO builder its build dependencies, and the share/host scripts their packages, services and /etc/fstab writes, while the mount and unmount operations they used to log are dropped. The page now reads in three sections: what ProxMenux optimized after install (each function under its menu name), what its other host scripts changed (by script), and what it installed (packages and utilities, each referencing the script that installed it). A file reads as created or modified with its diff, a service shows its state transition, and the undo line appears only when a revert is possible.
737 lines
29 KiB
Bash
737 lines
29 KiB
Bash
#!/bin/bash
|
|
# ==========================================================
|
|
# ProxMenux - NFS Client Manager for LXC
|
|
# ==========================================================
|
|
# Author : MacRimi
|
|
# Copyright : (c) 2024 MacRimi
|
|
# License : GPL-3.0
|
|
# https://github.com/MacRimi/ProxMenux/blob/main/LICENSE
|
|
# Version : 1.0
|
|
# ==========================================================
|
|
# Description:
|
|
# Manages NFS client mounts from inside a Proxmox LXC container.
|
|
# Requires a privileged container (kernel NFS client needs
|
|
# capabilities that unprivileged CTs do not expose).
|
|
#
|
|
# Features:
|
|
# - Mount NFS shares (temporary and permanent via /etc/fstab).
|
|
# - List current NFS mounts inside the CT.
|
|
# - Unmount and remove NFS shares cleanly.
|
|
# - Auto-discover NFS servers on the local network.
|
|
# ==========================================================
|
|
|
|
# Configuration
|
|
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
|
|
BASE_DIR="/usr/local/share/proxmenux"
|
|
UTILS_FILE="$BASE_DIR/utils.sh"
|
|
|
|
if [[ -f "$UTILS_FILE" ]]; then
|
|
source "$UTILS_FILE"
|
|
fi
|
|
|
|
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
|
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
|
fi
|
|
|
|
# Load shared functions
|
|
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
|
|
if ! source "$SHARE_COMMON_FILE" 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() {
|
|
local FUNC_VERSION="1.0"
|
|
pmx_journal_context "install_nfs_client" "$FUNC_VERSION"
|
|
|
|
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")"
|
|
|
|
# Pre-flight: refuse non-Debian-family CTs. The script targets
|
|
# `apt-get` only — Alpine / Rocky / AlmaLinux fail with cryptic
|
|
# errors mid-flow. Audit Tier 6 — `nfs_client.sh`/`samba_client.sh`
|
|
# asume distro Debian-family sin detección.
|
|
if ! pct exec "$CTID" -- bash -c 'command -v apt-get' &>/dev/null; then
|
|
msg_error "$(translate "This container does not have apt-get. NFS client installation only supports Debian/Ubuntu containers.")"
|
|
msg_success "$(translate "Press Enter to return to menu...")"
|
|
read -r
|
|
return 1
|
|
fi
|
|
|
|
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() {
|
|
local FUNC_VERSION="1.0"
|
|
pmx_journal_context "discover_nfs_servers" "$FUNC_VERSION"
|
|
|
|
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
|
|
pmx_install_pkg nmap
|
|
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() {
|
|
local FUNC_VERSION="1.0"
|
|
# 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
|
|
|
|
pmx_journal_context "mount_nfs_share" "$FUNC_VERSION"
|
|
|
|
|
|
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 --in-place "\|$MOUNT_POINT|d" /etc/fstab
|
|
FSTAB_ENTRY="$NFS_PATH $MOUNT_POINT nfs ${MOUNT_OPTIONS},_netdev,x-systemd.automount,noauto 0 0"
|
|
pct exec "$CTID" -- bash -c "printf '%s\\n' '$FSTAB_ENTRY' | tee -a /etc/fstab >/dev/null"
|
|
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() {
|
|
local FUNC_VERSION="1.0"
|
|
pmx_journal_context "unmount_nfs_share" "$FUNC_VERSION"
|
|
|
|
# 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 --in-place "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
|
|
msg_ok "$(translate "Removed from /etc/fstab.")"
|
|
|
|
# Actually unmount it now (the previous version only edited fstab,
|
|
# so the share remained mounted until the CT rebooted). Try a
|
|
# graceful umount first, fall back to lazy umount if busy. Audit
|
|
# Tier 7 — `unmount_nfs_share` only borra fstab.
|
|
if pct exec "$CTID" -- mountpoint -q "$SELECTED_MOUNT" 2>/dev/null; then
|
|
if pct exec "$CTID" -- umount "$SELECTED_MOUNT" 2>/dev/null; then
|
|
msg_ok "$(translate "Unmounted") $SELECTED_MOUNT"
|
|
elif pct exec "$CTID" -- umount -l "$SELECTED_MOUNT" 2>/dev/null; then
|
|
msg_warn "$(translate "Mount was busy — performed lazy unmount") $SELECTED_MOUNT"
|
|
else
|
|
msg_warn "$(translate "Could not unmount") $SELECTED_MOUNT $(translate "automatically. Reboot LXC to fully release.")"
|
|
fi
|
|
fi
|
|
|
|
echo -e ""
|
|
msg_ok "$(translate "NFS share unmount completed.")"
|
|
fi
|
|
|
|
echo -e ""
|
|
msg_success "$(translate "Press Enter to return to menu...")"
|
|
read -r
|
|
}
|
|
|
|
|
|
|
|
|
|
test_nfs_connectivity() {
|
|
local FUNC_VERSION="1.0"
|
|
pmx_journal_context "test_nfs_connectivity" "$FUNC_VERSION"
|
|
|
|
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
|