From e637a5327c967373520cf999cd826c5cb7c0828d Mon Sep 17 00:00:00 2001 From: MacRimi Date: Thu, 1 May 2025 23:35:19 +0200 Subject: [PATCH] Update menu create VM --- scripts/vm/create_vm.sh | 156 +++++++++++++++ scripts/vm/disk_selector.sh | 280 ++++++++++++++++++++++++++ scripts/vm/guest_agent_config.sh | 49 +++++ scripts/vm/select_linux_iso.sh | 239 ++++++++++++++++++++++ scripts/vm/select_nas_iso.sh | 94 +++++++++ scripts/vm/select_windows_iso.sh | 88 +++++++++ scripts/vm/uupdump_creator.sh | 162 +++++++++++++++ scripts/vm/vm_configurator.sh | 229 ++++++++++++++++++++++ scripts/vm/vm_creator.sh | 327 +++++++++++++++++++++++++++++++ 9 files changed, 1624 insertions(+) create mode 100644 scripts/vm/create_vm.sh create mode 100644 scripts/vm/disk_selector.sh create mode 100644 scripts/vm/guest_agent_config.sh create mode 100644 scripts/vm/select_linux_iso.sh create mode 100644 scripts/vm/select_nas_iso.sh create mode 100644 scripts/vm/select_windows_iso.sh create mode 100644 scripts/vm/uupdump_creator.sh create mode 100644 scripts/vm/vm_configurator.sh create mode 100644 scripts/vm/vm_creator.sh diff --git a/scripts/vm/create_vm.sh b/scripts/vm/create_vm.sh new file mode 100644 index 0000000..220024e --- /dev/null +++ b/scripts/vm/create_vm.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash + +# ================================================ +# ProxMenux - Create VM Entry Point +# ================================================ +# Author : MacRimi +# ================================================ + +REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" +VM_REPO="$REPO_URL/scripts/vm" +MENU_REPO="$REPO_URL/scripts/menus" +BASE_DIR="/usr/local/share/proxmenux" +UTILS_FILE="$BASE_DIR/utils.sh" +VENV_PATH="/opt/googletrans-env" + +[[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE" + + +source <(curl -s "$VM_REPO/vm_configurator.sh") +source <(curl -s "$VM_REPO/disk_selector.sh") +source <(curl -s "$VM_REPO/vm_creator.sh") +source <(curl -s "$VM_REPO/guest_agent_config.sh") + + +if [[ -f "$UTILS_FILE" ]]; then + source "$UTILS_FILE" +fi + +load_language +initialize_cache + +# Load modules +[[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE" +[[ -f "$VM_CONFIG" ]] && source "$VM_CONFIG" +[[ -f "$DISK_SELECTOR" ]] && source "$DISK_SELECTOR" +[[ -f "$VM_CREATOR" ]] && source "$VM_CREATOR" +[[ -f "$LINUX_ISO" ]] && source "$LINUX_ISO" +[[ -f "$GUEST_AGENT" ]] && source "$GUEST_AGENT" + + + + +function header_info() { + clear + show_proxmenux_logo + echo -e "${BL}╔═══════════════════════════════════════════════╗${CL}" + echo -e "${BL}║ ║${CL}" + echo -e "${BL}║${YWB} ProxMenux VM Creator ${BL}║${CL}" + echo -e "${BL}║ ║${CL}" + echo -e "${BL}╚═══════════════════════════════════════════════╝${CL}" + echo -e +} + +# ========================================================== +# MAIN EXECUTION +# ========================================================== + +header_info +echo -e "\n Loading..." +sleep 1 + + + + +function start_vm_configuration() { + + if (whiptail --title "ProxMenux" --yesno "$(translate "Use Default Settings?")" --no-button "$(translate "Advanced")" 10 60); then + header_info + load_default_vm_config "$OS_TYPE" + + if [[ -z "$HN" ]]; then + HN=$(whiptail --inputbox "$(translate "Enter a name for the new virtual machine:")" 10 60 --title "VM Hostname" 3>&1 1>&2 2>&3) + [[ -z "$HN" ]] && HN="custom-vm" + fi + + apply_default_vm_config + else + header_info + echo -e "${CUS}$(translate "Using advanced configuration")${CL}" + configure_vm_advanced "$OS_TYPE" + fi +} + + + +while true; do + OS_TYPE=$(dialog --backtitle "ProxMenux" \ + --title "$(translate "Select System Type")" \ + --menu "$(translate "Choose the type of virtual system to install:")" 18 64 8 \ + 1 "$(translate "Create") VM System NAS" \ + 2 "$(translate "Create") VM System Windows" \ + 3 "$(translate "Create") VM System Linux" \ + 4 "$(translate "Create") VM System Others (based Linux)" \ + 5 "$(translate "Return to Main Menu")" \ + 3>&1 1>&2 2>&3) + + + [[ $? -ne 0 || "$OS_TYPE" == "5" ]] && exec bash <(curl -s "$MENU_REPO/main_menu.sh") + + case "$OS_TYPE" in + 1) + source <(curl -fsSL "$ISO_REPO/select_nas_iso.sh") && select_nas_iso || continue + ;; + 2) + source <(curl -fsSL "$ISO_REPO/select_windows_iso.sh") && select_windows_iso || continue + ;; + 3) + source <(curl -fsSL "$ISO_REPO/select_linux_iso.sh") && select_linux_iso || continue + ;; + 4) + source <(curl -fsSL "$ISO_REPO/select_linux_iso.sh") && select_linux_other_scripts || continue + ;; + esac + + + if ! confirm_vm_creation; then + continue + fi + + + start_vm_configuration || continue + + + select_disk_type + if [[ -z "$DISK_TYPE" ]]; then + msg_error "$(translate "Disk type selection failed or cancelled")" + continue + fi + + create_vm + break +done + + + + + +function start_vm_configuration() { + + if (whiptail --title "ProxMenux" --yesno "$(translate "Use Default Settings?")" --no-button "$(translate "Advanced")" 10 60); then + header_info + load_default_vm_config "$OS_TYPE" + + if [[ -z "$HN" ]]; then + HN=$(whiptail --inputbox "$(translate "Enter a name for the new virtual machine:")" 10 60 --title "VM Hostname" 3>&1 1>&2 2>&3) + [[ -z "$HN" ]] && HN="custom-vm" + fi + + apply_default_vm_config + else + header_info + echo -e "${CUS}$(translate "Using advanced configuration")${CL}" + configure_vm_advanced "$OS_TYPE" + fi +} + diff --git a/scripts/vm/disk_selector.sh b/scripts/vm/disk_selector.sh new file mode 100644 index 0000000..82cb0fb --- /dev/null +++ b/scripts/vm/disk_selector.sh @@ -0,0 +1,280 @@ +#!/usr/bin/env bash + +# ========================================================== +# Disk Selector Module - ProxMenux +# ========================================================== +# Reutiliza la lógica original de selección de discos +# virtuales y físicos con integración de traducciones +# ========================================================== + + +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 + +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) + + [[ -z "$DISK_TYPE" ]] && return 1 + + 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 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 + + +} +# ========================================================== \ No newline at end of file diff --git a/scripts/vm/guest_agent_config.sh b/scripts/vm/guest_agent_config.sh new file mode 100644 index 0000000..92cb0f8 --- /dev/null +++ b/scripts/vm/guest_agent_config.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +# ========================================================== +# Guest Agent Configurator - ProxMenux +# ========================================================== +# Añade soporte al QEMU Guest Agent y dispositivos útiles. +# Se adapta según el sistema operativo. +# ========================================================== + +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 + + + +function configure_guest_agent() { + if [[ -z "$VMID" ]]; then + msg_error "$(translate "No VMID defined. Cannot apply guest agent config.")" + return 1 + fi + + msg_info "$(translate "Adding QEMU Guest Agent support...")" + + # Habilitar el agente en la VM + qm set "$VMID" -agent enabled=1 >/dev/null 2>&1 + + # Añadir canal de comunicación virtio + qm set "$VMID" -chardev socket,id=qga0,path=/var/run/qemu-server/$VMID.qga,server=on,wait=off >/dev/null 2>&1 + qm set "$VMID" -device virtio-serial-pci -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 >/dev/null 2>&1 + + msg_ok "$(translate "Guest Agent configuration applied")" + + if [[ "$OS_TYPE" == "windows" ]]; then + echo -e "${YW}$(translate "Reminder: You must install the QEMU Guest Agent inside the Windows VM")${NC}" + echo -e "${YW}$(translate "Tip: Also mount the VirtIO ISO for drivers and guest agent installer")${NC}" + echo -e "${TAB}- https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/" + elif [[ "$OS_TYPE" == "linux" || "$OS_TYPE" == "lite" ]]; then + echo -e "${YW}$(translate "Tip: You can install the QEMU Guest Agent inside the VM with:")${NC}" + echo -e "${TAB}apt install qemu-guest-agent -y && systemctl enable --now qemu-guest-agent" + fi +} + diff --git a/scripts/vm/select_linux_iso.sh b/scripts/vm/select_linux_iso.sh new file mode 100644 index 0000000..af257c3 --- /dev/null +++ b/scripts/vm/select_linux_iso.sh @@ -0,0 +1,239 @@ +#!/usr/bin/env bash + +# ============================================================== +# ProxMenux - Linux Installer Entry +# ============================================================== + +# Configuración base y entorno +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" +ISO_DIR="/var/lib/vz/template/iso" + +[[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE" +load_language +initialize_cache + +# ============================================================== +# FUNCIONES PRINCIPALES +# ============================================================== + +function select_linux_iso() { + local EXIT_FLAG="no" + + while [[ "$EXIT_FLAG" != "yes" ]]; do + CHOICE=$(dialog --backtitle "ProxMenux" \ + --title "$(translate "Linux Installation Options")" \ + --menu "$(translate "Select the type of Linux installation:")" 15 70 6 \ + 1 "$(translate "Install from ISO (Ubuntu, Debian, Fedora...)")" \ + 2 "$(translate "Install with Cloud-Init script (Automated)")" \ + 3 "$(translate "Use custom ISO from storage")" \ + 4 "$(translate "Back to Main Menu")" \ + 3>&1 1>&2 2>&3) + + if [[ $? -ne 0 ]] || [[ "$CHOICE" == "4" ]]; then + unset ISO_NAME ISO_TYPE ISO_URL ISO_FILE ISO_PATH HN + return 1 + fi + + case "$CHOICE" in + 1) + select_linux_iso_official && EXIT_FLAG="yes" + ;; + 2) + select_linux_cloudinit + ;; + 3) + select_linux_custom_iso && EXIT_FLAG="yes" + ;; + 4) + return 1 + ;; + esac + done + + return 0 +} + + + + +function select_linux_iso_official() { + DISTROS=( + "Ubuntu 22.04|Desktop|ProxMenux|https://releases.ubuntu.com/22.04/ubuntu-22.04.5-desktop-amd64.iso" + "Ubuntu 20.04|Desktop|ProxMenux|https://releases.ubuntu.com/20.04/ubuntu-20.04.6-desktop-amd64.iso" + "Ubuntu 22.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso" + "Ubuntu 20.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/20.04/ubuntu-20.04.6-live-server-amd64.iso" + "Debian 12|Desktop|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-12.10.0-amd64-DVD-1.iso" + "Debian 11|Desktop|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-dvd/debian-11.11.0-amd64-DVD-1.iso" + "Debian 12 Netinst|CLI|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.10.0-amd64-netinst.iso" + "Debian 11 Netinst|CLI|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-cd/debian-11.11.0-amd64-netinst.iso" + "Fedora Workstation 42|Desktop|ProxMenux|https://download.fedoraproject.org/pub/fedora/linux/releases/42/Workstation/x86_64/iso/Fedora-Workstation-Live-42-1.1.x86_64.iso" + "Rocky Linux 9.5|Desktop|ProxMenux|https://download.rockylinux.org/pub/rocky/9/isos/x86_64/Rocky-9.5-x86_64-dvd.iso" + "Linux Mint 22.1|Desktop|ProxMenux|https://mirrors.edge.kernel.org/linuxmint/stable/22.1/linuxmint-22.1-cinnamon-64bit.iso" + "openSUSE Leap 15.6|Desktop|ProxMenux|https://download.opensuse.org/distribution/leap/15.6/iso/openSUSE-Leap-15.6-DVD-x86_64-Media.iso" + "Alpine Linux 3.21|Desktop|ProxMenux|https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/x86_64/alpine-virt-3.21.3-x86_64.iso" + "Kali Linux 2025.1|Desktop|ProxMenux|https://cdimage.kali.org/kali-2025.1c/kali-linux-2025.1c-qemu-amd64.7z" + "Manjaro 25.0|Desktop|ProxMenux|https://download.manjaro.org/gnome/25.0.0/manjaro-gnome-25.0.0-250414-linux612.iso" + ) + + MENU_OPTIONS=() + INDEX=0 + for entry in "${DISTROS[@]}"; do + IFS='|' read -r NAME TYPE SOURCE URL <<< "$entry" + LINE=$(printf "%-40s │ %-10s │ %s" "$NAME" "$TYPE" "$SOURCE") + MENU_OPTIONS+=("$INDEX" "$LINE") + URLS[$INDEX]="$entry" + ((INDEX++)) + done + + HEADER="%-42s │ %-10s │ %s" + HEADER_TEXT=$(printf "$HEADER" "Distribution" "Type" "Source") + + CHOICE=$(dialog --backtitle "ProxMenux" \ + --title "$(translate "Official Linux Distributions")" \ + --menu "$(translate "Select the Linux distribution to install:")\n\n$HEADER_TEXT" 20 90 12 \ + "${MENU_OPTIONS[@]}" \ + 3>&1 1>&2 2>&3) + + [[ $? -ne 0 ]] && return 1 + + SELECTED="${URLS[$CHOICE]}" + IFS='|' read -r ISO_NAME ISO_TYPE SOURCE ISO_URL <<< "$SELECTED" + ISO_FILE=$(basename "$ISO_URL") + ISO_PATH="$ISO_DIR/$ISO_FILE" + + HN=$(echo "$ISO_NAME" | \ + sed 's/ (.*)//' | \ + tr -cs '[:alnum:]' '-' | \ + sed 's/^-*//' | \ + sed 's/-*$//' | \ + cut -c1-63) + + export ISO_NAME ISO_TYPE ISO_URL ISO_FILE ISO_PATH HN + return 0 +} + + +function select_linux_cloudinit() { + local CLOUDINIT_OPTIONS=( + "1" "Arch Linux (Cloud-Init automated) │ Helper Scripts" + "2" "Debian 12 (Cloud-Init automated) │ Helper Scripts" + "3" "Ubuntu 22.04 (Cloud-Init automated) │ Helper Scripts" + "4" "Ubuntu 24.04 (Cloud-Init automated) │ Helper Scripts" + "5" "Ubuntu 24.10 (Cloud-Init automated) │ Helper Scripts" + "6" "$(translate "Return to Main Menu")" + ) + + local script_selection + script_selection=$(dialog --backtitle "ProxMenux" --title "$(translate "Cloud-Init Automated Installers")" \ + --menu "$(translate "Select a pre-configured Linux VM script to execute:")" 20 78 10 \ + "${CLOUDINIT_OPTIONS[@]}" 3>&1 1>&2 2>&3) + + [[ $? -ne 0 ]] && return + + case "$script_selection" in + 1) + bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/archlinux-vm.sh") + ;; + 2) + bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh") + ;; + 3) + bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2204-vm.sh") + ;; + 4) + bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2404-vm.sh") + ;; + 5) + bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2410-vm.sh") + ;; + 6) + return + ;; + esac + + msg_success "$(translate "Press Enter to return to menu...")" + read -r + + whiptail --title "Proxmox VE Helper-Scripts" \ + --msgbox "$(translate "Visit the website to discover more scripts, stay updated with the latest updates, and support the project:\n\nhttps://community-scripts.github.io/ProxmoxVE")" 15 70 + + exec bash <(curl -s "$REPO_URL/scripts/vm/create_vm.sh") +} + + +function select_linux_custom_iso() { + ISO_LIST=() + while read -r line; do + FILENAME=$(basename "$line") + SIZE=$(du -h "$line" | cut -f1) + ISO_LIST+=("$FILENAME" "$SIZE") + done < <(find "$ISO_DIR" -type f -iname "*.iso" | sort) + + if [[ ${#ISO_LIST[@]} -eq 0 ]]; then + msg_error "$(translate "No ISO images found in $ISO_DIR.")" + sleep 2 + return 1 + fi + + ISO_FILE=$(dialog --backtitle "ProxMenux" --title "$(translate "Available ISO Images")" \ + --menu "$(translate "Select a custom ISO to use:")" 20 70 10 \ + "${ISO_LIST[@]}" 3>&1 1>&2 2>&3) + + if [[ -z "$ISO_FILE" ]]; then + msg_warn "$(translate "No ISO selected.")" + return 1 + fi + + ISO_PATH="$ISO_DIR/$ISO_FILE" + ISO_NAME="$ISO_FILE" + + export ISO_PATH ISO_FILE ISO_NAME + return 0 +} + +function select_linux_other_scripts() { + local OTHER_OPTIONS=( + "1" "Home Assistant OS VM (HAOS) │ Helper Scripts" + "2" "Docker VM (Debian + SSH + Docker) │ Helper Scripts" + "3" "$(translate "Return to Main Menu")" + ) + + local choice + choice=$(dialog --backtitle "ProxMenux" \ + --title "$(translate "Other Prebuilt Linux VMs")" \ + --menu "$(translate "Select one of the ready-to-run Linux VMs:")" 18 78 10 \ + "${OTHER_OPTIONS[@]}" 3>&1 1>&2 2>&3) + + [[ $? -ne 0 ]] && return + + case "$choice" in + 1) + bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/haos-vm.sh)" + ;; + 2) + bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/docker-vm.sh)" + + # Mostrar credenciales + whiptail --title "Docker VM Info" \ + --msgbox "$(translate "Default Login Credentials:\n\nUsername: root\nPassword: docker")" 12 50 + ;; + 3) + return + ;; + esac + + msg_success "$(translate "Press Enter to return to menu...")" + read -r + + whiptail --title "Proxmox VE Helper-Scripts" \ + --msgbox "$(translate "Visit the website to discover more scripts, stay updated with the latest updates, and support the project:\n\nhttps://community-scripts.github.io/ProxmoxVE")" 15 70 + + exec bash <(curl -s "$REPO_URL/scripts/vm/create_vm.sh") +} + + + + diff --git a/scripts/vm/select_nas_iso.sh b/scripts/vm/select_nas_iso.sh new file mode 100644 index 0000000..16f85e5 --- /dev/null +++ b/scripts/vm/select_nas_iso.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +# ============================================================== +# ProxMenux - NAS ISO Selector (Dialog Edition) +# ============================================================== + +# Configuración Base +BASE_DIR="/usr/local/share/proxmenux" +UTILS_FILE="$BASE_DIR/utils.sh" +VENV_PATH="/opt/googletrans-env" +REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" + +[[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE" +load_language +initialize_cache + +ISO_DIR="/var/lib/vz/template/iso" +mkdir -p "$ISO_DIR" + +function select_nas_iso() { + + local NAS_OPTIONS=( + "1" "Synology DSM VM" + "2" "TrueNAS SCALE VM (Fangtooth)" + "3" "TrueNAS CORE VM (FreeBSD based)" + "4" "OpenMediaVault VM (Debian based)" + "5" "Rockstor VM (openSUSE based)" + "6" "ZimaOS VM (R0GGER proxmox-zimaos)" + "7" "$(translate "Return to Main Menu")" + ) + + local NAS_TYPE + NAS_TYPE=$(dialog --backtitle "ProxMenux" \ + --title "$(translate "NAS Systems")" \ + --menu "$(translate "Select the NAS system to install:")" 18 64 10 \ + "${NAS_OPTIONS[@]}" 3>&1 1>&2 2>&3) + + # Si se pulsa ESC o Cancelar + [[ $? -ne 0 ]] && return 1 + + case "$NAS_TYPE" in + 1) + bash <(curl -s "$REPO_URL/scripts/vm/synology.sh") + msg_success "$(translate "Press Enter to return to menu...")" + read -r + return 1 + ;; + 2) + ISO_NAME="TrueNAS SCALE 25 (Fangtooth)" + ISO_URL="https://download.truenas.com/TrueNAS-SCALE-Fangtooth/25.04.0/TrueNAS-SCALE-25.04.0.iso" + ISO_FILE="TrueNAS-SCALE-25.04.0.iso" + ISO_PATH="$ISO_DIR/$ISO_FILE" + HN="TrueNAS-Scale" + ;; + 3) + ISO_NAME="TrueNAS CORE 13.3" + ISO_URL="https://download.freenas.org/13.3/STABLE/latest/x64/TrueNAS-13.3-U1.2.iso" + ISO_FILE="TrueNAS-13.3-U1.2.iso" + ISO_PATH="$ISO_DIR/$ISO_FILE" + HN="TrueNAS-Core" + ;; + 4) + ISO_NAME="OpenMediaVault 7.4.17" + ISO_URL="https://sourceforge.net/projects/openmediavault/files/iso/7.4.17/openmediavault_7.4.17-amd64.iso/download" + ISO_FILE="openmediavault_7.4.17-amd64.iso" + ISO_PATH="$ISO_DIR/$ISO_FILE" + HN="OpenMediaVault" + ;; + 5) + ISO_NAME="Rockstor" + ISO_URL="https://rockstor.com/downloads/installer/leap/15.6/x86_64/Rockstor-Leap15.6-generic.x86_64-5.0.15-0.install.iso" + ISO_FILE="Rockstor-Leap15.6-generic.x86_64-5.0.15-0.install.iso" + ISO_PATH="$ISO_DIR/$ISO_FILE" + HN="Rockstor" + ;; + 6) + bash -c "$(wget -qLO - https://raw.githubusercontent.com/R0GGER/proxmox-zimaos/refs/heads/main/zimaos_zimacube.sh)" + msg_success "$(translate "Press Enter to return to menu...")" + read -r + + whiptail --title "Proxmox VE - ZimaOS" \ + --msgbox "$(translate "ZimaOS installer script by R0GGER\n\nVisit the GitHub repo to learn more, contribute, or support the project:\n\nhttps://github.com/R0GGER/proxmox-zimaos")" 15 70 + + return 1 + ;; + + 7) + return 1 + ;; + esac + + export ISO_NAME ISO_URL ISO_FILE ISO_PATH HN + return 0 +} diff --git a/scripts/vm/select_windows_iso.sh b/scripts/vm/select_windows_iso.sh new file mode 100644 index 0000000..2b4f470 --- /dev/null +++ b/scripts/vm/select_windows_iso.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +# ============================================================== +# ProxMenux - Windows ISO Selector (Dialog Edition) +# ============================================================== + +BASE_DIR="/usr/local/share/proxmenux" +UTILS_FILE="$BASE_DIR/utils.sh" +VENV_PATH="/opt/googletrans-env" +ISO_DIR="/var/lib/vz/template/iso" + +[[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE" +load_language +initialize_cache +mkdir -p "$ISO_DIR" + +function select_windows_iso() { + local CHOICE + CHOICE=$(dialog --backtitle "ProxMenux" --title "$(translate "Windows ISO")" \ + --menu "$(translate "Select how to provide the Windows ISO:")" 15 60 5 \ + 1 "$(translate "Use existing ISO from storage")" \ + 2 "$(translate "Download ISO using UUP Dump")" \ + 3 "$(translate "Return to Main Menu")" 3>&1 1>&2 2>&3) + + [[ $? -ne 0 ]] && return 1 # ESC o cancelar + + case "$CHOICE" in + 1) + select_existing_iso || return 1 + ;; + 2) + if source <(curl -fsSL "$UUP_REPO/uupdump_creator.sh"); then + run_uupdump_creator || return 1 + detect_latest_iso_created || return 1 + else + msg_error "$(translate "UUP Dump script not found.")" + return 1 + fi + ;; + 3) + return 1 + ;; + esac + return 0 +} + +function select_existing_iso() { + ISO_LIST=() + while read -r line; do + FILENAME=$(basename "$line") + SIZE=$(du -h "$line" | cut -f1) + ISO_LIST+=("$FILENAME" "$SIZE") + done < <(find "$ISO_DIR" -type f -iname "*.iso" ! -iname "virtio*" | sort) + + if [[ ${#ISO_LIST[@]} -eq 0 ]]; then + msg_error "$(translate "No ISO images found in $ISO_DIR.")" + sleep 2 + return 1 + fi + + ISO_FILE=$(dialog --backtitle "ProxMenux" --title "$(translate "Available ISO Images")" \ + --menu "$(translate "Choose a Windows ISO to use:")" 20 70 10 \ + "${ISO_LIST[@]}" 3>&1 1>&2 2>&3) + + [[ -z "$ISO_FILE" ]] && msg_warn "$(translate "No ISO selected.")" && return 1 + + ISO_PATH="$ISO_DIR/$ISO_FILE" + ISO_NAME="$ISO_FILE" + + export ISO_PATH ISO_FILE ISO_NAME + return 0 +} + +function detect_latest_iso_created() { + ISO_FILE=$(find "$ISO_DIR" -maxdepth 1 -type f -iname "*.iso" ! -iname "virtio*" -printf "%T@ %p\n" | sort -n | awk '{print $2}' | tail -n 1) + + if [[ -z "$ISO_FILE" ]]; then + msg_error "$(translate "No ISO file detected after UUP Dump process.")" + sleep 2 + return 1 + fi + + ISO_NAME=$(basename "$ISO_FILE") + ISO_PATH="$ISO_FILE" + + export ISO_PATH ISO_FILE ISO_NAME + return 0 +} diff --git a/scripts/vm/uupdump_creator.sh b/scripts/vm/uupdump_creator.sh new file mode 100644 index 0000000..bea7afc --- /dev/null +++ b/scripts/vm/uupdump_creator.sh @@ -0,0 +1,162 @@ +#!/usr/bin/env bash + +# ============================================================== +# ProxMenux - Windows ISO Creator from UUP Dump +# ============================================================== + +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 + +function run_uupdump_creator() { + +clear +show_proxmenux_logo + + + +DEPS=(curl aria2 cabextract wimtools genisoimage gettext) +NEEDED=() + +for pkg in "${DEPS[@]}"; do + if ! command -v "$pkg" &>/dev/null; then + NEEDED+=("$pkg") + fi +done + +if [[ ${#NEEDED[@]} -gt 0 ]]; then + msg_info "Installing dependencies for create image ISO from UUP Dump..." + apt-get update -qq >/dev/null + DEBIAN_FRONTEND=noninteractive apt-get install -y "${NEEDED[@]}" >/dev/null 2>&1 + msg_ok "Dependencies successfully installed." +fi + + + +TMP_DIR="/root/uup-temp" +OUT_DIR="/var/lib/vz/template/iso" +CONVERTER="/root/uup-converter" + +mkdir -p "$TMP_DIR" "$OUT_DIR" +cd "$TMP_DIR" || exit 1 + + +UUP_URL=$(whiptail --inputbox "$(translate "Paste the UUP Dump URL here")" 10 90 3>&1 1>&2 2>&3) +[[ $? -ne 0 ]] && msg_error "$(translate "Cancelled by user.")" && exit 1 + + +if [[ ! "$UUP_URL" =~ id=.+\&pack=.+\&edition=.+ ]]; then + msg_error "$(translate "The URL does not contain the required parameters (id, pack, edition).")" + exit 1 +fi + + +BUILD_ID=$(echo "$UUP_URL" | grep -oP 'id=\K[^&]+') +LANG=$(echo "$UUP_URL" | grep -oP 'pack=\K[^&]+') +EDITION=$(echo "$UUP_URL" | grep -oP 'edition=\K[^&]+') +ARCH="amd64" + +echo -e "\n${BGN}=============== UUP Dump Creator ===============${CL}" +echo -e " ${BGN}🆔 ID:${CL} ${DGN}$BUILD_ID${CL}" +echo -e " ${BGN}🌐 Language:${CL} ${DGN}$LANG${CL}" +echo -e " ${BGN}💿 Edition:${CL} ${DGN}$EDITION${CL}" +echo -e " ${BGN}🖥️ Architecture:${CL} ${DGN}$ARCH${CL}" +echo -e "${BGN}===============================================${CL}\n" + + +if [[ ! -f "$CONVERTER/convert.sh" ]]; then + echo "📦 $(translate "Downloading UUP converter...")" + mkdir -p "$CONVERTER" + cd "$CONVERTER" || exit 1 + wget -q https://git.uupdump.net/uup-dump/converter/archive/refs/heads/master.tar.gz -O converter.tar.gz + tar -xzf converter.tar.gz --strip-components=1 + chmod +x convert.sh + cd "$TMP_DIR" || exit 1 +fi + +# Crear script de descarga uup_download_linux.sh +cat > uup_download_linux.sh < files/converter_multi + +for prog in aria2c cabextract wimlib-imagex chntpw; do + which \$prog &>/dev/null || { echo "\$prog not found."; exit 1; } +done +which genisoimage &>/dev/null || which mkisofs &>/dev/null || { echo "genisoimage/mkisofs not found."; exit 1; } + +destDir="UUPs" +tempScript="aria2_script.\$RANDOM.txt" + +aria2c --no-conf --console-log-level=warn --log-level=info --log="aria2_download.log" \ + -x16 -s16 -j2 --allow-overwrite=true --auto-file-renaming=false -d"files" -i"files/converter_multi" || exit 1 + +aria2c --no-conf --console-log-level=warn --log-level=info --log="aria2_download.log" \ + -o"\$tempScript" --allow-overwrite=true --auto-file-renaming=false \ + "https://uupdump.net/get.php?id=$BUILD_ID&pack=$LANG&edition=$EDITION&aria2=2" || exit 1 + +grep '#UUPDUMP_ERROR:' "\$tempScript" && { echo "❌ Error generating UUP download list."; exit 1; } + +aria2c --no-conf --console-log-level=warn --log-level=info --log="aria2_download.log" \ + -x16 -s16 -j5 -c -R -d"\$destDir" -i"\$tempScript" || exit 1 +EOF + +chmod +x uup_download_linux.sh + + + +# ========================== +./uup_download_linux.sh +# ========================== + + + +UUP_FOLDER=$(find "$TMP_DIR" -type d -name "UUPs" | head -n1) +[[ -z "$UUP_FOLDER" ]] && msg_error "$(translate "No UUP folder found.")" && exit 1 + + +echo -e "\n${GN}=======================================${CL}" +echo -e " 💿 ${GN}Starting ISO conversion...${CL}" +echo -e "${GN}=======================================${CL}\n" + +"$CONVERTER/convert.sh" wim "$UUP_FOLDER" 1 + + +ISO_FILE=$(find "$TMP_DIR" "$CONVERTER" "$UUP_FOLDER" -maxdepth 1 -iname "*.iso" | head -n1) +if [[ -f "$ISO_FILE" ]]; then + mv "$ISO_FILE" "$OUT_DIR/" + msg_ok "$(translate "ISO created successfully:") $OUT_DIR/$(basename "$ISO_FILE")" + + + msg_ok "$(translate "Cleaning temporary files...")" + rm -rf "$TMP_DIR" "$CONVERTER" + + export LANGUAGE=C + export LANG=C + export LC_ALL=C + load_language + initialize_cache + + msg_success "$(translate "Press Enter to return to menu...")" + read -r +else + msg_warn "$(translate "No ISO was generated.")" + + export LANGUAGE=C + export LANG=C + export LC_ALL=C + load_language + initialize_cache + + msg_success "$(translate "Press Enter to return to menu...")" + read -r +fi + +} \ No newline at end of file diff --git a/scripts/vm/vm_configurator.sh b/scripts/vm/vm_configurator.sh new file mode 100644 index 0000000..52a4250 --- /dev/null +++ b/scripts/vm/vm_configurator.sh @@ -0,0 +1,229 @@ +#!/usr/bin/env bash + +# ================================================ +# VM Configuration Module - ProxMenux +# ================================================ + + + +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 + + +function confirm_vm_creation() { + clear + local CONFIRM_TITLE="${HN:-$(translate "New Virtual Machine")}" + local CONFIRM_MSG="$(translate "This will create a new VM") $CONFIRM_TITLE. $(translate "Proceed?")" + + if ! whiptail --backtitle "ProxMenux" --title "$CONFIRM_TITLE" --yesno "$CONFIRM_MSG" 10 60; then + header_info + msg_warn "$(translate "VM creation cancelled by user.")" + sleep 1 + return 1 + fi + return 0 +} + + +function generate_mac() { + local GEN_MAC="02" + for i in {1..5}; do + BYTE=$(printf "%02X" $((RANDOM % 256))) + GEN_MAC="${GEN_MAC}:${BYTE}" + done + echo "$GEN_MAC" +} + +function load_default_vm_config() { + local os_type="$1" + + VMID=$(pvesh get /cluster/nextid 2>/dev/null || echo "100") + MAC=$(generate_mac) + + case "$os_type" in + "1") + + CORE_COUNT="2" + RAM_SIZE="8192" + MACHINE=" -machine q35" + BIOS_TYPE=" -bios ovmf" + START_VM="no" + ;; + "2") + + CORE_COUNT="4" + RAM_SIZE="8192" + MACHINE=" -machine q35" + BIOS_TYPE=" -bios ovmf" + START_VM="no" + ;; + "3") + + CORE_COUNT="2" + RAM_SIZE="4096" + MACHINE=" -machine q35" + BIOS_TYPE=" -bios ovmf" + START_VM="no" + ;; + esac + + + [[ -z "$CORE_COUNT" ]] && CORE_COUNT="2" + [[ -z "$RAM_SIZE" ]] && RAM_SIZE="4096" + + CPU_TYPE=" -cpu host" + BRG="vmbr0" + VLAN="" + MTU="" + SERIAL_PORT="socket" + FORMAT="" + DISK_CACHE="" + +} + +function apply_default_vm_config() { + echo -e "${DEF}$(translate "Applying default VM configuration")${CL}" + echo -e " ${TAB}${DGN}$(translate "Virtual Machine ID")${CL}: ${BGN}$VMID${CL}" + echo -e " ${TAB}${DGN}$(translate "Hostname")${CL}: ${BGN}$HN${CL}" + echo -e " ${TAB}${DGN}$(translate "CPU Cores")${CL}: ${BGN}$CORE_COUNT${CL}" + echo -e " ${TAB}${DGN}$(translate "RAM Size")${CL}: ${BGN}$RAM_SIZE MiB${CL}" + echo -e " ${TAB}${DGN}$(translate "Machine Type")${CL}: ${BGN}${MACHINE/ -machine /}${CL}" + echo -e " ${TAB}${DGN}$(translate "BIOS Type")${CL}: ${BGN}${BIOS_TYPE/ -bios /}${CL}" + echo -e " ${TAB}${DGN}$(translate "CPU Model")${CL}: ${BGN}${CPU_TYPE/ -cpu /}${CL}" + echo -e " ${TAB}${DGN}$(translate "Network Bridge")${CL}: ${BGN}$BRG${CL}" + echo -e " ${TAB}${DGN}$(translate "MAC Address")${CL}: ${BGN}$MAC${CL}" + echo -e " ${TAB}${DGN}$(translate "Start VM after creation")${CL}: ${BGN}$START_VM${CL}" + echo -e + echo -e "${DEF}$(translate "Creating VM with the above configuration")${CL}" +} + + + +function configure_vm_advanced() { + header_info + + # Obtener el siguiente ID libre + NEXTID=$(pvesh get /cluster/nextid 2>/dev/null || echo "100") + [[ -z "$MAC" ]] && MAC=$(generate_mac) + + # VMID + while true; do + VMID=$(whiptail --backtitle "ProxMenux" \ + --inputbox "$(translate "Set Virtual Machine ID")" 8 60 "$NEXTID" \ + --title "VM ID" --cancel-button Exit 3>&1 1>&2 2>&3) || return 1 + + if [[ -z "$VMID" ]]; then + VMID="$NEXTID" + fi + + if qm status "$VMID" &>/dev/null || pct status "$VMID" &>/dev/null; then + msg_error "$(translate "ID already in use. Please choose another.")" + sleep 1 + else + break + fi + done + + # Hostname + HN=$(whiptail --backtitle "ProxMenux" \ + --inputbox "$(translate "Set Hostname")" 8 60 "$HN" \ + --title "Hostname" 3>&1 1>&2 2>&3) || return 1 + [[ -z "$HN" ]] && HN="vm-proxmenux" + + # Machine Type + MACHINE_TYPE=$(whiptail --backtitle "ProxMenux" --title "$(translate "Machine Type")" \ + --radiolist "$(translate "Select machine type")" 10 60 2 \ + "q35" "QEMU q35" ON \ + "i440fx" "Legacy i440fx" OFF 3>&1 1>&2 2>&3) || return 1 + [[ "$MACHINE_TYPE" == "q35" ]] && MACHINE=" -machine q35" && FORMAT="" || MACHINE="" && FORMAT=",efitype=4m" + + # BIOS + BIOS=$(whiptail --backtitle "ProxMenux" --title "$(translate "BIOS Type")" \ + --radiolist "$(translate "Choose BIOS type")" 10 60 2 \ + "ovmf" "UEFI (OVMF)" ON \ + "seabios" "Legacy BIOS (SeaBIOS)" OFF 3>&1 1>&2 2>&3) || return 1 + BIOS_TYPE=" -bios $BIOS" + + # CPU Type + CPU_CHOICE=$(whiptail --backtitle "ProxMenux" --title "$(translate "CPU Model")" \ + --radiolist "$(translate "Select CPU model")" 10 60 2 \ + "host" "Host (recommended)" ON \ + "kvm64" "Generic KVM64" OFF 3>&1 1>&2 2>&3) || return 1 + [[ "$CPU_CHOICE" == "host" ]] && CPU_TYPE=" -cpu host" || CPU_TYPE=" -cpu kvm64" + + # Core Count + CORE_COUNT=$(whiptail --backtitle "ProxMenux" --inputbox "$(translate "Number of CPU cores (default: 2)")" \ + 8 60 "${CORE_COUNT:-2}" --title "CPU Cores" 3>&1 1>&2 2>&3) || return 1 + [[ -z "$CORE_COUNT" ]] && CORE_COUNT="2" + + # RAM + RAM_SIZE=$(whiptail --backtitle "ProxMenux" --inputbox "$(translate "Amount of RAM in MiB (default: 4096)")" \ + 8 60 "${RAM_SIZE:-4096}" --title "RAM" 3>&1 1>&2 2>&3) || return 1 + [[ -z "$RAM_SIZE" ]] && RAM_SIZE="4096" + + # Bridge + BRG=$(whiptail --backtitle "ProxMenux" --inputbox "$(translate "Set network bridge (default: vmbr0)")" \ + 8 60 "${BRG:-vmbr0}" --title "Bridge" 3>&1 1>&2 2>&3) || return 1 + + # MAC + MAC_INPUT=$(whiptail --backtitle "ProxMenux" --inputbox "$(translate "Set MAC Address (leave empty for automatic)")" \ + 8 60 "$MAC" --title "MAC Address" 3>&1 1>&2 2>&3) || return 1 + [[ -z "$MAC_INPUT" ]] && MAC=$(generate_mac) || MAC="$MAC_INPUT" + + # VLAN + VLAN_INPUT=$(whiptail --backtitle "ProxMenux" --inputbox "$(translate "Set VLAN (leave empty for none)")" \ + 8 60 "" --title "VLAN" 3>&1 1>&2 2>&3) || return 1 + VLAN="" + [[ -n "$VLAN_INPUT" ]] && VLAN=",tag=$VLAN_INPUT" + + # MTU + MTU_INPUT=$(whiptail --backtitle "ProxMenux" --inputbox "$(translate "Set MTU (leave empty for default)")" \ + 8 60 "" --title "MTU" 3>&1 1>&2 2>&3) || return 1 + MTU="" + [[ -n "$MTU_INPUT" ]] && MTU=",mtu=$MTU_INPUT" + + # Serial Port + if (whiptail --backtitle "ProxMenux" --title "Serial Port" \ + --yesno "$(translate "Do you want to enable the serial port") (socket)?" 10 60); then + SERIAL_PORT="socket" + else + SERIAL_PORT="" + fi + + # Start VM + if (whiptail --backtitle "ProxMenux" --title "$(translate "Start VM")" \ + --yesno "$(translate "Start VM after creation?")" 10 60); then + START_VM="yes" + else + START_VM="no" + fi + + echo -e "${CUS}$(translate "Using advanced configuration")${CL}" + echo -e " ${TAB}${DGN}$(translate "Virtual Machine ID")${CL}: ${BGN}$VMID${CL}" + echo -e " ${TAB}${DGN}$(translate "Hostname")${CL}: ${BGN}$HN${CL}" + echo -e " ${TAB}${DGN}$(translate "CPU Cores")${CL}: ${BGN}$CORE_COUNT${CL}" + echo -e " ${TAB}${DGN}$(translate "RAM Size")${CL}: ${BGN}$RAM_SIZE MiB${CL}" + echo -e " ${TAB}${DGN}$(translate "Machine Type")${CL}: ${BGN}${MACHINE/ -machine /}${CL}" + echo -e " ${TAB}${DGN}$(translate "BIOS Type")${CL}: ${BGN}${BIOS_TYPE/ -bios /}${CL}" + echo -e " ${TAB}${DGN}$(translate "CPU Model")${CL}: ${BGN}${CPU_TYPE/ -cpu /}${CL}" + echo -e " ${TAB}${DGN}$(translate "Network Bridge")${CL}: ${BGN}$BRG${CL}" + echo -e " ${TAB}${DGN}$(translate "MAC Address")${CL}: ${BGN}$MAC${CL}" + echo -e " ${TAB}${DGN}$(translate "VLAN")${CL}: ${BGN}${VLAN:-None}${CL}" + echo -e " ${TAB}${DGN}$(translate "Interface MTU")${CL}: ${BGN}${MTU:-Default}${CL}" + echo -e " ${TAB}${DGN}$(translate "Start VM")${CL}: ${BGN}$START_VM${CL}" + echo -e + echo -e "${CUS}$(translate "Creating VM with the above configuration")${CL}" + sleep 1 + + + + return 0 +} diff --git a/scripts/vm/vm_creator.sh b/scripts/vm/vm_creator.sh new file mode 100644 index 0000000..bcc283e --- /dev/null +++ b/scripts/vm/vm_creator.sh @@ -0,0 +1,327 @@ +#!/usr/bin/env bash + +# ========================================================== +# VM Creator Module - ProxMenux +# ========================================================== +# Este módulo recibe las variables globales y crea la VM +# con su configuración, discos y descripción. +# ========================================================== + +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 + +# ========================================================== +# Función para montar ISOs +# ========================================================== +function mount_iso_to_vm() { + local vmid="$1" + local iso_path="$2" + local device="$3" + + if [[ -f "$iso_path" ]]; then + local iso_basename + iso_basename=$(basename "$iso_path") + qm set "$vmid" -$device "local:iso/$iso_basename,media=cdrom" >/dev/null 2>&1 + msg_ok "$(translate "Mounted ISO on device") $device → $iso_basename" + else + msg_warn "$(translate "ISO not found to mount on device") $device" + fi +} + + + + +# ========================================================== +# Select Interface Type +# ========================================================== +function select_interface_type() { + INTERFACE_TYPE=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Disk Interface")" --radiolist \ + "$(translate "Select the bus type for the disks:")" 15 70 4 \ + "scsi" "$(translate "SCSI (recommended for Linux and Windows)")" ON \ + "sata" "$(translate "SATA (standard - high compatibility)")" OFF \ + "virtio" "$(translate "VirtIO (advanced - high performance)")" OFF \ + "ide" "IDE (legacy)" OFF \ + 3>&1 1>&2 2>&3) || exit 1 + + case "$INTERFACE_TYPE" in + "scsi"|"sata") + DISCARD_OPTS=",discard=on,ssd=on" + ;; + "virtio") + DISCARD_OPTS=",discard=on" + ;; + "ide") + DISCARD_OPTS="" + ;; + esac + + msg_ok "$(translate "Disk interface selected:") $INTERFACE_TYPE" +} + + +# ========================================================== +# Función select_efi_storage (no cambia) +# ========================================================== +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)}') + STORAGE_MENU+=("$TAG" "Type: $TYPE Free: $FREE" "OFF") + done < <(pvesm status -content images | awk 'NR>1') + + if [ ${#STORAGE_MENU[@]} -eq 0 ]; then + msg_error "$(translate "Unable to detect a valid storage location for EFI disk.")" + exit 1 + elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then + STORAGE=${STORAGE_MENU[0]} + else + STORAGE=$(whiptail --backtitle "ProxMenux" --title "$(translate "EFI Disk Storage")" --radiolist \ + "$(translate "Choose the storage volume for the EFI disk (4MB):")" 16 70 6 \ + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit 1 + fi + + echo "$STORAGE" +} + + + + +# ========================================================== +# Función principal para crear la VM +# ========================================================== +function create_vm() { + local BOOT_ORDER="" + local DISK_INFO="" + local DISK_INDEX=0 + local ISO_DIR="/var/lib/vz/template/iso" + + + # Descargar ISO si es necesario + if [[ -n "$ISO_PATH" && -n "$ISO_URL" && ! -f "$ISO_PATH" ]]; then + # Detectar si es una URL de SourceForge + if [[ "$ISO_URL" == *"sourceforge.net"* ]]; then + # SourceForge necesita --content-disposition para descargar correctamente + wget --content-disposition --show-progress -O "$ISO_PATH" "$ISO_URL" + else + # Normal para el resto + wget --no-verbose --show-progress -O "$ISO_PATH" "$ISO_URL" + fi + + # Verificar si se descargó bien + if [[ -f "$ISO_PATH" ]]; then + msg_ok "$(translate "ISO image downloaded")" + else + msg_error "$(translate "Failed to download ISO image")" + return + fi + fi + + + + # Crear la VM base primero (mínima) + 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" -ostype l26 \ + -scsihw virtio-scsi-pci \ + $( [[ -n "$SERIAL_PORT" ]] && echo "-serial0 $SERIAL_PORT" ) + + msg_ok "$(translate "Base VM created with ID") $VMID" + + + # Crear disco EFI si corresponde + if [[ "$BIOS_TYPE" == *"ovmf"* ]]; then + msg_info "$(translate "Configuring EFI disk")" + EFI_STORAGE=$(select_efi_storage "$VMID") + EFI_DISK_NAME="vm-${VMID}-disk-efivars" + + 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 "$(translate "EFI disk created and configured on") $EFI_STORAGE" + else + msg_error "$(translate "Failed to configure EFI disk")" + fi + else + msg_error "$(translate "Failed to create EFI disk")" + fi + fi + + + + +# ========================================================== +# Crear discos virtuales o físicos con interfaz seleccionada +# ========================================================== + +# Primero seleccionar la interfaz +select_interface_type + + if [[ "$DISK_TYPE" == "virtual" && ${#VIRTUAL_DISKS[@]} -gt 0 ]]; then + for i in "${!VIRTUAL_DISKS[@]}"; do + DISK_INDEX=$((i+1)) + IFS=':' read -r STORAGE SIZE <<< "${VIRTUAL_DISKS[$i]}" + DISK_NAME="vm-${VMID}-disk-${DISK_INDEX}" + SLOT_NAME="${INTERFACE_TYPE}${i}" + + STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') + case "$STORAGE_TYPE" in + dir|nfs) + DISK_EXT=".raw" + DISK_REF="$VMID/" + ;; + *) + DISK_EXT="" + DISK_REF="" + ;; + esac + + if pvesm alloc "$STORAGE" "$VMID" "$DISK_NAME$DISK_EXT" "$SIZE"G >/dev/null 2>&1; then + qm set "$VMID" -$SLOT_NAME "$STORAGE:${DISK_REF}${DISK_NAME}${DISK_EXT}${DISCARD_OPTS}" >/dev/null + msg_ok "$(translate "Virtual disk") $DISK_INDEX ${SIZE}GB - $STORAGE ($SLOT_NAME)" + DISK_INFO+="

