mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-03-04 01:45:55 +01:00
Compare commits
15 Commits
preflight_
...
2026-03-03
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7044b6e017 | ||
|
|
3ff140101d | ||
|
|
c4b6528bf0 | ||
|
|
7a85e33791 | ||
|
|
bbfffb97a5 | ||
|
|
4306b4018d | ||
|
|
3722533431 | ||
|
|
f773af17b2 | ||
|
|
56b4490554 | ||
|
|
b45842d76a | ||
|
|
ea279ace89 | ||
|
|
034061e744 | ||
|
|
dd07ba4453 | ||
|
|
380aa4bc0f | ||
|
|
aca721e9ee |
23
CHANGELOG.md
23
CHANGELOG.md
@@ -410,16 +410,39 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
||||
|
||||
</details>
|
||||
|
||||
## 2026-03-04
|
||||
|
||||
## 2026-03-03
|
||||
|
||||
### 🆕 New Scripts
|
||||
|
||||
- Tinyauth: v5 Support & add Debian Version [@MickLesk](https://github.com/MickLesk) ([#12501](https://github.com/community-scripts/ProxmoxVE/pull/12501))
|
||||
|
||||
### 🚀 Updated Scripts
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
- cross-seed: install build-essential to resolve missing `make` error [@Copilot](https://github.com/Copilot) ([#12522](https://github.com/community-scripts/ProxmoxVE/pull/12522))
|
||||
- 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
|
||||
|
||||
- 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))
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- Fix config_path casing in reactive-resume.json [@ScubyG](https://github.com/ScubyG) ([#12525](https://github.com/community-scripts/ProxmoxVE/pull/12525))
|
||||
|
||||
### 🌐 Website
|
||||
|
||||
- #### 🐞 Bug Fixes
|
||||
|
||||
@@ -25,6 +25,7 @@ function update_script() {
|
||||
check_container_resources
|
||||
|
||||
NODE_VERSION="24" setup_nodejs
|
||||
ensure_dependencies build-essential
|
||||
|
||||
if command -v cross-seed &>/dev/null; then
|
||||
current_version=$(cross-seed --version)
|
||||
|
||||
@@ -9,7 +9,7 @@ APP="MeshCentral"
|
||||
var_tags="${var_tags:-remote-management}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-512}"
|
||||
var_disk="${var_disk:-2}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generated": "2026-03-03T06:17:56Z",
|
||||
"generated": "2026-03-04T00:20:33Z",
|
||||
"versions": [
|
||||
{
|
||||
"slug": "2fauth",
|
||||
@@ -242,9 +242,9 @@
|
||||
{
|
||||
"slug": "cosmos",
|
||||
"repo": "azukaar/Cosmos-Server",
|
||||
"version": "v0.21.6",
|
||||
"version": "v0.21.7",
|
||||
"pinned": false,
|
||||
"date": "2026-02-28T22:00:49Z"
|
||||
"date": "2026-03-03T18:15:29Z"
|
||||
},
|
||||
{
|
||||
"slug": "cronicle",
|
||||
@@ -298,9 +298,9 @@
|
||||
{
|
||||
"slug": "docmost",
|
||||
"repo": "docmost/docmost",
|
||||
"version": "v0.25.3",
|
||||
"version": "v0.70.0",
|
||||
"pinned": false,
|
||||
"date": "2026-02-10T02:58:23Z"
|
||||
"date": "2026-03-03T20:33:08Z"
|
||||
},
|
||||
{
|
||||
"slug": "domain-locker",
|
||||
@@ -438,9 +438,9 @@
|
||||
{
|
||||
"slug": "ghostfolio",
|
||||
"repo": "ghostfolio/ghostfolio",
|
||||
"version": "2.245.0",
|
||||
"version": "2.246.0",
|
||||
"pinned": false,
|
||||
"date": "2026-03-01T09:09:57Z"
|
||||
"date": "2026-03-03T19:37:20Z"
|
||||
},
|
||||
{
|
||||
"slug": "gitea",
|
||||
@@ -557,9 +557,9 @@
|
||||
{
|
||||
"slug": "homebox",
|
||||
"repo": "sysadminsmedia/homebox",
|
||||
"version": "v0.23.1",
|
||||
"version": "v0.24.0",
|
||||
"pinned": false,
|
||||
"date": "2026-02-01T22:53:32Z"
|
||||
"date": "2026-03-03T16:09:55Z"
|
||||
},
|
||||
{
|
||||
"slug": "homepage",
|
||||
@@ -669,9 +669,9 @@
|
||||
{
|
||||
"slug": "kima-hub",
|
||||
"repo": "Chevron7Locked/kima-hub",
|
||||
"version": "v1.6.0",
|
||||
"version": "v1.6.1",
|
||||
"pinned": false,
|
||||
"date": "2026-03-02T05:43:31Z"
|
||||
"date": "2026-03-03T16:13:53Z"
|
||||
},
|
||||
{
|
||||
"slug": "kimai",
|
||||
@@ -753,9 +753,9 @@
|
||||
{
|
||||
"slug": "libretranslate",
|
||||
"repo": "LibreTranslate/LibreTranslate",
|
||||
"version": "v1.9.4",
|
||||
"version": "v1.9.5",
|
||||
"pinned": false,
|
||||
"date": "2026-02-24T17:06:05Z"
|
||||
"date": "2026-03-03T18:25:04Z"
|
||||
},
|
||||
{
|
||||
"slug": "lidarr",
|
||||
@@ -872,9 +872,9 @@
|
||||
{
|
||||
"slug": "metube",
|
||||
"repo": "alexta69/metube",
|
||||
"version": "2026.03.02",
|
||||
"version": "2026.03.03",
|
||||
"pinned": false,
|
||||
"date": "2026-03-02T19:19:10Z"
|
||||
"date": "2026-03-03T19:15:55Z"
|
||||
},
|
||||
{
|
||||
"slug": "miniflux",
|
||||
@@ -921,9 +921,9 @@
|
||||
{
|
||||
"slug": "netbox",
|
||||
"repo": "netbox-community/netbox",
|
||||
"version": "v4.5.3",
|
||||
"version": "v4.5.4",
|
||||
"pinned": false,
|
||||
"date": "2026-02-17T15:39:18Z"
|
||||
"date": "2026-03-03T20:32:16Z"
|
||||
},
|
||||
{
|
||||
"slug": "nextcloud-exporter",
|
||||
@@ -942,9 +942,9 @@
|
||||
{
|
||||
"slug": "nightscout",
|
||||
"repo": "nightscout/cgm-remote-monitor",
|
||||
"version": "v15.0.5",
|
||||
"version": "v15.0.6",
|
||||
"pinned": false,
|
||||
"date": "2026-03-01T21:22:37Z"
|
||||
"date": "2026-03-03T23:04:35Z"
|
||||
},
|
||||
{
|
||||
"slug": "nocodb",
|
||||
@@ -1229,9 +1229,9 @@
|
||||
{
|
||||
"slug": "pulse",
|
||||
"repo": "rcourtman/Pulse",
|
||||
"version": "v5.1.17",
|
||||
"version": "v5.1.18",
|
||||
"pinned": false,
|
||||
"date": "2026-03-02T20:15:31Z"
|
||||
"date": "2026-03-03T22:09:15Z"
|
||||
},
|
||||
{
|
||||
"slug": "pve-scripts-local",
|
||||
@@ -1446,9 +1446,9 @@
|
||||
{
|
||||
"slug": "sonobarr",
|
||||
"repo": "Dodelidoo-Labs/sonobarr",
|
||||
"version": "0.11.0",
|
||||
"version": "0.12.1",
|
||||
"pinned": false,
|
||||
"date": "2026-01-21T19:07:21Z"
|
||||
"date": "2026-03-03T13:43:02Z"
|
||||
},
|
||||
{
|
||||
"slug": "speedtest-tracker",
|
||||
@@ -1467,9 +1467,9 @@
|
||||
{
|
||||
"slug": "sportarr",
|
||||
"repo": "Sportarr/Sportarr",
|
||||
"version": "v4.0.983.1057",
|
||||
"version": "v4.0.984.1059",
|
||||
"pinned": false,
|
||||
"date": "2026-01-26T18:54:50Z"
|
||||
"date": "2026-03-03T23:28:08Z"
|
||||
},
|
||||
{
|
||||
"slug": "stirling-pdf",
|
||||
@@ -1562,6 +1562,13 @@
|
||||
"pinned": false,
|
||||
"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",
|
||||
"repo": "traccar/traccar",
|
||||
@@ -1572,9 +1579,9 @@
|
||||
{
|
||||
"slug": "tracearr",
|
||||
"repo": "connorgallopo/Tracearr",
|
||||
"version": "v1.4.19",
|
||||
"version": "v1.4.21",
|
||||
"pinned": false,
|
||||
"date": "2026-02-28T21:25:47Z"
|
||||
"date": "2026-03-03T18:43:20Z"
|
||||
},
|
||||
{
|
||||
"slug": "tracktor",
|
||||
@@ -1642,9 +1649,9 @@
|
||||
{
|
||||
"slug": "upgopher",
|
||||
"repo": "wanetty/upgopher",
|
||||
"version": "v1.14.0",
|
||||
"version": "v1.15.2",
|
||||
"pinned": false,
|
||||
"date": "2026-02-24T22:43:34Z"
|
||||
"date": "2026-03-03T13:40:45Z"
|
||||
},
|
||||
{
|
||||
"slug": "upsnap",
|
||||
@@ -1733,9 +1740,9 @@
|
||||
{
|
||||
"slug": "wealthfolio",
|
||||
"repo": "afadil/wealthfolio",
|
||||
"version": "v3.0.2",
|
||||
"version": "v3.0.3",
|
||||
"pinned": false,
|
||||
"date": "2026-03-03T05:01:49Z"
|
||||
"date": "2026-03-03T21:47:55Z"
|
||||
},
|
||||
{
|
||||
"slug": "web-check",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"resources": {
|
||||
"cpu": 1,
|
||||
"ram": 512,
|
||||
"hdd": 2,
|
||||
"hdd": 4,
|
||||
"os": "debian",
|
||||
"version": "13"
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"documentation": "https://docs.rxresume.org/",
|
||||
"website": "https://rxresume.org",
|
||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/reactive-resume.webp",
|
||||
"config_path": "/opt/reactive-resume/.env",
|
||||
"config_path": "/opt/Reactive-Resume/.env",
|
||||
"description": "A one-of-a-kind resume builder that keeps your privacy in mind. Completely secure, customizable, portable, open-source and free forever.",
|
||||
"install_methods": [
|
||||
{
|
||||
|
||||
@@ -13,6 +13,10 @@ setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y build-essential
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
NODE_VERSION="24" setup_nodejs
|
||||
|
||||
msg_info "Setup Cross-Seed"
|
||||
|
||||
685
misc/build.func
685
misc/build.func
@@ -100,624 +100,58 @@ fi
|
||||
# ==============================================================================
|
||||
# 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
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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()
|
||||
# maxkeys_check()
|
||||
#
|
||||
# - Reads kernel keyring limits (maxkeys, maxbytes)
|
||||
# - Checks current usage for LXC user (UID 100000)
|
||||
# - Warns if usage is close to limits and suggests sysctl tuning
|
||||
# - https://cleveruptime.com/docs/files/proc-key-users
|
||||
# - https://docs.kernel.org/security/keys/core.html
|
||||
# - Exits if thresholds are exceeded
|
||||
# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html
|
||||
# ------------------------------------------------------------------------------
|
||||
preflight_maxkeys() {
|
||||
local per_user_maxkeys per_user_maxbytes
|
||||
|
||||
maxkeys_check() {
|
||||
# Read kernel parameters
|
||||
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)
|
||||
|
||||
# Exit if kernel parameters are unavailable
|
||||
if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then
|
||||
preflight_fail "Unable to read kernel key parameters" 107
|
||||
echo -e " ${TAB}${INFO} Ensure proper permissions to /proc/sys/kernel/keys/"
|
||||
return 0
|
||||
msg_error "Unable to read kernel key parameters. Ensure proper permissions."
|
||||
exit 107
|
||||
fi
|
||||
|
||||
local used_lxc_keys used_lxc_bytes
|
||||
# Fetch key usage for user ID 100000 (typical for containers)
|
||||
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)
|
||||
|
||||
local threshold_keys=$((per_user_maxkeys - 100))
|
||||
local threshold_bytes=$((per_user_maxbytes - 1000))
|
||||
local new_limit_keys=$((per_user_maxkeys * 2))
|
||||
local new_limit_bytes=$((per_user_maxbytes * 2))
|
||||
# Calculate thresholds and suggested new limits
|
||||
threshold_keys=$((per_user_maxkeys - 100))
|
||||
threshold_bytes=$((per_user_maxbytes - 1000))
|
||||
new_limit_keys=$((per_user_maxkeys * 2))
|
||||
new_limit_bytes=$((per_user_maxbytes * 2))
|
||||
|
||||
local failure=0
|
||||
# Check if key or byte usage is near limits
|
||||
failure=0
|
||||
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
|
||||
fi
|
||||
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
|
||||
fi
|
||||
|
||||
# Provide next steps if issues are detected
|
||||
if [[ "$failure" -eq 1 ]]; then
|
||||
preflight_fail "Kernel key limits near threshold (keys: ${used_lxc_keys}/${per_user_maxkeys}, bytes: ${used_lxc_bytes}/${per_user_maxbytes})" 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
|
||||
msg_error "Kernel key limits exceeded - see suggestions above"
|
||||
exit 108
|
||||
fi
|
||||
|
||||
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
|
||||
# Silent success - only show errors if they exist
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
@@ -2417,7 +1851,7 @@ advanced_settings() {
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════
|
||||
# STEP 2: Root Password
|
||||
# ═════════════════════════════<EFBFBD><EFBFBD><EFBFBD>══════════════════════════════════════════<EFBFBD><EFBFBD><EFBFBD>══
|
||||
# ════════════════════════════════════════════════════════════════════════<EFBFBD><EFBFBD><EFBFBD>══
|
||||
2)
|
||||
if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts [Step $STEP/$MAX_STEP]" \
|
||||
--title "ROOT PASSWORD" \
|
||||
@@ -3487,7 +2921,7 @@ echo_default() {
|
||||
# install_script()
|
||||
#
|
||||
# - Main entrypoint for installation mode
|
||||
# - Runs safety checks (pve_check, root_check, diagnostics_check, run_preflight)
|
||||
# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check)
|
||||
# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit)
|
||||
# - Applies chosen settings and triggers container build
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -3497,6 +2931,7 @@ install_script() {
|
||||
root_check
|
||||
arch_check
|
||||
ssh_check
|
||||
maxkeys_check
|
||||
diagnostics_check
|
||||
|
||||
if systemctl is-active -q ping-instances.service; then
|
||||
@@ -3516,9 +2951,8 @@ install_script() {
|
||||
fi
|
||||
[[ "${timezone:-}" == Etc/* ]] && timezone="host" # pct doesn't accept Etc/* zones
|
||||
|
||||
# Show APP Header + run preflight checks
|
||||
# Show APP Header
|
||||
header_info
|
||||
run_preflight
|
||||
|
||||
# --- Support CLI argument as direct preset (default, advanced, …) ---
|
||||
CHOICE="${mode:-${1:-}}"
|
||||
@@ -4788,6 +4222,7 @@ EOF'
|
||||
local is_network_issue=false
|
||||
local is_apt_issue=false
|
||||
local is_cmd_not_found=false
|
||||
local is_disk_full=false
|
||||
local error_explanation=""
|
||||
if declare -f explain_exit_code >/dev/null 2>&1; then
|
||||
error_explanation="$(explain_exit_code "$install_exit_code")"
|
||||
@@ -4808,6 +4243,14 @@ EOF'
|
||||
;;
|
||||
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
|
||||
if [[ $install_exit_code -eq 127 ]]; then
|
||||
is_cmd_not_found=true
|
||||
@@ -4844,6 +4287,9 @@ EOF'
|
||||
if grep -qiE ': command not found|No such file or directory.*/s?bin/' "$combined_log"; then
|
||||
is_cmd_not_found=true
|
||||
fi
|
||||
if grep -qiE 'ENOSPC|no space left on device|Disk quota exceeded|errno -28' "$combined_log"; then
|
||||
is_disk_full=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Show error explanation if available
|
||||
@@ -4865,6 +4311,12 @@ EOF'
|
||||
echo ""
|
||||
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
|
||||
local missing_cmd=""
|
||||
if [[ -f "$combined_log" ]]; then
|
||||
@@ -4884,7 +4336,7 @@ EOF'
|
||||
echo -e " ${GN}3)${CL} Retry with verbose mode (full rebuild)"
|
||||
|
||||
local next_option=4
|
||||
local APT_OPTION="" OOM_OPTION="" DNS_OPTION=""
|
||||
local APT_OPTION="" OOM_OPTION="" DNS_OPTION="" DISK_OPTION=""
|
||||
|
||||
if [[ "$is_apt_issue" == true ]]; then
|
||||
if [[ "$var_os" == "alpine" ]]; then
|
||||
@@ -4909,6 +4361,18 @@ EOF'
|
||||
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
|
||||
echo -e " ${GN}${next_option})${CL} Retry with DNS override in LXC (8.8.8.8 / 1.1.1.1)"
|
||||
DNS_OPTION=$next_option
|
||||
@@ -5069,6 +4533,35 @@ EOF'
|
||||
return $?
|
||||
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
|
||||
# Retry with DNS override in LXC
|
||||
handled=true
|
||||
|
||||
@@ -105,7 +105,15 @@ function check_disk_space() {
|
||||
return 0
|
||||
}
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
# Use disk-backed temp directory to avoid tmpfs/RAM size limits in /tmp
|
||||
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
|
||||
function send_line_to_vm() {
|
||||
echo -e "${DGN}Sending line: ${YW}$1${CL}"
|
||||
@@ -260,6 +268,10 @@ function exit-script() {
|
||||
exit
|
||||
}
|
||||
|
||||
function get_available_bridges() {
|
||||
ip -o link show type bridge 2>/dev/null | awk -F': ' '{print $2}' | sort
|
||||
}
|
||||
|
||||
function default_settings() {
|
||||
VMID=$(get_valid_nextid)
|
||||
FORMAT=",efitype=4m"
|
||||
@@ -279,11 +291,17 @@ function default_settings() {
|
||||
VLAN=""
|
||||
MAC=$GEN_MAC
|
||||
WAN_MAC=$GEN_MAC_LAN
|
||||
WAN_BRG="vmbr1"
|
||||
WAN_BRG=""
|
||||
MTU=""
|
||||
START_VM="yes"
|
||||
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 Hostname: ${BGN}${HN}${CL}"
|
||||
echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}"
|
||||
@@ -297,26 +315,34 @@ function default_settings() {
|
||||
echo -e "${DGN}Using LAN VLAN: ${BGN}Default${CL}"
|
||||
echo -e "${DGN}Using LAN MAC Address: ${BGN}${MAC}${CL}"
|
||||
|
||||
if NETWORK_MODE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NETWORK CONFIGURATION" --radiolist --cancel-button Exit-Script \
|
||||
"Choose network setup mode for OPNsense:\n" 14 70 2 \
|
||||
"dual" "Dual Interface (Traditional Firewall/Router)" ON \
|
||||
"single" "Single Interface (Proxy/VPN/IDS Server)" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
if [ "$NETWORK_MODE" = "dual" ]; then
|
||||
echo -e "${DGN}Network Mode: ${BGN}Dual Interface (Firewall)${CL}"
|
||||
echo -e "${DGN}Using WAN MAC Address: ${BGN}${WAN_MAC}${CL}"
|
||||
if ! ip link show "${WAN_BRG}" &>/dev/null; then
|
||||
msg_error "Bridge '${WAN_BRG}' does not exist"
|
||||
exit
|
||||
else
|
||||
# Determine available network modes based on bridge count
|
||||
local DEFAULT_WAN_BRG
|
||||
DEFAULT_WAN_BRG=$(echo "$AVAILABLE_BRIDGES" | grep -v "^${BRG}$" | head -n1)
|
||||
|
||||
if [ "$BRIDGE_COUNT" -ge 2 ]; then
|
||||
# Multiple bridges available - offer dual or single mode
|
||||
if NETWORK_MODE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NETWORK CONFIGURATION" --radiolist --cancel-button Exit-Script \
|
||||
"Choose network setup mode for OPNsense:\n" 14 70 2 \
|
||||
"dual" "Dual Interface (Firewall/Router) - uses ${DEFAULT_WAN_BRG}" ON \
|
||||
"single" "Single Interface (Proxy/VPN/IDS Server)" OFF \
|
||||
3>&1 1>&2 2>&3); then
|
||||
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
|
||||
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
|
||||
WAN_BRG=""
|
||||
fi
|
||||
else
|
||||
echo -e "${DGN}Network Mode: ${BGN}Single Interface (Proxy/VPN/IDS)${CL}"
|
||||
WAN_BRG=""
|
||||
exit-script
|
||||
fi
|
||||
else
|
||||
exit-script
|
||||
# Only one bridge available - single interface mode only
|
||||
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
|
||||
echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}"
|
||||
echo -e "${DGN}Start VM when completed: ${BGN}yes${CL}"
|
||||
@@ -470,13 +496,29 @@ function advanced_settings() {
|
||||
exit-script
|
||||
fi
|
||||
|
||||
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
|
||||
if [ -z $WAN_BRG ]; then
|
||||
WAN_BRG="vmbr1"
|
||||
# Build WAN bridge selection from available bridges (excluding LAN bridge)
|
||||
local WAN_BRIDGES
|
||||
WAN_BRIDGES=$(get_available_bridges | grep -v "^${BRG}$")
|
||||
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
|
||||
if ! ip link show "${WAN_BRG}" &>/dev/null; then
|
||||
msg_error "WAN Bridge '${WAN_BRG}' does not exist"
|
||||
exit
|
||||
done <<<"$WAN_BRIDGES"
|
||||
|
||||
if WAN_BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "WAN BRIDGE" --radiolist "Select WAN Bridge" 14 58 6 \
|
||||
"${WAN_MENU[@]}" 3>&1 1>&2 2>&3); then
|
||||
if [ -z "$WAN_BRG" ]; then
|
||||
WAN_BRG=$(echo "$WAN_BRIDGES" | head -n1)
|
||||
fi
|
||||
echo -e "${DGN}Using WAN Bridge: ${BGN}$WAN_BRG${CL}"
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user