Files
serv_benchmark/scripts/bench.sh
2025-12-08 05:42:52 +01:00

1071 lines
34 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
#
# Linux BenchTools - Client Benchmark Script
# Version: 1.1.0
#
# Collecte les informations hardware, exécute les benchmarks
# puis envoie les résultats au serveur backend (payload JSON).
#
set -e
#=========================================================
# Version / variables globales
#=========================================================
BENCH_SCRIPT_VERSION="1.1.0"
# Couleurs
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
TOTAL_STEPS=8
CURRENT_STEP=0
# Paramètres réseau / API (fixés)
SERVER_URL="10.0.1.97:8007" # ajouter le port ou le schéma dans send_benchmark_payload si besoin
API_TOKEN="29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a"
IPERF_SERVER="10.0.1.97"
# Mode DEBUG
# Mettre à 1 pour afficher le payload JSON complet avant envoi
# Mettre à 0 pour désactiver le debug
DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-1}" # Par défaut: 1 (activé)
# Peut être désactivé via: DEBUG_PAYLOAD=0 bash bench.sh
# Forcer locale en anglais pour parsing
export LC_ALL=C
# Ajouter /usr/sbin au PATH pour dmidecode, smartctl, ethtool
export PATH="/usr/sbin:/sbin:$PATH"
# Variables JSON globales
SYSTEM_INFO="null"
CPU_INFO="null"
RAM_INFO="null"
GPU_INFO="null"
MOTHERBOARD_INFO="null"
STORAGE_INFO="[]"
NETWORK_INFO="[]"
BENCHMARK_RESULTS="null"
#=========================================================
# Affichage / logs
#=========================================================
log_step() {
CURRENT_STEP=$((CURRENT_STEP + 1))
echo -e "${BLUE}[${CURRENT_STEP}/${TOTAL_STEPS}]${NC} $1"
}
log_info() {
echo -e " ${GREEN}${NC} $1"
}
log_warn() {
echo -e " ${YELLOW}${NC} $1"
}
log_error() {
echo -e " ${RED}${NC} $1"
}
#=========================================================
# sudo + dépendances
#=========================================================
check_sudo() {
echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
echo -e " Linux BenchTools - Client Benchmark Script"
echo -e " Version ${BENCH_SCRIPT_VERSION}"
echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
echo ""
echo "Ce script nécessite les permissions sudo pour :"
echo " - dmidecode (infos RAM, carte mère, BIOS)"
echo " - smartctl (infos disques)"
echo " - ethtool (vitesse réseau)"
echo ""
if ! sudo -v; then
log_error "Permissions sudo requises. Relance le script avec sudo."
exit 1
fi
# Garder sudo actif
while true; do sudo -n true; sleep 50; kill -0 "$$" || exit; done 2>/dev/null &
log_info "Permissions sudo OK"
echo ""
}
check_dependencies() {
local missing=()
local bench_missing=()
echo -e "${BLUE}Vérification des dépendances...${NC}"
for tool in curl jq lscpu free lsblk ip bc; do
command -v "$tool" &>/dev/null || missing+=("$tool")
done
for tool in sysbench fio iperf3; do
command -v "$tool" &>/dev/null || bench_missing+=("$tool")
done
if [[ ${#missing[@]} -eq 0 && ${#bench_missing[@]} -eq 0 ]]; then
log_info "Toutes les dépendances de base sont présentes"
log_info "Vérification des dépendances terminée"
echo ""
return
fi
echo ""
if [[ ${#missing[@]} -gt 0 ]]; then
log_warn "Outils systèmes manquants: ${missing[*]}"
fi
if [[ ${#bench_missing[@]} -gt 0 ]]; then
log_warn "Outils de benchmark manquants: ${bench_missing[*]}"
fi
if ! command -v apt-get &>/dev/null; then
log_warn "apt-get non disponible, installation automatique impossible."
log_info "Vérification des dépendances terminée"
echo ""
return
fi
declare -A pkg_map=(
[curl]="curl"
[jq]="jq"
[lscpu]="util-linux"
[free]="procps"
[lsblk]="util-linux"
[ip]="iproute2"
[bc]="bc"
[sysbench]="sysbench"
[fio]="fio"
[iperf3]="iperf3"
)
local to_install=()
for t in "${missing[@]}" "${bench_missing[@]}"; do
[[ -n "${pkg_map[$t]}" ]] && to_install+=("${pkg_map[$t]}")
done
if [[ ${#to_install[@]} -gt 0 ]]; then
mapfile -t to_install < <(printf '%s\n' "${to_install[@]}" | sort -u)
echo -e "${BLUE}Installation automatique des paquets manquants...${NC}"
echo " Paquets: ${to_install[*]}"
echo ""
echo "► apt-get update..."
if ! sudo apt-get update -qq 2>&1 | grep -v "Policy will reject signature"; then
log_warn "Warning durant apt-get update (on continue)"
fi
echo "► apt-get install..."
if sudo apt-get install -y "${to_install[@]}"; then
log_info "Installation des dépendances OK"
else
log_error "Échec de l'installation des dépendances"
exit 1
fi
fi
log_info "Vérification des dépendances terminée"
echo ""
}
#=========================================================
# Utilitaire bc robuste
#=========================================================
safe_bc() {
local expr="$1"
local out
out=$(echo "$expr" | bc 2>/dev/null) || out="0"
echo "$out"
}
#=========================================================
# Étape 1 : Système
#=========================================================
collect_system_info() {
log_step "Collecte des informations système de base"
local hostname os_name os_version kernel arch
hostname=$(hostname)
os_name=$(grep '^ID=' /etc/os-release | cut -d= -f2 | tr -d '"')
os_version=$(grep '^VERSION=' /etc/os-release | cut -d= -f2 | tr -d '"')
kernel=$(uname -r)
arch=$(uname -m)
SYSTEM_INFO=$(jq -n \
--arg hostname "$hostname" \
--arg os_name "$os_name" \
--arg os_version "$os_version" \
--arg kernel "$kernel" \
--arg arch "$arch" \
'{
hostname: $hostname,
os: {
name: $os_name,
version: $os_version,
kernel_version: $kernel,
architecture: $arch
}
}')
log_info "Hostname: $hostname"
log_info "OS: $os_name $os_version"
log_info "Kernel: $kernel"
echo ""
}
#=========================================================
# Étape 2 : CPU
#=========================================================
collect_cpu_info() {
log_step "Collecte des informations CPU"
local vendor model cores threads
vendor=$(lscpu | awk -F: '/Vendor ID/ {gsub(/^[ \t]+/,"",$2); print $2}')
model=$(lscpu | awk -F: '/Model name/ {gsub(/^[ \t]+/,"",$2); print $2}')
cores=$(lscpu | awk -F: '/^CPU\(s\)/ {gsub(/^[ \t]+/,"",$2); print $2}')
threads=$(nproc)
local cpu_mhz cpu_max_mhz base_freq_ghz max_freq_ghz
cpu_mhz=$(lscpu | awk -F: '/CPU MHz/ {gsub(/^[ \t]+/,"",$2); print $2}')
cpu_max_mhz=$(lscpu | awk -F: '/CPU max MHz/ {gsub(/^[ \t]+/,"",$2); print $2}')
base_freq_ghz="null"
max_freq_ghz="null"
if [[ -n "$cpu_mhz" ]]; then
base_freq_ghz=$(safe_bc "scale=2; $cpu_mhz / 1000")
fi
if [[ -n "$cpu_max_mhz" ]]; then
max_freq_ghz=$(safe_bc "scale=2; $cpu_max_mhz / 1000")
fi
local cache_l1_kb cache_l2_kb cache_l3_kb
cache_l1_kb=$(lscpu | awk -F: '/L1d cache/ {gsub(/[^0-9]/,"",$2); print $2}')
cache_l2_kb=$(lscpu | awk -F: '/L2 cache/ {gsub(/[^0-9]/,"",$2); print $2}')
cache_l3_kb=$(lscpu | awk -F: '/L3 cache/ {gsub(/[^0-9]/,"",$2); print $2}')
local flags
flags=$(lscpu | awk -F: '/Flags/ {gsub(/^[ \t]+/,"",$2); print $2}')
local flags_array
flags_array=$(printf '%s\n' $flags | jq -R . | jq -s .)
# Important: on passe les numériques en --arg (string) puis on cast dans jq
CPU_INFO=$(jq -n \
--arg vendor "$vendor" \
--arg model "$model" \
--arg cores "$cores" \
--arg threads "$threads" \
--arg base_freq "$base_freq_ghz" \
--arg max_freq "$max_freq_ghz" \
--arg l1 "$cache_l1_kb" \
--arg l2 "$cache_l2_kb" \
--arg l3 "$cache_l3_kb" \
--argjson flags "$flags_array" \
'{
vendor: $vendor,
model: $model,
cores: ($cores | tonumber? // 0),
threads: ($threads | tonumber? // 0),
base_freq_ghz: (if $base_freq == "null" or $base_freq == "" then null else ($base_freq | tonumber?) end),
max_freq_ghz: (if $max_freq == "null" or $max_freq == "" then null else ($max_freq | tonumber?) end),
cache_l1_kb: ($l1 | tonumber? // 0),
cache_l2_kb: ($l2 | tonumber? // 0),
cache_l3_kb: ($l3 | tonumber? // 0),
flags: $flags
}')
log_info "CPU: $model"
log_info "Cores: ${cores:-0}, Threads: ${threads:-0}"
echo ""
}
#=========================================================
# Étape 3 : RAM
#=========================================================
collect_ram_info() {
log_step "Collecte des informations RAM"
local mem_total_kb mem_used_kb mem_free_kb mem_shared_kb
mem_total_kb=$(free -k | awk '/^Mem:/ {print $2}')
mem_used_kb=$(free -k | awk '/^Mem:/ {print $3}')
mem_free_kb=$(free -k | awk '/^Mem:/ {print $4}')
mem_shared_kb=$(free -k | awk '/^Mem:/ {print $5}')
local total_mb used_mb free_mb shared_mb
total_mb=$((mem_total_kb / 1024))
used_mb=$((mem_used_kb / 1024))
free_mb=$((mem_free_kb / 1024))
shared_mb=$((mem_shared_kb / 1024))
local slots_total=0 slots_used=0 ecc=false
local dimm_layout="[]"
if command -v dmidecode &>/dev/null; then
log_info "[DEBUG] Vérification dmidecode..."
if sudo dmidecode -t 16 &>/dev/null; then
log_info "[DEBUG] dmidecode trouvé: $(command -v dmidecode)"
else
log_warn "dmidecode accessible mais aucune info DMI"
fi
local slots
slots=$(sudo dmidecode -t 16 2>/dev/null | awk -F: '/Number Of Devices/ {gsub(/^[ \t]+/,"",$2); print $2}' | head -1)
[[ -n "$slots" ]] && slots_total="$slots"
if sudo dmidecode -t memory 2>/dev/null | grep -q 'Error Correction Type'; then
if sudo dmidecode -t memory 2>/dev/null | grep 'Error Correction Type' | grep -q 'None'; then
ecc=false
else
ecc=true
fi
fi
local dimm_data
dimm_data=$(sudo dmidecode -t 17 | grep -E 'Locator:|Size:|Type:|Speed:|Manufacturer:' | \
awk '
/Locator:/ && !/Bank/ { slot=$2; gsub(/_/, "", slot) }
/Size:/ && /[0-9]+ [GM]B/ {
size=$2;
if ($3 == "GB") size=size*1024;
if ($3 == "MB") size=size;
}
/Type:/ && !/Detail/ { type=$2 }
/Speed:/ && /MHz/ { speed=$2 }
/Manufacturer:/ {
manu=$2;
printf "%s;%s;%s;%s;%s\n", slot, size, type, speed, manu
}')
if [[ -n "$dimm_data" ]]; then
while IFS=';' read -r slot size type speed manu; do
[[ -z "$slot" ]] && continue
slots_used=$((slots_used + 1))
local entry
entry=$(jq -n \
--arg slot "$slot" \
--arg size_mb "$size" \
--arg type "$type" \
--arg speed_mhz "$speed" \
--arg manu "$manu" \
'{
slot: $slot,
size_mb: ($size_mb | tonumber? // 0),
type: $type,
speed_mhz: ($speed_mhz | tonumber? // 0),
manufacturer: $manu
}')
dimm_layout=$(echo "$dimm_layout" | jq --argjson e "$entry" '. + [$e]')
done <<< "$dimm_data"
fi
else
log_warn "dmidecode non disponible - layout RAM détaillé ignoré"
fi
RAM_INFO=$(jq -n \
--arg total_mb "$total_mb" \
--arg used_mb "$used_mb" \
--arg free_mb "$free_mb" \
--arg shared_mb "$shared_mb" \
--arg slots_total "$slots_total" \
--arg slots_used "$slots_used" \
--arg ecc_str "$([[ "$ecc" = true ]] && echo true || echo false)" \
--argjson layout "$dimm_layout" \
'{
total_mb: ($total_mb | tonumber? // 0),
used_mb: ($used_mb | tonumber? // 0),
free_mb: ($free_mb | tonumber? // 0),
shared_mb: ($shared_mb | tonumber? // 0),
slots_total: ($slots_total | tonumber? // 0),
slots_used: ($slots_used | tonumber? // 0),
ecc: (if $ecc_str == "true" then true else false end),
layout: $layout
}')
log_info "RAM Total: ${total_mb}MB (Utilisée: ${used_mb}MB, Libre: ${free_mb}MB)"
log_info "Slots: ${slots_used}/${slots_total}"
echo ""
}
#=========================================================
# Étape 4 : GPU / Carte mère
#=========================================================
collect_hardware_info() {
log_step "Collecte GPU, Carte mère, BIOS"
local gpu_vendor="null"
local gpu_model="null"
local gpu_vram="null"
if command -v lspci &>/dev/null; then
local gpu_line
gpu_line=$(lspci | grep -iE 'vga|3d' | head -1)
if [[ -n "$gpu_line" ]]; then
gpu_model=$(echo "$gpu_line" | sed 's/.*: //')
if echo "$gpu_line" | grep -qi 'nvidia'; then
gpu_vendor="NVIDIA"
elif echo "$gpu_line" | grep -qi 'amd'; then
gpu_vendor="AMD"
elif echo "$gpu_line" | grep -qi 'intel'; then
gpu_vendor="Intel"
else
gpu_vendor="Unknown"
fi
fi
fi
GPU_INFO=$(jq -n \
--arg vendor "$gpu_vendor" \
--arg model "$gpu_model" \
--arg vram "$gpu_vram" \
'{
vendor: $vendor,
model: $model,
vram_mb: (if $vram == "null" or $vram == "" then null else ($vram | tonumber?) end)
}')
if [[ "$gpu_model" != "null" ]]; then
log_info "GPU: $gpu_model"
else
log_warn "GPU non détecté"
fi
local mb_manufacturer="Unknown" mb_model="Unknown"
local bios_vendor="Unknown" bios_version="Unknown" bios_date="Unknown"
if command -v dmidecode &>/dev/null; then
mb_manufacturer=$(sudo dmidecode -s baseboard-manufacturer 2>/dev/null || echo "Unknown")
mb_model=$(sudo dmidecode -s baseboard-product-name 2>/dev/null || echo "Unknown")
bios_vendor=$(sudo dmidecode -s bios-vendor 2>/dev/null || echo "Unknown")
bios_version=$(sudo dmidecode -s bios-version 2>/dev/null || echo "Unknown")
bios_date=$(sudo dmidecode -s bios-release-date 2>/dev/null || echo "Unknown")
fi
MOTHERBOARD_INFO=$(jq -n \
--arg manu "$mb_manufacturer" \
--arg model "$mb_model" \
--arg bvend "$bios_vendor" \
--arg bver "$bios_version" \
--arg bdate "$bios_date" \
'{
manufacturer: $manu,
model: $model,
bios_vendor: $bvend,
bios_version: $bver,
bios_date: $bdate
}')
log_info "Motherboard: $mb_manufacturer $mb_model"
log_info "BIOS: $bios_version ($bios_date)"
echo ""
}
#=========================================================
# Étape 5 : Stockage
#=========================================================
collect_storage_info() {
log_step "Collecte des informations de stockage"
local storage_array="[]"
local disks
disks=$(lsblk -d -n -o NAME,TYPE | awk '$2=="disk"{print $1}')
for d in $disks; do
local size_h size_gb rota tran
size_h=$(lsblk -d -n -o SIZE "/dev/$d")
rota=$(lsblk -d -n -o ROTA "/dev/$d")
tran=$(lsblk -d -n -o TRAN "/dev/$d")
local disk_type="unknown"
[[ "$rota" = "0" ]] && disk_type="ssd" || disk_type="hdd"
local interface="unknown"
[[ -n "$tran" ]] && interface="$tran"
local model="Unknown" serial="Unknown"
if command -v smartctl &>/dev/null; then
if sudo smartctl -i "/dev/$d" &>/dev/null; then
local s
s=$(sudo smartctl -i "/dev/$d" 2>/dev/null)
model=$(echo "$s" | awk -F: '/Device Model|Model Number/ {gsub(/^[ \t]+/,"",$2); print $2}' | head -1)
serial=$(echo "$s" | awk -F: '/Serial Number/ {gsub(/^[ \t]+/,"",$2); print $2}' | head -1)
fi
fi
size_gb=$(echo "$size_h" | sed 's/[^0-9.]//g')
local disk_json
disk_json=$(jq -n \
--arg device "$d" \
--arg model "$model" \
--arg size "$size_gb" \
--arg type "$disk_type" \
--arg interface "$interface" \
--arg serial "$serial" \
'{
device: $device,
model: $model,
size_gb: $size,
type: $type,
interface: $interface,
serial: $serial
}')
storage_array=$(echo "$storage_array" | jq --argjson disk "$disk_json" '. + [$disk]')
log_info "Disque: /dev/$d - $model ($size_h, $disk_type)"
done
STORAGE_INFO="$storage_array"
echo ""
}
#=========================================================
# Étape 6 : Réseau
#=========================================================
collect_network_info() {
log_step "Collecte des informations réseau"
local network_array="[]"
local iface
for iface in $(ip -br link show | awk '$2 ~ /UP|UNKNOWN/ {print $1}'); do
[[ "$iface" =~ ^(lo|docker|br-|veth) ]] && continue
local mac
mac=$(ip link show "$iface" | awk '/link\/ether/ {print $2}')
[[ -z "$mac" ]] && continue
local type="unknown"
if [[ "$iface" =~ ^(eth|enp|eno) ]]; then
type="ethernet"
elif [[ "$iface" =~ ^(wlan|wl) ]]; then
type="wifi"
fi
local ip_addr
ip_addr=$(ip -4 addr show "$iface" | awk '/inet /{print $2}' | cut -d/ -f1 | head -1)
local speed="" wol_supported=""
if [[ "$type" = "ethernet" && -x /usr/sbin/ethtool ]]; then
local e
e=$(sudo ethtool "$iface" 2>/dev/null || true)
local spd
spd=$(echo "$e" | awk -F: '/Speed:/ {gsub(/ Mb\/s/,"",$2); gsub(/^[ \t]+/,"",$2); print $2}')
[[ -n "$spd" ]] && speed="$spd"
local wol
wol=$(echo "$e" | awk -F: '/Wake-on:/ {gsub(/^[ \t]+/,"",$2); print $2}')
if [[ -n "$wol" && "$wol" != "d" ]]; then
wol_supported="true"
elif [[ -n "$wol" ]]; then
wol_supported="false"
fi
fi
local net_json
net_json=$(jq -n \
--arg name "$iface" \
--arg type "$type" \
--arg mac "$mac" \
--arg ip "${ip_addr:-}" \
--arg speed "$speed" \
--arg wol "$wol_supported" \
'{
name: $name,
type: $type,
mac: $mac,
ip_address: ( $ip | select(. != "") ),
speed_mbps: ( ( $speed | tonumber? ) // null ),
wake_on_lan: ( if $wol == "" then null else ( $wol|test("true";"i") ) end )
}')
network_array=$(echo "$network_array" | jq --argjson net "$net_json" '. + [$net]')
log_info "Interface: $iface ($type) - IP: ${ip_addr:-N/A}"
done
NETWORK_INFO="$network_array"
echo ""
}
#=========================================================
# Étape 7 : Benchmarks
#=========================================================
run_benchmarks() {
log_step "Exécution des benchmarks (peut prendre plusieurs minutes)"
# CPU
local cpu_bench="null"
if command -v sysbench &>/dev/null; then
log_info "Benchmark CPU en cours..."
local cpu_res
cpu_res=$(sysbench cpu --cpu-max-prime=20000 --threads="$(nproc)" run 2>&1 || true)
local eps time_s
eps=$(echo "$cpu_res" | awk '/events per second/ {print $4}')
time_s=$(echo "$cpu_res" | awk '/total time:/ {gsub(/s/,"",$3); print $3}')
[[ -z "$eps" ]] && eps=0
[[ -z "$time_s" ]] && time_s=0
local cpu_score
cpu_score=$(safe_bc "scale=2; $eps / 100")
cpu_bench=$(jq -n \
--arg eps "$eps" \
--arg time "$time_s" \
--arg score "$cpu_score" \
'{
events_per_sec: ($eps | tonumber? // 0),
duration_s: ($time | tonumber? // 0),
score: ($score | tonumber? // 0)
}')
log_info "CPU: ${eps} events/sec (score: ${cpu_score})"
else
log_warn "sysbench non disponible - CPU bench ignoré"
fi
# Mémoire
local mem_bench="null"
if command -v sysbench &>/dev/null; then
log_info "Benchmark Mémoire en cours..."
local mem_res
mem_res=$(sysbench memory --memory-total-size=1G run 2>&1 || true)
local thr
thr=$(echo "$mem_res" | awk '/transferred/ {print $6}' | head -1)
[[ -z "$thr" ]] && thr=0
local mem_score
mem_score=$(safe_bc "scale=2; $thr / 100")
mem_bench=$(jq -n \
--arg thr "$thr" \
--arg score "$mem_score" \
'{
throughput_mib_s: ($thr | tonumber? // 0),
score: ($score | tonumber? // 0)
}')
log_info "Mémoire: ${thr} MiB/s (score: ${mem_score})"
else
log_warn "sysbench non disponible - Memory bench ignoré"
fi
# Disque
local disk_bench="null"
if command -v fio &>/dev/null; then
log_info "Benchmark Disque en cours (23 minutes)..."
local tmpdir
tmpdir=$(mktemp -d /tmp/fio-bench-XXXX)
local fio_res
fio_res=$(fio --name=bench --ioengine=libaio --rw=randrw --bs=4k \
--size=500M --numjobs=1 --direct=1 --runtime=30 --time_based \
--filename="$tmpdir/testfile" --output-format=json 2>/dev/null || echo '{}')
rm -rf "$tmpdir"
if [[ "$fio_res" != "{}" ]]; then
local rbw wbw riops wiops lat_ns
rbw=$(echo "$fio_res" | jq '.jobs[0].read.bw // 0')
wbw=$(echo "$fio_res" | jq '.jobs[0].write.bw // 0')
riops=$(echo "$fio_res" | jq '.jobs[0].read.iops // 0' | cut -d. -f1)
wiops=$(echo "$fio_res" | jq '.jobs[0].write.iops // 0' | cut -d. -f1)
lat_ns=$(echo "$fio_res" | jq '.jobs[0].read.clat_ns.mean // 0')
local rmb wmb lat_ms
rmb=$(safe_bc "scale=2; $rbw / 1024")
wmb=$(safe_bc "scale=2; $wbw / 1024")
lat_ms=$(safe_bc "scale=3; $lat_ns / 1000000")
local disk_score
disk_score=$(safe_bc "scale=2; ($rmb + $wmb) / 20")
disk_bench=$(jq -n \
--arg rmb "$rmb" \
--arg wmb "$wmb" \
--arg riops "$riops" \
--arg wiops "$wiops" \
--arg lat_ms "$lat_ms" \
--arg score "$disk_score" \
'{
read_mb_s: ($rmb | tonumber? // 0),
write_mb_s: ($wmb | tonumber? // 0),
iops_read: ($riops | tonumber? // 0),
iops_write: ($wiops | tonumber? // 0),
latency_ms: ($lat_ms | tonumber? // 0),
score: ($score | tonumber? // 0)
}')
log_info "Disque: R=${rmb}MB/s W=${wmb}MB/s (score: ${disk_score})"
else
log_warn "Échec du benchmark disque (fio)"
fi
else
log_warn "fio non disponible - Disk bench ignoré"
fi
# Réseau (iperf3)
local net_bench="null"
if command -v iperf3 &>/dev/null; then
local target="$IPERF_SERVER"
[[ -z "$target" ]] && target="10.0.1.97"
log_info "Benchmark Réseau en cours (vers $target)..."
if ping -c 1 -W 1 "$target" &>/dev/null; then
if nc -z "$target" 5201 &>/dev/null; then
# Test upload (client → serveur)
local upload_result=$(iperf3 -c "$target" -t 10 -J 2>/dev/null || echo '{}')
local upload_mbps="0"
local download_mbps="0"
local ping_ms="null"
if [[ "$upload_result" != "{}" ]]; then
# Extraire le débit d'upload en bits/sec et convertir en Mbps
local upload_bps=$(echo "$upload_result" | jq '.end.sum_sent.bits_per_second // 0')
upload_mbps=$(echo "scale=2; $upload_bps / 1000000" | bc)
# Test download (serveur → client avec -R)
local download_result=$(iperf3 -c "$target" -t 10 -R -J 2>/dev/null || echo '{}')
if [[ "$download_result" != "{}" ]]; then
local download_bps=$(echo "$download_result" | jq '.end.sum_received.bits_per_second // 0')
download_mbps=$(echo "scale=2; $download_bps / 1000000" | bc)
fi
# Mesurer le ping
local ping_output=$(ping -c 5 "$target" 2>/dev/null | grep 'avg' || echo "")
if [[ -n "$ping_output" ]]; then
ping_ms=$(echo "$ping_output" | awk -F'/' '{print $5}')
fi
# Score réseau
local net_score=$(echo "scale=2; ($upload_mbps + $download_mbps) / 20" | bc)
net_bench=$(jq -n \
--argjson upload "$upload_mbps" \
--argjson download "$download_mbps" \
--argjson ping "${ping_ms:-null}" \
--argjson score "$net_score" \
'{upload_mbps: $upload, download_mbps: $download, ping_ms: $ping, score: $score}')
log_info "Réseau: ↑${upload_mbps}Mbps ↓${download_mbps}Mbps (ping: ${ping_ms}ms, score: ${net_score})"
else
log_warn "Test iperf3 upload échoué - Network bench ignoré"
fi
else
log_warn "Port iperf3 (5201) fermé sur $target - Network bench ignoré"
fi
else
log_warn "Hôte $target non joignable - Network bench ignoré"
fi
else
log_warn "iperf3 non disponible - Network bench ignoré"
fi
# GPU bench non implémenté
local gpu_bench="null"
log_warn "GPU bench non implémenté - ignoré"
# Score global (CPU 40%, RAM 30%, Disque 30%)
local scores="" total_weight=0
if [[ "$cpu_bench" != "null" ]]; then
local cs
cs=$(echo "$cpu_bench" | jq '.score // 0')
scores="$scores + $cs * 0.4"
total_weight=$(safe_bc "$total_weight + 0.4")
fi
if [[ "$mem_bench" != "null" ]]; then
local ms
ms=$(echo "$mem_bench" | jq '.score // 0')
scores="$scores + $ms * 0.3"
total_weight=$(safe_bc "$total_weight + 0.3")
fi
if [[ "$disk_bench" != "null" ]]; then
local ds
ds=$(echo "$disk_bench" | jq '.score // 0')
scores="$scores + $ds * 0.3"
total_weight=$(safe_bc "$total_weight + 0.3")
fi
scores=$(echo "$scores" | sed -E 's/^[[:space:]]*\+ //')
local global_score=0
if [[ -n "$scores" && "$total_weight" != "0" ]]; then
global_score=$(safe_bc "scale=2; ($scores) / $total_weight")
fi
[[ -z "$global_score" ]] && global_score=0
BENCHMARK_RESULTS=$(jq -n \
--argjson cpu "$cpu_bench" \
--argjson mem "$mem_bench" \
--argjson disk "$disk_bench" \
--argjson net "$net_bench" \
--argjson gpu "$gpu_bench" \
--argjson global "$global_score" \
'{
cpu: $cpu,
memory: $mem,
disk: $disk,
network: $net,
gpu: $gpu,
global_score: $global
}')
log_info "Score global: ${global_score}/100"
echo ""
}
#=========================================================
# Envoi du payload JSON
#=========================================================
send_benchmark_payload() {
log_step "Construction du payload JSON et envoi au serveur"
# Si SERVER_URL na pas de schéma, on préfixe par http://
local base_url="$SERVER_URL"
if [[ "$base_url" != http*://* ]]; then
base_url="http://$base_url"
fi
local api_url="${base_url%/}/api/benchmark"
local payload
payload=$(
jq -n \
--arg version "$BENCH_SCRIPT_VERSION" \
--argjson system "$SYSTEM_INFO" \
--argjson cpu "$CPU_INFO" \
--argjson ram "$RAM_INFO" \
--argjson gpu "$GPU_INFO" \
--argjson mb "$MOTHERBOARD_INFO" \
--argjson storage "$STORAGE_INFO" \
--argjson network "$NETWORK_INFO" \
--argjson bench "$BENCHMARK_RESULTS" \
'
{
device_identifier: $system.hostname,
bench_script_version: $version,
hardware: {
cpu: ($cpu + {
microarchitecture: null,
tdp_w: null
}),
ram: (
$ram
| {
total_mb: .total_mb,
used_mb: .used_mb,
free_mb: .free_mb,
shared_mb: .shared_mb,
slots_total: .slots_total,
slots_used: .slots_used,
ecc: .ecc,
layout: [
.layout[]
| {
slot,
size_mb,
type,
speed_mhz,
vendor: .manufacturer,
part_number: null
}
]
}
),
gpu: (
if $gpu == null then
{
vendor: null,
model: null,
driver_version: null,
memory_dedicated_mb: null,
memory_shared_mb: null,
api_support: []
}
else
{
vendor: $gpu.vendor,
model: $gpu.model,
driver_version: null,
memory_dedicated_mb: null,
memory_shared_mb: null,
api_support: []
}
end
),
storage: {
devices: [
$storage[]
| {
name: ("/dev/" + .device),
type: (.type | ascii_upcase),
interface,
capacity_gb: (.size_gb | tonumber? // .size_gb),
vendor: null,
model,
smart_health: null,
temperature_c: null
}
],
partitions: []
},
network: {
interfaces: [
$network[]
| {
name,
type,
mac,
ip: .ip_address,
speed_mbps,
driver: null
}
]
},
motherboard: {
vendor: $mb.manufacturer,
model: $mb.model,
bios_version: $mb.bios_version,
bios_date: $mb.bios_date
},
os: (
$system.os
+ { virtualization_type: "none" }
),
sensors: {
cpu_temp_c: null,
disk_temps_c: {}
},
raw_info: {
lscpu: null,
lsblk: null
}
},
results: (
$bench as $b
| {
cpu: $b.cpu,
memory: $b.memory,
disk: $b.disk,
network: (
$b.network
+ {
jitter_ms: null,
packet_loss_percent: null
}
),
gpu: (
if $b.gpu == null then
{ glmark2_score: null, score: null }
else
$b.gpu
end
),
global_score: $b.global_score
}
)
}
'
)
# Debug : afficher le payload complet si DEBUG_PAYLOAD=1
if [[ "${DEBUG_PAYLOAD:-0}" == "1" ]]; then
echo ""
echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
echo -e "${YELLOW}DEBUG: Payload JSON complet${NC}"
echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
echo "$payload" | jq '.'
echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
echo ""
# Sauvegarder le payload dans un fichier avec timestamp
local debug_file="/tmp/bench_payload_$(date +%Y%m%d_%H%M%S).json"
echo "$payload" | jq '.' > "$debug_file"
echo -e "${GREEN}${NC} Payload sauvegardé dans: ${debug_file}"
echo ""
read -p "Appuyez sur Entrée pour continuer l'envoi ou Ctrl+C pour annuler..."
fi
log_info "Envoi du payload vers: $api_url"
local tmp_resp http_code
tmp_resp=$(mktemp /tmp/bench_response_XXXXXX.json)
# IMPORTANT : on envoie le payload via stdin (pas de @<(...) foireux)
http_code=$(
printf '%s\n' "$payload" | curl -sS -o "$tmp_resp" -w "%{http_code}" \
-X POST "$api_url" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_TOKEN" \
--data-binary @-
)
if [[ "$http_code" != "200" && "$http_code" != "201" ]]; then
log_error "Erreur lors de l'envoi (HTTP $http_code)"
echo "Réponse serveur :"
cat "$tmp_resp"
rm -f "$tmp_resp"
exit 1
fi
log_info "Payload envoyé avec succès (HTTP $http_code)"
rm -f "$tmp_resp"
echo ""
}
#=========================================================
# MAIN
#=========================================================
main() {
check_sudo
check_dependencies
echo -e "${BLUE}Début de la collecte et des benchmarks...${NC}"
echo ""
collect_system_info
collect_cpu_info
collect_ram_info
collect_hardware_info
collect_storage_info
collect_network_info
run_benchmarks
send_benchmark_payload
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
echo -e "${GREEN} Benchmark terminé avec succès !${NC}"
echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
echo ""
}
main "$@"