-(function() {
- 'use strict';
- function removeSubscriptionElements() {
- try {
- const dialogs = document.querySelectorAll('dialog.pwt-outer-dialog');
- dialogs.forEach(d => {
- const text = (d.textContent || '').toLowerCase();
- if (text.includes('subscription') || text.includes('no valid')) { d.remove(); }
- });
- const cards = document.querySelectorAll('.pwt-card.pwt-p-2.pwt-d-flex.pwt-interactive.pwt-justify-content-center');
- cards.forEach(c => {
- const text = (c.textContent || '').toLowerCase();
- const hasButton = c.querySelector('button');
- if (!hasButton && (text.includes('subscription') || text.includes('no valid'))) { c.remove(); }
- });
- const alerts = document.querySelectorAll('[class*="alert"], [class*="warning"], [class*="notice"]');
- alerts.forEach(a => {
- const text = (a.textContent || '').toLowerCase();
- if (text.includes('subscription') || text.includes('no valid')) { a.remove(); }
- });
- } catch (e) { console.warn('Error removing subscription elements:', e); }
- }
- if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', removeSubscriptionElements); }
- else { removeSubscriptionElements(); }
- const observer = new MutationObserver(removeSubscriptionElements);
- if (document.body) {
- observer.observe(document.body, { childList: true, subtree: true });
- const interval = setInterval(removeSubscriptionElements, 500);
- setTimeout(() => { try { observer.disconnect(); clearInterval(interval); } catch(e){} }, 30000);
- }
-})();
-
-EOM
-}
-
-reload_services() {
- systemctl is-active --quiet pveproxy 2>/dev/null && {
- systemctl reload pveproxy 2>/dev/null || systemctl restart pveproxy 2>/dev/null || true
- }
- systemctl is-active --quiet nginx 2>/dev/null && {
- systemctl reload nginx 2>/dev/null || true
- }
- systemctl is-active --quiet pvedaemon 2>/dev/null && {
- systemctl reload pvedaemon 2>/dev/null || true
- }
- find /var/cache/pve-manager/ -type f -delete 2>/dev/null || true
- find /var/lib/pve-manager/ -type f -delete 2>/dev/null || true
-}
-
-main() {
- patch_web || return 1
- patch_mobile
- reload_services
-}
-
-main
-EOF
- chmod 755 "$PATCH_BIN"
-}
-# ----------------------------------------------------
-
-
-create_apt_hook() {
- cat > "$APT_HOOK" <<'EOF'
-/* ProxMenux: reapply nag patch after upgrades */
-DPkg::Post-Invoke { "/usr/local/bin/pve-remove-nag.sh || true"; };
-EOF
- chmod 644 "$APT_HOOK"
- apt-config dump >/dev/null 2>&1 || { msg_warn "APT hook syntax issue"; rm -f "$APT_HOOK"; }
-}
-
-
-
-remove_subscription_banner_pve9() {
- local pve_version
- pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1 || true)
- local pve_major="${pve_version%%.*}"
-
- msg_info "$(translate "Detected Proxmox VE ${pve_version:-9.x} – removing subscription banner")"
-
- create_patch_script
- create_apt_hook
-
- if ! "$PATCH_BIN"; then
- msg_error "$(translate "Error applying patches")"
- return 1
- fi
-
- register_tool "subscription_banner" true
- msg_ok "$(translate "Subscription banner removed successfully.")"
- msg_ok "$(translate "Refresh your browser to see changes.")"
-}
-
-
-
-if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
- remove_subscription_banner_pve9
-fi
diff --git a/scripts/global/update-pve.sh b/scripts/global/update-pve.sh
deleted file mode 100644
index 8cfc0a69..00000000
--- a/scripts/global/update-pve.sh
+++ /dev/null
@@ -1,339 +0,0 @@
-#!/bin/bash
-# ==========================================================
-# Proxmox VE Update Script
-# ==========================================================
-
-# Configuration
-LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
-BASE_DIR="/usr/local/share/proxmenux"
-UTILS_FILE="$BASE_DIR/utils.sh"
-VENV_PATH="/opt/googletrans-env"
-TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
-
-if [[ -f "$UTILS_FILE" ]]; then
- source "$UTILS_FILE"
-fi
-
-load_language
-initialize_cache
-
-ensure_tools_json() {
- [ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
-}
-
-register_tool() {
- local tool="$1"
- local state="$2"
- ensure_tools_json
- jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
-}
-
-download_common_functions() {
- if ! source "$LOCAL_SCRIPTS/global/common-functions.sh"; then
- return 1
- fi
-}
-
-update_pve9() {
- local pve_version=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1)
- local start_time=$(date +%s)
- local log_file="/var/log/proxmox-update-$(date +%Y%m%d-%H%M%S).log"
- local changes_made=false
- local OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs)"
- local TARGET_CODENAME="trixie"
-
- if [ -z "$OS_CODENAME" ]; then
- OS_CODENAME=$(lsb_release -cs 2>/dev/null || echo "trixie")
- fi
-
- download_common_functions
-
-
- msg_info2 "$(translate "Detected: Proxmox VE $pve_version (Current: $OS_CODENAME, Target: $TARGET_CODENAME)")"
- echo -e
-
- local available_space=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}')
- if [ "$available_space" -lt 1024 ]; then
- msg_error "$(translate "Insufficient disk space. Available: ${available_space}MB")"
- echo -e
- msg_success "$(translate "Press Enter to return to menu...")"
- read -r
- return 1
- fi
-
- if ! ping -c 1 download.proxmox.com >/dev/null 2>&1; then
- msg_error "$(translate "Cannot reach Proxmox repositories")"
- echo -e
- msg_success "$(translate "Press Enter to return to menu...")"
- read -r
- return 1
- fi
-
-
-disable_sources_repo() {
- local file="$1"
- if [[ -f "$file" ]]; then
- sed -i ':a;/^\n*$/{$d;N;ba}' "$file"
-
- if grep -q "^Enabled:" "$file"; then
- sed -i 's/^Enabled:.*$/Enabled: false/' "$file"
- else
- echo "Enabled: false" >> "$file"
- fi
-
- if ! grep -q "^Types: " "$file"; then
- msg_warn "$(translate "Malformed .sources file detected, removing: $(basename "$file")")"
- rm -f "$file"
- fi
- return 0
- fi
- return 1
-}
-
-
-
-
- if disable_sources_repo "/etc/apt/sources.list.d/pve-enterprise.sources"; then
- msg_ok "$(translate "Enterprise Proxmox repository disabled")"
- changes_made=true
- fi
-
- if disable_sources_repo "/etc/apt/sources.list.d/ceph.sources"; then
- msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")"
- changes_made=true
- fi
-
- for legacy_file in /etc/apt/sources.list.d/pve-public-repo.list \
- /etc/apt/sources.list.d/pve-install-repo.list \
- /etc/apt/sources.list.d/debian.list; do
- if [[ -f "$legacy_file" ]]; then
- rm -f "$legacy_file"
- msg_ok "$(translate "Removed legacy repository: $(basename "$legacy_file")")"
- fi
- done
-
-
- if [[ -f /etc/apt/sources.list.d/debian.sources ]]; then
- rm -f /etc/apt/sources.list.d/debian.sources
- msg_ok "$(translate "Old debian.sources file removed to prevent duplication")"
- fi
-
-
- msg_info "$(translate "Creating Proxmox VE 9.x no-subscription repository...")"
- cat > /etc/apt/sources.list.d/proxmox.sources << EOF
-Enabled: true
-Types: deb
-URIs: http://download.proxmox.com/debian/pve
-Suites: ${TARGET_CODENAME}
-Components: pve-no-subscription
-Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
-EOF
- msg_ok "$(translate "Proxmox VE 9.x no-subscription repository created")"
- changes_made=true
-
-
-
- msg_info "$(translate "Creating Debian ${TARGET_CODENAME} sources file...")"
- cat > /etc/apt/sources.list.d/debian.sources << EOF
-Types: deb
-URIs: http://deb.debian.org/debian/
-Suites: ${TARGET_CODENAME} ${TARGET_CODENAME}-updates
-Components: main contrib non-free non-free-firmware
-Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
-
-Types: deb
-URIs: http://security.debian.org/debian-security/
-Suites: ${TARGET_CODENAME}-security
-Components: main contrib non-free non-free-firmware
-Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
-EOF
-
-
-
- msg_ok "$(translate "Debian repositories configured for $TARGET_CODENAME")"
-
- local firmware_conf="/etc/apt/apt.conf.d/no-firmware-warnings.conf"
- if [ ! -f "$firmware_conf" ]; then
- msg_info "$(translate "Disabling non-free firmware warnings...")"
- echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > "$firmware_conf"
- msg_ok "$(translate "Non-free firmware warnings disabled")"
- fi
-
- update_output=$(apt-get update 2>&1)
- update_exit_code=$?
-
- if [ $update_exit_code -eq 0 ]; then
- msg_ok "$(translate "Package lists updated successfully")"
- else
- if echo "$update_output" | grep -q "NO_PUBKEY\|GPG error"; then
- msg_info "$(translate "Fixing GPG key issues...")"
- apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $(echo "$update_output" | grep "NO_PUBKEY" | sed 's/.*NO_PUBKEY //' | head -1) 2>/dev/null
- if apt-get update > "$log_file" 2>&1; then
- msg_ok "$(translate "Package lists updated after GPG fix")"
- else
- msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
- return 1
- fi
- elif echo "$update_output" | grep -q "404\|Failed to fetch"; then
- msg_warn "$(translate "Some repositories are not available, continuing with available ones...")"
- else
- msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
- echo "Error details: $update_output"
- return 1
- fi
- fi
-
-
-
- if apt policy 2>/dev/null | grep -q "${TARGET_CODENAME}.*pve-no-subscription"; then
- msg_ok "$(translate "Proxmox VE 9.x repositories verified")"
- else
- msg_warn "$(translate "Proxmox VE 9.x repositories verification inconclusive, continuing...")"
- fi
-
- local current_pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
- local available_pve_version=$(apt-cache policy pve-manager 2>/dev/null | grep -oP 'Candidate: \K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
- local upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable")
- local security_updates=$(apt list --upgradable 2>/dev/null | grep -c "security")
-
- show_update_menu() {
- local current_version="$1"
- local target_version="$2"
- local upgradable_count="$3"
- local security_count="$4"
-
- local menu_text="$(translate "System Update Information")\n\n"
- menu_text+="$(translate "Current PVE Version"): $current_version\n"
- if [ -n "$target_version" ] && [ "$target_version" != "$current_version" ]; then
- menu_text+="$(translate "Available PVE Version"): $target_version\n"
- fi
- menu_text+="\n$(translate "Package Updates Available"): $upgradable_count\n"
- menu_text+="$(translate "Security Updates"): $security_count\n\n"
-
- if [ "$upgradable_count" -eq 0 ]; then
- menu_text+="$(translate "System is already up to date")"
- whiptail --title "$(translate "Update Status")" --msgbox "$menu_text" 15 70
- return 2
- else
- menu_text+="$(translate "Do you want to proceed with the system update?")"
- if whiptail --title "$(translate "Proxmox Update")" --yesno "$menu_text" 18 70; then
- return 0
- else
- return 1
- fi
- fi
- }
-
- show_update_menu "$current_pve_version" "$available_pve_version" "$upgradable" "$security_updates"
- MENU_RESULT=$?
-
- if [[ $MENU_RESULT -eq 1 ]]; then
- msg_info2 "$(translate "Update cancelled by user")"
- apt-get -y autoremove > /dev/null 2>&1 || true
- apt-get -y autoclean > /dev/null 2>&1 || true
- return 0
- elif [[ $MENU_RESULT -eq 2 ]]; then
- msg_ok "$(translate "System is already up to date. No update needed.")"
- apt-get -y autoremove > /dev/null 2>&1 || true
- apt-get -y autoclean > /dev/null 2>&1 || true
- return 0
- fi
-
- msg_info "$(translate "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 "Old time services removed successfully")"
- else
- 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")"
-
- tput sc
-
- DEBIAN_FRONTEND=noninteractive apt-get -y \
- -o Dpkg::Options::='--force-confdef' \
- -o Dpkg::Options::='--force-confold' \
- dist-upgrade 2>&1 | while IFS= read -r line; do
-
- echo "$line" >> "$log_file"
-
- if [[ "$line" =~ \[[#=\-]+\]\ *[0-9]{1,3}% ]]; then
- continue
- fi
-
- if [[ "$line" =~ ^(Setting\ up|Unpacking|Preparing\ to\ unpack|Processing\ triggers\ for) ]]; then
- package_name=$(echo "$line" | sed -E 's/.*(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ([^ :]+).*/\2/')
- [ -z "$package_name" ] && package_name="$(translate "Unknown")"
-
- row=$(( $(tput lines) - 6 ))
- tput cup $row 0; printf "%s\n" "$(translate "Installing packages...")"
- tput cup $((row + 1)) 0; printf "%s\n" "──────────────────────────────────────────────"
- tput cup $((row + 2)) 0; printf "%s %s\n" "$(translate "Package:")" "$package_name"
- tput cup $((row + 3)) 0; printf "%s\n" "Progress: [ ] 0%"
- tput cup $((row + 4)) 0; printf "%s\n" "──────────────────────────────────────────────"
-
- for i in $(seq 1 10); do
- sleep 0.1
- progress=$((i * 10))
- tput cup $((row + 3)) 9
- printf "[%-50s] %3d%%" "$(printf "#%.0s" $(seq 1 $((progress/2))))" "$progress"
- done
- fi
- done
-
- tput rc
- tput ed
-
- upgrade_exit_code=${PIPESTATUS[0]}
-
-
-
-
- if [ $upgrade_exit_code -eq 0 ]; then
- msg_ok "$(translate "System upgrade completed successfully")"
- else
- msg_error "$(translate "System upgrade failed. Check log: $log_file")"
- return 1
- fi
-
-
- msg_info "$(translate "Installing essential Proxmox packages...")"
- local additional_packages="zfsutils-linux proxmox-backup-restore-image chrony"
-
- if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install $additional_packages >> "$log_file" 2>&1; then
- msg_ok "$(translate "Essential Proxmox packages installed")"
- else
- msg_warn "$(translate "Some essential Proxmox packages may not have been installed")"
- fi
-
- lvm_repair_check
- cleanup_duplicate_repos
-
- #msg_info "$(translate "Performing system cleanup...")"
- apt-get -y autoremove > /dev/null 2>&1 || true
- apt-get -y autoclean > /dev/null 2>&1 || true
- msg_ok "$(translate "Cleanup finished")"
-
- local end_time=$(date +%s)
- local duration=$((end_time - start_time))
- local minutes=$((duration / 60))
- local seconds=$((duration % 60))
-
- echo -e "${TAB}${BGN}$(translate "====== PVE UPDATE COMPLETED ======")${CL}"
- echo -e "${TAB}${GN}⏱️ $(translate "Duration")${CL}: ${BL}${minutes}m ${seconds}s${CL}"
- echo -e "${TAB}${GN}📄 $(translate "Log file")${CL}: ${BL}$log_file${CL}"
- echo -e "${TAB}${GN}📦 $(translate "Packages upgraded")${CL}: ${BL}$upgradable${CL}"
- echo -e "${TAB}${GN}🖥️ $(translate "Proxmox VE")${CL}: ${BL}$target_version (Debian $OS_CODENAME)${CL}"
-
- msg_ok "$(translate "Proxmox VE 9.x configuration completed.")"
-
-}
-
-if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
- update_pve9
-fi
diff --git a/scripts/global/update-pve9_2_.sh b/scripts/global/update-pve9_2_.sh
deleted file mode 100644
index 7e1b7c76..00000000
--- a/scripts/global/update-pve9_2_.sh
+++ /dev/null
@@ -1,304 +0,0 @@
-#!/bin/bash
-# ==========================================================
-# Proxmox VE Update Script - Improved Version
-# ==========================================================
-
-# Configuration
-REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
-BASE_DIR="/usr/local/share/proxmenux"
-UTILS_FILE="$BASE_DIR/utils.sh"
-VENV_PATH="/opt/googletrans-env"
-TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
-
-if [[ -f "$UTILS_FILE" ]]; then
- source "$UTILS_FILE"
-fi
-
-load_language
-initialize_cache
-
-ensure_tools_json() {
- [ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
-}
-
-register_tool() {
- local tool="$1"
- local state="$2"
- ensure_tools_json
- jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
-}
-
-download_common_functions() {
- if ! source <(curl -s "$REPO_URL/scripts/global/common-functions.sh"); then
- return 1
- fi
-}
-
-update_pve9() {
- local pve_version=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1)
- local start_time=$(date +%s)
- local log_file="/var/log/proxmox-update-$(date +%Y%m%d-%H%M%S).log"
- local changes_made=false
- local OS_CODENAME="$(grep "VERSION_CODENAME=" /etc/os-release | cut -d"=" -f 2 | xargs)"
- local TARGET_CODENAME="trixie"
-
- local screen_capture="/tmp/proxmenux_screen_capture_$$.txt"
-
- if [ -z "$OS_CODENAME" ]; then
- OS_CODENAME=$(lsb_release -cs 2>/dev/null || echo "trixie")
- fi
-
- download_common_functions
-
- {
- msg_info2 "$(translate "Detected: Proxmox VE $pve_version (Current: $OS_CODENAME, Target: $TARGET_CODENAME)")"
- } | tee -a "$screen_capture"
-
-
- local available_space=$(df /var/cache/apt/archives | awk 'NR==2 {print int($4/1024)}')
- if [ "$available_space" -lt 1024 ]; then
- msg_error "$(translate "Insufficient disk space. Available: ${available_space}MB")"
- echo -e
- msg_success "$(translate "Press Enter to return to menu...")"
- read -r
- return 1
- fi
-
- if ! ping -c 1 download.proxmox.com >/dev/null 2>&1; then
- msg_error "$(translate "Cannot reach Proxmox repositories")"
- echo -e
- msg_success "$(translate "Press Enter to return to menu...")"
- read -r
- return 1
- fi
-
- disable_sources_repo() {
- local file="$1"
- if [[ -f "$file" ]]; then
- sed -i ':a;/^\n*$/{$d;N;ba}' "$file"
-
- if grep -q "^Enabled:" "$file"; then
- sed -i 's/^Enabled:.*$/Enabled: false/' "$file"
- else
- echo "Enabled: false" >> "$file"
- fi
-
- if ! grep -q "^Types: " "$file"; then
- msg_warn "$(translate "Malformed .sources file detected, removing: $(basename "$file")")"
- rm -f "$file"
- fi
- return 0
- fi
- return 1
- }
-
- if disable_sources_repo "/etc/apt/sources.list.d/pve-enterprise.sources"; then
- msg_ok "$(translate "Enterprise Proxmox repository disabled")" | tee -a "$screen_capture"
- changes_made=true
- fi
-
- if disable_sources_repo "/etc/apt/sources.list.d/ceph.sources"; then
- msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")" | tee -a "$screen_capture"
- changes_made=true
- fi
-
- for legacy_file in /etc/apt/sources.list.d/pve-public-repo.list \
- /etc/apt/sources.list.d/pve-install-repo.list \
- /etc/apt/sources.list.d/debian.list; do
- if [[ -f "$legacy_file" ]]; then
- rm -f "$legacy_file"
- msg_ok "$(translate "Removed legacy repository: $(basename "$legacy_file")")" | tee -a "$screen_capture"
- fi
- done
-
- if [[ -f /etc/apt/sources.list.d/debian.sources ]]; then
- rm -f /etc/apt/sources.list.d/debian.sources
- msg_ok "$(translate "Old debian.sources file removed to prevent duplication")" | tee -a "$screen_capture"
- fi
-
- msg_info "$(translate "Creating Proxmox VE 9.x no-subscription repository...")"
- cat > /etc/apt/sources.list.d/proxmox.sources << EOF
-Enabled: true
-Types: deb
-URIs: http://download.proxmox.com/debian/pve
-Suites: ${TARGET_CODENAME}
-Components: pve-no-subscription
-Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
-EOF
- msg_ok "$(translate "Proxmox VE 9.x no-subscription repository created")" | tee -a "$screen_capture"
- changes_made=true
-
- msg_info "$(translate "Creating Debian ${TARGET_CODENAME} sources file...")"
- cat > /etc/apt/sources.list.d/debian.sources << EOF
-Types: deb
-URIs: http://deb.debian.org/debian/
-Suites: ${TARGET_CODENAME} ${TARGET_CODENAME}-updates
-Components: main contrib non-free non-free-firmware
-Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
-
-Types: deb
-URIs: http://security.debian.org/debian-security/
-Suites: ${TARGET_CODENAME}-security
-Components: main contrib non-free non-free-firmware
-Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
-EOF
-
- msg_ok "$(translate "Debian repositories configured for $TARGET_CODENAME")"
-
- local firmware_conf="/etc/apt/apt.conf.d/no-firmware-warnings.conf"
- if [ ! -f "$firmware_conf" ]; then
- msg_info "$(translate "Disabling non-free firmware warnings...")"
- echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > "$firmware_conf"
- msg_ok "$(translate "Non-free firmware warnings disabled")"
- fi
-
- #update_output=$(apt-get update 2>&1)
- update_output=$(apt-get -o Dpkg::Progress-Fancy=1 update 2>&1)
- update_exit_code=$?
-
- if [ $update_exit_code -eq 0 ]; then
- msg_ok "$(translate "Package lists updated successfully")" | tee -a "$screen_capture"
- else
- if echo "$update_output" | grep -q "NO_PUBKEY\|GPG error"; then
- msg_info "$(translate "Fixing GPG key issues...")"
- apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $(echo "$update_output" | grep "NO_PUBKEY" | sed 's/.*NO_PUBKEY //' | head -1) 2>/dev/null
- if apt-get update > "$log_file" 2>&1; then
- msg_ok "$(translate "Package lists updated after GPG fix")" | tee -a "$screen_capture"
- else
- msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
- return 1
- fi
- elif echo "$update_output" | grep -q "404\|Failed to fetch"; then
- msg_warn "$(translate "Some repositories are not available, continuing with available ones...")"
- else
- msg_error "$(translate "Failed to update package lists. Check log: $log_file")"
- echo "Error details: $update_output"
- return 1
- fi
- fi
-
- if apt policy 2>/dev/null | grep -q "${TARGET_CODENAME}.*pve-no-subscription"; then
- msg_ok "$(translate "Proxmox VE 9.x repositories verified")" | tee -a "$screen_capture"
- else
- msg_warn "$(translate "Proxmox VE 9.x repositories verification inconclusive, continuing...")"
- fi
-
- local current_pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
- local available_pve_version=$(apt-cache policy pve-manager 2>/dev/null | grep -oP 'Candidate: \K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
- local upgradable=$(apt list --upgradable 2>/dev/null | grep -c "upgradable")
- local security_updates=$(apt list --upgradable 2>/dev/null | grep -c "security")
-
- show_update_menu() {
- local current_version="$1"
- local target_version="$2"
- local upgradable_count="$3"
- local security_count="$4"
-
- local menu_text="$(translate "System Update Information")\n\n"
- menu_text+="$(translate "Current PVE Version"): $current_version\n"
- if [ -n "$target_version" ] && [ "$target_version" != "$current_version" ]; then
- menu_text+="$(translate "Available PVE Version"): $target_version\n"
- fi
- menu_text+="\n$(translate "Package Updates Available"): $upgradable_count\n"
- menu_text+="$(translate "Security Updates"): $security_count\n\n"
-
- if [ "$upgradable_count" -eq 0 ]; then
- menu_text+="$(translate "System is already up to date")"
- whiptail --title "$(translate "Update Status")" --msgbox "$menu_text" 15 70
- return 2
- else
- menu_text+="$(translate "Do you want to proceed with the system update?")"
- if whiptail --title "$(translate "Proxmox Update")" --yesno "$menu_text" 18 70; then
- return 0
- else
- return 1
- fi
- fi
- }
-
- show_update_menu "$current_pve_version" "$available_pve_version" "$upgradable" "$security_updates"
- MENU_RESULT=$?
-
- clear
- show_proxmenux_logo
- msg_title "$(translate "$SCRIPT_TITLE")"
- cat "$screen_capture"
-
-
- if [[ $MENU_RESULT -eq 1 ]]; then
- msg_info2 "$(translate "Update cancelled by user")"
- apt-get -y autoremove > /dev/null 2>&1 || true
- apt-get -y autoclean > /dev/null 2>&1 || true
- rm -f "$screen_capture"
- return 0
- elif [[ $MENU_RESULT -eq 2 ]]; then
- msg_ok "$(translate "System is already up to date. No update needed.")"
- apt-get -y autoremove > /dev/null 2>&1 || true
- apt-get -y autoclean > /dev/null 2>&1 || true
- rm -f "$screen_capture"
- return 0
- fi
-
- 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 "Old time services removed successfully")"
- else
- msg_warn "$(translate "Some old time services could not be removed (not installed)")"
- fi
-
- echo -e
- DEBIAN_FRONTEND=noninteractive apt-get -y \
- -o Dpkg::Options::='--force-confdef' \
- -o Dpkg::Options::='--force-confold' \
- dist-upgrade 2>&1 | tee -a "$log_file"
-
- upgrade_exit_code=${PIPESTATUS[0]}
- echo -e
-
- clear
- show_proxmenux_logo
- msg_title "$(translate "$SCRIPT_TITLE")"
- cat "$screen_capture"
-
-
- if [ $upgrade_exit_code -ne 0 ]; then
- msg_error "$(translate "System upgrade failed. Check log: $log_file")"
- rm -f "$screen_capture"
- return 1
- fi
-
- msg_info "$(translate "Installing essential Proxmox packages...")"
- local additional_packages="zfsutils-linux proxmox-backup-restore-image chrony"
-
- if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install $additional_packages >> "$log_file" 2>&1; then
- msg_ok "$(translate "Essential Proxmox packages installed")"
- else
- msg_warn "$(translate "Some essential Proxmox packages may not have been installed")"
- fi
-
- lvm_repair_check
- cleanup_duplicate_repos
-
- apt-get -y autoremove > /dev/null 2>&1 || true
- apt-get -y autoclean > /dev/null 2>&1 || true
- msg_ok "$(translate "Cleanup finished")"
-
- local end_time=$(date +%s)
- local duration=$((end_time - start_time))
- local minutes=$((duration / 60))
- local seconds=$((duration % 60))
-
- echo -e "${TAB}${BGN}$(translate "====== PVE UPDATE COMPLETED ======")${CL}"
- echo -e "${TAB}${GN}⏱️ $(translate "Duration")${CL}: ${BL}${minutes}m ${seconds}s${CL}"
- echo -e "${TAB}${GN}📄 $(translate "Log file")${CL}: ${BL}$log_file${CL}"
- echo -e "${TAB}${GN}📦 $(translate "Packages upgraded")${CL}: ${BL}$upgradable${CL}"
- echo -e "${TAB}${GN}🖥️ $(translate "Proxmox VE")${CL}: ${BL}$available_pve_version (Debian $OS_CODENAME)${CL}"
-
- msg_ok "$(translate "Proxmox VE 9.x configuration completed.")"
-
- rm -f "$screen_capture"
-}
-
-if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
- update_pve9
-fi
diff --git a/scripts/configure_igpu_lxc.sh b/scripts/gpu_tpu/configure_igpu_lxc.sh
similarity index 100%
rename from scripts/configure_igpu_lxc.sh
rename to scripts/gpu_tpu/configure_igpu_lxc.sh
diff --git a/scripts/gpu_tpu/nvidia_installer_.sh b/scripts/gpu_tpu/nvidia_installer_.sh
deleted file mode 100644
index 691e897d..00000000
--- a/scripts/gpu_tpu/nvidia_installer_.sh
+++ /dev/null
@@ -1,916 +0,0 @@
-#!/bin/bash
-# ProxMenux - NVIDIA Driver Installer (PVE 9.x)
-# ============================================
-# Author : MacRimi
-# License : MIT
-# Version : 0.9 (PVE9, fixed download issues)
-# Last Updated: 29/11/2025
-# ============================================
-
-SCRIPT_TITLE="NVIDIA GPU Driver Installer for Proxmox VE"
-
-LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
-BASE_DIR="/usr/local/share/proxmenux"
-UTILS_FILE="$BASE_DIR/utils.sh"
-COMPONENTS_STATUS_FILE="$BASE_DIR/components_status.json"
-LOG_FILE="/tmp/nvidia_install.log"
-screen_capture="/tmp/proxmenux_nvidia_screen_capture_$$.txt"
-
-NVIDIA_BASE_URL="https://download.nvidia.com/XFree86/Linux-x86_64"
-NVIDIA_WORKDIR="/opt/nvidia"
-
-export BASE_DIR
-export COMPONENTS_STATUS_FILE
-
-if [[ -f "$UTILS_FILE" ]]; then
- source "$UTILS_FILE"
-fi
-
-if [[ ! -f "$COMPONENTS_STATUS_FILE" ]]; then
- echo "{}" > "$COMPONENTS_STATUS_FILE"
-fi
-
-load_language
-initialize_cache
-
-# ==========================================================
-# GPU detection and current status
-# ==========================================================
-detect_nvidia_gpus() {
- # Only video controllers (not audio)
- local lspci_output
- lspci_output=$(lspci | grep -i "NVIDIA" \
- | grep -Ei "VGA compatible controller|3D controller|Display controller" || true)
-
- if [[ -z "$lspci_output" ]]; then
- NVIDIA_GPU_PRESENT=false
- DETECTED_GPUS_TEXT="$(translate 'No NVIDIA GPU detected on this system.')"
- else
- NVIDIA_GPU_PRESENT=true
- DETECTED_GPUS_TEXT=""
- local i=1
- while IFS= read -r line; do
- DETECTED_GPUS_TEXT+=" ${i}. ${line}\n"
- ((i++))
- done <<< "$lspci_output"
- fi
-}
-
-detect_driver_status() {
- CURRENT_DRIVER_INSTALLED=false
- CURRENT_DRIVER_VERSION=""
-
- # First check if nvidia kernel module is actually loaded
- if lsmod | grep -q "^nvidia "; then
-
- modprobe nvidia-uvm 2>/dev/null || true
- sleep 1
-
-
- if command -v nvidia-smi >/dev/null 2>&1; then
- CURRENT_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1)
-
- if [[ -n "$CURRENT_DRIVER_VERSION" ]]; then
- CURRENT_DRIVER_INSTALLED=true
- # Register the installed driver version in components_status.json
- update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":false}'
- fi
- fi
- fi
-
- if $CURRENT_DRIVER_INSTALLED; then
- CURRENT_STATUS_TEXT="$(printf '%s %s' "$(translate 'NVIDIA driver installed:')" "$CURRENT_DRIVER_VERSION")"
- else
- CURRENT_STATUS_TEXT="$(translate 'No NVIDIA driver installed.')"
- fi
-
- if $CURRENT_DRIVER_INSTALLED; then
- CURRENT_STATUS_COLORED="\Z2${CURRENT_STATUS_TEXT}\Zn"
- else
- CURRENT_STATUS_COLORED="\Z3${CURRENT_STATUS_TEXT}\Zn"
- fi
-}
-
-# ==========================================================
-# System preparation (repos, headers, etc.)
-# ==========================================================
-ensure_repos_and_headers() {
- msg_info "$(translate 'Checking kernel headers and build tools...')"
-
- local kver
- kver=$(uname -r)
-
- apt-get update -qq >>"$LOG_FILE" 2>&1
-
- if ! dpkg -s "pve-headers-$kver" >/dev/null 2>&1 && \
- ! dpkg -s "proxmox-headers-$kver" >/dev/null 2>&1; then
- apt-get install -y "pve-headers-$kver" "proxmox-headers-$kver" build-essential dkms >>"$LOG_FILE" 2>&1 || true
- else
- apt-get install -y build-essential dkms >>"$LOG_FILE" 2>&1 || true
- fi
-
- msg_ok "$(translate 'Kernel headers and build tools verified.')" | tee -a "$screen_capture"
-}
-
-blacklist_nouveau() {
- msg_info "$(translate 'Blacklisting nouveau driver...')"
- if ! grep -q '^blacklist nouveau' /etc/modprobe.d/blacklist.conf 2>/dev/null; then
- echo "blacklist nouveau" >> /etc/modprobe.d/blacklist.conf
- fi
- msg_ok "$(translate 'nouveau driver has been blacklisted.')" | tee -a "$screen_capture"
-}
-
-ensure_modules_config() {
- msg_info "$(translate 'Configuring NVIDIA and VFIO modules...')"
- cat > /etc/modules-load.d/nvidia-vfio.conf <<'EOF'
-vfio
-vfio_iommu_type1
-vfio_pci
-vfio_virqfd
-nvidia
-nvidia_uvm
-EOF
- msg_ok "$(translate 'Modules configuration updated.')" | tee -a "$screen_capture"
-}
-
-stop_and_disable_nvidia_services() {
- local services=(
- "nvidia-persistenced.service"
- "nvidia-persistenced"
- "nvidia-powerd.service"
- )
-
- local services_detected=0
-
- for service in "${services[@]}"; do
- if systemctl is-active --quiet "$service" 2>/dev/null || \
- systemctl is-enabled --quiet "$service" 2>/dev/null; then
- services_detected=1
- break
- fi
- done
-
- if [ "$services_detected" -eq 1 ]; then
- msg_info "$(translate 'Stopping and disabling NVIDIA services...')"
-
- for service in "${services[@]}"; do
- if systemctl is-active --quiet "$service" 2>/dev/null; then
- systemctl stop "$service" >/dev/null 2>&1 || true
- fi
- if systemctl is-enabled --quiet "$service" 2>/dev/null; then
- systemctl disable "$service" >/dev/null 2>&1 || true
- fi
- done
-
- sleep 2
-
- msg_ok "$(translate 'NVIDIA services stopped and disabled.')" | tee -a "$screen_capture"
- fi
-}
-
-unload_nvidia_modules() {
- msg_info "$(translate 'Unloading NVIDIA kernel modules...')"
-
- for mod in nvidia_uvm nvidia_drm nvidia_modeset nvidia; do
- modprobe -r "$mod" >/dev/null 2>&1 || true
- done
-
-
- if lsmod | grep -qi '\bnvidia'; then
- for mod in nvidia_uvm nvidia_drm nvidia_modeset nvidia; do
- modprobe -r --force "$mod" >/dev/null 2>&1 || true
- done
- fi
-
- if lsmod | grep -qi '\bnvidia'; then
- msg_warn "$(translate 'Some NVIDIA modules could not be unloaded. Installation may fail. Ensure no processes are using the GPU.')"
- if command -v lsof >/dev/null 2>&1; then
- echo "$(translate 'Processes using NVIDIA:'):" >> "$LOG_FILE"
- lsof /dev/nvidia* 2>/dev/null >> "$LOG_FILE" || true
- fi
- else
- msg_ok "$(translate 'NVIDIA kernel modules unloaded successfully.')" | tee -a "$screen_capture"
- fi
-}
-
-complete_nvidia_uninstall() {
- stop_and_disable_nvidia_services
- unload_nvidia_modules
-
- if command -v nvidia-uninstall >/dev/null 2>&1; then
- msg_info "$(translate 'Running NVIDIA uninstaller...')"
- nvidia-uninstall --silent >>"$LOG_FILE" 2>&1 || true
- msg_ok "$(translate 'NVIDIA uninstaller completed.')"
- fi
-
- cleanup_nvidia_dkms
-
- msg_info "$(translate 'Removing NVIDIA packages...')"
- apt-get -y purge 'nvidia-*' 'libnvidia-*' 'cuda-*' 'libcudnn*' >>"$LOG_FILE" 2>&1 || true
- apt-get -y autoremove --purge >>"$LOG_FILE" 2>&1 || true
- apt-get -y autoclean >>"$LOG_FILE" 2>&1 || true
-
- rm -f /etc/modules-load.d/nvidia-vfio.conf
- rm -f /etc/udev/rules.d/70-nvidia.rules
- rm -rf /usr/lib/modprobe.d/nvidia*.conf
- rm -rf /etc/modprobe.d/nvidia*.conf
-
- if [[ -d "$NVIDIA_WORKDIR" ]]; then
- find "$NVIDIA_WORKDIR" -type d -name "nvidia-persistenced" -exec rm -rf {} + 2>/dev/null || true
- find "$NVIDIA_WORKDIR" -type d -name "nvidia-patch" -exec rm -rf {} + 2>/dev/null || true
- fi
-
- update_component_status "nvidia_driver" "removed" "" "gpu" '{}'
-
- msg_ok "$(translate 'Complete NVIDIA uninstallation finished.')" | tee -a "$screen_capture"
-}
-
-cleanup_nvidia_dkms() {
- local versions
- versions=$(dkms status 2>/dev/null | awk -F, '/nvidia/ {gsub(/ /,"",$2); print $2}' || true)
-
- [[ -z "$versions" ]] && return 0
-
- msg_info "$(translate 'Removing NVIDIA DKMS entries...')"
- while IFS= read -r ver; do
- [[ -z "$ver" ]] && continue
- dkms remove -m nvidia -v "$ver" --all >/dev/null 2>&1 || true
- done <<< "$versions"
- msg_ok "$(translate 'NVIDIA DKMS entries removed.')"
-}
-
-ensure_workdir() {
- mkdir -p "$NVIDIA_WORKDIR"
-}
-
-# ==========================================================
-# Kernel compatibility detection
-# ==========================================================
-get_kernel_compatibility_info() {
- local kernel_version
- kernel_version=$(uname -r)
-
- # Determine Proxmox and kernel version
- if [[ -f /etc/pve/.version ]]; then
- PVE_VERSION=$(cat /etc/pve/.version)
- else
- PVE_VERSION="unknown"
- fi
-
- # Extract kernel major version (6.x, 5.x, etc)
- KERNEL_MAJOR=$(echo "$kernel_version" | cut -d. -f1)
- KERNEL_MINOR=$(echo "$kernel_version" | cut -d. -f2)
-
- # Define minimum compatible versions based on kernel
- # Based on https://docs.nvidia.com/datacenter/tesla/drivers/index.html
- if [[ "$KERNEL_MAJOR" -ge 6 ]] && [[ "$KERNEL_MINOR" -ge 17 ]]; then
- # Kernel 6.17+ (Proxmox 9.x) - Requires 580.82.07 or higher
- MIN_DRIVER_VERSION="580.82.07"
- RECOMMENDED_BRANCH="580"
- COMPATIBILITY_NOTE="Kernel $kernel_version requires NVIDIA driver 580.82.07 or newer"
- elif [[ "$KERNEL_MAJOR" -ge 6 ]] && [[ "$KERNEL_MINOR" -ge 8 ]]; then
- # Kernel 6.8-6.16 (Proxmox 8.2+) - Works with 550.x or higher
- MIN_DRIVER_VERSION="550"
- RECOMMENDED_BRANCH="580"
- COMPATIBILITY_NOTE="Kernel $kernel_version works best with NVIDIA driver 550.x or newer"
- elif [[ "$KERNEL_MAJOR" -ge 6 ]]; then
- # Kernel 6.2-6.7 (Proxmox 8.x initial) - Works with 535.x or higher
- MIN_DRIVER_VERSION="535"
- RECOMMENDED_BRANCH="550"
- COMPATIBILITY_NOTE="Kernel $kernel_version works with NVIDIA driver 535.x or newer"
- elif [[ "$KERNEL_MAJOR" -eq 5 ]] && [[ "$KERNEL_MINOR" -ge 15 ]]; then
- # Kernel 5.15+ (Proxmox 7.x, 8.x legacy) - Works with 470.x or higher
- MIN_DRIVER_VERSION="470"
- RECOMMENDED_BRANCH="535"
- COMPATIBILITY_NOTE="Kernel $kernel_version works with NVIDIA driver 470.x or newer"
- else
- # Old kernels
- MIN_DRIVER_VERSION="450"
- RECOMMENDED_BRANCH="470"
- COMPATIBILITY_NOTE="For older kernels, compatibility may vary"
- fi
-}
-
-is_version_compatible() {
- local version="$1"
- local ver_major ver_minor ver_patch
-
- # Extract version components (major.minor.patch)
- ver_major=$(echo "$version" | cut -d. -f1)
- ver_minor=$(echo "$version" | cut -d. -f2)
- ver_patch=$(echo "$version" | cut -d. -f3)
-
- if [[ "$MIN_DRIVER_VERSION" == "580.82.07" ]]; then
- # Compare full version: must be >= 580.82.07
- if [[ ${ver_major} -gt 580 ]]; then
- return 0
- elif [[ ${ver_major} -eq 580 ]]; then
- if [[ $((10#${ver_minor})) -gt 82 ]]; then
- return 0
- elif [[ $((10#${ver_minor})) -eq 82 ]]; then
- if [[ $((10#${ver_patch:-0})) -ge 7 ]]; then
- return 0
- fi
- fi
- fi
- return 1
- fi
-
-
- if [[ ${ver_major} -ge ${MIN_DRIVER_VERSION} ]]; then
- return 0
- else
- return 1
- fi
-}
-
-# ==========================================================
-# NVIDIA version management - FIXED VERSION
-# ==========================================================
-download_latest_version() {
- local latest_line version
-
- latest_line=$(curl -fsSL "${NVIDIA_BASE_URL}/latest.txt" 2>&1)
- if [[ -z "$latest_line" ]]; then
- echo "" >&2
- return 1
- fi
-
- version=$(echo "$latest_line" | awk '{print $1}' | tr -d '[:space:]')
-
- if [[ -z "$version" ]]; then
- echo "" >&2
- return 1
- fi
-
- if [[ ! "$version" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
- echo "" >&2
- return 1
- fi
-
- echo "$version"
- return 0
-}
-
-list_available_versions() {
- local html_content versions
-
- html_content=$(curl -s "$NVIDIA_BASE_URL/" 2>&1)
-
- if [[ -z "$html_content" ]]; then
- echo "" >&2
- return 1
- fi
-
- versions=$(echo "$html_content" \
- | grep -o 'href=[^ >]*' \
- | awk -F"'" '{print $2}' \
- | grep -E '^[0-9]' \
- | sed 's/\/$//' \
- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' \
- | sort -Vr \
- | uniq)
-
- if [[ -z "$versions" ]]; then
- echo "" >&2
- return 1
- fi
-
- echo "$versions"
- return 0
-}
-
-verify_version_exists() {
- local version="$1"
- local url="${NVIDIA_BASE_URL}/${version}/"
-
- if curl -fsSL --head "$url" >/dev/null 2>&1; then
- return 0
- else
- return 1
- fi
-}
-
-download_nvidia_installer() {
- ensure_workdir
- local version="$1"
-
- version=$(echo "$version" | tr -d '[:space:]' | tr -d '\n' | tr -d '\r')
-
- if [[ ! "$version" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
- msg_error "Invalid version format: $version" >&2
- echo "ERROR: Invalid version format: '$version'" >> "$LOG_FILE"
- return 1
- fi
-
- local run_file="$NVIDIA_WORKDIR/NVIDIA-Linux-x86_64-${version}.run"
-
- if [[ -f "$run_file" ]]; then
- echo "Found existing file: $run_file" >> "$LOG_FILE"
- local existing_size file_type
- existing_size=$(stat -c%s "$run_file" 2>/dev/null || stat -f%z "$run_file" 2>/dev/null || echo "0")
- file_type=$(file "$run_file" 2>/dev/null || echo "unknown")
-
- echo "Existing file size: $existing_size bytes" >> "$LOG_FILE"
- echo "Existing file type: $file_type" >> "$LOG_FILE"
-
-
- if [[ $existing_size -gt 40000000 ]] && echo "$file_type" | grep -q "executable"; then
-
- if sh "$run_file" --check 2>&1 | tee -a "$LOG_FILE" | grep -q "OK"; then
- echo "Existing file passed integrity check" >> "$LOG_FILE"
- msg_ok "$(translate 'Installer already downloaded and verified.')" >&2
- printf '%s\n' "$run_file"
- return 0
- else
- echo "Existing file FAILED integrity check, removing..." >> "$LOG_FILE"
- msg_warn "$(translate 'Existing file failed verification, re-downloading...')" >&2
- rm -f "$run_file"
- fi
- else
- echo "Existing file invalid (size or type), removing..." >> "$LOG_FILE"
- msg_warn "$(translate 'Removing invalid existing file...')" >&2
- rm -f "$run_file"
- fi
- fi
-
- if ! verify_version_exists "$version"; then
- msg_error "Version $version does not exist on NVIDIA servers" >&2
- echo "ERROR: Version $version not found on server" >> "$LOG_FILE"
- return 1
- fi
-
- local urls=(
- "${NVIDIA_BASE_URL}/${version}/NVIDIA-Linux-x86_64-${version}.run"
- "${NVIDIA_BASE_URL}/${version}/NVIDIA-Linux-x86_64-${version}-no-compat32.run"
- )
-
- local success=false
- local url_index=0
-
- for url in "${urls[@]}"; do
- ((url_index++))
- echo "Attempting download from: $url" >> "$LOG_FILE"
-
-
- rm -f "$run_file"
-
-
- if curl -fL --connect-timeout 30 --max-time 600 "$url" -o "$run_file" >> "$LOG_FILE" 2>&1; then
- echo "Download completed, verifying file..." >> "$LOG_FILE"
-
-
- if [[ ! -f "$run_file" ]]; then
- echo "ERROR: File not created after download" >> "$LOG_FILE"
- continue
- fi
-
-
- local file_size
- file_size=$(stat -c%s "$run_file" 2>/dev/null || stat -f%z "$run_file" 2>/dev/null || echo "0")
- echo "Downloaded file size: $file_size bytes" >> "$LOG_FILE"
-
- if [[ $file_size -lt 40000000 ]]; then
- echo "ERROR: File too small ($file_size bytes, expected >40MB)" >> "$LOG_FILE"
- head -c 200 "$run_file" >> "$LOG_FILE" 2>&1
- rm -f "$run_file"
- continue
- fi
-
-
- local file_type
- file_type=$(file "$run_file" 2>/dev/null)
- echo "File type: $file_type" >> "$LOG_FILE"
-
- if echo "$file_type" | grep -q "executable"; then
- echo "SUCCESS: Valid executable downloaded" >> "$LOG_FILE"
- success=true
- break
- else
- echo "ERROR: Not a valid executable" >> "$LOG_FILE"
- head -c 200 "$run_file" | od -c >> "$LOG_FILE" 2>&1
- rm -f "$run_file"
- fi
- else
- echo "ERROR: curl failed for $url (exit code: $?)" >> "$LOG_FILE"
- rm -f "$run_file"
- fi
- done
-
- if ! $success; then
- msg_error "$(translate 'Download failed for all attempted URLs')" >&2
- msg_error "Version $version may not be available for your architecture" >&2
- echo "ERROR: All download attempts failed" >> "$LOG_FILE"
- return 1
- fi
-
- chmod +x "$run_file"
- echo "Installation file ready: $run_file" >> "$LOG_FILE"
- printf '%s\n' "$run_file"
-}
-
-# ==========================================================
-# Installation / uninstallation
-# ==========================================================
-run_nvidia_installer() {
- local installer="$1"
-
- msg_info2 "$(translate 'Starting NVIDIA installer. This may take several minutes...')"
- echo "" >>"$LOG_FILE"
- echo "=== Running NVIDIA installer: $installer ===" >>"$LOG_FILE"
-
- local tmp_extract_dir="$NVIDIA_WORKDIR/tmp_extract"
- mkdir -p "$tmp_extract_dir"
-
- sh "$installer" --tmpdir="$tmp_extract_dir" --no-questions --ui=none --disable-nouveau --dkms 2>&1 | tee -a "$LOG_FILE"
- local rc=${PIPESTATUS[0]}
- echo "" >>"$LOG_FILE"
-
- rm -rf "$tmp_extract_dir"
-
- if [[ $rc -ne 0 ]]; then
- msg_error "$(translate 'NVIDIA installer reported an error. Check /tmp/nvidia_install.log')"
- update_component_status "nvidia_driver" "failed" "" "gpu" '{"patched":false}'
- return 1
- fi
-
- msg_ok "$(translate 'NVIDIA driver installed successfully.')" | tee -a "$screen_capture"
- return 0
-}
-
-remove_nvidia_driver() {
- complete_nvidia_uninstall
-}
-
-install_udev_rules_and_persistenced() {
- msg_info "$(translate 'Installing NVIDIA udev rules and persistence service...')"
-
- cat >/etc/udev/rules.d/70-nvidia.rules <<'EOF'
-# /etc/udev/rules.d/70-nvidia.rules
-KERNEL=="nvidia", RUN+="/bin/bash -c '/usr/bin/nvidia-smi -L'"
-KERNEL=="nvidia_uvm", RUN+="/bin/bash -c '/usr/bin/nvidia-modprobe -c0 -u'"
-EOF
-
- udevadm control --reload-rules
- udevadm trigger --subsystem-match=drm --subsystem-match=pci || true
-
- ensure_workdir
- cd "$NVIDIA_WORKDIR" || return 1
- if [[ ! -d nvidia-persistenced ]]; then
- git clone https://github.com/NVIDIA/nvidia-persistenced.git >>"$LOG_FILE" 2>&1 || true
- fi
-
- if [[ -d nvidia-persistenced/init ]]; then
- cd nvidia-persistenced/init || return 1
- ./install.sh >>"$LOG_FILE" 2>&1 || true
- fi
-
- msg_ok "$(translate 'NVIDIA udev rules and persistence service installed.')" | tee -a "$screen_capture"
-}
-
-apply_nvidia_patch_if_needed() {
- if ! whiptail --title "$(translate 'NVIDIA Patch')" --yesno \
- "\n$(translate 'Do you want to apply the optional NVIDIA patch to remove some GPU limitations?')" 10 70; then
- msg_info2 "$(translate 'NVIDIA patch not applied.')"
- update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":false}'
- return 0
- fi
-
- msg_info "$(translate 'Cloning and applying NVIDIA patch (keylase/nvidia-patch)...')"
- ensure_workdir
- cd "$NVIDIA_WORKDIR" || return 1
- if [[ ! -d nvidia-patch ]]; then
- git clone https://github.com/keylase/nvidia-patch.git >>"$LOG_FILE" 2>&1 || true
- fi
-
- if [[ -x nvidia-patch/patch.sh ]]; then
- cd nvidia-patch || return 1
- ./patch.sh >>"$LOG_FILE" 2>&1 || true
- msg_ok "$(translate 'NVIDIA patch applied - check README for supported versions.')"
- update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":true}'
- else
- msg_warn "$(translate 'Could not run NVIDIA patch script. Please verify repository and driver version.')"
- update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":false}'
- fi
-}
-
-restart_prompt() {
- if whiptail --title "$(translate 'NVIDIA Drivers')" --yesno \
- "\n$(translate 'The installation/changes require a server restart to apply correctly. Do you want to reboot now?')" 10 70; then
- msg_success "$(translate 'Installation completed. Press Enter to continue...')"
- read -r
- msg_warn "$(translate 'Restarting the server...')"
- rm -f "$screen_capture"
- reboot
- else
- msg_success "$(translate 'Installation completed. Please reboot the server manually as soon as possible.')"
- msg_success "$(translate 'Completed. Press Enter to return to menu...')"
- read -r
- rm -f "$screen_capture"
- fi
-}
-
-# ==========================================================
-# Dialog menus
-# ==========================================================
-show_action_menu_if_installed() {
- if ! $CURRENT_DRIVER_INSTALLED; then
- ACTION="install"
- return 0
- fi
-
- local menu_choices=(
- "install" "$(translate 'Reinstall/Update NVIDIA drivers')"
- "remove" "$(translate 'Uninstall NVIDIA drivers and configuration')"
- )
-
- ACTION=$(dialog --clear --stdout \
- --backtitle "ProxMenux" \
- --title "$(translate 'NVIDIA GPU Driver Management')" \
- --menu "$(translate 'Choose an action:')" 14 80 8 \
- "${menu_choices[@]}") || ACTION="cancel"
-}
-
-show_install_overview() {
- local overview
- overview="\n$(translate 'This installation will:')\n\n"
- overview+=" • $(translate 'Install NVIDIA proprietary drivers')\n"
- overview+=" • $(translate 'Configure GPU passthrough with VFIO')\n"
- overview+=" • $(translate 'Blacklist nouveau driver')\n"
- overview+=" • $(translate 'Enable IOMMU support if not enabled')\n\n"
-
- overview+="$(translate 'Detected GPU(s):')\n"
- overview+="\Zb\Z4$DETECTED_GPUS_TEXT\Zn\n"
-
- overview+="\n\Zn$(translate 'Current status: ') "
- overview+="\Zb${CURRENT_STATUS_TEXT}\Zn\n\n"
-
- overview+="$(translate 'After confirming, you will be asked to choose the NVIDIA driver version to install.')\n\n"
- overview+="$(translate 'Do you want to continue?')"
-
- dialog --colors --backtitle "ProxMenux" \
- --title "$(translate 'NVIDIA GPU Driver Installation')" \
- --yesno "$overview" 22 90
-}
-
-show_version_menu() {
- local latest versions_list
- local kernel_version
- kernel_version=$(uname -r)
-
-
- latest=$(download_latest_version 2>/dev/null)
-
-
- versions_list=$(list_available_versions 2>/dev/null)
-
-
- if [[ -z "$latest" ]] && [[ -z "$versions_list" ]]; then
- dialog --backtitle "ProxMenux" --title "$(translate 'Error')" --msgbox \
- "$(translate 'Could not retrieve versions list from NVIDIA. Please check your internet connection.')\n\nURL: ${NVIDIA_BASE_URL}" 10 80
- DRIVER_VERSION="cancel"
- return 1
- fi
-
-
- if [[ -z "$latest" ]] && [[ -n "$versions_list" ]]; then
- latest=$(echo "$versions_list" | head -n1)
- fi
-
-
- if [[ -n "$latest" ]] && [[ -z "$versions_list" ]]; then
- versions_list="$latest"
- fi
-
- # Clean latest version
- latest=$(echo "$latest" | tr -d '[:space:]')
-
- local filter=""
- local selection
- local choices
- local current_list
- local menu_text
-
- while true; do
- current_list="$versions_list"
-
- if [[ -n "$MIN_DRIVER_VERSION" ]]; then
- local filtered_list=""
- while IFS= read -r ver; do
- [[ -z "$ver" ]] && continue
- if is_version_compatible "$ver"; then
- filtered_list+="$ver"$'\n'
- fi
- done <<< "$current_list"
- current_list="$filtered_list"
- fi
-
-
- if [[ -n "$filter" ]]; then
- current_list=$(echo "$current_list" | grep "$filter" || true)
- fi
-
- menu_text="$(translate 'Select the NVIDIA driver version to install:')\n\n"
- menu_text+="$(translate 'Use the filter entry to narrow the list. Latest available (recommended in most cases), or choose a specific version from the list.')"
-
- choices=()
- choices+=("latest" "$(translate 'Latest available') (${latest:-unknown})")
- choices+=("" "")
- choices+=("filter" "$(translate 'Filter versions')${filter:+: $filter}")
-
-
- if [[ -n "$current_list" ]]; then
- while IFS= read -r ver; do
- [[ -z "$ver" ]] && continue
- ver=$(echo "$ver" | tr -d '[:space:]')
- [[ -z "$ver" ]] && continue
-
- choices+=("$ver" "$ver")
- done <<< "$current_list"
- else
- choices+=("" "$(translate 'No versions match the current filter')")
- fi
-
- selection=$(dialog --clear --stdout \
- --backtitle "ProxMenux" \
- --title "$(translate 'NVIDIA Driver Version')" \
- --menu "$menu_text" 26 90 16 \
- "${choices[@]}") || { DRIVER_VERSION="cancel"; return 1; }
-
- case "$selection" in
- "")
- continue
- ;;
- filter)
- filter=$(dialog --clear --stdout \
- --backtitle "ProxMenux" \
- --title "$(translate 'Filter NVIDIA versions')" \
- --inputbox "$(translate 'Enter a filter (e.g., 560, 570, 580). Leave empty to show all.')" 10 80 "$filter") || true
- ;;
- latest)
- DRIVER_VERSION="$latest"
- DRIVER_VERSION=$(echo "$DRIVER_VERSION" | tr -d '[:space:]')
- return 0
- ;;
- *)
- DRIVER_VERSION="$selection"
- DRIVER_VERSION=$(echo "$DRIVER_VERSION" | tr -d '[:space:]')
- return 0
- ;;
- esac
- done
-}
-
-# ==========================================================
-# Main flow
-# ==========================================================
-main() {
- : >"$LOG_FILE"
- : >"$screen_capture"
-
- detect_nvidia_gpus
- detect_driver_status
-
- if ! $NVIDIA_GPU_PRESENT; then
- dialog --backtitle "ProxMenux" --title "$(translate 'NVIDIA GPU Driver Installation')" --msgbox \
- "\n$(translate 'No NVIDIA GPU has been detected on this system. The installer will now exit.')" 20 70
- exit 1
- fi
-
- show_action_menu_if_installed
-
- case "$ACTION" in
- install)
- if ! show_install_overview; then
- exit 0
- fi
-
- get_kernel_compatibility_info
-
- show_version_menu
- if [[ "$DRIVER_VERSION" == "cancel" || -z "$DRIVER_VERSION" ]]; then
- exit 0
- fi
-
- if $CURRENT_DRIVER_INSTALLED; then
- if [[ "$CURRENT_DRIVER_VERSION" == "$DRIVER_VERSION" ]]; then
- if ! dialog --colors --backtitle "ProxMenux" --title "$(translate 'Same Version Detected')" --yesno \
- "$(printf '\n\n\n%s \Zb%s\Zn\n\n%s' \
- "$(translate 'Version')" "$DRIVER_VERSION" \
- "$(translate 'is already installed. Do you want to reinstall it? This will perform a clean uninstall first.')")" 14 70; then
- exit 0
- fi
- else
- if ! dialog --colors --backtitle "ProxMenux" --title "$(translate 'Version Change Detected')" --yesno \
- "$(printf '\n\n%s \Zb%s\Zn\n%s \Zb\Z4%s\Zn\n\n%s' \
- "$(translate 'Current version:')" "$CURRENT_DRIVER_VERSION" \
- "$(translate 'New version:')" "$DRIVER_VERSION" \
- "$(translate 'The current driver will be completely uninstalled before installing the new version. Continue?')")" 20 70; then
- exit 0
- fi
- fi
-
- show_proxmenux_logo
- msg_title "$(translate "$SCRIPT_TITLE")"
- msg_info2 "$(translate 'Uninstalling current NVIDIA driver before installing new version...')"
- complete_nvidia_uninstall
-
- sleep 2
-
- CURRENT_DRIVER_INSTALLED=false
- CURRENT_DRIVER_VERSION=""
- fi
-
- show_proxmenux_logo
- msg_title "$(translate "$SCRIPT_TITLE")"
-
- ensure_repos_and_headers
- blacklist_nouveau
- ensure_modules_config
-
- stop_and_disable_nvidia_services
- unload_nvidia_modules
-
- msg_info "$(translate 'Downloading NVIDIA driver version:') $DRIVER_VERSION"
-
- local installer
- installer=$(download_nvidia_installer "$DRIVER_VERSION" 2>>"$LOG_FILE")
- local download_result=$?
-
- if [[ $download_result -ne 0 ]]; then
- msg_error "$(translate 'Failed to download NVIDIA installer')"
- exit 1
- fi
-
- msg_ok "$(translate 'NVIDIA installer downloaded successfully')"
-
- if [[ -z "$installer" || ! -f "$installer" ]]; then
- msg_error "$(translate 'Internal error: NVIDIA installer path is empty or file not found.')"
- rm -f "$screen_capture"
- exit 1
- fi
-
- if ! run_nvidia_installer "$installer"; then
- rm -f "$screen_capture"
- exit 1
- fi
-
- sleep 2
- show_proxmenux_logo
- msg_title "$(translate "$SCRIPT_TITLE")"
- cat "$screen_capture"
- echo -e "${TAB}${GN}📄 $(translate "Log file")${CL}: ${BL}$LOG_FILE${CL}"
-
- install_udev_rules_and_persistenced
-
- msg_info "$(translate 'Updating initramfs for all kernels...')"
- update-initramfs -u -k all >>"$LOG_FILE" 2>&1 || true
- msg_ok "$(translate 'initramfs updated.')"
-
- msg_info2 "$(translate 'Checking NVIDIA driver status with nvidia-smi')"
- if command -v nvidia-smi >/dev/null 2>&1; then
- nvidia-smi || true
- CURRENT_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1)
- CURRENT_DRIVER_INSTALLED=true
- else
- msg_warn "$(translate 'nvidia-smi not found in PATH. Please verify the driver installation.')"
- fi
-
- if [[ -n "$CURRENT_DRIVER_VERSION" ]]; then
- msg_ok "$(translate 'NVIDIA driver') $CURRENT_DRIVER_VERSION $(translate 'installed successfully.')"
- update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":false}'
- msg_success "$(translate 'Driver installed successfully. Press Enter to continue...')"
- read -r
- else
- msg_error "$(translate 'Failed to detect installed NVIDIA driver version.')"
- update_component_status "nvidia_driver" "failed" "" "gpu" '{"patched":false}'
- fi
-
- apply_nvidia_patch_if_needed
- restart_prompt
- ;;
- remove)
- if dialog --backtitle "ProxMenux" --title "$(translate 'NVIDIA Driver Uninstall')" --yesno \
- "\n\n\n$(translate 'This will remove NVIDIA drivers and related configuration. Do you want to continue?')" 14 70; then
-
- show_proxmenux_logo
- msg_title "$(translate "$SCRIPT_TITLE")"
-
- remove_nvidia_driver
-
- msg_info "$(translate 'Updating initramfs for all kernels...')"
- update-initramfs -u -k all >>"$LOG_FILE" 2>&1 || true
- msg_ok "$(translate 'initramfs updated.')"
-
- restart_prompt
- fi
- ;;
- cancel|*)
- exit 0
- ;;
- esac
-}
-
-if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
- main
-fi
diff --git a/scripts/import-disk-image.sh b/scripts/import-disk-image.sh
deleted file mode 100644
index fc73db5d..00000000
--- a/scripts/import-disk-image.sh
+++ /dev/null
@@ -1,194 +0,0 @@
-#!/bin/bash
-
-# ==========================================================
-# ProxMenux - A menu-driven script for Proxmox VE management
-# ==========================================================
-# Author : MacRimi
-# Copyright : (c) 2024 MacRimi
-# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
-# Version : 1.1
-# Last Updated: 29/05/2025
-# ==========================================================
-# Description:
-# This script automates the process of importing disk images into Proxmox VE virtual machines (VMs),
-# making it easy to attach pre-existing disk files without manual configuration.
-#
-# Before running the script, ensure that disk images are available in /var/lib/vz/template/images/.
-# The script scans this directory for compatible formats (.img, .qcow2, .vmdk, .raw) and lists the available files.
-#
-# Using an interactive menu, you can:
-# - Select a VM to attach the imported disk.
-# - Choose one or multiple disk images for import.
-# - Pick a storage volume in Proxmox for disk placement.
-# - Assign a suitable interface (SATA, SCSI, VirtIO, or IDE).
-# - Enable optional settings like SSD emulation or bootable disk configuration.
-#
-# Once completed, the script ensures the selected images are correctly attached and ready to use.
-# ==========================================================
-
-# Configuration ============================================
-LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
-BASE_DIR="/usr/local/share/proxmenux"
-UTILS_FILE="$BASE_DIR/utils.sh"
-VENV_PATH="/opt/googletrans-env"
-
-[[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE"
-load_language
-initialize_cache
-# Configuration ============================================
-
-
-detect_image_dir() {
- for store in $(pvesm status -content images | awk 'NR>1 {print $1}'); do
- path=$(pvesm path "${store}:template" 2>/dev/null)
- if [[ -d "$path" ]]; then
- for ext in raw img qcow2 vmdk; do
- if compgen -G "$path/*.$ext" > /dev/null; then
- echo "$path"
- return 0
- fi
- done
- for sub in images iso; do
- dir="$path/$sub"
- if [[ -d "$dir" ]]; then
- for ext in raw img qcow2 vmdk; do
- if compgen -G "$dir/*.$ext" > /dev/null; then
- echo "$dir"
- return 0
- fi
- done
- fi
- done
- fi
- done
- for fallback in /var/lib/vz/template/images /var/lib/vz/template/iso; do
- if [[ -d "$fallback" ]]; then
- for ext in raw img qcow2 vmdk; do
- if compgen -G "$fallback/*.$ext" > /dev/null; then
- echo "$fallback"
- return 0
- fi
- done
- fi
- done
- return 1
-}
-
-
-
-
-IMAGES_DIR=$(detect_image_dir)
-if [[ -z "$IMAGES_DIR" ]]; then
- dialog --title "$(translate 'No Images Found')" \
- --msgbox "$(translate 'Could not find any directory containing disk images')\n\n$(translate 'Make sure there is at least one file with extension .img, .qcow2, .vmdk or .raw')" 15 60
- exit 1
-fi
-
-IMAGES=$(ls -A "$IMAGES_DIR" | grep -E "\.(img|qcow2|vmdk|raw)$")
-if [ -z "$IMAGES" ]; then
- dialog --title "$(translate 'No Disk Images Found')" \
- --msgbox "$(translate 'No compatible disk images found in:')\n\n$IMAGES_DIR\n\n$(translate 'Supported formats: .img, .qcow2, .vmdk, .raw')" 15 60
- exit 1
-fi
-
-
-# === Select VM
-msg_info "$(translate 'Getting VM list')"
-VM_LIST=$(qm list | awk 'NR>1 {print $1" "$2}')
-[[ -z "$VM_LIST" ]] && { msg_error "$(translate 'No VMs available in the system')"; exit 1; }
-msg_ok "$(translate 'VM list obtained')"
-
-VMID=$(whiptail --title "$(translate 'Select VM')" \
- --menu "$(translate 'Select the VM where you want to import the disk image:')" 20 70 10 $VM_LIST 3>&1 1>&2 2>&3)
-[[ -z "$VMID" ]] && exit 1
-
-
-
-
-# === Select storage
-msg_info "$(translate 'Getting storage volumes')"
-STORAGE_LIST=$(pvesm status -content images | awk 'NR>1 {print $1}')
-[[ -z "$STORAGE_LIST" ]] && { msg_error "$(translate 'No storage volumes available')"; exit 1; }
-msg_ok "$(translate 'Storage volumes obtained')"
-
-STORAGE_OPTIONS=()
-while read -r storage; do STORAGE_OPTIONS+=("$storage" ""); done <<< "$STORAGE_LIST"
-STORAGE=$(whiptail --title "$(translate 'Select Storage')" \
- --menu "$(translate 'Select the storage volume for disk import:')" 20 70 10 "${STORAGE_OPTIONS[@]}" 3>&1 1>&2 2>&3)
-[[ -z "$STORAGE" ]] && exit 1
-
-
-
-# === Select images
-IMAGE_OPTIONS=()
-while read -r img; do IMAGE_OPTIONS+=("$img" "" "OFF"); done <<< "$IMAGES"
-SELECTED_IMAGES=$(whiptail --title "$(translate 'Select Disk Images')" \
- --checklist "$(translate 'Select the disk images to import:')" 20 70 12 "${IMAGE_OPTIONS[@]}" 3>&1 1>&2 2>&3)
-[[ -z "$SELECTED_IMAGES" ]] && exit 1
-
-
-
-# === Import each selected image
-for IMAGE in $SELECTED_IMAGES; do
- IMAGE=$(echo "$IMAGE" | tr -d '"')
- INTERFACE=$(whiptail --title "$(translate 'Interface Type')" --menu "$(translate 'Select the interface type for the image:') $IMAGE" 15 40 4 \
- "sata" "SATA" "scsi" "SCSI" "virtio" "VirtIO" "ide" "IDE" 3>&1 1>&2 2>&3)
- [[ -z "$INTERFACE" ]] && { msg_error "$(translate 'No interface type selected for') $IMAGE"; continue; }
-
- FULL_PATH="$IMAGES_DIR/$IMAGE"
- msg_info "$(translate 'Importing image:') $IMAGE"
- TEMP_DISK_FILE=$(mktemp)
-
- qm importdisk "$VMID" "$FULL_PATH" "$STORAGE" 2>&1 | while read -r line; do
- if [[ "$line" =~ transferred ]]; then
- PERCENT=$(echo "$line" | grep -oP "\(\d+\.\d+%\)" | tr -d '()%')
- echo -ne "\r${TAB}${BL}-$(translate 'Importing image:') $IMAGE-${CL} ${PERCENT}%"
- elif [[ "$line" =~ successfully\ imported\ disk ]]; then
- echo "$line" | grep -oP "(?<=successfully imported disk ').*(?=')" > "$TEMP_DISK_FILE"
- fi
- done
- echo -ne "\n"
- IMPORT_STATUS=${PIPESTATUS[0]}
-
-
-
- if [ "$IMPORT_STATUS" -eq 0 ]; then
- msg_ok "$(translate 'Image imported successfully')"
- IMPORTED_DISK=$(cat "$TEMP_DISK_FILE")
- rm -f "$TEMP_DISK_FILE"
-
- if [ -n "$IMPORTED_DISK" ]; then
- EXISTING_DISKS=$(qm config "$VMID" | grep -oP "${INTERFACE}\d+" | sort -n)
- NEXT_SLOT=0
- [[ -n "$EXISTING_DISKS" ]] && NEXT_SLOT=$(( $(echo "$EXISTING_DISKS" | tail -n1 | sed "s/${INTERFACE}//") + 1 ))
-
- SSD_OPTION=""
- if [ "$INTERFACE" != "virtio" ]; then
- whiptail --yesno "$(translate 'Do you want to use SSD emulation for this disk?')" 10 60 && SSD_OPTION=",ssd=1"
- fi
-
- msg_info "$(translate 'Configuring disk')"
- if qm set "$VMID" --${INTERFACE}${NEXT_SLOT} "$IMPORTED_DISK${SSD_OPTION}" &>/dev/null; then
- msg_ok "$(translate 'Image') $IMAGE $(translate 'configured as') ${INTERFACE}${NEXT_SLOT}"
- whiptail --yesno "$(translate 'Do you want to make this disk bootable?')" 10 60 && {
- msg_info "$(translate 'Configuring disk as bootable')"
- if qm set "$VMID" --boot c --bootdisk ${INTERFACE}${NEXT_SLOT} &>/dev/null; then
- msg_ok "$(translate 'Disk configured as bootable')"
- else
- msg_error "$(translate 'Could not configure the disk as bootable')"
- fi
- }
- else
- msg_error "$(translate 'Could not configure disk') ${INTERFACE}${NEXT_SLOT} $(translate 'for VM') $VMID"
- fi
- else
- msg_error "$(translate 'Could not find the imported disk')"
- fi
- else
- msg_error "$(translate 'Could not import') $IMAGE"
- fi
-done
-
-msg_ok "$(translate 'All selected images have been processed')"
-msg_success "$(translate "Press Enter to return to menu...")"
-read -r
\ No newline at end of file
diff --git a/scripts/install_coral_lxc.sh b/scripts/install_coral_lxc.sh
deleted file mode 100644
index 531e720d..00000000
--- a/scripts/install_coral_lxc.sh
+++ /dev/null
@@ -1,256 +0,0 @@
-#!/bin/bash
-
-# ==========================================================
-# ProxMenux - A menu-driven script for Proxmox VE management
-# ==========================================================
-# Author : MacRimi
-# Revision : @Blaspt (USB passthrough via udev rule with persistent /dev/coral)
-# Copyright : (c) 2024 MacRimi
-# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
-# Version : 1.1
-# Last Updated: 16/05/2025
-# ==========================================================
-# Description:
-# This script automates the configuration and installation of
-# Coral TPU and iGPU support in Proxmox VE containers. It:
-# - Configures a selected LXC container for hardware acceleration
-# - Installs and sets up Coral TPU drivers on the Proxmox host
-# - Installs necessary drivers inside the container
-# - Manages required system and container restarts
-#
-# Supports Coral USB and Coral M.2 (PCIe) devices.
-# Includes USB passthrough enhancement using persistent udev alias (/dev/coral).
-# ==========================================================
-
-LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
-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_container() {
- CONTAINERS=$(pct list | awk 'NR>1 {print $1, $3}' | xargs -n2)
- if [ -z "$CONTAINERS" ]; then
- msg_error "$(translate 'No containers available in Proxmox.')"
- exit 1
- fi
-
- CONTAINER_ID=$(whiptail --title "$(translate 'Select Container')" \
- --menu "$(translate 'Select the LXC container:')" 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
-
- if ! pct list | awk 'NR>1 {print $1}' | grep -qw "$CONTAINER_ID"; then
- msg_error "$(translate 'Container with ID') $CONTAINER_ID $(translate 'does not exist. Exiting.')"
- exit 1
- fi
-
- msg_ok "$(translate '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 status "$CONTAINER_ID" | grep -q "running"; then
- msg_info "$(translate 'Stopping the container before applying configuration...')"
- pct stop "$CONTAINER_ID"
- msg_ok "$(translate 'Container stopped.')"
- fi
-}
-
-
-add_udev_rule_for_coral_usb_() {
- RULE_FILE="/etc/udev/rules.d/99-coral-usb.rules"
- RULE_CONTENT='SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666", TAG+="uaccess"'
-
- if [[ ! -f "$RULE_FILE" ]] || ! grep -qF "$RULE_CONTENT" "$RULE_FILE"; then
- echo "$RULE_CONTENT" > "$RULE_FILE"
- udevadm control --reload-rules && udevadm trigger
- msg_ok "$(translate 'Udev rule for Coral USB added and rules reloaded.')"
- else
- msg_ok "$(translate 'Udev rule for Coral USB already exists.')"
- fi
-}
-
-
-add_udev_rule_for_coral_usb() {
- RULE_FILE="/etc/udev/rules.d/99-coral-usb.rules"
- RULE_CONTENT='# Coral USB Accelerator
-SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666", TAG+="uaccess", SYMLINK+="coral"
-# Coral Dev Board / Mini PCIe
-SUBSYSTEM=="usb", ATTRS{idVendor}=="1a6e", ATTRS{idProduct}=="089a", MODE="0666", TAG+="uaccess", SYMLINK+="coral"'
-
- if [[ ! -f "$RULE_FILE" ]] || ! grep -q "18d1.*9302\|1a6e.*089a" "$RULE_FILE"; then
- echo "$RULE_CONTENT" > "$RULE_FILE"
- udevadm control --reload-rules && udevadm trigger
- msg_ok "$(translate 'Udev rules for Coral USB devices added and rules reloaded.')"
- else
- msg_ok "$(translate 'Udev rules for Coral USB devices already exist.')"
- fi
-}
-
-
-add_mount_if_needed() {
- local DEVICE="$1"
- local DEST="$2"
- local CONFIG_FILE="$3"
- if [ -e "$DEVICE" ] && ! grep -q "lxc.mount.entry: $DEVICE" "$CONFIG_FILE"; then
- echo "lxc.mount.entry: $DEVICE $DEST none bind,optional,create=$( [ -c "$DEVICE" ] && echo file || echo dir )" >> "$CONFIG_FILE"
- fi
-}
-
-
-
-configure_lxc_hardware() {
- 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
- fi
-
- # Privileged container
- 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.')"
- else
- msg_ok "$(translate 'The container is already privileged.')"
- fi
-
-
- sed -i '/^dev[0-9]\+:/d' "$CONFIG_FILE"
-
- # Enable nesting feature
- if ! grep -q "features: nesting=1" "$CONFIG_FILE"; then
- echo "features: nesting=1" >> "$CONFIG_FILE"
- fi
-
- # iGPU support
- 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"
- fi
-
-
- add_mount_if_needed "/dev/dri" "dev/dri" "$CONFIG_FILE"
- add_mount_if_needed "/dev/dri/renderD128" "dev/dri/renderD128" "$CONFIG_FILE"
- add_mount_if_needed "/dev/dri/card0" "dev/dri/card0" "$CONFIG_FILE"
-
- # Framebuffer support
- if ! grep -q "c 29:0 rwm # Framebuffer" "$CONFIG_FILE"; then
- echo "lxc.cgroup2.devices.allow: c 29:0 rwm # Framebuffer" >> "$CONFIG_FILE"
- fi
- add_mount_if_needed "/dev/fb0" "dev/fb0" "$CONFIG_FILE"
-
-
- # ----------------------------------------------------------
- # Coral USB passthrough (via udev + /dev/coral)
- # ----------------------------------------------------------
- add_udev_rule_for_coral_usb
- if ! grep -Pq "^lxc.cgroup2.devices.allow: c 189:\* rwm # Coral USB$" "$CONFIG_FILE"; then
- echo "lxc.cgroup2.devices.allow: c 189:* rwm # Coral USB" >> "$CONFIG_FILE"
- fi
- add_mount_if_needed "/dev/coral" "dev/coral" "$CONFIG_FILE"
-
-
- # ----------------------------------------------------------
- # Coral M.2 (PCIe) support
- # ----------------------------------------------------------
- if lspci | grep -iq "Global Unichip"; then
- if ! grep -Pq "^lxc.cgroup2.devices.allow: c 245:0 rwm # Coral M2 Apex$" "$CONFIG_FILE"; then
- echo "lxc.cgroup2.devices.allow: c 245:0 rwm # Coral M2 Apex" >> "$CONFIG_FILE"
- fi
- add_mount_if_needed "/dev/apex_0" "dev/apex_0" "$CONFIG_FILE"
- fi
-
-
- msg_ok "$(translate 'Coral TPU and iGPU configuration added to container') $CONTAINER_ID."
-}
-
-install_coral_in_container() {
- msg_info2 "$(translate 'Installing iGPU and Coral TPU drivers inside the container...')"
- tput sc
- LOG_FILE=$(mktemp)
-
- pct start "$CONTAINER_ID"
-
- CORAL_M2=$(lspci | grep -i "Global Unichip")
- if [[ -n "$CORAL_M2" ]]; then
- DRIVER_OPTION=$(whiptail --title "$(translate 'Select driver version')" \
- --menu "$(translate 'Choose the driver version for Coral M.2:\n\nCaution: Maximum mode generates more heat.')" 15 60 2 \
- 1 "libedgetpu1-std ($(translate 'standard performance'))" \
- 2 "libedgetpu1-max ($(translate 'maximum performance'))" 3>&1 1>&2 2>&3)
-
- case "$DRIVER_OPTION" in
- 1) DRIVER_PACKAGE="libedgetpu1-std" ;;
- 2) DRIVER_PACKAGE="libedgetpu1-max" ;;
- *) DRIVER_PACKAGE="libedgetpu1-std" ;;
- esac
- else
- DRIVER_PACKAGE="libedgetpu1-std"
- fi
-
- script -q -c "pct exec \"$CONTAINER_ID\" -- bash -c '
- set -e
- echo \"- Updating package lists...\"
- apt-get update
- echo \"- Installing iGPU drivers...\"
- 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
-
- echo \"- Installing Coral TPU dependencies...\"
- apt-get install -y gnupg python3 python3-pip python3-venv
-
- echo \"- Adding Coral TPU repository...\"
- curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/coral-edgetpu.gpg
- echo \"deb [signed-by=/usr/share/keyrings/coral-edgetpu.gpg] https://packages.cloud.google.com/apt coral-edgetpu-stable main\" | tee /etc/apt/sources.list.d/coral-edgetpu.list
-
- echo \"- Updating package lists again...\"
- apt-get update
- echo \"- Installing Coral TPU driver ($DRIVER_PACKAGE)...\"
- apt-get install -y $DRIVER_PACKAGE
- '" "$LOG_FILE"
-
- if [ $? -eq 0 ]; then
- tput rc
- tput ed
- rm -f "$LOG_FILE"
- msg_ok "$(translate 'iGPU and Coral TPU drivers installed inside the container.')"
- else
- msg_error "$(translate 'Failed to install iGPU and Coral TPU drivers inside the container.')"
- cat "$LOG_FILE"
- rm -f "$LOG_FILE"
- exit 1
- fi
-}
-
-select_container
-show_proxmenux_logo
-configure_lxc_hardware
-install_coral_in_container
-
-msg_ok "$(translate 'Configuration completed.')"
-echo -e
-msg_success "$(translate "Press Enter to return to menu...")"
-read -r
diff --git a/scripts/install_coral_pve.sh b/scripts/install_coral_pve.sh
deleted file mode 100644
index 3f0730b8..00000000
--- a/scripts/install_coral_pve.sh
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/bin/bash
-
-
-# ProxMenux - A menu-driven script for Proxmox VE management
-# ==========================================================
-# Author : MacRimi
-# Copyright : (c) 2024 MacRimi
-# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
-# Version : 1.0
-# Last Updated: 28/01/2025
-# ==========================================================
-# Description:
-# This script installs the Coral TPU drivers on the Proxmox VE host.
-# It ensures that necessary packages are installed and compiles the
-# Coral TPU drivers for proper functionality.
-# ==========================================================
-
-
-# Configuration ============================================
-LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
-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
-
-# ==========================================================
-
-
-# Prompt before installation
-pre_install_prompt() {
- if ! whiptail --title "$(translate 'Coral TPU Installation')" --yesno "$(translate 'Installing Coral TPU drivers requires rebooting the server after installation. Do you want to proceed?')" 10 70; then
- msg_warn "$(translate 'Installation cancelled by user.')"
- exit 0
- fi
-}
-
-# Verify and configure repositories on the host
-verify_and_add_repos() {
- msg_info "$(translate 'Configuring necessary repositories on the host...')"
- sleep 2
-
- if ! grep -q "pve-no-subscription" /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null; then
- echo "deb http://download.proxmox.com/debian/pve $(lsb_release -sc) pve-no-subscription" | tee /etc/apt/sources.list.d/pve-no-subscription.list
- msg_ok "$(translate 'pve-no-subscription repository added.')"
- fi
-
- if ! grep -q "non-free-firmware" /etc/apt/sources.list; then
- echo "deb http://deb.debian.org/debian $(lsb_release -sc) main contrib non-free-firmware
- deb http://deb.debian.org/debian $(lsb_release -sc)-updates main contrib non-free-firmware
- deb http://security.debian.org/debian-security $(lsb_release -sc)-security main contrib non-free-firmware" | tee -a /etc/apt/sources.list
- msg_ok "$(translate 'non-free-firmware repositories added.')"
- fi
-
- msg_ok "$(translate 'Added repositories')"
- sleep 2
-
- msg_info "$(translate 'Verifying repositories...')"
- apt-get update &>/dev/null
-
- msg_ok "$(translate 'Verified and updated repositories.')"
-}
-
-# Function to install Coral TPU drivers on the host
-install_coral_host() {
- show_proxmenux_logo
- verify_and_add_repos
-
- apt-get install -y git devscripts dh-dkms dkms pve-headers-$(uname -r) >/dev/null 2>&1
-
- cd /tmp
- rm -rf gasket-driver
- git clone https://github.com/google/gasket-driver.git
- if [ $? -ne 0 ]; then
- msg_error "$(translate 'Error: Could not clone the repository.')"
- exit 1
- fi
-
- cd gasket-driver/
- debuild -us -uc -tc -b
- if [ $? -ne 0 ]; then
- msg_error "$(translate 'Error: Failed to build driver packages.')"
- exit 1
- fi
-
- dpkg -i ../gasket-dkms_*.deb
- if [ $? -ne 0 ]; then
- msg_error "$(translate 'Error: Failed to install the driver packages.')"
- exit 1
- fi
-
- msg_success "$(translate 'Coral TPU drivers installed successfully on the host.')"
- echo -e
-}
-
-# Prompt for reboot after installation
- restart_prompt() {
- if whiptail --title "$(translate 'Coral TPU Installation')" --yesno "$(translate 'The installation requires a server restart to apply changes. Do you want to restart now?')" 10 70; then
- msg_warn "$(translate 'Restarting the server...')"
- reboot
- else
- echo -e
- msg_success "$(translate "Press Enter to return to menu...")"
- read -r
- fi
-}
-
-
-pre_install_prompt
-install_coral_host
-restart_prompt
diff --git a/scripts/menus/hw_grafics_menu.sh b/scripts/menus/hw_grafics_menu.sh
index c0c7d346..fd7288c7 100644
--- a/scripts/menus/hw_grafics_menu.sh
+++ b/scripts/menus/hw_grafics_menu.sh
@@ -35,13 +35,13 @@ initialize_cache
case $OPTION in
1)
- bash "$LOCAL_SCRIPTS/configure_igpu_lxc.sh"
+ bash "$LOCAL_SCRIPTS/gpu_tpu/configure_igpu_lxc.sh"
if [ $? -ne 0 ]; then
return
fi
;;
2)
- bash "$LOCAL_SCRIPTS/install_coral_lxc.sh"
+ bash "$LOCAL_SCRIPTS/gpu_tpu/install_coral_lxc.sh"
if [ $? -ne 0 ]; then
return
fi
diff --git a/scripts/menus/sm.sh b/scripts/menus/sm.sh
deleted file mode 100644
index 961d729d..00000000
--- a/scripts/menus/sm.sh
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/bin/bash
-
-# ==========================================================
-# ProxMenux - A menu-driven script for Proxmox VE management
-# ==========================================================
-# Author : MacRimi
-# Copyright : (c) 2024 MacRimi
-# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
-# Version : 1.0
-# Last Updated: 28/01/2025
-# ==========================================================
-
-
-# Configuration ============================================
-LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
-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=$(whiptail --title "$(translate "Disk and Storage Manager Menu")" --menu "$(translate "Select an option:")" 20 70 10 \
- "1" "$(translate "Add Disk Passthrough to a VM")" \
- "2" "$(translate "Add Disk") Passthrough $(translate "to a CT")" \
- "3" "$(translate "Import Disk Image to a VM")" \
- "4" "$(translate "Mount point to CT")" \
- "5" "$(translate "Mount disk on HOST")" \
- "6" "$(translate "Unmount disk from HOST")" \
- "7" "$(translate "Format disk")" \
- "8" "$(translate "Return to Main Menu")" 3>&1 1>&2 2>&3)
-
- case $OPTION in
- 1)
- msg_info2 "$(translate "Running script: Add Disk Passthrough to a VM")..."
- bash "$LOCAL_SCRIPTS/storage/disk-passthrough.sh"
- ;;
- 2)
- msg_info2 "$(translate "Running script: Add Disk Passthrough to a CT")..."
- bash "$LOCAL_SCRIPTS/storage/disk-passthrough_ct.sh"
- ;;
- 3)
- msg_info2 "$(translate "Running script: Import Disk Image to a VM")..."
- bash "$LOCAL_SCRIPTS/storage/import-disk-image.sh"
- ;;
- 4)
- msg_info2 "$(translate "Running script: Mount point to CT")..."
- bash "$LOCAL_SCRIPTS/storage/mount-point-to-ct.sh"
- ;;
- 5)
- msg_info2 "$(translate "Running script: Mount disk on HOST")..."
- bash "$LOCAL_SCRIPTS/storage/mount-disk-on-host.sh"
- ;;
- 6)
- msg_info2 "$(translate "Running script: Unmount disk from HOST")..."
- bash "$LOCAL_SCRIPTS/storage/unmount-disk-from-host.sh"
- ;;
- 7)
- msg_info2 "$(translate "Running script: Format disk")..."
- bash "$LOCAL_SCRIPTS/storage/format-disk.sh"
- ;;
- 8)
- exec bash "$LOCAL_SCRIPTS/menus/main_menu.sh"
- ;;
- *)
- exec bash "$LOCAL_SCRIPTS/menus/main_menu.sh"
- ;;
- esac
-done
diff --git a/scripts/share/guia.md b/scripts/share/guia.md
deleted file mode 100644
index 9dd58a71..00000000
--- a/scripts/share/guia.md
+++ /dev/null
@@ -1,1292 +0,0 @@
-# 📘 Guía Completa: Compartir Recursos entre Proxmox Host y Contenedores LXC
-
-## 📋 Índice
-
-1. [Conceptos Fundamentales](#1-conceptos-fundamentales)
- - [Usuarios y Grupos en Linux](#usuarios-y-grupos-en-linux)
- - [Permisos Básicos](#permisos-básicos)
- - [El Bit Setgid](#el-bit-setgid)
- - [Access Control Lists (ACL)](#access-control-lists-acl)
- - [Contenedores Privilegiados vs No Privilegiados](#contenedores-privilegiados-vs-no-privilegiados)
-
-2. [Cómo Funcionan los Permisos en Recursos Compartidos](#2-cómo-funcionan-los-permisos-en-recursos-compartidos)
- - [Servidores NFS](#servidores-nfs)
- - [Servidores Samba/CIFS](#servidores-sambacifs)
- - [Directorios Locales](#directorios-locales)
-
-3. [Preparación del Host Proxmox](#3-preparación-del-host-proxmox)
- - [Crear Directorio Local](#crear-directorio-local)
- - [Montar Recurso NFS](#montar-recurso-nfs)
- - [Montar Recurso Samba](#montar-recurso-samba)
-
-4. [Configuración de Contenedores](#4-configuración-de-contenedores)
- - [Contenedores Privilegiados](#contenedores-privilegiados)
- - [Contenedores No Privilegiados](#contenedores-no-privilegiados)
-
-5. [Montaje en Contenedores](#5-montaje-en-contenedores)
-
-6. [Verificación y Pruebas](#6-verificación-y-pruebas)
-
-7. [Solución de Problemas](#7-solución-de-problemas)
-
----
-
-## 1. Conceptos Fundamentales
-
-### Usuarios y Grupos en Linux
-
-#### ¿Qué es un Usuario?
-
-Un **usuario** en Linux es una identidad que puede:
-- Poseer archivos y directorios
-- Ejecutar procesos
-- Tener permisos específicos
-
-Cada usuario tiene:
-- **Nombre**: Como `root`, `www-data`, `juan`, `jonatan`, `proxmenux`, `rafa` etc...
-- **UID (User ID)**: Número único, como `0` (root), `33` (www-data), `1000` (primer usuario),`1001` (segundo usuario), etc...
-
-```bash
-# Ver información de un usuario
-id www-data
-# Salida: uid=33(www-data) gid=33(www-data) groups=33(www-data)
-
-
-# Listar todos los usuarios del sistema
-cat /etc/passwd | head -5
-# Salida:
-# root:x:0:0:root:/root:/bin/bash
-# daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
-# bin:x:2:2:bin:/bin:/usr/sbin/nologin
-```
-
-#### ¿Qué es un Grupo?
-
-Un **grupo** es una colección de usuarios que comparten permisos. Permite:
-- Dar acceso a múltiples usuarios sin configurar cada uno individualmente
-- Organizar permisos de manera lógica
-- Simplificar la administración
-
-Cada grupo tiene:
-- **Nombre**: Como `root`, `www-data`, `sharedfiles`
-- **GID (Group ID)**: Número único, como `0` (root), `33` (www-data), `1000` (usuarios)
-
-```bash
-# Ver todos los grupos
-cat /etc/group | head -5
-# Salida:
-# root:x:0:
-# daemon:x:1:
-# bin:x:2:
-
-
-# Ver grupos de un usuario
-groups www-data
-# Salida: www-data : www-data
-
-
-# Crear un nuevo grupo
-groupadd -g 1001 migrupo
-
-
-# Añadir usuario a un grupo
-usermod -aG migrupo www-data
-```
-
-#### ¿Por qué son importantes los grupos?
-
-**Ejemplo práctico**: Tienes 3 contenedores que ejecutan aplicaciones web:
-- **Sin grupos**: Tendrías que dar permisos individuales a cada usuario de cada contenedor
-- **Con grupos**: Creas un grupo `webapps`, añades todos los usuarios web, y das permisos al grupo
-
-### Permisos Básicos
-
-#### Tipos de Permisos
-
-Cada archivo y directorio tiene tres tipos de permisos para tres categorías de usuarios:
-
-| Permiso | Símbolo | Valor | En Archivos | En Directorios |
-|---------|---------|-------|-------------|----------------|
-| **Lectura** | `r` | 4 | Leer contenido | Listar archivos |
-| **Escritura** | `w` | 2 | Modificar archivo | Crear/eliminar archivos |
-| **Ejecución** | `x` | 1 | Ejecutar archivo | Entrar al directorio |
-
-#### Categorías de Usuarios
-
-| Categoría | Descripción | Posición |
-|-----------|-------------|----------|
-| **Propietario** (user) | El dueño del archivo | Primera posición |
-| **Grupo** (group) | Miembros del grupo propietario | Segunda posición |
-| **Otros** (others) | Todos los demás usuarios | Tercera posición |
-
-#### Lectura de Permisos
-
-```bash
-ls -l /mnt/ejemplo
-# Salida: drwxrwxr-x 2 root sharedfiles 4096 sep 8 10:30 ejemplo
-# │││││││││
-# │└┴┴┴┴┴┴┴─ Permisos
-# └─────────── Tipo (d=directorio, -=archivo)
-
-
-# Desglose de permisos: rwxrwxr-x
-# Propietario (root): rwx = 7 (lectura + escritura + ejecución)
-# Grupo (sharedfiles): rwx = 7 (lectura + escritura + ejecución)
-# Otros: r-x = 5 (lectura + ejecución, sin escritura)
-```
-
-#### Permisos Numéricos
-
-Los permisos se pueden expresar como números de 3 dígitos:
-
-```bash
-# Ejemplos comunes
-chmod 755 archivo # rwxr-xr-x (propietario: todo, otros: lectura+ejecución)
-chmod 644 archivo # rw-r--r-- (propietario: lectura+escritura, otros: solo lectura)
-chmod 775 directorio # rwxrwxr-x (propietario y grupo: todo, otros: lectura+ejecución)
-```
-
-### El Bit Setgid
-
-El **setgid** es uno de los conceptos más importantes para directorios compartidos, pero también uno de los menos comprendidos.
-
-#### ¿Qué hace el setgid?
-
-Cuando se aplica a un directorio, el **setgid** hace que:
-- **Todos los archivos y subdirectorios creados dentro hereden automáticamente el grupo del directorio padre**
-- **No importa qué usuario o proceso cree el archivo, siempre tendrá el mismo grupo**
-
-#### Ejemplo Práctico: Sin setgid
-
-```bash
-# Crear directorio sin setgid
-mkdir /tmp/sin_setgid
-chmod 775 /tmp/sin_setgid
-chgrp sharedfiles /tmp/sin_setgid
-
-
-ls -ld /tmp/sin_setgid
-# Salida: drwxrwxr-x 2 root sharedfiles 4096 sep 8 10:30 /tmp/sin_setgid
-# ↑ No hay 's' en la posición del grupo
-
-
-# Crear archivo como usuario www-data
-sudo -u www-data touch /tmp/sin_setgid/archivo1
-ls -l /tmp/sin_setgid/archivo1
-# Salida: -rw-r--r-- 1 www-data www-data 0 sep 8 10:35 archivo1
-# ❌ El archivo pertenece al grupo 'www-data', NO 'sharedfiles'
-
-
-# Crear archivo como usuario root
-touch /tmp/sin_setgid/archivo2
-ls -l /tmp/sin_setgid/archivo2
-# Salida: -rw-r--r-- 1 root root 0 sep 8 10:36 archivo2
-# ❌ El archivo pertenece al grupo 'root', NO 'sharedfiles'
-```
-
-**Problema**: Cada usuario crea archivos con su grupo primario, causando inconsistencias.
-
-#### Ejemplo Práctico: Con setgid
-
-```bash
-# Crear directorio CON setgid
-mkdir /tmp/con_setgid
-chmod 2775 /tmp/con_setgid # El '2' inicial activa setgid
-chgrp sharedfiles /tmp/con_setgid
-
-
-ls -ld /tmp/con_setgid
-# Salida: drwxrwsr-x 2 root sharedfiles 4096 sep 8 10:37 /tmp/con_setgid
-# ↑ La 's' indica que setgid está activo
-
-
-# Crear archivo como usuario www-data
-sudo -u www-data touch /tmp/con_setgid/archivo1
-ls -l /tmp/con_setgid/archivo1
-# Salida: -rw-r--r-- 1 www-data sharedfiles 0 sep 8 10:38 archivo1
-# ✅ El archivo hereda el grupo 'sharedfiles' del directorio padre
-
-
-# Crear archivo como usuario root
-touch /tmp/con_setgid/archivo2
-ls -l /tmp/con_setgid/archivo2
-# Salida: -rw-r--r-- 1 root sharedfiles 0 sep 8 10:39 archivo2
-# ✅ El archivo también hereda el grupo 'sharedfiles'
-
-
-# Crear subdirectorio
-mkdir /tmp/con_setgid/subdir
-ls -ld /tmp/con_setgid/subdir
-# Salida: drwxr-sr-x 2 root sharedfiles 4096 sep 8 10:40 subdir
-# ✅ El subdirectorio hereda el grupo Y también tiene setgid activo
-```
-
-#### ¿Por qué es crucial setgid para recursos compartidos?
-
-1. **Consistencia**: Todos los archivos tienen el mismo grupo, independientemente de quién los cree
-2. **Simplicidad**: No necesitas cambiar manualmente el grupo de cada archivo nuevo
-3. **Herencia**: Los subdirectorios también heredan el setgid, manteniendo la consistencia en toda la estructura
-4. **Compatibilidad**: Funciona con NFS, Samba, y contenedores sin configuración adicional
-
-### Access Control Lists (ACL)
-
-#### Limitaciones de los Permisos Tradicionales
-
-Los permisos tradicionales de Linux solo permiten:
-- **1 propietario**
-- **1 grupo**
-- **Permisos para "otros"**
-
-**Problema**: ¿Qué pasa si necesitas que 3 grupos diferentes tengan acceso de escritura?
-
-#### ¿Qué son las ACL?
-
-Las **Access Control Lists (ACL)** extienden los permisos tradicionales permitiendo:
-- **Múltiples usuarios** con permisos específicos
-- **Múltiples grupos** con permisos específicos
-- **Permisos por defecto** que se heredan automáticamente
-
-```bash
-# Instalar herramientas ACL
-apt-get install acl
-
-# Ver ACL de un archivo/directorio
-getfacl /mnt/shared
-# Salida:
-# file: mnt/shared
-# owner: root
-# group: sharedfiles
-# user::rwx
-# group::rwx
-# group:webapps:rwx
-# group:developers:r-x
-# other::r-x
-# default:user::rwx
-# default:group::rwx
-# default:group:webapps:rwx
-# default:other::r-x
-```
-
-#### Configurar ACL
-
-```bash
-# Dar permisos a un grupo específico
-setfacl -m g:webapps:rwx /mnt/shared
-
-# Dar permisos a un usuario específico
-setfacl -m u:juan:rw /mnt/shared
-
-# Configurar permisos por defecto (se heredan)
-setfacl -d -m g:webapps:rwx /mnt/shared
-
-# Aplicar ACL recursivamente a todo el contenido existente
-setfacl -R -m g:webapps:rwx /mnt/shared
-
-# Combinar: aplicar a existente Y configurar por defecto
-setfacl -R -m g:webapps:rwx /mnt/shared
-setfacl -d -m g:webapps:rwx /mnt/shared
-```
-
-#### ¿Por qué son importantes las ACL para NFS y Samba?
-
-**NFS y mapeo de IDs**:
-- NFS transmite solo números (UID/GID), no nombres
-- Si el cliente tiene usuarios con IDs diferentes, los permisos se rompen
-- Las ACL aseguran que el grupo correcto siempre tenga acceso
-
-**Ejemplo práctico**:
-```bash
-# Servidor NFS: usuario 'web' tiene UID 1001
-# Cliente NFS: usuario 'web' tiene UID 1002
-
-# Sin ACL: El cliente ve archivos del UID 1001 (usuario inexistente)
-# Con ACL: El grupo 'webapps' siempre tiene acceso, independiente de UIDs
-```
-
-### Contenedores Privilegiados vs No Privilegiados
-
-#### ¿Qué son los Contenedores No Privilegiados?
-
-Los contenedores **no privilegiados** son más seguros porque:
-- El usuario `root` del contenedor NO es `root` del host
-- Los IDs de usuario/grupo están "desplazados"
-- Limitan el daño si el contenedor es comprometido
-
-#### Mapeo de IDs
-
-En un contenedor no privilegiado típico:
-
-| Contenedor | Host | Explicación |
-|------------|------|-------------|
-| UID 0 (root) | UID 100000 | Root del contenedor = usuario 100000 del host |
-| UID 1 | UID 100001 | Usuario 1 del contenedor = usuario 100001 del host |
-| UID 1000 (usuario) | UID 101000 | Usuario 1000 del contenedor = usuario 101000 del host |
-| GID 0 (root) | GID 100000 | Grupo root del contenedor = grupo 100000 del host |
-| GID 1000 (grupo) | GID 101000 | Grupo 1000 del contenedor = grupo 101000 del host |
-
-#### El Problema en la Práctica
-
-```bash
-# En el HOST: Crear archivo con grupo 'sharedfiles' (GID 1000)
-echo "datos" > /mnt/shared/archivo.txt
-chgrp sharedfiles /mnt/shared/archivo.txt
-ls -l /mnt/shared/archivo.txt
-# Salida: -rw-r--r-- 1 root sharedfiles 6 sep 8 archivo.txt
-
-
-# En el CONTENEDOR NO PRIVILEGIADO: Ver el mismo archivo
-pct exec 101 -- ls -l /mnt/shared/archivo.txt
-# Salida: -rw-r--r-- 1 nobody nogroup 6 sep 8 archivo.txt
-# ❌ El contenedor ve 'nobody:nogroup' porque no conoce el GID 1000 del host
-```
-
-#### ¿Por qué pasa esto?
-
-1. **Host**: El archivo pertenece al GID 1000 (`sharedfiles`)
-2. **Contenedor**: Busca qué grupo tiene GID 1000 en SU `/etc/group`
-3. **Resultado**: Si no existe ese GID en el contenedor, muestra `nogroup`
-4. **Consecuencia**: El usuario del contenedor no puede escribir
-
----
-
-## 2. Cómo Funcionan los Permisos en Recursos Compartidos
-
-### Servidores NFS
-
-#### ¿Cómo maneja NFS los permisos?
-
-Los servidores NFS (ya sean Linux, TrueNAS, Synology, etc.) funcionan de manera similar:
-
-1. **Solo transmiten números**: NFS envía UID/GID numéricos, no nombres de usuario
-2. **No hay autenticación de usuario**: NFS confía en que el cliente ya autenticó al usuario
-3. **Los permisos se evalúan en el servidor**: El servidor NFS verifica permisos usando sus propios archivos `/etc/passwd` y `/etc/group`
-
-#### Ejemplo con TrueNAS
-
-```bash
-# En TrueNAS: Crear dataset con permisos
-# Dataset: /mnt/pool/shared
-# Propietario: root (UID 0)
-# Grupo: shared (GID 1001)
-# Permisos: 775
-
-
-# En Proxmox: Montar el NFS
-mount -t nfs 192.168.1.100:/mnt/pool/shared /mnt/truenas_shared
-
-
-# Ver cómo se ven los permisos en Proxmox
-ls -ld /mnt/truenas_shared
-# Salida: drwxrwxr-x 2 root 1001 4096 sep 8 10:45 /mnt/truenas_shared
-# ↑ Aparece el GID numérico porque Proxmox no tiene grupo con GID 1001
-```
-
-#### Solución: Crear grupo con el mismo GID
-
-```bash
-# En Proxmox: Crear grupo con el mismo GID que TrueNAS
-groupadd -g 1001 truenas_shared
-
-
-# Ahora se ve correctamente
-ls -ld /mnt/truenas_shared
-# Salida: drwxrwxr-x 2 root truenas_shared 4096 sep 8 10:45 /mnt/truenas_shared
-```
-
-#### Configuraciones comunes en servidores NFS
-
-**TrueNAS/FreeNAS**:
-- Maptype: Unix
-- Usuario: root o usuario específico
-- Grupo: wheel, shared, o grupo personalizado
-
-**Synology**:
-- Squash: No mapping
-- Usuario: root o admin
-- Grupo: users o grupo personalizado
-
-**Linux (Ubuntu/Debian)**:
-```bash
-# /etc/exports
-/export/shared 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
-```
-
-### Servidores Samba/CIFS
-
-#### ¿Cómo maneja Samba los permisos?
-
-Samba es más complejo porque debe mapear entre:
-1. **Usuarios Windows** (autenticación SMB)
-2. **Usuarios Linux** (permisos del sistema de archivos)
-
-#### Ejemplo con Synology
-
-```bash
-# En Synology: Crear carpeta compartida
-# Nombre: SharedData
-# Permisos SMB: Grupo 'users' con lectura/escritura
-# Permisos Linux: 775, propietario 'admin', grupo 'users'
-
-
-# En Proxmox: Montar Samba
-mount -t cifs //192.168.1.200/SharedData /mnt/synology_shared \
- -o username=admin,password=mipassword,uid=root,gid=1000,file_mode=0664,dir_mode=0775
-
-
-# Ver permisos
-ls -ld /mnt/synology_shared
-# Salida: drwxrwxr-x 2 root 1000 4096 sep 8 10:50 /mnt/synology_shared
-# ↑ Usa el GID especificado en las opciones de montaje
-```
-
-#### Opciones importantes de montaje CIFS
-
-- `uid=`: UID que se asigna a todos los archivos
-- `gid=`: GID que se asigna a todos los archivos
-- `file_mode=`: Permisos para archivos (ej: 0664)
-- `dir_mode=`: Permisos para directorios (ej: 0775)
-- `forceuid/forcegid`: Fuerza el uso de uid/gid especificados
-
-### Directorios Locales
-
-Los directorios locales son los más simples:
-- Los permisos se respetan directamente
-- No hay mapeo de red
-- Funciona con usuarios y grupos locales del sistema
-
----
-
-## 3. Preparación del Host Proxmox
-
-### Crear Directorio Local
-
-Para crear un directorio local que se compartirá con contenedores:
-
-```bash
-# 1. Crear directorio
-mkdir -p /mnt/local_shared
-
-
-# 2. Crear grupo común (usaremos GID 101000 para compatibilidad universal)
-groupadd -g 101000 sharedfiles
-
-
-# 3. Configurar propietario y permisos con setgid
-chown root:sharedfiles /mnt/local_shared
-chmod 2775 /mnt/local_shared
-
-
-# 4. Aplicar ACL para garantizar permisos
-setfacl -R -m g:sharedfiles:rwx /mnt/local_shared
-setfacl -d -m g:sharedfiles:rwx /mnt/local_shared
-
-
-# 5. Verificar configuración
-ls -ld /mnt/local_shared
-# Salida esperada: drwxrwsr-x+ 2 root sharedfiles 4096 sep 8 11:00 /mnt/local_shared
-# ↑ 's' indica setgid activo
-# ↑ '+' indica ACL aplicadas
-
-
-getfacl /mnt/local_shared
-# Salida esperada:
-# file: mnt/local_shared
-# owner: root
-# group: sharedfiles
-# user::rwx
-# group::rwx
-# other::r-x
-# default:user::rwx
-# default:group::rwx
-# default:other::r-x
-```
-
-### Montar Recurso NFS
-
-#### Montaje Temporal
-
-```bash
-# 1. Crear punto de montaje
-mkdir -p /mnt/nfs_shared
-
-
-# 2. Montar NFS
-mount -t nfs 192.168.1.100:/export/shared /mnt/nfs_shared
-
-
-# 3. Verificar montaje
-df -h | grep nfs
-mount | grep nfs
-
-
-# 4. Ver permisos originales
-ls -ld /mnt/nfs_shared
-# Ejemplo: drwxrwxr-x 2 root 1001 4096 sep 8 11:05 /mnt/nfs_shared
-```
-
-#### Crear Grupo Compatible
-
-```bash
-# Si el directorio NFS tiene un GID específico (ej: 1001), crear grupo local
-groupadd -g 1001 nfs_shared
-
-
-# Verificar que ahora se ve el nombre del grupo
-ls -ld /mnt/nfs_shared
-# Salida: drwxrwxr-x 2 root nfs_shared 4096 sep 8 11:05 /mnt/nfs_shared
-```
-
-#### Aplicar Configuración Universal
-
-```bash
-# Para compatibilidad con contenedores, aplicar nuestro esquema estándar
-# IMPORTANTE: Solo si tienes permisos de escritura en el NFS
-
-
-# Crear nuestro grupo estándar
-groupadd -g 101000 sharedfiles
-
-
-# Cambiar grupo del directorio NFS (si es posible)
-chgrp sharedfiles /mnt/nfs_shared
-
-
-# Aplicar setgid y ACL
-chmod 2775 /mnt/nfs_shared
-setfacl -R -m g:sharedfiles:rwx /mnt/nfs_shared
-setfacl -d -m g:sharedfiles:rwx /mnt/nfs_shared
-```
-
-#### Montaje Persistente
-
-```bash
-# Editar /etc/fstab
-nano /etc/fstab
-
-
-# Añadir línea:
-192.168.1.100:/export/shared /mnt/nfs_shared nfs rw,hard,nofail,rsize=131072,wsize=131072,timeo=600,retrans=2,_netdev 0 0
-
-
-# Explicación de opciones:
-# rw: lectura/escritura
-# hard: reintentar indefinidamente si el servidor no responde
-# nofail: no fallar el arranque si no se puede montar
-# rsize/wsize: tamaño de buffer para lectura/escritura (mejor rendimiento)
-# timeo: timeout en décimas de segundo (60 segundos)
-# retrans: número de reintentos antes de reportar error
-# _netdev: esperar a que la red esté disponible
-# 0 0: no hacer dump ni fsck (siempre para recursos de red)
-
-# Probar montaje
-mount -a
-
-
-# Verificar
-df -h | grep nfs
-```
-
-### Montar Recurso Samba
-
-#### Crear Archivo de Credenciales
-
-```bash
-# Crear archivo de credenciales seguro
-nano /etc/cifs-credentials
-
-
-# Contenido:
-username=tu_usuario
-password=tu_password
-domain=tu_dominio
-
-
-# Proteger archivo
-chmod 600 /etc/cifs-credentials
-chown root:root /etc/cifs-credentials
-```
-
-#### Montaje Temporal
-
-```bash
-# 1. Crear punto de montaje
-mkdir -p /mnt/samba_shared
-
-
-# 2. Montar Samba con opciones específicas
-mount -t cifs //192.168.1.200/SharedData /mnt/samba_shared \
- -o credentials=/etc/cifs-credentials,uid=root,gid=101000,file_mode=0664,dir_mode=2775,iocharset=utf8,vers=3.0
-
-
-# Explicación de opciones:
-# credentials: archivo con usuario/password
-# uid=root: todos los archivos aparecen como propietario root
-# gid=101000: todos los archivos aparecen con grupo sharedfiles
-# file_mode=0664: permisos para archivos (rw-rw-r--)
-# dir_mode=2775: permisos para directorios (rwxrwsr-x) con setgid
-# iocharset=utf8: codificación de caracteres
-# vers=3.0: versión del protocolo SMB
-
-
-# 3. Verificar montaje
-df -h | grep cifs
-ls -ld /mnt/samba_shared
-```
-
-#### Configurar Grupo y Permisos
-
-```bash
-# Crear grupo si no existe
-groupadd -g 101000 sharedfiles
-
-
-# Verificar que los permisos son correctos
-ls -ld /mnt/samba_shared
-# Salida esperada: drwxrwsr-x 2 root sharedfiles 4096 sep 8 11:10 /mnt/samba_shared
-
-
-# Aplicar ACL adicionales si es necesario
-setfacl -R -m g:sharedfiles:rwx /mnt/samba_shared
-setfacl -d -m g:sharedfiles:rwx /mnt/samba_shared
-```
-
-#### Montaje Persistente
-
-```bash
-# Editar /etc/fstab
-nano /etc/fstab
-
-
-# Añadir línea:
-//192.168.1.200/SharedData /mnt/samba_shared cifs credentials=/etc/cifs-credentials,uid=root,gid=101000,file_mode=0664,dir_mode=2775,iocharset=utf8,vers=3.0,_netdev,nofail 0 0
-
-
-# Probar montaje
-mount -a
-
-
-# Verificar
-df -h | grep cifs
-```
-
----
-
-## 4. Configuración de Contenedores
-
-### Contenedores Privilegiados
-
-Los contenedores privilegiados comparten los mismos UIDs/GIDs que el host, por lo que la configuración es más directa.
-
-#### ¿Necesitan configuración especial?
-
-**Respuesta corta**: Generalmente NO, pero hay casos donde SÍ es recomendable.
-
-#### Cuándo NO necesitan configuración
-
-Si el contenedor privilegiado:
-- Solo usa el usuario `root`
-- No ejecuta servicios con usuarios específicos (como `www-data`)
-- Los archivos siempre se crean como `root:root`
-
-```bash
-# Ejemplo: Contenedor que solo usa root
-pct exec 100 -- bash
-whoami # root
-id # uid=0(root) gid=0(root) groups=0(root)
-
-
-# Crear archivo en directorio compartido
-echo "test" > /mnt/shared/archivo.txt
-ls -l /mnt/shared/archivo.txt
-# Salida: -rw-r--r-- 1 root root 5 sep 8 11:15 archivo.txt
-# ❌ El archivo pertenece al grupo 'root', no 'sharedfiles'
-```
-
-#### Cuándo SÍ necesitan configuración
-
-Si el contenedor privilegiado:
-- Ejecuta servicios web (`www-data`, `nginx`, `apache`)
-- Tiene aplicaciones que crean archivos con usuarios específicos
-- Necesita compatibilidad con otros contenedores o servicios
-
-```bash
-# Ejemplo: Contenedor con Nextcloud
-pct exec 100 -- bash
-
-
-# Ver usuarios del sistema
-cat /etc/passwd | grep -E "(www-data|nginx|apache)"
-# Salida: www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
-
-
-# Sin configuración: Nextcloud crea archivos como www-data:www-data
-sudo -u www-data touch /mnt/shared/nextcloud_file.txt
-ls -l /mnt/shared/nextcloud_file.txt
-# Salida: -rw-r--r-- 1 www-data www-data 0 sep 8 11:20 nextcloud_file.txt
-# ❌ Grupo 'www-data' no es compatible con otros contenedores
-```
-
-#### Configuración Recomendada para Contenedores Privilegiados
-
-```bash
-# 1. Entrar al contenedor
-pct exec 100 -- bash
-
-
-# 2. Crear grupo con el mismo GID que el host
-groupadd -g 101000 sharedfiles
-
-
-# 3. Añadir usuarios relevantes al grupo
-usermod -aG sharedfiles root
-usermod -aG sharedfiles www-data
-
-
-# Si tienes otros usuarios específicos de aplicaciones:
-usermod -aG sharedfiles nextcloud 2>/dev/null || true
-usermod -aG sharedfiles nginx 2>/dev/null || true
-# (añade los usuaros que desees)
-
-
-# 4. Verificar membresía
-groups root
-# Salida: root : root sharedfiles
-
-
-groups www-data
-# Salida: www-data : www-data sharedfiles
-
-
-# 5. Salir del contenedor
-exit
-```
-
-#### ¿Por qué es importante esta configuración?
-
-1. **Consistencia**: Los archivos creados por diferentes usuarios mantienen el grupo `sharedfiles`
-2. **Compatibilidad**: Funciona con contenedores no privilegiados y otros servicios
-3. **Flexibilidad**: Permite que múltiples usuarios/servicios accedan a los mismos archivos
-
-### Contenedores No Privilegiados
-
-Los contenedores no privilegiados SIEMPRE necesitan configuración especial debido al mapeo de UIDs/GIDs.
-
-#### Configuración Obligatoria
-
-```bash
-# 1. Entrar al contenedor
-pct exec 101 -- bash
-
-
-# 2. Crear grupo con GID mapeado
-# GID 1000 en contenedor = GID 101000 en host
-groupadd -g 1000 sharedfiles
-
-
-# 3. Listar usuarios disponibles en el contenedor
-awk -F: '$3>=1000 && $1!="nobody" {print $1 " (UID: " $3 ")"}' /etc/passwd
-# Salida típica:
-# root (UID: 0)
-# www-data (UID: 33)
-# ncp (UID: 1000)
-
-
-# También incluir usuarios del sistema si es necesario
-awk -F: '$3<1000 && $3>0 && $1!="nobody" {print $1 " (UID: " $3 ")"}' /etc/passwd
-
-
-# 4. Añadir usuarios al grupo
-usermod -aG sharedfiles root
-usermod -aG sharedfiles www-data
-
-
-# Si tienes usuarios específicos de aplicaciones:
-usermod -aG sharedfiles ncp 2>/dev/null || true
-usermod -aG sharedfiles nextcloud 2>/dev/null || true
-# (añade los usuaros que desees)
-
-
-# 5. Verificar configuración
-id www-data
-# Salida esperada: uid=33(www-data) gid=33(www-data) groups=33(www-data),1000(sharedfiles)
-
-
-# 6. Salir del contenedor
-exit
-```
-
-#### Cómo Añadir Más Usuarios
-
-Si instalas nuevas aplicaciones que crean usuarios adicionales:
-
-```bash
-# Entrar al contenedor
-pct exec 101 -- bash
-
-
-# Buscar nuevos usuarios (UID >= 100)
-awk -F: '$3>=100 && $3<65534 {print $1 " (UID: " $3 ", GID: " $4 ")"}' /etc/passwd
-
-
-# Añadir al grupo sharedfiles
-usermod -aG sharedfiles nombre_usuario
-
-
-# Verificar
-groups nombre_usuario
-```
-
-#### Añadir TODOS los usuarios automáticamente
-
-```bash
-# Script para añadir todos los usuarios relevantes
-pct exec 101 -- bash -c '
-# Obtener todos los usuarios con UID >= 100 y < 65534 (excluyendo nobody)
-for user in $(awk -F: "$3>=100 && $3<65534 && $1!=\"nobody\" {print $1}" /etc/passwd); do
- echo "Añadiendo usuario: $user"
- usermod -aG sharedfiles "$user" 2>/dev/null || echo "Error añadiendo $user"
-done
-
-# Verificar usuarios añadidos
-echo "Usuarios en grupo sharedfiles:"
-getent group sharedfiles
-'
-```
-
----
-
-## 5. Montaje en Contenedores
-
-### Configuración del Montaje
-
-Para ambos tipos de contenedores, el montaje se configura igual, pero con consideraciones importantes:
-Por ejemplo:
-```bash
-# Para contenedor privilegiado (ID 100)
-pct set 100 -mp0 /mnt/shared_data,mp=/mnt/shared,backup=0,acl=1,shared=1
-
-
-# Para contenedor no privilegiado (ID 101)
-pct set 101 -mp0 /mnt/shared_data,mp=/mnt/shared,backup=0,acl=1,shared=1
-
-
-# Reiniciar contenedores para activar montajes
-pct reboot 100
-pct reboot 101
-
-```
-
-#### Explicación de Parámetros
-
-- **`/mnt/shared_data`**: Ruta en el HOST (donde está montado el recurso)
-- **`mp=/mnt/shared`**: Ruta en el CONTENEDOR (donde aparecerá el directorio)
-- **`backup=0`**: Excluir del backup de vzdump (recomendado para recursos de red)
-- **`acl=1`**: Habilitar soporte para ACL dentro del contenedor
-- **`shared=1`**: **CRUCIAL para clusters** - permite migración sin copiar datos
-
-#### ¿Por qué shared=1 es importante?
-
-Sin `shared=1`:
-- Proxmox intenta copiar todo el contenido durante migraciones
-- Falla si el directorio contiene muchos datos
-- No funciona con recursos de red
-
-Con `shared=1`:
-- Proxmox asume que el directorio está disponible en todos los nodos
-- Solo migra la configuración, no los datos
-- Funciona perfectamente con NFS, Samba, y almacenamiento compartido
-
-### Verificación del Montaje
-
-```bash
-# Verificar que el montaje está activo
-pct exec 100 -- df -h | grep shared
-pct exec 101 -- df -h | grep shared
-
-
-# Verificar permisos dentro de los contenedores
-pct exec 100 -- ls -ld /mnt/shared
-pct exec 101 -- ls -ld /mnt/shared
-
-
-# Verificar ACL dentro de los contenedores
-pct exec 100 -- getfacl /mnt/shared
-pct exec 101 -- getfacl /mnt/shared
-```
-
----
-
-## 6. Verificación y Pruebas
-
-### Prueba Básica de Escritura
-
-#### Desde el Host
-
-```bash
-# Crear archivo de prueba desde el host
-echo "Archivo creado desde HOST" > /mnt/shared_data/test_host.txt
-
-
-# Verificar propietario y permisos
-ls -l /mnt/shared_data/test_host.txt
-# Salida esperada: -rw-rw-r--+ 1 root sharedfiles 26 sep 8 12:00 test_host.txt
-```
-
-#### Desde Contenedor Privilegiado
-
-```bash
-# Crear archivo como root
-pct exec 100 -- bash -c 'echo "Archivo desde contenedor privilegiado (root)" > /mnt/shared/test_priv_root.txt'
-
-
-# Crear archivo como www-data
-pct exec 100 -- sudo -u www-data bash -c 'echo "Archivo desde contenedor privilegiado (www-data)" > /mnt/shared/test_priv_www.txt'
-
-
-# Verificar en el host
-ls -l /mnt/shared_data/test_priv_*
-# Salida esperada:
-# -rw-rw-r--+ 1 root sharedfiles 42 sep 8 12:01 test_priv_root.txt
-# -rw-rw-r--+ 1 www-data sharedfiles 48 sep 8 12:01 test_priv_www.txt
-```
-
-#### Desde Contenedor No Privilegiado
-
-```bash
-# Crear archivo como root del contenedor
-pct exec 101 -- bash -c 'echo "Archivo desde contenedor no privilegiado (root)" > /mnt/shared/test_unpriv_root.txt'
-
-
-# Crear archivo como www-data del contenedor
-pct exec 101 -- sudo -u www-data bash -c 'echo "Archivo desde contenedor no privilegiado (www-data)" > /mnt/shared/test_unpriv_www.txt'
-
-
-# Verificar en el host
-ls -l /mnt/shared_data/test_unpriv_*
-# Salida esperada:
-# -rw-rw-r--+ 1 100000 sharedfiles 46 sep 8 12:02 test_unpriv_root.txt
-# -rw-rw-r--+ 1 100033 sharedfiles 52 sep 8 12:02 test_unpriv_www.txt
-# ↑ UIDs mapeados (+100000)
-```
-
-### Prueba de Acceso Cruzado
-
-```bash
-# Desde contenedor privilegiado, leer archivo del no privilegiado
-pct exec 100 -- cat /mnt/shared/test_unpriv_root.txt
-# Salida: Archivo desde contenedor no privilegiado (root)
-
-
-# Desde contenedor no privilegiado, leer archivo del privilegiado
-pct exec 101 -- cat /mnt/shared/test_priv_root.txt
-# Salida: Archivo desde contenedor privilegiado (root)
-
-
-# Modificar archivo desde diferentes contenedores
-pct exec 100 -- bash -c 'echo "Modificado desde privilegiado" >> /mnt/shared/test_unpriv_root.txt'
-pct exec 101 -- bash -c 'echo "Modificado desde no privilegiado" >> /mnt/shared/test_priv_root.txt'
-
-
-# Verificar contenido
-cat /mnt/shared_data/test_unpriv_root.txt
-cat /mnt/shared_data/test_priv_root.txt
-```
-
-### Prueba de Herencia de Permisos
-
-```bash
-# Crear subdirectorio desde contenedor
-pct exec 100 -- mkdir /mnt/shared/subdir_test
-
-
-# Verificar que hereda setgid y grupo
-ls -ld /mnt/shared_data/subdir_test
-# Salida esperada: drwxrwsr-x+ 2 root sharedfiles 4096 sep 8 12:05 subdir_test
-# ↑ 's' indica setgid heredado
-
-
-# Crear archivo en subdirectorio
-pct exec 101 -- touch /mnt/shared/subdir_test/archivo_en_subdir.txt
-
-
-# Verificar herencia
-ls -l /mnt/shared_data/subdir_test/archivo_en_subdir.txt
-# Salida esperada: -rw-rw-r--+ 1 100000 sharedfiles 0 sep 8 12:06 archivo_en_subdir.txt
-```
-
-
----
-
-## 7. Solución de Problemas
-
-### Error: "Permission denied" al escribir
-
-#### Síntomas
-```bash
-pct exec 101 -- touch /mnt/shared/test.txt
-# touch: cannot touch '/mnt/shared/test.txt': Permission denied
-```
-
-#### Diagnóstico
-```bash
-# 1. Verificar permisos en el host
-ls -ld /mnt/shared_data
-getfacl /mnt/shared_data
-
-
-# 2. Verificar grupo en el contenedor
-pct exec 101 -- getent group sharedfiles
-
-
-# 3. Verificar membresía del usuario
-pct exec 101 -- groups www-data
-```
-
-#### Soluciones
-```bash
-# Solución 1: Recrear grupo en contenedor
-pct exec 101 -- groupadd -g 1000 sharedfiles
-pct exec 101 -- usermod -aG sharedfiles www-data
-
-
-# Solución 2: Reaplicar ACL en host
-setfacl -R -m g:sharedfiles:rwx /mnt/shared_data
-setfacl -d -m g:sharedfiles:rwx /mnt/shared_data
-
-
-# Solución 3: Verificar setgid
-chmod 2775 /mnt/shared_data
-```
-
-### Error: Archivos aparecen como "nobody:nogroup"
-
-#### Síntomas
-```bash
-pct exec 101 -- ls -l /mnt/shared/
-# -rw-r--r-- 1 nobody nogroup 100 sep 8 12:00 archivo.txt
-```
-
-#### Causa
-El contenedor no tiene un grupo con el GID del archivo.
-
-#### Solución
-```bash
-# 1. Ver GID numérico en el host
-ls -n /mnt/shared_data/archivo.txt
-# -rw-r--r-- 1 0 101000 100 sep 8 12:00 archivo.txt
-# ↑ GID 101000
-
-
-# 2. Crear grupo en contenedor con GID mapeado
-# GID 101000 en host = GID 1000 en contenedor no privilegiado
-pct exec 101 -- groupadd -g 1000 sharedfiles
-
-
-# 3. Verificar que ahora se ve correctamente
-pct exec 101 -- ls -l /mnt/shared/archivo.txt
-# -rw-r--r-- 1 nobody sharedfiles 100 sep 8 12:00 archivo.txt
-```
-
-### Error: "Transport endpoint is not connected" (NFS)
-
-#### Síntomas
-```bash
-ls /mnt/nfs_shared
-# ls: cannot access '/mnt/nfs_shared': Transport endpoint is not connected
-```
-
-#### Diagnóstico
-```bash
-# Verificar estado del montaje
-mount | grep nfs
-df -h | grep nfs
-
-
-# Verificar conectividad
-ping 192.168.1.100
-showmount -e 192.168.1.100
-```
-
-#### Soluciones
-```bash
-# Solución 1: Remontar
-umount /mnt/nfs_shared
-mount -t nfs 192.168.1.100:/export/shared /mnt/nfs_shared
-
-
-# Solución 2: Usar opciones más robustas
-mount -t nfs 192.168.1.100:/export/shared /mnt/nfs_shared \
- -o hard,intr,rsize=32768,wsize=32768,timeo=600,retrans=2
-
-
-# Solución 3: Verificar servicios NFS
-systemctl status nfs-common
-systemctl restart nfs-common
-```
-
-### Error: Contenedor no puede acceder después de migración
-
-#### Síntomas
-Después de migrar un contenedor a otro nodo, no puede acceder al directorio compartido.
-
-#### Causa
-El directorio compartido no está montado en el nodo destino, o falta `shared=1`.
-
-#### Solución
-```bash
-# 1. Verificar configuración del contenedor
-cat /etc/pve/lxc/101.conf | grep mp0
-# Debe incluir: shared=1
-
-
-# 2. Si falta shared=1, añadirlo
-pct set 101 -mp0 /mnt/shared_data,mp=/mnt/shared,backup=0,acl=1,shared=1
-
-
-# 3. Verificar que el directorio existe en el nodo destino
-ls -ld /mnt/shared_data
-
-
-# 4. Si no existe, montar el recurso en el nodo destino
-# (repetir pasos de montaje NFS/Samba según corresponda)
-```
-
-### Error: "Operation not supported" con ACL
-
-#### Síntomas
-```bash
-setfacl -m g:sharedfiles:rwx /mnt/shared_data
-# setfacl: /mnt/shared_data: Operation not supported
-```
-
-#### Causa
-El sistema de archivos no soporta ACL.
-
-#### Diagnóstico
-```bash
-# Verificar tipo de sistema de archivos
-df -T /mnt/shared_data
-
-
-# Verificar opciones de montaje
-mount | grep shared_data
-```
-
-#### Soluciones
-```bash
-# Para ext4: Remontar con soporte ACL
-mount -o remount,acl /mnt/shared_data
-
-
-# Para NFS: Añadir opción acl
-umount /mnt/shared_data
-mount -t nfs 192.168.1.100:/export/shared /mnt/shared_data -o acl
-
-
-# Para sistemas de archivos que no soportan ACL:
-# Usar solo permisos tradicionales con setgid
-chmod 2775 /mnt/shared_data
-```
-
-### Archivos creados con permisos incorrectos
-
-#### Síntomas
-Los archivos se crean con permisos 644 en lugar de 664.
-
-#### Causa
-La umask del proceso no permite escritura de grupo.
-
-#### Solución
-```bash
-# Verificar umask actual
-pct exec 101 -- umask
-# Si es 022, cambiar a 002
-
-
-# Cambiar umask temporalmente
-pct exec 101 -- umask 002
-
-
-# Cambiar umask permanentemente
-pct exec 101 -- bash -c 'echo "umask 002" >> /etc/profile'
-
-
-# Alternativa: Usar ACL por defecto (más robusta)
-setfacl -d -m g:sharedfiles:rwx /mnt/shared_data
-setfacl -d -m o::r-x /mnt/shared_data
-```
-
----
-
-## 📋 Resumen de Comandos Clave
-
-### Configuración del Host
-
-```bash
-# Crear directorio local
-mkdir -p /mnt/shared_data
-groupadd -g 101000 sharedfiles
-chown root:sharedfiles /mnt/shared_data
-chmod 2775 /mnt/shared_data
-setfacl -R -m g:sharedfiles:rwx /mnt/shared_data
-setfacl -d -m g:sharedfiles:rwx /mnt/shared_data
-
-
-# Montar NFS persistente
-echo "192.168.1.100:/export/shared /mnt/nfs_shared nfs rw,hard,nofail,_netdev 0 0" >> /etc/fstab
-
-
-# Montar Samba persistente
-echo "//192.168.1.200/share /mnt/samba_shared cifs credentials=/etc/cifs-creds,uid=root,gid=101000,file_mode=0664,dir_mode=2775,_netdev,nofail 0 0" >> /etc/fstab
-```
-
-### Configuración de Contenedores
-
-```bash
-# Contenedor privilegiado
-pct exec 100 -- groupadd -g 101000 sharedfiles
-pct exec 100 -- usermod -aG sharedfiles root
-pct exec 100 -- usermod -aG sharedfiles www-data
-
-
-# Contenedor no privilegiado
-pct exec 101 -- groupadd -g 1000 sharedfiles
-pct exec 101 -- usermod -aG sharedfiles root
-pct exec 101 -- usermod -aG sharedfiles www-data
-
-
-# Montaje
-pct set 100 -mp0 /mnt/shared_data,mp=/mnt/shared,backup=0,acl=1,shared=1
-pct set 101 -mp0 /mnt/shared_data,mp=/mnt/shared,backup=0,acl=1,shared=1
-```
-
-### Verificación
-
-```bash
-# Verificar configuración
-ls -ld /mnt/shared_data
-getfacl /mnt/shared_data
-pct exec 101 -- groups www-data
-
-
-# Prueba de escritura
-echo "test" > /mnt/shared_data/test.txt
-pct exec 100 -- touch /mnt/shared/test_priv.txt
-pct exec 101 -- touch /mnt/shared/test_unpriv.txt
-```
-
----
-
-## 🎯 Conclusión
-
-Esta guía te ha mostrado cómo configurar correctamente recursos compartidos entre Proxmox y contenedores LXC, tanto privilegiados como no privilegiados. Los conceptos clave son:
-
-1. **Grupo común** (`sharedfiles`) con GID consistente
-2. **Setgid** (2775) para herencia automática de grupo
-3. **ACL** para garantizar permisos robustos
-4. **Mapeo correcto** de UIDs/GIDs en contenedores no privilegiados
-5. **Configuración adecuada** de montajes con `shared=1`, `backup=0`, `acl=1`
-
-Con esta configuración, tendrás un sistema robusto que funciona con NFS, Samba, directorios locales, y es compatible con clusters y migraciones de Proxmox.
-
-
diff --git a/scripts/storage/notes.txt b/scripts/storage/notes.txt
deleted file mode 100644
index 2216b1ad..00000000
Binary files a/scripts/storage/notes.txt and /dev/null differ
diff --git a/scripts/vm/synology_.sh b/scripts/vm/synology_.sh
deleted file mode 100644
index e49717dd..00000000
--- a/scripts/vm/synology_.sh
+++ /dev/null
@@ -1,1192 +0,0 @@
-#!/usr/bin/env bash
-
-# ==========================================================
-# ProxMenuX - Synology DSM VM Creator Script
-# ==========================================================
-# Author : MacRimi
-# Copyright : (c) 2024 MacRimi
-# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
-# Version : 1.0
-# Last Updated: 13/03/2025
-# ==========================================================
-# Description:
-# This script automates the creation and configuration of a Synology DSM
-# (DiskStation Manager) virtual machine (VM) in Proxmox VE. It simplifies the
-# setup process by allowing both default and advanced configuration options.
-#
-# The script automates the complete VM creation process, including loader
-# download, disk configuration, and VM boot setup.
-#
-# **Credits**
-# This script is an original idea but incorporates ideas and elements from
-# a similar script by user **tim104979** from the ProxmoxVE branch:
-# (https://raw.githubusercontent.com/tim104979/ProxmoxVE/refs/heads/main/vm/synology-vm.sh)
-#
-# Copyright (c) Proxmox VE Helper-Scripts Community
-# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
-#
-# ==========================================================
-
-
-# Configuration ============================================
-LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
-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
-# ==========================================================
-
-GEN_MAC="02"
-for i in {1..5}; do
- BYTE=$(printf "%02X" $((RANDOM % 256)))
- GEN_MAC="${GEN_MAC}:${BYTE}"
-done
-
-NEXTID=$(pvesh get /cluster/nextid 2>/dev/null || echo "100")
-NAME="Synology VM"
-IMAGES_DIR="/var/lib/vz/template/iso"
-ERROR_FLAG=false
-
-
-
-
-
-function exit_script() {
- clear
- if whiptail --backtitle "ProxMenuX" --title "$NAME" --yesno "$(translate "This will create a New $NAME. Proceed?")" 10 58; then
- start_script
- else
- clear
- exit
- fi
-}
-
-
-# Define the header_info function at the beginning of the script
-
-function header_info() {
- clear
- show_proxmenux_logo
- echo -e "${BL}╔═══════════════════════════════════════════════╗${CL}"
- echo -e "${BL}║ ║${CL}"
- echo -e "${BL}║${YWB} Synology VM Creator ${BL}║${CL}"
- echo -e "${BL}║ ║${CL}"
- echo -e "${BL}╚═══════════════════════════════════════════════╝${CL}"
- echo -e
-}
-# ==========================================================
-
-
-
-
-
-
-# ==========================================================
-# start Script
-# ==========================================================
-function start_script() {
- if (whiptail --backtitle "ProxMenuX" --title "SETTINGS" --yesno "$(translate "Use Default Settings?")" --no-button Advanced 10 58); then
- header_info
- echo -e "${DEF}Using Default Settings${CL}"
- default_settings
- else
- header_info
- echo -e "${CUS}Using Advanced Settings${CL}"
- advanced_settings
- fi
-}
-# ==========================================================
-
-
-
-
-# ==========================================================
-# Default Settings
-# ==========================================================
-function default_settings() {
- VMID="$NEXTID"
- FORMAT=""
- MACHINE=" -machine q35"
- BIOS_TYPE=" -bios ovmf"
- DISK_CACHE=""
- HN="Synology-DSM"
- CPU_TYPE=" -cpu host"
- CORE_COUNT="2"
- RAM_SIZE="4096"
- BRG="vmbr0"
- MAC="$GEN_MAC"
- VLAN=""
- MTU=""
- SERIAL_PORT="socket"
- START_VM="no"
-
- echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}"
- echo -e "${DGN}Using Machine Type: ${BGN}q35${CL}"
- echo -e "${DGN}Using BIOS Type: ${BGN}OVMF (UEFI)${CL}"
- echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}"
- echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}"
- echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}"
- echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}"
- echo -e "${DGN}Using Bridge: ${BGN}${BRG}${CL}"
- echo -e "${DGN}Using MAC Address: ${BGN}${MAC}${CL}"
- echo -e "${DGN}Using VLAN: ${BGN}Default${CL}"
- echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}"
- echo -e "${DGN}Configuring Serial Port: ${BGN}${SERIAL_PORT}${CL}"
- echo -e "${DGN}Start VM when completed: ${BGN}${START_VM}${CL}"
- echo -e
- echo -e "${DEF}Creating a $NAME using the above default settings${CL}"
-
- sleep 1
- select_disk_type
-}
-# ==========================================================
-
-
-
-
-
-# ==========================================================
-# advanced Settings
-# ==========================================================
-function advanced_settings() {
- # VM ID Selection
- while true; do
- if VMID=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "Set Virtual Machine ID")" 8 58 $NEXTID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
- if [ -z "$VMID" ]; then
- VMID="$NEXTID"
- fi
- if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then
- echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"
- sleep 1
- continue
- fi
- echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
- break
- else
- exit_script
- fi
- done
-
- # Machine Type Selection
- if MACH=$(whiptail --backtitle "ProxMenuX" --title "$(translate "MACHINE TYPE")" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
- "q35" "Machine q35" ON \
- "i440fx" "Machine i440fx" OFF \
- 3>&1 1>&2 2>&3); then
- if [ $MACH = q35 ]; then
- echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}"
- FORMAT=""
- MACHINE=" -machine q35"
- else
- echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}"
- FORMAT=",efitype=4m"
- MACHINE=""
- fi
- else
- exit_script
- fi
-
- # BIOS Type Selection
- if BIOS=$(whiptail --backtitle "ProxMenuX" --title "$(translate "BIOS TYPE")" --radiolist --cancel-button Exit-Script "Choose BIOS Type" 10 58 2 \
- "ovmf" "UEFI (OVMF)" ON \
- "seabios" "SeaBIOS (Legacy)" OFF \
- 3>&1 1>&2 2>&3); then
- if [ "$BIOS" = "seabios" ]; then
- echo -e "${DGN}Using BIOS Type: ${BGN}SeaBIOS${CL}"
- BIOS_TYPE=" -bios seabios"
- else
- echo -e "${DGN}Using BIOS Type: ${BGN}OVMF (UEFI)${CL}"
- BIOS_TYPE=" -bios ovmf"
- fi
- else
- exit_script
- fi
-
- # Hostname Selection
- if VM_NAME=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "Set Hostname")" 8 58 Synology-DSM --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
- if [ -z $VM_NAME ]; then
- HN="Synology-DSM"
- echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}"
- else
- HN=$(echo ${VM_NAME,,} | tr -d ' ')
- echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}"
- fi
- else
- exit_script
- fi
-
- # CPU Type Selection
- if CPU_TYPE1=$(whiptail --backtitle "ProxMenuX" --title "$(translate "CPU MODEL")" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
- "1" "Host" ON \
- "0" "KVM64" OFF \
- 3>&1 1>&2 2>&3); then
- if [ $CPU_TYPE1 = "1" ]; then
- echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}"
- CPU_TYPE=" -cpu host"
- else
- echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}"
- CPU_TYPE=""
- fi
- else
- exit_script
- fi
-
- # Core Count Selection
- if CORE_COUNT=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "Allocate CPU Cores")" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
- if [ -z $CORE_COUNT ]; then
- CORE_COUNT="2"
- echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}"
- else
- echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}"
- fi
- else
- exit_script
- fi
-
- # RAM Size Selection
- if RAM_SIZE=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "Allocate RAM in MiB")" 8 58 4096 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
- if [ -z $RAM_SIZE ]; then
- RAM_SIZE="4096"
- echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}"
- else
- echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}"
- fi
- else
- exit_script
- fi
-
- # Bridge Selection
- if BRG=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "Set a Bridge")" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
- if [ -z $BRG ]; then
- BRG="vmbr0"
- echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}"
- else
- echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}"
- fi
- else
- exit_script
- fi
-
- # MAC Address Selection
- if MAC1=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "Set a MAC Address")" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
- if [ -z $MAC1 ]; then
- MAC="$GEN_MAC"
- echo -e "${DGN}Using MAC Address: ${BGN}$MAC${CL}"
- else
- MAC="$MAC1"
- echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}"
- fi
- else
- exit_script
- fi
-
- # VLAN Selection
- if VLAN1=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "Set a Vlan(leave blank for default)")" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
- if [ -z $VLAN1 ]; then
- VLAN1="Default"
- VLAN=""
- echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}"
- else
- VLAN=",tag=$VLAN1"
- echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}"
- fi
- else
- exit_script
- fi
-
- # MTU Selection
- if MTU1=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "Set Interface MTU Size (leave blank for default)")" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
- if [ -z $MTU1 ]; then
- MTU1="Default"
- MTU=""
- echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}"
- else
- MTU=",mtu=$MTU1"
- echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}"
- fi
- else
- exit_script
- fi
-
-
-
- # Confirmation
- if (whiptail --backtitle "ProxMenuX" --title "$(translate "ADVANCED SETTINGS COMPLETE")" --yesno "Ready to create a $NAME?" --no-button Do-Over 10 58); then
- echo -e
- echo -e "${CUS}Creating a $NAME using the above advanced settings${CL}"
- sleep 1
- select_disk_type
- else
- header_info
- sleep 1
- echo -e "${CUS}Using Advanced Settings${CL}"
- advanced_settings
- fi
-}
-# ==========================================================
-
-
-
-
-
-# ==========================================================
-# Select Disk
-# ==========================================================
-function select_disk_type() {
-
- DISK_TYPE=$(whiptail --backtitle "ProxMenuX" --title "DISK TYPE" --menu "$(translate "Choose disk type:")" 12 58 2 \
- "virtual" "$(translate "Create virtual disk")" \
- "passthrough" "$(translate "Use physical disk passthrough")" \
- --ok-button "Select" --cancel-button "Cancel" 3>&1 1>&2 2>&3)
-
- EXIT_STATUS=$?
-
- if [[ $EXIT_STATUS -ne 0 ]]; then
- clear
- header_info
- msg_error "Operation cancelled by user. Returning to start scrip..."
- sleep 2
- if whiptail --backtitle "ProxMenuX" --title "$NAME" --yesno "$(translate "This will create a New $NAME. Proceed?")" 10 58; then
- start_script
- else
- clear
- exit
- fi
- fi
-
- if [[ "$DISK_TYPE" == "virtual" ]]; then
- select_virtual_disk
- else
- select_passthrough_disk
- fi
-}
-
-# ==========================================================
-
-
-
-
-
-# ==========================================================
-# Select Virtual Disks
-# ==========================================================
-function select_virtual_disk() {
-
- VIRTUAL_DISKS=()
-
- # Loop to add multiple disks
- local add_more_disks=true
- while $add_more_disks; do
-
- msg_info "Detecting available storage volumes..."
-
- # Get list of available storage
- STORAGE_MENU=()
- while read -r line; do
- TAG=$(echo $line | awk '{print $1}')
- TYPE=$(echo $line | awk '{print $2}')
- FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format "%.2f" | awk '{printf( "%9sB", $6)}')
- ITEM=$(printf "%-15s %-10s %-15s" "$TAG" "$TYPE" "$FREE")
- STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
- done < <(pvesm status -content images | awk 'NR>1')
-
- # Check that storage is available
- VALID=$(pvesm status -content images | awk 'NR>1')
- if [ -z "$VALID" ]; then
- msg_error "Unable to detect a valid storage location."
- sleep 2
- select_disk_type
- fi
-
-
- # Select storage
- if [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
- STORAGE=${STORAGE_MENU[0]}
- msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
- else
-
- kill $SPINNER_PID > /dev/null
- STORAGE=$(whiptail --backtitle "ProxMenuX" --title "$(translate "Select Storage Volume")" --radiolist \
- "$(translate "Choose the storage volume for the virtual disk:\n")" 20 78 10 \
- "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
-
- if [ $? -ne 0 ] || [ -z "$STORAGE" ]; then
- if [ ${#VIRTUAL_DISKS[@]} -eq 0 ]; then
- msg_error "No storage selected. At least one disk is required."
- select_disk_type
- else
- add_more_disks=false
- continue
- fi
- fi
-
-
- fi
-
- # Request disk size
- DISK_SIZE=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "System Disk Size (GB)")" 8 58 32 --title "VIRTUAL DISK" --cancel-button Cancel 3>&1 1>&2 2>&3)
-
- if [ $? -ne 0 ]; then
- if [ ${#VIRTUAL_DISKS[@]} -eq 0 ]; then
- msg_error "Disk size not specified. At least one disk is required."
- sleep 2
- select_disk_type
-
- else
- add_more_disks=false
- continue
- fi
- fi
-
- if [ -z "$DISK_SIZE" ]; then
- DISK_SIZE="32"
- fi
-
- # Store the configuration in the disk list
- VIRTUAL_DISKS+=("${STORAGE}:${DISK_SIZE}")
-
-
- # Ask if you want to create another disk
- if ! whiptail --backtitle "ProxMenuX" --title "$(translate "Add Another Disk")" \
- --yesno "$(translate "Do you want to add another virtual disk?")" 8 58; then
- add_more_disks=false
- fi
- done
-
- # Show summary of the created disks
- if [ ${#VIRTUAL_DISKS[@]} -gt 0 ]; then
-
- msg_ok "Virtual Disks Created:"
- for i in "${!VIRTUAL_DISKS[@]}"; do
- echo -e "${TAB}${BL}- Disk $((i+1)): ${VIRTUAL_DISKS[$i]}GB${CL}"
- done
- fi
-
-
- export VIRTUAL_DISKS
-
-
- select_loader
-}
-
-# ==========================================================
-
-
-
-
-
-
-# ==========================================================
-# Select Physical Disks
-# ==========================================================
-function select_passthrough_disk() {
-
- msg_info "$(translate "Detecting available disks...")"
-
- FREE_DISKS=()
-
- USED_DISKS=$(lsblk -n -o PKNAME,TYPE | grep 'lvm' | awk '{print "/dev/" $1}')
- MOUNTED_DISKS=$(lsblk -ln -o NAME,MOUNTPOINT | awk '$2!="" {print "/dev/" $1}')
-
- ZFS_DISKS=""
- ZFS_RAW=$(zpool list -v -H 2>/dev/null | awk '{print $1}' | grep -v '^NAME$' | grep -v '^-' | grep -v '^mirror')
-
- for entry in $ZFS_RAW; do
- path=""
- if [[ "$entry" == wwn-* || "$entry" == ata-* ]]; then
- if [ -e "/dev/disk/by-id/$entry" ]; then
- path=$(readlink -f "/dev/disk/by-id/$entry")
- fi
- elif [[ "$entry" == /dev/* ]]; then
- path="$entry"
- fi
-
- if [ -n "$path" ]; then
- base_disk=$(lsblk -no PKNAME "$path" 2>/dev/null)
- if [ -n "$base_disk" ]; then
- ZFS_DISKS+="/dev/$base_disk"$'\n'
- fi
- fi
- done
-
- ZFS_DISKS=$(echo "$ZFS_DISKS" | sort -u)
- LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -n1 readlink -f | sort -u)
-
- 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
- [[ "$DISK" =~ /dev/zd ]] && continue
-
- INFO=($(lsblk -dn -o MODEL,SIZE "$DISK"))
- MODEL="${INFO[@]::${#INFO[@]}-1}"
- SIZE="${INFO[-1]}"
- LABEL=""
- SHOW_DISK=true
-
- IS_MOUNTED=false
- IS_RAID=false
- IS_ZFS=false
- IS_LVM=false
-
- while read -r part fstype; do
- [[ "$fstype" == "zfs_member" ]] && IS_ZFS=true
- [[ "$fstype" == "linux_raid_member" ]] && IS_RAID=true
- [[ "$fstype" == "LVM2_member" ]] && IS_LVM=true
- if grep -q "/dev/$part" <<< "$MOUNTED_DISKS"; then
- IS_MOUNTED=true
- fi
- done < <(lsblk -ln -o NAME,FSTYPE "$DISK" | tail -n +2)
-
- REAL_PATH=$(readlink -f "$DISK")
- if echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then
- IS_MOUNTED=true
- fi
-
- USED_BY=""
- REAL_PATH=$(readlink -f "$DISK")
- CONFIG_DATA=$(cat /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf 2>/dev/null)
-
- if grep -Fq "$REAL_PATH" <<< "$CONFIG_DATA"; then
- USED_BY="⚠ $(translate "In use")"
- else
- for SYMLINK in /dev/disk/by-id/*; do
- if [[ "$(readlink -f "$SYMLINK")" == "$REAL_PATH" ]]; then
- if grep -Fq "$SYMLINK" <<< "$CONFIG_DATA"; then
- USED_BY="⚠ $(translate "In use")"
- break
- fi
- fi
- done
- fi
-
- if $IS_RAID && grep -q "$DISK" <<< "$(cat /proc/mdstat)" && grep -q "active raid" /proc/mdstat; then
- SHOW_DISK=false
- fi
-
- if $IS_ZFS || $IS_MOUNTED || [[ "$ZFS_DISKS" == *"$DISK"* ]]; then
- SHOW_DISK=false
- fi
-
- if $SHOW_DISK; then
- [[ -n "$USED_BY" ]] && LABEL+=" [$USED_BY]"
- [[ "$IS_RAID" == true ]] && LABEL+=" ⚠ RAID"
- [[ "$IS_LVM" == true ]] && LABEL+=" ⚠ LVM"
- [[ "$IS_ZFS" == true ]] && LABEL+=" ⚠ ZFS"
- DESCRIPTION=$(printf "%-30s %10s%s" "$MODEL" "$SIZE" "$LABEL")
- FREE_DISKS+=("$DISK" "$DESCRIPTION" "OFF")
- fi
- done < <(lsblk -dn -e 7,11 -o PATH)
-
-
- if [ "${#FREE_DISKS[@]}" -eq 0 ]; then
- cleanup
- whiptail --title "Error" --msgbox "$(translate "No disks available for this VM.")" 8 40
- select_disk_type
- return
- fi
-
- MAX_WIDTH=$(printf "%s\n" "${FREE_DISKS[@]}" | awk '{print length}' | sort -nr | head -n1)
- TOTAL_WIDTH=$((MAX_WIDTH + 20))
- [ $TOTAL_WIDTH -lt 50 ] && TOTAL_WIDTH=50
- cleanup
- SELECTED_DISKS=$(whiptail --title "Select Disks" --checklist \
- "$(translate "Select the disks you want to use (use spacebar to select):")" 20 $TOTAL_WIDTH 10 \
- "${FREE_DISKS[@]}" 3>&1 1>&2 2>&3)
-
- if [ -z "$SELECTED_DISKS" ]; then
- msg_error "Disk not specified. At least one disk is required."
- sleep 2
- select_disk_type
- return
- fi
-
-
- msg_ok "Disk passthrough selected:"
- PASSTHROUGH_DISKS=()
- for DISK in $(echo "$SELECTED_DISKS" | tr -d '"'); do
- DISK_INFO=$(lsblk -ndo MODEL,SIZE "$DISK" | xargs)
- echo -e "${TAB}${CL}${BL}- $DISK $DISK_INFO${GN}${CL}"
- PASSTHROUGH_DISKS+=("$DISK")
- done
-
-
- select_loader
-}
-# ==========================================================
-
-
-
-
-
-
-# ==========================================================
-# Select Loader
-# ==========================================================
-function select_loader() {
- # Ensure the images directory exists
- if [ ! -d "$IMAGES_DIR" ]; then
- msg_info "Creating images directory"
- mkdir -p "$IMAGES_DIR"
- chmod 755 "$IMAGES_DIR"
- msg_ok "Images directory created: $IMAGES_DIR"
- fi
-
- # Create the loader selection menu
- LOADER_OPTION=$(whiptail --backtitle "ProxMenuX" --title "SELECT LOADER" --menu "$(translate "Choose a loader for Synology DSM:")" 15 70 4 \
- "1" "AuxXxilium Arc Loader" \
- "2" "RedPill Loader (RROrg - RR)" \
- "3" "TinyCore RedPill Loader (PeterSuh-Q3 M-shell)" \
- "4" "Custom Loader Image (from $IMAGES_DIR)" \
- 3>&1 1>&2 2>&3)
-
- if [ -z "$LOADER_OPTION" ]; then
- exit_script
- fi
-
- case $LOADER_OPTION in
- 1)
- LOADER_TYPE="arc"
- LOADER_NAME="AuxXxilium Arc"
- LOADER_URL="https://github.com/AuxXxilium/arc/"
- echo -e "${DGN}${TAB}Selected Loader: ${BGN}$LOADER_NAME${CL}"
- download_loader
- ;;
- 2)
- LOADER_TYPE="redpill"
- LOADER_NAME="RedPill RR"
- LOADER_URL="https://github.com/RROrg/rr/"
- echo -e "${DGN}${TAB}Selected Loader: ${BGN}$LOADER_NAME${CL}"
- download_loader
- ;;
- 3)
- LOADER_TYPE="tinycore"
- LOADER_NAME="TinyCore RedPill M-shell"
- LOADER_URL="https://github.com/PeterSuh-Q3/tinycore-redpill/"
- echo -e "${DGN}${TAB}Selected Loader: ${BGN}$LOADER_NAME${CL}"
- download_loader
- ;;
- 4)
- LOADER_TYPE="custom"
- LOADER_NAME="Custom Image"
- LOADER_URL="https://xpenology.com/forum/"
- echo -e "${DGN}${TAB}Selected Loader: ${BGN}$LOADER_NAME${CL}"
- select_custom_image
- ;;
- esac
-}
-
-function select_custom_image() {
- # Check if there are any images in the directory
- IMAGES=$(find "$IMAGES_DIR" -type f -name "*.img" -o -name "*.iso" -o -name "*.qcow2" -o -name "*.vmdk" | sort)
-
- if [ -z "$IMAGES" ]; then
- whiptail --title "$(translate "No Images Found")" --msgbox "No compatible images found in $IMAGES_DIR\n\nSupported formats: .img, .iso, .qcow2, .vmdk\n\nPlease add some images and try again." 15 70
- select_loader
- fi
-
- # Create an array of image options for whiptail
- IMAGE_OPTIONS=()
-
- while read -r img; do
- filename=$(basename "$img")
- filesize=$(du -h "$img" | cut -f1)
- IMAGE_OPTIONS+=("$img" "$filesize")
- done <<< "$IMAGES"
-
- # Let the user select an image
- LOADER_FILE=$(whiptail --backtitle "ProxMenuX" --title "SELECT CUSTOM IMAGE" --menu "$(translate "Choose a custom image:")" 20 70 10 "${IMAGE_OPTIONS[@]}" 3>&1 1>&2 2>&3)
-
- if [ -z "$LOADER_FILE" ]; then
- msg_error "No custom image selected"
- exit_script
- fi
-
- echo -e "${DGN}${TAB}Using Custom Image: ${BGN}$(basename "$LOADER_FILE")${CL}"
- FILE=$(basename "$LOADER_FILE")
-}
-# ==========================================================
-
-
-
-
-
-
-
-# ==========================================================
-# Download Loader
-# ==========================================================
-function download_loader() {
-
- echo -e "${DGN}${TAB}Retrieving the URL for the ${BGN}$LOADER_NAME loader${CL}"
-
- if [[ "$LOADER_TYPE" == "arc" || "$LOADER_TYPE" == "redpill" ]] && ! command -v unzip &> /dev/null; then
- msg_info "Installing unzip..."
- apt-get update -qq && apt-get install -y unzip -qq >/dev/null 2>&1
- if ! command -v unzip &> /dev/null; then
- msg_error "Failed to install unzip"
- sleep 2
- return 1
- fi
- msg_ok "Installed unzip successfully."
- fi
-
- case $LOADER_TYPE in
- arc)
- curl -s https://api.github.com/repos/AuxXxilium/arc/releases/latest \
- | grep "browser_download_url.*\.img\.zip" \
- | cut -d '"' -f 4 \
- | xargs wget -q --show-progress -O "$IMAGES_DIR/arc.img.zip"
-
- if [ -f "$IMAGES_DIR/arc.img.zip" ]; then
- cd "$IMAGES_DIR"
- unzip -q arc.img.zip
- rm arc.img.zip
- FILE="arc.img"
- LOADER_FILE="$IMAGES_DIR/$FILE"
- cd - > /dev/null
- else
- msg_error "Failed to download $LOADER_NAME loader"
- sleep 1
- select_loader
- fi
- ;;
-
- redpill)
- curl -s https://api.github.com/repos/RROrg/rr/releases/latest \
- | grep "browser_download_url.*\.img\.zip" \
- | cut -d '"' -f 4 \
- | xargs wget -q --show-progress -O "$IMAGES_DIR/rr.img.zip"
-
- if [ -f "$IMAGES_DIR/rr.img.zip" ]; then
- cd "$IMAGES_DIR"
- msg_info "Unzipping $LOADER_NAME loader. Please wait..."
- unzip -qo rr.img.zip
- msg_ok "Unzipped $LOADER_NAME loader successfully."
- rm -f rr.img.zip
- FILE="rr.img"
- LOADER_FILE="$IMAGES_DIR/$FILE"
- cd - > /dev/null
- fi
-
- ;;
-
- tinycore)
- curl -s https://api.github.com/repos/PeterSuh-Q3/tinycore-redpill/releases/latest \
- | grep "browser_download_url.*tinycore-redpill.v.*img.gz" \
- | cut -d '"' -f 4 \
- | xargs wget -q --show-progress -O "$IMAGES_DIR/tinycore.img.gz"
-
- if [ -f "$IMAGES_DIR/tinycore.img.gz" ]; then
- cd "$IMAGES_DIR"
-
- msg_info "Unzipping $LOADER_NAME loader. Please wait..."
- gunzip -f tinycore.img.gz 2> /dev/null
- msg_ok "Unzipped $LOADER_NAME loader successfully."
- FILE="tinycore.img"
- LOADER_FILE="$IMAGES_DIR/$FILE"
- cd - > /dev/null
-
- else
- msg_error "Failed to download $LOADER_NAME loader"
- sleep 1
- select_loader
-
- fi
- ;;
- esac
-
- msg_ok "Downloaded ${CL}${BL}${FILE}${CL} to ${IMAGES_DIR}"
-}
-# =======================================================
-
-
-
-
-
-# ==========================================================
-# Select UEFI Storage
-# ==========================================================
-function select_efi_storage() {
- local vmid=$1
- local STORAGE=""
-
- STORAGE_MENU=()
-
- while read -r line; do
- TAG=$(echo $line | awk '{print $1}')
- TYPE=$(echo $line | awk '{printf "%-10s", $2}')
- FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format "%.2f" | awk '{printf( "%9sB", $6)}')
-
- ITEM=" Type: $TYPE Free: $FREE"
- OFFSET=2
- if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
- MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
- fi
-
- STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
- done < <(pvesm status -content images | awk 'NR>1')
-
- VALID=$(pvesm status -content images | awk 'NR>1')
- if [ -z "$VALID" ]; then
- msg_error "Unable to detect a valid storage location for EFI disk."
-
- elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
- STORAGE=${STORAGE_MENU[0]}
-
- else
- kill $SPINNER_PID > /dev/null
- while [ -z "${STORAGE:+x}" ]; do
- STORAGE=$(whiptail --backtitle "ProxMenuX" --title "EFI Disk Storage" --radiolist \
- "$(translate "Choose the storage volume for the EFI disk (4MB):\n\nUse Spacebar to select.")" \
- 16 $(($MSG_MAX_LENGTH + 23)) 6 \
- "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit
-
- done
-
- fi
-
- echo "$STORAGE"
-}
-# ==========================================================
-
-
-
-
-
-# ==========================================================
-# Select Storage Loader
-# ==========================================================
-function select_storage_volume() {
- local vmid=$1
- local purpose=$2
- local STORAGE=""
-
- STORAGE_MENU=()
-
- while read -r line; do
- TAG=$(echo $line | awk '{print $1}')
- TYPE=$(echo $line | awk '{printf "%-10s", $2}')
- FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format "%.2f" | awk '{printf( "%9sB", $6)}')
-
- ITEM=" Type: $TYPE Free: $FREE"
- OFFSET=2
- if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then
- MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET))
- fi
-
- STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
- done < <(pvesm status -content images | awk 'NR>1')
-
- VALID=$(pvesm status -content images | awk 'NR>1')
- if [ -z "$VALID" ]; then
- msg_error "Unable to detect a valid storage location."
- exit 1
- elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
- STORAGE=${STORAGE_MENU[0]}
- else
- while [ -z "${STORAGE:+x}" ]; do
- STORAGE=$(whiptail --backtitle "ProxMenuX" --title "Storage Pools" --radiolist \
- "$(translate "Choose the storage volume for $purpose:\n\nUse Spacebar to select.")" \
- 16 $(($MSG_MAX_LENGTH + 23)) 6 \
- "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit
- done
- fi
-
- echo "$STORAGE"
-}
-
-
-
-
-
-
-# ==========================================================
-# Create VM
-# ==========================================================
-function create_vm() {
-
- # Create the VM
- qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1${BIOS_TYPE}${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \
- -name $HN -tags proxmenux -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci \
- -serial0 socket
- msg_ok "Create a $NAME"
-
-
-
-# Check if UEFI (OVMF) is being used ===================
- if [[ "$BIOS_TYPE" == *"ovmf"* ]]; then
-
- msg_info "Configuring EFI disk"
- EFI_STORAGE=$(select_efi_storage $VMID)
- EFI_DISK_NAME="vm-${VMID}-disk-efivars"
-
- # Determine storage type and extension
- STORAGE_TYPE=$(pvesm status -storage $EFI_STORAGE | awk 'NR>1 {print $2}')
- case $STORAGE_TYPE in
- nfs | dir)
- EFI_DISK_EXT=".raw"
- EFI_DISK_REF="$VMID/"
- ;;
- *)
- EFI_DISK_EXT=""
- EFI_DISK_REF=""
- ;;
- esac
-
- if pvesm alloc "$EFI_STORAGE" "$VMID" "$EFI_DISK_NAME$EFI_DISK_EXT" 4M >/dev/null 2>&1; then
- if qm set "$VMID" -efidisk0 "$EFI_STORAGE:${EFI_DISK_REF}$EFI_DISK_NAME$EFI_DISK_EXT,pre-enrolled-keys=0" >/dev/null 2>&1; then
- msg_ok "EFI disk created and configured on ${CL}${BL}$EFI_STORAGE${GN}${CL}"
- else
- msg_error "Failed to configure EFI disk"
- ERROR_FLAG=true
- fi
- else
- msg_error "Failed to create EFI disk"
- ERROR_FLAG=true
- fi
-
- fi
-# ==========================================================
-
-
-# Select storage volume for loader =======================
-
- LOADER_STORAGE=$(select_storage_volume $VMID "loader disk")
-
-
- #Run the command in the background and capture its PID
- qm importdisk $VMID ${LOADER_FILE} $LOADER_STORAGE > /tmp/import_log_$VMID.txt 2>&1 &
- import_pid=$!
-
- # Show a simple progress indicator
- echo -n "Importing loader disk: "
- while kill -0 $import_pid 2>/dev/null; do
- echo -n "."
- sleep 2.5
- done
-
- wait $import_pid
- rm -f /tmp/import_log_$VMID.txt
-
- IMPORTED_DISK=$(qm config $VMID | grep -E 'unused[0-9]+' | tail -1 | cut -d: -f1)
-
- # If the disk was not imported correctly, show an error message but continue
- if [ -z "$IMPORTED_DISK" ]; then
- msg_error "Loader import failed. No disk detected."
- ERROR_FLAG=true
- else
- msg_ok "Loader imported successfully to ${CL}${BL}$LOADER_STORAGE${GN}${CL}"
- fi
-
- # Configure the loader disk as scsi0
- DISK_NAME="vm-${VMID}-disk-0"
-
- result=$(qm set "$VMID" -ide0 "${LOADER_STORAGE}:${DISK_NAME}" 2>&1 > /dev/null)
- if [[ $? -eq 0 ]]; then
- msg_ok "Configured loader disk as ide0"
- else
- ERROR_FLAG=true
- fi
- result=$(qm set "$VMID" -boot order=ide0 2>&1)
- if [[ $? -eq 0 ]]; then
- msg_ok "Loader configured as boot device."
- else
- ERROR_FLAG=true
- fi
-
-# ==========================================================
-
-if [ "$DISK_TYPE" = "virtual" ]; then
- if [ ${#VIRTUAL_DISKS[@]} -eq 0 ]; then
- msg_error "No virtual disks configured."
- exit_script
- fi
-
- DISK_INFO=""
- CONSOLE_DISK_INFO=""
-
- for i in "${!VIRTUAL_DISKS[@]}"; do
- IFS=':' read -r STORAGE SIZE <<< "${VIRTUAL_DISKS[$i]}"
-
- STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}')
- case $STORAGE_TYPE in
- nfs | dir)
- DISK_EXT=".raw"
- DISK_REF="$VMID/"
- ;;
- *)
- DISK_EXT=""
- DISK_REF=""
- ;;
- esac
-
-
- DISK_NUM=$((i+1))
- DISK_NAME="vm-${VMID}-disk-${DISK_NUM}${DISK_EXT}"
-
-
- # Create virtual disk
- msg_info "Creating virtual disk..."
- if ! pvesm alloc "$STORAGE" "$VMID" "$DISK_NAME" "$SIZE"G >/dev/null 2>&1; then
- msg_error "Failed to allocate virtual disk $DISK_NUM"
-
- fi
-
- # Configure disk in the VM (sata0, sata1, etc.)
- SATA_ID="sata$i"
- if ! qm set "$VMID" -$SATA_ID "$STORAGE:${DISK_REF}$DISK_NAME" >/dev/null 2>&1; then
- msg_error "Failed to configure virtual disk as $SATA_ID"
-
- fi
- msg_ok "Configured virtual disk as $SATA_ID, ${SIZE}GB on ${CL}${BL}$STORAGE${CL} ${GN}"
-
- # Add information to the description
- DISK_INFO="${DISK_INFO}Virtual Disk $DISK_NUM: ${SIZE}GB on ${STORAGE}
"
- CONSOLE_DISK_INFO="${CONSOLE_DISK_INFO}- Virtual Disk $DISK_NUM: ${SIZE}GB on ${STORAGE} ($SATA_ID)\n"
- done
-
-
-
- # HTML description
-HTML_DESC="
-
-
-
-
- |
-
-Synology DSM VM
-Created with ProxMenuX
-Loader: $LOADER_NAME
- |
-
-
-
-
-
-
-
-
-
-
-
-${DISK_INFO}
-
-
"
-
- msg_info "Setting VM description"
- if ! qm set "$VMID" -description "$HTML_DESC" >/dev/null 2>&1; then
- msg_error "Failed to set VM description"
- exit_script
- fi
- msg_ok "Configured VM description"
-
-
-else
-
-
- # Configure multiple passthrough disks
- DISK_INFO=""
- CONSOLE_DISK_INFO=""
-
- for i in "${!PASSTHROUGH_DISKS[@]}"; do
- DISK="${PASSTHROUGH_DISKS[$i]}"
- DISK_MODEL=$(lsblk -ndo MODEL "$DISK" | xargs)
- DISK_SIZE=$(lsblk -ndo SIZE "$DISK" | xargs)
- DISK_ID="sata$i"
-
-
- result=$(qm set $VMID -${DISK_ID} ${DISK} 2>&1)
- if [[ $? -eq 0 ]]; then
- msg_ok "Configured disk ${CL}${BL}($DISK_MODEL $DISK_SIZE)${CL}${GN} as $DISK_ID"
- fi
- # Add information to the description
- DISK_INFO="${DISK_INFO}Passthrough Disk $((i+1)): $DISK ($DISK_MODEL $DISK_SIZE)
"
- CONSOLE_DISK_INFO="${CONSOLE_DISK_INFO}- Passthrough Disk $((i+1)): $DISK ($DISK_MODEL $DISK_SIZE) (${DISK_ID})\n"
- done
-
-
- # HTML description
-HTML_DESC="
-
-
-
-
- |
-
-Synology DSM VM
-Created with ProxMenuX
-Loader: $LOADER_NAME
- |
-
-
-
-
-
-
-
-
-
-
-
-${DISK_INFO}
-
-
"
-
-
- result=$(qm set $VMID -description "$HTML_DESC" 2>&1)
- if [[ $? -eq 0 ]]; then
- msg_ok "Configured VM description"
- fi
-
-
-fi
-
-
-if [ "$ERROR_FLAG" = true ]; then
- msg_error "VM created with errors. Check configuration."
-else
-msg_success "$(translate "Completed Successfully!")"
-
-echo -e "${TAB}${GN}$(translate "Next Steps:")${CL}"
-echo -e "${TAB}1. $(translate "Start the VM")"
-echo -e "${TAB}2. $(translate "Open the VM console and wait for the loader to boot")"
-echo -e "${TAB}3. $(translate "In the loader interface, follow the instructions to select your Synology model")"
-echo -e "${TAB}4. $(translate "Complete the DSM installation wizard")"
-echo -e "${TAB}5. $(translate "Find your device using https://finds.synology.com")"
-echo -e
-
-msg_success "$(translate "Press Enter to return to the main menu...")"
-read -r
-
-fi
-
-}
-
-# ==========================================================
-
-
-
-# ==========================================================
-# Main execution
-# ==========================================================
-header_info
-#echo -e "\n Loading..."
-sleep 1
-
-# Start script
-if whiptail --backtitle "ProxMenuX" --title "$NAME" --yesno "$(translate "This will create a New $NAME. Proceed?")" 10 58; then
- start_script
-else
- clear
- exit
-fi
-
-# Create VM
-create_vm
-
-# ==========================================================
\ No newline at end of file