update
This commit is contained in:
@@ -80,9 +80,10 @@ ISO repository.
|
|||||||
The legacy Dell OMSA installer is limited to supported PowerEdge x30/x40
|
The legacy Dell OMSA installer is limited to supported PowerEdge x30/x40
|
||||||
systems running Proxmox VE 9 on Debian 13 (Trixie), amd64.
|
systems running Proxmox VE 9 on Debian 13 (Trixie), amd64.
|
||||||
|
|
||||||
CPU compatibility detection previews cluster-wide QEMU VM and template
|
CPU compatibility analysis compares every cluster host's physical processor,
|
||||||
changes before applying the ProxCLMC recommendation through the Proxmox CLI.
|
processor generation/family, and supported generic VM CPU baseline. It then previews
|
||||||
Running VMs are not restarted automatically.
|
QEMU VM and template changes before applying the highest baseline shared by
|
||||||
|
all nodes through the Proxmox CLI. Running VMs are not restarted automatically.
|
||||||
|
|
||||||
The native TAPM host configuration workflow replaces the former ProxMenux
|
The native TAPM host configuration workflow replaces the former ProxMenux
|
||||||
post-install dependency. It can audit a host, apply the recommended PVE 9
|
post-install dependency. It can audit a host, apply the recommended PVE 9
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
action="${1:-}"
|
action="${1:-}"
|
||||||
FOLDER='/opt/idssys/ta-proxmenu'
|
FOLDER='/opt/idssys/ta-proxmenu'
|
||||||
VERS='2026.7.28-10'
|
VERS='2026.7.28-11'
|
||||||
|
|
||||||
noupdate=' '
|
noupdate=' '
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Parse the per-node rows printed by ProxCLMC. Fields are separated with an
|
||||||
|
# ASCII unit separator so processor descriptions can safely contain spaces.
|
||||||
|
TAPM_PROXCLMC_HOST_ROWS() {
|
||||||
|
awk -F '|' '
|
||||||
|
function trim(value) {
|
||||||
|
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
node = trim($1)
|
||||||
|
address = trim($2)
|
||||||
|
level = trim($3)
|
||||||
|
|
||||||
|
if (node ~ /^[[:alnum:]_.-]+$/ && level ~ /^x86-64-v(1|2-AES|3|4)$/) {
|
||||||
|
printf "%s\034%s\034%s\n", node, address, level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Produce a conservative generation label from the processor name exposed by
|
||||||
|
# Proxmox. Ambiguous products are described as a family instead of being given
|
||||||
|
# a potentially incorrect codename.
|
||||||
|
TAPM_CPU_GENERATION_FROM_NAME() {
|
||||||
|
local cpu_name="${1:-}"
|
||||||
|
local normalized
|
||||||
|
local generation=""
|
||||||
|
local model_number=""
|
||||||
|
|
||||||
|
normalized="$(printf '%s' "$cpu_name" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
|
||||||
|
if [[ "$normalized" =~ xeon.*e5-[[:digit:]]+[[:space:]]+v([1-4]) ]]; then
|
||||||
|
generation="${BASH_REMATCH[1]}"
|
||||||
|
case "$generation" in
|
||||||
|
1) printf '%s\n' "Sandy Bridge-EP" ;;
|
||||||
|
2) printf '%s\n' "Ivy Bridge-EP" ;;
|
||||||
|
3) printf '%s\n' "Haswell-EP" ;;
|
||||||
|
4) printf '%s\n' "Broadwell-EP" ;;
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$normalized" =~ xeon.*(bronze|silver|gold|platinum)[[:space:]]+([[:digit:]]{4}) ]]; then
|
||||||
|
model_number="${BASH_REMATCH[2]}"
|
||||||
|
generation="${model_number:1:1}"
|
||||||
|
case "$generation" in
|
||||||
|
1) printf '%s\n' "1st Gen Xeon Scalable (Skylake-SP)" ;;
|
||||||
|
2) printf '%s\n' "2nd Gen Xeon Scalable (Cascade Lake)" ;;
|
||||||
|
3) printf '%s\n' "3rd Gen Xeon Scalable (Ice Lake)" ;;
|
||||||
|
4) printf '%s\n' "4th Gen Xeon Scalable (Sapphire Rapids)" ;;
|
||||||
|
5) printf '%s\n' "5th Gen Xeon Scalable (Emerald Rapids)" ;;
|
||||||
|
*) printf '%s\n' "Intel Xeon Scalable family" ;;
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$normalized" =~ epyc[[:space:]]+([[:digit:]]{4}) ]]; then
|
||||||
|
model_number="${BASH_REMATCH[1]}"
|
||||||
|
generation="${model_number:3:1}"
|
||||||
|
case "$generation" in
|
||||||
|
1) printf '%s\n' "1st Gen EPYC (Naples / Zen)" ;;
|
||||||
|
2) printf '%s\n' "2nd Gen EPYC (Rome / Zen 2)" ;;
|
||||||
|
3) printf '%s\n' "3rd Gen EPYC (Milan / Zen 3)" ;;
|
||||||
|
4) printf '%s\n' "4th Gen EPYC (Zen 4 family)" ;;
|
||||||
|
5) printf '%s\n' "5th Gen EPYC (Zen 5 family)" ;;
|
||||||
|
*) printf '%s\n' "AMD EPYC family" ;;
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$normalized" =~ core.*i[3579]-([[:digit:]]{4,5}) ]]; then
|
||||||
|
model_number="${BASH_REMATCH[1]}"
|
||||||
|
if (( ${#model_number} == 4 )); then
|
||||||
|
generation="${model_number:0:1}"
|
||||||
|
else
|
||||||
|
generation="${model_number:0:2}"
|
||||||
|
fi
|
||||||
|
printf '%s\n' "Intel Core Gen ${generation}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$normalized" =~ ryzen.*[[:space:]]([[:digit:]]{4,5}) ]]; then
|
||||||
|
model_number="${BASH_REMATCH[1]}"
|
||||||
|
printf '%s\n' "AMD Ryzen ${model_number:0:1}000 family"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$normalized" in
|
||||||
|
*intel*) printf '%s\n' "Intel generation unknown" ;;
|
||||||
|
*amd*) printf '%s\n' "AMD generation unknown" ;;
|
||||||
|
*) printf '%s\n' "Generation unknown" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
+76
-3
@@ -10,6 +10,7 @@ source /opt/idssys/ta-proxmenu/inc/header-info.inc
|
|||||||
source /opt/idssys/ta-proxmenu/inc/post-install.inc
|
source /opt/idssys/ta-proxmenu/inc/post-install.inc
|
||||||
source /opt/idssys/ta-proxmenu/inc/rmm.inc
|
source /opt/idssys/ta-proxmenu/inc/rmm.inc
|
||||||
source /opt/idssys/ta-proxmenu/inc/secure-input.inc
|
source /opt/idssys/ta-proxmenu/inc/secure-input.inc
|
||||||
|
source /opt/idssys/ta-proxmenu/inc/cpu-compat.inc
|
||||||
source /opt/idssys/ta-proxmenu/inc/deploy-iso-nfs-lxc.sh
|
source /opt/idssys/ta-proxmenu/inc/deploy-iso-nfs-lxc.sh
|
||||||
source /opt/idssys/ta-proxmenu/inc/deploy-pulse-lxc.sh
|
source /opt/idssys/ta-proxmenu/inc/deploy-pulse-lxc.sh
|
||||||
source /opt/idssys/ta-proxmenu/inc/virtio-helpers.inc
|
source /opt/idssys/ta-proxmenu/inc/virtio-helpers.inc
|
||||||
@@ -1225,6 +1226,73 @@ TAPM_SET_QEMU_CPU_MODEL() {
|
|||||||
--cpu "$cpu_model"
|
--cpu "$cpu_model"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TAPM_NODE_CPU_NAME() {
|
||||||
|
local node="${1:?node is required}"
|
||||||
|
local status_json
|
||||||
|
|
||||||
|
status_json="$(
|
||||||
|
pvesh get "/nodes/${node}/status" --output-format json 2>/dev/null
|
||||||
|
)" || return 1
|
||||||
|
|
||||||
|
TAPM_NODE_STATUS_JSON="$status_json" python3 -c '
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
try:
|
||||||
|
status = json.loads(os.environ["TAPM_NODE_STATUS_JSON"])
|
||||||
|
except (KeyError, TypeError, ValueError):
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
cpu_info = status.get("cpuinfo") or {}
|
||||||
|
model = str(cpu_info.get("model") or "").strip()
|
||||||
|
if not model:
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
print(" ".join(model.split()))
|
||||||
|
' 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
TAPM_SHOW_CLUSTER_CPU_TABLE() {
|
||||||
|
local proxclmc_output
|
||||||
|
local host_rows
|
||||||
|
local node
|
||||||
|
local address
|
||||||
|
local host_max
|
||||||
|
local cpu_name
|
||||||
|
local generation
|
||||||
|
|
||||||
|
proxclmc_output="$(proxclmc 2>/dev/null)" || proxclmc_output=""
|
||||||
|
host_rows="$(printf '%s\n' "$proxclmc_output" | TAPM_PROXCLMC_HOST_ROWS)"
|
||||||
|
|
||||||
|
printf '\nCluster host CPU capabilities:\n\n'
|
||||||
|
|
||||||
|
if [[ -z "$host_rows" ]]; then
|
||||||
|
echo -e " ${idsCL[Yellow]}Per-host CPU details were unavailable from ProxCLMC.${idsCL[Default]}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf ' %-16s %-42s %-34s %-14s\n' \
|
||||||
|
'HOST' 'PHYSICAL CPU' 'GENERATION / FAMILY' 'BEST VM MODEL'
|
||||||
|
printf ' %-16s %-42s %-34s %-14s\n' \
|
||||||
|
'----------------' '------------------------------------------' \
|
||||||
|
'----------------------------------' '--------------'
|
||||||
|
|
||||||
|
while IFS=$'\034' read -r node address host_max; do
|
||||||
|
[[ -n "$node" ]] || continue
|
||||||
|
|
||||||
|
cpu_name="$(TAPM_NODE_CPU_NAME "$node" 2>/dev/null || true)"
|
||||||
|
if [[ -n "$cpu_name" ]]; then
|
||||||
|
generation="$(TAPM_CPU_GENERATION_FROM_NAME "$cpu_name")"
|
||||||
|
else
|
||||||
|
cpu_name='Unavailable'
|
||||||
|
generation='Unavailable'
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf ' %-16.16s %-42.42s %-34.34s %-14s\n' \
|
||||||
|
"$node" "$cpu_name" "$generation" "$host_max"
|
||||||
|
done <<< "$host_rows"
|
||||||
|
}
|
||||||
|
|
||||||
DETECT_CPU() {
|
DETECT_CPU() {
|
||||||
local answer
|
local answer
|
||||||
local cpu_model
|
local cpu_model
|
||||||
@@ -1266,6 +1334,12 @@ DETECT_CPU() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
TAPM_SHOW_CLUSTER_CPU_TABLE
|
||||||
|
printf '\nRecommended cluster-wide VM CPU baseline: %s\n' "$cpu_model"
|
||||||
|
printf '%s\n' \
|
||||||
|
'This is the highest generic model shared by every node for HA placement,' \
|
||||||
|
'load balancing, and moving workloads between cluster hosts.'
|
||||||
|
|
||||||
guest_output="$(TAPM_CLUSTER_QEMU_GUESTS)" || {
|
guest_output="$(TAPM_CLUSTER_QEMU_GUESTS)" || {
|
||||||
echo -e "${idsCL[LightRed]}Could not read cluster QEMU resources.${idsCL[Default]}"
|
echo -e "${idsCL[LightRed]}Could not read cluster QEMU resources.${idsCL[Default]}"
|
||||||
FINISH_FAILED_ACTION
|
FINISH_FAILED_ACTION
|
||||||
@@ -1296,7 +1370,6 @@ DETECT_CPU() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '\nRecommended cluster CPU model: %s\n' "$cpu_model"
|
|
||||||
printf '\n%-7s %-28s %-20s %-10s %-24s %s\n' \
|
printf '\n%-7s %-28s %-20s %-10s %-24s %s\n' \
|
||||||
'VMID' 'NAME' 'NODE' 'TEMPLATE' 'CURRENT CPU' 'PROPOSED CPU'
|
'VMID' 'NAME' 'NODE' 'TEMPLATE' 'CURRENT CPU' 'PROPOSED CPU'
|
||||||
printf '%-7s %-28s %-20s %-10s %-24s %s\n' \
|
printf '%-7s %-28s %-20s %-10s %-24s %s\n' \
|
||||||
@@ -1308,7 +1381,7 @@ DETECT_CPU() {
|
|||||||
"$vmid" "${name:-unnamed}" "$node" "$template" "$current_cpu" "$cpu_model"
|
"$vmid" "${name:-unnamed}" "$node" "$template" "$current_cpu" "$cpu_model"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo -en "\n${idsCL[LightCyan]}Apply this CPU model to ${#changes[@]} VM(s) and template(s) (y/N)?${idsCL[Default]} "
|
echo -en "\n${idsCL[LightCyan]}Apply cluster-wide baseline ${cpu_model} to ${#changes[@]} VM(s) and template(s) (y/N)?${idsCL[Default]} "
|
||||||
read -r -n 1 answer
|
read -r -n 1 answer
|
||||||
echo
|
echo
|
||||||
[[ "$answer" =~ ^[Yy]$ ]] || return
|
[[ "$answer" =~ ^[Yy]$ ]] || return
|
||||||
@@ -2269,7 +2342,7 @@ HOST_SETUP_MENU() {
|
|||||||
values=("post_install")
|
values=("post_install")
|
||||||
TAPM_POST_PROXMENUX_DETECTED &&
|
TAPM_POST_PROXMENUX_DETECTED &&
|
||||||
labels[0]="TAPM Host Configuration — ProxMenux migration recommended"
|
labels[0]="TAPM Host Configuration — ProxMenux migration recommended"
|
||||||
labels+=("Detect CPU model for live migrations")
|
labels+=("Analyze VM CPU compatibility for HA and workload mobility")
|
||||||
values+=("cpu")
|
values+=("cpu")
|
||||||
|
|
||||||
# Discover ISO storage only after the VirtIO submenu is selected.
|
# Discover ISO storage only after the VirtIO submenu is selected.
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TEST_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_DIR="$(cd "${TEST_DIR}/.." && pwd)"
|
||||||
|
|
||||||
|
source "${REPO_DIR}/inc/cpu-compat.inc"
|
||||||
|
|
||||||
|
assert_equal() {
|
||||||
|
local expected="${1:?expected value is required}"
|
||||||
|
local actual="${2:?actual value is required}"
|
||||||
|
local description="${3:?description is required}"
|
||||||
|
|
||||||
|
if [[ "$actual" != "$expected" ]]; then
|
||||||
|
printf 'FAIL: %s\nExpected: %s\nActual: %s\n' \
|
||||||
|
"$description" "$expected" "$actual" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal \
|
||||||
|
"Broadwell-EP" \
|
||||||
|
"$(TAPM_CPU_GENERATION_FROM_NAME "Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz")" \
|
||||||
|
"identifies Xeon E5 v4"
|
||||||
|
|
||||||
|
assert_equal \
|
||||||
|
"3rd Gen Xeon Scalable (Ice Lake)" \
|
||||||
|
"$(TAPM_CPU_GENERATION_FROM_NAME "Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz")" \
|
||||||
|
"identifies third-generation Xeon Scalable"
|
||||||
|
|
||||||
|
assert_equal \
|
||||||
|
"3rd Gen EPYC (Milan / Zen 3)" \
|
||||||
|
"$(TAPM_CPU_GENERATION_FROM_NAME "AMD EPYC 7543 32-Core Processor")" \
|
||||||
|
"identifies EPYC Milan"
|
||||||
|
|
||||||
|
proxclmc_fixture='node-a | 10.0.0.1 | x86-64-v4
|
||||||
|
node-b | 10.0.0.2 | x86-64-v3
|
||||||
|
|
||||||
|
Cluster CPU type: x86-64-v3'
|
||||||
|
|
||||||
|
expected_rows=$'node-a\03410.0.0.1\034x86-64-v4\nnode-b\03410.0.0.2\034x86-64-v3'
|
||||||
|
actual_rows="$(printf '%s\n' "$proxclmc_fixture" | TAPM_PROXCLMC_HOST_ROWS)"
|
||||||
|
|
||||||
|
assert_equal "$expected_rows" "$actual_rows" "parses ProxCLMC host rows"
|
||||||
|
|
||||||
|
printf 'CPU compatibility tests passed.\n'
|
||||||
Reference in New Issue
Block a user