diff --git a/assets/base-packages.txt b/assets/base-packages.txt deleted file mode 100644 index ae51d0a0..00000000 --- a/assets/base-packages.txt +++ /dev/null @@ -1,720 +0,0 @@ -# base-packages.txt - Generated on 2025-05-15 21:15:29 -# Proxmox Version: pve-manager/8.4.1/ (running kernel: 6.8.12-9-pve) - -adduser -apparmor -apt -apt-listchanges -apt-utils -attr -base-files -base-passwd -bash -bash-completion -bc -bind9-dnsutils -bind9-host -bind9-libs -binutils -binutils-common -binutils-x86-64-linux-gnu -bridge-utils -bsdextrautils -bsd-mailx -bsdutils -btrfs-progs -busybox -bzip2 -ca-certificates -ceph-common -ceph-fuse -chrony -cifs-utils -console-setup -console-setup-linux -coreutils -corosync -cpio -criu -cron -cron-daemon-common -cstream -curl -dash -dbus -dbus-bin -dbus-daemon -dbus-session-bus-common -dbus-system-bus-common -debconf -debconf-i18n -debian-archive-keyring -debian-faq -debianutils -dialog -diffutils -dirmngr -distro-info-data -dmeventd -dmidecode -dmsetup -doc-debian -dosfstools -dpkg -dtach -e2fsprogs -ebtables -efibootmgr -eject -ethtool -faketime -fdisk -fdutils -file -findutils -fontconfig -fontconfig-config -fonts-dejavu-core -fonts-font-awesome -fonts-font-logos -fonts-glyphicons-halflings -frr -frr-pythontools -fuse -gcc-12-base -gdisk -genisoimage -gettext-base -glusterfs-client -glusterfs-common -gnupg -gnupg-l10n -gnupg-utils -gnutls-bin -gpg -gpg-agent -gpgconf -gpgsm -gpgv -gpg-wks-client -gpg-wks-server -grep -groff-base -grub2-common -grub-common -grub-efi-amd64 -grub-efi-amd64-bin -grub-efi-amd64-signed -grub-pc-bin -gzip -hdparm -hostname -ifupdown2 -inetutils-telnet -init -initramfs-tools -initramfs-tools-core -init-system-helpers -iproute2 -ipset -iptables -iputils-ping -isc-dhcp-client -isc-dhcp-common -iso-codes -jq -kbd -keyboard-configuration -keyutils -klibc-utils -kmod -krb5-locales -ksm-control-daemon -less -libacl1 -libaio1 -libanyevent-http-perl -libanyevent-perl -libapparmor1 -libappconfig-perl -libapt-pkg6.0 -libapt-pkg-perl -libarchive13 -libargon2-1 -libasound2 -libasound2-data -libassuan0 -libasyncns0 -libattr1 -libaudit1 -libaudit-common -libauthen-pam-perl -libavahi-client3 -libavahi-common3 -libavahi-common-data -libbabeltrace1 -libbinutils -libblas3 -libblkid1 -libbpf1 -libbrotli1 -libbsd0 -libbytes-random-secure-perl -libbz2-1.0 -libc6 -libcairo2 -libcap2 -libcap2-bin -libcap-ng0 -libc-ares2 -libc-bin -libcbor0.8 -libcephfs2 -libcfg7 -libc-l10n -libclone-perl -libcmap4 -libcom-err2 -libcommon-sense-perl -libconvert-asn1-perl -libcorosync-common4 -libcpg4 -libcrypt1 -libcrypt-openssl-bignum-perl -libcrypt-openssl-random-perl -libcrypt-openssl-rsa-perl -libcrypt-random-seed-perl -libcryptsetup12 -libcrypt-ssleay-perl -libctf0 -libctf-nobfd0 -libcurl3-gnutls -libcurl4 -libdatrie1 -libdb5.3 -libdbi1 -libdbus-1-3 -libdebconfclient0 -libdevel-cycle-perl -libdevmapper1.02.1 -libdevmapper-event1.02.1 -libdigest-hmac-perl -libdouble-conversion3 -libdrm2 -libdrm-common -libdw1 -libedit2 -libefiboot1 -libefivar1 -libelf1 -libencode-locale-perl -libepoxy0 -libevent-2.1-7 -libevent-core-2.1-7 -libexpat1 -libext2fs2 -libfaketime -libfdisk1 -libfdt1 -libffi8 -libfido2-1 -libfile-chdir-perl -libfile-find-rule-perl -libfile-listing-perl -libfile-readbackwards-perl -libfilesys-df-perl -libflac12 -libfmt9 -libfontconfig1 -libfreetype6 -libfribidi0 -libfstrm0 -libfuse2 -libfuse3-3 -libgbm1 -libgcc-s1 -libgcrypt20 -libgdbm6 -libgdbm-compat4 -libgfapi0 -libgfchangelog0 -libgfrpc0 -libgfxdr0 -libglib2.0-0 -libglusterd0 -libglusterfs0 -libgmp10 -libgnutls30 -libgnutls-dane0 -libgnutlsxx30 -libgoogle-perftools4 -libgpg-error0 -libgprofng0 -libgraphite2-3 -libgssapi-krb5-2 -libgstreamer1.0-0 -libgstreamer-plugins-base1.0-0 -libharfbuzz0b -libhogweed6 -libhtml-parser-perl -libhtml-tagset-perl -libhtml-tree-perl -libhttp-cookies-perl -libhttp-daemon-perl -libhttp-date-perl -libhttp-message-perl -libhttp-negotiate-perl -libibverbs1 -libicu72 -libidn2-0 -libinih1 -libio-html-perl -libio-multiplex-perl -libio-socket-ssl-perl -libio-stringy-perl -libip4tc2 -libip6tc2 -libipset13 -libiscsi7 -libisns0 -libjansson4 -libjemalloc2 -libjpeg62-turbo -libjq1 -libjs-bootstrap -libjs-extjs -libjs-jquery -libjson-c5 -libjson-glib-1.0-0 -libjson-glib-1.0-common -libjson-perl -libjson-xs-perl -libjs-qrcodejs -libjs-sencha-touch -libk5crypto3 -libkeyutils1 -libklibc -libkmod2 -libknet1 -libkrb5-3 -libkrb5support0 -libksba8 -libldap-2.5-0 -libldb2 -liblinear4 -liblinux-inotify2-perl -liblmdb0 -liblocale-gettext-perl -liblockfile1 -liblockfile-bin -liblttng-ust1 -liblttng-ust-common1 -liblttng-ust-ctl5 -liblua5.3-0 -liblvm2cmd2.03 -liblwp-mediatypes-perl -liblwp-protocol-https-perl -liblz4-1 -liblzma5 -liblzo2-2 -libmagic1 -libmagic-mgc -libmath-random-isaac-perl -libmaxminddb0 -libmd0 -libmime-base32-perl -libmnl0 -libmount1 -libmp3lame0 -libmpg123-0 -libncurses6 -libncursesw6 -libnet1 -libnetaddr-ip-perl -libnet-dbus-perl -libnet-dns-perl -libnetfilter-conntrack3 -libnetfilter-log1 -libnet-http-perl -libnet-ip-perl -libnet-ldap-perl -libnet-ssleay-perl -libnet-subnet-perl -libnettle8 -libnewt0.52 -libnfnetlink0 -libnfsidmap1 -libnftables1 -libnftnl11 -libnghttp2-14 -libnl-3-200 -libnl-route-3-200 -libnozzle1 -libnpth0 -libnsl2 -libnspr4 -libnss3 -libnss-systemd -libnuma1 -libnumber-compare-perl -libnvpair3linux -liboath0 -libogg0 -libonig5 -libopeniscsiusr -libopus0 -liborc-0.4-0 -libp11-kit0 -libpam0g -libpam-modules -libpam-modules-bin -libpam-runtime -libpam-systemd -libpango-1.0-0 -libpangocairo-1.0-0 -libpangoft2-1.0-0 -libpcap0.8 -libpci3 -libpcre2-16-0 -libpcre2-8-0 -libpcre3 -libperl5.36 -libpipeline1 -libpixman-1-0 -libpng16-16 -libpopt0 -libposix-strptime-perl -libproc2-0 -libprotobuf32 -libprotobuf-c1 -libproxmox-acme-perl -libproxmox-acme-plugins -libproxmox-backup-qemu0 -libproxmox-rs-perl -libpsl5 -libpulse0 -libpve-access-control -libpve-apiclient-perl -libpve-cluster-api-perl -libpve-cluster-perl -libpve-common-perl -libpve-guest-common-perl -libpve-http-server-perl -libpve-network-api-perl -libpve-network-perl -libpve-notify-perl -libpve-rs-perl -libpve-storage-perl -libpve-u2f-server-perl -libpython3.11-minimal -libpython3.11-stdlib -libpython3-stdlib -libqb100 -libqrencode4 -libqt5core5a -libqt5dbus5 -libqt5network5 -libquorum5 -librabbitmq4 -librados2 -librados2-perl -libradosstriper1 -librbd1 -librdkafka1 -librdmacm1 -libreadline8 -libregexp-ipv6-perl -librgw2 -librrd8 -librrds-perl -librtmp1 -libsasl2-2 -libsasl2-modules-db -libseccomp2 -libselinux1 -libsemanage2 -libsemanage-common -libsepol2 -libslang2 -libslirp0 -libsmartcols1 -libsmbclient -libsnappy1v5 -libsndfile1 -libsocket6-perl -libspice-server1 -libsqlite3-0 -libss2 -libssh2-1 -libssl3 -libstatgrab10 -libstdc++6 -libstring-shellquote-perl -libsubid4 -libsystemd0 -libsystemd-shared -libtalloc2 -libtasn1-6 -libtcmalloc-minimal4 -libtdb1 -libtemplate-perl -libterm-readline-gnu-perl -libtevent0 -libtext-charwidth-perl -libtext-glob-perl -libtext-iconv-perl -libtext-wrapi18n-perl -libthai0 -libthai-data -libthrift-0.17.0 -libtimedate-perl -libtinfo6 -libtirpc3 -libtirpc-common -libtpms0 -libtry-tiny-perl -libtypes-serialiser-perl -libu2f-server0 -libuchardet0 -libudev1 -libunbound8 -libunistring2 -libunwind8 -liburcu8 -liburing2 -liburi-perl -libusb-1.0-0 -libusbredirparser1 -libuuid1 -libuuid-perl -libuutil3linux -libuv1 -libva2 -libva-drm2 -libvirglrenderer1 -libvorbis0a -libvorbisenc2 -libvotequorum8 -libvulkan1 -libwayland-server0 -libwbclient0 -libwrap0 -libwww-perl -libwww-robotrules-perl -libx11-6 -libx11-data -libx11-xcb1 -libxau6 -libxcb1 -libxcb-render0 -libxcb-shm0 -libxdmcp6 -libxext6 -libxml2 -libxml-libxml-perl -libxml-namespacesupport-perl -libxml-parser-perl -libxml-sax-base-perl -libxml-sax-perl -libxml-twig-perl -libxrender1 -libxslt1.1 -libxtables12 -libxxhash0 -libyaml-0-2 -libyaml-libyaml-perl -libyang3 -libzfs4linux -libzpool5linux -libzstd1 -linux-base -locales -login -logrotate -logsave -lsof -lua-lpeg -lvm2 -lxcfs -lxc-pve -lzop -mailcap -man-db -manpages -mawk -media-types -memtest86+ -mime-support -mokutil -mount -nano -ncurses-base -ncurses-bin -ncurses-term -netbase -netcat-traditional -nfs-common -nftables -nmap -nmap-common -novnc-pve -open-iscsi -openssh-client -openssh-server -openssh-sftp-server -openssl -passwd -pci.ids -pciutils -perl -perl-base -perl-modules-5.36 -perl-openssl-defaults -pinentry-curses -postfix -procmail -procps -proxmox-archive-keyring -proxmox-backup-client -proxmox-backup-file-restore -proxmox-backup-restore-image -proxmox-default-kernel -proxmox-firewall -proxmox-grub -proxmox-kernel-6.8 -proxmox-kernel-6.8.12-10-pve-signed -proxmox-kernel-6.8.12-9-pve-signed -proxmox-kernel-helper -proxmox-mail-forward -proxmox-mini-journalreader -proxmox-offline-mirror-docs -proxmox-offline-mirror-helper -proxmox-termproxy -proxmox-ve -proxmox-websocket-tunnel -proxmox-widget-toolkit -psmisc -pv -pve-cluster -pve-container -pve-docs -pve-edk2-firmware -pve-edk2-firmware-legacy -pve-edk2-firmware-ovmf -pve-esxi-import-tools -pve-firewall -pve-firmware -pve-ha-manager -pve-i18n -pve-lxc-syscalld -pve-manager -pve-qemu-kvm -pve-xtermjs -python3 -python3.11 -python3.11-minimal -python3.11-venv -python3-apt -python3-ceph-argparse -python3-ceph-common -python3-cephfs -python3-certifi -python3-chardet -python3-charset-normalizer -python3-debconf -python3-debian -python3-debianbts -python3-distutils -python3-httplib2 -python3-idna -python3-jwt -python3-lib2to3 -python3-minimal -python3-pip-whl -python3-pkg-resources -python3-prettytable -python3-protobuf -python3-pycurl -python3-pyparsing -python3-pysimplesoap -python3-pyvmomi -python3-rados -python3-rbd -python3-reportbug -python3-requests -python3-rgw -python3-setuptools -python3-setuptools-whl -python3-six -python3-systemd -python3-urllib3 -python3-venv -python3-wcwidth -python3-yaml -python-apt-common -qemu-server -qrencode -readline-common -reportbug -rpcbind -rrdcached -rsync -runit-helper -samba-common -samba-libs -sed -sensible-utils -sgml-base -shared-mime-info -shim-helpers-amd64-signed -shim-signed -shim-signed-common -shim-unsigned -smartmontools -smbclient -socat -spiceterm -spl -sqlite3 -ssh -ssl-cert -strace -swtpm -swtpm-libs -swtpm-tools -systemd -systemd-boot -systemd-boot-efi -systemd-sysv -sysvinit-utils -tar -tasksel -tasksel-data -tcpdump -thin-provisioning-tools -time -traceroute -tzdata -ucf -udev -uidmap -usbutils -usrmerge -util-linux -util-linux-extra -vim-common -vim-tiny -virtiofsd -vncterm -wamerican -wget -whiptail -xfsprogs -xkb-data -xsltproc -xz-utils -zfs-initramfs -zfsutils-linux -zfs-zed -zlib1g -zstd \ No newline at end of file diff --git a/scripts/disk-passthrough.sh b/scripts/disk-passthrough.sh deleted file mode 100644 index 2ee02ee0..00000000 --- a/scripts/disk-passthrough.sh +++ /dev/null @@ -1,368 +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 allows users to assign physical disks to existing -# Proxmox virtual machines (VMs) through an interactive menu. -# - Detects the system disk and excludes it from selection. -# - Lists all available VMs for the user to choose from. -# - Identifies and displays unassigned physical disks. -# - Allows the user to select multiple disks and attach them to a VM. -# - Supports interface types: SATA, SCSI, VirtIO, and IDE. -# - Ensures that disks are not already assigned to active VMs. -# - Warns about disk sharing between multiple VMs to avoid data corruption. -# - Configures the selected disks for the VM and verifies the assignment. -# -# The goal of this script is to simplify the process of assigning -# physical disks to Proxmox VMs, reducing manual configurations -# and preventing potential errors. -# ========================================================== - - -# 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 -show_proxmenux_logo -# ========================================================== - - - -get_disk_info() { - local disk=$1 - MODEL=$(lsblk -dn -o MODEL "$disk" | xargs) - SIZE=$(lsblk -dn -o SIZE "$disk" | xargs) - echo "$MODEL" "$SIZE" -} - - -VM_LIST=$(qm list | awk 'NR>1 {print $1, $2}') -if [ -z "$VM_LIST" ]; then - whiptail --title "$(translate "Error")" --msgbox "$(translate "No VMs available in the system.")" 8 40 - exit 1 -fi - - -VMID=$(whiptail --title "$(translate "Select VM")" --menu "$(translate "Select the VM to which you want to add disks:")" 15 60 8 $VM_LIST 3>&1 1>&2 2>&3) - -if [ -z "$VMID" ]; then - whiptail --title "$(translate "Error")" --msgbox "$(translate "No VM was selected.")" 8 40 - exit 1 -fi - -VMID=$(echo "$VMID" | tr -d '"') - - -msg_ok "$(translate "VM selected successfully.")" - - -VM_STATUS=$(qm status "$VMID" | awk '{print $2}') -if [ "$VM_STATUS" == "running" ]; then - whiptail --title "$(translate "Warning")" --msgbox "$(translate "The VM is powered on. Turn it off before adding disks.")" 12 60 - exit 1 -fi - - -########################################## - -msg_info "$(translate "Detecting available 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) - - -is_disk_in_use() { - local disk="$1" - - - while read -r part fstype; do - case "$fstype" in - zfs_member|linux_raid_member) - return 0 ;; - esac - - if echo "$MOUNTED_DISKS" | grep -q "/dev/$part"; then - return 0 - fi - done < <(lsblk -ln -o NAME,FSTYPE "$disk" | tail -n +2) - - - if echo "$USED_DISKS" | grep -q "$disk" || echo "$ZFS_DISKS" | grep -q "$disk"; then - return 0 - fi - - return 1 -} - - - - -FREE_DISKS=() - -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=($(get_disk_info "$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=$(grep -vE '^\s*#' /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf 2>/dev/null) - - if grep -Fq "$REAL_PATH" <<< "$CONFIG_DATA"; then - USED_BY="⚠ $(translate "In use")" - else - 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)"; then - if grep -q "active raid" /proc/mdstat; then - SHOW_DISK=false - fi - fi - - - if $IS_ZFS; then - SHOW_DISK=false - fi - - - if $IS_MOUNTED; then - SHOW_DISK=false - fi - - - if qm config "$VMID" | grep -vE '^\s*#|^description:' | grep -q "$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 "$(translate "Error")" --msgbox "$(translate "No disks available for this VM.")" 8 40 - clear - exit 1 -fi - -msg_ok "$(translate "Available disks detected.")" - - - -###################################################### - - - - -MAX_WIDTH=$(printf "%s\n" "${FREE_DISKS[@]}" | awk '{print length}' | sort -nr | head -n1) -TOTAL_WIDTH=$((MAX_WIDTH + 20)) - -if [ $TOTAL_WIDTH -lt 50 ]; then - TOTAL_WIDTH=50 -fi - - -SELECTED=$(whiptail --title "$(translate "Select Disks")" --checklist \ - "$(translate "Select the disks you want to add:")" 20 $TOTAL_WIDTH 10 "${FREE_DISKS[@]}" 3>&1 1>&2 2>&3) - -if [ -z "$SELECTED" ]; then - whiptail --title "$(translate "Error")" --msgbox "$(translate "No disks were selected.")" 10 64 - clear - exit 1 -fi - -msg_ok "$(translate "Disks selected successfully.")" - - -INTERFACE=$(whiptail --title "$(translate "Interface Type")" --menu "$(translate "Select the interface type for all disks:")" 15 40 4 \ - "sata" "$(translate "Add as SATA")" \ - "scsi" "$(translate "Add as SCSI")" \ - "virtio" "$(translate "Add as VirtIO")" \ - "ide" "$(translate "Add as IDE")" 3>&1 1>&2 2>&3) - -if [ -z "$INTERFACE" ]; then - whiptail --title "$(translate "Error")" --msgbox "$(translate "No interface type was selected for the disks.")" 8 40 - clear - exit 1 -fi - -msg_ok "$(translate "Interface type selected: $INTERFACE")" - -DISKS_ADDED=0 -ERROR_MESSAGES="" -SUCCESS_MESSAGES="" - - - -msg_info "$(translate "Processing selected disks...")" - -for DISK in $SELECTED; do - DISK=$(echo "$DISK" | tr -d '"') - DISK_INFO=$(get_disk_info "$DISK") - - ASSIGNED_TO="" - RUNNING_VMS="" - RUNNING_CTS="" - - - while read -r VM_ID VM_NAME; do - if [[ "$VM_ID" =~ ^[0-9]+$ ]] && qm config "$VM_ID" | grep -q "$DISK"; then - ASSIGNED_TO+="VM $VM_ID $VM_NAME\n" - VM_STATUS=$(qm status "$VM_ID" | awk '{print $2}') - if [ "$VM_STATUS" == "running" ]; then - RUNNING_VMS+="VM $VM_ID $VM_NAME\n" - fi - fi - done < <(qm list | awk 'NR>1 {print $1, $2}') - - - while read -r CT_ID CT_NAME; do - if [[ "$CT_ID" =~ ^[0-9]+$ ]] && pct config "$CT_ID" | grep -q "$DISK"; then - ASSIGNED_TO+="CT $CT_ID $CT_NAME\n" - CT_STATUS=$(pct status "$CT_ID" | awk '{print $2}') - if [ "$CT_STATUS" == "running" ]; then - RUNNING_CTS+="CT $CT_ID $CT_NAME\n" - fi - fi - done < <(pct list | awk 'NR>1 {print $1, $2}') - - if [ -n "$RUNNING_VMS" ] || [ -n "$RUNNING_CTS" ]; then - ERROR_MESSAGES+="$(translate "The disk") $DISK_INFO $(translate "is currently in use by the following running VM(s) or CT(s):")\\n$RUNNING_VMS$RUNNING_CTS\\n\\n$(translate "You cannot add this disk while the VM or CT is running.")\\n$(translate "Please shut it down first and run this script again to add the disk.")\\n\\n" - continue - fi - - if [ -n "$ASSIGNED_TO" ]; then - cleanup - whiptail --title "$(translate "Disk Already Assigned")" --yesno "$(translate "The disk") $DISK_INFO $(translate "is already assigned to the following VM(s) or CT(s):")\\n$ASSIGNED_TO\\n\\n$(translate "Do you want to continue anyway?")" 15 70 - if [ $? -ne 0 ]; then - sleep 1 - exec "$0" - fi - fi - - - INDEX=0 - while qm config "$VMID" | grep -q "${INTERFACE}${INDEX}"; do - ((INDEX++)) - done - - RESULT=$(qm set "$VMID" -${INTERFACE}${INDEX} "$DISK" 2>&1) - - if [ $? -eq 0 ]; then - MESSAGE="$(translate "The disk") $DISK_INFO $(translate "has been successfully added to VM") $VMID." - if [ -n "$ASSIGNED_TO" ]; then - MESSAGE+="\\n\\n$(translate "WARNING: This disk is also assigned to the following VM(s):")\\n$ASSIGNED_TO" - MESSAGE+="\\n$(translate "Make sure not to start VMs that share this disk at the same time to avoid data corruption.")" - fi - SUCCESS_MESSAGES+="$MESSAGE\\n\\n" - ((DISKS_ADDED++)) - else - ERROR_MESSAGES+="$(translate "Could not add disk") $DISK_INFO $(translate "to VM") $VMID.\\n$(translate "Error:") $RESULT\\n\\n" - fi -done - -msg_ok "$(translate "Disk processing completed.")" - - - -if [ -n "$SUCCESS_MESSAGES" ]; then - MSG_LINES=$(echo "$SUCCESS_MESSAGES" | wc -l) - whiptail --title "$(translate "Successful Operations")" --msgbox "$SUCCESS_MESSAGES" 16 70 -fi - -if [ -n "$ERROR_MESSAGES" ]; then - whiptail --title "$(translate "Warnings and Errors")" --msgbox "$ERROR_MESSAGES" 16 70 -fi - - - -exit 0 diff --git a/scripts/disk-passthrough_ct.sh b/scripts/disk-passthrough_ct.sh deleted file mode 100644 index 079e021d..00000000 --- a/scripts/disk-passthrough_ct.sh +++ /dev/null @@ -1,537 +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 allows users to assign physical disks to existing -# Proxmox containers (CTs) through an interactive menu. -# - Detects the system disk and excludes it from selection. -# - Lists all available CTs for the user to choose from. -# - Identifies and displays unassigned physical disks. -# - Allows the user to select multiple disks and attach them to a CT. -# - Configures the selected disks for the CT and verifies the assignment. -# ========================================================== - - -# 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 - -# ========================================================== - - - -get_disk_info() { - local disk=$1 - MODEL=$(lsblk -dn -o MODEL "$disk" | xargs) - SIZE=$(lsblk -dn -o SIZE "$disk" | xargs) - echo "$MODEL" "$SIZE" -} - - - -CT_LIST=$(pct list | awk 'NR>1 {print $1, $3}') -if [ -z "$CT_LIST" ]; then - whiptail --title "$(translate "Error")" --msgbox "$(translate "No CTs available in the system.")" 8 40 - exit 1 -fi - - -CTID=$(whiptail --title "$(translate "Select CT")" --menu "$(translate "Select the CT to which you want to add disks:")" 15 60 8 $CT_LIST 3>&1 1>&2 2>&3) - -if [ -z "$CTID" ]; then - whiptail --title "$(translate "Error")" --msgbox "$(translate "No CT was selected.")" 8 40 - exit 1 -fi - -CTID=$(echo "$CTID" | tr -d '"') - -msg_ok "$(translate "CT selected successfully.")" - - - - -CT_STATUS=$(pct status "$CTID" | awk '{print $2}') -if [ "$CT_STATUS" != "running" ]; then - msg_info "$(translate "Starting CT") $CTID..." - pct start "$CTID" - sleep 2 - if [ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]; then - msg_error "$(translate "Failed to start the CT.")" - exit 1 - fi - msg_ok "$(translate "CT started successfully.")" -fi - - - - -CONF_FILE="/etc/pve/lxc/$CTID.conf" - -if grep -q '^unprivileged: 1' "$CONF_FILE"; then - if whiptail --title "$(translate "Privileged Container")" \ - --yesno "$(translate "The selected container is unprivileged. A privileged container is required for direct device passthrough.")\\n\\n$(translate "Do you want to convert it to a privileged container now?")" 12 70; then - - msg_info "$(translate "Stopping container") $CTID..." - pct shutdown "$CTID" & - for i in {1..10}; do - sleep 1 - if [ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]; then - break - fi - done - - if [ "$(pct status "$CTID" | awk '{print $2}')" == "running" ]; then - msg_error "$(translate "Failed to stop the container.")" - exit 1 - fi - - msg_ok "$(translate "Container stopped.")" - - cp "$CONF_FILE" "$CONF_FILE.bak" - sed -i '/^unprivileged: 1/d' "$CONF_FILE" - echo "unprivileged: 0" >> "$CONF_FILE" - - msg_ok "$(translate "Container successfully converted to privileged.")" - - msg_info "$(translate "Starting container") $CTID..." - pct start "$CTID" - sleep 2 - if [ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]; then - msg_error "$(translate "Failed to start the container.")" - exit 1 - fi - msg_ok "$(translate "Container started successfully.")" - - else - whiptail --title "$(translate "Aborted")" \ - --msgbox "$(translate "Operation cancelled. Cannot continue with an unprivileged container.")" 10 60 - exit 1 - fi -fi - - - - - -########################################## - - - - - -msg_info "$(translate "Detecting available 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) - -is_disk_in_use() { - local disk="$1" - - while read -r part fstype; do - case "$fstype" in - zfs_member|linux_raid_member) - return 0 ;; - esac - - if echo "$MOUNTED_DISKS" | grep -q "/dev/$part"; then - return 0 - fi - done < <(lsblk -ln -o NAME,FSTYPE "$disk" | tail -n +2) - - if echo "$USED_DISKS" | grep -q "$disk" || echo "$ZFS_DISKS" | grep -q "$disk"; then - return 0 - fi - - return 1 -} - -FREE_DISKS=() - -LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -r -n1 readlink -f | sort -u) - -if [[ -n "$LVM_DEVICES" ]] && echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then - IS_MOUNTED=true -fi - -RAID_ACTIVE=$(grep -Po 'md\d+\s*:\s*active\s+raid[0-9]+' /proc/mdstat | awk '{print $1}' | sort -u) - -while read -r DISK; do - [[ "$DISK" =~ /dev/zd ]] && continue - - INFO=($(get_disk_info "$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=$(grep -vE '^\s*#' /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf 2>/dev/null) - - if grep -Fq "$REAL_PATH" <<< "$CONFIG_DATA"; then - USED_BY="⚠ $(translate "In use")" - else - 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)"; then - if grep -q "active raid" /proc/mdstat; then - SHOW_DISK=false - fi - fi - - if $IS_ZFS; then - SHOW_DISK=false - fi - - if $IS_MOUNTED; then - SHOW_DISK=false - fi - - if pct config "$CTID" | grep -vE '^\s*#|^description:' | grep -q "$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 "$(translate "Error")" --msgbox "$(translate "No disks available for this CT.")" 8 40 - clear - exit 1 -fi - -msg_ok "$(translate "Available disks detected.")" - - - - - -###################################################### - - - - - -MAX_WIDTH=$(printf "%s\n" "${FREE_DISKS[@]}" | awk '{print length}' | sort -nr | head -n1) -TOTAL_WIDTH=$((MAX_WIDTH + 20)) - -if [ $TOTAL_WIDTH -lt 50 ]; then - TOTAL_WIDTH=50 -fi - -SELECTED=$(whiptail --title "$(translate "Select Disks")" --radiolist \ - "$(translate "Select the disks you want to add:")" 20 $TOTAL_WIDTH 10 "${FREE_DISKS[@]}" 3>&1 1>&2 2>&3) - -if [ -z "$SELECTED" ]; then - whiptail --title "$(translate "Error")" --msgbox "$(translate "No disks were selected.")" 10 64 - clear - exit 1 -fi - -msg_ok "$(translate "Disks selected successfully.")" - -DISKS_ADDED=0 -ERROR_MESSAGES="" -SUCCESS_MESSAGES="" - -msg_info "$(translate "Processing selected disks...")" - -for DISK in $SELECTED; do - DISK=$(echo "$DISK" | tr -d '"') - DISK_INFO=$(get_disk_info "$DISK") - - ASSIGNED_TO="" - RUNNING_CTS="" - RUNNING_VMS="" - - # Comprobar CTs - while read -r CT_ID CT_NAME; do - if [[ "$CT_ID" =~ ^[0-9]+$ ]] && pct config "$CT_ID" | grep -q "$DISK"; then - ASSIGNED_TO+="CT $CT_ID $CT_NAME\n" - CT_STATUS=$(pct status "$CT_ID" | awk '{print $2}') - if [ "$CT_STATUS" == "running" ]; then - RUNNING_CTS+="CT $CT_ID $CT_NAME\n" - fi - fi - done < <(pct list | awk 'NR>1 {print $1, $3}') - - # Comprobar VMs - while read -r VM_ID VM_NAME; do - if [[ "$VM_ID" =~ ^[0-9]+$ ]] && qm config "$VM_ID" | grep -q "$DISK"; then - ASSIGNED_TO+="VM $VM_ID $VM_NAME\n" - VM_STATUS=$(qm status "$VM_ID" | awk '{print $2}') - if [ "$VM_STATUS" == "running" ]; then - RUNNING_VMS+="VM $VM_ID $VM_NAME\n" - fi - fi - done < <(qm list | awk 'NR>1 {print $1, $2}') - - if [ -n "$RUNNING_CTS" ] || [ -n "$RUNNING_VMS" ]; then - ERROR_MESSAGES+="$(translate "The disk") $DISK_INFO $(translate "is in use by the following running VM(s) or CT(s):")\\n$RUNNING_CTS$RUNNING_VMS\\n\\n" - continue - fi - - if [ -n "$ASSIGNED_TO" ]; then - cleanup - whiptail --title "$(translate "Disk Already Assigned")" --yesno "$(translate "The disk") $DISK_INFO $(translate "is already assigned to the following VM(s) or CT(s):")\\n$ASSIGNED_TO\\n\\n$(translate "Do you want to continue anyway?")" 15 70 - if [ $? -ne 0 ]; then - sleep 1 - exec "$0" - fi - fi - - cleanup - - - - - if lsblk "$DISK" | grep -q "raid" || grep -q "${DISK##*/}" /proc/mdstat; then - whiptail --title "$(translate "RAID Detected")" --msgbox "$(translate "The disk") $DISK_INFO $(translate "appears to be part of a") RAID. $(translate "For security reasons, the system cannot format it.")\\n\\n$(translate "If you are sure you want to use it, please remove the") RAID metadata $(translate "or format it manually using external tools.")\\n\\n$(translate "After that, run this script again to add it.")" 18 70 - exit - fi - - - - - MOUNT_POINT=$(whiptail --title "$(translate "Mount Point")" --inputbox "$(translate "Enter the mount point for the disk (e.g., /mnt/disk_passthrough):")" 10 60 "/mnt/disk_passthrough" 3>&1 1>&2 2>&3) - - if [ -z "$MOUNT_POINT" ]; then - whiptail --title "$(translate "Error")" --msgbox "$(translate "No mount point was specified.")" 8 40 - continue - fi - - msg_ok "$(translate "Mount point specified: $MOUNT_POINT")" - - - - - - - PARTITION=$(lsblk -rno NAME "$DISK" | awk -v disk="$(basename "$DISK")" '$1 != disk {print $1; exit}') - SKIP_FORMAT=false - - if [ -n "$PARTITION" ]; then - PARTITION="/dev/$PARTITION" - CURRENT_FS=$(lsblk -no FSTYPE "$PARTITION" | xargs) - - if [[ "$CURRENT_FS" == "ext4" || "$CURRENT_FS" == "xfs" || "$CURRENT_FS" == "btrfs" ]]; then - SKIP_FORMAT=true - msg_ok "$(translate "Detected existing filesystem") $CURRENT_FS $(translate "on") $PARTITION." - else - whiptail --title "$(translate "Unsupported Filesystem")" --yesno "$(translate "The partition") $PARTITION $(translate "has an unsupported filesystem ($CURRENT_FS).\\nDo you want to format it?")" 10 70 - if [ $? -ne 0 ]; then - continue - fi - fi - else - - CURRENT_FS=$(lsblk -no FSTYPE "$DISK" | xargs) - - if [[ "$CURRENT_FS" == "ext4" || "$CURRENT_FS" == "xfs" || "$CURRENT_FS" == "btrfs" ]]; then - SKIP_FORMAT=true - PARTITION="$DISK" - msg_ok "$(translate "Detected filesystem") $CURRENT_FS $(translate "directly on disk") $DISK.)" - else - - whiptail --title "$(translate "No Valid Partitions")" --yesno "$(translate "The disk has no partitions and no valid filesystem. Do you want to create a new partition and format it?")" 10 70 - if [ $? -ne 0 ]; then - continue - fi - - echo -e "$(translate "Creating partition table and partition...")" - parted -s "$DISK" mklabel gpt - parted -s "$DISK" mkpart primary 0% 100% - sleep 2 - partprobe "$DISK" - sleep 2 - - PARTITION=$(lsblk -rno NAME "$DISK" | awk -v disk="$(basename "$DISK")" '$1 != disk {print $1; exit}') - if [ -n "$PARTITION" ]; then - PARTITION="/dev/$PARTITION" - else - whiptail --title "$(translate "Partition Error")" --msgbox "$(translate "Failed to create partition on disk") $DISK_INFO." 8 70 - continue - fi - fi - fi - - - - - - if [ "$SKIP_FORMAT" != true ]; then - CURRENT_FS=$(lsblk -no FSTYPE "$PARTITION" | xargs) - if [[ "$CURRENT_FS" == "ext4" || "$CURRENT_FS" == "xfs" || "$CURRENT_FS" == "btrfs" ]]; then - SKIP_FORMAT=true - msg_ok "$(translate "Detected existing filesystem") $CURRENT_FS $(translate "on") $PARTITION. $(translate "Skipping format.")" - else - - FORMAT_TYPE=$(whiptail --title "$(translate "Select Format Type")" --menu "$(translate "Select the filesystem type for") $DISK_INFO:" 15 60 6 \ - "ext4" "$(translate "Extended Filesystem 4 (recommended)")" \ - "xfs" "$(translate "XFS Filesystem")" \ - "btrfs" "$(translate "Btrfs Filesystem")" 3>&1 1>&2 2>&3) - - if [ -z "$FORMAT_TYPE" ]; then - whiptail --title "$(translate "Format Cancelled")" --msgbox "$(translate "Format operation cancelled. The disk will not be added.")" 8 60 - continue - fi - - whiptail --title "$(translate "WARNING")" --yesno "$(translate "WARNING: This operation will FORMAT the disk") $DISK_INFO $(translate "with") $FORMAT_TYPE.\\n\\n$(translate "ALL DATA ON THIS DISK WILL BE PERMANENTLY LOST!")\\n\\n$(translate "Are you sure you want to continue")" 15 70 - if [ $? -ne 0 ]; then - whiptail --title "$(translate "Format Cancelled")" --msgbox "$(translate "Format operation cancelled. The disk will not be added.")" 8 60 - continue - fi - fi - fi - - - - - - if [ "$SKIP_FORMAT" != true ]; then - echo -e "$(translate "Formatting partition") $PARTITION $(translate "with") $FORMAT_TYPE..." - - case "$FORMAT_TYPE" in - "ext4") mkfs.ext4 -F "$PARTITION" ;; - "xfs") mkfs.xfs -f "$PARTITION" ;; - "btrfs") mkfs.btrfs -f "$PARTITION" ;; - esac - - if [ $? -ne 0 ]; then - whiptail --title "$(translate "Format Failed")" --msgbox "$(translate "Failed to format partition") $PARTITION $(translate "with") $FORMAT_TYPE.\\n\\n$(translate "The disk may be in use by the system or have hardware issues.")" 12 70 - continue - else - msg_ok "$(translate "Partition") $PARTITION $(translate "successfully formatted with") $FORMAT_TYPE." - partprobe "$DISK" - sleep 2 - fi - fi - - - - - INDEX=0 - while pct config "$CTID" | grep -q "mp${INDEX}:"; do - ((INDEX++)) - done - - - - - ############################################################################## - - RESULT=$(pct set "$CTID" -mp${INDEX} "$PARTITION,mp=$MOUNT_POINT,backup=0,ro=0,acl=1" 2>&1) - - pct exec "$CTID" -- chmod -R 775 "$MOUNT_POINT" - - ############################################################################## - - - - - if [ $? -eq 0 ]; then - MESSAGE="$(translate "The disk") $DISK_INFO $(translate "has been successfully added to CT") $CTID $(translate "as a mount point at") $MOUNT_POINT." - if [ -n "$ASSIGNED_TO" ]; then - MESSAGE+="\\n\\n$(translate "WARNING: This disk is also assigned to the following CT(s):")\\n$ASSIGNED_TO" - MESSAGE+="\\n$(translate "Make sure not to start CTs that share this disk at the same time to avoid data corruption.")" - fi - SUCCESS_MESSAGES+="$MESSAGE\\n\\n" - ((DISKS_ADDED++)) - else - ERROR_MESSAGES+="$(translate "Could not add disk") $DISK_INFO $(translate "to CT") $CTID.\\n$(translate "Error:") $RESULT\\n\\n" - fi -done - - - -msg_ok "$(translate "Disk processing completed.")" - -if [ -n "$SUCCESS_MESSAGES" ]; then - MSG_LINES=$(echo "$SUCCESS_MESSAGES" | wc -l) - whiptail --title "$(translate "Successful Operations")" --msgbox "$SUCCESS_MESSAGES" 16 70 -fi - -if [ -n "$ERROR_MESSAGES" ]; then - whiptail --title "$(translate "Warnings and Errors")" --msgbox "$ERROR_MESSAGES" 16 70 -fi - -exit 0 diff --git a/scripts/global/remove-banner-pve-v3_.sh b/scripts/global/remove-banner-pve-v3_.sh deleted file mode 100644 index f5b80e3d..00000000 --- a/scripts/global/remove-banner-pve-v3_.sh +++ /dev/null @@ -1,277 +0,0 @@ -#!/bin/bash -# ========================================================== -# Remove Subscription Banner - Proxmox VE (v3 - Minimal Intrusive) -# ========================================================== -# This version makes a surgical change to the checked_command function -# by changing the condition to 'if (false)' and commenting out the banner logic. -# Also patches the mobile UI to remove the subscription dialog. -# ========================================================== - -set -euo pipefail - -# Source utilities if available -BASE_DIR="/usr/local/share/proxmenux" -UTILS_FILE="$BASE_DIR/utils.sh" -TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json" - -if [[ -f "$UTILS_FILE" ]]; then - source "$UTILS_FILE" -fi - -load_language -initialize_cache - -# File paths -JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" -GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz" -MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js" -MOBILE_UI_FILE="/usr/share/pve-yew-mobile-gui/index.html.tpl" -BACKUP_DIR="$BASE_DIR/backups" -APT_HOOK="/etc/apt/apt.conf.d/no-nag-script" -PATCH_BIN="/usr/local/bin/pve-remove-nag-v3.sh" -MARK="/* PROXMENUX_NAG_PATCH_V3 */" -MOBILE_MARK="" - -# Ensure tools JSON exists -ensure_tools_json() { - [ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON" -} - -# Register tool in JSON -register_tool() { - command -v jq >/dev/null 2>&1 || return 0 - local tool="$1" 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" -} - -# Verify JS file integrity -verify_js_integrity() { - local file="$1" - [ -f "$file" ] || return 1 - [ -s "$file" ] || return 1 - grep -Eq 'Ext|function|var|const|let' "$file" || return 1 - if LC_ALL=C grep -qP '\x00' "$file" 2>/dev/null; then - return 1 - fi - return 0 -} - -# Create timestamped backup -create_backup() { - local file="$1" - local timestamp - timestamp=$(date +%Y%m%d_%H%M%S) - local backup_file="$BACKUP_DIR/$(basename "$file").backup.$timestamp" - - mkdir -p "$BACKUP_DIR" - - if [ -f "$file" ]; then - rm -f "$BACKUP_DIR"/"$(basename "$file")".backup.* 2>/dev/null || true - - cp -a "$file" "$backup_file" - echo "$backup_file" - fi -} - -# Create the patch script that will be called by APT hook -create_patch_script() { - cat > "$PATCH_BIN" <<'EOFPATCH' -#!/usr/bin/env bash -# ========================================================== -# Proxmox Subscription Banner Patch (v3 - Minimal) -# ========================================================== -set -euo pipefail - -JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" -GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz" -MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js" -MOBILE_UI_FILE="/usr/share/pve-yew-mobile-gui/index.html.tpl" -BACKUP_DIR="/usr/local/share/proxmenux/backups" -MARK="/* PROXMENUX_NAG_PATCH_V3 */" -MOBILE_MARK="" - -verify_js_integrity() { - local file="$1" - [ -f "$file" ] && [ -s "$file" ] && grep -Eq 'Ext|function' "$file" && ! LC_ALL=C grep -qP '\x00' "$file" 2>/dev/null -} - -patch_checked_command() { - [ -f "$JS_FILE" ] || return 0 - - # Check if already patched - grep -q "$MARK" "$JS_FILE" && return 0 - - # Create backup - mkdir -p "$BACKUP_DIR" - local backup="$BACKUP_DIR/$(basename "$JS_FILE").backup.$(date +%Y%m%d_%H%M%S)" - cp -a "$JS_FILE" "$backup" - - # Set trap to restore on error - trap "cp -a '$backup' '$JS_FILE' 2>/dev/null || true" ERR - - # Add patch marker at the beginning - sed -i "1s|^|$MARK\n|" "$JS_FILE" - - # Surgical patch: Change the condition in checked_command function - # This changes the if condition to 'if (false)' making the banner never show - if grep -q "res\.data\.status\.toLowerCase() !== 'active'" "$JS_FILE"; then - # Pattern for newer versions (8.4.5+) - sed -i "/checked_command: function/,/},$/s/res === null || res === undefined || !res || res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE" - elif grep -q "res\.data\.status !== 'Active'" "$JS_FILE"; then - # Pattern for older versions - sed -i "/checked_command: function/,/},$/s/res === null || res === undefined || !res || res\.data\.status !== 'Active'/false/g" "$JS_FILE" - fi - - # Also handle the NoMoreNagging pattern if present - if grep -q "res\.data\.status\.toLowerCase() !== 'NoMoreNagging'" "$JS_FILE"; then - sed -i "/checked_command: function/,/},$/s/res === null || res === undefined || !res || res\.data\.status\.toLowerCase() !== 'NoMoreNagging'/false/g" "$JS_FILE" - fi - - # Verify integrity after patch - if ! verify_js_integrity "$JS_FILE"; then - cp -a "$backup" "$JS_FILE" - return 1 - fi - - # Clean up generated files - rm -f "$MIN_JS_FILE" "$GZ_FILE" 2>/dev/null || true - find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true - find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true - find /var/cache/nginx/ -type f -delete 2>/dev/null || true - - trap - ERR - return 0 -} - -patch_mobile_ui() { - [ -f "$MOBILE_UI_FILE" ] || return 0 - - # Check if already patched - grep -q "$MOBILE_MARK" "$MOBILE_UI_FILE" && return 0 - - # Create backup - mkdir -p "$BACKUP_DIR" - local backup="$BACKUP_DIR/$(basename "$MOBILE_UI_FILE").backup.$(date +%Y%m%d_%H%M%S)" - cp -a "$MOBILE_UI_FILE" "$backup" - - # Set trap to restore on error - trap "cp -a '$backup' '$MOBILE_UI_FILE' 2>/dev/null || true" ERR - - # Insert the script before tag - sed -i "/<\/head>/i\\ -$MOBILE_MARK\\ - \\ - " "$MOBILE_UI_FILE" - - trap - ERR - return 0 -} - -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 - } -} - -main() { - patch_checked_command || return 1 - patch_mobile_ui || true - reload_services -} - -main -EOFPATCH - - chmod 755 "$PATCH_BIN" -} - -# Create APT hook to reapply patch after updates -create_apt_hook() { - cat > "$APT_HOOK" <<'EOFAPT' -/* ProxMenux: reapply minimal nag patch after upgrades */ -DPkg::Post-Invoke { "/usr/local/bin/pve-remove-nag-v3.sh || true"; }; -EOFAPT - - chmod 644 "$APT_HOOK" - - # Verify APT hook syntax - apt-config dump >/dev/null 2>&1 || { - rm -f "$APT_HOOK" - } -} - -# Main function to remove subscription banner -remove_subscription_banner_v3() { - local pve_version - pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1 || echo "unknown") - - msg_info "$(translate "Detected Proxmox VE") ${pve_version} - $(translate "applying banner patch")" - - - - # Remove old APT hooks - for f in /etc/apt/apt.conf.d/*nag*; do - [[ -e "$f" ]] && rm -f "$f" - done - - # Create backup for desktop UI - local backup_file - backup_file=$(create_backup "$JS_FILE") - if [ -n "$backup_file" ]; then - # msg_ok "$(translate "Desktop UI backup created"): $backup_file" - : - fi - - if [ -f "$MOBILE_UI_FILE" ]; then - local mobile_backup - mobile_backup=$(create_backup "$MOBILE_UI_FILE") - if [ -n "$mobile_backup" ]; then - # msg_ok "$(translate "Mobile UI backup created"): $mobile_backup" - : - fi - fi - - # Create patch script and APT hook - create_patch_script - create_apt_hook - - # Apply the patch - if ! "$PATCH_BIN"; then - msg_error "$(translate "Error applying patch. Backups preserved at"): $BACKUP_DIR" - return 1 - fi - - # Register tool as applied - register_tool "subscription_banner" true - - msg_ok "$(translate "Subscription banner removed successfully")" - msg_ok "$(translate "Desktop and Mobile UI patched")" - msg_ok "$(translate "Refresh your browser (Ctrl+Shift+R) to see changes")" - -} - -# Run if executed directly -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - remove_subscription_banner_v3 -fi diff --git a/scripts/global/remove-banner-pve9.sh b/scripts/global/remove-banner-pve9.sh deleted file mode 100644 index 97948f60..00000000 --- a/scripts/global/remove-banner-pve9.sh +++ /dev/null @@ -1,124 +0,0 @@ -#!/bin/bash -# ========================================================== -# Remove Subscription Banner - Proxmox VE 9.x -# ========================================================== -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" -} - -remove_subscription_banner_pve9() { - local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" - local MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js" - local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz" - local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script" - - - local pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1) - local pve_major=$(echo "$pve_version" | cut -d. -f1) - - if [ "$pve_major" -lt 9 ] 2>/dev/null; then - msg_error "This script is for PVE 9.x only. Detected PVE $pve_version" - return 1 - fi - - msg_info "Detected Proxmox VE $pve_version - Applying PVE 9.x patches" - - - if [ ! -f "$JS_FILE" ]; then - msg_error "JavaScript file not found: $JS_FILE" - return 1 - fi - - - - - local backup_file="${JS_FILE}.backup.pve9.$(date +%Y%m%d_%H%M%S)" - cp "$JS_FILE" "$backup_file" - - - for f in /etc/apt/apt.conf.d/*nag*; do - [[ -e "$f" ]] && rm -f "$f" - done - - [[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE" - [[ -f "$MIN_JS_FILE" ]] && rm -f "$MIN_JS_FILE" - - find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true - find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true - find /var/cache/nginx/ -type f -delete 2>/dev/null || true - - - sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE" - sed -i "s/subscriptionActive: ''/subscriptionActive: true/g" "$JS_FILE" - sed -i "s/title: gettext('No valid subscription')/title: gettext('Community Edition')/g" "$JS_FILE" - - - sed -i "s/You do not have a valid subscription for this server/Community Edition - No subscription required/g" "$JS_FILE" - sed -i "s/Enterprise repository needs valid subscription/Enterprise repository configured/g" "$JS_FILE" - sed -i "s/icon: Ext\.Msg\.WARNING/icon: Ext.Msg.INFO/g" "$JS_FILE" - - - sed -i "s/subscription = !(/subscription = false \&\& (/g" "$JS_FILE" - - if grep -q "res\.data\.status\.toLowerCase() !== 'active'" "$JS_FILE"; then - msg_warn "Some patches may not have applied correctly, retrying..." - sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE" - fi - - - [[ -f "$APT_HOOK" ]] && rm -f "$APT_HOOK" - cat > "$APT_HOOK" << 'EOF' -DPkg::Post-Invoke { - "test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/res\\.data\\.status\\.toLowerCase() !== '\''active'\''/false/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true"; - "test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscriptionActive: '\'\'\''/subscriptionActive: true/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true"; - "test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/title: gettext('\''No valid subscription'\'')/title: gettext('\''Community Edition'\'')/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true"; - "test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscription = !(/subscription = false \\&\\& (/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true"; - "rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz || true"; -}; -EOF - - chmod 644 "$APT_HOOK" - - - if ! apt-config dump >/dev/null 2>&1; then - msg_warn "APT hook has syntax issues, removing..." - rm -f "$APT_HOOK" - else - msg_ok "APT hook created successfully" - fi - - - - systemctl reload nginx 2>/dev/null || true - - msg_ok "Subscription banner removed successfully for Proxmox VE $pve_version" - msg_ok "Banner removal process completed - refresh your browser to see changes" - - register_tool "subscription_banner" true -} - - -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - remove_subscription_banner_pve9 -fi diff --git a/scripts/global/remove-banner-pve9_.sh b/scripts/global/remove-banner-pve9_.sh deleted file mode 100644 index 8183edb8..00000000 --- a/scripts/global/remove-banner-pve9_.sh +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/bash -# ========================================================== -# Remove Subscription Banner - Proxmox VE 9.x ONLY -# ========================================================== -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 - -# Tool registration system -ensure_tools_json() { - [ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON" -} - -register_tool() { - local tool="$1" - local state="$2" - ensure_tools_json - jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON" -} - -remove_subscription_banner_pve9() { - local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" - local MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js" - local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz" - local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script" - - # Verify PVE 9.x - local pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1) - local pve_major=$(echo "$pve_version" | cut -d. -f1) - - if [ "$pve_major" -lt 9 ] 2>/dev/null; then - msg_error "This script is for PVE 9.x only. Detected PVE $pve_version" - return 1 - fi - - msg_info "Detected Proxmox VE $pve_version - Applying PVE 9.x patches" - - # Verify that the file exists - if [ ! -f "$JS_FILE" ]; then - msg_error "JavaScript file not found: $JS_FILE" - return 1 - fi - - - # Create backup of original file - local backup_file="${JS_FILE}.backup.pve9.$(date +%Y%m%d_%H%M%S)" - cp "$JS_FILE" "$backup_file" - - # Clean any existing problematic APT hooks - for f in /etc/apt/apt.conf.d/*nag*; do - [[ -e "$f" ]] && rm -f "$f" - done - - - # Main subscription check patches for PVE 9 - sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE" - sed -i "s/subscriptionActive: ''/subscriptionActive: true/g" "$JS_FILE" - sed -i "s/title: gettext('No valid subscription')/title: gettext('Community Edition')/g" "$JS_FILE" - - # Additional UX improvements for PVE 9 - sed -i "s/You do not have a valid subscription for this server/Community Edition - No subscription required/g" "$JS_FILE" - sed -i "s/Enterprise repository needs valid subscription/Enterprise repository configured/g" "$JS_FILE" - sed -i "s/icon: Ext\.Msg\.WARNING/icon: Ext.Msg.INFO/g" "$JS_FILE" - - # Additional subscription patterns that may exist in PVE 9 - sed -i "s/subscription = !(/subscription = false \&\& (/g" "$JS_FILE" - - # Remove compressed/minified files to force regeneration - [[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE" - [[ -f "$MIN_JS_FILE" ]] && rm -f "$MIN_JS_FILE" - - # Clear various caches - find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true - find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true - - # Create PVE 9.x specific APT hook - [[ -f "$APT_HOOK" ]] && rm -f "$APT_HOOK" - cat > "$APT_HOOK" << 'EOF' -DPkg::Post-Invoke { - "test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/res\\.data\\.status\\.toLowerCase() !== '\''active'\''/false/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true"; - "test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscriptionActive: '\'\'\''/subscriptionActive: true/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true"; - "test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/title: gettext('\''No valid subscription'\'')/title: gettext('\''Community Edition'\'')/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true"; - "test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscription = !(/subscription = false \\&\\& (/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true"; - "rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz || true"; -}; -EOF - - chmod 644 "$APT_HOOK" - - # Verify APT hook syntax - if ! apt-config dump >/dev/null 2>&1; then - msg_warn "APT hook has syntax issues, removing..." - rm -f "$APT_HOOK" - else - msg_ok "APT hook created successfully" - fi - - - msg_ok "Subscription banner removed successfully for Proxmox VE $pve_version" - msg_ok "Banner removal process completed" - - - register_tool "subscription_banner" true -} - - -# Execute function if called directly -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - remove_subscription_banner_pve9 -fi diff --git a/scripts/global/remove-banner-pve9_2.sh b/scripts/global/remove-banner-pve9_2.sh deleted file mode 100644 index db4a556f..00000000 --- a/scripts/global/remove-banner-pve9_2.sh +++ /dev/null @@ -1,257 +0,0 @@ -#!/bin/bash -# ========================================================== -# Remove Subscription Banner - Proxmox VE 9.x (Clean Version) -# ========================================================== - -set -euo pipefail - - -BASE_DIR="/usr/local/share/proxmenux" -UTILS_FILE="$BASE_DIR/utils.sh" -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() { - command -v jq >/dev/null 2>&1 || return 0 - local tool="$1" 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" -} - -JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" -MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js" -GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz" -MOBILE_TPL="/usr/share/pve-yew-mobile-gui/index.html.tpl" -APT_HOOK="/etc/apt/apt.conf.d/no-nag-script" -PATCH_BIN="/usr/local/bin/pve-remove-nag.sh" - -MARK_JS="PROXMENUX_NAG_REMOVED_v2" -MARK_MOBILE="" - - -verify_js_integrity() { - local file="$1" - [ -f "$file" ] || return 1 - [ -s "$file" ] || return 1 - grep -Eq 'Ext|function|var|const|let' "$file" || return 1 - if LC_ALL=C grep -qP '\x00' "$file" 2>/dev/null; then - return 1 - fi - return 0 -} - -create_backup() { - local file="$1" - local backup_dir="$BASE_DIR/backups" - local timestamp - timestamp=$(date +%Y%m%d_%H%M%S) - local backup_file="$backup_dir/$(basename "$file").backup.$timestamp" - mkdir -p "$backup_dir" - if [ -f "$file" ]; then - cp -a "$file" "$backup_file" - ls -t "$backup_dir"/"$(basename "$file")".backup.* 2>/dev/null | tail -n +6 | xargs -r rm -f 2>/dev/null || true - echo "$backup_file" - fi -} - -# ---------------------------------------------------- - -create_patch_script() { - cat > "$PATCH_BIN" <<'EOF' -#!/usr/bin/env bash -set -euo pipefail - -JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" -MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js" -GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz" -MOBILE_TPL="/usr/share/pve-yew-mobile-gui/index.html.tpl" -MARK_JS="PROXMENUX_NAG_REMOVED_v2" -MARK_MOBILE="" -BASE_DIR="/usr/local/share/proxmenux" - -verify_js_integrity() { - local file="$1" - [ -f "$file" ] && [ -s "$file" ] && grep -Eq 'Ext|function' "$file" && ! LC_ALL=C grep -qP '\x00' "$file" 2>/dev/null -} - -patch_web() { - [ -f "$JS_FILE" ] || return 0 - grep -q "$MARK_JS" "$JS_FILE" && return 0 - - local backup_dir="$BASE_DIR/backups" - mkdir -p "$backup_dir" - local backup="$backup_dir/$(basename "$JS_FILE").backup.$(date +%Y%m%d_%H%M%S)" - cp -a "$JS_FILE" "$backup" - trap "cp -a '$backup' '$JS_FILE' 2>/dev/null || true" ERR - - sed -i '1s|^|/* '"$MARK_JS"' */\n|' "$JS_FILE" - - local patterns_found=0 - - if grep -q "res\.data\.status\.toLowerCase() !== 'active'" "$JS_FILE"; then - sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE" - patterns_found=$((patterns_found + 1)) - fi - - if grep -q "subscriptionActive: ''" "$JS_FILE"; then - sed -i "s/subscriptionActive: ''/subscriptionActive: true/g" "$JS_FILE" - patterns_found=$((patterns_found + 1)) - fi - - if grep -q "title: gettext('No valid subscription')" "$JS_FILE"; then - sed -i "s/title: gettext('No valid subscription')/title: gettext('Community Edition')/g" "$JS_FILE" - patterns_found=$((patterns_found + 1)) - fi - - if grep -q "icon: Ext\.Msg\.WARNING" "$JS_FILE"; then - sed -i "s/icon: Ext\.Msg\.WARNING/icon: Ext.Msg.INFO/g" "$JS_FILE" - patterns_found=$((patterns_found + 1)) - fi - - if grep -q "subscription = !(" "$JS_FILE"; then - sed -i "s/subscription = !(/subscription = false \&\& (/g" "$JS_FILE" - patterns_found=$((patterns_found + 1)) - fi - - # Si nada coincidió (cambio upstream), restaura y sal limpio - if [ "${patterns_found:-0}" -eq 0 ]; then - cp -a "$backup" "$JS_FILE" - return 0 - fi - - # Verificación final - if ! verify_js_integrity "$JS_FILE"; then - cp -a "$backup" "$JS_FILE" - return 1 - fi - - # Limpiar artefactos/cachés - rm -f "$MIN_JS_FILE" "$GZ_FILE" 2>/dev/null || true - find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true - find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true - find /var/cache/nginx/ -type f -delete 2>/dev/null || true - - trap - ERR -} - -patch_mobile() { - [ -f "$MOBILE_TPL" ] || return 0 - grep -q "$MARK_MOBILE" "$MOBILE_TPL" && return 0 - - local backup_dir="$BASE_DIR/backups" - mkdir -p "$backup_dir" - cp -a "$MOBILE_TPL" "$backup_dir/$(basename "$MOBILE_TPL").backup.$(date +%Y%m%d_%H%M%S)" - - cat >> "$MOBILE_TPL" < -(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="
- - - - - -
-ProxMenux Logo - -

Synology DSM VM

-

Created with ProxMenuX

-

Loader: $LOADER_NAME

-
- -

-Docs -Code -Loader -Ko-fi -

- -
-${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="
- - - - - -
-ProxMenux Logo - -

Synology DSM VM

-

Created with ProxMenuX

-

Loader: $LOADER_NAME

-
- -

-Docs -Code -Loader -Ko-fi -

- -
-${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