mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-03-04 09:55:59 +01:00
Compare commits
3 Commits
copilot/fi
...
preflight_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b68d65c64d | ||
|
|
5f9f283fa6 | ||
|
|
f81d0b5188 |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -416,26 +416,10 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
|||||||
|
|
||||||
- Tinyauth: v5 Support & add Debian Version [@MickLesk](https://github.com/MickLesk) ([#12501](https://github.com/community-scripts/ProxmoxVE/pull/12501))
|
- Tinyauth: v5 Support & add Debian Version [@MickLesk](https://github.com/MickLesk) ([#12501](https://github.com/community-scripts/ProxmoxVE/pull/12501))
|
||||||
|
|
||||||
### 🚀 Updated Scripts
|
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
|
||||||
|
|
||||||
- meshcentral: increased disk space to 4GB [@MickLesk](https://github.com/MickLesk) ([#12509](https://github.com/community-scripts/ProxmoxVE/pull/12509))
|
|
||||||
|
|
||||||
- #### 🔧 Refactor
|
|
||||||
|
|
||||||
- opnsense-vm: harden temp dir, bridge detection and network selection [@MickLesk](https://github.com/MickLesk) ([#12513](https://github.com/community-scripts/ProxmoxVE/pull/12513))
|
|
||||||
|
|
||||||
### 🗑️ Deleted Scripts
|
### 🗑️ Deleted Scripts
|
||||||
|
|
||||||
- Remove Unifi Network Server scripts (dead APT repo) [@Copilot](https://github.com/Copilot) ([#12500](https://github.com/community-scripts/ProxmoxVE/pull/12500))
|
- Remove Unifi Network Server scripts (dead APT repo) [@Copilot](https://github.com/Copilot) ([#12500](https://github.com/community-scripts/ProxmoxVE/pull/12500))
|
||||||
|
|
||||||
### 💾 Core
|
|
||||||
|
|
||||||
- #### ✨ New Features
|
|
||||||
|
|
||||||
- core: recovery - add ENOSPC disk-full detection with auto-retry using * 2 hdd [@MickLesk](https://github.com/MickLesk) ([#12511](https://github.com/community-scripts/ProxmoxVE/pull/12511))
|
|
||||||
|
|
||||||
### 🌐 Website
|
### 🌐 Website
|
||||||
|
|
||||||
- #### 🐞 Bug Fixes
|
- #### 🐞 Bug Fixes
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ function update_script() {
|
|||||||
check_container_resources
|
check_container_resources
|
||||||
|
|
||||||
NODE_VERSION="24" setup_nodejs
|
NODE_VERSION="24" setup_nodejs
|
||||||
ensure_dependencies build-essential
|
|
||||||
|
|
||||||
if command -v cross-seed &>/dev/null; then
|
if command -v cross-seed &>/dev/null; then
|
||||||
current_version=$(cross-seed --version)
|
current_version=$(cross-seed --version)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ APP="MeshCentral"
|
|||||||
var_tags="${var_tags:-remote-management}"
|
var_tags="${var_tags:-remote-management}"
|
||||||
var_cpu="${var_cpu:-1}"
|
var_cpu="${var_cpu:-1}"
|
||||||
var_ram="${var_ram:-512}"
|
var_ram="${var_ram:-512}"
|
||||||
var_disk="${var_disk:-4}"
|
var_disk="${var_disk:-2}"
|
||||||
var_os="${var_os:-debian}"
|
var_os="${var_os:-debian}"
|
||||||
var_version="${var_version:-13}"
|
var_version="${var_version:-13}"
|
||||||
var_unprivileged="${var_unprivileged:-1}"
|
var_unprivileged="${var_unprivileged:-1}"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"generated": "2026-03-03T12:12:16Z",
|
"generated": "2026-03-03T06:17:56Z",
|
||||||
"versions": [
|
"versions": [
|
||||||
{
|
{
|
||||||
"slug": "2fauth",
|
"slug": "2fauth",
|
||||||
@@ -1562,13 +1562,6 @@
|
|||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-13T16:30:09Z"
|
"date": "2026-02-13T16:30:09Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"slug": "tinyauth",
|
|
||||||
"repo": "steveiliop56/tinyauth",
|
|
||||||
"version": "v5.0.0",
|
|
||||||
"pinned": false,
|
|
||||||
"date": "2026-03-02T18:43:57Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"slug": "traccar",
|
"slug": "traccar",
|
||||||
"repo": "traccar/traccar",
|
"repo": "traccar/traccar",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"cpu": 1,
|
"cpu": 1,
|
||||||
"ram": 512,
|
"ram": 512,
|
||||||
"hdd": 4,
|
"hdd": 2,
|
||||||
"os": "debian",
|
"os": "debian",
|
||||||
"version": "13"
|
"version": "13"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,6 @@ setting_up_container
|
|||||||
network_check
|
network_check
|
||||||
update_os
|
update_os
|
||||||
|
|
||||||
msg_info "Installing Dependencies"
|
|
||||||
$STD apt install -y build-essential
|
|
||||||
msg_ok "Installed Dependencies"
|
|
||||||
|
|
||||||
NODE_VERSION="24" setup_nodejs
|
NODE_VERSION="24" setup_nodejs
|
||||||
|
|
||||||
msg_info "Setup Cross-Seed"
|
msg_info "Setup Cross-Seed"
|
||||||
|
|||||||
685
misc/build.func
685
misc/build.func
@@ -100,58 +100,624 @@ fi
|
|||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
# SECTION 2: PRE-FLIGHT CHECKS & SYSTEM VALIDATION
|
# SECTION 2: PRE-FLIGHT CHECKS & SYSTEM VALIDATION
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# Runs comprehensive system checks BEFORE container creation to catch common
|
||||||
|
# issues early. This prevents users from going through the entire configuration
|
||||||
|
# menu only to have creation fail due to a system-level problem.
|
||||||
|
#
|
||||||
|
# Checks performed (via run_preflight):
|
||||||
|
# - Kernel: keyring limits (maxkeys/maxbytes for UID 100000)
|
||||||
|
# - Storage: rootdir support, vztmpl support, available space
|
||||||
|
# - Network: bridge availability, DNS resolution
|
||||||
|
# - Repos: enterprise repo subscription validation
|
||||||
|
# - Cluster: quorum status (if clustered)
|
||||||
|
# - Proxmox: LXC stack health, container ID availability
|
||||||
|
# - Template: download server reachability
|
||||||
|
#
|
||||||
|
# Design:
|
||||||
|
# - All checks run and results are collected (no exit on first failure)
|
||||||
|
# - Clear, actionable error messages with suggested fixes
|
||||||
|
# - Reports "aborted" status to telemetry (not "failed")
|
||||||
|
# - Uses existing exit codes for consistency with error_handler/api.func
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# --- Preflight tracking globals ---
|
||||||
|
PREFLIGHT_PASSED=0
|
||||||
|
PREFLIGHT_FAILED=0
|
||||||
|
PREFLIGHT_WARNINGS=0
|
||||||
|
PREFLIGHT_FAILURES=()
|
||||||
|
PREFLIGHT_EXIT_CODE=0
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# maxkeys_check()
|
# preflight_pass() / preflight_fail() / preflight_warn()
|
||||||
|
#
|
||||||
|
# - Track individual check results
|
||||||
|
# - preflight_fail stores message + exit_code for summary
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_pass() {
|
||||||
|
local msg="$1"
|
||||||
|
((PREFLIGHT_PASSED++)) || true
|
||||||
|
echo -e " ${CM} ${GN}${msg}${CL}"
|
||||||
|
}
|
||||||
|
|
||||||
|
preflight_fail() {
|
||||||
|
local msg="$1"
|
||||||
|
local exit_code="${2:-1}"
|
||||||
|
((PREFLIGHT_FAILED++)) || true
|
||||||
|
PREFLIGHT_FAILURES+=("${exit_code}|${msg}")
|
||||||
|
[[ "$PREFLIGHT_EXIT_CODE" -eq 0 ]] && PREFLIGHT_EXIT_CODE="$exit_code"
|
||||||
|
echo -e " ${CROSS} ${RD}${msg}${CL}"
|
||||||
|
}
|
||||||
|
|
||||||
|
preflight_warn() {
|
||||||
|
local msg="$1"
|
||||||
|
((PREFLIGHT_WARNINGS++)) || true
|
||||||
|
echo -e " ${INFO} ${YW}${msg}${CL}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_maxkeys()
|
||||||
#
|
#
|
||||||
# - Reads kernel keyring limits (maxkeys, maxbytes)
|
# - Reads kernel keyring limits (maxkeys, maxbytes)
|
||||||
# - Checks current usage for LXC user (UID 100000)
|
# - Checks current usage for LXC user (UID 100000)
|
||||||
# - Warns if usage is close to limits and suggests sysctl tuning
|
# - Warns if usage is close to limits and suggests sysctl tuning
|
||||||
# - Exits if thresholds are exceeded
|
# - https://cleveruptime.com/docs/files/proc-key-users
|
||||||
# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html
|
# - https://docs.kernel.org/security/keys/core.html
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_maxkeys() {
|
||||||
maxkeys_check() {
|
local per_user_maxkeys per_user_maxbytes
|
||||||
# Read kernel parameters
|
|
||||||
per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0)
|
per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0)
|
||||||
per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0)
|
per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0)
|
||||||
|
|
||||||
# Exit if kernel parameters are unavailable
|
|
||||||
if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then
|
if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then
|
||||||
msg_error "Unable to read kernel key parameters. Ensure proper permissions."
|
preflight_fail "Unable to read kernel key parameters" 107
|
||||||
exit 107
|
echo -e " ${TAB}${INFO} Ensure proper permissions to /proc/sys/kernel/keys/"
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Fetch key usage for user ID 100000 (typical for containers)
|
local used_lxc_keys used_lxc_bytes
|
||||||
used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0)
|
used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0)
|
||||||
used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0)
|
used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0)
|
||||||
|
|
||||||
# Calculate thresholds and suggested new limits
|
local threshold_keys=$((per_user_maxkeys - 100))
|
||||||
threshold_keys=$((per_user_maxkeys - 100))
|
local threshold_bytes=$((per_user_maxbytes - 1000))
|
||||||
threshold_bytes=$((per_user_maxbytes - 1000))
|
local new_limit_keys=$((per_user_maxkeys * 2))
|
||||||
new_limit_keys=$((per_user_maxkeys * 2))
|
local new_limit_bytes=$((per_user_maxbytes * 2))
|
||||||
new_limit_bytes=$((per_user_maxbytes * 2))
|
|
||||||
|
|
||||||
# Check if key or byte usage is near limits
|
local failure=0
|
||||||
failure=0
|
|
||||||
if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then
|
if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then
|
||||||
msg_warn "Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys})"
|
|
||||||
echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}."
|
|
||||||
failure=1
|
failure=1
|
||||||
fi
|
fi
|
||||||
if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then
|
if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then
|
||||||
msg_warn "Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes})"
|
|
||||||
echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}."
|
|
||||||
failure=1
|
failure=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Provide next steps if issues are detected
|
|
||||||
if [[ "$failure" -eq 1 ]]; then
|
if [[ "$failure" -eq 1 ]]; then
|
||||||
msg_error "Kernel key limits exceeded - see suggestions above"
|
preflight_fail "Kernel key limits near threshold (keys: ${used_lxc_keys}/${per_user_maxkeys}, bytes: ${used_lxc_bytes}/${per_user_maxbytes})" 108
|
||||||
exit 108
|
echo -e " ${TAB}${INFO} Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} and ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL}"
|
||||||
|
echo -e " ${TAB}${INFO} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}, then run: ${GN}sysctl --system${CL}"
|
||||||
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Silent success - only show errors if they exist
|
preflight_pass "Kernel key limits OK (keys: ${used_lxc_keys}/${per_user_maxkeys})"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_storage_rootdir()
|
||||||
|
#
|
||||||
|
# - Verifies at least one storage supports 'rootdir' content type
|
||||||
|
# - Without this, no LXC container can be created
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_storage_rootdir() {
|
||||||
|
local count
|
||||||
|
count=$(pvesm status -content rootdir 2>/dev/null | awk 'NR>1 {count++} END {print count+0}')
|
||||||
|
|
||||||
|
if [[ "$count" -eq 0 ]]; then
|
||||||
|
preflight_fail "No storage with 'rootdir' support found" 119
|
||||||
|
echo -e " ${TAB}${INFO} Enable 'rootdir' content on a storage in Datacenter → Storage"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
preflight_pass "Storage with 'rootdir' support available (${count} storage(s))"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_storage_vztmpl()
|
||||||
|
#
|
||||||
|
# - Verifies at least one storage supports 'vztmpl' content type
|
||||||
|
# - Required for downloading and storing OS templates
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_storage_vztmpl() {
|
||||||
|
local count
|
||||||
|
count=$(pvesm status -content vztmpl 2>/dev/null | awk 'NR>1 {count++} END {print count+0}')
|
||||||
|
|
||||||
|
if [[ "$count" -eq 0 ]]; then
|
||||||
|
preflight_fail "No storage with 'vztmpl' support found" 120
|
||||||
|
echo -e " ${TAB}${INFO} Enable 'vztmpl' content on a storage in Datacenter → Storage"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
preflight_pass "Storage with 'vztmpl' support available (${count} storage(s))"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_storage_space()
|
||||||
|
#
|
||||||
|
# - Checks if any rootdir-capable storage has enough free space
|
||||||
|
# - Uses the app-declared var_disk as minimum requirement
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_storage_space() {
|
||||||
|
local required_gb="${var_disk:-4}"
|
||||||
|
local required_kb=$((required_gb * 1024 * 1024))
|
||||||
|
local has_enough=0
|
||||||
|
local best_storage=""
|
||||||
|
local best_free=0
|
||||||
|
|
||||||
|
while read -r storage_name _ _ _ _ free_kb _; do
|
||||||
|
[[ -z "$storage_name" || -z "$free_kb" ]] && continue
|
||||||
|
[[ "$free_kb" == "0" ]] && continue
|
||||||
|
|
||||||
|
if [[ "$free_kb" -ge "$required_kb" ]]; then
|
||||||
|
has_enough=1
|
||||||
|
if [[ "$free_kb" -gt "$best_free" ]]; then
|
||||||
|
best_free="$free_kb"
|
||||||
|
best_storage="$storage_name"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < <(pvesm status -content rootdir 2>/dev/null | awk 'NR>1')
|
||||||
|
|
||||||
|
if [[ "$has_enough" -eq 0 ]]; then
|
||||||
|
preflight_fail "No storage has enough space (need ${required_gb}GB for ${APP})" 214
|
||||||
|
echo -e " ${TAB}${INFO} Free up disk space or add a new storage with sufficient capacity"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local best_free_fmt
|
||||||
|
best_free_fmt=$(numfmt --to=iec --from-unit=1024 --suffix=B --format %.1f "$best_free" 2>/dev/null || echo "${best_free}KB")
|
||||||
|
preflight_pass "Sufficient storage space (${best_storage}: ${best_free_fmt} free, need ${required_gb}GB)"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_network_bridge()
|
||||||
|
#
|
||||||
|
# - Checks if at least one network bridge exists (vmbr*)
|
||||||
|
# - Verifies vmbr0 specifically (default bridge used by most scripts)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_network_bridge() {
|
||||||
|
local bridges
|
||||||
|
bridges=$(ip -o link show type bridge 2>/dev/null | grep -oE 'vmbr[0-9]+' | sort -u)
|
||||||
|
|
||||||
|
if [[ -z "$bridges" ]]; then
|
||||||
|
preflight_fail "No network bridge (vmbr*) found" 116
|
||||||
|
echo -e " ${TAB}${INFO} Create a bridge in Network → Create → Linux Bridge"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if echo "$bridges" | grep -qx "vmbr0"; then
|
||||||
|
preflight_pass "Default network bridge vmbr0 available"
|
||||||
|
else
|
||||||
|
local first_bridge
|
||||||
|
first_bridge=$(echo "$bridges" | head -1)
|
||||||
|
preflight_warn "Default bridge vmbr0 not found, but ${first_bridge} is available"
|
||||||
|
echo -e " ${TAB}${INFO} Scripts default to vmbr0 — use Advanced Settings to select ${first_bridge}"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_dns_resolution()
|
||||||
|
#
|
||||||
|
# - Tests if DNS resolution works (required for template downloads)
|
||||||
|
# - Tries multiple hosts to avoid false positives
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_dns_resolution() {
|
||||||
|
local test_hosts=("download.proxmox.com" "raw.githubusercontent.com" "community-scripts.org")
|
||||||
|
local resolved=0
|
||||||
|
|
||||||
|
for host in "${test_hosts[@]}"; do
|
||||||
|
if getent hosts "$host" &>/dev/null; then
|
||||||
|
resolved=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$resolved" -eq 0 ]]; then
|
||||||
|
for host in "${test_hosts[@]}"; do
|
||||||
|
if command -v nslookup &>/dev/null && nslookup "$host" &>/dev/null; then
|
||||||
|
resolved=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$resolved" -eq 0 ]]; then
|
||||||
|
preflight_fail "DNS resolution failed — cannot reach template servers" 222
|
||||||
|
echo -e " ${TAB}${INFO} Check /etc/resolv.conf and network connectivity"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
preflight_pass "DNS resolution working"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_repo_access()
|
||||||
|
#
|
||||||
|
# - Checks if Proxmox enterprise repos are enabled without a valid subscription
|
||||||
|
# - Scans /etc/apt/sources.list.d/ for enterprise.proxmox.com entries
|
||||||
|
# - Tests HTTP access to detect 401 Unauthorized
|
||||||
|
# - Warning only (not a blocker — packages come from no-subscription repo)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_repo_access() {
|
||||||
|
local enterprise_files
|
||||||
|
enterprise_files=$(grep -rlE '^\s*deb\s+https://enterprise\.proxmox\.com' /etc/apt/sources.list.d/ 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [[ -z "$enterprise_files" ]]; then
|
||||||
|
preflight_pass "No enterprise repositories enabled"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enterprise repo found — test if subscription is valid
|
||||||
|
local http_code
|
||||||
|
http_code=$(curl -sS -o /dev/null -w "%{http_code}" -m 5 "https://enterprise.proxmox.com/debian/pve/dists/" 2>/dev/null) || http_code="000"
|
||||||
|
|
||||||
|
if [[ "$http_code" == "401" || "$http_code" == "403" ]]; then
|
||||||
|
preflight_warn "Enterprise repo enabled without valid subscription (HTTP ${http_code})"
|
||||||
|
echo -e " ${TAB}${INFO} apt-get update will show '401 Unauthorized' errors"
|
||||||
|
echo -e " ${TAB}${INFO} Disable in ${GN}/etc/apt/sources.list.d/${CL} or add a subscription key"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$http_code" =~ ^2[0-9]{2}$ ]]; then
|
||||||
|
preflight_pass "Enterprise repository accessible (subscription valid)"
|
||||||
|
else
|
||||||
|
preflight_warn "Enterprise repo check inconclusive (HTTP ${http_code})"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_cluster_quorum()
|
||||||
|
#
|
||||||
|
# - Checks cluster quorum status (only if node is part of a cluster)
|
||||||
|
# - Skipped on standalone nodes
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_cluster_quorum() {
|
||||||
|
if [[ ! -f /etc/pve/corosync.conf ]]; then
|
||||||
|
preflight_pass "Standalone node (no cluster quorum needed)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if pvecm status 2>/dev/null | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then
|
||||||
|
preflight_pass "Cluster is quorate"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
preflight_fail "Cluster is not quorate — container operations will fail" 210
|
||||||
|
echo -e " ${TAB}${INFO} Ensure all cluster nodes are running, or configure a QDevice"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_lxc_stack()
|
||||||
|
#
|
||||||
|
# - Validates pve-container and lxc-pve packages are installed
|
||||||
|
# - Checks for available updates (informational only)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_lxc_stack() {
|
||||||
|
local pve_container_ver lxc_pve_ver
|
||||||
|
|
||||||
|
pve_container_ver=$(dpkg-query -W -f='${Version}\n' pve-container 2>/dev/null || echo "")
|
||||||
|
lxc_pve_ver=$(dpkg-query -W -f='${Version}\n' lxc-pve 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if [[ -z "$pve_container_ver" ]]; then
|
||||||
|
preflight_fail "Package 'pve-container' is not installed" 231
|
||||||
|
echo -e " ${TAB}${INFO} Run: apt-get install pve-container"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$lxc_pve_ver" ]]; then
|
||||||
|
preflight_fail "Package 'lxc-pve' is not installed" 231
|
||||||
|
echo -e " ${TAB}${INFO} Run: apt-get install lxc-pve"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pve_container_cand lxc_pve_cand
|
||||||
|
pve_container_cand=$(apt-cache policy pve-container 2>/dev/null | awk '/Candidate:/ {print $2}') || true
|
||||||
|
lxc_pve_cand=$(apt-cache policy lxc-pve 2>/dev/null | awk '/Candidate:/ {print $2}') || true
|
||||||
|
|
||||||
|
local update_available=0
|
||||||
|
if [[ -n "$pve_container_cand" && "$pve_container_cand" != "none" ]]; then
|
||||||
|
if dpkg --compare-versions "$pve_container_cand" gt "$pve_container_ver" 2>/dev/null; then
|
||||||
|
update_available=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ -n "$lxc_pve_cand" && "$lxc_pve_cand" != "none" ]]; then
|
||||||
|
if dpkg --compare-versions "$lxc_pve_cand" gt "$lxc_pve_ver" 2>/dev/null; then
|
||||||
|
update_available=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$update_available" -eq 1 ]]; then
|
||||||
|
preflight_warn "LXC stack update available (current: pve-container=${pve_container_ver}, lxc-pve=${lxc_pve_ver})"
|
||||||
|
echo -e " ${TAB}${INFO} An upgrade will be offered during container creation if needed"
|
||||||
|
else
|
||||||
|
preflight_pass "LXC stack is up to date (pve-container=${pve_container_ver})"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_container_id()
|
||||||
|
#
|
||||||
|
# - Verifies that container IDs can be allocated
|
||||||
|
# - Uses pvesh /cluster/nextid (cluster-aware)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_container_id() {
|
||||||
|
local nextid
|
||||||
|
nextid=$(pvesh get /cluster/nextid 2>/dev/null) || true
|
||||||
|
|
||||||
|
if [[ -z "$nextid" || ! "$nextid" =~ ^[0-9]+$ ]]; then
|
||||||
|
preflight_fail "Cannot allocate container ID (pvesh /cluster/nextid failed)" 109
|
||||||
|
echo -e " ${TAB}${INFO} Check Proxmox cluster health and datacenter.cfg ID ranges"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
preflight_pass "Container IDs available (next: ${nextid})"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_template_connectivity()
|
||||||
|
#
|
||||||
|
# - Tests connectivity to the Proxmox template download server
|
||||||
|
# - Warns but does not fail (local templates may be available)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_template_connectivity() {
|
||||||
|
local http_code
|
||||||
|
http_code=$(curl -sS -o /dev/null -w "%{http_code}" -m 5 "http://download.proxmox.com/images/system/" 2>/dev/null) || http_code="000"
|
||||||
|
|
||||||
|
if [[ "$http_code" =~ ^2[0-9]{2}$ || "$http_code" =~ ^3[0-9]{2}$ ]]; then
|
||||||
|
preflight_pass "Template server reachable (download.proxmox.com)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local local_count=0
|
||||||
|
while read -r storage_name _; do
|
||||||
|
[[ -z "$storage_name" ]] && continue
|
||||||
|
local count
|
||||||
|
count=$(pveam list "$storage_name" 2>/dev/null | awk 'NR>1' | wc -l)
|
||||||
|
local_count=$((local_count + count))
|
||||||
|
done < <(pvesm status -content vztmpl 2>/dev/null | awk 'NR>1 {print $1}')
|
||||||
|
|
||||||
|
if [[ "$local_count" -gt 0 ]]; then
|
||||||
|
preflight_warn "Template server unreachable, but ${local_count} local template(s) available"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
preflight_fail "Template server unreachable and no local templates available" 222
|
||||||
|
echo -e " ${TAB}${INFO} Check internet connectivity or manually upload templates"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_template_available()
|
||||||
|
#
|
||||||
|
# - Validates that a template exists for the configured var_os/var_version
|
||||||
|
# - Checks both local templates and the online pveam catalog
|
||||||
|
# - Fails if no matching template can be found anywhere
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_template_available() {
|
||||||
|
local os="${var_os:-}"
|
||||||
|
local version="${var_version:-}"
|
||||||
|
|
||||||
|
# Skip if os/version not set (e.g. Alpine scripts set them differently)
|
||||||
|
if [[ -z "$os" || -z "$version" ]]; then
|
||||||
|
preflight_pass "Template check skipped (OS/version not configured yet)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local search_pattern="${os}-${version}"
|
||||||
|
|
||||||
|
# Check local templates first
|
||||||
|
local local_match=0
|
||||||
|
while read -r storage_name _; do
|
||||||
|
[[ -z "$storage_name" ]] && continue
|
||||||
|
if pveam list "$storage_name" 2>/dev/null | awk '{print $1}' | grep -qE "^${storage_name}:vztmpl/${search_pattern}"; then
|
||||||
|
local_match=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done < <(pvesm status -content vztmpl 2>/dev/null | awk 'NR>1 {print $1}')
|
||||||
|
|
||||||
|
if [[ "$local_match" -eq 1 ]]; then
|
||||||
|
preflight_pass "Template available locally for ${os} ${version}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check online catalog
|
||||||
|
local online_match=0
|
||||||
|
if pveam available -section system 2>/dev/null | awk '{print $2}' | grep -qE "^${search_pattern}[.-]"; then
|
||||||
|
online_match=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$online_match" -eq 1 ]]; then
|
||||||
|
preflight_pass "Template available online for ${os} ${version}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Gather available versions for the hint
|
||||||
|
local available_versions
|
||||||
|
available_versions=$(
|
||||||
|
pveam available -section system 2>/dev/null |
|
||||||
|
awk '{print $2}' |
|
||||||
|
grep -oE "^${os}-[0-9]+(\.[0-9]+)?" |
|
||||||
|
sed "s/^${os}-//" |
|
||||||
|
sort -uV 2>/dev/null | tr '\n' ', ' | sed 's/,$//' | sed 's/,/, /g'
|
||||||
|
)
|
||||||
|
|
||||||
|
preflight_fail "No template found for ${os} ${version}" 225
|
||||||
|
if [[ -n "$available_versions" ]]; then
|
||||||
|
echo -e " ${TAB}${INFO} Available ${os} versions: ${GN}${available_versions}${CL}"
|
||||||
|
fi
|
||||||
|
echo -e " ${TAB}${INFO} Check var_version in your CT script or use an available version"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# preflight_package_repos()
|
||||||
|
#
|
||||||
|
# - Tests connectivity to OS package repositories from the host
|
||||||
|
# - Selects repos based on var_os (debian, ubuntu, alpine)
|
||||||
|
# - Uses HTTP HEAD requests to verify the repo index is reachable
|
||||||
|
# - Warning only (user may use local mirrors, apt-cacher-ng, etc.)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
preflight_package_repos() {
|
||||||
|
local os="${var_os:-debian}"
|
||||||
|
local version="${var_version:-}"
|
||||||
|
local -a repo_urls=()
|
||||||
|
local repo_label=""
|
||||||
|
|
||||||
|
case "$os" in
|
||||||
|
debian)
|
||||||
|
repo_label="Debian"
|
||||||
|
repo_urls=(
|
||||||
|
"http://deb.debian.org/debian/dists/${version:-bookworm}/Release"
|
||||||
|
"http://security.debian.org/debian-security/dists/${version:-bookworm}-security/Release"
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
ubuntu)
|
||||||
|
repo_label="Ubuntu"
|
||||||
|
repo_urls=(
|
||||||
|
"http://archive.ubuntu.com/ubuntu/dists/${version:-jammy}/Release"
|
||||||
|
"http://security.ubuntu.com/ubuntu/dists/${version:-jammy}-security/Release"
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
alpine)
|
||||||
|
repo_label="Alpine"
|
||||||
|
# Alpine versions use x.y format (e.g. 3.20)
|
||||||
|
local alpine_branch="v${version:-3.20}"
|
||||||
|
repo_urls=(
|
||||||
|
"https://dl-cdn.alpinelinux.org/alpine/${alpine_branch}/main/x86_64/APKINDEX.tar.gz"
|
||||||
|
)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
preflight_pass "Package repo check skipped (OS: ${os})"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
local all_ok=true
|
||||||
|
local failed_urls=()
|
||||||
|
|
||||||
|
for url in "${repo_urls[@]}"; do
|
||||||
|
local http_code
|
||||||
|
http_code=$(curl -sS -o /dev/null -w "%{http_code}" -m 8 --head "$url" 2>/dev/null) || http_code="000"
|
||||||
|
|
||||||
|
# Accept 200, 3xx redirects, and 405 (HEAD not allowed but server is up)
|
||||||
|
if [[ ! "$http_code" =~ ^(2[0-9]{2}|3[0-9]{2}|405)$ ]]; then
|
||||||
|
all_ok=false
|
||||||
|
# Extract hostname for readable output
|
||||||
|
local host
|
||||||
|
host=$(echo "$url" | sed -E 's|https?://([^/]+).*|\1|')
|
||||||
|
failed_urls+=("${host} (HTTP ${http_code})")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$all_ok" == true ]]; then
|
||||||
|
preflight_pass "${repo_label} package repositories reachable"
|
||||||
|
else
|
||||||
|
local fail_summary
|
||||||
|
fail_summary=$(printf '%s, ' "${failed_urls[@]}")
|
||||||
|
fail_summary="${fail_summary%, }"
|
||||||
|
preflight_warn "${repo_label} package repository not fully reachable: ${fail_summary}"
|
||||||
|
echo -e " ${TAB}${INFO} Container may fail during ${GN}apt-get update${CL} / ${GN}apk update${CL}"
|
||||||
|
echo -e " ${TAB}${INFO} Check firewall rules, proxy settings, or DNS from this host"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# run_preflight()
|
||||||
|
#
|
||||||
|
# - Executes all preflight checks and collects results
|
||||||
|
# - Displays a summary with pass/fail/warn counts
|
||||||
|
# - On failure: reports to telemetry with "aborted" status and exits cleanly
|
||||||
|
# - On success: brief pause (2s) then returns (caller shows next screen)
|
||||||
|
# - Called from install_script() after header_info()
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
run_preflight() {
|
||||||
|
# Reset counters
|
||||||
|
PREFLIGHT_PASSED=0
|
||||||
|
PREFLIGHT_FAILED=0
|
||||||
|
PREFLIGHT_WARNINGS=0
|
||||||
|
PREFLIGHT_FAILURES=()
|
||||||
|
PREFLIGHT_EXIT_CODE=0
|
||||||
|
|
||||||
|
echo -e "${INFO}${BOLD}${DGN} Running pre-flight checks...${CL}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- Kernel checks ---
|
||||||
|
preflight_maxkeys
|
||||||
|
|
||||||
|
# --- Storage checks ---
|
||||||
|
preflight_storage_rootdir
|
||||||
|
preflight_storage_vztmpl
|
||||||
|
preflight_storage_space
|
||||||
|
|
||||||
|
# --- Network checks ---
|
||||||
|
preflight_network_bridge
|
||||||
|
preflight_dns_resolution
|
||||||
|
|
||||||
|
# --- Repository checks ---
|
||||||
|
preflight_repo_access
|
||||||
|
|
||||||
|
# --- Proxmox/Cluster checks ---
|
||||||
|
preflight_cluster_quorum
|
||||||
|
preflight_lxc_stack
|
||||||
|
preflight_container_id
|
||||||
|
|
||||||
|
# --- Template availability ---
|
||||||
|
preflight_template_connectivity
|
||||||
|
preflight_template_available
|
||||||
|
|
||||||
|
# --- Package repository checks ---
|
||||||
|
preflight_package_repos
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- Summary ---
|
||||||
|
if [[ "$PREFLIGHT_FAILED" -gt 0 ]]; then
|
||||||
|
echo -e "${CROSS}${BOLD}${RD} Pre-flight failed: ${PREFLIGHT_FAILED} error(s), ${PREFLIGHT_WARNINGS} warning(s), ${PREFLIGHT_PASSED} passed${CL}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${INFO}${BOLD}${DGN} Failure details:${CL}"
|
||||||
|
for failure in "${PREFLIGHT_FAILURES[@]}"; do
|
||||||
|
local code="${failure%%|*}"
|
||||||
|
local msg="${failure#*|}"
|
||||||
|
echo -e " ${CROSS} [Exit ${code}] ${msg}"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo -e "${INFO} Please resolve the above issues before creating a container."
|
||||||
|
echo -e "${INFO} Documentation: ${BL}https://community-scripts.github.io/ProxmoxVE/${CL}"
|
||||||
|
|
||||||
|
# Report to telemetry (if consent was given)
|
||||||
|
post_preflight_to_api
|
||||||
|
|
||||||
|
exit "$PREFLIGHT_EXIT_CODE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Success — brief pause so user can see results, then clear for next screen
|
||||||
|
if [[ "$PREFLIGHT_WARNINGS" -gt 0 ]]; then
|
||||||
|
echo -e "${CM}${BOLD}${GN} Pre-flight passed with ${PREFLIGHT_WARNINGS} warning(s) (${PREFLIGHT_PASSED} checks passed)${CL}"
|
||||||
|
else
|
||||||
|
echo -e "${CM}${BOLD}${GN} All pre-flight checks passed (${PREFLIGHT_PASSED}/${PREFLIGHT_PASSED})${CL}"
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
}
|
}
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
@@ -1851,7 +2417,7 @@ advanced_settings() {
|
|||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════
|
||||||
# STEP 2: Root Password
|
# STEP 2: Root Password
|
||||||
# ════════════════════════════════════════════════════════════════════════<EFBFBD><EFBFBD><EFBFBD>══
|
# ═════════════════════════════<EFBFBD><EFBFBD><EFBFBD>══════════════════════════════════════════<EFBFBD><EFBFBD><EFBFBD>══
|
||||||
2)
|
2)
|
||||||
if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
|
if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
|
||||||
--title "ROOT PASSWORD" \
|
--title "ROOT PASSWORD" \
|
||||||
@@ -2921,7 +3487,7 @@ echo_default() {
|
|||||||
# install_script()
|
# install_script()
|
||||||
#
|
#
|
||||||
# - Main entrypoint for installation mode
|
# - Main entrypoint for installation mode
|
||||||
# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check)
|
# - Runs safety checks (pve_check, root_check, diagnostics_check, run_preflight)
|
||||||
# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit)
|
# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit)
|
||||||
# - Applies chosen settings and triggers container build
|
# - Applies chosen settings and triggers container build
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@@ -2931,7 +3497,6 @@ install_script() {
|
|||||||
root_check
|
root_check
|
||||||
arch_check
|
arch_check
|
||||||
ssh_check
|
ssh_check
|
||||||
maxkeys_check
|
|
||||||
diagnostics_check
|
diagnostics_check
|
||||||
|
|
||||||
if systemctl is-active -q ping-instances.service; then
|
if systemctl is-active -q ping-instances.service; then
|
||||||
@@ -2951,8 +3516,9 @@ install_script() {
|
|||||||
fi
|
fi
|
||||||
[[ "${timezone:-}" == Etc/* ]] && timezone="host" # pct doesn't accept Etc/* zones
|
[[ "${timezone:-}" == Etc/* ]] && timezone="host" # pct doesn't accept Etc/* zones
|
||||||
|
|
||||||
# Show APP Header
|
# Show APP Header + run preflight checks
|
||||||
header_info
|
header_info
|
||||||
|
run_preflight
|
||||||
|
|
||||||
# --- Support CLI argument as direct preset (default, advanced, …) ---
|
# --- Support CLI argument as direct preset (default, advanced, …) ---
|
||||||
CHOICE="${mode:-${1:-}}"
|
CHOICE="${mode:-${1:-}}"
|
||||||
@@ -4222,7 +4788,6 @@ EOF'
|
|||||||
local is_network_issue=false
|
local is_network_issue=false
|
||||||
local is_apt_issue=false
|
local is_apt_issue=false
|
||||||
local is_cmd_not_found=false
|
local is_cmd_not_found=false
|
||||||
local is_disk_full=false
|
|
||||||
local error_explanation=""
|
local error_explanation=""
|
||||||
if declare -f explain_exit_code >/dev/null 2>&1; then
|
if declare -f explain_exit_code >/dev/null 2>&1; then
|
||||||
error_explanation="$(explain_exit_code "$install_exit_code")"
|
error_explanation="$(explain_exit_code "$install_exit_code")"
|
||||||
@@ -4243,14 +4808,6 @@ EOF'
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Disk full / ENOSPC detection: errno -28 (ENOSPC), exit 228 (custom handler), exit 23 (curl write error)
|
|
||||||
if [[ $install_exit_code -eq 228 || $install_exit_code -eq 23 ]]; then
|
|
||||||
is_disk_full=true
|
|
||||||
fi
|
|
||||||
if [[ -f "$combined_log" ]] && grep -qiE 'ENOSPC|no space left on device|No space left on device|Disk quota exceeded|errno -28' "$combined_log"; then
|
|
||||||
is_disk_full=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Command not found detection
|
# Command not found detection
|
||||||
if [[ $install_exit_code -eq 127 ]]; then
|
if [[ $install_exit_code -eq 127 ]]; then
|
||||||
is_cmd_not_found=true
|
is_cmd_not_found=true
|
||||||
@@ -4287,9 +4844,6 @@ EOF'
|
|||||||
if grep -qiE ': command not found|No such file or directory.*/s?bin/' "$combined_log"; then
|
if grep -qiE ': command not found|No such file or directory.*/s?bin/' "$combined_log"; then
|
||||||
is_cmd_not_found=true
|
is_cmd_not_found=true
|
||||||
fi
|
fi
|
||||||
if grep -qiE 'ENOSPC|no space left on device|Disk quota exceeded|errno -28' "$combined_log"; then
|
|
||||||
is_disk_full=true
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Show error explanation if available
|
# Show error explanation if available
|
||||||
@@ -4311,12 +4865,6 @@ EOF'
|
|||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$is_disk_full" == true ]]; then
|
|
||||||
echo -e "${TAB}${INFO} The container ran out of disk space during installation (${GN}ENOSPC${CL})."
|
|
||||||
echo -e "${TAB}${INFO} Current disk size: ${GN}${DISK_SIZE} GB${CL}. A rebuild with doubled disk may resolve this."
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$is_cmd_not_found" == true ]]; then
|
if [[ "$is_cmd_not_found" == true ]]; then
|
||||||
local missing_cmd=""
|
local missing_cmd=""
|
||||||
if [[ -f "$combined_log" ]]; then
|
if [[ -f "$combined_log" ]]; then
|
||||||
@@ -4336,7 +4884,7 @@ EOF'
|
|||||||
echo -e " ${GN}3)${CL} Retry with verbose mode (full rebuild)"
|
echo -e " ${GN}3)${CL} Retry with verbose mode (full rebuild)"
|
||||||
|
|
||||||
local next_option=4
|
local next_option=4
|
||||||
local APT_OPTION="" OOM_OPTION="" DNS_OPTION="" DISK_OPTION=""
|
local APT_OPTION="" OOM_OPTION="" DNS_OPTION=""
|
||||||
|
|
||||||
if [[ "$is_apt_issue" == true ]]; then
|
if [[ "$is_apt_issue" == true ]]; then
|
||||||
if [[ "$var_os" == "alpine" ]]; then
|
if [[ "$var_os" == "alpine" ]]; then
|
||||||
@@ -4361,18 +4909,6 @@ EOF'
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$is_disk_full" == true ]]; then
|
|
||||||
local disk_recovery_attempt="${DISK_RECOVERY_ATTEMPT:-0}"
|
|
||||||
if [[ $disk_recovery_attempt -lt 2 ]]; then
|
|
||||||
local new_disk=$((DISK_SIZE * 2))
|
|
||||||
echo -e " ${GN}${next_option})${CL} Retry with more disk space (Disk: ${DISK_SIZE}→${new_disk} GB)"
|
|
||||||
DISK_OPTION=$next_option
|
|
||||||
next_option=$((next_option + 1))
|
|
||||||
else
|
|
||||||
echo -e " ${DGN}-)${CL} ${DGN}Disk resize retry exhausted (already retried ${disk_recovery_attempt}x)${CL}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$is_network_issue" == true ]]; then
|
if [[ "$is_network_issue" == true ]]; then
|
||||||
echo -e " ${GN}${next_option})${CL} Retry with DNS override in LXC (8.8.8.8 / 1.1.1.1)"
|
echo -e " ${GN}${next_option})${CL} Retry with DNS override in LXC (8.8.8.8 / 1.1.1.1)"
|
||||||
DNS_OPTION=$next_option
|
DNS_OPTION=$next_option
|
||||||
@@ -4533,35 +5069,6 @@ EOF'
|
|||||||
return $?
|
return $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "${DISK_OPTION}" && "${response}" == "${DISK_OPTION}" ]]; then
|
|
||||||
# Retry with doubled disk size
|
|
||||||
handled=true
|
|
||||||
echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID} for rebuild with more disk space...${CL}"
|
|
||||||
pct stop "$CTID" &>/dev/null || true
|
|
||||||
pct destroy "$CTID" &>/dev/null || true
|
|
||||||
echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}"
|
|
||||||
echo ""
|
|
||||||
local old_ctid="$CTID"
|
|
||||||
local old_disk="$DISK_SIZE"
|
|
||||||
export CTID=$(get_valid_container_id "$CTID")
|
|
||||||
export DISK_SIZE=$((DISK_SIZE * 2))
|
|
||||||
export var_disk="$DISK_SIZE"
|
|
||||||
export VERBOSE="yes"
|
|
||||||
export var_verbose="yes"
|
|
||||||
export DISK_RECOVERY_ATTEMPT=$((${DISK_RECOVERY_ATTEMPT:-0} + 1))
|
|
||||||
|
|
||||||
echo -e "${YW}Rebuilding with increased disk space (attempt ${DISK_RECOVERY_ATTEMPT}/2):${CL}"
|
|
||||||
echo -e " Container ID: ${old_ctid} → ${CTID}"
|
|
||||||
echo -e " Disk: ${old_disk} → ${GN}${DISK_SIZE}${CL} GB (x2)"
|
|
||||||
echo -e " RAM: ${RAM_SIZE} MiB | CPU: ${CORE_COUNT} cores"
|
|
||||||
echo -e " Network: ${NET:-dhcp} | Bridge: ${BRG:-vmbr0}"
|
|
||||||
echo -e " Verbose: ${GN}enabled${CL}"
|
|
||||||
echo ""
|
|
||||||
msg_info "Restarting installation..."
|
|
||||||
build_container
|
|
||||||
return $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "${DNS_OPTION}" && "${response}" == "${DNS_OPTION}" ]]; then
|
if [[ -n "${DNS_OPTION}" && "${response}" == "${DNS_OPTION}" ]]; then
|
||||||
# Retry with DNS override in LXC
|
# Retry with DNS override in LXC
|
||||||
handled=true
|
handled=true
|
||||||
|
|||||||
@@ -105,15 +105,7 @@ function check_disk_space() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Use disk-backed temp directory to avoid tmpfs/RAM size limits in /tmp
|
TEMP_DIR=$(mktemp -d)
|
||||||
if [ -d "/var/tmp" ] && check_disk_space "/var/tmp" 20; then
|
|
||||||
TEMP_DIR=$(mktemp -d /var/tmp/opnsense-vm.XXXXXX)
|
|
||||||
elif [ -d "/tmp" ] && check_disk_space "/tmp" 20; then
|
|
||||||
TEMP_DIR=$(mktemp -d)
|
|
||||||
else
|
|
||||||
# Fallback: try /var/tmp anyway, disk space check will catch it later
|
|
||||||
TEMP_DIR=$(mktemp -d /var/tmp/opnsense-vm.XXXXXX)
|
|
||||||
fi
|
|
||||||
pushd $TEMP_DIR >/dev/null
|
pushd $TEMP_DIR >/dev/null
|
||||||
function send_line_to_vm() {
|
function send_line_to_vm() {
|
||||||
echo -e "${DGN}Sending line: ${YW}$1${CL}"
|
echo -e "${DGN}Sending line: ${YW}$1${CL}"
|
||||||
@@ -268,10 +260,6 @@ function exit-script() {
|
|||||||
exit
|
exit
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_available_bridges() {
|
|
||||||
ip -o link show type bridge 2>/dev/null | awk -F': ' '{print $2}' | sort
|
|
||||||
}
|
|
||||||
|
|
||||||
function default_settings() {
|
function default_settings() {
|
||||||
VMID=$(get_valid_nextid)
|
VMID=$(get_valid_nextid)
|
||||||
FORMAT=",efitype=4m"
|
FORMAT=",efitype=4m"
|
||||||
@@ -291,17 +279,11 @@ function default_settings() {
|
|||||||
VLAN=""
|
VLAN=""
|
||||||
MAC=$GEN_MAC
|
MAC=$GEN_MAC
|
||||||
WAN_MAC=$GEN_MAC_LAN
|
WAN_MAC=$GEN_MAC_LAN
|
||||||
WAN_BRG=""
|
WAN_BRG="vmbr1"
|
||||||
MTU=""
|
MTU=""
|
||||||
START_VM="yes"
|
START_VM="yes"
|
||||||
METHOD="default"
|
METHOD="default"
|
||||||
|
|
||||||
# Detect available bridges
|
|
||||||
local AVAILABLE_BRIDGES
|
|
||||||
AVAILABLE_BRIDGES=$(get_available_bridges)
|
|
||||||
local BRIDGE_COUNT
|
|
||||||
BRIDGE_COUNT=$(echo "$AVAILABLE_BRIDGES" | wc -l)
|
|
||||||
|
|
||||||
echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}"
|
echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}"
|
||||||
echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}"
|
echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}"
|
||||||
echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}"
|
echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}"
|
||||||
@@ -315,34 +297,26 @@ function default_settings() {
|
|||||||
echo -e "${DGN}Using LAN VLAN: ${BGN}Default${CL}"
|
echo -e "${DGN}Using LAN VLAN: ${BGN}Default${CL}"
|
||||||
echo -e "${DGN}Using LAN MAC Address: ${BGN}${MAC}${CL}"
|
echo -e "${DGN}Using LAN MAC Address: ${BGN}${MAC}${CL}"
|
||||||
|
|
||||||
# Determine available network modes based on bridge count
|
if NETWORK_MODE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NETWORK CONFIGURATION" --radiolist --cancel-button Exit-Script \
|
||||||
local DEFAULT_WAN_BRG
|
"Choose network setup mode for OPNsense:\n" 14 70 2 \
|
||||||
DEFAULT_WAN_BRG=$(echo "$AVAILABLE_BRIDGES" | grep -v "^${BRG}$" | head -n1)
|
"dual" "Dual Interface (Traditional Firewall/Router)" ON \
|
||||||
|
"single" "Single Interface (Proxy/VPN/IDS Server)" OFF \
|
||||||
if [ "$BRIDGE_COUNT" -ge 2 ]; then
|
3>&1 1>&2 2>&3); then
|
||||||
# Multiple bridges available - offer dual or single mode
|
if [ "$NETWORK_MODE" = "dual" ]; then
|
||||||
if NETWORK_MODE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NETWORK CONFIGURATION" --radiolist --cancel-button Exit-Script \
|
echo -e "${DGN}Network Mode: ${BGN}Dual Interface (Firewall)${CL}"
|
||||||
"Choose network setup mode for OPNsense:\n" 14 70 2 \
|
echo -e "${DGN}Using WAN MAC Address: ${BGN}${WAN_MAC}${CL}"
|
||||||
"dual" "Dual Interface (Firewall/Router) - uses ${DEFAULT_WAN_BRG}" ON \
|
if ! ip link show "${WAN_BRG}" &>/dev/null; then
|
||||||
"single" "Single Interface (Proxy/VPN/IDS Server)" OFF \
|
msg_error "Bridge '${WAN_BRG}' does not exist"
|
||||||
3>&1 1>&2 2>&3); then
|
exit
|
||||||
if [ "$NETWORK_MODE" = "dual" ]; then
|
|
||||||
WAN_BRG="$DEFAULT_WAN_BRG"
|
|
||||||
echo -e "${DGN}Network Mode: ${BGN}Dual Interface (Firewall)${CL}"
|
|
||||||
echo -e "${DGN}Using WAN Bridge: ${BGN}${WAN_BRG}${CL}"
|
|
||||||
echo -e "${DGN}Using WAN MAC Address: ${BGN}${WAN_MAC}${CL}"
|
|
||||||
else
|
else
|
||||||
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
|
echo -e "${DGN}Using WAN Bridge: ${BGN}${WAN_BRG}${CL}"
|
||||||
WAN_BRG=""
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
exit-script
|
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
|
||||||
|
WAN_BRG=""
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Only one bridge available - single interface mode only
|
exit-script
|
||||||
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
|
|
||||||
echo -e "${YW} (Only one bridge detected, dual interface requires a second bridge)${CL}"
|
|
||||||
WAN_BRG=""
|
|
||||||
fi
|
fi
|
||||||
echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}"
|
echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}"
|
||||||
echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}"
|
echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}"
|
||||||
@@ -496,29 +470,13 @@ function advanced_settings() {
|
|||||||
exit-script
|
exit-script
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Build WAN bridge selection from available bridges (excluding LAN bridge)
|
if WAN_BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN Bridge" 8 58 vmbr1 --title "WAN BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
||||||
local WAN_BRIDGES
|
if [ -z $WAN_BRG ]; then
|
||||||
WAN_BRIDGES=$(get_available_bridges | grep -v "^${BRG}$")
|
WAN_BRG="vmbr1"
|
||||||
if [ -z "$WAN_BRIDGES" ]; then
|
|
||||||
msg_error "No additional bridge available for WAN. Only '${BRG}' exists."
|
|
||||||
msg_error "Create a second bridge (e.g. vmbr1) in Proxmox network config first."
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
local WAN_MENU=()
|
|
||||||
local first=true
|
|
||||||
while IFS= read -r brg; do
|
|
||||||
if $first; then
|
|
||||||
WAN_MENU+=("$brg" "" "ON")
|
|
||||||
first=false
|
|
||||||
else
|
|
||||||
WAN_MENU+=("$brg" "" "OFF")
|
|
||||||
fi
|
fi
|
||||||
done <<<"$WAN_BRIDGES"
|
if ! ip link show "${WAN_BRG}" &>/dev/null; then
|
||||||
|
msg_error "WAN Bridge '${WAN_BRG}' does not exist"
|
||||||
if WAN_BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "WAN BRIDGE" --radiolist "Select WAN Bridge" 14 58 6 \
|
exit
|
||||||
"${WAN_MENU[@]}" 3>&1 1>&2 2>&3); then
|
|
||||||
if [ -z "$WAN_BRG" ]; then
|
|
||||||
WAN_BRG=$(echo "$WAN_BRIDGES" | head -n1)
|
|
||||||
fi
|
fi
|
||||||
echo -e "${DGN}Using WAN Bridge: ${BGN}$WAN_BRG${CL}"
|
echo -e "${DGN}Using WAN Bridge: ${BGN}$WAN_BRG${CL}"
|
||||||
else
|
else
|
||||||
|
|||||||
Reference in New Issue
Block a user