diff --git a/ct/authentik.sh b/ct/authentik.sh index 0d049c47..2879eed6 100644 --- a/ct/authentik.sh +++ b/ct/authentik.sh @@ -9,7 +9,7 @@ APP="authentik" var_tags="${var_tags:-auth}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" -var_disk="${var_disk:-10}" +var_disk="${var_disk:-16}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" diff --git a/ct/clickhouse.sh b/ct/clickhouse.sh index 13dbc264..4c6ede16 100644 --- a/ct/clickhouse.sh +++ b/ct/clickhouse.sh @@ -51,7 +51,14 @@ function update_script() { msg_info "Building HyperDX" $STD yarn install $STD yarn workspace @hyperdx/common-utils run build - $STD yarn workspace @hyperdx/api run build + rm -rf /opt/clickstack/packages/api/build + yarn workspace @hyperdx/api exec tsc >>"$(get_active_logfile)" 2>&1 || true + $STD yarn workspace @hyperdx/api exec tsc-alias + cp -r /opt/clickstack/packages/api/src/opamp/proto /opt/clickstack/packages/api/build/opamp/ 2>/dev/null || true + [[ -f /opt/clickstack/packages/api/build/index.js ]] || { + msg_error "HyperDX API build failed: build/index.js not found" + exit 1 + } $STD yarn workspace @hyperdx/app run build msg_ok "Built HyperDX" @@ -83,6 +90,7 @@ function update_script() { exit } +export CLICKSTACK="no" if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLICKSTACK" --yesno "Install ClickStack observability stack?\n\n(HyperDX UI + OTel Collector + MongoDB)\nRequires: 4 CPU, 8GB RAM, 30GB Disk" 12 58); then export CLICKSTACK="yes" var_cpu="4" diff --git a/ct/headers/stoatchat b/ct/headers/stoatchat new file mode 100644 index 00000000..870f0c19 --- /dev/null +++ b/ct/headers/stoatchat @@ -0,0 +1,6 @@ + _____ __ __ __ __ + / ___// /_____ ____ _/ /______/ /_ ____ _/ /_ + \__ \/ __/ __ \/ __ `/ __/ ___/ __ \/ __ `/ __/ + ___/ / /_/ /_/ / /_/ / /_/ /__/ / / / /_/ / /_ +/____/\__/\____/\__,_/\__/\___/_/ /_/\__,_/\__/ + diff --git a/ct/headers/xyops b/ct/headers/xyops new file mode 100644 index 00000000..7a8be0a2 --- /dev/null +++ b/ct/headers/xyops @@ -0,0 +1,6 @@ + ____ + _ ____ __/ __ \____ _____ + | |/_/ / / / / / / __ \/ ___/ + _> >"$(get_active_logfile)" 2>&1 || true + $STD yarn workspace @hyperdx/api exec tsc-alias + cp -r /opt/clickstack/packages/api/src/opamp/proto /opt/clickstack/packages/api/build/opamp/ 2>/dev/null || true + [[ -f /opt/clickstack/packages/api/build/index.js ]] || { + msg_error "HyperDX API build failed: build/index.js not found" + exit 1 + } $STD yarn workspace @hyperdx/app run build msg_ok "Built HyperDX" diff --git a/install/hoodik-install.sh b/install/hoodik-install.sh index 8edc7e63..1138e4c4 100644 --- a/install/hoodik-install.sh +++ b/install/hoodik-install.sh @@ -23,8 +23,10 @@ DATA_DIR=/opt/hoodik_data HTTP_PORT=5443 HTTP_ADDRESS=0.0.0.0 JWT_SECRET=${JWT_SECRET} -APP_URL=http://127.0.0.1:5443 +APP_URL=http://${LOCAL_IP}:5443 SSL_DISABLED=true +COOKIE_SECURE=false +COOKIE_HTTP_ONLY=false MAILER_TYPE=none RUST_LOG=hoodik=info,error=info EOF @@ -41,14 +43,14 @@ Type=simple User=root WorkingDirectory=/opt/hoodik_data EnvironmentFile=/opt/hoodik/.env -ExecStart=/opt/hoodik +ExecStart=/opt/hoodik/hoodik Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now hoodik.service +systemctl enable -q --now hoodik msg_ok "Created Service" motd_ssh diff --git a/install/lobehub-install.sh b/install/lobehub-install.sh index 3ec0b1bc..ad85b04b 100644 --- a/install/lobehub-install.sh +++ b/install/lobehub-install.sh @@ -14,36 +14,28 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt install -y \ - build-essential +$STD apt install -y build-essential msg_ok "Installed Dependencies" PG_VERSION="17" PG_MODULES="pgvector" setup_postgresql -msg_info "Installing pg_search from ParadeDB" -ARCH=$(dpkg --print-architecture) CODENAME=$(. /etc/os-release && echo "${VERSION_CODENAME:-bookworm}") -PDB_VERSION=$(curl -fsSL "https://api.github.com/repos/paradedb/paradedb/releases/latest" | grep -oP '"tag_name":\s*"\K[^"]+') -PDB_VERSION_NUM="${PDB_VERSION#v}" -DEB_NAME="postgresql-17-pg-search_${PDB_VERSION_NUM}-1PARADEDB-${CODENAME}_${ARCH}.deb" -DEB_URL="https://github.com/paradedb/paradedb/releases/download/${PDB_VERSION}/${DEB_NAME}" -curl -fsSL -o "/tmp/${DEB_NAME}" "$DEB_URL" -dpkg -i "/tmp/${DEB_NAME}" >/dev/null 2>&1 || $STD apt install -f -y -rm -f "/tmp/${DEB_NAME}" -msg_ok "Installed pg_search from ParadeDB" +fetch_and_deploy_gh_release "paradedb" "paradedb/paradedb" "binary" "latest" "" "postgresql-17-pg-search_*-1PARADEDB-${CODENAME}_$(dpkg --print-architecture).deb" + +msg_info "Configuring pg_search preload library" +if ! grep -q "shared_preload_libraries.*pg_search" /etc/postgresql/17/main/postgresql.conf; then + echo "shared_preload_libraries = 'pg_search'" >>/etc/postgresql/17/main/postgresql.conf +fi +systemctl restart postgresql +msg_ok "Configured pg_search preload library" PG_DB_NAME="lobehub" PG_DB_USER="lobehub" PG_DB_EXTENSIONS="vector,pg_search" setup_postgresql_db -NODE_VERSION="24" setup_nodejs - -msg_info "Installing pnpm" -$STD npm install -g pnpm -msg_ok "Installed pnpm" +NODE_VERSION="24" NODE_MODULE="pnpm" setup_nodejs fetch_and_deploy_gh_release "lobehub" "lobehub/lobehub" "tarball" msg_info "Building Application" cd /opt/lobehub -export NODE_OPTIONS="--max-old-space-size=8192" export DATABASE_URL="postgres://${PG_DB_USER}:${PG_DB_PASS}@localhost:5432/${PG_DB_NAME}" export DATABASE_DRIVER="node" export KEY_VAULTS_SECRET="$(openssl rand -base64 32)" @@ -51,7 +43,6 @@ export AUTH_SECRET="$(openssl rand -base64 32)" export APP_URL="http://localhost:3210" $STD pnpm install $STD pnpm run build:docker -unset NODE_OPTIONS msg_ok "Built Application" msg_info "Configuring Application" diff --git a/install/puter-install.sh b/install/puter-install.sh index 6c6d2af5..685b4dba 100644 --- a/install/puter-install.sh +++ b/install/puter-install.sh @@ -26,13 +26,8 @@ fetch_and_deploy_gh_release "puter" "HeyPuter/puter" "tarball" msg_info "Building Application" cd /opt/puter -node -e "const f=require('fs'),p=JSON.parse(f.readFileSync('package.json'));p.overrides={'better-sqlite3':'>=12.0.0'};f.writeFileSync('package.json',JSON.stringify(p,null,2))" -rm -f package-lock.json -$STD npm install -cd /opt/puter/src/gui +$STD npm ci $STD npm run build -cd /opt/puter -cp -r src/gui/dist dist msg_ok "Built Application" msg_info "Creating Directories" @@ -43,10 +38,10 @@ msg_info "Configuring Application" cat </etc/puter/config.json { "config_name": "proxmox", - "domain": "${LOCAL_IP}", + "domain": "${LOCAL_IP}.nip.io", "protocol": "http", "http_port": 4100, - "experimental_no_subdomain": true, + "allow_nipio_domains": true, "services": { "database": { "engine": "sqlite", @@ -67,8 +62,8 @@ After=network.target Type=simple User=root WorkingDirectory=/opt/puter -Environment=CONFIG_PATH=/etc/puter -ExecStart=/usr/bin/npm start +Environment=PUTER_CONFIG_PATH=/etc/puter/config.json +ExecStart=/usr/bin/node --enable-source-maps -r /opt/puter/dist/src/backend/telemetry.js /opt/puter/dist/src/backend/index.js Restart=on-failure RestartSec=5 diff --git a/install/stoatchat-install.sh b/install/stoatchat-install.sh new file mode 100644 index 00000000..ea2652a3 --- /dev/null +++ b/install/stoatchat-install.sh @@ -0,0 +1,242 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/stoatchat/stoatchat + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + pkg-config \ + libssl-dev \ + build-essential \ + git \ + redis-server \ + rabbitmq-server \ + nginx +msg_ok "Installed Dependencies" + +setup_mongodb + +msg_info "Configuring RabbitMQ" +systemctl enable -q --now rabbitmq-server +until rabbitmqctl status &>/dev/null; do sleep 1; done +$STD rabbitmqctl add_user rabbituser rabbitpass +$STD rabbitmqctl set_permissions -p / rabbituser ".*" ".*" ".*" +msg_ok "Configured RabbitMQ" + +setup_rust + +fetch_and_deploy_gh_release "stoatchat" "stoatchat/stoatchat" "tarball" + +msg_info "Building Backend (Patience)" +cd /opt/stoatchat +$STD cargo build --release --bins -j 2 +msg_ok "Built Backend" + +NODE_VERSION="22" setup_nodejs + +msg_info "Installing pnpm" +$STD npm install -g pnpm@10.28.1 +msg_ok "Installed pnpm" + +msg_info "Cloning Web Frontend" +FORWEB_VERSION=$(get_latest_github_release "stoatchat/for-web") +$STD git clone --recursive "https://github.com/stoatchat/for-web" /opt/stoatchat-web +$STD git -C /opt/stoatchat-web checkout "$FORWEB_VERSION" +$STD git -C /opt/stoatchat-web submodule update --init --recursive +msg_ok "Cloned Web Frontend" + +msg_info "Building Web Frontend" +cd /opt/stoatchat-web +$STD pnpm install --frozen-lockfile +$STD pnpm --filter stoat.js build +$STD pnpm --filter solid-livekit-components build +$STD pnpm --filter "@lingui-solid/babel-plugin-lingui-macro" build +$STD pnpm --filter "@lingui-solid/babel-plugin-extract-messages" build +$STD pnpm --filter client exec lingui compile --typescript +$STD pnpm --filter client exec node scripts/copyAssets.mjs +$STD pnpm --filter client exec panda codegen +VITE_API_URL="http://${LOCAL_IP}/api" \ + VITE_WS_URL="ws://${LOCAL_IP}/ws" \ + VITE_MEDIA_URL="http://${LOCAL_IP}/autumn" \ + VITE_PROXY_URL="http://${LOCAL_IP}/january" \ + $STD pnpm --filter client exec vite build +msg_ok "Built Web Frontend" + +fetch_and_deploy_gh_release "minio" "minio/minio" "singlefile" "latest" "/opt/stoatchat" "minio_linux_amd64" +mv /opt/stoatchat/minio_linux_amd64 /usr/local/bin/minio +chmod +x /usr/local/bin/minio + +fetch_and_deploy_gh_release "mc" "minio/mc" "singlefile" "latest" "/opt/stoatchat" "mc_linux_amd64" +mv /opt/stoatchat/mc_linux_amd64 /usr/local/bin/mc +chmod +x /usr/local/bin/mc + +msg_info "Configuring MinIO" +mkdir -p /opt/stoatchat/data/minio +cat </etc/systemd/system/stoatchat-minio.service +[Unit] +Description=Stoatchat MinIO Object Storage +After=network.target + +[Service] +Type=simple +User=root +Environment=MINIO_ROOT_USER=minioautumn +Environment=MINIO_ROOT_PASSWORD=minioautumn +ExecStart=/usr/local/bin/minio server /opt/stoatchat/data/minio --console-address :9001 +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now stoatchat-minio +msg_ok "Configured MinIO" + +msg_info "Creating MinIO Bucket" +until mc alias set local http://127.0.0.1:9000 minioautumn minioautumn &>/dev/null; do sleep 1; done +$STD mc mb local/revolt-uploads +msg_ok "Created MinIO Bucket" + +FILES_ENCRYPTION_KEY=$(openssl rand -base64 32) + +msg_info "Creating Configuration" +cat </Revolt.toml +[database] +mongodb = "mongodb://127.0.0.1:27017" +redis = "redis://127.0.0.1:6379/" + +[hosts] +app = "http://${LOCAL_IP}" +api = "http://${LOCAL_IP}/api" +events = "ws://${LOCAL_IP}/ws" +autumn = "http://${LOCAL_IP}/autumn" +january = "http://${LOCAL_IP}/january" + +[rabbit] +host = "127.0.0.1" +port = 5672 +username = "rabbituser" +password = "rabbitpass" + +[files] +encryption_key = "${FILES_ENCRYPTION_KEY}" + +[files.s3] +endpoint = "http://127.0.0.1:9000" +path_style_buckets = true +region = "minio" +access_key_id = "minioautumn" +secret_access_key = "minioautumn" +default_bucket = "revolt-uploads" + +[api.registration] +invite_only = false +EOF +ln -sf /Revolt.toml /opt/stoatchat/Revolt.toml +msg_ok "Created Configuration" + +msg_info "Configuring Nginx" +cat </etc/nginx/sites-available/stoatchat +server { + listen 80; + + client_max_body_size 20M; + + location /api { + proxy_pass http://127.0.0.1:14702; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + } + + location /ws { + proxy_pass http://127.0.0.1:14703; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + } + + location /autumn { + proxy_pass http://127.0.0.1:14704; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + } + + location /january { + proxy_pass http://127.0.0.1:14705; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + } + + location / { + root /opt/stoatchat-web/packages/client/dist; + try_files \$uri \$uri/ /index.html; + } +} +EOF +ln -sf /etc/nginx/sites-available/stoatchat /etc/nginx/sites-enabled/stoatchat +rm -f /etc/nginx/sites-enabled/default +systemctl enable -q --now nginx +msg_ok "Configured Nginx" + +msg_info "Creating Backend Services" +for SVC in api events autumn january crond; do + case $SVC in + api) + PORT=14702 + BIN=delta + ;; + events) + PORT=14703 + BIN=bonfire + ;; + autumn) + PORT=14704 + BIN=autumn + ;; + january) + PORT=14705 + BIN=january + ;; + crond) + PORT=0 + BIN=crond + ;; + esac + cat </etc/systemd/system/stoatchat-${SVC}.service +[Unit] +Description=Stoatchat ${SVC} service +After=network.target stoatchat-minio.service + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/stoatchat +ExecStart=/opt/stoatchat/target/release/${BIN} +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF + systemctl enable -q --now "stoatchat-${SVC}" +done +msg_ok "Created Backend Services" + +motd_ssh +customize +cleanup_lxc diff --git a/install/xyops-install.sh b/install/xyops-install.sh new file mode 100644 index 00000000..67c2627a --- /dev/null +++ b/install/xyops-install.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/pixlcore/xyops + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + build-essential \ + python3 \ + python3-setuptools \ + pkg-config \ + libssl-dev \ + zlib1g-dev +msg_ok "Installed Dependencies" + +NODE_VERSION="22" setup_nodejs + +fetch_and_deploy_gh_release "xyops" "pixlcore/xyops" "tarball" + +msg_info "Building Application" +cd /opt/xyops +$STD npm install +$STD node bin/build.js dist +chmod 644 /opt/xyops/node_modules/useragent-ng/lib/regexps.js +msg_ok "Built Application" + +fetch_and_deploy_gh_release "xysat" "pixlcore/xysat" "tarball" "latest" "/opt/xyops/satellite" + +msg_info "Building xySat Satellite" +cd /opt/xyops/satellite +$STD npm install +msg_ok "Built xySat Satellite" + +msg_info "Setting up Directories" +mkdir -p /opt/xyops/data /opt/xyops/logs /opt/xyops/temp /opt/xyops/conf +msg_ok "Set up Directories" + +msg_info "Creating Service" +cat </etc/systemd/system/xyops.service +[Unit] +Description=xyOps Task Scheduler and Server Monitor +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/xyops +Environment=XYOPS_foreground=1 +ExecStart=/usr/bin/node /opt/xyops/lib/main.js +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now xyops +msg_ok "Created Service" + +motd_ssh +customize +cleanup_lxc diff --git a/json/blinko.json b/json/blinko.json index 70f3508f..b7b3a570 100644 --- a/json/blinko.json +++ b/json/blinko.json @@ -41,4 +41,4 @@ "type": "warning" } ] -} +} \ No newline at end of file diff --git a/json/hoodik.json b/json/hoodik.json index 20fc904b..f51b08b3 100644 --- a/json/hoodik.json +++ b/json/hoodik.json @@ -19,9 +19,9 @@ "script": "ct/hoodik.sh", "config_path": "/opt/hoodik/.env", "resources": { - "cpu": 4, - "ram": 4096, - "hdd": 20, + "cpu": 1, + "ram": 1024, + "hdd": 5, "os": "Debian", "version": "13" } @@ -36,21 +36,9 @@ "text": "First visit will prompt you to create an admin account", "type": "info" }, - { - "text": "Installation builds Rust backend and Vue frontend from source - takes 15-20 minutes", - "type": "warning" - }, - { - "text": "Requires 4GB RAM and 20GB disk for build process", - "type": "warning" - }, { "text": "Data is stored in /opt/hoodik_data", "type": "info" - }, - { - "text": "SSL is disabled by default - use a reverse proxy for HTTPS", - "type": "warning" } ] -} +} \ No newline at end of file diff --git a/json/invidious.json b/json/invidious.json index 5a8837e1..17c914a3 100644 --- a/json/invidious.json +++ b/json/invidious.json @@ -37,4 +37,4 @@ "type": "info" } ] -} +} \ No newline at end of file diff --git a/json/paperclip.json b/json/paperclip.json index 187f8a67..b60c621d 100644 --- a/json/paperclip.json +++ b/json/paperclip.json @@ -45,4 +45,4 @@ "type": "info" } ] -} +} \ No newline at end of file diff --git a/json/puter.json b/json/puter.json index 5c3adee5..9028c933 100644 --- a/json/puter.json +++ b/json/puter.json @@ -33,8 +33,16 @@ }, "notes": [ { - "text": "Configuration is stored in /etc/puter and data in /var/puter.", + "text": "Access via http://<IP>.nip.io:4100 (not the raw IP). nip.io is required for Puter's subdomain routing.", + "type": "info" + }, + { + "text": "Camera, microphone, and other media capture features require HTTPS (a secure context). They will not work on the default HTTP setup.", + "type": "warning" + }, + { + "text": "Configuration is stored in /etc/puter/config.json and data in /var/puter.", "type": "info" } ] -} +} \ No newline at end of file diff --git a/json/stoatchat.json b/json/stoatchat.json new file mode 100644 index 00000000..412117e8 --- /dev/null +++ b/json/stoatchat.json @@ -0,0 +1,52 @@ +{ + "name": "Stoatchat", + "slug": "stoatchat", + "categories": [ + 22 + ], + "date_created": "2026-05-08", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://github.com/stoatchat/self-hosted", + "website": "https://stoat.chat", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/stoatchat.webp", + "description": "A self-hostable open-source chat platform and community server. Stoatchat is a fork of Revolt, featuring real-time messaging, voice channels, file sharing, and a full-featured web client. Built with Rust (backend) and SolidJS (frontend).", + "install_methods": [ + { + "type": "default", + "script": "ct/stoatchat.sh", + "config_path": "/Revolt.toml", + "resources": { + "cpu": 4, + "ram": 10240, + "hdd": 30, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Initial setup takes 30-60 minutes due to Rust compilation and frontend build. Do not interrupt the process.", + "type": "warning" + }, + { + "text": "The first account registered becomes the instance administrator. Registration is open by default; set invite_only = true in /Revolt.toml to restrict it.", + "type": "info" + }, + { + "text": "Voice and video calls require additional LiveKit setup. See https://github.com/stoatchat/self-hosted for details.", + "type": "info" + }, + { + "text": "The files encryption key in /Revolt.toml is generated during installation. Back it up — losing it will make all uploaded files unreadable.", + "type": "warning" + } + ] +} \ No newline at end of file diff --git a/json/xyops.json b/json/xyops.json new file mode 100644 index 00000000..2f8c31b0 --- /dev/null +++ b/json/xyops.json @@ -0,0 +1,48 @@ +{ + "name": "xyOps", + "slug": "xyops", + "categories": [ + 19 + ], + "date_created": "2026-05-08", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 5522, + "documentation": "https://github.com/pixlcore/xyops/tree/main/docs", + "website": "https://github.com/pixlcore/xyops", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/xyops.webp", + "description": "A complete task scheduler and server monitoring system with workflow automation, multi-server management, real-time monitoring, and a built-in satellite agent (xySat) for running jobs on remote servers.", + "install_methods": [ + { + "type": "default", + "script": "ct/xyops.sh", + "config_path": "/opt/xyops/conf/config.json", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": "admin", + "password": "admin" + }, + "notes": [ + { + "text": "Change the default admin password immediately after first login.", + "type": "warning" + }, + { + "text": "A local xySat satellite is started automatically alongside the conductor, allowing jobs to run on this server right away.", + "type": "info" + }, + { + "text": "The WebSocket port 5523 is used for secure wss:// connections. Port 5522 serves the web interface and ws:// connections.", + "type": "info" + } + ] +} \ No newline at end of file diff --git a/misc/vm-core.func b/misc/vm-core.func index c57a1197..1383c67e 100644 --- a/misc/vm-core.func +++ b/misc/vm-core.func @@ -14,9 +14,18 @@ declare -A MSG_INFO_SHOWN [[ -n "${_CORE_FUNC_LOADED:-}" ]] && return _CORE_FUNC_LOADED=1 +COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main}" + +load_api_functions() { + if ! declare -f post_to_api_vm >/dev/null 2>&1; then + source /dev/stdin <<<$(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/api.func") + fi +} + load_functions() { [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return __FUNCTIONS_LOADED=1 + load_api_functions color formatting icons @@ -31,6 +40,12 @@ load_functions() { arch_check } +load_cloud_init_functions() { + if ! declare -f setup_cloud_init >/dev/null 2>&1; then + source <(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/cloud-init.func") 2>/dev/null || true + fi +} + # Function to download & save header files get_header() { local app_name=$(echo "${APP,,}" | tr ' ' '-') @@ -98,6 +113,7 @@ icons() { DNSOK="✔️ " DNSFAIL="${TAB}✖️${TAB}" INFO="${TAB}💡${TAB}${CL}" + CLOUD="${TAB}☁️${TAB}${CL}" OS="${TAB}🖥️${TAB}${CL}" OSVERSION="${TAB}🌟${TAB}${CL}" CONTAINERTYPE="${TAB}📦${TAB}${CL}" @@ -495,6 +511,20 @@ msg_debug() { fi } +error_handler() { + local exit_code="$?" + local line_number="${1:-unknown}" + local command="${2:-unknown}" + + if declare -f post_update_to_api >/dev/null 2>&1; then + post_update_to_api "failed" "$exit_code" + fi + + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + echo -e "\n$error_message\n" + cleanup_vmid +} + # Displays error message and immediately terminates script fatal() { msg_error "$1" @@ -530,9 +560,13 @@ cleanup_vmid() { cleanup() { local exit_code=$? + stop_spinner if [[ "$(dirs -p | wc -l)" -gt 1 ]]; then popd >/dev/null || true fi + if [[ -n "${TEMP_DIR:-}" && -d "$TEMP_DIR" ]]; then + rm -rf "$TEMP_DIR" + fi # Report final telemetry status if post_to_api_vm was called but no update was sent if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then if declare -f post_update_to_api >/dev/null 2>&1; then @@ -557,13 +591,32 @@ check_root() { } pve_check() { - if ! pveversion | grep -Eq "pve-manager/(8\.[1-4]|9\.[0-1])(\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 - 8.4 or 9.0 - 9.1." - echo -e "Exiting..." - sleep 2 - exit + local pve_ver + pve_ver="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + + if [[ "$pve_ver" =~ ^8\.([0-9]+) ]]; then + local minor="${BASH_REMATCH[1]}" + if ((minor < 0 || minor > 9)); then + msg_error "This version of Proxmox VE is not supported." + msg_error "Supported: Proxmox VE version 8.0 – 8.9" + exit 105 + fi + return 0 fi + + if [[ "$pve_ver" =~ ^9\.([0-9]+) ]]; then + local minor="${BASH_REMATCH[1]}" + if ((minor < 0 || minor > 1)); then + msg_error "This version of Proxmox VE is not supported." + msg_error "Supported: Proxmox VE version 9.0 – 9.1" + exit 105 + fi + return 0 + fi + + msg_error "This version of Proxmox VE is not supported." + msg_error "Supported versions: Proxmox VE 8.0 – 8.9 or 9.0 – 9.1" + exit 105 } arch_check() { @@ -576,12 +629,460 @@ arch_check() { fi } +ssh_check() { + if command -v pveversion >/dev/null 2>&1 && [ -n "${SSH_CLIENT:-}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then + : + else + clear + exit + fi + fi +} + exit_script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } +sanitize_vm_hostname() { + local hostname="${1,,}" + hostname=$(echo "$hostname" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') + echo "${hostname:0:63}" +} + +vm_confirm_new_vm() { + local title="$1" + local message="$2" + local height="${3:-10}" + local width="${4:-58}" + + whiptail --backtitle "Proxmox VE Helper Scripts" --title "$title" --yesno "$message" "$height" "$width" +} + +vm_choose_settings_mode() { + local message="${1:-Use Default Settings?}" + local height="${2:-10}" + local width="${3:-58}" + + whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "$message" --no-button Advanced "$height" "$width" +} + +vm_confirm_advanced_settings() { + local message="$1" + local height="${2:-10}" + local width="${3:-58}" + + whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "$message" --no-button Do-Over "$height" "$width" +} + +vm_prompt_vmid() { + local default_vmid="${1:-$(get_valid_nextid)}" + + while true; do + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 "$default_vmid" --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VMID" ]; then + VMID=$(get_valid_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 2 + continue + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + break + else + exit_script + fi + done +} + +vm_apply_machine_type() { + local machine_type="${1:-i440fx}" + + if [ "$machine_type" = "q35" ]; then + MACHINE_TYPE="q35" + FORMAT="" + MACHINE=" -machine q35" + else + MACHINE_TYPE="i440fx" + FORMAT=",efitype=4m" + MACHINE="" + fi +} + +vm_machine_type_label() { + case "${1:-i440fx}" in + q35) + echo "Q35 (Modern)" + ;; + *) + echo "i440fx" + ;; + esac +} + +vm_prompt_machine_type() { + local default_machine="${1:-i440fx}" + local i440fx_default="ON" + local q35_default="OFF" + local machine_choice + + if [ "$default_machine" = "q35" ]; then + i440fx_default="OFF" + q35_default="ON" + fi + + if machine_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ + "i440fx" "Machine i440fx" "$i440fx_default" \ + "q35" "Machine q35" "$q35_default" \ + 3>&1 1>&2 2>&3); then + vm_apply_machine_type "$machine_choice" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$(vm_machine_type_label "$MACHINE_TYPE")${CL}" + else + exit_script + fi +} + +vm_prompt_cloud_init() { + local default_user="${1:-root}" + + USE_CLOUD_INIT="no" + load_cloud_init_functions + + if ! declare -f configure_cloud_init_interactive >/dev/null 2>&1; then + echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}unavailable${CL}" + return 1 + fi + + configure_cloud_init_interactive "$default_user" || true + USE_CLOUD_INIT="${CLOUDINIT_ENABLE:-no}" + echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}" + + if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f configure_cloudinit_ssh_keys >/dev/null 2>&1; then + configure_cloudinit_ssh_keys || true + fi + + return 0 +} + +vm_prompt_disk_size() { + local default_size="${1:-8G}" + local prompt_message="${2:-Set Disk Size in GiB (e.g., 10, 20)}" + + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "$prompt_message" 8 58 "$default_size" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit_script + fi + else + exit_script + fi +} + +vm_prompt_disk_cache() { + local default_cache="${1:-none}" + local none_default="ON" + local write_default="OFF" + local cache_choice + + if [ "$default_cache" = "writethrough" ]; then + none_default="OFF" + write_default="ON" + fi + + if cache_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "None (Default)" "$none_default" \ + "1" "Write Through" "$write_default" \ + 3>&1 1>&2 2>&3); then + if [ "$cache_choice" = "1" ]; then + DISK_CACHE="cache=writethrough," + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" + else + DISK_CACHE="" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + fi + else + exit_script + fi +} + +vm_prompt_hostname() { + local default_hostname="${1:-vm}" + local adjusted_hostname + local input_hostname + + if input_hostname=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$default_hostname" --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$input_hostname" ]; then + HN="$default_hostname" + else + adjusted_hostname=$(sanitize_vm_hostname "$input_hostname") + HN="${adjusted_hostname:-$default_hostname}" + if [ "$HN" != "${input_hostname,,}" ]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 + fi + fi + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + else + exit_script + fi +} + +vm_prompt_cpu_model() { + local default_model="${1:-kvm64}" + local kvm_default="ON" + local host_default="OFF" + local cpu_choice + + if [ "$default_model" = "host" ]; then + kvm_default="OFF" + host_default="ON" + fi + + if cpu_choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "KVM64 (Default)" "$kvm_default" \ + "1" "Host" "$host_default" \ + 3>&1 1>&2 2>&3); then + if [ "$cpu_choice" = "1" ]; then + CPU_TYPE=" -cpu host" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" + else + CPU_TYPE="" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + fi + else + exit_script + fi +} + +vm_prompt_cpu_cores() { + local default_cores="${1:-2}" + + while true; do + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$default_cores" --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$default_cores" + fi + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + fi + whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 + else + exit_script + fi + done +} + +vm_prompt_ram() { + local default_ram="${1:-2048}" + + while true; do + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$default_ram" --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$default_ram" + fi + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + break + fi + whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 + else + exit_script + fi + done +} + +vm_prompt_bridge() { + local default_bridge="${1:-vmbr0}" + + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 "$default_bridge" --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$BRG" ]; then + BRG="$default_bridge" + fi + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + exit_script + fi +} + +vm_prompt_mac() { + local default_mac="${1:-$GEN_MAC}" + local input_mac + + while true; do + if input_mac=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 "$default_mac" --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$input_mac" ]; then + MAC="$default_mac" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + break + fi + if [[ "$input_mac" =~ ^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$ ]]; then + MAC="$input_mac" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + break + fi + whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 + else + exit_script + fi + done +} + +vm_prompt_vlan() { + local default_vlan="${1:-}" + local input_vlan + + while true; do + if input_vlan=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan (leave blank for default)" 8 58 "$default_vlan" --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$input_vlan" ]; then + VLAN="" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + break + fi + if [[ "$input_vlan" =~ ^[0-9]+$ ]] && [ "$input_vlan" -ge 1 ] && [ "$input_vlan" -le 4094 ]; then + VLAN=",tag=$input_vlan" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$input_vlan${CL}" + break + fi + whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 + else + exit_script + fi + done +} + +vm_prompt_mtu() { + local default_mtu="${1:-}" + local input_mtu + + while true; do + if input_mtu=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 "$default_mtu" --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$input_mtu" ]; then + MTU="" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + break + fi + if [[ "$input_mtu" =~ ^[0-9]+$ ]] && [ "$input_mtu" -ge 576 ] && [ "$input_mtu" -le 65520 ]; then + MTU=",mtu=$input_mtu" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$input_mtu${CL}" + break + fi + whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 + else + exit_script + fi + done +} + +vm_prompt_start_vm() { + local default_start="${1:-yes}" + local default_flag=() + + if [ "$default_start" = "no" ]; then + default_flag=(--defaultno) + fi + + if whiptail --backtitle "Proxmox VE Helper Scripts" "${default_flag[@]}" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58; then + START_VM="yes" + else + START_VM="no" + fi + + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}${START_VM}${CL}" +} + +vm_apply_storage_layout() { + local storage_type="$1" + + case $storage_type in + nfs | dir | cifs) + DISK_EXT=".qcow2" + DISK_REF="$VMID/" + DISK_IMPORT_FORMAT="qcow2" + THIN="" + ;; + btrfs) + DISK_EXT=".raw" + DISK_REF="$VMID/" + DISK_IMPORT_FORMAT="raw" + FORMAT=",efitype=4m" + THIN="" + ;; + *) + DISK_EXT="" + DISK_REF="" + DISK_IMPORT_FORMAT="raw" + ;; + esac +} + +vm_select_storage() { + local hostname="${1:-${HN:-vm}}" + local storage_menu=() + local msg_max_length=0 + local line tag type free item + local offset=2 + local valid_storage + + msg_info "Validating Storage" + + 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 " + 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_storage=$(pvesm status -content images | awk 'NR>1') + if [ -z "$valid_storage" ]; then + msg_error "Unable to detect a valid storage location." + exit + elif [ $((${#storage_menu[@]} / 3)) -eq 1 ]; then + STORAGE=${storage_menu[0]} + else + if [ -n "${SPINNER_PID:-}" ] && ps -p "$SPINNER_PID" >/dev/null 2>&1; then + kill "$SPINNER_PID" >/dev/null 2>&1 || true + SPINNER_ACTIVE=0 + printf "\r\e[2K" >&2 + fi + while [ -z "${STORAGE:+x}" ]; do + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ + "Which storage pool would you like to use for ${hostname}?\nTo make a selection, use the Spacebar.\n" \ + 16 $(($msg_max_length + 23)) 6 \ + "${storage_menu[@]}" 3>&1 1>&2 2>&3) + done + fi + + msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." + msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." + + STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') + vm_apply_storage_layout "$STORAGE_TYPE" +} + +vm_define_disk_references() { + local disk_count="${1:-2}" + local i disk_name + + for ((i = 0; i < disk_count; i++)); do + disk_name="vm-${VMID}-disk-${i}${DISK_EXT:-}" + printf -v "DISK${i}" '%s' "$disk_name" + printf -v "DISK${i}_REF" '%s' "${STORAGE}:${DISK_REF:-}${disk_name}" + done +} + check_hostname_conflict() { local hostname="$1" if qm list | awk '{print $2}' | grep -qx "$hostname"; then @@ -591,6 +1092,8 @@ check_hostname_conflict() { } set_description() { + local description_title="${APP:-${NSAPP} VM}" + DESCRIPTION=$( cat < @@ -598,7 +1101,7 @@ set_description() { Logo -

${NSAPP} VM

+

${description_title}

diff --git a/vm/almalinux-10-vm.sh b/vm/almalinux-10-vm.sh index 47173dad..21b84a43 100644 --- a/vm/almalinux-10-vm.sh +++ b/vm/almalinux-10-vm.sh @@ -2,221 +2,51 @@ # Copyright (c) 2021-2026 community-scripts ORG # Author: Agent-Fennec -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) +COMMUNITY_SCRIPTS_URL="${COMMUNITY_SCRIPTS_URL:-https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main}" +source <(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/api.func") +source <(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/vm-core.func") +source <(curl -fsSL "$COMMUNITY_SCRIPTS_URL/misc/cloud-init.func") || true +load_functions -function header_info() { - clear - cat <<"EOF" - - █████╗ ██╗ ███╗ ███╗ █████╗ ██╗ ██╗███╗ ██╗██╗ ██╗██╗ ██╗ - ██╔══██╗██║ ████╗ ████║██╔══██╗██║ ██║████╗ ██║██║ ██║╚██╗██╔╝ - ███████║██║ ██╔████╔██║███████║██║ ██║██╔██╗ ██║██║ ██║ ╚███╔╝ - ██╔══██║██║ ██║╚██╔╝██║██╔══██║██║ ██║██║╚██╗██║██║ ██║ ██╔██╗ - ██║ ██║███████╗██║ ╚═╝ ██║██║ ██║███████╗██║██║ ╚████║╚██████╔╝██╔╝ ██╗ - ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ - - AlmaLinux 10 Installer - (Heliotrope Lion) - -EOF -} -header_info -echo -e "\n Loading..." -GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') -RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" -METHOD="" +APP="AlmaLinux 10 VM" +APP_TYPE="vm" NSAPP="almalinux10vm" var_os="almalinux" var_version="10" -YW=$(echo "\033[33m") -BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -BGN=$(echo "\033[4;92m") -GN=$(echo "\033[1;92m") -DGN=$(echo "\033[32m") -CL=$(echo "\033[m") - -CL=$(echo "\033[m") -BOLD=$(echo "\033[1m") -BFR="\\r\\033[K" -HOLD=" " -TAB=" " - -CM="${TAB}✔️${TAB}${CL}" -CROSS="${TAB}✖️${TAB}${CL}" -INFO="${TAB}💡${TAB}${CL}" -OS="${TAB}🖥️${TAB}${CL}" -CONTAINERTYPE="${TAB}📦${TAB}${CL}" -DISKSIZE="${TAB}💾${TAB}${CL}" -CPUCORE="${TAB}🧠${TAB}${CL}" -RAMSIZE="${TAB}🛠️${TAB}${CL}" -CONTAINERID="${TAB}🆔${TAB}${CL}" -HOSTNAME="${TAB}🏠${TAB}${CL}" -BRIDGE="${TAB}🌉${TAB}${CL}" -GATEWAY="${TAB}🌐${TAB}${CL}" -DEFAULT="${TAB}⚙️${TAB}${CL}" -MACADDRESS="${TAB}🔗${TAB}${CL}" -VLANTAG="${TAB}🏷️${TAB}${CL}" -CREATING="${TAB}🚀${TAB}${CL}" -ADVANCED="${TAB}🧩${TAB}${CL}" - +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +METHOD="" THIN="discard=on,ssd=1," + set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT -trap 'post_update_to_api "failed" "130"' SIGINT -trap 'post_update_to_api "failed" "143"' SIGTERM +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM + function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - post_update_to_api "failed" "${exit_code}" + post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" cleanup_vmid } -function get_valid_nextid() { - local try_id - try_id=$(pvesh get /cluster/nextid) - while true; do - if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then - try_id=$((try_id + 1)) - continue - fi - if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then - try_id=$((try_id + 1)) - continue - fi - break - done - echo "$try_id" -} - -function cleanup_vmid() { - if qm status "$VMID" &>/dev/null; then - qm stop "$VMID" &>/dev/null - qm destroy "$VMID" &>/dev/null - fi -} - -function cleanup() { - local exit_code=$? - popd 2>/dev/null || true - if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then - if [[ $exit_code -eq 0 ]]; then - post_update_to_api "done" "none" || true - else - post_update_to_api "failed" "$exit_code" || true - fi - fi - rm -rf "$TEMP_DIR" - rm -f "${WORK_FILE:-}" -} - TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "AlmaLinux 10 VM" --yesno "This will create a New AlmaLinux 10 VM. Proceed?" 10 58; then : else - header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit + header_info && exit_script fi -function msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" -} - -function msg_ok() { - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -function msg_error() { - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} - -function check_root() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi -} - -# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. -# Supported: Proxmox VE 8.0.x – 8.9.x, 9.0 and 9.1 -pve_check() { - local PVE_VER - PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - - # Check for Proxmox VE 8.x: allow 8.0–8.9 - if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 0 || MINOR > 9)); then - msg_error "This version of Proxmox VE is not supported." - msg_error "Supported: Proxmox VE version 8.0 – 8.9" - exit 1 - fi - return 0 - fi - - # Check for Proxmox VE 9.x: allow 9.0 and 9.1 - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 0 || MINOR > 1)); then - msg_error "This version of Proxmox VE is not supported." - msg_error "Supported: Proxmox VE version 9.0 – 9.1" - exit 1 - fi - return 0 - fi - - # All other unsupported versions - msg_error "This version of Proxmox VE is not supported." - msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0 – 9.1" - exit 1 -} - -function arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" - echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi -} - -function ssh_check() { - if command -v pveversion >/dev/null 2>&1; then - if [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then - echo "you've been warned" - else - clear - exit - fi - fi - fi -} - -function exit-script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - popd >/dev/null 2>&1 || true - rm -rf "${TEMP_DIR:-}" - rm -f "${WORK_FILE:-}" - exec bash -} - function default_settings() { + configure_cloudinit_ssh_keys || true VMID=$(get_valid_nextid) FORMAT="" MACHINE=" -machine q35" @@ -264,7 +94,7 @@ function advanced_settings() { echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else - exit-script + exit_script fi done @@ -282,10 +112,10 @@ function advanced_settings() { MACHINE=" -machine q35" fi else - exit-script + exit_script fi - if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "10" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G" @@ -294,10 +124,10 @@ function advanced_settings() { echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" - exit-script + exit_script fi else - exit-script + exit_script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ @@ -312,7 +142,7 @@ function advanced_settings() { DISK_CACHE="" fi else - exit-script + exit_script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 almalinux --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then @@ -320,13 +150,10 @@ function advanced_settings() { HN="almalinux" else HN=$(echo "${VM_NAME,,}" | tr -cs 'a-z0-9-' '-' | sed 's/^-//;s/-$//') - if [ "$HN" != "${VM_NAME,,}" ]; then - whiptail --backtitle "Proxmox VE Helper Scripts" --title "HOSTNAME ADJUSTED" --msgbox "Invalid characters detected. Hostname has been adjusted to:\n\n $HN" 10 58 - fi fi echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else - exit-script + exit_script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ @@ -341,7 +168,7 @@ function advanced_settings() { CPU_TYPE=" -cpu x86-64-v3" fi else - exit-script + exit_script fi while true; do @@ -353,7 +180,7 @@ function advanced_settings() { fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "CPU Cores must be a positive integer (e.g., 2)." 8 58 else - exit-script + exit_script fi done @@ -366,17 +193,15 @@ function advanced_settings() { fi whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "RAM Size must be a positive integer in MiB (e.g., 2048)." 8 58 else - exit-script + exit_script fi done if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "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" - fi + if [ -z "$BRG" ]; then BRG="vmbr0"; fi echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else - exit-script + exit_script fi while true; do @@ -391,9 +216,9 @@ function advanced_settings() { echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" break fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX (e.g., AA:BB:CC:DD:EE:FF)." 8 58 + whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "Invalid MAC address format. Use XX:XX:XX:XX:XX:XX." 8 58 else - exit-script + exit_script fi done @@ -410,9 +235,9 @@ function advanced_settings() { echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" break fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank for default." 8 58 + whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "VLAN must be a number between 1 and 4094, or leave blank." 8 58 else - exit-script + exit_script fi done @@ -429,9 +254,9 @@ function advanced_settings() { echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" break fi - whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be a number between 576 and 65520, or leave blank for default." 8 58 + whiptail --backtitle "Proxmox VE Helper Scripts" --title "INVALID INPUT" --msgbox "MTU Size must be between 576 and 65520, or leave blank." 8 58 else - exit-script + exit_script fi done @@ -443,6 +268,8 @@ function advanced_settings() { START_VM="no" fi + configure_cloudinit_ssh_keys || true + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create an AlmaLinux 10 VM?" --no-button Do-Over 10 58); then echo -e "${CREATING}${BOLD}${DGN}Creating an AlmaLinux 10 VM using the above advanced settings${CL}" else @@ -464,12 +291,7 @@ function start_script() { fi } -check_root -arch_check -pve_check -ssh_check start_script - post_to_api_vm msg_info "Validating Storage" @@ -496,7 +318,7 @@ while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $((MSG_MAX_LENGTH + 23)) 6 \ - "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit-script + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit_script done msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." @@ -506,8 +328,8 @@ msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." # ============================================================================== if ! command -v virt-customize &>/dev/null; then msg_info "Installing libguestfs-tools" - apt-get -qq update >/dev/null 2>&1 - apt-get -qq install -y libguestfs-tools >/dev/null 2>&1 + $STD apt-get update + $STD apt-get install -y libguestfs-tools msg_ok "Installed libguestfs-tools" fi @@ -530,24 +352,13 @@ cp "$FILE" "$WORK_FILE" popd >/dev/null rm -rf "$TEMP_DIR" -# Set hostname virt-customize -q -a "$WORK_FILE" --hostname "${HN}" >/dev/null 2>&1 - -# Prepare for unique machine-id on first boot virt-customize -q -a "$WORK_FILE" --run-command "truncate -s 0 /etc/machine-id" >/dev/null 2>&1 virt-customize -q -a "$WORK_FILE" --run-command "rm -f /var/lib/dbus/machine-id" >/dev/null 2>&1 - -# Disable systemd-firstboot to prevent interactive prompts blocking the console virt-customize -q -a "$WORK_FILE" --run-command "systemctl disable systemd-firstboot.service 2>/dev/null; rm -f /etc/systemd/system/sysinit.target.wants/systemd-firstboot.service; ln -sf /dev/null /etc/systemd/system/systemd-firstboot.service" >/dev/null 2>&1 || true - -# Cloud-Init handles SSH and login virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true - -# Enable serial console login virt-customize -q -a "$WORK_FILE" --run-command "systemctl enable serial-getty@ttyS0.service" >/dev/null 2>&1 || true - -# Relabel SELinux contexts after all modifications (required for AlmaLinux/RHEL) virt-customize -q -a "$WORK_FILE" --selinux-relabel >/dev/null 2>&1 || true msg_ok "Customized image" @@ -579,7 +390,6 @@ for i in {0,1,2}; do eval DISK"${i}"_REF="${STORAGE}":"${DISK_REF:-}""${!disk}" done -# For block/btrfs storage, pre-convert qcow2 to raw to ensure compatibility if [[ "$STORAGE_TYPE" != "nfs" && "$STORAGE_TYPE" != "dir" ]]; then msg_info "Converting image to raw format" RAW_FILE=$(mktemp --suffix=.raw) @@ -598,49 +408,19 @@ qm importdisk "$VMID" "${WORK_FILE}" "$STORAGE" ${DISK_IMPORT:-} 1>&/dev/null qm set "$VMID" \ -efidisk0 "${DISK0_REF}"${FORMAT} \ -scsi0 "${DISK1_REF}",${DISK_CACHE}${THIN}size="${DISK_SIZE}" \ - -scsi1 "${STORAGE}":cloudinit \ -tpmstate0 "${DISK2_REF}",version=v2.0 \ -boot order=scsi0 \ -serial0 socket >/dev/null -# Clean up work file rm -f "$WORK_FILE" +msg_ok "Created an AlmaLinux 10 VM ${CL}${BL}(${HN})" -DESCRIPTION=$( - cat < - - Logo - - -

AlmaLinux 10 VM

- -

- - spend Coffee - -

- - - - GitHub - - - - Discussions - - - - Issues - - -EOF -) -qm set "$VMID" -description "$DESCRIPTION" >/dev/null msg_info "Resizing disk to ${DISK_SIZE}" qm resize "$VMID" scsi0 "${DISK_SIZE}" >/dev/null +msg_ok "Resized disk to ${DISK_SIZE}" + +setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" -msg_ok "Created an AlmaLinux 10 VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting AlmaLinux 10 VM" qm start "$VMID" @@ -649,5 +429,8 @@ fi post_update_to_api "done" "none" -msg_ok "Completed successfully" -echo -e "Setup Cloud-Init before starting \nMore info at https://github.com/community-scripts/ProxmoxVE/discussions/272 \n" +msg_ok "Completed successfully!" +if [ -n "${CLOUDINIT_CRED_FILE:-}" ]; then + echo -e "${INFO}${YW} Cloud-Init credentials saved to: ${BGN}${CLOUDINIT_CRED_FILE}${CL}" +fi + diff --git a/vm/headers/almalinux-10-vm b/vm/headers/almalinux-10-vm new file mode 100644 index 00000000..f4d423de --- /dev/null +++ b/vm/headers/almalinux-10-vm @@ -0,0 +1,6 @@ + ___ __ __ _ _______ _ ____ ___ + / | / /___ ___ ____ _/ / (_)___ __ ___ __ < / __ \ | | / / |/ / + / /| | / / __ `__ \/ __ `/ / / / __ \/ / / / |/_/ / / / / / | | / / /|_/ / + / ___ |/ / / / / / / /_/ / /___/ / / / / /_/ /> < / / /_/ / | |/ / / / / +/_/ |_/_/_/ /_/ /_/\__,_/_____/_/_/ /_/\__,_/_/|_| /_/\____/ |___/_/ /_/ + diff --git a/vm/headers/ubuntu2604-vm b/vm/headers/ubuntu2604-vm new file mode 100644 index 00000000..92de494a --- /dev/null +++ b/vm/headers/ubuntu2604-vm @@ -0,0 +1,6 @@ + __ ____ __ ___ _____ ____ __ __ _ ____ ___ + / / / / /_ __ ______ / /___ __ |__ \ / ___/ / __ \/ // / | | / / |/ / + / / / / __ \/ / / / __ \/ __/ / / / __/ // __ \ / / / / // /_ | | / / /|_/ / +/ /_/ / /_/ / /_/ / / / / /_/ /_/ / / __// /_/ // /_/ /__ __/ | |/ / / / / +\____/_.___/\__,_/_/ /_/\__/\__,_/ /____/\____(_)____/ /_/ |___/_/ /_/ + diff --git a/vm/ubuntu2604-vm.sh b/vm/ubuntu2604-vm.sh new file mode 100644 index 00000000..48d691ee --- /dev/null +++ b/vm/ubuntu2604-vm.sh @@ -0,0 +1,177 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE + +source <(curl -fsSL "${COMMUNITY_SCRIPTS_URL:-https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main}/misc/vm-core.func") +load_functions + +function header_info { + clear + cat <<"EOF" + __ ____ __ ___ ______ ____ __ __ _ ____ ___ + / / / / /_ __ ______ / /___ __ |__ \ / ____// __ \/ // / | | / / |/ / + / / / / __ \/ / / / __ \/ __/ / / / __/ //___ \ / / / / // /_ | | / / /|_/ / +/ /_/ / /_/ / /_/ / / / / /_/ /_/ / / __/____/ // /_/ /__ __/ | |/ / / / / +\____/_.___/\__,_/_/ /_/\__/\__,_/ /____/_____(_)____/ /_/ |___/_/ /_/ (Plucky Puffin) + +EOF +} + +APP="Ubuntu 26.04 VM" +APP_TYPE="vm" +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +METHOD="" +NSAPP="ubuntu2604-vm" +var_os="ubuntu" +var_version="2604" +THIN="discard=on,ssd=1," +USE_CLOUD_INIT="no" + +header_info +echo -e "\n Loading..." + +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +trap cleanup EXIT +trap 'post_update_to_api "failed" "130"' SIGINT +trap 'post_update_to_api "failed" "143"' SIGTERM +trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP + +TEMP_DIR=$(mktemp -d) +pushd "$TEMP_DIR" >/dev/null + +if vm_confirm_new_vm "$APP" "This will create a New $APP. Proceed?"; then + : +else + header_info && exit_script +fi + +check_root +arch_check +pve_check +ssh_check +vm_prompt_cloud_init "ubuntu" + +function default_settings() { + VMID=$(get_valid_nextid) + vm_apply_machine_type "q35" + DISK_SIZE="7G" + DISK_CACHE="" + HN="ubuntu" + CPU_TYPE="" + CORE_COUNT="2" + RAM_SIZE="2048" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + START_VM="yes" + METHOD="default" + + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$(vm_machine_type_label "$MACHINE_TYPE")${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}${START_VM}${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 26.04 VM using the above default settings${CL}" +} + +function advanced_settings() { + METHOD="advanced" + echo -e "${CLOUD}${BOLD}${DGN}Cloud-Init: ${BGN}${USE_CLOUD_INIT}${CL}" + vm_prompt_vmid "${VMID:-$(get_valid_nextid)}" + vm_prompt_machine_type "q35" + vm_prompt_disk_size "${DISK_SIZE:-7G}" "Set Disk Size in GiB (e.g., 10, 20)" + vm_prompt_disk_cache "none" + vm_prompt_hostname "ubuntu" + vm_prompt_cpu_model "kvm64" + vm_prompt_cpu_cores "2" + vm_prompt_ram "2048" + vm_prompt_bridge "vmbr0" + vm_prompt_mac "$GEN_MAC" + vm_prompt_vlan + vm_prompt_mtu + vm_prompt_start_vm "yes" + + if vm_confirm_advanced_settings "Ready to create a Ubuntu 26.04 VM?"; then + echo -e "${CREATING}${BOLD}${DGN}Creating a Ubuntu 26.04 VM using the above advanced settings${CL}" + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +function start_script() { + if vm_choose_settings_mode; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" + default_settings + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +start_script +post_to_api_vm + +vm_select_storage "$HN" +vm_define_disk_references 2 +DISK_IMPORT="-format ${DISK_IMPORT_FORMAT}" + +msg_info "Retrieving the URL for the Ubuntu 26.04 Disk Image" +URL="https://cloud-images.ubuntu.com/releases/server/26.04/release/ubuntu-26.04-server-cloudimg-amd64.img" +sleep 2 +msg_ok "${CL}${BL}${URL}${CL}" +curl -f#SL -o "$(basename "$URL")" "$URL" +echo -en "\e[1A\e[0K" +FILE="$(basename "$URL")" +msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" + +msg_info "Creating a Ubuntu 26.04 VM" +qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ + -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci +pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null +qm importdisk $VMID $FILE $STORAGE ${DISK_IMPORT:-} 1>&/dev/null +qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +set_description + +msg_info "Resizing disk to $DISK_SIZE" +qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null + +if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f setup_cloud_init >/dev/null 2>&1; then + setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes" "${CLOUDINIT_USER:-ubuntu}" "${CLOUDINIT_NETWORK_MODE:-dhcp}" "${CLOUDINIT_IP:-}" "${CLOUDINIT_GW:-}" "${CLOUDINIT_DNS:-${CLOUDINIT_DNS_SERVERS:-1.1.1.1 8.8.8.8}}" +fi + +msg_ok "Created a Ubuntu 26.04 VM ${CL}${BL}(${HN})" +if [ "$START_VM" = "yes" ]; then + msg_info "Starting Ubuntu 26.04 VM" + qm start $VMID + msg_ok "Started Ubuntu 26.04 VM" +fi + +post_update_to_api "done" "none" +msg_ok "Completed successfully!\n" +if [ "$USE_CLOUD_INIT" = "yes" ] && declare -f display_cloud_init_info >/dev/null 2>&1; then + display_cloud_init_info "$VMID" "$HN" +else + echo -e "Cloud-Init is disabled. The VM disk was resized on the Proxmox side only.\nIf the guest does not auto-expand its root filesystem after first boot, expand it manually inside the VM.\n\nMore info at https://github.com/community-scripts/ProxmoxVED/discussions/272 \n" +fi