mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-06-15 14:28:21 +00:00
update beta 1.2.2.2
This commit is contained in:
222
scripts/backup_restore/collectors/collect_hardware.sh
Normal file
222
scripts/backup_restore/collectors/collect_hardware.sh
Normal file
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==========================================================
|
||||
# ProxMenux backup manifest collector — hardware_inventory
|
||||
# ==========================================================
|
||||
# Detects GPUs (with vendor → ProxMenux installer mapping),
|
||||
# TPUs (Coral PCIe/USB), NICs (with bridge membership), and
|
||||
# Wireless interfaces. Read-only. Schema:
|
||||
# scripts/backup_restore/schema/manifest.schema.json
|
||||
# ==========================================================
|
||||
set -euo pipefail
|
||||
|
||||
# Vendor → installer path mapping. Update when ProxMenux adds new
|
||||
# installers for hardware that depends on out-of-tree drivers.
|
||||
# Vendors WITHOUT a mapping get null (e.g. Intel/AMD iGPUs work with
|
||||
# in-tree drivers, no special installer needed).
|
||||
gpu_installer_for() {
|
||||
case "$1" in
|
||||
NVIDIA) echo "scripts/gpu_tpu/nvidia_installer.sh" ;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ── GPUs ──
|
||||
# lspci -nnD outputs:
|
||||
# 0000:01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP107GL [Quadro P620] [10de:1cb6] (rev a1)
|
||||
# We pick anything classified as VGA/3D/Display (display controllers).
|
||||
gpu_array='[]'
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
pci_address="$(printf '%s' "$line" | awk '{print $1}')"
|
||||
pci_id="$(printf '%s' "$line" | grep -oE '\[[0-9a-f]{4}:[0-9a-f]{4}\]' | tail -1 | tr -d '[]')"
|
||||
# Description: everything between the "controller]:" header and the
|
||||
# final "[pci_id]" tag. For AMD this includes the [AMD/ATI] tag; for
|
||||
# NVIDIA/Intel it's just vendor + model.
|
||||
desc="$(printf '%s' "$line" | sed -nE "s@.*\]:[[:space:]]*(.*)[[:space:]]+\[[0-9a-f]{4}:[0-9a-f]{4}\].*@\1@p")"
|
||||
|
||||
# Vendor classification
|
||||
case "$desc" in
|
||||
*NVIDIA*) vendor="NVIDIA" ;;
|
||||
*"Advanced Micro Devices"*|*AMD*) vendor="AMD" ;;
|
||||
*"Intel Corporation"*|*Intel*) vendor="Intel" ;;
|
||||
*) vendor="Other" ;;
|
||||
esac
|
||||
|
||||
# Model: strip every known vendor prefix from desc. Order matters —
|
||||
# the longest specific prefix (AMD's "Inc. [AMD/ATI]") must come before
|
||||
# the generic short one.
|
||||
model="$(printf '%s' "$desc" | sed -E '
|
||||
s/^Advanced Micro Devices, Inc\. \[AMD\/ATI\][[:space:]]+//
|
||||
s/^Advanced Micro Devices(, Inc\.)?[[:space:]]+//
|
||||
s/^NVIDIA Corporation[[:space:]]+//
|
||||
s/^Intel Corporation[[:space:]]+//
|
||||
s/[[:space:]]+$//
|
||||
')"
|
||||
# Kernel driver in use (may be empty if module not loaded yet)
|
||||
kernel_driver="$(lspci -nnks "$pci_address" 2>/dev/null | awk -F: '/Kernel driver in use/{sub(/^[ \t]+/,"",$2); print $2; exit}')"
|
||||
# Passthrough eligible if the GPU is bound to vfio-pci OR it's a discrete
|
||||
# secondary GPU (not the primary console). Pragmatic heuristic: discrete
|
||||
# GPUs are usually eligible; iGPUs (Intel HD/UHD, AMD APU iGPUs) usually not
|
||||
# because they drive the host console.
|
||||
passthrough_eligible=false
|
||||
case "$kernel_driver" in
|
||||
vfio-pci) passthrough_eligible=true ;;
|
||||
nvidia|nouveau) passthrough_eligible=true ;; # discrete by definition
|
||||
esac
|
||||
|
||||
# ProxMenux installer for this GPU vendor
|
||||
proxmenux_installer="$(gpu_installer_for "$vendor")"
|
||||
|
||||
# Installed driver version from the managed_installs registry
|
||||
installed_driver_version=""
|
||||
if [[ "$vendor" == "NVIDIA" ]] && [[ -f /usr/local/share/proxmenux/managed_installs.json ]]; then
|
||||
installed_driver_version="$(jq -r '
|
||||
.items[]
|
||||
| select(.removed_at == null and .type == "nvidia_xfree86")
|
||||
| .current_version // ""
|
||||
' /usr/local/share/proxmenux/managed_installs.json 2>/dev/null | head -1)"
|
||||
fi
|
||||
|
||||
gpu_array="$(jq --argjson acc "$gpu_array" \
|
||||
--arg vendor "$vendor" \
|
||||
--arg model "$model" \
|
||||
--arg pci_address "$pci_address" \
|
||||
--arg pci_id "$pci_id" \
|
||||
--arg kernel_driver "$kernel_driver" \
|
||||
--argjson passthrough_eligible "$passthrough_eligible" \
|
||||
--arg proxmenux_installer "$proxmenux_installer" \
|
||||
--arg installed_driver_version "$installed_driver_version" \
|
||||
-n '
|
||||
$acc + [{
|
||||
vendor: $vendor,
|
||||
model: $model,
|
||||
pci_address: $pci_address,
|
||||
pci_id: $pci_id,
|
||||
kernel_driver: (if $kernel_driver == "" then null else $kernel_driver end),
|
||||
passthrough_eligible: $passthrough_eligible,
|
||||
proxmenux_installer: (if $proxmenux_installer == "" then null else $proxmenux_installer end),
|
||||
installed_driver_version: (if $installed_driver_version == "" then null else $installed_driver_version end)
|
||||
}]
|
||||
')"
|
||||
done < <(lspci -nnD 2>/dev/null | grep -E 'VGA compatible|3D controller|Display controller' || true)
|
||||
|
||||
# ── TPUs (Google Coral) ──
|
||||
# PCIe variant: vendor 1ac1 (Global Unichip Corp) is the Coral M.2 / mPCIe.
|
||||
# USB variant: vendor 18d1 product 9302 (Google).
|
||||
tpu_array='[]'
|
||||
|
||||
# PCIe Coral
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
pci_address="$(printf '%s' "$line" | awk '{print $1}')"
|
||||
pci_id="$(printf '%s' "$line" | grep -oE '\[[0-9a-f]{4}:[0-9a-f]{4}\]' | tail -1 | tr -d '[]')"
|
||||
tpu_array="$(jq --argjson acc "$tpu_array" \
|
||||
--arg model "Coral PCIe" \
|
||||
--arg pci_address "$pci_address" \
|
||||
-n '
|
||||
$acc + [{
|
||||
vendor: "Google",
|
||||
model: $model,
|
||||
bus: "PCIe",
|
||||
pci_address: $pci_address,
|
||||
proxmenux_installer: "scripts/gpu_tpu/install_coral.sh",
|
||||
installed_version: null
|
||||
}]
|
||||
')"
|
||||
done < <(lspci -nnD 2>/dev/null | grep -iE '1ac1:|global unichip' || true)
|
||||
|
||||
# USB Coral
|
||||
if command -v lsusb >/dev/null 2>&1; then
|
||||
if lsusb 2>/dev/null | grep -qE '18d1:9302|Google.*Coral'; then
|
||||
tpu_array="$(jq --argjson acc "$tpu_array" \
|
||||
-n '
|
||||
$acc + [{
|
||||
vendor: "Google",
|
||||
model: "Coral USB",
|
||||
bus: "USB",
|
||||
pci_address: null,
|
||||
proxmenux_installer: "scripts/gpu_tpu/install_coral.sh",
|
||||
installed_version: null
|
||||
}]
|
||||
')"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── NICs ──
|
||||
# We want PHYSICAL interfaces (skip lo, veth*, tap*, fwln*, fwbr*, fwpr*).
|
||||
# Also distinguish wired from wireless.
|
||||
nic_array='[]'
|
||||
wireless_array='[]'
|
||||
|
||||
# Map each interface → its bridge by walking /sys/class/net/<bridge>/brif/.
|
||||
# We use bash glob expansion instead of `find -path` because find doesn't
|
||||
# follow the symlinks under /sys cleanly.
|
||||
declare -A bridge_for
|
||||
for brif_dir in /sys/class/net/*/brif; do
|
||||
[[ -d "$brif_dir" ]] || continue
|
||||
bridge="$(basename "$(dirname "$brif_dir")")"
|
||||
for member_link in "$brif_dir"/*; do
|
||||
[[ -e "$member_link" ]] || continue
|
||||
member="$(basename "$member_link")"
|
||||
bridge_for["$member"]="$bridge"
|
||||
done
|
||||
done
|
||||
|
||||
# Iterate over each physical net device
|
||||
for dev_path in /sys/class/net/*; do
|
||||
ifname="$(basename "$dev_path")"
|
||||
case "$ifname" in
|
||||
lo|veth*|tap*|fwln*|fwbr*|fwpr*|vmbr*|bond*) continue ;;
|
||||
esac
|
||||
# Bridges and bonds we record as their own thing; PHY interfaces only here.
|
||||
# Detect virtual interfaces (no device symlink → virtual)
|
||||
[[ ! -e "$dev_path/device" ]] && continue
|
||||
|
||||
mac="$(cat "$dev_path/address" 2>/dev/null || echo "")"
|
||||
[[ -z "$mac" ]] && continue
|
||||
operstate="$(cat "$dev_path/operstate" 2>/dev/null | tr '[:lower:]' '[:upper:]' || echo "UNKNOWN")"
|
||||
case "$operstate" in
|
||||
UP|DOWN) ;;
|
||||
*) operstate="UNKNOWN" ;;
|
||||
esac
|
||||
kernel_driver="$(basename "$(readlink "$dev_path/device/driver" 2>/dev/null || echo "")")"
|
||||
|
||||
# Wireless detection
|
||||
if [[ -d "$dev_path/wireless" ]] || [[ -d "$dev_path/phy80211" ]]; then
|
||||
wireless_array="$(jq --argjson acc "$wireless_array" \
|
||||
--arg ifname "$ifname" \
|
||||
--arg mac "$mac" \
|
||||
-n '$acc + [{ifname: $ifname, mac: $mac}]')"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Bridge membership: which vmbr* contains this NIC?
|
||||
in_bridges_json='[]'
|
||||
if [[ -n "${bridge_for[$ifname]:-}" ]]; then
|
||||
in_bridges_json="$(jq -n --arg b "${bridge_for[$ifname]}" '[$b]')"
|
||||
fi
|
||||
|
||||
nic_array="$(jq --argjson acc "$nic_array" \
|
||||
--arg ifname "$ifname" \
|
||||
--arg mac "$mac" \
|
||||
--arg kernel_driver "$kernel_driver" \
|
||||
--argjson in_bridges "$in_bridges_json" \
|
||||
--arg operstate "$operstate" \
|
||||
-n '
|
||||
$acc + [{
|
||||
ifname: $ifname,
|
||||
mac: $mac,
|
||||
kernel_driver: (if $kernel_driver == "" then null else $kernel_driver end),
|
||||
in_bridges: $in_bridges,
|
||||
operstate: $operstate
|
||||
}]
|
||||
')"
|
||||
done
|
||||
|
||||
# Compose the final object
|
||||
jq -n \
|
||||
--argjson gpu "$gpu_array" \
|
||||
--argjson tpu "$tpu_array" \
|
||||
--argjson nic "$nic_array" \
|
||||
--argjson wireless "$wireless_array" \
|
||||
'{ gpu: $gpu, tpu: $tpu, nic: $nic, wireless: $wireless }'
|
||||
Reference in New Issue
Block a user