mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-02-16 02:03:25 +01:00
Compare commits
14 Commits
fix/teleme
...
copilot/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3f36f2253 | ||
|
|
2f13b4dbc8 | ||
|
|
8ead654ebb | ||
|
|
da915b87f6 | ||
|
|
b46dddbd7d | ||
|
|
b3e3ed5fb3 | ||
|
|
7b767ff58b | ||
|
|
79baf4360e | ||
|
|
773f3f67b8 | ||
|
|
9a95d81f17 | ||
|
|
ed9a6d9d4b | ||
|
|
c6005af29d | ||
|
|
911f533e6a | ||
|
|
cecadf5681 |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -403,12 +403,24 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
|
|||||||
|
|
||||||
## 2026-02-14
|
## 2026-02-14
|
||||||
|
|
||||||
|
### 🚀 Updated Scripts
|
||||||
|
|
||||||
|
- #### ✨ New Features
|
||||||
|
|
||||||
|
- core: overwriteable app version [@CrazyWolf13](https://github.com/CrazyWolf13) ([#11753](https://github.com/community-scripts/ProxmoxVE/pull/11753))
|
||||||
|
|
||||||
### 💾 Core
|
### 💾 Core
|
||||||
|
|
||||||
- #### ✨ New Features
|
- #### ✨ New Features
|
||||||
|
|
||||||
|
- core: validate container IDs cluster-wide across all nodes [@MickLesk](https://github.com/MickLesk) ([#11906](https://github.com/community-scripts/ProxmoxVE/pull/11906))
|
||||||
|
- core: improve error reporting with structured error strings and better categorization + output formatting [@MickLesk](https://github.com/MickLesk) ([#11907](https://github.com/community-scripts/ProxmoxVE/pull/11907))
|
||||||
- core: unified logging system with combined logs [@MickLesk](https://github.com/MickLesk) ([#11761](https://github.com/community-scripts/ProxmoxVE/pull/11761))
|
- core: unified logging system with combined logs [@MickLesk](https://github.com/MickLesk) ([#11761](https://github.com/community-scripts/ProxmoxVE/pull/11761))
|
||||||
|
|
||||||
|
### 🧰 Tools
|
||||||
|
|
||||||
|
- lxc-updater: add patchmon aware [@failure101](https://github.com/failure101) ([#11905](https://github.com/community-scripts/ProxmoxVE/pull/11905))
|
||||||
|
|
||||||
### ❔ Uncategorized
|
### ❔ Uncategorized
|
||||||
|
|
||||||
- Disable UniFi script - APT packages no longer available [@Copilot](https://github.com/Copilot) ([#11898](https://github.com/community-scripts/ProxmoxVE/pull/11898))
|
- Disable UniFi script - APT packages no longer available [@Copilot](https://github.com/Copilot) ([#11898](https://github.com/community-scripts/ProxmoxVE/pull/11898))
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ APP="Ollama"
|
|||||||
var_tags="${var_tags:-ai}"
|
var_tags="${var_tags:-ai}"
|
||||||
var_cpu="${var_cpu:-4}"
|
var_cpu="${var_cpu:-4}"
|
||||||
var_ram="${var_ram:-4096}"
|
var_ram="${var_ram:-4096}"
|
||||||
var_disk="${var_disk:-35}"
|
var_disk="${var_disk:-40}"
|
||||||
var_os="${var_os:-ubuntu}"
|
var_os="${var_os:-ubuntu}"
|
||||||
var_version="${var_version:-24.04}"
|
var_version="${var_version:-24.04}"
|
||||||
var_gpu="${var_gpu:-yes}"
|
var_gpu="${var_gpu:-yes}"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ APP="Open WebUI"
|
|||||||
var_tags="${var_tags:-ai;interface}"
|
var_tags="${var_tags:-ai;interface}"
|
||||||
var_cpu="${var_cpu:-4}"
|
var_cpu="${var_cpu:-4}"
|
||||||
var_ram="${var_ram:-8192}"
|
var_ram="${var_ram:-8192}"
|
||||||
var_disk="${var_disk:-25}"
|
var_disk="${var_disk:-50}"
|
||||||
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-02-14T12:08:41Z",
|
"generated": "2026-02-14T18:07:29Z",
|
||||||
"versions": [
|
"versions": [
|
||||||
{
|
{
|
||||||
"slug": "2fauth",
|
"slug": "2fauth",
|
||||||
@@ -116,9 +116,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "bentopdf",
|
"slug": "bentopdf",
|
||||||
"repo": "alam00000/bentopdf",
|
"repo": "alam00000/bentopdf",
|
||||||
"version": "v2.2.0",
|
"version": "v2.2.1",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-09T07:07:40Z"
|
"date": "2026-02-14T16:33:47Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "beszel",
|
"slug": "beszel",
|
||||||
@@ -354,9 +354,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "firefly",
|
"slug": "firefly",
|
||||||
"repo": "firefly-iii/firefly-iii",
|
"repo": "firefly-iii/firefly-iii",
|
||||||
"version": "v6.4.19",
|
"version": "v6.4.20",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-14T11:55:40Z"
|
"date": "2026-02-14T12:39:02Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "fladder",
|
"slug": "fladder",
|
||||||
@@ -578,9 +578,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "jackett",
|
"slug": "jackett",
|
||||||
"repo": "Jackett/Jackett",
|
"repo": "Jackett/Jackett",
|
||||||
"version": "v0.24.1109",
|
"version": "v0.24.1113",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-14T05:54:26Z"
|
"date": "2026-02-14T17:46:58Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "jellystat",
|
"slug": "jellystat",
|
||||||
@@ -1411,9 +1411,9 @@
|
|||||||
{
|
{
|
||||||
"slug": "tandoor",
|
"slug": "tandoor",
|
||||||
"repo": "TandoorRecipes/recipes",
|
"repo": "TandoorRecipes/recipes",
|
||||||
"version": "2.5.1",
|
"version": "2.5.3",
|
||||||
"pinned": false,
|
"pinned": false,
|
||||||
"date": "2026-02-13T15:57:27Z"
|
"date": "2026-02-14T12:42:14Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"slug": "tasmoadmin",
|
"slug": "tasmoadmin",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"cpu": 4,
|
"cpu": 4,
|
||||||
"ram": 4096,
|
"ram": 4096,
|
||||||
"hdd": 35,
|
"hdd": 40,
|
||||||
"os": "Ubuntu",
|
"os": "Ubuntu",
|
||||||
"version": "24.04"
|
"version": "24.04"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"cpu": 4,
|
"cpu": 4,
|
||||||
"ram": 8192,
|
"ram": 8192,
|
||||||
"hdd": 25,
|
"hdd": 50,
|
||||||
"os": "debian",
|
"os": "debian",
|
||||||
"version": "13"
|
"version": "13"
|
||||||
}
|
}
|
||||||
|
|||||||
115
misc/api.func
115
misc/api.func
@@ -237,16 +237,21 @@ explain_exit_code() {
|
|||||||
# json_escape()
|
# json_escape()
|
||||||
#
|
#
|
||||||
# - Escapes a string for safe JSON embedding
|
# - Escapes a string for safe JSON embedding
|
||||||
|
# - Strips ANSI escape sequences and non-printable control characters
|
||||||
# - Handles backslashes, quotes, newlines, tabs, and carriage returns
|
# - Handles backslashes, quotes, newlines, tabs, and carriage returns
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
json_escape() {
|
json_escape() {
|
||||||
local s="$1"
|
local s="$1"
|
||||||
|
# Strip ANSI escape sequences (color codes etc.)
|
||||||
|
s=$(printf '%s' "$s" | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g')
|
||||||
s=${s//\\/\\\\}
|
s=${s//\\/\\\\}
|
||||||
s=${s//"/\\"/}
|
s=${s//"/\\"/}
|
||||||
s=${s//$'\n'/\\n}
|
s=${s//$'\n'/\\n}
|
||||||
s=${s//$'\r'/}
|
s=${s//$'\r'/}
|
||||||
s=${s//$'\t'/\\t}
|
s=${s//$'\t'/\\t}
|
||||||
echo "$s"
|
# Remove any remaining control characters (0x00-0x1F except those already handled)
|
||||||
|
s=$(printf '%s' "$s" | tr -d '\000-\010\013\014\016-\037')
|
||||||
|
printf '%s' "$s"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@@ -283,7 +288,33 @@ get_error_text() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$logfile" && -s "$logfile" ]]; then
|
if [[ -n "$logfile" && -s "$logfile" ]]; then
|
||||||
tail -n 20 "$logfile" 2>/dev/null | sed 's/\r$//'
|
tail -n 20 "$logfile" 2>/dev/null | sed 's/\r$//' | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# build_error_string()
|
||||||
|
#
|
||||||
|
# - Builds a structured error string for telemetry reporting
|
||||||
|
# - Format: "exit_code=<N> | <explanation>\n---\n<last 20 log lines>"
|
||||||
|
# - If no log lines available, returns just the explanation
|
||||||
|
# - Arguments:
|
||||||
|
# * $1: exit_code (numeric)
|
||||||
|
# * $2: log_text (optional, output from get_error_text)
|
||||||
|
# - Returns structured error string via stdout
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
build_error_string() {
|
||||||
|
local exit_code="${1:-1}"
|
||||||
|
local log_text="${2:-}"
|
||||||
|
local explanation
|
||||||
|
explanation=$(explain_exit_code "$exit_code")
|
||||||
|
|
||||||
|
if [[ -n "$log_text" ]]; then
|
||||||
|
# Structured format: header + separator + log lines
|
||||||
|
printf 'exit_code=%s | %s\n---\n%s' "$exit_code" "$explanation" "$log_text"
|
||||||
|
else
|
||||||
|
# No log available - just the explanation with exit code
|
||||||
|
printf 'exit_code=%s | %s' "$exit_code" "$explanation"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,13 +696,12 @@ post_update_to_api() {
|
|||||||
else
|
else
|
||||||
exit_code=1
|
exit_code=1
|
||||||
fi
|
fi
|
||||||
|
# Get log lines and build structured error string
|
||||||
local error_text=""
|
local error_text=""
|
||||||
error_text=$(get_error_text)
|
error_text=$(get_error_text)
|
||||||
if [[ -n "$error_text" ]]; then
|
local full_error
|
||||||
error=$(json_escape "$error_text")
|
full_error=$(build_error_string "$exit_code" "$error_text")
|
||||||
else
|
error=$(json_escape "$full_error")
|
||||||
error=$(json_escape "$(explain_exit_code "$exit_code")")
|
|
||||||
fi
|
|
||||||
short_error=$(json_escape "$(explain_exit_code "$exit_code")")
|
short_error=$(json_escape "$(explain_exit_code "$exit_code")")
|
||||||
error_category=$(categorize_error "$exit_code")
|
error_category=$(categorize_error "$exit_code")
|
||||||
[[ -z "$error" ]] && error="Unknown error"
|
[[ -z "$error" ]] && error="Unknown error"
|
||||||
@@ -814,31 +844,52 @@ EOF
|
|||||||
categorize_error() {
|
categorize_error() {
|
||||||
local code="$1"
|
local code="$1"
|
||||||
case "$code" in
|
case "$code" in
|
||||||
# Network errors
|
# Network errors (curl/wget)
|
||||||
6 | 7 | 22 | 28 | 35) echo "network" ;;
|
6 | 7 | 22 | 35) echo "network" ;;
|
||||||
|
|
||||||
# Storage errors
|
# Timeout errors
|
||||||
214 | 217 | 219) echo "storage" ;;
|
28 | 124 | 211) echo "timeout" ;;
|
||||||
|
|
||||||
# Dependency/Package errors
|
# Storage errors (Proxmox storage)
|
||||||
100 | 101 | 102 | 127 | 160 | 161 | 162) echo "dependency" ;;
|
214 | 217 | 219 | 224) echo "storage" ;;
|
||||||
|
|
||||||
|
# Dependency/Package errors (APT, DPKG, pip, commands)
|
||||||
|
100 | 101 | 102 | 127 | 160 | 161 | 162 | 255) echo "dependency" ;;
|
||||||
|
|
||||||
# Permission errors
|
# Permission errors
|
||||||
126 | 152) echo "permission" ;;
|
126 | 152) echo "permission" ;;
|
||||||
|
|
||||||
# Timeout errors
|
# Configuration errors (Proxmox config, invalid args)
|
||||||
124 | 28 | 211) echo "timeout" ;;
|
128 | 203 | 204 | 205 | 206 | 207 | 208) echo "config" ;;
|
||||||
|
|
||||||
# Configuration errors
|
# Proxmox container/template errors
|
||||||
203 | 204 | 205 | 206 | 207 | 208) echo "config" ;;
|
200 | 209 | 210 | 212 | 213 | 215 | 216 | 218 | 220 | 221 | 222 | 223 | 225 | 231) echo "proxmox" ;;
|
||||||
|
|
||||||
|
# Service/Systemd errors
|
||||||
|
150 | 151 | 153 | 154) echo "service" ;;
|
||||||
|
|
||||||
|
# Database errors (PostgreSQL, MySQL, MongoDB)
|
||||||
|
170 | 171 | 172 | 173 | 180 | 181 | 182 | 183 | 190 | 191 | 192 | 193) echo "database" ;;
|
||||||
|
|
||||||
|
# Node.js / JavaScript runtime errors
|
||||||
|
243 | 245 | 246 | 247 | 248 | 249) echo "runtime" ;;
|
||||||
|
|
||||||
|
# Python environment errors
|
||||||
|
# (already covered: 160-162 under dependency)
|
||||||
|
|
||||||
# Aborted by user
|
# Aborted by user
|
||||||
130) echo "aborted" ;;
|
130) echo "aborted" ;;
|
||||||
|
|
||||||
# Resource errors (OOM, etc)
|
# Resource errors (OOM, SIGKILL, SIGABRT)
|
||||||
137 | 134) echo "resource" ;;
|
134 | 137) echo "resource" ;;
|
||||||
|
|
||||||
# Default
|
# Signal/Process errors (SIGTERM, SIGPIPE, SIGSEGV)
|
||||||
|
139 | 141 | 143) echo "signal" ;;
|
||||||
|
|
||||||
|
# Shell errors (general error, syntax error)
|
||||||
|
1 | 2) echo "shell" ;;
|
||||||
|
|
||||||
|
# Default - truly unknown
|
||||||
*) echo "unknown" ;;
|
*) echo "unknown" ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -901,11 +952,9 @@ post_tool_to_api() {
|
|||||||
[[ ! "$exit_code" =~ ^[0-9]+$ ]] && exit_code=1
|
[[ ! "$exit_code" =~ ^[0-9]+$ ]] && exit_code=1
|
||||||
local error_text=""
|
local error_text=""
|
||||||
error_text=$(get_error_text)
|
error_text=$(get_error_text)
|
||||||
if [[ -n "$error_text" ]]; then
|
local full_error
|
||||||
error=$(json_escape "$error_text")
|
full_error=$(build_error_string "$exit_code" "$error_text")
|
||||||
else
|
error=$(json_escape "$full_error")
|
||||||
error=$(json_escape "$(explain_exit_code "$exit_code")")
|
|
||||||
fi
|
|
||||||
error_category=$(categorize_error "$exit_code")
|
error_category=$(categorize_error "$exit_code")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -968,11 +1017,9 @@ post_addon_to_api() {
|
|||||||
[[ ! "$exit_code" =~ ^[0-9]+$ ]] && exit_code=1
|
[[ ! "$exit_code" =~ ^[0-9]+$ ]] && exit_code=1
|
||||||
local error_text=""
|
local error_text=""
|
||||||
error_text=$(get_error_text)
|
error_text=$(get_error_text)
|
||||||
if [[ -n "$error_text" ]]; then
|
local full_error
|
||||||
error=$(json_escape "$error_text")
|
full_error=$(build_error_string "$exit_code" "$error_text")
|
||||||
else
|
error=$(json_escape "$full_error")
|
||||||
error=$(json_escape "$(explain_exit_code "$exit_code")")
|
|
||||||
fi
|
|
||||||
error_category=$(categorize_error "$exit_code")
|
error_category=$(categorize_error "$exit_code")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1067,11 +1114,9 @@ post_update_to_api_extended() {
|
|||||||
fi
|
fi
|
||||||
local error_text=""
|
local error_text=""
|
||||||
error_text=$(get_error_text)
|
error_text=$(get_error_text)
|
||||||
if [[ -n "$error_text" ]]; then
|
local full_error
|
||||||
error=$(json_escape "$error_text")
|
full_error=$(build_error_string "$exit_code" "$error_text")
|
||||||
else
|
error=$(json_escape "$full_error")
|
||||||
error=$(json_escape "$(explain_exit_code "$exit_code")")
|
|
||||||
fi
|
|
||||||
error_category=$(categorize_error "$exit_code")
|
error_category=$(categorize_error "$exit_code")
|
||||||
[[ -z "$error" ]] && error="Unknown error"
|
[[ -z "$error" ]] && error="Unknown error"
|
||||||
fi
|
fi
|
||||||
|
|||||||
119
misc/build.func
119
misc/build.func
@@ -277,8 +277,9 @@ install_ssh_keys_into_ct() {
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# validate_container_id()
|
# validate_container_id()
|
||||||
#
|
#
|
||||||
# - Validates if a container ID is available for use
|
# - Validates if a container ID is available for use (CLUSTER-WIDE)
|
||||||
# - Checks if ID is already used by VM or LXC container
|
# - Checks cluster resources via pvesh for VMs/CTs on ALL nodes
|
||||||
|
# - Falls back to local config file check if pvesh unavailable
|
||||||
# - Checks if ID is used in LVM logical volumes
|
# - Checks if ID is used in LVM logical volumes
|
||||||
# - Returns 0 if ID is available, 1 if already in use
|
# - Returns 0 if ID is available, 1 if already in use
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@@ -290,11 +291,35 @@ validate_container_id() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if config file exists for VM or LXC
|
# CLUSTER-WIDE CHECK: Query all VMs/CTs across all nodes
|
||||||
|
# This catches IDs used on other nodes in the cluster
|
||||||
|
# NOTE: Works on single-node too - Proxmox always has internal cluster structure
|
||||||
|
# Falls back gracefully if pvesh unavailable or returns empty
|
||||||
|
if command -v pvesh &>/dev/null; then
|
||||||
|
local cluster_ids
|
||||||
|
cluster_ids=$(pvesh get /cluster/resources --type vm --output-format json 2>/dev/null |
|
||||||
|
grep -oP '"vmid":\s*\K[0-9]+' 2>/dev/null || true)
|
||||||
|
if [[ -n "$cluster_ids" ]] && echo "$cluster_ids" | grep -qw "$ctid"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# LOCAL FALLBACK: Check if config file exists for VM or LXC
|
||||||
|
# This handles edge cases where pvesh might not return all info
|
||||||
if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then
|
if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check ALL nodes in cluster for config files (handles pmxcfs sync delays)
|
||||||
|
# NOTE: On single-node, /etc/pve/nodes/ contains just the one node - still works
|
||||||
|
if [[ -d "/etc/pve/nodes" ]]; then
|
||||||
|
for node_dir in /etc/pve/nodes/*/; do
|
||||||
|
if [[ -f "${node_dir}qemu-server/${ctid}.conf" ]] || [[ -f "${node_dir}lxc/${ctid}.conf" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# Check if ID is used in LVM logical volumes
|
# Check if ID is used in LVM logical volumes
|
||||||
if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then
|
if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then
|
||||||
return 1
|
return 1
|
||||||
@@ -306,63 +331,30 @@ validate_container_id() {
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# get_valid_container_id()
|
# get_valid_container_id()
|
||||||
#
|
#
|
||||||
# - Returns a valid, unused container ID
|
# - Returns a valid, unused container ID (CLUSTER-AWARE)
|
||||||
|
# - Uses pvesh /cluster/nextid as starting point (already cluster-aware)
|
||||||
# - If provided ID is valid, returns it
|
# - If provided ID is valid, returns it
|
||||||
# - Otherwise increments from suggested ID until a free one is found
|
# - Otherwise increments until a free one is found across entire cluster
|
||||||
# - Calls validate_container_id() to check availability
|
# - Calls validate_container_id() to check availability
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
get_valid_container_id() {
|
get_valid_container_id() {
|
||||||
local suggested_id="${1:-$(pvesh get /cluster/nextid)}"
|
local suggested_id="${1:-$(pvesh get /cluster/nextid 2>/dev/null || echo 100)}"
|
||||||
|
|
||||||
while ! validate_container_id "$suggested_id"; do
|
# Ensure we have a valid starting ID
|
||||||
suggested_id=$((suggested_id + 1))
|
if ! [[ "$suggested_id" =~ ^[0-9]+$ ]]; then
|
||||||
done
|
suggested_id=$(pvesh get /cluster/nextid 2>/dev/null || echo 100)
|
||||||
|
fi
|
||||||
echo "$suggested_id"
|
|
||||||
}
|
local max_attempts=1000
|
||||||
|
local attempts=0
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# validate_container_id()
|
|
||||||
#
|
|
||||||
# - Validates if a container ID is available for use
|
|
||||||
# - Checks if ID is already used by VM or LXC container
|
|
||||||
# - Checks if ID is used in LVM logical volumes
|
|
||||||
# - Returns 0 if ID is available, 1 if already in use
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
validate_container_id() {
|
|
||||||
local ctid="$1"
|
|
||||||
|
|
||||||
# Check if ID is numeric
|
|
||||||
if ! [[ "$ctid" =~ ^[0-9]+$ ]]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if config file exists for VM or LXC
|
|
||||||
if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if ID is used in LVM logical volumes
|
|
||||||
if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# get_valid_container_id()
|
|
||||||
#
|
|
||||||
# - Returns a valid, unused container ID
|
|
||||||
# - If provided ID is valid, returns it
|
|
||||||
# - Otherwise increments from suggested ID until a free one is found
|
|
||||||
# - Calls validate_container_id() to check availability
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
get_valid_container_id() {
|
|
||||||
local suggested_id="${1:-$(pvesh get /cluster/nextid)}"
|
|
||||||
|
|
||||||
while ! validate_container_id "$suggested_id"; do
|
while ! validate_container_id "$suggested_id"; do
|
||||||
suggested_id=$((suggested_id + 1))
|
suggested_id=$((suggested_id + 1))
|
||||||
|
attempts=$((attempts + 1))
|
||||||
|
if [[ $attempts -ge $max_attempts ]]; then
|
||||||
|
msg_error "Could not find available container ID after $max_attempts attempts"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "$suggested_id"
|
echo "$suggested_id"
|
||||||
@@ -4133,8 +4125,7 @@ EOF'
|
|||||||
|
|
||||||
# Show combined log location
|
# Show combined log location
|
||||||
if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then
|
if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then
|
||||||
echo ""
|
msg_custom "📋" "${YW}" "Installation log: ${combined_log}"
|
||||||
echo -e "${GN}✔${CL} Installation log: ${BL}${combined_log}${CL}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Dev mode: Keep container or open breakpoint shell
|
# Dev mode: Keep container or open breakpoint shell
|
||||||
@@ -4157,19 +4148,21 @@ EOF'
|
|||||||
exit $install_exit_code
|
exit $install_exit_code
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner)
|
# Prompt user for cleanup with 60s timeout
|
||||||
echo ""
|
echo ""
|
||||||
echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
|
echo -en "${TAB}❓${TAB}${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
|
||||||
|
|
||||||
if read -t 60 -r response; then
|
if read -t 60 -r response; then
|
||||||
if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then
|
if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then
|
||||||
# Remove container
|
# Remove container
|
||||||
echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}"
|
echo ""
|
||||||
|
msg_info "Removing container ${CTID}"
|
||||||
pct stop "$CTID" &>/dev/null || true
|
pct stop "$CTID" &>/dev/null || true
|
||||||
pct destroy "$CTID" &>/dev/null || true
|
pct destroy "$CTID" &>/dev/null || true
|
||||||
echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}"
|
msg_ok "Container ${CTID} removed"
|
||||||
elif [[ "$response" =~ ^[Nn]$ ]]; then
|
elif [[ "$response" =~ ^[Nn]$ ]]; then
|
||||||
echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}"
|
echo ""
|
||||||
|
msg_warn "Container ${CTID} kept for debugging"
|
||||||
|
|
||||||
# Dev mode: Setup MOTD/SSH for debugging access to broken container
|
# Dev mode: Setup MOTD/SSH for debugging access to broken container
|
||||||
if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then
|
if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then
|
||||||
@@ -4185,11 +4178,11 @@ EOF'
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Timeout - auto-remove
|
# Timeout - auto-remove
|
||||||
echo -e "\n${YW}No response - auto-removing container${CL}"
|
echo ""
|
||||||
echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}"
|
msg_info "No response - removing container ${CTID}"
|
||||||
pct stop "$CTID" &>/dev/null || true
|
pct stop "$CTID" &>/dev/null || true
|
||||||
pct destroy "$CTID" &>/dev/null || true
|
pct destroy "$CTID" &>/dev/null || true
|
||||||
echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}"
|
msg_ok "Container ${CTID} removed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Force one final status update attempt after cleanup
|
# Force one final status update attempt after cleanup
|
||||||
|
|||||||
@@ -522,15 +522,9 @@ silent() {
|
|||||||
msg_custom "→" "${YWB}" "${cmd}"
|
msg_custom "→" "${YWB}" "${cmd}"
|
||||||
|
|
||||||
if [[ -s "$logfile" ]]; then
|
if [[ -s "$logfile" ]]; then
|
||||||
local log_lines=$(wc -l <"$logfile")
|
echo -e "\n${TAB}--- Last 10 lines of log ---"
|
||||||
echo "--- Last 10 lines of silent log ---"
|
|
||||||
tail -n 10 "$logfile"
|
tail -n 10 "$logfile"
|
||||||
echo "-----------------------------------"
|
echo -e "${TAB}-----------------------------------\n"
|
||||||
|
|
||||||
# Show how to view full log if there are more lines
|
|
||||||
if [[ $log_lines -gt 10 ]]; then
|
|
||||||
msg_custom "📋" "${YW}" "View full log (${log_lines} lines): ${logfile}"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit "$rc"
|
exit "$rc"
|
||||||
|
|||||||
@@ -175,9 +175,9 @@ error_handler() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$active_log" && -s "$active_log" ]]; then
|
if [[ -n "$active_log" && -s "$active_log" ]]; then
|
||||||
echo "--- Last 20 lines of silent log ---"
|
echo -e "\n${TAB}--- Last 20 lines of log ---"
|
||||||
tail -n 20 "$active_log"
|
tail -n 20 "$active_log"
|
||||||
echo "-----------------------------------"
|
echo -e "${TAB}-----------------------------------\n"
|
||||||
|
|
||||||
# Detect context: Container (INSTALL_LOG set + /root exists) vs Host (BUILD_LOG)
|
# Detect context: Container (INSTALL_LOG set + /root exists) vs Host (BUILD_LOG)
|
||||||
if [[ -n "${INSTALL_LOG:-}" && -d /root ]]; then
|
if [[ -n "${INSTALL_LOG:-}" && -d /root ]]; then
|
||||||
@@ -204,23 +204,50 @@ error_handler() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
|
if declare -f msg_custom >/dev/null 2>&1; then
|
||||||
|
echo -en "${TAB}❓${TAB}${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
|
||||||
|
else
|
||||||
|
echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
|
||||||
|
fi
|
||||||
|
|
||||||
if read -t 60 -r response; then
|
if read -t 60 -r response; then
|
||||||
if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then
|
if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then
|
||||||
echo -e "\n${YW}Removing container ${CTID}${CL}"
|
echo ""
|
||||||
|
if declare -f msg_info >/dev/null 2>&1; then
|
||||||
|
msg_info "Removing container ${CTID}"
|
||||||
|
else
|
||||||
|
echo -e "${YW}Removing container ${CTID}${CL}"
|
||||||
|
fi
|
||||||
pct stop "$CTID" &>/dev/null || true
|
pct stop "$CTID" &>/dev/null || true
|
||||||
pct destroy "$CTID" &>/dev/null || true
|
pct destroy "$CTID" &>/dev/null || true
|
||||||
echo -e "${GN}✔${CL} Container ${CTID} removed"
|
if declare -f msg_ok >/dev/null 2>&1; then
|
||||||
|
msg_ok "Container ${CTID} removed"
|
||||||
|
else
|
||||||
|
echo -e "${GN}✔${CL} Container ${CTID} removed"
|
||||||
|
fi
|
||||||
elif [[ "$response" =~ ^[Nn]$ ]]; then
|
elif [[ "$response" =~ ^[Nn]$ ]]; then
|
||||||
echo -e "\n${YW}Container ${CTID} kept for debugging${CL}"
|
echo ""
|
||||||
|
if declare -f msg_warn >/dev/null 2>&1; then
|
||||||
|
msg_warn "Container ${CTID} kept for debugging"
|
||||||
|
else
|
||||||
|
echo -e "${YW}Container ${CTID} kept for debugging${CL}"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
# Timeout - auto-remove
|
# Timeout - auto-remove
|
||||||
echo -e "\n${YW}No response - auto-removing container${CL}"
|
echo ""
|
||||||
|
if declare -f msg_info >/dev/null 2>&1; then
|
||||||
|
msg_info "No response - removing container ${CTID}"
|
||||||
|
else
|
||||||
|
echo -e "${YW}No response - removing container ${CTID}${CL}"
|
||||||
|
fi
|
||||||
pct stop "$CTID" &>/dev/null || true
|
pct stop "$CTID" &>/dev/null || true
|
||||||
pct destroy "$CTID" &>/dev/null || true
|
pct destroy "$CTID" &>/dev/null || true
|
||||||
echo -e "${GN}✔${CL} Container ${CTID} removed"
|
if declare -f msg_ok >/dev/null 2>&1; then
|
||||||
|
msg_ok "Container ${CTID} removed"
|
||||||
|
else
|
||||||
|
echo -e "${GN}✔${CL} Container ${CTID} removed"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Force one final status update attempt after cleanup
|
# Force one final status update attempt after cleanup
|
||||||
@@ -254,6 +281,10 @@ on_exit() {
|
|||||||
# post_to_api was called ("installing" sent) but post_update_to_api was never called
|
# post_to_api was called ("installing" sent) but post_update_to_api was never called
|
||||||
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||||
if declare -f post_update_to_api >/dev/null 2>&1; then
|
if declare -f post_update_to_api >/dev/null 2>&1; then
|
||||||
|
# Ensure log is accessible on host before reporting
|
||||||
|
if declare -f ensure_log_on_host >/dev/null 2>&1; then
|
||||||
|
ensure_log_on_host
|
||||||
|
fi
|
||||||
if [[ $exit_code -ne 0 ]]; then
|
if [[ $exit_code -ne 0 ]]; then
|
||||||
post_update_to_api "failed" "$exit_code"
|
post_update_to_api "failed" "$exit_code"
|
||||||
else
|
else
|
||||||
@@ -273,6 +304,10 @@ on_exit() {
|
|||||||
# - Exits with code 130 (128 + SIGINT=2)
|
# - Exits with code 130 (128 + SIGINT=2)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
on_interrupt() {
|
on_interrupt() {
|
||||||
|
# Ensure log is accessible on host before reporting
|
||||||
|
if declare -f ensure_log_on_host >/dev/null 2>&1; then
|
||||||
|
ensure_log_on_host
|
||||||
|
fi
|
||||||
# Report interruption to telemetry API (prevents stuck "installing" records)
|
# Report interruption to telemetry API (prevents stuck "installing" records)
|
||||||
if declare -f post_update_to_api >/dev/null 2>&1; then
|
if declare -f post_update_to_api >/dev/null 2>&1; then
|
||||||
post_update_to_api "failed" "130"
|
post_update_to_api "failed" "130"
|
||||||
@@ -294,6 +329,10 @@ on_interrupt() {
|
|||||||
# - Triggered by external process termination
|
# - Triggered by external process termination
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
on_terminate() {
|
on_terminate() {
|
||||||
|
# Ensure log is accessible on host before reporting
|
||||||
|
if declare -f ensure_log_on_host >/dev/null 2>&1; then
|
||||||
|
ensure_log_on_host
|
||||||
|
fi
|
||||||
# Report termination to telemetry API (prevents stuck "installing" records)
|
# Report termination to telemetry API (prevents stuck "installing" records)
|
||||||
if declare -f post_update_to_api >/dev/null 2>&1; then
|
if declare -f post_update_to_api >/dev/null 2>&1; then
|
||||||
post_update_to_api "failed" "143"
|
post_update_to_api "failed" "143"
|
||||||
|
|||||||
@@ -1913,7 +1913,7 @@ function fetch_and_deploy_codeberg_release() {
|
|||||||
local app="$1"
|
local app="$1"
|
||||||
local repo="$2"
|
local repo="$2"
|
||||||
local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile | tag
|
local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile | tag
|
||||||
local version="${4:-latest}"
|
local version="${var_appversion:-${4:-latest}}"
|
||||||
local target="${5:-/opt/$app}"
|
local target="${5:-/opt/$app}"
|
||||||
local asset_pattern="${6:-}"
|
local asset_pattern="${6:-}"
|
||||||
|
|
||||||
@@ -2443,7 +2443,7 @@ function fetch_and_deploy_gh_release() {
|
|||||||
local app="$1"
|
local app="$1"
|
||||||
local repo="$2"
|
local repo="$2"
|
||||||
local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile
|
local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile
|
||||||
local version="${4:-latest}"
|
local version="${var_appversion:-${4:-latest}}"
|
||||||
local target="${5:-/opt/$app}"
|
local target="${5:-/opt/$app}"
|
||||||
local asset_pattern="${6:-}"
|
local asset_pattern="${6:-}"
|
||||||
|
|
||||||
|
|||||||
@@ -207,15 +207,9 @@ silent() {
|
|||||||
msg_custom "→" "${YWB}" "${cmd}"
|
msg_custom "→" "${YWB}" "${cmd}"
|
||||||
|
|
||||||
if [[ -s "$logfile" ]]; then
|
if [[ -s "$logfile" ]]; then
|
||||||
local log_lines=$(wc -l <"$logfile")
|
echo -e "\n${TAB}--- Last 10 lines of log ---"
|
||||||
echo "--- Last 10 lines of log ---"
|
|
||||||
tail -n 10 "$logfile"
|
tail -n 10 "$logfile"
|
||||||
echo "----------------------------"
|
echo -e "${TAB}----------------------------\n"
|
||||||
|
|
||||||
# Show how to view full log if there are more lines
|
|
||||||
if [[ $log_lines -gt 10 ]]; then
|
|
||||||
msg_custom "📋" "${YW}" "View full log (${log_lines} lines): ${logfile}"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit "$rc"
|
exit "$rc"
|
||||||
|
|||||||
@@ -110,6 +110,11 @@ for container in $(pct list | awk '{if(NR>1) print $1}'); do
|
|||||||
container_hostname=$(pct exec "$container" hostname)
|
container_hostname=$(pct exec "$container" hostname)
|
||||||
containers_needing_reboot+=("$container ($container_hostname)")
|
containers_needing_reboot+=("$container ($container_hostname)")
|
||||||
fi
|
fi
|
||||||
|
# check if patchmon agent is present in container and run a report if found
|
||||||
|
if pct exec "$container" -- [ -e "/usr/local/bin/patchmon-agent" ]; then
|
||||||
|
echo -e "${BL}[Info]${GN} patchmon-agent found in ${BL} $container ${CL}, triggering report. \n"
|
||||||
|
pct exec "$container" -- "/usr/local/bin/patchmon-agent" "report"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
wait
|
wait
|
||||||
|
|||||||
Reference in New Issue
Block a user