1071 lines
34 KiB
Bash
Executable File
1071 lines
34 KiB
Bash
Executable File
#!/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 (2–3 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 n’a 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 "$@"
|