Virtual Disk $DISK_INDEX: ${SIZE}GB ($STORAGE / $SLOT_NAME)

" + [[ -z "$BOOT_ORDER" ]] && BOOT_ORDER="$SLOT_NAME" + else + msg_error "$(translate "Failed to create disk") $DISK_INDEX" + fi + done + fi + + if [[ "$DISK_TYPE" == "passthrough" && ${#PASSTHROUGH_DISKS[@]} -gt 0 ]]; then + for i in "${!PASSTHROUGH_DISKS[@]}"; do + SLOT_NAME="${INTERFACE_TYPE}${i}" + DISK="${PASSTHROUGH_DISKS[$i]}" + MODEL=$(lsblk -ndo MODEL "$DISK") + SIZE=$(lsblk -ndo SIZE "$DISK") + qm set "$VMID" -$SLOT_NAME "$DISK${DISCARD_OPTS}" >/dev/null 2>&1 + msg_ok "$(translate "Passthrough disk assigned") ($DISK → $SLOT_NAME)" + DISK_INFO+="

Passthrough Disk $((i+1)): $DISK ($MODEL $SIZE)

" + [[ -z "$BOOT_ORDER" ]] && BOOT_ORDER="$SLOT_NAME" + done + fi + + + + + # Ahora montamos las ISOs + if [[ -f "$ISO_PATH" ]]; then + mount_iso_to_vm "$VMID" "$ISO_PATH" "ide2" + fi + + # Para Windows, preguntar y montar ISO VirtIO + if [[ "$OS_TYPE" == "windows" ]]; then + local VIRTIO_DIR="/var/lib/vz/template/iso" + local VIRTIO_SELECTED="" + local VIRTIO_DOWNLOAD_URL="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" + + while true; do + VIRTIO_OPTION=$(whiptail --title "ProxMenux - VirtIO Drivers" --menu "$(translate "Select how to provide VirtIO drivers")" 15 70 2 \ + "1" "$(translate "Download latest VirtIO ISO automatically")" \ + "2" "$(translate "Use existing VirtIO ISO from storage")" 3>&1 1>&2 2>&3) + + [[ $? -ne 0 ]] && msg_warn "$(translate "VirtIO ISO selection cancelled.")" && break + + case "$VIRTIO_OPTION" in + 1) + + if [[ -f "$VIRTIO_DIR/virtio-win.iso" ]]; then + if whiptail --title "ProxMenux" --yesno "$(translate "A VirtIO ISO already exists. Do you want to overwrite it?")" 10 60; then + wget -q --show-progress -O "$VIRTIO_DIR/virtio-win.iso" "$VIRTIO_DOWNLOAD_URL" + if [[ -f "$VIRTIO_DIR/virtio-win.iso" ]]; then + msg_ok "$(translate "VirtIO driver ISO downloaded successfully.")" + else + msg_error "$(translate "Failed to download VirtIO driver ISO.")" + fi + fi + else + wget -q --show-progress -O "$VIRTIO_DIR/virtio-win.iso" "$VIRTIO_DOWNLOAD_URL" + if [[ -f "$VIRTIO_DIR/virtio-win.iso" ]]; then + msg_ok "$(translate "VirtIO driver ISO downloaded successfully.")" + else + msg_error "$(translate "Failed to download VirtIO driver ISO.")" + fi + fi + + VIRTIO_SELECTED="$VIRTIO_DIR/virtio-win.iso" + ;; + 2) + + VIRTIO_LIST=() + while read -r line; do + FILENAME=$(basename "$line") + SIZE=$(du -h "$line" | cut -f1) + VIRTIO_LIST+=("$FILENAME" "$SIZE") + done < <(find "$VIRTIO_DIR" -type f -iname "virtio*.iso" | sort) + + if [[ ${#VIRTIO_LIST[@]} -eq 0 ]]; then + msg_warn "$(translate "No VirtIO ISO found. Please download one.")" + continue # Volver a preguntar + fi + + VIRTIO_FILE=$(whiptail --title "ProxMenux - VirtIO ISOs" --menu "$(translate "Select a VirtIO ISO to use:")" 20 70 10 "${VIRTIO_LIST[@]}" 3>&1 1>&2 2>&3) + + if [[ -n "$VIRTIO_FILE" ]]; then + VIRTIO_SELECTED="$VIRTIO_DIR/$VIRTIO_FILE" + else + msg_warn "$(translate "No VirtIO ISO selected. Please choose again.")" + continue + fi + ;; + esac + + if [[ -n "$VIRTIO_SELECTED" && -f "$VIRTIO_SELECTED" ]]; then + mount_iso_to_vm "$VMID" "$VIRTIO_SELECTED" "ide3" + else + msg_warn "$(translate "VirtIO ISO not found after selection.")" + fi + + break + done + fi + + + # Configurar el orden de arranque (primer disco, luego CD) + local BOOT_FINAL="$BOOT_ORDER" + [[ -f "$ISO_PATH" ]] && BOOT_FINAL="$BOOT_ORDER;ide2" + qm set "$VMID" -boot order="$BOOT_FINAL" >/dev/null + msg_ok "$(translate "Boot order set to") $BOOT_FINAL" + + # Crear descripción + local DESC="

$HN

Created with ProxMenux

$DISK_INFO
" + qm set "$VMID" -description "$DESC" >/dev/null + msg_ok "$(translate "VM description configured")" + + # Arrancar la VM si corresponde + if [[ "$START_VM" == "yes" ]]; then + qm start "$VMID" + msg_ok "$(translate "VM started")" + fi + configure_guest_agent + msg_success "$(translate "VM creation completed")" +} + + +