Add CPU/RAM and GPU model telemetry
Extend telemetry collection to include GPU model, CPU vendor/model, and RAM speed. misc/api.func: enhance detect_gpu to capture GPU_MODEL and default unknown values; add detect_cpu and detect_ram (dmidecode used for RAM speed), export new globals, and include cpu_*/gpu_model/ram_speed in post_to_api and post_update_to_api payloads. misc/data/service.go: add GPUModel/CPUVendor/CPUModel/RAMSpeed fields to TelemetryIn/Out/StatusUpdate, update PBClient mapping, expand allowed enum values to accept "unknown", sanitize and default empty vendor/passthrough fields to "unknown", and validate new cpu_vendor values. Changes maintain backward compatibility by using "unknown" where data is unavailable.
This commit is contained in:
parent
1dcd83abea
commit
18fa3ec4e9
135
misc/api.func
135
misc/api.func
@ -171,38 +171,97 @@ explain_exit_code() {
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# detect_gpu()
|
# detect_gpu()
|
||||||
#
|
#
|
||||||
# - Detects GPU vendor and passthrough type
|
# - Detects GPU vendor, model, and passthrough type
|
||||||
# - Sets GPU_VENDOR and GPU_PASSTHROUGH globals
|
# - Sets GPU_VENDOR, GPU_MODEL, and GPU_PASSTHROUGH globals
|
||||||
# - Used for GPU analytics
|
# - Used for GPU analytics
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
detect_gpu() {
|
detect_gpu() {
|
||||||
GPU_VENDOR=""
|
GPU_VENDOR="unknown"
|
||||||
GPU_PASSTHROUGH="none"
|
GPU_MODEL=""
|
||||||
|
GPU_PASSTHROUGH="unknown"
|
||||||
|
|
||||||
# Detect Intel GPU
|
local gpu_line
|
||||||
if lspci 2>/dev/null | grep -qi "VGA.*Intel"; then
|
gpu_line=$(lspci 2>/dev/null | grep -iE "VGA|3D|Display" | head -1)
|
||||||
|
|
||||||
|
if [[ -n "$gpu_line" ]]; then
|
||||||
|
# Extract model: everything after the colon, clean up
|
||||||
|
GPU_MODEL=$(echo "$gpu_line" | sed 's/.*: //' | sed 's/ (rev .*)$//' | cut -c1-64)
|
||||||
|
|
||||||
|
# Detect vendor and passthrough type
|
||||||
|
if echo "$gpu_line" | grep -qi "Intel"; then
|
||||||
GPU_VENDOR="intel"
|
GPU_VENDOR="intel"
|
||||||
GPU_PASSTHROUGH="igpu"
|
GPU_PASSTHROUGH="igpu"
|
||||||
fi
|
elif echo "$gpu_line" | grep -qi "AMD\|ATI"; then
|
||||||
|
|
||||||
# Detect AMD GPU
|
|
||||||
if lspci 2>/dev/null | grep -qi "VGA.*AMD\|VGA.*ATI"; then
|
|
||||||
GPU_VENDOR="amd"
|
GPU_VENDOR="amd"
|
||||||
# Check if discrete
|
if echo "$gpu_line" | grep -qi "Radeon RX\|Radeon Pro"; then
|
||||||
if lspci 2>/dev/null | grep -qi "AMD.*Radeon"; then
|
|
||||||
GPU_PASSTHROUGH="dgpu"
|
GPU_PASSTHROUGH="dgpu"
|
||||||
else
|
else
|
||||||
GPU_PASSTHROUGH="igpu"
|
GPU_PASSTHROUGH="igpu"
|
||||||
fi
|
fi
|
||||||
fi
|
elif echo "$gpu_line" | grep -qi "NVIDIA"; then
|
||||||
|
|
||||||
# Detect NVIDIA GPU
|
|
||||||
if lspci 2>/dev/null | grep -qi "VGA.*NVIDIA\|3D.*NVIDIA"; then
|
|
||||||
GPU_VENDOR="nvidia"
|
GPU_VENDOR="nvidia"
|
||||||
GPU_PASSTHROUGH="dgpu"
|
GPU_PASSTHROUGH="dgpu"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
export GPU_VENDOR GPU_PASSTHROUGH
|
export GPU_VENDOR GPU_MODEL GPU_PASSTHROUGH
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# detect_cpu()
|
||||||
|
#
|
||||||
|
# - Detects CPU vendor and model
|
||||||
|
# - Sets CPU_VENDOR (intel/amd/arm/unknown) and CPU_MODEL globals
|
||||||
|
# - Used for CPU analytics
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
detect_cpu() {
|
||||||
|
CPU_VENDOR="unknown"
|
||||||
|
CPU_MODEL=""
|
||||||
|
|
||||||
|
if [[ -f /proc/cpuinfo ]]; then
|
||||||
|
local vendor_id
|
||||||
|
vendor_id=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | tr -d ' ')
|
||||||
|
|
||||||
|
case "$vendor_id" in
|
||||||
|
GenuineIntel) CPU_VENDOR="intel" ;;
|
||||||
|
AuthenticAMD) CPU_VENDOR="amd" ;;
|
||||||
|
*)
|
||||||
|
# ARM doesn't have vendor_id, check for CPU implementer
|
||||||
|
if grep -qi "CPU implementer" /proc/cpuinfo 2>/dev/null; then
|
||||||
|
CPU_VENDOR="arm"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Extract model name and clean it up
|
||||||
|
CPU_MODEL=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | cut -d: -f2 | sed 's/^ *//' | sed 's/(R)//g' | sed 's/(TM)//g' | sed 's/ */ /g' | cut -c1-64)
|
||||||
|
fi
|
||||||
|
|
||||||
|
export CPU_VENDOR CPU_MODEL
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# detect_ram()
|
||||||
|
#
|
||||||
|
# - Detects RAM speed using dmidecode
|
||||||
|
# - Sets RAM_SPEED global (e.g., "4800" for DDR5-4800)
|
||||||
|
# - Requires root access for dmidecode
|
||||||
|
# - Returns empty if not available
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
detect_ram() {
|
||||||
|
RAM_SPEED=""
|
||||||
|
|
||||||
|
if command -v dmidecode &>/dev/null; then
|
||||||
|
# Get configured memory speed (actual running speed)
|
||||||
|
RAM_SPEED=$(dmidecode -t memory 2>/dev/null | grep -m1 "Configured Memory Speed:" | grep -oE "[0-9]+" | head -1)
|
||||||
|
|
||||||
|
# Fallback to Speed: if Configured not available
|
||||||
|
if [[ -z "$RAM_SPEED" ]]; then
|
||||||
|
RAM_SPEED=$(dmidecode -t memory 2>/dev/null | grep -m1 "Speed:" | grep -oE "[0-9]+" | head -1)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
export RAM_SPEED
|
||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@ -256,8 +315,22 @@ post_to_api() {
|
|||||||
if [[ -z "${GPU_VENDOR:-}" ]]; then
|
if [[ -z "${GPU_VENDOR:-}" ]]; then
|
||||||
detect_gpu
|
detect_gpu
|
||||||
fi
|
fi
|
||||||
local gpu_vendor="${GPU_VENDOR:-}"
|
local gpu_vendor="${GPU_VENDOR:-unknown}"
|
||||||
local gpu_passthrough="${GPU_PASSTHROUGH:-none}"
|
local gpu_model="${GPU_MODEL:-}"
|
||||||
|
local gpu_passthrough="${GPU_PASSTHROUGH:-unknown}"
|
||||||
|
|
||||||
|
# Detect CPU if not already set
|
||||||
|
if [[ -z "${CPU_VENDOR:-}" ]]; then
|
||||||
|
detect_cpu
|
||||||
|
fi
|
||||||
|
local cpu_vendor="${CPU_VENDOR:-unknown}"
|
||||||
|
local cpu_model="${CPU_MODEL:-}"
|
||||||
|
|
||||||
|
# Detect RAM if not already set
|
||||||
|
if [[ -z "${RAM_SPEED:-}" ]]; then
|
||||||
|
detect_ram
|
||||||
|
fi
|
||||||
|
local ram_speed="${RAM_SPEED:-}"
|
||||||
|
|
||||||
local JSON_PAYLOAD
|
local JSON_PAYLOAD
|
||||||
JSON_PAYLOAD=$(
|
JSON_PAYLOAD=$(
|
||||||
@ -275,8 +348,12 @@ post_to_api() {
|
|||||||
"os_version": "${var_version:-}",
|
"os_version": "${var_version:-}",
|
||||||
"pve_version": "${pve_version}",
|
"pve_version": "${pve_version}",
|
||||||
"method": "${METHOD:-default}",
|
"method": "${METHOD:-default}",
|
||||||
|
"cpu_vendor": "${cpu_vendor}",
|
||||||
|
"cpu_model": "${cpu_model}",
|
||||||
"gpu_vendor": "${gpu_vendor}",
|
"gpu_vendor": "${gpu_vendor}",
|
||||||
"gpu_passthrough": "${gpu_passthrough}"
|
"gpu_model": "${gpu_model}",
|
||||||
|
"gpu_passthrough": "${gpu_passthrough}",
|
||||||
|
"ram_speed": "${ram_speed}"
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
@ -384,8 +461,16 @@ post_update_to_api() {
|
|||||||
local exit_code=0 error="" pb_status error_category=""
|
local exit_code=0 error="" pb_status error_category=""
|
||||||
|
|
||||||
# Get GPU info (if detected)
|
# Get GPU info (if detected)
|
||||||
local gpu_vendor="${GPU_VENDOR:-}"
|
local gpu_vendor="${GPU_VENDOR:-unknown}"
|
||||||
local gpu_passthrough="${GPU_PASSTHROUGH:-}"
|
local gpu_model="${GPU_MODEL:-}"
|
||||||
|
local gpu_passthrough="${GPU_PASSTHROUGH:-unknown}"
|
||||||
|
|
||||||
|
# Get CPU info (if detected)
|
||||||
|
local cpu_vendor="${CPU_VENDOR:-unknown}"
|
||||||
|
local cpu_model="${CPU_MODEL:-}"
|
||||||
|
|
||||||
|
# Get RAM info (if detected)
|
||||||
|
local ram_speed="${RAM_SPEED:-}"
|
||||||
|
|
||||||
# Map status to telemetry values: installing, success, failed, unknown
|
# Map status to telemetry values: installing, success, failed, unknown
|
||||||
case "$status" in
|
case "$status" in
|
||||||
@ -449,8 +534,12 @@ post_update_to_api() {
|
|||||||
"error": "${error}",
|
"error": "${error}",
|
||||||
"error_category": "${error_category}",
|
"error_category": "${error_category}",
|
||||||
"install_duration": ${duration},
|
"install_duration": ${duration},
|
||||||
|
"cpu_vendor": "${cpu_vendor}",
|
||||||
|
"cpu_model": "${cpu_model}",
|
||||||
"gpu_vendor": "${gpu_vendor}",
|
"gpu_vendor": "${gpu_vendor}",
|
||||||
"gpu_passthrough": "${gpu_passthrough}"
|
"gpu_model": "${gpu_model}",
|
||||||
|
"gpu_passthrough": "${gpu_passthrough}",
|
||||||
|
"ram_speed": "${ram_speed}"
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
|
|||||||
@ -86,8 +86,16 @@ type TelemetryIn struct {
|
|||||||
|
|
||||||
// GPU Passthrough stats
|
// GPU Passthrough stats
|
||||||
GPUVendor string `json:"gpu_vendor,omitempty"` // "intel", "amd", "nvidia"
|
GPUVendor string `json:"gpu_vendor,omitempty"` // "intel", "amd", "nvidia"
|
||||||
|
GPUModel string `json:"gpu_model,omitempty"` // e.g., "Intel Arc Graphics"
|
||||||
GPUPassthrough string `json:"gpu_passthrough,omitempty"` // "igpu", "dgpu", "vgpu", "none"
|
GPUPassthrough string `json:"gpu_passthrough,omitempty"` // "igpu", "dgpu", "vgpu", "none"
|
||||||
|
|
||||||
|
// CPU stats
|
||||||
|
CPUVendor string `json:"cpu_vendor,omitempty"` // "intel", "amd", "arm"
|
||||||
|
CPUModel string `json:"cpu_model,omitempty"` // e.g., "Intel Core Ultra 7 155H"
|
||||||
|
|
||||||
|
// RAM stats
|
||||||
|
RAMSpeed string `json:"ram_speed,omitempty"` // e.g., "4800" (MT/s)
|
||||||
|
|
||||||
// Performance metrics
|
// Performance metrics
|
||||||
InstallDuration int `json:"install_duration,omitempty"` // Seconds
|
InstallDuration int `json:"install_duration,omitempty"` // Seconds
|
||||||
|
|
||||||
@ -114,7 +122,11 @@ type TelemetryOut struct {
|
|||||||
|
|
||||||
// Extended fields
|
// Extended fields
|
||||||
GPUVendor string `json:"gpu_vendor,omitempty"`
|
GPUVendor string `json:"gpu_vendor,omitempty"`
|
||||||
|
GPUModel string `json:"gpu_model,omitempty"`
|
||||||
GPUPassthrough string `json:"gpu_passthrough,omitempty"`
|
GPUPassthrough string `json:"gpu_passthrough,omitempty"`
|
||||||
|
CPUVendor string `json:"cpu_vendor,omitempty"`
|
||||||
|
CPUModel string `json:"cpu_model,omitempty"`
|
||||||
|
RAMSpeed string `json:"ram_speed,omitempty"`
|
||||||
InstallDuration int `json:"install_duration,omitempty"`
|
InstallDuration int `json:"install_duration,omitempty"`
|
||||||
ErrorCategory string `json:"error_category,omitempty"`
|
ErrorCategory string `json:"error_category,omitempty"`
|
||||||
}
|
}
|
||||||
@ -127,7 +139,11 @@ type TelemetryStatusUpdate struct {
|
|||||||
InstallDuration int `json:"install_duration,omitempty"`
|
InstallDuration int `json:"install_duration,omitempty"`
|
||||||
ErrorCategory string `json:"error_category,omitempty"`
|
ErrorCategory string `json:"error_category,omitempty"`
|
||||||
GPUVendor string `json:"gpu_vendor,omitempty"`
|
GPUVendor string `json:"gpu_vendor,omitempty"`
|
||||||
|
GPUModel string `json:"gpu_model,omitempty"`
|
||||||
GPUPassthrough string `json:"gpu_passthrough,omitempty"`
|
GPUPassthrough string `json:"gpu_passthrough,omitempty"`
|
||||||
|
CPUVendor string `json:"cpu_vendor,omitempty"`
|
||||||
|
CPUModel string `json:"cpu_model,omitempty"`
|
||||||
|
RAMSpeed string `json:"ram_speed,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PBClient struct {
|
type PBClient struct {
|
||||||
@ -380,7 +396,11 @@ func (p *PBClient) UpsertTelemetry(ctx context.Context, payload TelemetryOut) er
|
|||||||
InstallDuration: payload.InstallDuration,
|
InstallDuration: payload.InstallDuration,
|
||||||
ErrorCategory: payload.ErrorCategory,
|
ErrorCategory: payload.ErrorCategory,
|
||||||
GPUVendor: payload.GPUVendor,
|
GPUVendor: payload.GPUVendor,
|
||||||
|
GPUModel: payload.GPUModel,
|
||||||
GPUPassthrough: payload.GPUPassthrough,
|
GPUPassthrough: payload.GPUPassthrough,
|
||||||
|
CPUVendor: payload.CPUVendor,
|
||||||
|
CPUModel: payload.CPUModel,
|
||||||
|
RAMSpeed: payload.RAMSpeed,
|
||||||
}
|
}
|
||||||
return p.UpdateTelemetryStatus(ctx, recordID, update)
|
return p.UpdateTelemetryStatus(ctx, recordID, update)
|
||||||
}
|
}
|
||||||
@ -548,10 +568,13 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allowed values for 'gpu_vendor' field
|
// Allowed values for 'gpu_vendor' field
|
||||||
allowedGPUVendor = map[string]bool{"intel": true, "amd": true, "nvidia": true, "": true}
|
allowedGPUVendor = map[string]bool{"intel": true, "amd": true, "nvidia": true, "unknown": true, "": true}
|
||||||
|
|
||||||
// Allowed values for 'gpu_passthrough' field
|
// Allowed values for 'gpu_passthrough' field
|
||||||
allowedGPUPassthrough = map[string]bool{"igpu": true, "dgpu": true, "vgpu": true, "none": true, "": true}
|
allowedGPUPassthrough = map[string]bool{"igpu": true, "dgpu": true, "vgpu": true, "none": true, "unknown": true, "": true}
|
||||||
|
|
||||||
|
// Allowed values for 'cpu_vendor' field
|
||||||
|
allowedCPUVendor = map[string]bool{"intel": true, "amd": true, "arm": true, "apple": true, "qualcomm": true, "unknown": true, "": true}
|
||||||
|
|
||||||
// Allowed values for 'error_category' field
|
// Allowed values for 'error_category' field
|
||||||
allowedErrorCategory = map[string]bool{
|
allowedErrorCategory = map[string]bool{
|
||||||
@ -587,9 +610,24 @@ func validate(in *TelemetryIn) error {
|
|||||||
|
|
||||||
// Sanitize extended fields
|
// Sanitize extended fields
|
||||||
in.GPUVendor = strings.ToLower(sanitizeShort(in.GPUVendor, 16))
|
in.GPUVendor = strings.ToLower(sanitizeShort(in.GPUVendor, 16))
|
||||||
|
in.GPUModel = sanitizeShort(in.GPUModel, 64)
|
||||||
in.GPUPassthrough = strings.ToLower(sanitizeShort(in.GPUPassthrough, 16))
|
in.GPUPassthrough = strings.ToLower(sanitizeShort(in.GPUPassthrough, 16))
|
||||||
|
in.CPUVendor = strings.ToLower(sanitizeShort(in.CPUVendor, 16))
|
||||||
|
in.CPUModel = sanitizeShort(in.CPUModel, 64)
|
||||||
|
in.RAMSpeed = sanitizeShort(in.RAMSpeed, 16)
|
||||||
in.ErrorCategory = strings.ToLower(sanitizeShort(in.ErrorCategory, 32))
|
in.ErrorCategory = strings.ToLower(sanitizeShort(in.ErrorCategory, 32))
|
||||||
|
|
||||||
|
// Default empty values to "unknown" for consistency
|
||||||
|
if in.GPUVendor == "" {
|
||||||
|
in.GPUVendor = "unknown"
|
||||||
|
}
|
||||||
|
if in.GPUPassthrough == "" {
|
||||||
|
in.GPUPassthrough = "unknown"
|
||||||
|
}
|
||||||
|
if in.CPUVendor == "" {
|
||||||
|
in.CPUVendor = "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
// IMPORTANT: "error" must be short and not contain identifiers/logs
|
// IMPORTANT: "error" must be short and not contain identifiers/logs
|
||||||
in.Error = sanitizeShort(in.Error, 120)
|
in.Error = sanitizeShort(in.Error, 120)
|
||||||
|
|
||||||
@ -613,10 +651,13 @@ func validate(in *TelemetryIn) error {
|
|||||||
|
|
||||||
// Validate new enum fields
|
// Validate new enum fields
|
||||||
if !allowedGPUVendor[in.GPUVendor] {
|
if !allowedGPUVendor[in.GPUVendor] {
|
||||||
return errors.New("invalid gpu_vendor (must be 'intel', 'amd', 'nvidia', or empty)")
|
return errors.New("invalid gpu_vendor (must be 'intel', 'amd', 'nvidia', 'unknown')")
|
||||||
}
|
}
|
||||||
if !allowedGPUPassthrough[in.GPUPassthrough] {
|
if !allowedGPUPassthrough[in.GPUPassthrough] {
|
||||||
return errors.New("invalid gpu_passthrough (must be 'igpu', 'dgpu', 'vgpu', 'none', or empty)")
|
return errors.New("invalid gpu_passthrough (must be 'igpu', 'dgpu', 'vgpu', 'none', 'unknown')")
|
||||||
|
}
|
||||||
|
if !allowedCPUVendor[in.CPUVendor] {
|
||||||
|
return errors.New("invalid cpu_vendor (must be 'intel', 'amd', 'arm', 'apple', 'qualcomm', 'unknown')")
|
||||||
}
|
}
|
||||||
if !allowedErrorCategory[in.ErrorCategory] {
|
if !allowedErrorCategory[in.ErrorCategory] {
|
||||||
return errors.New("invalid error_category")
|
return errors.New("invalid error_category")
|
||||||
@ -993,7 +1034,11 @@ func main() {
|
|||||||
Error: in.Error,
|
Error: in.Error,
|
||||||
ExitCode: in.ExitCode,
|
ExitCode: in.ExitCode,
|
||||||
GPUVendor: in.GPUVendor,
|
GPUVendor: in.GPUVendor,
|
||||||
|
GPUModel: in.GPUModel,
|
||||||
GPUPassthrough: in.GPUPassthrough,
|
GPUPassthrough: in.GPUPassthrough,
|
||||||
|
CPUVendor: in.CPUVendor,
|
||||||
|
CPUModel: in.CPUModel,
|
||||||
|
RAMSpeed: in.RAMSpeed,
|
||||||
InstallDuration: in.InstallDuration,
|
InstallDuration: in.InstallDuration,
|
||||||
ErrorCategory: in.ErrorCategory,
|
ErrorCategory: in.ErrorCategory,
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user