diff --git a/ANALYSE_DONNEES.md b/ANALYSE_DONNEES.md new file mode 100644 index 0000000..356a0df --- /dev/null +++ b/ANALYSE_DONNEES.md @@ -0,0 +1,767 @@ +# Analyse des Données de Benchmarking + +Ce document identifie toutes les données intéressantes à collecter basées sur les résultats réels des benchmarks. + +--- + +## 📊 1. BENCHMARKS - Données à récupérer + +### 1.1 CPU (sysbench) + +**Commande testée** : +```bash +sysbench cpu --cpu-max-prime=10000 --threads=$(nproc) run +``` + +**Output analysé** : +``` +CPU speed: + events per second: 3409.87 + +General statistics: + total time: 10.0005s + total number of events: 34107 + +Latency (ms): + min: 1.10 + avg: 1.17 + max: 15.92 + 95th percentile: 1.23 +``` + +**✅ Données IMPORTANTES à extraire** : +- `events_per_second` : **3409.87** (métrique principale pour score) +- `total_time` : 10.0005s (durée du test) +- `avg_latency_ms` : 1.17 (latence moyenne) + +**❌ Données OPTIONNELLES** (peuvent être ignorées pour l'instant) : +- min/max latency +- 95th percentile +- threads fairness + +**Parsing recommandé** : +```bash +events_per_sec=$(sysbench cpu ... | grep 'events per second' | awk '{print $4}') +``` + +--- + +### 1.2 MEMORY (sysbench) + +**Commande testée** : +```bash +sysbench memory --memory-block-size=1M --memory-total-size=512M run +``` + +**Output analysé** : +``` +512.00 MiB transferred (13806.03 MiB/sec) + +General statistics: + total time: 0.0351s +``` + +**✅ Données IMPORTANTES à extraire** : +- `throughput_mib_s` : **13806.03** MiB/sec (métrique principale) +- `total_time` : 0.0351s + +**Parsing recommandé** : +```bash +throughput=$(sysbench memory ... | grep 'MiB/sec' | grep -oP '\d+\.\d+ MiB/sec' | awk '{print $1}') +``` + +--- + +### 1.3 DISK (fio) - Format JSON + +**Commande testée** : +```bash +fio --name=test --ioengine=libaio --rw=randrw --bs=4k --size=100M \ + --numjobs=1 --direct=1 --filename=/tmp/fio-test-file --output-format=json +``` + +**Output JSON (extrait)** : +```json +{ + "read": { + "bw_bytes": 728177777, + "bw": 711111, + "iops": 177777.777778, + "runtime": 72 + }, + "write": { + "bw_bytes": 728177777, + "bw": 711111, + "iops": 177777.777778 + } +} +``` + +**✅ Données IMPORTANTES à extraire** : +- `read.bw` : 711111 KiB/s → convertir en MB/s : **711 MB/s** +- `write.bw` : 711111 KiB/s → convertir en MB/s : **711 MB/s** +- `read.iops` : **177777** (operations/sec) +- `write.iops` : **177777** (operations/sec) +- `read.clat_ns.mean` : latence moyenne en nanosecondes + +**Parsing recommandé (avec jq)** : +```bash +fio_output=$(fio ... --output-format=json) +read_mb_s=$(echo "$fio_output" | jq '.jobs[0].read.bw / 1024') +write_mb_s=$(echo "$fio_output" | jq '.jobs[0].write.bw / 1024') +iops_read=$(echo "$fio_output" | jq '.jobs[0].read.iops') +iops_write=$(echo "$fio_output" | jq '.jobs[0].write.iops') +latency_ns=$(echo "$fio_output" | jq '.jobs[0].read.clat_ns.mean') +latency_ms=$(echo "scale=3; $latency_ns / 1000000" | bc) +``` + +--- + +### 1.4 NETWORK (iperf3) - Format JSON + +**Commande testée** : +```bash +iperf3 -c 10.0.1.97 -t 5 -J +``` + +**Output JSON (extrait)** : +```json +{ + "end": { + "sum_sent": { + "bits_per_second": 327057987.07346243, + "retransmits": 102 + }, + "sum_received": { + "bits_per_second": 323523050.66083658 + } + } +} +``` + +**✅ Données IMPORTANTES à extraire** : +- `upload_mbps` : 327057987 / 1000000 = **327 Mbps** (bits_per_second sender) +- `download_mbps` : **323 Mbps** (pour test en reverse -R) +- `retransmits` : 102 (nombre de retransmissions) +- `mean_rtt` : 53143 µs (ping moyen) + +**Parsing recommandé (avec jq)** : +```bash +upload_bits=$(echo "$iperf_json" | jq '.end.sum_sent.bits_per_second') +upload_mbps=$(echo "scale=2; $upload_bits / 1000000" | bc) +rtt_us=$(echo "$iperf_json" | jq '.end.streams[0].sender.mean_rtt') +ping_ms=$(echo "scale=2; $rtt_us / 1000" | bc) +``` + +--- + +## 🖥️ 2. HARDWARE - Données à récupérer + +### 2.1 CPU (lscpu + dmidecode) + +**Commande testée** : `lscpu` (output en français sur ta machine) + +**Output analysé** : +``` +Identifiant constructeur : GenuineIntel +Nom de modèle : Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz +Processeur(s) : 4 +Thread(s) par cœur : 1 +Cœur(s) par socket : 4 +Socket(s) : 1 +Vitesse maximale du processeur en MHz : 3400,0000 +Vitesse minimale du processeur en MHz : 1600,0000 + +Caches (somme de toutes) : + L1d : 128 KiB (4 instances) + L1i : 128 KiB (4 instances) + L2 : 1 MiB (4 instances) + L3 : 6 MiB (1 instance) + +Drapeaux : fpu vme de pse tsc msr ... avx sse4_1 sse4_2 ... +``` + +**✅ Données IMPORTANTES à extraire** : +- `vendor` : **GenuineIntel** +- `model` : **Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz** +- `microarchitecture` : Peut être déduit du model (Sandy Bridge pour i5-2400) +- `cores` : **4** (cœurs physiques) +- `threads` : **4** (4 cœurs × 1 thread = 4) +- `base_freq_ghz` : **3.1** GHz (du nom du modèle) +- `max_freq_ghz` : **3.4** GHz +- `cache_l1_kb` : 128 + 128 = **256 KB** (L1d + L1i) +- `cache_l2_kb` : 1024 KB = **1024 KB** +- `cache_l3_kb` : 6144 KB = **6144 KB** +- `flags` : ["avx", "sse4_1", "sse4_2", "aes", "pclmulqdq", ...] (important pour virtualisation) + +**⚠️ PROBLÈME DÉTECTÉ** : +```bash +echo "Cores: $(lscpu | grep '^CPU(s):' | awk '{print $2}')" +# Retourne vide car en français : "Processeur(s) :" +``` + +**✅ SOLUTION - Forcer locale en anglais** : +```bash +LC_ALL=C lscpu +# Ou utiliser lscpu -J pour JSON et parser avec jq +``` + +**Parsing recommandé** : +```bash +export LC_ALL=C +vendor=$(lscpu | grep 'Vendor ID' | awk '{print $3}') +model=$(lscpu | grep 'Model name' | sed 's/Model name: *//' | xargs) +cores=$(lscpu | grep '^CPU(s):' | awk '{print $2}') +threads=$(nproc) +max_freq=$(lscpu | grep 'CPU max MHz' | awk '{print $4}') +max_freq_ghz=$(echo "scale=2; $max_freq / 1000" | bc) + +# Caches +cache_l1d=$(lscpu | grep 'L1d cache' | awk '{print $3}' | sed 's/K//') +cache_l1i=$(lscpu | grep 'L1i cache' | awk '{print $3}' | sed 's/K//') +cache_l2=$(lscpu | grep 'L2 cache' | awk '{print $3}' | sed 's/[MK]//') +cache_l3=$(lscpu | grep 'L3 cache' | awk '{print $3}' | sed 's/[MK]//') + +# Flags (liste complète) +flags=$(lscpu | grep 'Flags:' | sed 's/Flags: *//') +``` + +**❌ Données MANQUANTES actuellement** : +- `tdp_w` : Non disponible via lscpu, peut être récupéré via dmidecode ou base de données constructeur + +--- + +### 2.2 RAM (free + dmidecode) + +**Commande testée** : `free -m` + `sudo dmidecode -t memory` + +**Output free -m** : +``` + total utilisé libre partagé tamp/cache disponible +Mem: 7771 5847 1479 678 1410 1923 +``` + +**Output dmidecode -t memory** (extrait) : +``` +Physical Memory Array + Error Correction Type: None + Maximum Capacity: 32 GB + Number Of Devices: 4 + +Memory Device + Size: 2 GB + Form Factor: DIMM + Locator: A1_DIMM0 + Type: DDR3 + Speed: 1333 MT/s + Manufacturer: Samsung + Part Number: M378B5773DH0-CH9 + Rank: 1 +``` + +**✅ Données IMPORTANTES à extraire** : +- `total_mb` : **7771 MB** (du free -m) +- `slots_total` : **4** (Number Of Devices) +- `slots_used` : **4** (compter les Memory Device avec Size > 0) +- `ecc` : **false** (Error Correction Type: None) +- `layout` : Array de: + - `slot` : "A1_DIMM0", "A1_DIMM1", "A1_DIMM2", "A1_DIMM3" + - `size_mb` : 2048 (2 GB) + - `type` : "DDR3" + - `speed_mhz` : 1333 + - `manufacturer` : "Samsung" ou "Kingston" + - `part_number` : "M378B5773DH0-CH9" ou "ACR256X64D3U1333C9" + +**Parsing recommandé** : +```bash +# Total RAM +total_mb=$(free -m | grep '^Mem:' | awk '{print $2}') + +# Avec dmidecode (nécessite sudo) +if command -v dmidecode &> /dev/null && [ $(id -u) -eq 0 ]; then + ecc=$(sudo dmidecode -t 16 | grep 'Error Correction Type' | grep -q 'None' && echo "false" || echo "true") + slots_total=$(sudo dmidecode -t 16 | grep 'Number Of Devices' | awk '{print $4}') + + # Parser chaque DIMM (complexe, nécessite parsing multi-lignes) + # Voir script complet ci-dessous +fi +``` + +--- + +### 2.3 GPU (lspci + nvidia-smi) + +**Commande testée** : `lspci | grep -i vga` + +**Output analysé** : +``` +00:02.0 VGA compatible controller: Intel Corporation 2nd Generation Core Processor Family Integrated Graphics Controller (rev 09) +``` + +**✅ Données IMPORTANTES à extraire** : +- `vendor` : **Intel Corporation** +- `model` : **2nd Generation Core Processor Family Integrated Graphics Controller** +- `pci_id` : **00:02.0** + +**Pour GPU NVIDIA** (nvidia-smi) : +```bash +nvidia-smi --query-gpu=name,memory.total,driver_version,vbios_version --format=csv,noheader +# Output: GeForce RTX 3070, 8192 MiB, 525.105.17, 94.02.42.00.35 +``` + +**Parsing recommandé** : +```bash +gpu_info=$(lspci | grep -i vga | head -1) +if echo "$gpu_info" | grep -qi 'nvidia'; then + gpu_vendor="NVIDIA" + gpu_model=$(nvidia-smi --query-gpu=name --format=csv,noheader | head -1) + gpu_vram=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits | head -1) +elif echo "$gpu_info" | grep -qi 'intel'; then + gpu_vendor="Intel" + gpu_model=$(echo "$gpu_info" | sed 's/.*: //' | sed 's/ (rev.*//') + gpu_vram=null +else + gpu_vendor=$(echo "$gpu_info" | awk -F: '{print $3}' | awk '{print $1, $2}') + gpu_model=$(echo "$gpu_info" | sed 's/.*: //') +fi +``` + +--- + +### 2.4 MOTHERBOARD (dmidecode) + +**Commande testée** : `sudo dmidecode -t baseboard` + +**Output analysé** : +``` +Base Board Information + Manufacturer: LENOVO + Product Name: + Version: + Serial Number: INVALID +``` + +**⚠️ PROBLÈME** : Product Name est vide sur ta machine Lenovo (OEM) + +**✅ Données IMPORTANTES à extraire** : +- `manufacturer` : **LENOVO** +- `model` : **(vide sur certaines machines)** +- `version` : (vide) + +**Alternative - dmidecode -t 1 (System Information)** : +```bash +sudo dmidecode -t 1 | grep -E 'Manufacturer|Product Name|Version' +``` + +**Parsing recommandé** : +```bash +if command -v dmidecode &> /dev/null && [ $(id -u) -eq 0 ]; then + mb_manufacturer=$(sudo dmidecode -t 2 | grep 'Manufacturer:' | sed 's/.*: *//' | xargs) + mb_model=$(sudo dmidecode -t 2 | grep 'Product Name:' | sed 's/.*: *//' | xargs) + [ -z "$mb_model" ] && mb_model="Unknown" +fi +``` + +**❌ Données MANQUANTES actuellement** : +- `bios_version` : Disponible via `dmidecode -t 0` +- `bios_date` : Disponible via `dmidecode -t 0` + +--- + +### 2.5 STORAGE (lsblk + smartctl) + +**Commande testée** : `lsblk -J` + `sudo smartctl -i /dev/sda` + +**Output lsblk -J** (extrait) : +```json +{ + "blockdevices": [ + { + "name": "sda", + "size": "447,1G", + "type": "disk", + "children": [ + {"name": "sda1", "size": "7,4G", "type": "part", "mountpoints": ["[SWAP]"]}, + {"name": "sda5", "size": "439,7G", "type": "part", "mountpoints": ["/"]} + ] + } + ] +} +``` + +**Output smartctl** : +``` +Device Model: KINGSTON SA400S37480G +Serial Number: 50026B77833E25E3 +User Capacity: 480 103 981 056 bytes [480 GB] +Rotation Rate: Solid State Device +SATA Version is: SATA 3.2, 6.0 Gb/s (current: 6.0 Gb/s) +``` + +**✅ Données IMPORTANTES à extraire** : +- `device` : **sda** +- `model` : **KINGSTON SA400S37480G** +- `size_gb` : **480** GB +- `type` : **ssd** (ou hdd si Rotation Rate ≠ SSD) +- `interface` : **sata** (ou nvme, usb) +- `serial` : **50026B77833E25E3** + +**Parsing recommandé** : +```bash +# Liste des disques (JSON) +disks_json=$(lsblk -J -o NAME,SIZE,TYPE,ROTA,TRAN) + +# Pour chaque disque +for disk in $(lsblk -d -n -o NAME); do + model=$(sudo smartctl -i /dev/$disk 2>/dev/null | grep 'Device Model' | sed 's/.*: *//') + size=$(lsblk -d -n -o SIZE /dev/$disk | sed 's/,/./') + type=$(lsblk -d -n -o ROTA /dev/$disk) # 0=SSD, 1=HDD + [ "$type" = "0" ] && disk_type="ssd" || disk_type="hdd" + interface=$(lsblk -d -n -o TRAN /dev/$disk) # sata, nvme, usb +done +``` + +--- + +### 2.6 NETWORK (ip link) + +**Commande testée** : `ip link show` + +**Output analysé** : +``` +2: eno1: mtu 1500 qdisc fq_codel state UP + link/ether 44:37:e6:6b:53:86 brd ff:ff:ff:ff:ff:ff + +3: wlp2s0: mtu 1500 qdisc noop state DOWN + link/ether 14:f6:d8:67:27:b7 brd ff:ff:ff:ff:ff:ff +``` + +**✅ Données IMPORTANTES à extraire** : +- `name` : **eno1**, **wlp2s0** +- `mac` : **44:37:e6:6b:53:86** +- `state` : **UP** ou **DOWN** +- `type` : **ethernet** (eno*, eth*) ou **wifi** (wlp*, wlan*) +- `speed_mbps` : Via `ethtool eno1 | grep Speed` → "Speed: 1000Mb/s" + +**Parsing recommandé** : +```bash +# Interfaces actives +for iface in $(ip -br link show | grep UP | awk '{print $1}'); do + # Ignorer loopback, docker, bridges + [[ "$iface" =~ ^(lo|docker|br-|veth) ]] && continue + + mac=$(ip link show $iface | grep 'link/ether' | awk '{print $2}') + + # Type d'interface + if [[ "$iface" =~ ^(eth|eno|enp) ]]; then + type="ethernet" + elif [[ "$iface" =~ ^(wlan|wlp) ]]; then + type="wifi" + else + type="unknown" + fi + + # Vitesse (nécessite ethtool) + if command -v ethtool &> /dev/null; then + speed=$(sudo ethtool $iface 2>/dev/null | grep 'Speed:' | awk '{print $2}' | sed 's/Mb\/s//') + fi +done +``` + +--- + +### 2.7 OS (os-release + uname) + +**Commande testée** : `cat /etc/os-release` + `uname -r` + `uname -m` + +**Output analysé** : +``` +ID=debian +VERSION_ID="13" +VERSION="13 (trixie)" +VERSION_CODENAME=trixie + +6.12.57+deb13-amd64 +x86_64 +``` + +**✅ Données IMPORTANTES à extraire** : +- `name` : **debian** +- `version` : **13 (trixie)** +- `kernel_version` : **6.12.57+deb13-amd64** +- `architecture` : **x86_64** + +**Parsing recommandé** : +```bash +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) +``` + +--- + +## 🔍 3. DONNÉES SUPPLÉMENTAIRES INTÉRESSANTES + +### 3.1 BIOS (dmidecode -t 0) + +**Pourquoi c'est utile** : Version BIOS/UEFI peut affecter performances, compatibilité + +```bash +sudo dmidecode -t 0 +# Output: +# Vendor: LENOVO +# Version: 9SKT91AUS +# Release Date: 03/30/2012 +``` + +**✅ Données à extraire** : +- `bios_vendor` : LENOVO +- `bios_version` : 9SKT91AUS +- `bios_date` : 03/30/2012 + +--- + +### 3.2 PCI Version (lspci -v) + +**Pourquoi c'est utile** : PCIe gen (2.0, 3.0, 4.0, 5.0) affecte bande passante GPU/NVMe + +```bash +sudo lspci -vv -s 00:02.0 | grep 'LnkCap:' +# Output: +# LnkCap: Port #0, Speed 2.5GT/s, Width x16 +# → PCIe 2.0 x16 +``` + +**✅ Données à extraire** : +- `pcie_version` : 2.0, 3.0, 4.0, 5.0 +- `pcie_lanes` : x1, x4, x8, x16 + +**❌ COMPLEXITÉ** : Parsing difficile, pas prioritaire pour MVP + +--- + +### 3.3 USB Version (lsusb) + +**Pourquoi c'est utile** : USB 2.0 / 3.0 / 3.1 / 4.0 pour périphériques + +```bash +lsusb +# Output: +# Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub +# Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub +``` + +**✅ Données à extraire** : +- `usb_controllers` : ["USB 2.0", "USB 3.0"] + +**❌ PAS PRIORITAIRE** pour benchmarking hardware + +--- + +### 3.4 Température CPU (sensors) + +**Pourquoi c'est utile** : Throttling thermique affecte performances + +```bash +sensors +# Output: +# coretemp-isa-0000 +# Core 0: +45.0°C +# Core 1: +43.0°C +``` + +**✅ Données à extraire** : +- `cpu_temp_celsius` : 45.0 (moyenne des cores) + +**❌ PAS PRIORITAIRE** pour MVP (nécessite lm-sensors installé) + +--- + +### 3.5 Consommation électrique (TDP) + +**Pourquoi c'est utile** : Efficacité énergétique (perf/watt) + +**Problème** : Pas accessible via commandes Linux standard +- Nécessite lookup dans base de données (ark.intel.com, AMD specs) +- Ou lecture via RAPL (Running Average Power Limit) si supporté + +**❌ NON IMPLÉMENTABLE** facilement sans DB externe + +--- + +## 📋 4. RÉSUMÉ - Priorités d'implémentation + +### ✅ NIVEAU 1 - ESSENTIEL (déjà partiellement fait) + +| Catégorie | Données | Outil | Parsing | +|-----------|---------|-------|---------| +| CPU Bench | events_per_sec, latency | sysbench | ✅ Simple (grep/awk) | +| Memory Bench | throughput_mib_s | sysbench | ✅ Simple (grep/awk) | +| Disk Bench | read/write MB/s, IOPS | fio JSON | ✅ Moyen (jq) | +| Network Bench | upload/download Mbps | iperf3 JSON | ✅ Moyen (jq) | +| CPU Info | vendor, model, cores, threads | lscpu | ⚠️ Locale issue | +| RAM Info | total_mb | free | ✅ Simple | +| OS Info | name, version, kernel | os-release | ✅ Simple | + +**Actions requises** : +1. ✅ Forcer `LC_ALL=C` pour lscpu +2. ✅ Utiliser JSON pour fio/iperf3 +3. ✅ Parser correctement les résultats + +--- + +### ✅ NIVEAU 2 - IMPORTANT (enrichissement) + +| Catégorie | Données | Outil | Complexité | +|-----------|---------|-------|-----------| +| CPU Detail | freq, caches, flags | lscpu | Moyen | +| RAM Detail | slots, type, speed, ECC | dmidecode | Élevée (sudo required) | +| GPU | vendor, model, vram | lspci, nvidia-smi | Moyen | +| Storage | model, size, type (SSD/HDD) | lsblk, smartctl | Moyen (sudo) | +| Network | interfaces, MAC, speed | ip, ethtool | Moyen | +| Motherboard | manufacturer, model | dmidecode | Simple (sudo) | + +**Actions requises** : +1. ✅ Implémenter détection dmidecode (avec fallback si sudo manquant) +2. ✅ Parser GPU (lspci minimum, nvidia-smi si dispo) +3. ✅ Collecter storage avec smartctl (optionnel) + +--- + +### ⚠️ NIVEAU 3 - NICE TO HAVE (futur) + +| Données | Outil | Priorité | Raison | +|---------|-------|----------|--------| +| BIOS version/date | dmidecode -t 0 | Basse | Utile mais pas critique | +| PCIe version | lspci -vv | Basse | Parsing complexe | +| USB controllers | lsusb | Très basse | Peu utile pour bench | +| CPU temp | sensors | Moyenne | Nécessite lm-sensors | +| TDP | DB externe | Très basse | Non faisable simplement | + +**Actions** : À voir après MVP fonctionnel + +--- + +## 🎯 5. RECOMMANDATIONS POUR LE SCRIPT + +### Stratégie de collecte + +1. **Hardware basique (sans sudo)** : + - ✅ lscpu (avec LC_ALL=C) + - ✅ free + - ✅ lspci + - ✅ lsblk + - ✅ ip link + - ✅ /etc/os-release + +2. **Hardware avancé (avec sudo optionnel)** : + - ⚠️ dmidecode (RAM layout, motherboard, BIOS) + - ⚠️ smartctl (modèle SSD/HDD) + - ⚠️ ethtool (vitesse réseau) + +3. **Gestion des cas** : + - Si `sudo` non disponible → Collecter infos basiques uniquement + - Si outil manquant → Retourner `null` pour ce champ + - Si erreur → Log warning, continuer + +### JSON final recommandé + +```json +{ + "hardware": { + "cpu": { + "vendor": "GenuineIntel", + "model": "Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz", + "cores": 4, + "threads": 4, + "base_freq_ghz": 3.1, + "max_freq_ghz": 3.4, + "cache_l1_kb": 256, + "cache_l2_kb": 1024, + "cache_l3_kb": 6144, + "flags": ["avx", "sse4_1", "sse4_2"] + }, + "ram": { + "total_mb": 7771, + "slots_total": 4, + "slots_used": 4, + "ecc": false, + "layout": [ + {"slot": "DIMM0", "size_mb": 2048, "type": "DDR3", "speed_mhz": 1333, "manufacturer": "Samsung"} + ] + }, + "gpu": { + "vendor": "Intel Corporation", + "model": "2nd Gen Core Processor Family IGP", + "vram_mb": null + }, + "motherboard": { + "manufacturer": "LENOVO", + "model": "Unknown", + "bios_version": "9SKT91AUS", + "bios_date": "03/30/2012" + }, + "storage": [ + {"device": "sda", "model": "KINGSTON SA400S37480G", "size_gb": 480, "type": "ssd", "interface": "sata"} + ], + "network": [ + {"name": "eno1", "type": "ethernet", "speed_mbps": 1000, "mac": "44:37:e6:6b:53:86"} + ], + "os": { + "name": "debian", + "version": "13 (trixie)", + "kernel_version": "6.12.57+deb13-amd64", + "architecture": "x86_64" + } + }, + "results": { + "cpu": { + "events_per_sec": 3409.87, + "duration_s": 10.0005, + "score": 34.1 + }, + "memory": { + "throughput_mib_s": 13806.03, + "score": 69.0 + }, + "disk": { + "read_mb_s": 711, + "write_mb_s": 711, + "iops_read": 177777, + "iops_write": 177777, + "latency_ms": 0.002, + "score": 85.0 + }, + "network": { + "upload_mbps": 327, + "download_mbps": 323, + "ping_ms": 53.1, + "score": 32.7 + }, + "global_score": 55.2 + } +} +``` + +--- + +## ✅ CONCLUSION + +**Données ESSENTIELLES déjà disponibles** : ✅ +- Benchmarks (CPU, RAM, Disk, Network) → Parsing fonctionne +- Infos basiques (CPU, RAM, OS) → Parsing à corriger (locale) + +**Données IMPORTANTES à ajouter** : ⚠️ +- CPU détaillé (fréquences, caches, flags) +- RAM détaillée (dmidecode) +- GPU (lspci minimum) +- Storage (lsblk + smartctl) +- Network (ip link + ethtool) + +**Données OPTIONNELLES** : 💡 +- BIOS +- PCI version +- Température CPU + +**Prochaine étape** : Modifier le script bench.sh pour collecter toutes les données NIVEAU 1 et NIVEAU 2 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d4d221e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,192 @@ +# Changelog - script_test.sh + +## Version 1.0.1 - Améliorations demandées + +### Nouvelles fonctionnalités + +#### 1. Wake-on-LAN pour cartes Ethernet +- **Fichier** : [script_test.sh:546-555](script_test.sh#L546-L555) +- Détection automatique du support Wake-on-LAN via `ethtool` +- Ajout du champ `wake_on_lan` (true/false/null) dans les informations réseau +- Vérifie si la carte supporte le "magic packet" (flag 'g') + +```json +{ + "name": "eth0", + "type": "ethernet", + "wake_on_lan": true +} +``` + +#### 2. Statistiques RAM détaillées +- **Fichier** : [script_test.sh:298-303](script_test.sh#L298-L303) et [script_test.sh:367-385](script_test.sh#L367-L385) +- Ajout de la RAM utilisée (`used_mb`) +- Ajout de la RAM libre (`free_mb`) +- Ajout de la RAM partagée (`shared_mb`) - inclut tmpfs, vidéo partagée, etc. +- Distinction entre RAM physique totale et RAM disponible dans l'OS + +```json +{ + "total_mb": 16384, + "used_mb": 8192, + "free_mb": 7500, + "shared_mb": 692 +} +``` + +#### 3. Test réseau iperf3 vers 10.0.1.97 +- **Fichier** : [script_test.sh:675-726](script_test.sh#L675-L726) +- Test de connectivité préalable avec `ping` +- Test upload (client → serveur) pendant 10 secondes +- Test download (serveur → client avec `-R`) pendant 10 secondes +- Mesure du ping moyen (5 paquets) +- Calcul du score réseau basé sur la moyenne upload/download + +**Prérequis** : Le serveur 10.0.1.97 doit avoir `iperf3 -s` en cours d'exécution. + +```bash +# Sur le serveur 10.0.1.97 +iperf3 -s +``` + +```json +{ + "upload_mbps": 940.50, + "download_mbps": 950.20, + "ping_ms": 0.5, + "score": 94.54 +} +``` + +#### 4. Données SMART de vieillissement des disques +- **Fichier** : [script_test.sh:492-602](script_test.sh#L492-L602) +- Extraction complète des données SMART pour chaque disque via `smartctl` +- **Indicateurs de santé globale** : + - `health_status` : PASSED/FAILED (test auto-diagnostic SMART) + - `temperature_celsius` : Température actuelle du disque + +- **Indicateurs de vieillissement** : + - `power_on_hours` : Heures de fonctionnement totales + - `power_cycle_count` : Nombre de démarrages/arrêts + - `reallocated_sectors` : Secteurs défectueux réalloués (⚠️ signe de défaillance) + - `pending_sectors` : Secteurs en attente de réallocation (⚠️ attention) + - `udma_crc_errors` : Erreurs de transmission (câble/interface) + +- **Pour SSD uniquement** : + - `wear_leveling_count` : Compteur d'usure des cellules + - `total_lbas_written` : Volume total de données écrites + +**Interprétation** : +- ✅ `health_status: "PASSED"` + `reallocated_sectors: 0` = Disque sain +- ⚠️ `reallocated_sectors > 0` = Début de défaillance, surveiller +- 🔴 `pending_sectors > 0` = Défaillance imminente, sauvegarder immédiatement +- 🔴 `health_status: "FAILED"` = Disque défaillant, remplacer + +```json +{ + "device": "sda", + "model": "Samsung SSD 970 EVO Plus 500GB", + "type": "ssd", + "smart": { + "health_status": "PASSED", + "power_on_hours": 12543, + "power_cycle_count": 1876, + "temperature_celsius": 42, + "reallocated_sectors": 0, + "pending_sectors": 0, + "udma_crc_errors": 0, + "wear_leveling_count": 97, + "total_lbas_written": 45678901234 + } +} +``` + +#### 5. Correction du calcul global_score +- **Fichier** : [script_test.sh:732-760](script_test.sh#L732-L760) +- Le score global n'inclut **que** CPU, RAM et Disk (pas réseau, pas GPU) +- Nouvelle pondération : + - **CPU** : 40% + - **RAM** : 30% + - **Disk** : 30% +- Normalisation automatique si certains benchmarks sont manquants +- Score sur 100 + +### Corrections + +- **PATH Fix** : Ajout de `/usr/sbin` et `/sbin` au PATH ([script_test.sh:30](script_test.sh#L30)) + - Résout le problème de détection de `dmidecode`, `smartctl`, `ethtool` + +### Format JSON mis à jour + +```json +{ + "hardware": { + "ram": { + "total_mb": 16384, + "used_mb": 8192, + "free_mb": 7500, + "shared_mb": 692, + "slots_total": 4, + "slots_used": 2, + "ecc": false, + "layout": [...] + }, + "network": [ + { + "name": "eth0", + "type": "ethernet", + "mac": "00:11:22:33:44:55", + "ip_address": "10.0.1.100", + "speed_mbps": 1000, + "wake_on_lan": true + } + ] + }, + "benchmarks": { + "cpu": { + "events_per_sec": 5234.89, + "duration_s": 10.0, + "score": 52.35 + }, + "memory": { + "throughput_mib_s": 15234.5, + "score": 76.17 + }, + "disk": { + "read_mb_s": 450.0, + "write_mb_s": 420.0, + "iops_read": 112000, + "iops_write": 105000, + "latency_ms": 0.08, + "score": 43.50 + }, + "network": { + "upload_mbps": 940.5, + "download_mbps": 950.2, + "ping_ms": 0.5, + "score": 94.54 + }, + "gpu": null, + "global_score": 57.00 + } +} +``` + +### Notes d'utilisation + +1. **Serveur iperf3** : Assurez-vous que `iperf3 -s` tourne sur 10.0.1.97 avant de lancer le script +2. **Permissions** : Le script nécessite `sudo` pour dmidecode, smartctl, ethtool +3. **Durée** : Le script prend environ 3-4 minutes (10s iperf3 upload + 10s download + 30s disk) + +### Commande de test + +```bash +# Lancer le serveur iperf3 sur 10.0.1.97 +ssh user@10.0.1.97 'iperf3 -s -D' + +# Lancer le script de test +sudo bash script_test.sh + +# Voir le résultat +cat result.json | jq . +``` diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md new file mode 100644 index 0000000..1665fe8 --- /dev/null +++ b/DEPLOYMENT_GUIDE.md @@ -0,0 +1,316 @@ +# Guide de Déploiement - Linux BenchTools + +## Modifications Apportées + +### 1. Backend API +- ✅ **Schémas Pydantic mis à jour** ([backend/app/schemas/hardware.py](backend/app/schemas/hardware.py)) + - `RAMInfo` : Ajout de `used_mb`, `free_mb`, `shared_mb` + - `StorageDevice` : `capacity_gb` changé de `int` à `float` + +- ✅ **API endpoint mis à jour** ([backend/app/api/benchmark.py](backend/app/api/benchmark.py)) + - Sauvegarde des nouveaux champs RAM + +- ✅ **Modèles de base de données** ([backend/app/models/](backend/app/models/)) + - `HardwareSnapshot` : Nouveaux champs RAM + - `DiskSMART` : Nouveau modèle pour données SMART (créé mais pas encore utilisé) + +### 2. Docker +- ✅ **Service iperf3 ajouté** ([docker-compose.yml](docker-compose.yml)) + - Port 5201 TCP/UDP exposé + +### 3. Script Client +- ✅ **Script bench.sh complètement réécrit** ([scripts/bench.sh](scripts/bench.sh)) + - Génère le payload JSON directement + - Collecte toutes les données hardware + - Exécute les benchmarks + - Envoie au serveur API + +## Déploiement + +### Étape 1: Arrêter les Services + +```bash +cd /home/gilles/Documents/vscode/serv_benchmark +docker-compose down +``` + +### Étape 2: Appliquer la Migration Base de Données + +Si la base de données existe déjà : + +```bash +cd backend +python3 apply_migration.py +``` + +**OU** si vous préférez partir de zéro (⚠️ PERTE DE DONNÉES) : + +```bash +rm -f backend/data/data.db +``` + +### Étape 3: Démarrer les Services + +```bash +docker-compose up -d +``` + +Vérifier les logs : + +```bash +docker-compose logs -f backend +docker-compose logs iperf3 +``` + +### Étape 4: Tester le Serveur iperf3 + +Depuis un client : + +```bash +iperf3 -c 10.0.1.97 -p 5201 +``` + +Vous devriez voir le test de bande passante s'exécuter. + +### Étape 5: Exécuter le Script Bench + +Depuis un client Linux : + +```bash +# Option 1: Télécharger et exécuter +curl -sSL https://gitea.maison43.duckdns.org/gilles/serv_benchmark/raw/branch/main/scripts/bench.sh | sudo bash + +# Option 2: Exécuter localement +cd /chemin/vers/serv_benchmark +sudo bash scripts/bench.sh +``` + +**Note** : Le script `bench.sh` a les paramètres serveur codés en dur : +- `SERVER_URL="10.0.1.97:8007"` +- `API_TOKEN="29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a"` +- `IPERF_SERVER="10.0.1.97"` + +Pour les modifier, éditez le fichier ou passez-les en variables d'environnement. + +## Vérification + +### 1. Vérifier la Base de Données + +```bash +sqlite3 backend/data/data.db + +# Lister les tables +.tables + +# Vérifier les colonnes de hardware_snapshots +PRAGMA table_info(hardware_snapshots); + +# Voir les derniers benchmarks +SELECT id, device_id, global_score, run_at FROM benchmarks ORDER BY run_at DESC LIMIT 5; + +# Voir les données RAM détaillées +SELECT + d.hostname, + h.ram_total_mb, + h.ram_used_mb, + h.ram_free_mb, + h.ram_shared_mb +FROM hardware_snapshots h +JOIN devices d ON h.device_id = d.id +ORDER BY h.captured_at DESC +LIMIT 5; +``` + +### 2. Vérifier l'API + +```bash +# Tester l'endpoint +curl http://10.0.1.97:8007/docs + +# Voir les devices +curl http://10.0.1.97:8007/api/devices +``` + +### 3. Consulter le Frontend + +Ouvrir dans un navigateur : +``` +http://10.0.1.97:8087 +``` + +## Troubleshooting + +### Erreur: HTTP 422 "Input should be a valid integer" + +**Solution** : Cette erreur a été corrigée en changeant `capacity_gb` de `int` à `float` dans le schéma. + +Si vous rencontrez encore cette erreur : +1. Vérifiez que le backend a bien été redémarré après les modifications +2. Vérifiez les logs : `docker-compose logs backend` + +### Port 5201 déjà utilisé + +```bash +# Trouver le processus +sudo lsof -i :5201 + +# Arrêter le service Docker iperf3 +docker-compose stop iperf3 +``` + +### Script bench.sh ne trouve pas les dépendances + +```bash +# Installer manuellement +sudo apt-get update +sudo apt-get install -y curl jq sysbench fio iperf3 dmidecode smartmontools ethtool +``` + +### Base de données verrouillée (SQLite locked) + +```bash +# Arrêter tous les services +docker-compose down + +# Supprimer les locks +rm -f backend/data/data.db-shm backend/data/data.db-wal + +# Redémarrer +docker-compose up -d +``` + +## Structure des Données Envoyées + +Le script `bench.sh` envoie un payload JSON avec cette structure : + +```json +{ + "device_identifier": "hostname", + "bench_script_version": "1.1.0", + "hardware": { + "cpu": { + "vendor": "GenuineIntel", + "model": "Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz", + "cores": 4, + "threads": 4, + ... + }, + "ram": { + "total_mb": 7771, + "used_mb": 6123, // NOUVEAU + "free_mb": 923, // NOUVEAU + "shared_mb": 760, // NOUVEAU + "slots_total": 4, + "slots_used": 4, + "ecc": false, + "layout": [...] + }, + "storage": { + "devices": [ + { + "name": "/dev/sda", + "type": "SSD", + "capacity_gb": 447.1, // FLOAT maintenant + ... + } + ], + "partitions": [] + }, + "network": { + "interfaces": [...] + }, + "motherboard": {...}, + "os": {...} + }, + "results": { + "cpu": { + "events_per_sec": 1206.65, + "duration_s": 10.0, + "score": 12.06 + }, + "memory": {...}, + "disk": {...}, + "network": {...}, + "gpu": null, + "global_score": 10.85 + } +} +``` + +## Prochaines Étapes + +### À Implémenter + +1. **Données SMART des disques** + - Le modèle `DiskSMART` existe mais n'est pas encore utilisé + - Modifier `bench.sh` pour collecter les données SMART + - Adapter l'API pour sauvegarder dans `disk_smart_data` + +2. **Wake-on-LAN** + - Ajouter le champ dans le schéma `NetworkInterface` + - Collecter dans `bench.sh` + - Afficher dans le frontend + +3. **Frontend** + - Afficher les nouvelles données RAM + - Graphiques d'utilisation + - Dashboard SMART pour la santé des disques + +4. **Amélioration du script bench.sh** + - Supporter les arguments en ligne de commande + - Mode verbose/debug + - Retry automatique en cas d'échec + +## Logs Utiles + +### Backend +```bash +docker-compose logs -f backend +``` + +### iperf3 +```bash +docker-compose logs -f iperf3 +``` + +### Tous les services +```bash +docker-compose logs -f +``` + +## Performance + +Le script `bench.sh` prend environ **3-5 minutes** pour : +- Collecter toutes les données hardware (30s) +- Benchmark CPU (10s) +- Benchmark RAM (10s) +- Benchmark Disk (2-3 minutes avec fio) +- Benchmark Network (optionnel, 20s) + +## Sécurité + +⚠️ **Le token API est codé en dur dans le script** : +```bash +API_TOKEN="29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a" +``` + +**Recommandations** : +- Changer ce token en production +- Utiliser des variables d'environnement +- Limiter l'accès au serveur API par IP +- Utiliser HTTPS en production + +## Support + +Pour toute question ou problème : +1. Consulter les logs Docker +2. Vérifier le fichier [IMPLEMENTATION_STATUS.md](IMPLEMENTATION_STATUS.md) +3. Tester manuellement avec `curl` : + +```bash +# Test manuel de l'API +curl -X POST http://10.0.1.97:8007/api/benchmark \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -d @result.json +``` diff --git a/FIXES_APPLIED.md b/FIXES_APPLIED.md new file mode 100644 index 0000000..d36ad2a --- /dev/null +++ b/FIXES_APPLIED.md @@ -0,0 +1,248 @@ +# Corrections Appliquées - Erreur HTTP 422 + +## Problème Initial + +Lors de l'exécution de `bench.sh`, erreur HTTP 422 : + +``` +{"detail":[{ + "type":"int_from_float", + "loc":["body","hardware","storage","devices",0,"capacity_gb"], + "msg":"Input should be a valid integer, got a number with a fractional part", + "input":447.1 +}]} +``` + +## Cause + +Le schéma Pydantic `StorageDevice` définissait `capacity_gb` comme `int`, mais le script `bench.sh` envoyait un `float` (447.1). + +## Solutions Appliquées + +### 1. ✅ Schéma StorageDevice ([backend/app/schemas/hardware.py](backend/app/schemas/hardware.py:59)) + +**Avant** : +```python +class StorageDevice(BaseModel): + capacity_gb: Optional[int] = None +``` + +**Après** : +```python +class StorageDevice(BaseModel): + capacity_gb: Optional[float] = None # Changed from int to float +``` + +### 2. ✅ Schéma RAMInfo ([backend/app/schemas/hardware.py](backend/app/schemas/hardware.py:35-44)) + +Ajout des nouveaux champs pour les statistiques RAM : + +```python +class RAMInfo(BaseModel): + total_mb: int + used_mb: Optional[int] = None # NEW + free_mb: Optional[int] = None # NEW + shared_mb: Optional[int] = None # NEW + slots_total: Optional[int] = None + slots_used: Optional[int] = None + ecc: Optional[bool] = None + layout: Optional[List[RAMSlot]] = None +``` + +### 3. ✅ API Backend ([backend/app/api/benchmark.py](backend/app/api/benchmark.py:72-80)) + +Sauvegarde des nouveaux champs RAM : + +```python +# RAM +ram_total_mb=hw.ram.total_mb if hw.ram else None, +ram_used_mb=hw.ram.used_mb if hw.ram else None, # NEW +ram_free_mb=hw.ram.free_mb if hw.ram else None, # NEW +ram_shared_mb=hw.ram.shared_mb if hw.ram else None, # NEW +ram_slots_total=hw.ram.slots_total if hw.ram else None, +# ... +``` + +### 4. ✅ Modèle de Base de Données ([backend/app/models/hardware_snapshot.py](backend/app/models/hardware_snapshot.py:35-43)) + +Ajout des colonnes dans la table `hardware_snapshots` : + +```python +# RAM +ram_total_mb = Column(Integer, nullable=True) +ram_used_mb = Column(Integer, nullable=True) # NEW +ram_free_mb = Column(Integer, nullable=True) # NEW +ram_shared_mb = Column(Integer, nullable=True) # NEW +ram_slots_total = Column(Integer, nullable=True) +# ... +``` + +### 5. ✅ Migration SQL ([backend/migrations/001_add_ram_stats_and_smart.sql](backend/migrations/001_add_ram_stats_and_smart.sql)) + +Script SQL pour mettre à jour une base existante : + +```sql +ALTER TABLE hardware_snapshots ADD COLUMN ram_used_mb INTEGER; +ALTER TABLE hardware_snapshots ADD COLUMN ram_free_mb INTEGER; +ALTER TABLE hardware_snapshots ADD COLUMN ram_shared_mb INTEGER; +``` + +### 6. ✅ Script d'Application de Migration ([backend/apply_migration.py](backend/apply_migration.py)) + +Script Python pour appliquer automatiquement la migration : + +```bash +python3 backend/apply_migration.py +``` + +## Test de Validation + +```bash +# Test des schémas Pydantic +python3 -c " +from app.schemas.hardware import RAMInfo, StorageDevice + +# Test RAMInfo avec nouveaux champs +ram = RAMInfo(total_mb=8000, used_mb=6000, free_mb=1500, shared_mb=500) +print('✅ RAMInfo OK') + +# Test StorageDevice avec float +storage = StorageDevice(name='/dev/sda', capacity_gb=447.1) +print(f'✅ StorageDevice OK - capacity_gb={storage.capacity_gb}') +" +``` + +Résultat : +``` +✅ RAMInfo OK +✅ StorageDevice OK - capacity_gb=447.1 +``` + +## Prochaines Étapes pour le Déploiement + +### Option 1 : Nouvelle Base de Données (Recommandé pour les tests) + +```bash +# Arrêter les services +docker-compose down + +# Supprimer l'ancienne base +rm -f backend/data/data.db + +# Redémarrer (la base sera recréée automatiquement avec les nouveaux champs) +docker-compose up -d +``` + +### Option 2 : Migration de la Base Existante (Production) + +```bash +# Arrêter les services +docker-compose down + +# Appliquer la migration +python3 backend/apply_migration.py + +# Redémarrer +docker-compose up -d +``` + +## Vérification + +### 1. Tester avec le script bench.sh + +```bash +sudo bash scripts/bench.sh +``` + +Vous devriez voir : +``` +[8/8] Construction du payload JSON et envoi au serveur + ✓ Envoi du payload vers: http://10.0.1.97:8007/api/benchmark + ✓ Payload envoyé avec succès (HTTP 200) +``` + +### 2. Vérifier la base de données + +```bash +sqlite3 backend/data/data.db + +# Vérifier les nouvelles colonnes +PRAGMA table_info(hardware_snapshots); + +# Voir les données +SELECT + d.hostname, + h.ram_total_mb, + h.ram_used_mb, + h.ram_free_mb, + h.ram_shared_mb +FROM hardware_snapshots h +JOIN devices d ON h.device_id = d.id +ORDER BY h.captured_at DESC +LIMIT 1; +``` + +### 3. Vérifier les logs backend + +```bash +docker-compose logs backend | grep -i error +``` + +Pas d'erreurs = succès ! ✅ + +## Résumé des Fichiers Modifiés + +| Fichier | Modification | Status | +|---------|-------------|--------| +| [backend/app/schemas/hardware.py](backend/app/schemas/hardware.py) | `capacity_gb: int` → `float`, ajout champs RAM | ✅ | +| [backend/app/api/benchmark.py](backend/app/api/benchmark.py) | Sauvegarde nouveaux champs RAM | ✅ | +| [backend/app/models/hardware_snapshot.py](backend/app/models/hardware_snapshot.py) | Ajout colonnes RAM | ✅ | +| [backend/migrations/001_add_ram_stats_and_smart.sql](backend/migrations/001_add_ram_stats_and_smart.sql) | Migration SQL | ✅ | +| [backend/apply_migration.py](backend/apply_migration.py) | Script d'application | ✅ | + +## Données Maintenant Collectées + +### Avant +```json +{ + "ram": { + "total_mb": 8000 + }, + "storage": { + "devices": [{ + "capacity_gb": 500 // int uniquement + }] + } +} +``` + +### Après +```json +{ + "ram": { + "total_mb": 7771, + "used_mb": 6123, // ✨ NOUVEAU + "free_mb": 923, // ✨ NOUVEAU + "shared_mb": 760 // ✨ NOUVEAU + }, + "storage": { + "devices": [{ + "capacity_gb": 447.1 // ✨ Supporte maintenant les décimales + }] + } +} +``` + +## Notes + +- ✅ Les modifications sont **rétrocompatibles** : les anciens benchmarks sans `used_mb`/`free_mb`/`shared_mb` continueront de fonctionner +- ✅ Le script `bench.sh` est maintenant **totalement autonome** et ne dépend plus de `script_test.sh` +- ✅ Le payload JSON est **conforme** au format attendu par l'API +- ✅ La validation Pydantic fonctionne correctement + +## Support + +En cas de problème, consulter : +1. [DEPLOYMENT_GUIDE.md](DEPLOYMENT_GUIDE.md) - Guide complet de déploiement +2. [IMPLEMENTATION_STATUS.md](IMPLEMENTATION_STATUS.md) - État d'implémentation +3. Logs Docker : `docker-compose logs -f backend` diff --git a/FRONTEND_UPDATES.md b/FRONTEND_UPDATES.md new file mode 100644 index 0000000..4afbe5e --- /dev/null +++ b/FRONTEND_UPDATES.md @@ -0,0 +1,257 @@ +# Mises à jour Frontend - Linux BenchTools + +Date: 2025-12-07 + +## Vue d'ensemble + +Le frontend a été mis à jour pour afficher les nouvelles données collectées par le script bench.sh v1.1.0, notamment : +- Statistiques RAM détaillées (utilisée, libre, partagée) +- Informations réseau complètes (Wake-on-LAN, vitesse) +- Résultats des tests iperf3 (upload, download, ping) + +## Fichiers modifiés + +### 1. `frontend/device_detail.html` + +**Ajout** d'une nouvelle section "Informations Réseau Détaillées" après la section "Dernier Benchmark" (lignes 67-75) : + +```html + +
+
🌐 Informations Réseau Détaillées
+
+
+
Chargement...
+
+
+
+``` + +### 2. `frontend/js/device_detail.js` + +#### Modifications apportées : + +**a) Appel de la nouvelle fonction dans `loadDeviceDetail()` (ligne 39)** +```javascript +renderNetworkDetails(); +``` + +**b) Affichage RAM détaillé dans `renderHardwareSummary()` (lignes 89-105)** + +Ajout du calcul et de l'affichage des statistiques RAM : +- Pourcentage d'utilisation +- RAM utilisée en GB +- RAM libre en GB +- RAM partagée en GB (si disponible, ex: mémoire vidéo partagée) + +```javascript +// RAM usage info +const ramTotalGB = Math.round((snapshot.ram_total_mb || 0) / 1024); +const ramUsedMB = snapshot.ram_used_mb || 0; +const ramFreeMB = snapshot.ram_free_mb || 0; +const ramSharedMB = snapshot.ram_shared_mb || 0; + +let ramValue = `${ramTotalGB} GB`; +if (ramUsedMB > 0 || ramFreeMB > 0) { + const usagePercent = ramTotalGB > 0 ? Math.round((ramUsedMB / (snapshot.ram_total_mb || 1)) * 100) : 0; + ramValue = `${ramTotalGB} GB (${usagePercent}% utilisé)
Utilisée: ${Math.round(ramUsedMB / 1024)}GB • Libre: ${Math.round(ramFreeMB / 1024)}GB`; + if (ramSharedMB > 0) { + ramValue += ` • Partagée: ${Math.round(ramSharedMB / 1024)}GB`; + } + ramValue += `
${snapshot.ram_slots_used || '?'} / ${snapshot.ram_slots_total || '?'} slots
`; +} +``` + +**c) Nouvelle fonction `renderNetworkDetails()` (lignes 163-281)** + +Cette fonction affiche : +1. **Interfaces réseau** : + - Type (ethernet/wifi) avec icône appropriée (🔌/📡) + - Badge Wake-on-LAN (WoL ✓ ou WoL ✗) + - Adresse IP + - Adresse MAC + - Vitesse en Mbps + - Driver (si disponible) + +2. **Résultats benchmark réseau (iperf3)** : + - Upload en Mbps avec flèche ↑ + - Download en Mbps avec flèche ↓ + - Ping moyen en ms + - Score réseau + +```javascript +// Render network details +function renderNetworkDetails() { + const container = document.getElementById('networkDetails'); + const snapshot = currentDevice.last_hardware_snapshot; + const bench = currentDevice.last_benchmark; + + // Parse network interfaces JSON + const interfaces = JSON.parse(snapshot.network_interfaces_json); + + // Display each interface with WoL badge + interfaces.forEach(iface => { + const typeIcon = iface.type === 'ethernet' ? '🔌' : '📡'; + const wolBadge = iface.wake_on_lan === true + ? 'WoL ✓' + : 'WoL ✗'; + // ... affichage des détails + }); + + // Display iperf3 results if available + if (bench && bench.network_results_json) { + const netResults = JSON.parse(bench.network_results_json); + // Affichage upload_mbps, download_mbps, ping_ms, score + } +} +``` + +## Backend - Modifications associées + +### 1. Modèle Benchmark (`backend/app/models/benchmark.py`) + +**Ajout** du champ `network_results_json` (ligne 33) : +```python +network_results_json = Column(Text, nullable=True) # Network benchmark details (iperf3) +``` + +### 2. API Benchmark (`backend/app/api/benchmark.py`) + +**Ajout** de l'extraction et de la sauvegarde des résultats réseau (lignes 134-142, 158) : +```python +# Extract network results for easier frontend access +network_results = None +if results.network: + network_results = { + "upload_mbps": results.network.upload_mbps, + "download_mbps": results.network.download_mbps, + "ping_ms": results.network.ping_ms, + "score": results.network.score + } + +benchmark = Benchmark( + # ... + network_results_json=json.dumps(network_results) if network_results else None +) +``` + +### 3. Migration SQL + +**Fichier** : `backend/migrations/002_add_network_results.sql` +```sql +ALTER TABLE benchmarks ADD COLUMN network_results_json TEXT; +``` + +**Script d'application** : `backend/apply_migration_002.py` + +## Déploiement + +Pour appliquer les mises à jour : + +### 1. Appliquer la migration 002 +```bash +cd backend +python3 apply_migration_002.py +``` + +### 2. Redémarrer le backend +```bash +docker compose restart backend +``` + +### 3. Frontend +Aucune action requise - les fichiers HTML/JS sont servis directement par nginx. +Il suffit de rafraîchir la page dans le navigateur (F5 ou Ctrl+R). + +## Données affichées + +### Section "Résumé Hardware" +- **RAM** : Affiche maintenant le pourcentage d'utilisation et le détail utilisée/libre/partagée + +**Avant** : +``` +💾 RAM: 16 GB + 2 / 4 slots +``` + +**Après** : +``` +💾 RAM: 16 GB (45% utilisé) + Utilisée: 7GB • Libre: 9GB • Partagée: 0GB + 2 / 4 slots +``` + +### Section "Informations Réseau Détaillées" (nouvelle) + +**Interfaces** : +``` +🔌 enp0s31f6 (ethernet) [WoL ✓] + IP: 10.0.1.100 + MAC: a0:b1:c2:d3:e4:f5 + Vitesse: 1000 Mbps +``` + +**Benchmark iperf3** : +``` +📈 Résultats Benchmark Réseau (iperf3) + + ↑ 945.23 ↓ 938.17 52.3 47.2 + Upload Mbps Download Mbps Ping ms Score +``` + +## Compatibilité + +- ✅ Compatible avec les anciens benchmarks (affichage gracieux si données manquantes) +- ✅ Gestion des cas où `network_results_json` est null +- ✅ Gestion des cas où `ram_used_mb`, `ram_free_mb`, `ram_shared_mb` sont null +- ✅ Affichage "N/A" ou messages appropriés si données non disponibles + +## Tests à effectuer + +1. **Nouveau benchmark** : + - Exécuter `sudo bash scripts/bench.sh` depuis un client + - Vérifier l'affichage des nouvelles données sur la page device detail + +2. **Anciens benchmarks** : + - Vérifier que les devices avec anciens benchmarks s'affichent correctement + - Vérifier l'absence d'erreurs JavaScript dans la console + +3. **Wake-on-LAN** : + - Vérifier le badge "WoL ✓" pour les interfaces ethernet supportant WoL + - Vérifier le badge "WoL ✗" pour les interfaces ne supportant pas WoL + +4. **iperf3** : + - Vérifier l'affichage des débits upload/download + - Vérifier l'affichage du ping moyen + - Vérifier le calcul du score réseau + +## Notes techniques + +### Parsing JSON +Le frontend parse plusieurs champs JSON : +- `network_interfaces_json` : Array des interfaces réseau +- `network_results_json` : Objet avec upload_mbps, download_mbps, ping_ms, score + +### Gestion des erreurs +Tous les parsings JSON sont dans des blocs try/catch pour éviter les crashes si le JSON est invalide. + +### Classes CSS utilisées +- `.badge-success` : Badge vert pour WoL activé +- `.badge-muted` : Badge gris pour WoL désactivé +- `.hardware-item` : Conteneur pour chaque interface réseau +- Couleurs CSS variables : `--color-success`, `--color-info`, `--color-warning`, `--color-primary` + +## Améliorations futures possibles + +1. **Graphiques** : Ajouter des graphiques pour visualiser l'historique des performances réseau +2. **SMART** : Ajouter une section pour afficher les données SMART des disques (déjà collectées par le script) +3. **Alertes** : Notifier si Wake-on-LAN n'est pas activé sur les serveurs +4. **Comparaison** : Permettre de comparer les résultats iperf3 entre plusieurs benchmarks +5. **Export** : Permettre d'exporter les données réseau en CSV/JSON + +## Support + +Pour toute question ou problème concernant ces mises à jour, vérifiez : +1. Les logs du backend : `docker compose logs backend` +2. La console JavaScript du navigateur (F12) +3. Les migrations ont été appliquées : `python3 backend/apply_migration_002.py` diff --git a/IMPLEMENTATION_STATUS.md b/IMPLEMENTATION_STATUS.md new file mode 100644 index 0000000..25db287 --- /dev/null +++ b/IMPLEMENTATION_STATUS.md @@ -0,0 +1,201 @@ +# État d'Implémentation - Linux BenchTools + +## ✅ Complété + +### 1. Script de Collecte Local (`script_test.sh`) +- ✅ Collecte complète des informations hardware +- ✅ Données SMART des disques (santé + vieillissement) +- ✅ Statistiques RAM détaillées (used, free, shared) +- ✅ Wake-on-LAN pour cartes ethernet +- ✅ Benchmarks (CPU, RAM, Disk, Network via iperf3) +- ✅ Génération de `result.json` complet +- ✅ PATH fix pour `/usr/sbin` + +### 2. Docker Infrastructure +- ✅ Service backend (FastAPI) +- ✅ Service frontend (Nginx) +- ✅ Service iperf3 (port 5201) + +### 3. Modèles de Base de Données +- ✅ Modèle `HardwareSnapshot` mis à jour (RAM used/free/shared) +- ✅ Nouveau modèle `DiskSMART` pour données de vieillissement +- ✅ Relations entre modèles configurées + +### 4. Script Client (`scripts/bench.sh`) +- ✅ Wrapper pour `script_test.sh` +- ✅ Parsing d'arguments (--server, --token, --iperf-server) +- ✅ Vérification et installation des dépendances +- ✅ Envoi POST des résultats au serveur + +## 🚧 En Cours / À Faire + +### 5. Schémas Pydantic (Backend) +**Problème**: Les schémas actuels ne correspondent pas à la structure de `result.json` + +#### Format actuel attendu par l'API: +```json +{ + "device_identifier": "hostname", + "bench_script_version": "1.0.0", + "hardware": { + "cpu": { "vendor": "...", "model": "..." }, + "ram": { "total_mb": 8000, "layout": [...] }, + "storage": { "devices": [...], "partitions": [...] }, + ... + }, + "results": { + "cpu": { "score": 50 }, + "memory": { "score": 60 }, + ... + } +} +``` + +#### Format généré par `result.json`: +```json +{ + "metadata": { + "script_version": "1.0.0", + "timestamp": "2025-12-07T19:47:34Z" + }, + "system": { + "hostname": "lenovo-bureau", + "os": { "name": "debian", ... } + }, + "hardware": { + "cpu": { "vendor": "...", "model": "...", "flags": [...] }, + "ram": { + "total_mb": 7771, + "used_mb": 6123, // NOUVEAU + "free_mb": 923, // NOUVEAU + "shared_mb": 760, // NOUVEAU + "layout": [...] + }, + "storage": [ // Format différent: array au lieu d'objet + { + "device": "sda", + "model": "...", + "smart": { // NOUVEAU + "health_status": "PASSED", + "power_on_hours": 7101, + ... + } + } + ], + "network": [ // Format différent: array au lieu d'objet + { + "name": "eno1", + "wake_on_lan": true // NOUVEAU + } + ] + }, + "benchmarks": { // "results" dans l'API + "cpu": { "events_per_sec": 1206, "score": 12.06 }, + "global_score": 10.85 + } +} +``` + +### Actions Requises: + +#### Option A: Modifier les Schémas Pydantic (Recommandé) +- [ ] Mettre à jour `RAMInfo` pour ajouter `used_mb`, `free_mb`, `shared_mb` +- [ ] Créer `SMARTData` schema pour les données SMART +- [ ] Mettre à jour `StorageDevice` pour inclure `smart: SMARTData` +- [ ] Mettre à jour `NetworkInterface` pour ajouter `wake_on_lan`, `ip_address` +- [ ] Créer un mapper qui convertit `result.json` → format API attendu + - `metadata.script_version` → `bench_script_version` + - `system.hostname` → `device_identifier` + - `hardware.storage[]` → `hardware.storage.devices[]` + - `hardware.network[]` → `hardware.network.interfaces[]` + - `benchmarks` → `results` + +#### Option B: Adapter le script client +- [ ] Modifier `script_test.sh` pour générer le format attendu par l'API +- ⚠️ Moins flexible, car le format local serait différent du format serveur + +### 6. API Backend (`backend/app/api/benchmark.py`) +- [ ] Adapter le parsing pour les nouvelles données +- [ ] Sauvegarder les données SMART dans la table `disk_smart_data` +- [ ] Gérer les nouveaux champs RAM (used/free/shared) +- [ ] Gérer wake_on_lan pour les interfaces réseau + +### 7. Migration Alembic +- [ ] Créer migration pour: + - `hardware_snapshots.ram_used_mb` + - `hardware_snapshots.ram_free_mb` + - `hardware_snapshots.ram_shared_mb` + - Nouvelle table `disk_smart_data` + +### 8. Frontend +- [ ] Afficher les données SMART (santé des disques) +- [ ] Afficher l'utilisation RAM détaillée +- [ ] Afficher Wake-on-LAN status +- [ ] Graphiques d'évolution SMART (power_on_hours, température) + +### 9. Tests +- [ ] Tester le flux complet local → serveur +- [ ] Vérifier que toutes les données sont bien sauvegardées +- [ ] Tester avec plusieurs machines + +## 🎯 Prochaines Étapes Recommandées + +1. **Créer un mapper `result.json` → API format** + - Fichier: `backend/app/utils/result_mapper.py` + - Fonction: `map_result_json_to_api_payload(result_json) -> BenchmarkPayload` + +2. **Mettre à jour les schémas Pydantic** + - Ajouter les nouveaux champs dans `schemas/hardware.py` + +3. **Adapter l'API `benchmark.py`** + - Utiliser le mapper + - Sauvegarder les données SMART + +4. **Créer la migration Alembic** + ```bash + cd backend + alembic revision --autogenerate -m "Add RAM stats and SMART data" + alembic upgrade head + ``` + +5. **Tester le flux complet** + ```bash + # 1. Démarrer le serveur + docker-compose up -d + + # 2. Exécuter le benchmark + sudo bash scripts/bench.sh --server http://10.0.1.97:8007 --token YOUR_TOKEN + + # 3. Vérifier dans la base de données + sqlite3 backend/data/data.db "SELECT * FROM disk_smart_data LIMIT 5;" + ``` + +## 📊 Structure de `result.json` Complète + +Voir le fichier [result.json](result.json) pour un exemple réel complet. + +### Nouvelles Données vs Ancien Format + +| Donnée | Ancien | Nouveau | Location | +|--------|--------|---------|----------| +| RAM utilisée | ❌ | ✅ `ram.used_mb` | `hardware.ram.used_mb` | +| RAM libre | ❌ | ✅ `ram.free_mb` | `hardware.ram.free_mb` | +| RAM partagée | ❌ | ✅ `ram.shared_mb` | `hardware.ram.shared_mb` | +| SMART santé | ❌ | ✅ `smart.health_status` | `hardware.storage[].smart.health_status` | +| SMART heures | ❌ | ✅ `smart.power_on_hours` | `hardware.storage[].smart.power_on_hours` | +| SMART secteurs | ❌ | ✅ `smart.reallocated_sectors` | `hardware.storage[].smart.reallocated_sectors` | +| Wake-on-LAN | ❌ | ✅ `wake_on_lan` | `hardware.network[].wake_on_lan` | +| Network ping | ❌ | ✅ `ping_ms` | `benchmarks.network.ping_ms` | +| IP address | ❌ | ✅ `ip_address` | `hardware.network[].ip_address` | + +## 📝 Notes de Migration + +### Base de Données +- Nouvelle table: `disk_smart_data` +- Champs ajoutés à `hardware_snapshots`: `ram_used_mb`, `ram_free_mb`, `ram_shared_mb` +- Relations: `HardwareSnapshot` ← (1:N) → `DiskSMART` + +### Compatibilité Ascendante +- Les anciens benchmarks sans ces données afficheront `null` +- Le système reste compatible avec les scripts clients plus anciens + diff --git a/SMART_GUIDE.md b/SMART_GUIDE.md new file mode 100644 index 0000000..0988cc5 --- /dev/null +++ b/SMART_GUIDE.md @@ -0,0 +1,247 @@ +# Guide des Données SMART - Santé et Vieillissement des Disques + +## Introduction + +Le script [script_test.sh](script_test.sh) collecte automatiquement les données SMART (Self-Monitoring, Analysis and Reporting Technology) pour tous les disques physiques détectés. Ces données permettent de surveiller l'état de santé et le vieillissement des disques durs (HDD) et SSD. + +## Données Collectées + +### Indicateurs Communs (HDD et SSD) + +#### 1. État de Santé Global +```json +"health_status": "PASSED" // ou "FAILED" ou null +``` +- **PASSED** : Le disque passe tous les auto-tests SMART ✅ +- **FAILED** : Le disque a échoué aux tests SMART 🔴 (remplacer immédiatement) +- **null** : SMART non supporté ou données non disponibles + +#### 2. Heures de Fonctionnement +```json +"power_on_hours": 12543 +``` +- Nombre total d'heures pendant lesquelles le disque a été alimenté +- **Interprétation** : + - < 10,000h : Jeune + - 10,000-30,000h : Utilisé normalement + - \> 30,000h : Vieillissant (> 3.4 ans de fonctionnement continu) + - \> 50,000h : Très vieux (> 5.7 ans) + +#### 3. Cycles d'Alimentation +```json +"power_cycle_count": 1876 +``` +- Nombre de fois où le disque a été allumé/éteint +- Les arrêts/démarrages fréquents peuvent réduire la durée de vie + +#### 4. Température +```json +"temperature_celsius": 42 +``` +- Température actuelle du disque en degrés Celsius +- **Plages recommandées** : + - HDD : 25-45°C optimal, < 50°C acceptable + - SSD : 30-50°C optimal, < 70°C acceptable + - ⚠️ > 60°C (HDD) ou > 80°C (SSD) : Surchauffe dangereuse + +### Indicateurs Critiques de Défaillance + +#### 5. Secteurs Réalloués +```json +"reallocated_sectors": 0 +``` +- Nombre de secteurs défectueux qui ont été remplacés par des secteurs de réserve +- **CRITIQUE** : Ce compteur ne devrait JAMAIS augmenter sur un disque sain +- **Interprétation** : + - **0** : Disque sain ✅ + - **1-10** : Début de défaillance ⚠️ (surveiller de près) + - **> 10** : Défaillance avancée 🔴 (planifier le remplacement) + - **> 100** : Défaillance critique 🔴 (remplacer immédiatement) + +#### 6. Secteurs en Attente de Réallocation +```json +"pending_sectors": 0 +``` +- Secteurs instables en attente de réallocation +- **TRÈS CRITIQUE** : Indique une défaillance imminente +- **Interprétation** : + - **0** : Normal ✅ + - **> 0** : Défaillance imminente 🔴 (sauvegarder et remplacer MAINTENANT) + +#### 7. Erreurs CRC UDMA +```json +"udma_crc_errors": 0 +``` +- Erreurs de transmission de données (câble SATA/USB ou interface défectueux) +- **Interprétation** : + - **0** : Connexion saine ✅ + - **1-5** : Problème de câble/connexion ⚠️ (vérifier le câble) + - **> 10** : Câble défectueux ou problème d'interface 🔴 + +### Indicateurs Spécifiques aux SSD + +#### 8. Wear Leveling Count +```json +"wear_leveling_count": 97 +``` +- Représente le pourcentage de durée de vie restante des cellules NAND +- Généralement affiché comme une valeur de 0 à 100 +- **Interprétation** : + - **100-80** : SSD neuf/très bon état ✅ + - **79-50** : Usure normale ✅ + - **49-20** : Usure avancée ⚠️ + - **< 20** : Fin de vie proche 🔴 (planifier remplacement) + +#### 9. Total LBAs Written +```json +"total_lbas_written": 45678901234 +``` +- Volume total de données écrites sur le SSD (en blocs logiques) +- Permet de calculer l'usure totale du SSD +- **Calcul approximatif de données écrites** : + - `Total_GB_Written = (total_lbas_written × 512) / 1,073,741,824` + - Exemple : 45,678,901,234 LBAs ≈ 21.8 TB écrits + +## Exemple de Sortie Complète + +### Disque SSD Sain +```json +{ + "device": "nvme0n1", + "model": "Samsung SSD 970 EVO Plus 500GB", + "size_gb": "465.8", + "type": "ssd", + "interface": "nvme", + "serial": "S4EWNX0R123456", + "smart": { + "health_status": "PASSED", + "power_on_hours": 8543, + "power_cycle_count": 1234, + "temperature_celsius": 38, + "reallocated_sectors": 0, + "pending_sectors": 0, + "udma_crc_errors": 0, + "wear_leveling_count": 95, + "total_lbas_written": 12345678901 + } +} +``` +**Diagnostic** : Disque en excellent état ✅ + +### Disque HDD avec Début de Défaillance +```json +{ + "device": "sda", + "model": "WD Blue 1TB", + "size_gb": "931.5", + "type": "hdd", + "interface": "sata", + "serial": "WD-WCC1234567890", + "smart": { + "health_status": "PASSED", + "power_on_hours": 42567, + "power_cycle_count": 3456, + "temperature_celsius": 41, + "reallocated_sectors": 8, + "pending_sectors": 0, + "udma_crc_errors": 1, + "wear_leveling_count": null, + "total_lbas_written": null + } +} +``` +**Diagnostic** : +- ⚠️ 8 secteurs réalloués : Début de défaillance +- ⚠️ 42,567 heures (4.8 ans) : Disque vieillissant +- **Action** : Surveiller de près, planifier un remplacement préventif + +### Disque Défaillant (À Remplacer) +```json +{ + "device": "sdb", + "model": "Seagate Barracuda 2TB", + "size_gb": "1863.0", + "type": "hdd", + "interface": "sata", + "serial": "ST2000DM001-XXXXXXX", + "smart": { + "health_status": "FAILED", + "power_on_hours": 67890, + "power_cycle_count": 5678, + "temperature_celsius": 52, + "reallocated_sectors": 156, + "pending_sectors": 3, + "udma_crc_errors": 45, + "wear_leveling_count": null, + "total_lbas_written": null + } +} +``` +**Diagnostic** : +- 🔴 `health_status: "FAILED"` : Test SMART échoué +- 🔴 156 secteurs réalloués : Défaillance massive +- 🔴 3 secteurs en attente : Défaillance active en cours +- 🔴 52°C : Surchauffe +- **Action** : SAUVEGARDER IMMÉDIATEMENT et remplacer le disque + +## Commandes de Diagnostic Manuel + +### Voir les données SMART brutes +```bash +sudo smartctl -A /dev/sda +``` + +### Tester la santé d'un disque +```bash +sudo smartctl -H /dev/sda +``` + +### Voir toutes les informations SMART +```bash +sudo smartctl -a /dev/sda +``` + +### Lancer un test court (5 minutes) +```bash +sudo smartctl -t short /dev/sda +# Attendre 5 minutes, puis vérifier les résultats +sudo smartctl -l selftest /dev/sda +``` + +### Lancer un test long (plusieurs heures) +```bash +sudo smartctl -t long /dev/sda +# Attendre la fin du test (peut prendre 2-12h selon le disque) +sudo smartctl -l selftest /dev/sda +``` + +## Recommandations + +### Surveillance Régulière +- Exécuter le script de benchmark **mensuellement** pour suivre l'évolution +- Comparer les valeurs SMART d'un mois à l'autre +- Toute augmentation de `reallocated_sectors` ou `pending_sectors` est un signal d'alarme + +### Planification de Remplacement +- **Remplacer immédiatement** si : + - `health_status: "FAILED"` + - `pending_sectors > 0` + - `reallocated_sectors > 100` + +- **Planifier un remplacement** si : + - `reallocated_sectors` augmente régulièrement + - `power_on_hours > 50,000` sur un HDD + - `wear_leveling_count < 20` sur un SSD + - Température constamment > 55°C (HDD) ou > 75°C (SSD) + +### Sauvegardes +- **Sauvegarde 3-2-1** : + - **3** copies de vos données + - **2** supports différents + - **1** copie hors site + +## Références + +- [Wikipedia SMART](https://en.wikipedia.org/wiki/S.M.A.R.T.) +- [Backblaze Hard Drive Stats](https://www.backblaze.com/blog/backblaze-drive-stats-for-2024/) - Statistiques de défaillance réelles +- [smartmontools Documentation](https://www.smartmontools.org/) diff --git a/TEST_BENCH.md b/TEST_BENCH.md new file mode 100644 index 0000000..7c5a71a --- /dev/null +++ b/TEST_BENCH.md @@ -0,0 +1,129 @@ +# Test du script bench.sh avec corrections + +## Corrections appliquées + +1. ✅ **Migration 002 appliquée** - Ajout de la colonne `network_results_json` à la table `benchmarks` +2. ✅ **Payload RAM corrigé** - Ajout de `used_mb`, `free_mb`, `shared_mb` dans le payload JSON +3. ✅ **Mode debug implémenté** - Variable `DEBUG_PAYLOAD=1` pour afficher le payload complet + +## Comment tester + +### Test normal (sans debug) +```bash +sudo bash /home/gilles/Documents/vscode/serv_benchmark/scripts/bench.sh +``` + +### Test avec debug (affiche le payload) +```bash +DEBUG_PAYLOAD=1 sudo bash /home/gilles/Documents/vscode/serv_benchmark/scripts/bench.sh +``` + +Le mode debug va : +1. Afficher le payload JSON complet formaté +2. Sauvegarder le fichier dans `/tmp/bench_payload_YYYYMMDD_HHMMSS.json` +3. Demander confirmation avant d'envoyer + +## Problèmes résolus + +### 1. Erreur HTTP 500 +**Cause** : Colonne `network_results_json` manquante dans la base de données +**Solution** : Migration SQL appliquée directement via Docker + +```sql +ALTER TABLE benchmarks ADD COLUMN network_results_json TEXT; +``` + +### 2. Données RAM manquantes dans le payload +**Cause** : Le script collectait les données (used_mb, free_mb, shared_mb) mais ne les incluait pas dans le payload JSON +**Solution** : Ajout des champs dans la construction du payload (lignes 869-871 du script) + +**Avant** : +```javascript +ram: { + total_mb: .total_mb, + slots_total: .slots_total, + // ... manque used_mb, free_mb, shared_mb +} +``` + +**Après** : +```javascript +ram: { + total_mb: .total_mb, + used_mb: .used_mb, // ✅ AJOUTÉ + free_mb: .free_mb, // ✅ AJOUTÉ + shared_mb: .shared_mb, // ✅ AJOUTÉ + slots_total: .slots_total, + // ... +} +``` + +### 3. Debug du payload +**Implémentation** : Mode debug avec variable d'environnement + +```bash +# Activer le debug +DEBUG_PAYLOAD=1 sudo bash scripts/bench.sh + +# Le script affichera : +# ════════════════════════════════════════════════════════ +# DEBUG: Payload JSON complet +# ════════════════════════════════════════════════════════ +# { +# "device_identifier": "lenovo-bureau", +# "bench_script_version": "1.1.0", +# "hardware": { +# "cpu": { ... }, +# "ram": { +# "total_mb": 7771, +# "used_mb": 4530, // ✅ Maintenant présent +# "free_mb": 1291, // ✅ Maintenant présent +# "shared_mb": 1950, // ✅ Maintenant présent +# ... +# }, +# ... +# }, +# "results": { +# "network": { +# "upload_mbps": 427.86, +# "download_mbps": 398.94, +# "ping_ms": 44.16, +# ... +# } +# } +# } +# ════════════════════════════════════════════════════════ +# ✓ Payload sauvegardé dans: /tmp/bench_payload_20251207_233745.json +# +# Appuyez sur Entrée pour continuer l'envoi ou Ctrl+C pour annuler... +``` + +## Vérification frontend + +Après un nouveau benchmark réussi, vérifier dans l'interface web : + +1. **Section RAM** : Doit afficher le pourcentage d'utilisation et les détails + ``` + 💾 RAM: 8 GB (58% utilisé) + Utilisée: 4GB • Libre: 1GB • Partagée: 2GB + 4 / 4 slots + ``` + +2. **Section Réseau** : Doit afficher les interfaces avec WoL et les résultats iperf3 + ``` + 🔌 eno1 (ethernet) [WoL ✓] + IP: 10.0.1.169 + MAC: xx:xx:xx:xx:xx:xx + Vitesse: 1000 Mbps + + 📈 Résultats Benchmark Réseau (iperf3) + ↑ 427.86 ↓ 398.94 44.16 41.34 + Upload Mbps Download Mbps Ping ms Score + ``` + +## Prochaines étapes + +1. Lancer un nouveau benchmark : `sudo bash scripts/bench.sh` +2. Vérifier qu'il n'y a plus d'erreur HTTP 500 +3. Consulter la page device detail dans l'interface web +4. Vérifier l'affichage des nouvelles données RAM et réseau diff --git a/USAGE_DEBUG.md b/USAGE_DEBUG.md new file mode 100644 index 0000000..43909d5 --- /dev/null +++ b/USAGE_DEBUG.md @@ -0,0 +1,267 @@ +# Mode DEBUG - Guide d'utilisation + +## Configuration du mode DEBUG + +Le mode DEBUG permet d'afficher le payload JSON complet avant l'envoi au serveur et de le sauvegarder dans un fichier. + +### Option 1 : Activer directement dans le script (permanent) + +Éditez le fichier `scripts/bench.sh` à la ligne 37 : + +```bash +# Avant (debug désactivé) +DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-0}" + +# Après (debug activé en permanence) +DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-1}" +``` + +Puis exécutez normalement : +```bash +sudo bash scripts/bench.sh +``` + +### Option 2 : Activer via variable d'environnement (temporaire) + +Sans modifier le script, utilisez : +```bash +DEBUG_PAYLOAD=1 sudo bash scripts/bench.sh +``` + +**Note** : Cette méthode nécessite `DEBUG_PAYLOAD=1` à chaque exécution. + +## Ce que fait le mode DEBUG + +Lorsque `DEBUG_PAYLOAD=1` : + +1. **Affiche le payload complet** formaté avec `jq` : + ``` + ════════════════════════════════════════════════════════ + DEBUG: Payload JSON complet + ════════════════════════════════════════════════════════ + { + "device_identifier": "lenovo-bureau", + "bench_script_version": "1.1.0", + "hardware": { + "cpu": { + "vendor": "GenuineIntel", + "model": "Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz", + "cores": 4, + "threads": 4, + ... + }, + "ram": { + "total_mb": 7771, + "used_mb": 4530, + "free_mb": 1291, + "shared_mb": 1950, + ... + }, + "network": { + "interfaces": [ + { + "name": "eno1", + "type": "ethernet", + "mac": "xx:xx:xx:xx:xx:xx", + "ip": "10.0.1.169", + "speed_mbps": 1000, + "wake_on_lan": true + } + ] + } + }, + "results": { + "cpu": { "score": 12.17, ... }, + "memory": { "score": 0.0, ... }, + "disk": { "score": 67.65, ... }, + "network": { + "upload_mbps": 427.86, + "download_mbps": 398.94, + "ping_ms": 44.16, + "score": 41.34 + }, + "global_score": 25.15 + } + } + ════════════════════════════════════════════════════════ + ``` + +2. **Sauvegarde dans un fichier** avec timestamp : + ``` + ✓ Payload sauvegardé dans: /tmp/bench_payload_20251207_234512.json + ``` + +3. **Attend confirmation** avant d'envoyer : + ``` + Appuyez sur Entrée pour continuer l'envoi ou Ctrl+C pour annuler... + ``` + +## Utilisation pratique + +### Vérifier les données avant envoi + +```bash +# Activer le debug +DEBUG_PAYLOAD=1 sudo bash scripts/bench.sh + +# Le script s'arrête avant l'envoi +# Vous pouvez alors : +# - Vérifier le payload affiché +# - Consulter le fichier JSON sauvegardé +# - Appuyer sur Entrée pour continuer +# - Ou Ctrl+C pour annuler +``` + +### Sauvegarder le payload sans envoyer + +```bash +# Lancer avec debug +DEBUG_PAYLOAD=1 sudo bash scripts/bench.sh + +# Quand le script demande confirmation : +# Appuyez sur Ctrl+C pour annuler l'envoi + +# Le payload est déjà sauvegardé dans /tmp/bench_payload_*.json +``` + +### Analyser les payloads sauvegardés + +```bash +# Lister tous les payloads sauvegardés +ls -lh /tmp/bench_payload_*.json + +# Afficher le dernier payload +jq '.' /tmp/bench_payload_*.json | tail -n 1 + +# Comparer deux payloads +diff <(jq -S '.' /tmp/bench_payload_20251207_234512.json) \ + <(jq -S '.' /tmp/bench_payload_20251207_235612.json) +``` + +## Vérifications importantes + +Avec le mode DEBUG activé, vous pouvez vérifier que : + +### ✅ Données RAM complètes +```json +"ram": { + "total_mb": 7771, + "used_mb": 4530, // Présent ✓ + "free_mb": 1291, // Présent ✓ + "shared_mb": 1950 // Présent ✓ +} +``` + +### ✅ Données réseau Wake-on-LAN +```json +"network": { + "interfaces": [ + { + "wake_on_lan": true // Présent ✓ + } + ] +} +``` + +### ✅ Résultats iperf3 +```json +"results": { + "network": { + "upload_mbps": 427.86, // Présent ✓ + "download_mbps": 398.94, // Présent ✓ + "ping_ms": 44.16, // Présent ✓ + "score": 41.34 + } +} +``` + +## Désactiver le mode DEBUG + +### Méthode 1 : Dans le script (permanent) + +Éditez `scripts/bench.sh` ligne 37 : +```bash +DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-0}" # Remettre à 0 +``` + +### Méthode 2 : Sans variable d'environnement + +Simplement exécuter : +```bash +sudo bash scripts/bench.sh # Sans DEBUG_PAYLOAD=1 +``` + +## Nettoyage des fichiers debug + +Les fichiers de debug s'accumulent dans `/tmp/`. Pour les nettoyer : + +```bash +# Supprimer tous les payloads de debug +rm /tmp/bench_payload_*.json + +# Ou supprimer les payloads de plus de 7 jours +find /tmp -name "bench_payload_*.json" -mtime +7 -delete +``` + +## Cas d'usage + +### 1. Développement / Tests +Activez le debug en permanence dans le script pendant le développement. + +### 2. Debugging d'erreurs +Si vous avez une erreur HTTP 422 ou 500, activez le debug pour voir exactement ce qui est envoyé. + +### 3. Validation après modifications +Après modification du script, vérifiez que toutes les données attendues sont présentes. + +### 4. Documentation +Sauvegardez un payload exemple pour la documentation de l'API. + +## Exemple complet + +```bash +# Terminal 1 : Lancer avec debug +gilles@serveur:~$ DEBUG_PAYLOAD=1 sudo bash scripts/bench.sh + +# ... Le script collecte les données ... + +# Affichage du payload complet +════════════════════════════════════════════════════════ +DEBUG: Payload JSON complet +════════════════════════════════════════════════════════ +{ + "device_identifier": "lenovo-bureau", + ... +} +════════════════════════════════════════════════════════ + +✓ Payload sauvegardé dans: /tmp/bench_payload_20251207_234512.json + +Appuyez sur Entrée pour continuer l'envoi ou Ctrl+C pour annuler... + +# [Vous appuyez sur Entrée] + +✓ Envoi du payload vers: http://10.0.1.97:8007/api/benchmark +✓ Payload envoyé avec succès (HTTP 200) + +# Terminal 2 : Analyser le payload sauvegardé +gilles@serveur:~$ jq '.hardware.ram' /tmp/bench_payload_20251207_234512.json +{ + "total_mb": 7771, + "used_mb": 4530, + "free_mb": 1291, + "shared_mb": 1950, + ... +} +``` + +## Résumé + +| Action | Commande | +|--------|----------| +| Activer temporairement | `DEBUG_PAYLOAD=1 sudo bash scripts/bench.sh` | +| Activer en permanence | Éditer ligne 37 du script : `DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-1}"` | +| Désactiver | `sudo bash scripts/bench.sh` (sans DEBUG_PAYLOAD=1) | +| Voir les payloads sauvegardés | `ls /tmp/bench_payload_*.json` | +| Analyser un payload | `jq '.' /tmp/bench_payload_YYYYMMDD_HHMMSS.json` | +| Nettoyer | `rm /tmp/bench_payload_*.json` | diff --git a/analyse_chatgpt.md b/analyse_chatgpt.md new file mode 100644 index 0000000..e5d59f2 --- /dev/null +++ b/analyse_chatgpt.md @@ -0,0 +1,2457 @@ + +Vous avez dit : +sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run +sysbench 1.0.20 (using system LuaJIT 2.1.1700206165) + +Running the test with following options: +Number of threads: 4 +Initializing random number generator from current time + + +Prime numbers limit: 20000 + +Initializing worker threads... + +Threads started! + +CPU speed: + events per second: 1274.88 + +General statistics: + total time: 10.0029s + total number of events: 12755 + +Latency (ms): + min: 2.86 + avg: 3.14 + max: 20.37 + 95th percentile: 4.10 + sum: 39998.99 + +Threads fairness: + events (avg/stddev): 3188.7500/80.13 + execution time (avg/stddev): 9.9997/0.00 peut tu m'expliquer en detail +ChatGPT a dit : + +Voici ce que signifie chaque ligne de ton test sysbench cpu et comment l’interpréter. +1. La commande + +sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run + + cpu : tu lances le test CPU intégré à sysbench. + + --cpu-max-prime=20000 : pour chaque “événement”, sysbench calcule les nombres premiers jusqu’à 20 000. Plus cette valeur est grande, plus chaque événement est lourd. + + --threads=$(nproc) : tu utilises autant de threads que de cœurs logiques détectés par le système. + Dans ton cas, sysbench affiche ensuite : + + Number of threads: 4 + + Donc la machine a 4 cœurs logiques (ou tu as limité à 4). + +2. Début du test + +Prime numbers limit: 20000 + +Initializing worker threads... + +Threads started! + + Prime numbers limit: 20000 confirme que chaque “événement” = calcul des nombres premiers jusqu’à 20 000. + + worker threads : sysbench lance 4 threads en parallèle. + + Chaque thread enchaîne ces calculs jusqu’à ce que la durée du test (par défaut 10 s) soit atteinte. + +3. CPU speed – events per second + +CPU speed: + events per second: 1274.88 + + Un event = “un passage complet de l’algorithme qui teste tous les nombres jusqu’à 20000 pour trouver les nombres premiers”. + + events per second: 1274.88 signifie : + + Au total, l’ensemble des 4 threads a réalisé environ 1 275 événements par seconde. + + Sur ~10 s, ça donne ~12 755 événements (on le voit plus bas). + +On peut estimer le volume de calcul brut : + + 1 événement ≈ 20 000 nombres testés. + + Nombre total d’événements : 12 755. + + Nombre approximatif de tests effectués : + + 12 755 × 20 000 = 255 100 000 opérations de test de nombres (ordre de grandeur). + +Ce n’est pas un benchmark “scientifique”, mais ça donne une idée des capacités de la machine sur un calcul entier simple monothread/multithread. +4. General statistics + +General statistics: + total time: 10.0029s + total number of events: 12755 + + total time: 10.0029s : le test a duré 10 s (valeur par défaut), plus la petite dérive (0.0029 s). + + total number of events: 12755 : c’est le nombre total d’itérations (tous threads confondus) réalisées pendant ces 10 s. + +Vérification rapide : + + 12 755 événements / 10.0029 s ≈ 1 274.88 événements/s + → cohérent avec la ligne events per second. + +5. Latency + +Latency (ms): + min: 2.86 + avg: 3.14 + max: 20.37 + 95th percentile: 4.10 + sum: 39998.99 + +Ici, la “latence” = le temps que met un événement à s’exécuter (calcul des primes jusqu’à 20000), mesuré pour chaque événement. + + min: 2.86 ms + L’événement le plus rapide a pris 2,86 millisecondes. + + avg: 3.14 ms + En moyenne, un événement dure 3,14 ms. + + max: 20.37 ms + Le plus lent des événements a pris 20,37 ms. + + Cela peut être dû à une interruption, un scheduling du CPU, un autre processus qui se réveille, etc. + + 95th percentile: 4.10 ms + 95 % des événements ont une durée ≤ 4,10 ms. + + Cela donne une idée de la stabilité du temps de calcul : la majorité des événements sont proches de 3–4 ms. + + sum: 39998.99 ms + Somme de toutes les latences mesurées pour les événements. + Ce chiffre est en millisecondes, donc ≈ 40 000 ms ≈ 40 s. + +Pourquoi 40 s alors que le test dure 10 s ? + + Tu as 4 threads. + + Chaque thread a travaillé environ 10 s. + + 4 threads × 10 s ≈ 40 s de temps CPU cumulé. + → Ce “sum” est cohérent : c’est la somme du temps passé par tous les threads, pas seulement le temps “mur”. + +6. Threads fairness (équité des threads) + +Threads fairness: + events (avg/stddev): 3188.7500/80.13 + execution time (avg/stddev): 9.9997/0.00 + + events (avg/stddev): 3188.7500/80.13 + + En moyenne, chaque thread a traité environ 3 188,75 événements. + + Avec un écart-type de ~80, ce qui signifie que les threads ont été assez bien équilibrés : + + Certains threads ont fait un peu plus, d’autres un peu moins, mais la différence reste faible. + + Vérification : + + 3 188,75 événements/thread × 4 threads ≈ 12 755 événements → cohérent. + + execution time (avg/stddev): 9.9997/0.00 + + Chaque thread a tourné en moyenne 9,9997 s, avec un écart-type à 0,00. + + Donc les 4 threads ont travaillé quasiment exactement la même durée, ce qui est normal sur ce type de test. + +7. Comment interpréter ce résultat pour ta machine + +En résumé : + + Ta machine (4 threads) réalise : + + Environ 1 275 itérations complètes de calcul de nombres premiers jusqu’à 20 000 par seconde. + + Soit ~255 millions de tests de nombres en ~10 secondes. + + La latence par tâche est stable : en moyenne 3 ms, 95 % des tâches en dessous de 4,1 ms. + + Répartition du travail : + + Les 4 threads sont bien utilisés (même temps d’exécution, nombre d’événements proche). + +Ce test est surtout utile : + + Pour comparer avant/après (overclock, changement de noyau, options d’énergie, virtualisation, etc.). + + Pour comparer plusieurs machines entre elles en gardant exactement les mêmes paramètres (--cpu-max-prime, --threads, durée, etc.). + +Si tu veux aller plus loin, on peut : + + Comparer ce résultat avec une autre machine. + + Tester avec d’autres valeurs de --cpu-max-prime (par ex. 10000 / 50000) pour voir l’impact. + + Lancer avec un seul thread (--threads=1) pour voir les performances monothread. + +Vous avez dit : +quel sont donc les donnée importante a reterer de ce bench ? +ChatGPT a dit : + +Voici les seules données réellement importantes à retenir dans un bench sysbench cpu, c’est-à-dire celles qui serviront à comparer des machines, évaluer une optimisation, ou diagnostiquer un problème CPU. +1. events per second (EPS) → INDICATEUR PRINCIPAL + +events per second: 1274.88 + +C’est la métrique la plus importante. + + C’est le débit de calcul : combien d’itérations complètes (prime jusqu’à 20000) la machine exécute par seconde. + + Plus la valeur est haute, plus le CPU est rapide. + + C’est celle qu’on compare entre machines. + +À retenir absolument. +2. total number of events + +total number of events: 12755 + + Confirme la cohérence du résultat. + + Utile pour valider que le test n’a pas eu de ralentissements majeurs. + +3. latency avg + 95th percentile + +avg latency: 3.14 ms +95th percentile: 4.10 ms + +Ce sont les deux valeurs de latence importantes : + + avg donne la stabilité moyenne. + + 95th percentile indique si le CPU a eu des “pics” de lenteur (interruptions, scheduling). + +Si le 95th est proche de l’avg → CPU stable. +Si le 95th explose → le système est perturbé. +4. threads fairness + +events (avg/stddev): 3188.75 / 80.13 +execution time stddev: 0.00 + +À retenir uniquement si tu veux vérifier : + + que les threads ont travaillé équitablement, + + qu’un core n’était pas bridé ou occupé par un autre processus. + +Écart-type faible → répartition normale. +5. (optionnel) Nombre de threads et niveau de charge + +Number of threads: 4 + +À retenir uniquement pour contextualiser le benchmark. +On ne compare pas une machine à 4 threads avec une machine à 16 threads. +Résumé ultra-court : les 3 valeurs qui comptent +Donnée Pourquoi c’est important +events per second Vitesse globale du CPU (métrique clé) +latency avg / 95th Stabilité du calcul et absence de perturbation +threads fairness Vérifie que tous les cœurs ont été utilisés correctement +Si tu veux je peux te faire un tableau de comparaison pour d’autres machines que tu testes ensuite. +Vous avez dit : +et si je veut faire le test sur un seul thread ? +ChatGPT a dit : + +Pour exécuter sysbench CPU sur un seul thread, tu utilises simplement : + +sysbench cpu --cpu-max-prime=20000 --threads=1 run + +Pourquoi faire un test 1 thread ? + +Le test en thread unique mesure : + + la performance monothread pure, + + la qualité du core, + + l’impact de la fréquence CPU, + + la latence liée au scheduler, + + l’efficacité IPC (instructions par cycle). + +C’est souvent plus représentatif des performances réelles dans : + + tâches Python, + + jeux vidéo, + + I/O filesystem, + + services web mono-worker, + + automatisations HA ou scripts, + + VM légères. + +Ce que tu dois comparer dans un test 1 thread + +Tu dois extraire ces données : + + events per second → métrique principale + + avg latency (ms) + + 95th percentile latency + + max latency (pour repérer les “spikes”) + +Le reste est secondaire. +Exemple de comparaison 1 thread vs multi-thread + +Si tu veux, tu peux me donner ton résultat 1 thread et je te fais une analyse : + + performance monothread brute, + + scaling des performances (1 thread → 4 threads), + + efficacité CPU, + + éventuels problèmes thermiques ou de scheduling. + +Si tu veux, je peux aussi te proposer un tableau type pour suivre tous tes benchmarks CPU sur tes machines Debian, Proxmox, NAS, VM, laptop et mini PC. +Vous avez dit : +pourquoi un tel ecart entre ce 2 commandes : sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run +sysbench 1.0.20 (using system LuaJIT 2.1.1700206165) + +Running the test with following options: +Number of threads: 4 +Initializing random number generator from current time + + +Prime numbers limit: 20000 + +Initializing worker threads... + +Threads started! + +CPU speed: + events per second: 1203.40 + +General statistics: + total time: 10.0006s + total number of events: 12037 + +Latency (ms): + min: 2.83 + avg: 3.32 + max: 24.05 + 95th percentile: 6.91 + sum: 39995.46 + +Threads fairness: + events (avg/stddev): 3009.2500/43.11 + execution time (avg/stddev): 9.9989/0.00 + +gilles@lenovo-bureau:~/Documents/vscode$ sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run 2>&1 | grep 'events per second' + events per second: 1247.67 +ChatGPT a dit : + +Voici la raison exacte pour laquelle tu vois un écart entre : + + la commande (A) + +sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run + +→ 1203 events/sec + +et + + la commande (B) + +sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run 2>&1 | grep 'events per second' + +→ 1247 events/sec + +Cet écart est normal et s’explique par 3 facteurs cumulés. +1. sysbench relance le test à chaque commande + +Chaque exécution de sysbench est un nouveau test, avec : + + une nouvelle initialisation, + + un nouvel état du CPU, + + une charge système potentiellement différente, + + une température différente, + + une fréquence CPU dynamique (Intel P-states), + + potentiellement des interruptions différentes. + +Donc deux commandes successives ne donneront jamais exactement le même score. + +Un écart de : + + 3 à 5 % est totalement normal sur une machine “vivante” + + Ici : 1247.67 vs 1203.40 = +3.67 % → parfait, dans la norme. + +2. L’utilisation de grep change parfois la sortie, mais pas le calcul + +Important : + +grep ne modifie pas le benchmark, il lit seulement le stdout après l’exécution du test. + +Donc l’augmentation n’est pas causée par grep. + +La cause = nouvelle exécution → nouvelles conditions du CPU. +3. Variations naturelles de fréquence CPU (Intel SpeedShift / Turbo Boost) + +Sysbench CPU est un benchmark pur entier / calcul continu. + +La fréquence du CPU peut varier : + + car il chauffe, + + car le scheduler Linux utilise d’autres tâches, + + car le Turbo Boost s’active/désactive, + + car l’état énergétique change (C-states), + + car l’alimentation varie (secteur vs batterie), + + parce que le CPU est revenu au repos entre deux commandes. + +→ un CPU peut perdre 50 à 100 events/sec juste à cause du passage d’une fréquence à une autre. +4. Température CPU = impact direct sur les performances + +Si la première commande chauffe le CPU, la seconde peut tourner à fréquence un peu plus basse, ou l’inverse. + +Les écarts typiques : +Température Fréquence Impact +CPU froid Turbo max events/sec ↑ +CPU chaud Turbo réduit events/sec ↓ + +Ici, ton écart est positif → le CPU était probablement un peu plus chaud ou occupé au premier test. +Synthèse : pourquoi l’écart existe ? + +Parce que tu as lancé deux fois un benchmark complet. +Chaque lancement produit naturellement une variation de performance, due aux fluctuations : + + du scheduler, + + du Turbo Boost, + + de la température, + + du bruit système, + + des interruptions kernel. + +L’écart de 3–4 % est parfaitement normal sur un CPU grand public. +Si tu veux une comparaison fiable + +Il faut exécuter par exemple : + +for i in {1..10}; do + sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run | grep 'events per second'; +done + +Ensuite je te calcule automatiquement : + + moyenne + + écart-type + + min + + max + +→ pour obtenir un score fiable de ta machine. + +Si tu veux, je peux te donner le script complet + un tableau d’analyse. +Vous avez dit : +events per second: 1247.67 et events (avg/stddev): 3009.2500/43.11 ? +ChatGPT a dit : + +Les deux valeurs : + +events per second: 1247.67 + +et + +events (avg/stddev): 3009.2500/43.11 + +sont liées, mais elles ne représentent pas du tout la même chose. + +Voici l’explication claire. +1. events per second = débit global de la machine + +events per second: 1247.67 + +C’est le nombre total d’événements exécutés par seconde, en additionnant tous les threads. + +Autrement dit : + +events_per_second = total_events / total_time + +Ce chiffre représente la performance totale. + +Dans ton cas : + + ~1250 events/s pour les 4 threads combinés. + +2. events (avg/stddev) = répartition par thread + +events (avg/stddev): 3009.2500/43.11 + +Ce bloc indique : + + 3009.25 = nombre d’événements par thread en moyenne + + 43.11 = variation (écart-type) entre les threads + +Donc chaque thread a traité environ 3009 événements durant les 10 secondes. +3. Vérifions que les deux chiffres concordent +Total events = avg events per thread × number of threads + +Tu as 4 threads. + +3009.25 × 4 = 12037 événements + +Et sysbench affiche : + +total number of events: 12037 + +→ C’est cohérent à 100 % +events per second = total_events / 10 s + +12037 / 10 = 1203.7 events/s + +Dans ton autre test : + +events per second: 1247.67 + +Il faut comprendre que ce sont deux exécutions différentes, donc les valeurs changent légèrement à chaque test. + +Mais la relation mathématique reste la même. +4. Synthèse simple +Valeur Signification +events per second Performance totale (tous threads combinés) +events (avg) Nombre d’événements effectués par thread +events (stddev) Écart entre les threads (équilibrage load-balancing) +5. Pourquoi les chiffres n'ont pas la même échelle ? + +Parce que : + + events per second = événements/seconde + + events (avg) = événements en 10 secondes, par thread + +Donc naturellement, les valeurs “avg per thread” sont beaucoup plus grandes (≈ 3000) que le débit instantané (≈ 1250/s). +Si tu veux, je peux te produire un schéma clair qui relie toutes les valeurs du benchmark pour bien visualiser les relations. +Vous avez dit : +gilles@lenovo-bureau:~/Documents/vscode$ lsblk -o NAME,SIZE,TYPE,MOUNTPOINT +NAME SIZE TYPE MOUNTPOINT +loop1 55,5M loop /snap/core18/2976 +loop2 55,5M loop /snap/core18/2952 +loop3 63,8M loop /snap/core20/2669 +loop4 164,8M loop /snap/gnome-3-28-1804/198 +loop5 4K loop /snap/bare/5 +loop6 50,8M loop /snap/snapd/25202 +loop7 91,7M loop /snap/gtk-common-themes/1535 +loop8 56,5M loop /snap/mqtt-explorer/29 +loop9 92,5M loop /snap/mqtt-explorer/31 +loop10 50,9M loop /snap/snapd/25577 +loop11 63,8M loop /snap/core20/2682 +sda 447,1G disk +├─sda1 7,4G part [SWAP] +├─sda2 1K part +└─sda5 439,7G part / +sdb 0B disk +ChatGPT a dit : + +Voici l’analyse détaillée de ton lsblk et ce que signifient les lignes affichées. +1. Les périphériques loopX + +loop1 ... loop11 + +Ce sont des images montées par Snap. +Elles ne sont pas de vrais disques. + +Chaque snap installé crée une image squashfs montée comme un pseudo-disque en lecture seule. + +Exemples de packages montés : +Loop Taille Package +loop1 55,5M snap core18 +loop3 63,8M snap core20 +loop4 164,8M GNOME runtime +loop6 50,8M snapd +loop8 / loop9 56,5M / 92,5M MQTT Explorer + +Normal sur Ubuntu / Debian avec Snap → rien à corriger. +2. Ton disque principal : sda + +sda 447,1G disk +├─sda1 7,4G part [SWAP] +├─sda2 1K part +└─sda5 439,7G part / + +Analyse : +sda = SSD/HDD de 447 Go + +Tu peux obtenir le modèle avec : + +sudo smartctl -i /dev/sda + +sda1 = 7,4 Go → SWAP + +Un swap de 7,4 Go est cohérent pour une machine avec 8 à 16 Go de RAM. +sda2 = 1K → partition de boot GRUB BIOS + +La petite partition de 1K est une partition étendue / legacy utilisée par certains schémas MBR. +Tu peux l’ignorer. +sda5 = 439,7 Go → partition racine / + +Toute ton installation Linux est dessus. +C’est une structure typique : + + table de partition MBR (dos) + + swap en primaire + + puis partition étendue contenant sda5 + +3. Le disque sdb = 0B + +sdb 0B disk + +Ça paraît bizarre, mais c’est normal dans deux cas : +Cas 1 : C’est un périphérique fantôme créé par le contrôleur + +Certains contrôleurs SATA/USB listent un deuxième canal vide → lsblk le voit mais sans capacité. + +→ Probable si tu n’as qu’un seul disque physique dans la machine. +Cas 2 : Une carte SD / clé USB mal reconnue + +Un support mal connecté peut apparaître comme “0B”. +Cas 3 : Un port NVMe/SATA vide exposé par le BIOS + +Très fréquent sur : + + Lenovo + + HP + + Dell + +On voit sdb mais il n’y a rien dessus → 0B. +Si tu veux l’identifier, lance : + +sudo dmesg | grep -i sdb + +ou + +sudo smartctl -i /dev/sdb + +ou + +sudo fdisk -l /dev/sdb + +Si tout répond “no such device”, c’est bien un port vide. +4. Résumé simple et utile pour toi +Périphérique Rôle Commentaire +loop1–loop11 images snap normal, ignorer +sda disque principal 447 Go +sda1 swap 7.4 Go +sda5 racine Linux 439 Go ext4 +sdb périphérique fantôme port SATA/NVMe non utilisé ou bug driver +Si tu veux, je peux te générer aussi : + + l’analyse SMART complète du disque sda, + + un diagnostic filesystem, + + l’état d’usure du SSD, + + un schéma ASCII clair de ton partitionnement. + +Tu veux aller plus loin ? +Vous avez dit : +gilles@lenovo-bureau:~/Documents/vscode$ which sysbench fio iperf3 dmidecode lscpu free jq lsblk +/usr/bin/sysbench +/usr/bin/fio +/usr/bin/iperf3 +/usr/bin/lscpu +/usr/bin/free +/usr/bin/jq +/usr/bin/lsblk +gilles@lenovo-bureau:~/Documents/vscode$ sysbench cpu --cpu-max-prime=10000 --threads=$(nproc) run +sysbench 1.0.20 (using system LuaJIT 2.1.1700206165) + +Running the test with following options: +Number of threads: 4 +Initializing random number generator from current time + + +Prime numbers limit: 10000 + +Initializing worker threads... + +Threads started! + +CPU speed: + events per second: 3409.87 + +General statistics: + total time: 10.0005s + total number of events: 34107 + +Latency (ms): + min: 1.10 + avg: 1.17 + max: 15.92 + 95th percentile: 1.23 + sum: 39987.41 + +Threads fairness: + events (avg/stddev): 8526.7500/114.76 + execution time (avg/stddev): 9.9969/0.00 + +gilles@lenovo-bureau:~/Documents/vscode$ sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run +sysbench 1.0.20 (using system LuaJIT 2.1.1700206165) + +Running the test with following options: +Number of threads: 4 +Initializing random number generator from current time + + +Prime numbers limit: 20000 + +Initializing worker threads... + +Threads started! + +CPU speed: + events per second: 1274.88 + +General statistics: + total time: 10.0029s + total number of events: 12755 + +Latency (ms): + min: 2.86 + avg: 3.14 + max: 20.37 + 95th percentile: 4.10 + sum: 39998.99 + +Threads fairness: + events (avg/stddev): 3188.7500/80.13 + execution time (avg/stddev): 9.9997/0.00 + +gilles@lenovo-bureau:~/Documents/vscode$ sysbench cpu --cpu-max-prime=10000 --threads=$(nproc) run 2>&1 | grep 'events per second' + events per second: 3371.59 +gilles@lenovo-bureau:~/Documents/vscode$ sysbench cpu --cpu-max-prime=20000 --threads=1 run +sysbench 1.0.20 (using system LuaJIT 2.1.1700206165) + +Running the test with following options: +Number of threads: 1 +Initializing random number generator from current time + + +Prime numbers limit: 20000 + +Initializing worker threads... + +Threads started! + +CPU speed: + events per second: 357.09 + +General statistics: + total time: 10.0002s + total number of events: 3572 + +Latency (ms): + min: 2.70 + avg: 2.80 + max: 4.75 + 95th percentile: 2.91 + sum: 9997.51 + +Threads fairness: + events (avg/stddev): 3572.0000/0.00 + execution time (avg/stddev): 9.9975/0.00 + +gilles@lenovo-bureau:~/Documents/vscode$ sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run 2>&1 | grep 'events per second' + events per second: 1209.68 +gilles@lenovo-bureau:~/Documents/vscode$ sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run 2>&1 | grep 'events per second' + events per second: 1222.35 +gilles@lenovo-bureau:~/Documents/vscode$ sysbench cpu --cpu-max-prime=10000 --threads=$(nproc) run 2>&1 | grep 'events per second' + events per second: 3286.23 +gilles@lenovo-bureau:~/Documents/vscode$ sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run +sysbench 1.0.20 (using system LuaJIT 2.1.1700206165) + +Running the test with following options: +Number of threads: 4 +Initializing random number generator from current time + + +Prime numbers limit: 20000 + +Initializing worker threads... + +Threads started! + +CPU speed: + events per second: 1203.40 + +General statistics: + total time: 10.0006s + total number of events: 12037 + +Latency (ms): + min: 2.83 + avg: 3.32 + max: 24.05 + 95th percentile: 6.91 + sum: 39995.46 + +Threads fairness: + events (avg/stddev): 3009.2500/43.11 + execution time (avg/stddev): 9.9989/0.00 + +gilles@lenovo-bureau:~/Documents/vscode$ sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run 2>&1 | grep 'events per second' + events per second: 1247.67 +gilles@lenovo-bureau:~/Documents/vscode$ sysbench memory --memory-block-size=1M --memory-total-size=512M run +sysbench 1.0.20 (using system LuaJIT 2.1.1700206165) + +Running the test with following options: +Number of threads: 1 +Initializing random number generator from current time + + +Running memory speed test with the following options: + block size: 1024KiB + total size: 512MiB + operation: write + scope: global + +Initializing worker threads... + +Threads started! + +Total operations: 512 (13806.03 per second) + +512.00 MiB transferred (13806.03 MiB/sec) + + +General statistics: + total time: 0.0351s + total number of events: 512 + +Latency (ms): + min: 0.06 + avg: 0.07 + max: 2.47 + 95th percentile: 0.07 + sum: 34.40 + +Threads fairness: + events (avg/stddev): 512.0000/0.00 + execution time (avg/stddev): 0.0344/0.00 + +gilles@lenovo-bureau:~/Documents/vscode$ sysbench memory --memory-block-size=1M --memory-total-size=1G run +sysbench 1.0.20 (using system LuaJIT 2.1.1700206165) + +Running the test with following options: +Number of threads: 1 +Initializing random number generator from current time + + +Running memory speed test with the following options: + block size: 1024KiB + total size: 1024MiB + operation: write + scope: global + +Initializing worker threads... + +Threads started! + +Total operations: 1024 (15213.54 per second) + +1024.00 MiB transferred (15213.54 MiB/sec) + + +General statistics: + total time: 0.0645s + total number of events: 1024 + +Latency (ms): + min: 0.06 + avg: 0.06 + max: 0.11 + 95th percentile: 0.07 + sum: 63.82 + +Threads fairness: + events (avg/stddev): 1024.0000/0.00 + execution time (avg/stddev): 0.0638/0.00 + +gilles@lenovo-bureau:~/Documents/vscode$ sysbench memory --memory-block-size=1M --memory-total-size=512M run 2>&1 | grep 'MiB/sec' +512.00 MiB transferred (13200.66 MiB/sec) +gilles@lenovo-bureau:~/Documents/vscode$ fio --name=seqread --ioengine=libaio --rw=read --bs=1M --size=1G --numjobs=1 --direct=1 --filename=/tmp/fio-test-file +seqread: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=1 +fio-3.39 +Starting 1 process +seqread: Laying out IO file (1 file / 1024MiB) + +seqread: (groupid=0, jobs=1): err= 0: pid=187410: Sun Dec 7 18:59:47 2025 + read: IOPS=2592, BW=2592MiB/s (2718MB/s)(1024MiB/395msec) + slat (usec): min=117, max=23346, avg=380.74, stdev=1290.59 + clat (nsec): min=977, max=26622, avg=2359.69, stdev=1741.78 + lat (usec): min=118, max=23357, avg=383.09, stdev=1291.52 + clat percentiles (nsec): + | 1.00th=[ 1128], 5.00th=[ 1256], 10.00th=[ 1352], 20.00th=[ 1464], + | 30.00th=[ 1592], 40.00th=[ 1720], 50.00th=[ 1880], 60.00th=[ 2096], + | 70.00th=[ 2352], 80.00th=[ 2736], 90.00th=[ 3440], 95.00th=[ 5088], + | 99.00th=[ 9536], 99.50th=[10688], 99.90th=[18304], 99.95th=[26496], + | 99.99th=[26496] + lat (nsec) : 1000=0.20% + lat (usec) : 2=55.27%, 4=37.11%, 10=6.54%, 20=0.78%, 50=0.10% + cpu : usr=0.00%, sys=56.60%, ctx=1038, majf=0, minf=263 + IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% + submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + issued rwts: total=1024,0,0,0 short=0,0,0,0 dropped=0,0,0,0 + latency : target=0, window=0, percentile=100.00%, depth=1 + +Run status group 0 (all jobs): + READ: bw=2592MiB/s (2718MB/s), 2592MiB/s-2592MiB/s (2718MB/s-2718MB/s), io=1024MiB (1074MB), run=395-395msec +gilles@lenovo-bureau:~/Documents/vscode$ fio --name=seqwrite --ioengine=libaio --rw=write --bs=1M --size=1G --numjobs=1 --direct=1 --filename=/tmp/fio-test-file +seqwrite: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=1 +fio-3.39 +Starting 1 process + +seqwrite: (groupid=0, jobs=1): err= 0: pid=187837: Sun Dec 7 19:00:17 2025 + write: IOPS=1432, BW=1432MiB/s (1502MB/s)(1024MiB/715msec); 0 zone resets + slat (usec): min=204, max=11961, avg=694.16, stdev=1520.00 + clat (nsec): min=1065, max=17330, avg=2072.35, stdev=1337.13 + lat (usec): min=206, max=11969, avg=696.24, stdev=1521.09 + clat percentiles (nsec): + | 1.00th=[ 1112], 5.00th=[ 1224], 10.00th=[ 1288], 20.00th=[ 1400], + | 30.00th=[ 1480], 40.00th=[ 1576], 50.00th=[ 1688], 60.00th=[ 1816], + | 70.00th=[ 2008], 80.00th=[ 2256], 90.00th=[ 2832], 95.00th=[ 4704], + | 99.00th=[ 7136], 99.50th=[ 8512], 99.90th=[13632], 99.95th=[17280], + | 99.99th=[17280] + bw ( KiB/s): min=917504, max=917504, per=62.56%, avg=917504.00, stdev= 0.00, samples=1 + iops : min= 896, max= 896, avg=896.00, stdev= 0.00, samples=1 + lat (usec) : 2=69.34%, 4=22.75%, 10=7.62%, 20=0.29% + cpu : usr=1.68%, sys=56.02%, ctx=2531, majf=0, minf=8 + IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% + submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + issued rwts: total=0,1024,0,0 short=0,0,0,0 dropped=0,0,0,0 + latency : target=0, window=0, percentile=100.00%, depth=1 + +Run status group 0 (all jobs): + WRITE: bw=1432MiB/s (1502MB/s), 1432MiB/s-1432MiB/s (1502MB/s-1502MB/s), io=1024MiB (1074MB), run=715-715msec +gilles@lenovo-bureau:~/Documents/vscode$ fio --name=randrw --ioengine=libaio --rw=randrw --bs=4k --size=100M --numjobs=1 --direct=1 --filename=/tmp/fio-test-file +randrw: (g=0): rw=randrw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=1 +fio-3.39 +Starting 1 process + +randrw: (groupid=0, jobs=1): err= 0: pid=188034: Sun Dec 7 19:00:29 2025 + read: IOPS=175k, BW=685MiB/s (718MB/s)(50.0MiB/73msec) + slat (nsec): min=1268, max=18616, avg=1555.05, stdev=490.87 + clat (nsec): min=676, max=14956, avg=734.49, stdev=251.65 + lat (nsec): min=1993, max=26902, avg=2289.55, stdev=581.73 + clat percentiles (nsec): + | 1.00th=[ 692], 5.00th=[ 700], 10.00th=[ 708], 20.00th=[ 716], + | 30.00th=[ 716], 40.00th=[ 716], 50.00th=[ 724], 60.00th=[ 724], + | 70.00th=[ 732], 80.00th=[ 740], 90.00th=[ 748], 95.00th=[ 764], + | 99.00th=[ 820], 99.50th=[ 860], 99.90th=[ 5216], 99.95th=[ 8096], + | 99.99th=[ 8256] + write: IOPS=175k, BW=685MiB/s (718MB/s)(50.0MiB/73msec); 0 zone resets + slat (nsec): min=1326, max=67794, avg=1571.91, stdev=710.30 + clat (nsec): min=679, max=32515, avg=735.70, stdev=442.05 + lat (nsec): min=2044, max=70714, avg=2307.61, stdev=872.94 + clat percentiles (nsec): + | 1.00th=[ 692], 5.00th=[ 700], 10.00th=[ 700], 20.00th=[ 708], + | 30.00th=[ 716], 40.00th=[ 716], 50.00th=[ 716], 60.00th=[ 724], + | 70.00th=[ 724], 80.00th=[ 732], 90.00th=[ 748], 95.00th=[ 764], + | 99.00th=[ 804], 99.50th=[ 844], 99.90th=[ 7968], 99.95th=[ 8256], + | 99.99th=[21888] + lat (nsec) : 750=91.15%, 1000=8.63% + lat (usec) : 2=0.11%, 4=0.01%, 10=0.09%, 20=0.01%, 50=0.01% + cpu : usr=36.11%, sys=58.33%, ctx=3, majf=0, minf=10 + IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% + submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + issued rwts: total=12800,12800,0,0 short=0,0,0,0 dropped=0,0,0,0 + latency : target=0, window=0, percentile=100.00%, depth=1 + +Run status group 0 (all jobs): + READ: bw=685MiB/s (718MB/s), 685MiB/s-685MiB/s (718MB/s-718MB/s), io=50.0MiB (52.4MB), run=73-73msec + WRITE: bw=685MiB/s (718MB/s), 685MiB/s-685MiB/s (718MB/s-718MB/s), io=50.0MiB (52.4MB), run=73-73msec +gilles@lenovo-bureau:~/Documents/vscode$ fio --name=test --ioengine=libaio --rw=randrw --bs=4k --size=100M --numjobs=1 --direct=1 --filename=/tmp/fio-test-file --output-format=json +{ + "fio version" : "fio-3.39", + "timestamp" : 1765130445, + "timestamp_ms" : 1765130445593, + "time" : "Sun Dec 7 19:00:45 2025", + "jobs" : [ + { + "jobname" : "test", + "groupid" : 0, + "job_start" : 1765130445203, + "error" : 0, + "eta" : 2147483647, + "elapsed" : 1, + "job options" : { + "name" : "test", + "ioengine" : "libaio", + "rw" : "randrw", + "bs" : "4k", + "size" : "100M", + "numjobs" : "1", + "direct" : "1", + "filename" : "/tmp/fio-test-file" + }, + "read" : { + "io_bytes" : 52428800, + "io_kbytes" : 51200, + "bw_bytes" : 728177777, + "bw" : 711111, + "iops" : 177777.777778, + "runtime" : 72, + "total_ios" : 12800, + "short_ios" : 0, + "drop_ios" : 0, + "slat_ns" : { + "min" : 1253, + "max" : 25366, + "mean" : 1558.008203, + "stddev" : 521.711230, + "N" : 12800 + }, + "clat_ns" : { + "min" : 679, + "max" : 19250, + "mean" : 729.195469, + "stddev" : 281.986042, + "N" : 12800, + "percentile" : { + "1.000000" : 692, + "5.000000" : 700, + "10.000000" : 700, + "20.000000" : 708, + "30.000000" : 708, + "40.000000" : 716, + "50.000000" : 716, + "60.000000" : 716, + "70.000000" : 724, + "80.000000" : 724, + "90.000000" : 740, + "95.000000" : 764, + "99.000000" : 812, + "99.500000" : 860, + "99.900000" : 8032, + "99.950000" : 8096, + "99.990000" : 8256 + } + }, + "lat_ns" : { + "min" : 1966, + "max" : 26734, + "mean" : 2287.203672, + "stddev" : 621.748655, + "N" : 12800 + }, + "bw_min" : 0, + "bw_max" : 0, + "bw_agg" : 0.000000, + "bw_mean" : 0.000000, + "bw_dev" : 0.000000, + "bw_samples" : 0, + "iops_min" : 0, + "iops_max" : 0, + "iops_mean" : 0.000000, + "iops_stddev" : 0.000000, + "iops_samples" : 0 + }, + "write" : { + "io_bytes" : 52428800, + "io_kbytes" : 51200, + "bw_bytes" : 728177777, + "bw" : 711111, + "iops" : 177777.777778, + "runtime" : 72, + "total_ios" : 12800, + "short_ios" : 0, + "drop_ios" : 0, + "slat_ns" : { + "min" : 1321, + "max" : 24501, + "mean" : 1569.442187, + "stddev" : 501.083557, + "N" : 12800 + }, + "clat_ns" : { + "min" : 678, + "max" : 17589, + "mean" : 729.085625, + "stddev" : 345.327276, + "N" : 12800, + "percentile" : { + "1.000000" : 692, + "5.000000" : 692, + "10.000000" : 700, + "20.000000" : 700, + "30.000000" : 708, + "40.000000" : 708, + "50.000000" : 708, + "60.000000" : 716, + "70.000000" : 716, + "80.000000" : 724, + "90.000000" : 740, + "95.000000" : 756, + "99.000000" : 812, + "99.500000" : 900, + "99.900000" : 8096, + "99.950000" : 8160, + "99.990000" : 15808 + } + }, + "lat_ns" : { + "min" : 2025, + "max" : 25994, + "mean" : 2298.527813, + "stddev" : 629.810410, + "N" : 12800 + }, + "bw_min" : 0, + "bw_max" : 0, + "bw_agg" : 0.000000, + "bw_mean" : 0.000000, + "bw_dev" : 0.000000, + "bw_samples" : 0, + "iops_min" : 0, + "iops_max" : 0, + "iops_mean" : 0.000000, + "iops_stddev" : 0.000000, + "iops_samples" : 0 + }, + "trim" : { + "io_bytes" : 0, + "io_kbytes" : 0, + "bw_bytes" : 0, + "bw" : 0, + "iops" : 0.000000, + "runtime" : 0, + "total_ios" : 0, + "short_ios" : 0, + "drop_ios" : 0, + "slat_ns" : { + "min" : 0, + "max" : 0, + "mean" : 0.000000, + "stddev" : 0.000000, + "N" : 0 + }, + "clat_ns" : { + "min" : 0, + "max" : 0, + "mean" : 0.000000, + "stddev" : 0.000000, + "N" : 0 + }, + "lat_ns" : { + "min" : 0, + "max" : 0, + "mean" : 0.000000, + "stddev" : 0.000000, + "N" : 0 + }, + "bw_min" : 0, + "bw_max" : 0, + "bw_agg" : 0.000000, + "bw_mean" : 0.000000, + "bw_dev" : 0.000000, + "bw_samples" : 0, + "iops_min" : 0, + "iops_max" : 0, + "iops_mean" : 0.000000, + "iops_stddev" : 0.000000, + "iops_samples" : 0 + }, + "sync" : { + "total_ios" : 0, + "lat_ns" : { + "min" : 0, + "max" : 0, + "mean" : 0.000000, + "stddev" : 0.000000, + "N" : 0 + } + }, + "job_runtime" : 71, + "usr_cpu" : 32.394366, + "sys_cpu" : 64.788732, + "ctx" : 0, + "majf" : 0, + "minf" : 10, + "iodepth_level" : { + "1" : 100.000000, + "2" : 0.000000, + "4" : 0.000000, + "8" : 0.000000, + "16" : 0.000000, + "32" : 0.000000, + ">=64" : 0.000000 + }, + "iodepth_submit" : { + "0" : 0.000000, + "4" : 100.000000, + "8" : 0.000000, + "16" : 0.000000, + "32" : 0.000000, + "64" : 0.000000, + ">=64" : 0.000000 + }, + "iodepth_complete" : { + "0" : 0.000000, + "4" : 100.000000, + "8" : 0.000000, + "16" : 0.000000, + "32" : 0.000000, + "64" : 0.000000, + ">=64" : 0.000000 + }, + "latency_ns" : { + "2" : 0.000000, + "4" : 0.000000, + "10" : 0.000000, + "20" : 0.000000, + "50" : 0.000000, + "100" : 0.000000, + "250" : 0.000000, + "500" : 0.000000, + "750" : 92.949219, + "1000" : 6.773438 + }, + "latency_us" : { + "2" : 0.152344, + "4" : 0.000000, + "10" : 0.109375, + "20" : 0.015625, + "50" : 0.000000, + "100" : 0.000000, + "250" : 0.000000, + "500" : 0.000000, + "750" : 0.000000, + "1000" : 0.000000 + }, + "latency_ms" : { + "2" : 0.000000, + "4" : 0.000000, + "10" : 0.000000, + "20" : 0.000000, + "50" : 0.000000, + "100" : 0.000000, + "250" : 0.000000, + "500" : 0.000000, + "750" : 0.000000, + "1000" : 0.000000, + "2000" : 0.000000, + ">=2000" : 0.000000 + }, + "latency_depth" : 1, + "latency_target" : 0, + "latency_percentile" : 100.000000, + "latency_window" : 0 + } + ] +} +gilles@lenovo-bureau:~/Documents/vscode$ rm -f /tmp/fio-test-file +gilles@lenovo-bureau:~/Documents/vscode$ iperf3 -c 10.0.1.97 -t 5 +iperf3: error - unable to connect to server - server may have stopped running or use a different port, firewall issue, etc.: Connection refused +gilles@lenovo-bureau:~/Documents/vscode$ iperf3 -c 10.0.1.97 -t 5 +Connecting to host 10.0.1.97, port 5201 +[ 5] local 10.0.1.169 port 34042 connected to 10.0.1.97 port 5201 +[ ID] Interval Transfer Bitrate Retr Cwnd +[ 5] 0.00-1.00 sec 53.1 MBytes 445 Mbits/sec 1 375 KBytes +[ 5] 1.00-2.00 sec 57.0 MBytes 478 Mbits/sec 0 477 KBytes +[ 5] 2.00-3.00 sec 60.8 MBytes 510 Mbits/sec 0 566 KBytes +[ 5] 3.00-4.00 sec 62.6 MBytes 525 Mbits/sec 0 645 KBytes +[ 5] 4.00-5.00 sec 61.6 MBytes 515 Mbits/sec 0 716 KBytes +- - - - - - - - - - - - - - - - - - - - - - - - - +[ ID] Interval Transfer Bitrate Retr +[ 5] 0.00-5.00 sec 296 MBytes 496 Mbits/sec 1 sender +[ 5] 0.00-5.01 sec 293 MBytes 491 Mbits/sec receiver + +iperf Done. +gilles@lenovo-bureau:~/Documents/vscode$ iperf3 -c 10.0.1.97 -t 5 -R +Connecting to host 10.0.1.97, port 5201 +Reverse mode, remote host 10.0.1.97 is sending +[ 5] local 10.0.1.169 port 45146 connected to 10.0.1.97 port 5201 +[ ID] Interval Transfer Bitrate +[ 5] 0.00-1.00 sec 49.6 MBytes 416 Mbits/sec +[ 5] 1.00-2.00 sec 48.1 MBytes 404 Mbits/sec +[ 5] 2.00-3.00 sec 45.9 MBytes 385 Mbits/sec +[ 5] 3.00-4.00 sec 51.5 MBytes 432 Mbits/sec +[ 5] 4.00-5.00 sec 51.0 MBytes 428 Mbits/sec +- - - - - - - - - - - - - - - - - - - - - - - - - +[ ID] Interval Transfer Bitrate Retr +[ 5] 0.00-5.01 sec 249 MBytes 417 Mbits/sec 0 sender +[ 5] 0.00-5.00 sec 246 MBytes 413 Mbits/sec receiver + +iperf Done. +gilles@lenovo-bureau:~/Documents/vscode$ iperf3 -c 10.0.1.97 -t 5 -J +{ + "start": { + "connected": [{ + "socket": 5, + "local_host": "10.0.1.169", + "local_port": 50206, + "remote_host": "10.0.1.97", + "remote_port": 5201 + }], + "version": "iperf 3.18", + "system_info": "Linux lenovo-bureau 6.12.57+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.57-1 (2025-11-05) x86_64", + "timestamp": { + "time": "Sun, 07 Dec 2025 18:02:43 GMT", + "timesecs": 1765130563 + }, + "connecting_to": { + "host": "10.0.1.97", + "port": 5201 + }, + "cookie": "ejecghjijivkeodhyn5viyfm2nafnaz443zx", + "tcp_mss_default": 1448, + "target_bitrate": 0, + "fq_rate": 0, + "sock_bufsize": 0, + "sndbuf_actual": 16384, + "rcvbuf_actual": 131072, + "test_start": { + "protocol": "TCP", + "num_streams": 1, + "blksize": 131072, + "omit": 0, + "duration": 5, + "bytes": 0, + "blocks": 0, + "reverse": 0, + "tos": 0, + "target_bitrate": 0, + "bidir": 0, + "fqrate": 0, + "interval": 1 + } + }, + "intervals": [{ + "streams": [{ + "socket": 5, + "start": 0, + "end": 1.001087, + "seconds": 1.001086950302124, + "bytes": 39452672, + "bits_per_second": 315278683.73945612, + "retransmits": 0, + "snd_cwnd": 1811448, + "snd_wnd": 2893824, + "rtt": 58187, + "rttvar": 2322, + "pmtu": 1500, + "omitted": false, + "sender": true + }], + "sum": { + "start": 0, + "end": 1.001087, + "seconds": 1.001086950302124, + "bytes": 39452672, + "bits_per_second": 315278683.73945612, + "retransmits": 0, + "omitted": false, + "sender": true + } + }, { + "streams": [{ + "socket": 5, + "start": 1.001087, + "end": 2.000814, + "seconds": 0.99972701072692871, + "bytes": 30015488, + "bits_per_second": 240189473.1496745, + "retransmits": 102, + "snd_cwnd": 2112632, + "snd_wnd": 6225920, + "rtt": 54365, + "rttvar": 4457, + "pmtu": 1500, + "omitted": false, + "sender": true + }], + "sum": { + "start": 1.001087, + "end": 2.000814, + "seconds": 0.99972701072692871, + "bytes": 30015488, + "bits_per_second": 240189473.1496745, + "retransmits": 102, + "omitted": false, + "sender": true + } + }, { + "streams": [{ + "socket": 5, + "start": 2.000814, + "end": 3.000798, + "seconds": 0.9999840259552002, + "bytes": 32899072, + "bits_per_second": 263196780.31715995, + "retransmits": 0, + "snd_cwnd": 2321144, + "snd_wnd": 6225920, + "rtt": 63651, + "rttvar": 965, + "pmtu": 1500, + "omitted": false, + "sender": true + }], + "sum": { + "start": 2.000814, + "end": 3.000798, + "seconds": 0.9999840259552002, + "bytes": 32899072, + "bits_per_second": 263196780.31715995, + "retransmits": 0, + "omitted": false, + "sender": true + } + }, { + "streams": [{ + "socket": 5, + "start": 3.000798, + "end": 4.000786, + "seconds": 0.9999880194664, + "bytes": 45350912, + "bits_per_second": 362811642.67707551, + "retransmits": 0, + "snd_cwnd": 2503592, + "snd_wnd": 6225920, + "rtt": 42059, + "rttvar": 1399, + "pmtu": 1500, + "omitted": false, + "sender": true + }], + "sum": { + "start": 3.000798, + "end": 4.000786, + "seconds": 0.9999880194664, + "bytes": 45350912, + "bits_per_second": 362811642.67707551, + "retransmits": 0, + "omitted": false, + "sender": true + } + }, { + "streams": [{ + "socket": 5, + "start": 4.000786, + "end": 5.001494, + "seconds": 1.0007079839706421, + "bytes": 56754176, + "bits_per_second": 453712187.04428768, + "retransmits": 0, + "snd_cwnd": 2645496, + "snd_wnd": 6225920, + "rtt": 47457, + "rttvar": 586, + "pmtu": 1500, + "omitted": false, + "sender": true + }], + "sum": { + "start": 4.000786, + "end": 5.001494, + "seconds": 1.0007079839706421, + "bytes": 56754176, + "bits_per_second": 453712187.04428768, + "retransmits": 0, + "omitted": false, + "sender": true + } + }], + "end": { + "streams": [{ + "sender": { + "socket": 5, + "start": 0, + "end": 5.001494, + "seconds": 5.001494, + "bytes": 204472320, + "bits_per_second": 327057987.07346243, + "retransmits": 102, + "max_snd_cwnd": 2645496, + "max_snd_wnd": 6225920, + "max_rtt": 63651, + "min_rtt": 42059, + "mean_rtt": 53143, + "sender": true + }, + "receiver": { + "socket": 5, + "start": 0, + "end": 5.04966, + "seconds": 5.001494, + "bytes": 204210176, + "bits_per_second": 323523050.66083658, + "sender": true + } + }], + "sum_sent": { + "start": 0, + "end": 5.001494, + "seconds": 5.001494, + "bytes": 204472320, + "bits_per_second": 327057987.07346243, + "retransmits": 102, + "sender": true + }, + "sum_received": { + "start": 0, + "end": 5.04966, + "seconds": 5.04966, + "bytes": 204210176, + "bits_per_second": 323523050.66083658, + "sender": true + }, + "cpu_utilization_percent": { + "host_total": 1.39268421448378, + "host_user": 0, + "host_system": 1.3926443249220208, + "remote_total": 3.0024393557312581, + "remote_user": 0.37536927980190482, + "remote_system": 2.6270502727492 + }, + "sender_tcp_congestion": "cubic", + "receiver_tcp_congestion": "bbr" + } +} +gilles@lenovo-bureau:~/Documents/vscode$ lscpu +Architecture : x86_64 + Mode(s) opératoire(s) des processeurs : 32-bit, 64-bit + Tailles des adresses: 36 bits physical, 48 bits virtual + Boutisme : Little Endian +Processeur(s) : 4 + Liste de processeur(s) en ligne : 0-3 +Identifiant constructeur : GenuineIntel + Nom de modèle : Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz + Famille de processeur : 6 + Modèle : 42 + Thread(s) par cœur : 1 + Cœur(s) par socket : 4 + Socket(s) : 1 + Révision : 7 + multiplication des MHz du/des CPU(s) : 96% + Vitesse maximale du processeur en MHz : 3400,0000 + Vitesse minimale du processeur en MHz : 1600,0000 + BogoMIPS : 6185,77 + Drapeaux : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ht tm pbe syscall nx rdtscp lm constant_ts + c arch_perfmon pebs bts nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4 + _2 x2apic popcnt tsc_deadline_timer aes xsave avx lahf_lm epb pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid xsaveopt dtherm ida arat pln + pts vnmi md_clear flush_l1d +Fonctionnalités de virtualisation : + Virtualisation : VT-x +Caches (somme de toutes) : + L1d : 128 KiB (4 instances) + L1i : 128 KiB (4 instances) + L2 : 1 MiB (4 instances) + L3 : 6 MiB (1 instance) +NUMA : + Nœud(s) NUMA : 1 + Nœud NUMA 0 de processeur(s) : 0-3 +Vulnérabilités : + Gather data sampling : Not affected + Indirect target selection : Not affected + Itlb multihit : KVM: Mitigation: Split huge pages + L1tf : Mitigation; PTE Inversion; VMX conditional cache flushes, SMT disabled + Mds : Mitigation; Clear CPU buffers; SMT disabled + Meltdown : Mitigation; PTI + Mmio stale data : Unknown: No mitigations + Reg file data sampling : Not affected + Retbleed : Not affected + Spec rstack overflow : Not affected + Spec store bypass : Mitigation; Speculative Store Bypass disabled via prctl + Spectre v1 : Mitigation; usercopy/swapgs barriers and __user pointer sanitization + Spectre v2 : Mitigation; Retpolines; IBPB conditional; IBRS_FW; STIBP disabled; RSB filling; PBRSB-eIBRS Not affected; BHI Not affected + Srbds : Not affected + Tsa : Not affected + Tsx async abort : Not affected + Vmscape : Mitigation; IBPB before exit to userspace +gilles@lenovo-bureau:~/Documents/vscode$ echo "Vendor: $(lscpu | grep 'Vendor ID' | awk '{print $3}')" +echo "Model: $(lscpu | grep 'Model name' | sed 's/Model name: *//')" +echo "Cores: $(lscpu | grep '^CPU(s):' | awk '{print $2}')" +echo "Threads: $(nproc)" +echo "Base Freq: $(lscpu | grep 'CPU MHz' | awk '{print $3}')" +echo "Max Freq: $(lscpu | grep 'CPU max MHz' | awk '{print $4}')" +Vendor: +Model: +Cores: +Threads: 4 +Base Freq: +Max Freq: +gilles@lenovo-bureau:~/Documents/vscode$ free -m + total utilisé libre partagé tamp/cache disponible +Mem: 7771 5847 1479 678 1410 1923 +Échange: 7627 4569 3058 +gilles@lenovo-bureau:~/Documents/vscode$ free -m | grep '^Mem:' | awk '{print $2}' +7771 +gilles@lenovo-bureau:~/Documents/vscode$ sudo dmidecode -t memory +[sudo] Mot de passe de gilles : +# dmidecode 3.6 +Getting SMBIOS data from sysfs. +SMBIOS 2.6 present. + +Handle 0x001B, DMI type 16, 15 bytes +Physical Memory Array + Location: System Board Or Motherboard + Use: System Memory + Error Correction Type: None + Maximum Capacity: 32 GB + Error Information Handle: No Error + Number Of Devices: 4 + +Handle 0x001E, DMI type 17, 28 bytes +Memory Device + Array Handle: 0x001B + Error Information Handle: No Error + Total Width: 64 bits + Data Width: 64 bits + Size: 2 GB + Form Factor: DIMM + Set: None + Locator: A1_DIMM0 + Bank Locator: A1_BANK0 + Type: DDR3 + Type Detail: Synchronous + Speed: 1333 MT/s + Manufacturer: Samsung + Serial Number: 93771681 + Asset Tag: A1_AssetTagNum0 + Part Number: M378B5773DH0-CH9 + Rank: 1 + +Handle 0x0021, DMI type 17, 28 bytes +Memory Device + Array Handle: 0x001B + Error Information Handle: No Error + Total Width: 64 bits + Data Width: 64 bits + Size: 2 GB + Form Factor: DIMM + Set: None + Locator: A1_DIMM1 + Bank Locator: A1_BANK1 + Type: DDR3 + Type Detail: Synchronous + Speed: 1333 MT/s + Manufacturer: Samsung + Serial Number: 984CD5E6 + Asset Tag: A1_AssetTagNum1 + Part Number: M378B5773DH0-CH9 + Rank: 1 + +Handle 0x0024, DMI type 17, 28 bytes +Memory Device + Array Handle: 0x001B + Error Information Handle: No Error + Total Width: 64 bits + Data Width: 64 bits + Size: 2 GB + Form Factor: DIMM + Set: None + Locator: A1_DIMM2 + Bank Locator: A1_BANK2 + Type: DDR3 + Type Detail: Synchronous + Speed: 1333 MT/s + Manufacturer: Kingston + Serial Number: 0F0AF7F2 + Asset Tag: A1_AssetTagNum2 + Part Number: ACR256X64D3U1333C9 + Rank: 2 + +Handle 0x0027, DMI type 17, 28 bytes +Memory Device + Array Handle: 0x001B + Error Information Handle: No Error + Total Width: 64 bits + Data Width: 64 bits + Size: 2 GB + Form Factor: DIMM + Set: None + Locator: A1_DIMM3 + Bank Locator: A1_BANK3 + Type: DDR3 + Type Detail: Synchronous + Speed: 1333 MT/s + Manufacturer: Kingston + Serial Number: 0B0A90F3 + Asset Tag: A1_AssetTagNum3 + Part Number: ACR256X64D3U1333C9 + Rank: 2 + +gilles@lenovo-bureau:~/Documents/vscode$ sudo dmidecode -t memory | grep -E 'Size|Type:|Speed:|Manufacturer' + Error Correction Type: None + Size: 2 GB + Type: DDR3 + Speed: 1333 MT/s + Manufacturer: Samsung + Size: 2 GB + Type: DDR3 + Speed: 1333 MT/s + Manufacturer: Samsung + Size: 2 GB + Type: DDR3 + Speed: 1333 MT/s + Manufacturer: Kingston + Size: 2 GB + Type: DDR3 + Speed: 1333 MT/s + Manufacturer: Kingston +gilles@lenovo-bureau:~/Documents/vscode$ lspci | grep -i vga +lspci | grep -i 3d +00:02.0 VGA compatible controller: Intel Corporation 2nd Generation Core Processor Family Integrated Graphics Controller (rev 09) +gilles@lenovo-bureau:~/Documents/vscode$ sudo dmidecode -t baseboard | grep -E 'Manufacturer|Product Name' + Manufacturer: LENOVO + Product Name: +gilles@lenovo-bureau:~/Documents/vscode$ sudo dmidecode -t baseboard +# dmidecode 3.6 +Getting SMBIOS data from sysfs. +SMBIOS 2.6 present. + +Handle 0x0002, DMI type 2, 15 bytes +Base Board Information + Manufacturer: LENOVO + Product Name: + Version: + Serial Number: INVALID + Asset Tag: + Features: + Board is a hosting board + Board is replaceable + Location In Chassis: To be filled by O.E.M. + Chassis Handle: 0x0003 + Type: Motherboard + Contained Object Handles: 0 + +Handle 0x0014, DMI type 10, 6 bytes +On Board Device Information + Type: Video + Status: Enabled + Description: Onboard Video + +Handle 0x0015, DMI type 10, 6 bytes +On Board Device Information + Type: Ethernet + Status: Enabled + Description: Onboard LAN + +Handle 0x0016, DMI type 10, 6 bytes +On Board Device Information + Type: Sound + Status: Enabled + Description: Onboard Audio + +Handle 0x0017, DMI type 10, 6 bytes +On Board Device Information + Type: SATA Controller + Status: Enabled + Description: Sata Controller + +Handle 0x0057, DMI type 41, 11 bytes +Onboard Device + Reference Designation: Onboard IGD + Type: Video + Status: Enabled + Type Instance: 1 + Bus Address: 0000:00:02.0 + +Handle 0x0058, DMI type 41, 11 bytes +Onboard Device + Reference Designation: Onboard LAN + Type: Ethernet + Status: Enabled + Type Instance: 1 + Bus Address: 0000:00:19.0 + +Handle 0x0059, DMI type 41, 11 bytes +Onboard Device + Reference Designation: Onboard 1394 + Type: Other + Status: Enabled + Type Instance: 1 + Bus Address: 0000:03:1c.2 + +Handle 0x0062, DMI type 10, 6 bytes +On Board Device Information + Type: Other + Status: Disabled + Description: IBM Embedded Security Hardware Type 3 + +gilles@lenovo-bureau:~/Documents/vscode$ cat /etc/os-release +uname -r # kernel version +uname -m # architecture +PRETTY_NAME="Debian GNU/Linux 13 (trixie)" +NAME="Debian GNU/Linux" +VERSION_ID="13" +VERSION="13 (trixie)" +VERSION_CODENAME=trixie +DEBIAN_VERSION_FULL=13.2 +ID=debian +HOME_URL="https://www.debian.org/" +SUPPORT_URL="https://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" +6.12.57+deb13-amd64 +x86_64 +gilles@lenovo-bureau:~/Documents/vscode$ lsblk -o NAME,SIZE,TYPE,MOUNTPOINT +NAME SIZE TYPE MOUNTPOINT +loop1 55,5M loop /snap/core18/2976 +loop2 55,5M loop /snap/core18/2952 +loop3 63,8M loop /snap/core20/2669 +loop4 164,8M loop /snap/gnome-3-28-1804/198 +loop5 4K loop /snap/bare/5 +loop6 50,8M loop /snap/snapd/25202 +loop7 91,7M loop /snap/gtk-common-themes/1535 +loop8 56,5M loop /snap/mqtt-explorer/29 +loop9 92,5M loop /snap/mqtt-explorer/31 +loop10 50,9M loop /snap/snapd/25577 +loop11 63,8M loop /snap/core20/2682 +sda 447,1G disk +├─sda1 7,4G part [SWAP] +├─sda2 1K part +└─sda5 439,7G part / +sdb 0B disk +gilles@lenovo-bureau:~/Documents/vscode$ sudo smartctl -i /dev/sda +smartctl 7.4 2023-08-01 r5530 [x86_64-linux-6.12.57+deb13-amd64] (local build) +Copyright (C) 2002-23, Bruce Allen, Christian Franke, www.smartmontools.org + +=== START OF INFORMATION SECTION === +Model Family: Phison Driven SSDs +Device Model: KINGSTON SA400S37480G +Serial Number: 50026B77833E25E3 +LU WWN Device Id: 5 0026b7 7833e25e3 +Firmware Version: SBFKK1B3 +User Capacity: 480 103 981 056 bytes [480 GB] +Sector Size: 512 bytes logical/physical +Rotation Rate: Solid State Device +TRIM Command: Available +Device is: In smartctl database 7.3/5528 +ATA Version is: ACS-3 T13/2161-D revision 4 +SATA Version is: SATA 3.2, 6.0 Gb/s (current: 6.0 Gb/s) +Local Time is: Sun Dec 7 19:07:51 2025 CET +SMART support is: Available - device has SMART capability. +SMART support is: Enabled + +gilles@lenovo-bureau:~/Documents/vscode$ sudo dmesg | grep -i sdb +[ 3.311044] sd 6:0:0:0: [sdb] Media removed, stopped polling +[ 3.311324] sd 6:0:0:0: [sdb] Attached SCSI removable disk +gilles@lenovo-bureau:~/Documents/vscode$ sudo smartctl -i /dev/sdb +smartctl 7.4 2023-08-01 r5530 [x86_64-linux-6.12.57+deb13-amd64] (local build) +Copyright (C) 2002-23, Bruce Allen, Christian Franke, www.smartmontools.org + +/dev/sdb: Unknown USB bridge [0x05e3:0x0745 (0x904)] +Please specify device type with the -d option. + +Use smartctl -h to get a usage summary + +gilles@lenovo-bureau:~/Documents/vscode$ sudo fdisk -l /dev/sdb +fdisk: impossible d'ouvrir /dev/sdb: Aucun médium trouvé +gilles@lenovo-bureau:~/Documents/vscode$ lsblk -J # JSON format +{ + "blockdevices": [ + { + "name": "loop1", + "maj:min": "7:1", + "rm": false, + "size": "55,5M", + "ro": true, + "type": "loop", + "mountpoints": [ + "/snap/core18/2976" + ] + },{ + "name": "loop2", + "maj:min": "7:2", + "rm": false, + "size": "55,5M", + "ro": true, + "type": "loop", + "mountpoints": [ + "/snap/core18/2952" + ] + },{ + "name": "loop3", + "maj:min": "7:3", + "rm": false, + "size": "63,8M", + "ro": true, + "type": "loop", + "mountpoints": [ + "/snap/core20/2669" + ] + },{ + "name": "loop4", + "maj:min": "7:4", + "rm": false, + "size": "164,8M", + "ro": true, + "type": "loop", + "mountpoints": [ + "/snap/gnome-3-28-1804/198" + ] + },{ + "name": "loop5", + "maj:min": "7:5", + "rm": false, + "size": "4K", + "ro": true, + "type": "loop", + "mountpoints": [ + "/snap/bare/5" + ] + },{ + "name": "loop6", + "maj:min": "7:6", + "rm": false, + "size": "50,8M", + "ro": true, + "type": "loop", + "mountpoints": [ + "/snap/snapd/25202" + ] + },{ + "name": "loop7", + "maj:min": "7:7", + "rm": false, + "size": "91,7M", + "ro": true, + "type": "loop", + "mountpoints": [ + "/snap/gtk-common-themes/1535" + ] + },{ + "name": "loop8", + "maj:min": "7:8", + "rm": false, + "size": "56,5M", + "ro": true, + "type": "loop", + "mountpoints": [ + "/snap/mqtt-explorer/29" + ] + },{ + "name": "loop9", + "maj:min": "7:9", + "rm": false, + "size": "92,5M", + "ro": true, + "type": "loop", + "mountpoints": [ + "/snap/mqtt-explorer/31" + ] + },{ + "name": "loop10", + "maj:min": "7:10", + "rm": false, + "size": "50,9M", + "ro": true, + "type": "loop", + "mountpoints": [ + "/snap/snapd/25577" + ] + },{ + "name": "loop11", + "maj:min": "7:11", + "rm": false, + "size": "63,8M", + "ro": true, + "type": "loop", + "mountpoints": [ + "/snap/core20/2682" + ] + },{ + "name": "sda", + "maj:min": "8:0", + "rm": false, + "size": "447,1G", + "ro": false, + "type": "disk", + "mountpoints": [], + "children": [ + { + "name": "sda1", + "maj:min": "8:1", + "rm": false, + "size": "7,4G", + "ro": false, + "type": "part", + "mountpoints": [ + "[SWAP]" + ] + },{ + "name": "sda2", + "maj:min": "8:2", + "rm": false, + "size": "1K", + "ro": false, + "type": "part", + "mountpoints": [] + },{ + "name": "sda5", + "maj:min": "8:5", + "rm": false, + "size": "439,7G", + "ro": false, + "type": "part", + "mountpoints": [ + "/" + ] + } + ] + },{ + "name": "sdb", + "maj:min": "8:16", + "rm": true, + "size": "0B", + "ro": false, + "type": "disk", + "mountpoints": [] + } + ] +} +gilles@lenovo-bureau:~/Documents/vscode$ ip link show +1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 +2: eno1: mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 + link/ether 44:37:e6:6b:53:86 brd ff:ff:ff:ff:ff:ff + altname enp0s25 + altname enx4437e66b5386 +3: wlp2s0: mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 + link/ether 62:a5:f7:52:3e:06 brd ff:ff:ff:ff:ff:ff permaddr 14:f6:d8:67:27:b7 + altname wlx14f6d86727b7 +4: br-7dbed573109c: mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default + link/ether 96:dd:6f:58:69:7a brd ff:ff:ff:ff:ff:ff +5: docker0: mtu 1500 qdisc noqueue state UP mode DEFAULT group default + link/ether fa:4b:12:7b:47:24 brd ff:ff:ff:ff:ff:ff +6: veth146accf@if2: mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default + link/ether e2:6e:84:f2:1f:f9 brd ff:ff:ff:ff:ff:ff link-netnsid 0 +gilles@lenovo-bureau:~/Documents/vscode$ ip -br link show | grep UP +lo UNKNOWN 00:00:00:00:00:00 +eno1 UP 44:37:e6:6b:53:86 +br-7dbed573109c DOWN 96:dd:6f:58:69:7a +docker0 UP fa:4b:12:7b:47:24 +veth146accf@if2 UP e2:6e:84:f2:1f:f9 +gilles@lenovo-bureau:~/Documents/vscode$ +ChatGPT a dit : + +Je vais te faire une lecture “profil complet” de cette machine + les chiffres importants à retenir, puis corriger tes commandes de collecte (lscpu, etc.). +1. Profil matériel détecté + +CPU + + Modèle : Intel Core i5-2400 + + Architecture : Sandy Bridge, 4 cœurs / 4 threads, pas d’Hyper-Threading. + + Fréquences : + + Min : 1,6 GHz + + Max/Turbo : 3,4 GHz + + VT-x présent, AVX présent, CPU assez ancien mais correct pour serveur light / petites VM / Docker. + +RAM + + Total : ~8 Go (free -m → 7771 Mo utilisables). + + Slots : 4 × 2 Go DDR3-1333. + + DMI : + + 2 × Samsung 2 Go DDR3-1333 + + 2 × Kingston 2 Go DDR3-1333 + + Capacité max carte mère : 32 Go. + +Stockage + + SSD : KINGSTON SA400S37480G 480 Go, SATA 6 Gb/s. + + Partitions : + + sda1 : 7,4 Go swap + + sda5 : 439,7 Go / + + sdb : + + périphérique USB / lecteur de carte : + sd 6:0:0:0: [sdb] Media removed, stopped polling + fdisk: Aucun médium trouvé + → c’est un lecteur sans support inséré, à ignorer comme “disque” réel. + +OS / Réseau + + OS : Debian 13 (trixie), kernel 6.12.57+deb13-amd64. + + Réseau : + + eno1 : Ethernet 1 Gbit/s (Onboard Intel), UP. + + Wi-Fi présent mais DOWN. + + Interfaces Docker (docker0, bridge br-…). + +2. Bench CPU (sysbench) +Multithread (4 threads) + + cpu-max-prime=10000 → ~3300–3400 events/s + + cpu-max-prime=20000 → ~1200–1275 events/s + +Donc : + + Quand tu doubles la charge (10000 → 20000), tu divises grosso modo les events/s par ~2,7 – ce qui est cohérent vu la complexité des tests de primalité. + + Latence moyenne à 20000 : ~3,1–3,3 ms par event, 95ᵉ percentile 4–7 ms → CPU stable, peu de “spikes”. + +Monothread (1 thread, 20000) + + events per second: 357 events/s + + Latence : ~2,8 ms, 95ᵉ percentile ~2,9 ms → encore plus stable que le multi-thread. + +Ce que ça donne comme image + + Perf monothread : ~357 events/s → correct pour un i5 Sandy Bridge. + + Scaling 1 → 4 threads : 357 → ~1200 events/s → ≈ 3,3× au lieu de 4× parfait. + + Normal : contention caches, scheduler, fréquence qui baisse un peu à pleine charge. + +👉 Pour ton futur outil, les valeurs à garder : + + cpu_max_prime + + threads + + events_per_second + + latency_avg_ms + + latency_95th_ms + + éventuellement events_per_second_1thread pour comparer mono vs multi. + +3. Bench mémoire (sysbench memory) + + 512 MiB : 13 806 MiB/s + + 1 GiB : 15 213 MiB/s + + Autre run : 13 200 MiB/s + +Donc on tourne autour de 13–15 GiB/s en écriture. + +Pour de la DDR3-1333 dual-channel (théorique ≈ 21,3 Go/s), c’est normal : +on est à ~60–70 % du théorique, ce qui est typique. + +👉 À retenir : “RAM write ~14 GiB/s”. +4. Bench disque (fio) + +Attention : tu utilises /tmp/fio-test-file. Sur Debian 13, /tmp est généralement un tmpfs en RAM → tu benches la RAM, pas le SSD, même avec --direct=1 tu restes sur un FS spécial. + +Résultats : + + Seq read 1 MiB, 1 GiB : + + ~2590 MiB/s (~2,6 Go/s). + + Seq write 1 MiB, 1 GiB : + + ~1430 MiB/s (~1,5 Go/s). + + RandRW 4k, 100 MiB : + + ~685 MiB/s lecture + écriture, ~175k IOPS. + +Ces chiffres sont impossibles pour un SATA A400 (limite physique ~500–550 Mo/s, 60–80k IOPS max). +Donc c’est bien le tmpfs RAM qui est benché. + +👉 Pour tester le SSD réel, il faudra utiliser un fichier sur / (ext4 sur sda5), par ex : + +mkdir -p ~/fio-ssd-test +fio --name=seqread \ + --ioengine=libaio \ + --rw=read \ + --bs=1M \ + --size=4G \ + --numjobs=1 \ + --direct=1 \ + --filename=~/fio-ssd-test/fio-test-file + +Même chose pour write / randrw. +5. Bench réseau (iperf3) + + Normal (client→serveur) : + ~0,5 Gbit/s (496 Mbit/s, retrans très faibles). + + Reverse (serveur→client) : + ~0,4 Gbit/s (413–417 Mbit/s). + + Run JSON : ~0,32 Gbit/s avec un peu de retrans (102 sur un intervalle). + +Donc : + + Tu es entre 0,3 et 0,5 Gbit/s sur un lien Gigabit. + + Ce n’est pas saturé (1 Gbit/s), mais correct : + + peut être limité par l’autre machine (10.0.1.97), + + câble, switch, réglage TCP (Cubic vs BBR), charge CPU côté serveur. + +👉 Pour ton app, les champs utiles : + + bandwidth_tx_mbps + + bandwidth_rx_mbps + + retransmits + + tcp_congestion_algo (ici cubic/bbr). + +6. Pourquoi tes echo "Vendor: $(lscpu | grep 'Vendor ID' ...) sont vides + +Ton lscpu est localisé en français : + + Pas de "Vendor ID" mais "Identifiant constructeur". + + Pas de "Model name" mais "Nom de modèle". + + Pas de "CPU(s):" mais "Processeur(s)". + + Pas de "CPU max MHz" mais "Vitesse maximale du processeur en MHz". + +Donc tes grep 'Vendor ID' et autres ne trouvent rien → champs vides. +Solution 1 : forcer l’anglais pour parser proprement + +LC_ALL=C lscpu | grep '^Vendor ID:' | awk -F: '{gsub(/^ +/,"",$2); print $2}' +LC_ALL=C lscpu | grep '^Model name:' | sed 's/^Model name:[ \t]*//' +LC_ALL=C lscpu | grep '^CPU(s):' | awk -F: '{gsub(/^ +/,"",$2); print $2}' +LC_ALL=C lscpu | grep '^CPU max MHz:' | awk -F: '{gsub(/^ +/,"",$2); print $2}' +LC_ALL=C lscpu | grep '^CPU min MHz:' | awk -F: '{gsub(/^ +/,"",$2); print $2}' + +Exemple compact : + +echo "Vendor: $(LC_ALL=C lscpu | awk -F: '/^Vendor ID:/ {gsub(/^ +/,"",$2); print $2}')" +echo "Model: $(LC_ALL=C lscpu | sed -n 's/^Model name:[ \t]*//p')" +echo "Cores: $(LC_ALL=C lscpu | awk -F: '/^CPU(s):/ {gsub(/^ +/,"",$2); print $2}')" + +Solution 2 (si tu veux du JSON) : lscpu -J + jq + +Comme tu as jq : + +LC_ALL=C lscpu -J | jq -r '.lscpu[] | select(.field=="Model name:") | .data' +LC_ALL=C lscpu -J | jq -r '.lscpu[] | select(.field=="Vendor ID:") | .data' + +Parfait pour ton futur collecteur hardware. +7. Résumé “fiche machine” exploitable + +Si je condense tout ce que tu viens de mesurer : + + CPU : Intel i5-2400 (4C/4T, 3,1/3,4 GHz) + + sysbench 1T (prime 20000) : ~357 events/s + + sysbench 4T (prime 20000) : ~1200–1270 events/s + + sysbench 4T (prime 10000) : ~3300–3400 events/s + + RAM : 8 Go DDR3-1333 (4×2 Go), bande passante écriture ~14 GiB/s. + + Disque : Kingston A400 480 Go SATA (à bench à nouveau hors /tmp). + + Réseau : 0,3–0,5 Gbit/s iperf3 sur lien 1 Gbit/s. + + Divers : lecteur USB/SD vide exposé comme /dev/sdb (removable, 0B). + +Si tu veux, je peux te proposer un petit JSON “standardisé” qui regroupe tous ces champs comme gabarit pour ton futur script de collecte. +Vous avez dit : +mkdir -p ~/fio-ssd-test +fio --name=seqread \ + --ioengine=libaio \ + --rw=read \ + --bs=1M \ + --size=4G \ + --numjobs=1 \ + --direct=1 \ + --filename=~/fio-ssd-test/fio-test-file +seqread: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=1 +fio-3.39 +Starting 1 process +seqread: Laying out IO file (1 file / 4096MiB) +Jobs: 1 (f=1): [R(1)][100.0%][r=339MiB/s][r=339 IOPS][eta 00m:00s] +seqread: (groupid=0, jobs=1): err= 0: pid=200179: Sun Dec 7 19:14:47 2025 + read: IOPS=340, BW=341MiB/s (357MB/s)(4096MiB/12014msec) + slat (usec): min=43, max=556, avg=158.69, stdev=37.00 + clat (msec): min=2, max=115, avg= 2.77, stdev= 2.88 + lat (msec): min=2, max=115, avg= 2.93, stdev= 2.89 + clat percentiles (msec): + | 1.00th=[ 3], 5.00th=[ 3], 10.00th=[ 3], 20.00th=[ 3], + | 30.00th=[ 3], 40.00th=[ 3], 50.00th=[ 3], 60.00th=[ 3], + | 70.00th=[ 3], 80.00th=[ 3], 90.00th=[ 3], 95.00th=[ 3], + | 99.00th=[ 4], 99.50th=[ 5], 99.90th=[ 53], 99.95th=[ 54], + | 99.99th=[ 116] + bw ( KiB/s): min=260096, max=403456, per=100.00%, avg=349354.67, stdev=31978.90, samples=24 + iops : min= 254, max= 394, avg=341.17, stdev=31.23, samples=24 + lat (msec) : 4=99.29%, 10=0.42%, 20=0.07%, 50=0.10%, 100=0.07% + lat (msec) : 250=0.05% + cpu : usr=0.56%, sys=5.98%, ctx=4103, majf=0, minf=265 + IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% + submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% + issued rwts: total=4096,0,0,0 short=0,0,0,0 dropped=0,0,0,0 + latency : target=0, window=0, percentile=100.00%, depth=1 + +Run status group 0 (all jobs): + READ: bw=341MiB/s (357MB/s), 341MiB/s-341MiB/s (357MB/s-357MB/s), io=4096MiB (4295MB), run=12014-12014msec + +Disk stats (read/write): + sda: ios=8129/107, sectors=8276848/2272, merge=31/128, ticks=20107/1243, in_queue=21716, util=96.46% +gilles@lenovo-bureau:~/Documents/vscode$ echo "Vendor: $(LC_ALL=C lscpu | awk -F: '/^Vendor ID:/ {gsub(/^ +/,"",$2); print $2}')" +echo "Model: $(LC_ALL=C lscpu | sed -n 's/^Model name:[ \t]*//p')" +echo "Cores: $(LC_ALL=C lscpu | awk -F: '/^CPU(s):/ {gsub(/^ +/,"",$2); print $2}')" +Vendor: GenuineIntel +Model: Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz +Cores: +gilles@lenovo-bureau:~/Documents/vscode$ +ChatGPT a dit : + + Lecture du test FIO sur le SSD + +Là tu testes enfin bien le SSD réel (fichier sur ~/fio-ssd-test → donc sur /dev/sda). + +Résultat important : + +READ: bw=341MiB/s (357MB/s), io=4096MiB, run=12014msec +... +Disk stats: + sda: ... util=96.46% + +À retenir : + + Débit séquentiel lecture ≈ 340 MiB/s (~357 Mo/s). + + Disque utilisé à ~96 % pendant le test → tu es limité par le SSD, pas par le CPU. + + Pour un Kingston A400 SATA, c’est cohérent si : + + le disque est un peu usé, + + il y a de la fragmentation, + + tu passes par ext4 + overhead du FS, + + tu lis 4 Gio d’un coup avec iodepth=1 (peu de requêtes en vol). + +En pratique, on voit souvent : + + 350–450 Mo/s en réel sur ce genre de SSD SATA, + + 500–550 Mo/s au mieux dans des conditions idéales. + +Donc ton 341 MiB/s est tout à fait crédible pour cette machine. + + Pourquoi Cores: est vide dans ton echo + +Commande : + +echo "Vendor: $(LC_ALL=C lscpu | awk -F: '/^Vendor ID:/ {gsub(/^ +/,"",$2); print $2}')" +echo "Model: $(LC_ALL=C lscpu | sed -n 's/^Model name:[ \t]*//p')" +echo "Cores: $(LC_ALL=C lscpu | awk -F: '/^CPU(s):/ {gsub(/^ +/,"",$2); print $2}')" + +Résultat : + +Vendor: GenuineIntel +Model: Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz +Cores: + +Donc : + + Vendor OK → Vendor ID: est bien trouvé. + + Model OK → Model name: aussi. + + CPU(s): n’est pas trouvé par awk → résultat vide. + +Les causes possibles : + + Sous LC_ALL=C, il est possible que le champ s’appelle autrement (ex. CPU(s): vs CPU(s) avec espace ou autre). + + Il peut y avoir des caractères bizarres (tabulation, non-breaking space) qui font que le ^CPU(s): ne matche pas exactement. + +Tu peux le vérifier en faisant : + +LC_ALL=C lscpu | grep 'CPU' + +Tu verras exactement la forme du champ. + + Méthode plus robuste avec lscpu -J + jq + +Comme tu as déjà jq, le plus propre pour ton futur script de collecte, c’est d’utiliser le JSON de lscpu : + +LC_ALL=C lscpu -J | jq -r '.lscpu[] | select(.field=="CPU(s):") | .data' + +Tu peux donc écrire : + +echo "Vendor: $(LC_ALL=C lscpu -J | jq -r '.lscpu[] | select(.field=="Vendor ID:") | .data')" +echo "Model: $(LC_ALL=C lscpu -J | jq -r '.lscpu[] | select(.field=="Model name:") | .data')" +echo "Cores: $(LC_ALL=C lscpu -J | jq -r '.lscpu[] | select(.field=="CPU(s):") | .data')" + +Là : + + tu ne dépends plus des espaces, + + tu ne dépends plus du nombre de colonnes, + + tu gardes juste le champ .field tel que lscpu le donne en anglais. + +Si tu veux, je peux te proposer un petit script bash qui sort directement un JSON complet du type : + +{ + "cpu": { ... }, + "memory": { ... }, + "disk": { ... }, + "network": { ... }, + "benchmarks": { ... } +} + +en réutilisant tous les tests que tu viens de lancer. + + +ChatGPT peut commettre des erreurs. Il est recommandé de vérifier les informations importantes. Voir les préférences en matière de cookies. diff --git a/backend/app/api/benchmark.py b/backend/app/api/benchmark.py index 4df341e..4985166 100644 --- a/backend/app/api/benchmark.py +++ b/backend/app/api/benchmark.py @@ -71,6 +71,9 @@ async def submit_benchmark( # RAM ram_total_mb=hw.ram.total_mb if hw.ram else None, + ram_used_mb=hw.ram.used_mb if hw.ram else None, # NEW + ram_free_mb=hw.ram.free_mb if hw.ram else None, # NEW + ram_shared_mb=hw.ram.shared_mb if hw.ram else None, # NEW ram_slots_total=hw.ram.slots_total if hw.ram else None, ram_slots_used=hw.ram.slots_used if hw.ram else None, ram_ecc=hw.ram.ecc if hw.ram else None, @@ -128,6 +131,16 @@ async def submit_benchmark( if results.global_score is not None: global_score = results.global_score + # Extract network results for easier frontend access + network_results = None + if results.network: + network_results = { + "upload_mbps": results.network.upload_mbps if hasattr(results.network, 'upload_mbps') else None, + "download_mbps": results.network.download_mbps if hasattr(results.network, 'download_mbps') else None, + "ping_ms": results.network.ping_ms if hasattr(results.network, 'ping_ms') else None, + "score": results.network.score + } + benchmark = Benchmark( device_id=device.id, hardware_snapshot_id=snapshot.id, @@ -141,7 +154,8 @@ async def submit_benchmark( network_score=results.network.score if results.network else None, gpu_score=results.gpu.score if results.gpu else None, - details_json=json.dumps(results.dict()) + details_json=json.dumps(results.dict()), + network_results_json=json.dumps(network_results) if network_results else None ) db.add(benchmark) diff --git a/backend/app/db/base.py b/backend/app/db/base.py index a5fb1a8..d9e8b97 100644 --- a/backend/app/db/base.py +++ b/backend/app/db/base.py @@ -10,5 +10,6 @@ Base = declarative_base() from app.models.device import Device # noqa from app.models.hardware_snapshot import HardwareSnapshot # noqa from app.models.benchmark import Benchmark # noqa +from app.models.disk_smart import DiskSMART # noqa from app.models.manufacturer_link import ManufacturerLink # noqa from app.models.document import Document # noqa diff --git a/backend/app/models/benchmark.py b/backend/app/models/benchmark.py index e1c949c..664fc67 100644 --- a/backend/app/models/benchmark.py +++ b/backend/app/models/benchmark.py @@ -30,6 +30,7 @@ class Benchmark(Base): # Details details_json = Column(Text, nullable=False) # JSON object with all raw results + network_results_json = Column(Text, nullable=True) # Network benchmark details (iperf3) notes = Column(Text, nullable=True) # Relationships diff --git a/backend/app/models/disk_smart.py b/backend/app/models/disk_smart.py new file mode 100644 index 0000000..626697d --- /dev/null +++ b/backend/app/models/disk_smart.py @@ -0,0 +1,48 @@ +""" +Linux BenchTools - Disk SMART Data Model +""" + +from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey +from sqlalchemy.orm import relationship +from datetime import datetime +from app.db.base import Base + + +class DiskSMART(Base): + """ + SMART health and aging data for storage devices + """ + __tablename__ = "disk_smart_data" + + id = Column(Integer, primary_key=True, index=True, autoincrement=True) + hardware_snapshot_id = Column(Integer, ForeignKey("hardware_snapshots.id"), nullable=False, index=True) + captured_at = Column(DateTime, nullable=False, default=datetime.utcnow) + + # Disk identification + device_name = Column(String(50), nullable=False) # e.g., "sda", "nvme0n1" + model = Column(String(255), nullable=True) + serial_number = Column(String(100), nullable=True) + size_gb = Column(Float, nullable=True) + disk_type = Column(String(20), nullable=True) # "ssd" or "hdd" + interface = Column(String(50), nullable=True) # "sata", "nvme", "usb" + + # SMART Health Status + health_status = Column(String(20), nullable=True) # "PASSED", "FAILED", or null + temperature_celsius = Column(Integer, nullable=True) + + # Aging indicators + power_on_hours = Column(Integer, nullable=True) + power_cycle_count = Column(Integer, nullable=True) + reallocated_sectors = Column(Integer, nullable=True) # Critical: bad sectors + pending_sectors = Column(Integer, nullable=True) # Very critical: imminent failure + udma_crc_errors = Column(Integer, nullable=True) # Cable/interface issues + + # SSD-specific + wear_leveling_count = Column(Integer, nullable=True) # 0-100 (higher is better) + total_lbas_written = Column(Float, nullable=True) # Total data written + + # Relationship + hardware_snapshot = relationship("HardwareSnapshot", back_populates="disk_smart_data") + + def __repr__(self): + return f"" diff --git a/backend/app/models/hardware_snapshot.py b/backend/app/models/hardware_snapshot.py index 2a3ccee..7ae261e 100644 --- a/backend/app/models/hardware_snapshot.py +++ b/backend/app/models/hardware_snapshot.py @@ -34,6 +34,9 @@ class HardwareSnapshot(Base): # RAM ram_total_mb = Column(Integer, nullable=True) + ram_used_mb = Column(Integer, nullable=True) # NEW: RAM utilisée + ram_free_mb = Column(Integer, nullable=True) # NEW: RAM libre + ram_shared_mb = Column(Integer, nullable=True) # NEW: RAM partagée (tmpfs/vidéo) ram_slots_total = Column(Integer, nullable=True) ram_slots_used = Column(Integer, nullable=True) ram_ecc = Column(Boolean, nullable=True) @@ -74,6 +77,7 @@ class HardwareSnapshot(Base): # Relationships device = relationship("Device", back_populates="hardware_snapshots") benchmarks = relationship("Benchmark", back_populates="hardware_snapshot") + disk_smart_data = relationship("DiskSMART", back_populates="hardware_snapshot", cascade="all, delete-orphan") def __repr__(self): return f"" diff --git a/backend/app/schemas/hardware.py b/backend/app/schemas/hardware.py index 46e6962..0a07ecc 100644 --- a/backend/app/schemas/hardware.py +++ b/backend/app/schemas/hardware.py @@ -35,6 +35,9 @@ class RAMSlot(BaseModel): class RAMInfo(BaseModel): """RAM information schema""" total_mb: int + used_mb: Optional[int] = None # NEW + free_mb: Optional[int] = None # NEW + shared_mb: Optional[int] = None # NEW slots_total: Optional[int] = None slots_used: Optional[int] = None ecc: Optional[bool] = None @@ -56,7 +59,7 @@ class StorageDevice(BaseModel): name: str type: Optional[str] = None interface: Optional[str] = None - capacity_gb: Optional[int] = None + capacity_gb: Optional[float] = None # Changed from int to float vendor: Optional[str] = None model: Optional[str] = None smart_health: Optional[str] = None diff --git a/backend/apply_migration.py b/backend/apply_migration.py new file mode 100755 index 0000000..9942929 --- /dev/null +++ b/backend/apply_migration.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +""" +Apply SQL migration to existing database +Usage: python apply_migration.py +""" + +import sqlite3 +import os + +# Database path +DB_PATH = os.path.join(os.path.dirname(__file__), "data", "data.db") +MIGRATION_PATH = os.path.join(os.path.dirname(__file__), "migrations", "001_add_ram_stats_and_smart.sql") + +def apply_migration(): + """Apply the SQL migration""" + + if not os.path.exists(DB_PATH): + print(f"❌ Database not found at {DB_PATH}") + print(" The database will be created automatically on first run.") + return + + if not os.path.exists(MIGRATION_PATH): + print(f"❌ Migration file not found at {MIGRATION_PATH}") + return + + print(f"📂 Database: {DB_PATH}") + print(f"📄 Migration: {MIGRATION_PATH}") + print() + + # Read migration SQL + with open(MIGRATION_PATH, 'r') as f: + migration_sql = f.read() + + # Connect to database + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + try: + # Check if columns already exist + cursor.execute("PRAGMA table_info(hardware_snapshots)") + columns = [row[1] for row in cursor.fetchall()] + + if 'ram_used_mb' in columns: + print("⚠️ Migration already applied (ram_used_mb column exists)") + + # Check if disk_smart_data table exists + cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='disk_smart_data'") + if cursor.fetchone(): + print("⚠️ disk_smart_data table already exists") + print("✅ Database is up to date") + return + + # Apply migration + print("🔄 Applying migration...") + cursor.executescript(migration_sql) + conn.commit() + + print("✅ Migration applied successfully!") + print() + print("New columns added to hardware_snapshots:") + print(" - ram_used_mb") + print(" - ram_free_mb") + print(" - ram_shared_mb") + print() + print("New table created:") + print(" - disk_smart_data") + + except sqlite3.Error as e: + print(f"❌ Error applying migration: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + apply_migration() diff --git a/backend/apply_migration_002.py b/backend/apply_migration_002.py new file mode 100644 index 0000000..49458c2 --- /dev/null +++ b/backend/apply_migration_002.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +""" +Apply SQL migration 002 to existing database +Migration 002: Add network_results_json column to benchmarks table +Usage: python apply_migration_002.py +""" + +import sqlite3 +import os + +# Database path +DB_PATH = os.path.join(os.path.dirname(__file__), "data", "data.db") +MIGRATION_PATH = os.path.join(os.path.dirname(__file__), "migrations", "002_add_network_results.sql") + +def apply_migration(): + """Apply the SQL migration 002""" + + if not os.path.exists(DB_PATH): + print(f"❌ Database not found at {DB_PATH}") + print(" The database will be created automatically on first run.") + return + + if not os.path.exists(MIGRATION_PATH): + print(f"❌ Migration file not found at {MIGRATION_PATH}") + return + + print(f"📂 Database: {DB_PATH}") + print(f"📄 Migration: {MIGRATION_PATH}") + print() + + # Read migration SQL + with open(MIGRATION_PATH, 'r') as f: + migration_sql = f.read() + + # Connect to database + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + try: + # Check if column already exists + cursor.execute("PRAGMA table_info(benchmarks)") + columns = [row[1] for row in cursor.fetchall()] + + if 'network_results_json' in columns: + print("⚠️ Migration 002 already applied (network_results_json column exists)") + print("✅ Database is up to date") + return + + # Apply migration + print("🔄 Applying migration 002...") + cursor.executescript(migration_sql) + conn.commit() + + print("✅ Migration 002 applied successfully!") + print() + print("New column added to benchmarks:") + print(" - network_results_json") + + except sqlite3.Error as e: + print(f"❌ Error applying migration: {e}") + conn.rollback() + finally: + conn.close() + +if __name__ == "__main__": + apply_migration() diff --git a/backend/migrations/001_add_ram_stats_and_smart.sql b/backend/migrations/001_add_ram_stats_and_smart.sql new file mode 100644 index 0000000..442f28a --- /dev/null +++ b/backend/migrations/001_add_ram_stats_and_smart.sql @@ -0,0 +1,43 @@ +-- Migration 001: Add RAM statistics and SMART data table +-- Date: 2025-12-07 +-- Description: Adds used_mb, free_mb, shared_mb to hardware_snapshots and creates disk_smart_data table + +-- Add new RAM columns to hardware_snapshots +ALTER TABLE hardware_snapshots ADD COLUMN ram_used_mb INTEGER; +ALTER TABLE hardware_snapshots ADD COLUMN ram_free_mb INTEGER; +ALTER TABLE hardware_snapshots ADD COLUMN ram_shared_mb INTEGER; + +-- Create disk_smart_data table +CREATE TABLE IF NOT EXISTS disk_smart_data ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + hardware_snapshot_id INTEGER NOT NULL, + captured_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + + -- Disk identification + device_name VARCHAR(50) NOT NULL, + model VARCHAR(255), + serial_number VARCHAR(100), + size_gb REAL, + disk_type VARCHAR(20), -- 'ssd' or 'hdd' + interface VARCHAR(50), -- 'sata', 'nvme', 'usb' + + -- SMART Health Status + health_status VARCHAR(20), -- 'PASSED', 'FAILED', or NULL + temperature_celsius INTEGER, + + -- Aging indicators + power_on_hours INTEGER, + power_cycle_count INTEGER, + reallocated_sectors INTEGER, -- Critical: bad sectors + pending_sectors INTEGER, -- Very critical: imminent failure + udma_crc_errors INTEGER, -- Cable/interface issues + + -- SSD-specific + wear_leveling_count INTEGER, -- 0-100 (higher is better) + total_lbas_written REAL, -- Total data written + + FOREIGN KEY (hardware_snapshot_id) REFERENCES hardware_snapshots(id) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS idx_disk_smart_hardware_snapshot ON disk_smart_data(hardware_snapshot_id); +CREATE INDEX IF NOT EXISTS idx_disk_smart_device ON disk_smart_data(device_name); diff --git a/backend/migrations/002_add_network_results.sql b/backend/migrations/002_add_network_results.sql new file mode 100644 index 0000000..cdf17ab --- /dev/null +++ b/backend/migrations/002_add_network_results.sql @@ -0,0 +1,4 @@ +-- Migration 002: Add network_results_json column to benchmarks table +-- Date: 2025-12-07 + +ALTER TABLE benchmarks ADD COLUMN network_results_json TEXT; diff --git a/docker-compose.yml b/docker-compose.yml index c2c0437..5d03e6a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,6 +28,17 @@ services: networks: - benchtools + iperf3: + image: networkstatic/iperf3 + container_name: linux_benchtools_iperf3 + command: ["-s"] + ports: + - "5201:5201/tcp" + - "5201:5201/udp" + restart: unless-stopped + networks: + - benchtools + networks: benchtools: driver: bridge diff --git a/frontend/device_detail.html b/frontend/device_detail.html index 87572d3..7dc3bf8 100644 --- a/frontend/device_detail.html +++ b/frontend/device_detail.html @@ -64,6 +64,16 @@ + +
+
🌐 Informations Réseau Détaillées
+
+
+
Chargement...
+
+
+
+
diff --git a/frontend/devices.html b/frontend/devices.html index f4ee2a1..c0903c8 100644 --- a/frontend/devices.html +++ b/frontend/devices.html @@ -24,25 +24,29 @@ -
- - +
+ +
+ +
+
+ 📋 Devices +
+
+
Chargement...
+
+
- -
-
Chargement des devices
+ +
+
+
+
📊
+

Sélectionnez un device dans la liste pour afficher ses détails

+
+
+
- - -
diff --git a/frontend/js/device_detail.js b/frontend/js/device_detail.js index b25136e..3e6f6be 100644 --- a/frontend/js/device_detail.js +++ b/frontend/js/device_detail.js @@ -36,6 +36,7 @@ async function loadDeviceDetail() { renderDeviceHeader(); renderHardwareSummary(); renderLastBenchmark(); + renderNetworkDetails(); await loadBenchmarkHistory(); await loadDocuments(); await loadLinks(); @@ -86,9 +87,27 @@ function renderHardwareSummary() { return; } + // RAM usage info + const ramTotalGB = Math.round((snapshot.ram_total_mb || 0) / 1024); + const ramUsedMB = snapshot.ram_used_mb || 0; + const ramFreeMB = snapshot.ram_free_mb || 0; + const ramSharedMB = snapshot.ram_shared_mb || 0; + + let ramValue = `${ramTotalGB} GB`; + if (ramUsedMB > 0 || ramFreeMB > 0) { + const usagePercent = ramTotalGB > 0 ? Math.round((ramUsedMB / (snapshot.ram_total_mb || 1)) * 100) : 0; + ramValue = `${ramTotalGB} GB (${usagePercent}% utilisé)
Utilisée: ${Math.round(ramUsedMB / 1024)}GB • Libre: ${Math.round(ramFreeMB / 1024)}GB`; + if (ramSharedMB > 0) { + ramValue += ` • Partagée: ${Math.round(ramSharedMB / 1024)}GB`; + } + ramValue += `
${snapshot.ram_slots_used || '?'} / ${snapshot.ram_slots_total || '?'} slots
`; + } else { + ramValue += `
${snapshot.ram_slots_used || '?'} / ${snapshot.ram_slots_total || '?'} slots`; + } + const hardwareItems = [ { label: 'CPU', icon: '🔲', value: `${snapshot.cpu_model || 'N/A'}
${snapshot.cpu_cores || 0}C / ${snapshot.cpu_threads || 0}T @ ${snapshot.cpu_max_freq_ghz || snapshot.cpu_base_freq_ghz || '?'} GHz` }, - { label: 'RAM', icon: '💾', value: `${Math.round((snapshot.ram_total_mb || 0) / 1024)} GB
${snapshot.ram_slots_used || '?'} / ${snapshot.ram_slots_total || '?'} slots` }, + { label: 'RAM', icon: '💾', value: ramValue }, { label: 'GPU', icon: '🎮', value: snapshot.gpu_model || snapshot.gpu_summary || 'N/A' }, { label: 'Stockage', icon: '💿', value: snapshot.storage_summary || 'N/A' }, { label: 'Réseau', icon: '🌐', value: snapshot.network_interfaces_json ? `${JSON.parse(snapshot.network_interfaces_json).length} interface(s)` : 'N/A' }, @@ -141,6 +160,126 @@ function renderLastBenchmark() { `; } +// Render network details +function renderNetworkDetails() { + const container = document.getElementById('networkDetails'); + const snapshot = currentDevice.last_hardware_snapshot; + const bench = currentDevice.last_benchmark; + + if (!snapshot || !snapshot.network_interfaces_json) { + container.innerHTML = '

Aucune information réseau disponible

'; + return; + } + + try { + const interfaces = JSON.parse(snapshot.network_interfaces_json); + + if (!interfaces || interfaces.length === 0) { + container.innerHTML = '

Aucune interface réseau détectée

'; + return; + } + + let html = '
'; + + // Interface details + interfaces.forEach(iface => { + const typeIcon = iface.type === 'ethernet' ? '🔌' : (iface.type === 'wifi' ? '📡' : '🌐'); + const wol = iface.wake_on_lan; + const wolBadge = wol === true + ? 'WoL ✓' + : (wol === false ? 'WoL ✗' : ''); + + html += ` +
+
+
+
${typeIcon} ${escapeHtml(iface.name)}
+
${escapeHtml(iface.type || 'unknown')}
+
+
${wolBadge}
+
+ +
+ ${iface.ip ? `
IP: ${escapeHtml(iface.ip)}
` : ''} + ${iface.mac ? `
MAC: ${escapeHtml(iface.mac)}
` : ''} + ${iface.speed_mbps ? `
Vitesse: ${iface.speed_mbps} Mbps
` : ''} + ${iface.driver ? `
Driver: ${escapeHtml(iface.driver)}
` : ''} +
+
+ `; + }); + + // Network benchmark results (iperf3) + if (bench && bench.network_score !== null && bench.network_score !== undefined) { + let netBenchHtml = '
'; + netBenchHtml += '
📈 Résultats Benchmark Réseau (iperf3)
'; + netBenchHtml += '
'; + + // Try to parse network_results_json if available + let uploadMbps = null; + let downloadMbps = null; + let pingMs = null; + + if (bench.network_results_json) { + try { + const netResults = typeof bench.network_results_json === 'string' + ? JSON.parse(bench.network_results_json) + : bench.network_results_json; + uploadMbps = netResults.upload_mbps; + downloadMbps = netResults.download_mbps; + pingMs = netResults.ping_ms; + } catch (e) { + console.warn('Failed to parse network_results_json:', e); + } + } + + if (uploadMbps !== null && uploadMbps !== undefined) { + netBenchHtml += ` +
+
↑ ${uploadMbps.toFixed(2)}
+
Upload Mbps
+
+ `; + } + + if (downloadMbps !== null && downloadMbps !== undefined) { + netBenchHtml += ` +
+
↓ ${downloadMbps.toFixed(2)}
+
Download Mbps
+
+ `; + } + + if (pingMs !== null && pingMs !== undefined) { + netBenchHtml += ` +
+
${typeof pingMs === 'number' ? pingMs.toFixed(2) : pingMs}
+
Ping ms
+
+ `; + } + + netBenchHtml += ` +
+
${bench.network_score.toFixed(2)}
+
Score
+
+ `; + + netBenchHtml += '
'; + html += netBenchHtml; + } + + html += '
'; + container.innerHTML = html; + + } catch (error) { + console.error('Failed to parse network interfaces:', error); + container.innerHTML = '

Erreur lors du parsing des données réseau

'; + } +} + // Load benchmark history async function loadBenchmarkHistory() { const container = document.getElementById('benchmarkHistory'); diff --git a/frontend/js/devices.js b/frontend/js/devices.js index 6be9045..e2c4d5d 100644 --- a/frontend/js/devices.js +++ b/frontend/js/devices.js @@ -1,194 +1,327 @@ -// Linux BenchTools - Devices List Logic +// Linux BenchTools - Devices Two-Panel Layout -const { formatRelativeTime, createScoreBadge, getScoreBadgeText, escapeHtml, showError, showEmptyState, formatTags, debounce } = window.BenchUtils; +const { formatRelativeTime, createScoreBadge, getScoreBadgeText, escapeHtml, showError, showEmptyState, formatTags } = window.BenchUtils; const api = window.BenchAPI; -let currentPage = 1; -const pageSize = 20; -let searchQuery = ''; let allDevices = []; +let selectedDeviceId = null; // Load devices async function loadDevices() { - const container = document.getElementById('devicesContainer'); + const listContainer = document.getElementById('deviceList'); try { - const data = await api.getDevices({ page_size: 1000 }); // Get all for client-side filtering + const data = await api.getDevices({ page_size: 1000 }); // Get all devices allDevices = data.items || []; if (allDevices.length === 0) { - showEmptyState(container, 'Aucun device trouvé. Exécutez un benchmark sur une machine pour commencer.', '📊'); + listContainer.innerHTML = '
📊
Aucun device
'; return; } - renderDevices(); + // Sort by global_score descending + allDevices.sort((a, b) => { + const scoreA = a.last_benchmark?.global_score ?? -1; + const scoreB = b.last_benchmark?.global_score ?? -1; + return scoreB - scoreA; + }); + + renderDeviceList(); + + // Auto-select first device if none selected + if (!selectedDeviceId && allDevices.length > 0) { + selectDevice(allDevices[0].id); + } } catch (error) { console.error('Failed to load devices:', error); - showError(container, 'Impossible de charger les devices. Vérifiez que le backend est accessible.'); + listContainer.innerHTML = '
❌ Erreur de chargement
'; } } -// Filter devices based on search query -function filterDevices() { - if (!searchQuery) { - return allDevices; - } +// Render device list (left panel) +function renderDeviceList() { + const listContainer = document.getElementById('deviceList'); - const query = searchQuery.toLowerCase(); + listContainer.innerHTML = allDevices.map(device => { + const globalScore = device.last_benchmark?.global_score; + const isSelected = device.id === selectedDeviceId; - return allDevices.filter(device => { - const hostname = (device.hostname || '').toLowerCase(); - const description = (device.description || '').toLowerCase(); - const tags = (device.tags || '').toLowerCase(); - const location = (device.location || '').toLowerCase(); + const scoreText = globalScore !== null && globalScore !== undefined + ? Math.round(globalScore) + : 'N/A'; - return hostname.includes(query) || - description.includes(query) || - tags.includes(query) || - location.includes(query); - }); + const scoreClass = globalScore !== null && globalScore !== undefined + ? window.BenchUtils.getScoreBadgeClass(globalScore) + : 'badge'; + + return ` +
+
+
+ ${escapeHtml(device.hostname)} +
+ + ${scoreText} + +
+ ${device.last_benchmark?.run_at ? ` +
+ ⏱️ ${formatRelativeTime(device.last_benchmark.run_at)} +
+ ` : '
⚠️ Pas de benchmark
'} +
+ `; + }).join(''); } -// Render devices -function renderDevices() { - const container = document.getElementById('devicesContainer'); - const filteredDevices = filterDevices(); +// Select device and display details +async function selectDevice(deviceId) { + selectedDeviceId = deviceId; + renderDeviceList(); // Update selection in list - if (filteredDevices.length === 0) { - showEmptyState(container, 'Aucun device ne correspond à votre recherche.', '🔍'); - return; + const detailsContainer = document.getElementById('deviceDetailsContainer'); + detailsContainer.innerHTML = '
Chargement des détails...
'; + + try { + const device = await api.getDevice(deviceId); + renderDeviceDetails(device); + } catch (error) { + console.error('Failed to load device details:', error); + showError(detailsContainer, 'Impossible de charger les détails du device.'); } - - // Sort by global_score descending - const sortedDevices = filteredDevices.sort((a, b) => { - const scoreA = a.last_benchmark?.global_score ?? -1; - const scoreB = b.last_benchmark?.global_score ?? -1; - return scoreB - scoreA; - }); - - // Pagination - const startIndex = (currentPage - 1) * pageSize; - const endIndex = startIndex + pageSize; - const paginatedDevices = sortedDevices.slice(startIndex, endIndex); - - // Render device cards - container.innerHTML = paginatedDevices.map(device => createDeviceCard(device)).join(''); - - // Render pagination - renderPagination(filteredDevices.length); } -// Create device card HTML -function createDeviceCard(device) { +// Render device details (right panel) +function renderDeviceDetails(device) { + const detailsContainer = document.getElementById('deviceDetailsContainer'); + const snapshot = device.last_hardware_snapshot; const bench = device.last_benchmark; + // Hardware summary + const cpuModel = snapshot?.cpu_model || 'N/A'; + const cpuCores = snapshot?.cpu_cores || '?'; + const cpuThreads = snapshot?.cpu_threads || '?'; + const ramTotalGB = Math.round((snapshot?.ram_total_mb || 0) / 1024); + const ramUsedMB = snapshot?.ram_used_mb || 0; + const ramFreeMB = snapshot?.ram_free_mb || 0; + const ramSharedMB = snapshot?.ram_shared_mb || 0; + const gpuSummary = snapshot?.gpu_summary || 'N/A'; + const storage = snapshot?.storage_summary || 'N/A'; + const osName = snapshot?.os_name || 'N/A'; + const kernelVersion = snapshot?.kernel_version || 'N/A'; + + // RAM usage calculation + let ramUsageHtml = `${ramTotalGB} GB`; + if (ramUsedMB > 0 || ramFreeMB > 0) { + const usagePercent = ramTotalGB > 0 ? Math.round((ramUsedMB / (snapshot.ram_total_mb || 1)) * 100) : 0; + ramUsageHtml = ` + ${ramTotalGB} GB (${usagePercent}% utilisé)
+ + Utilisée: ${Math.round(ramUsedMB / 1024)}GB • + Libre: ${Math.round(ramFreeMB / 1024)}GB${ramSharedMB > 0 ? ` • Partagée: ${Math.round(ramSharedMB / 1024)}GB` : ''} + + `; + } + + // Benchmark scores const globalScore = bench?.global_score; const cpuScore = bench?.cpu_score; const memScore = bench?.memory_score; const diskScore = bench?.disk_score; const netScore = bench?.network_score; const gpuScore = bench?.gpu_score; - const runAt = bench?.run_at; const globalScoreHtml = globalScore !== null && globalScore !== undefined - ? `${getScoreBadgeText(globalScore)}` + ? `${getScoreBadgeText(globalScore)}` : 'N/A'; - return ` -
-
-
-
${escapeHtml(device.hostname)}
-
- ${escapeHtml(device.description || 'Aucune description')} + // Network details + let networkHtml = ''; + if (snapshot?.network_interfaces_json) { + try { + const interfaces = JSON.parse(snapshot.network_interfaces_json); + networkHtml = interfaces.map(iface => { + const typeIcon = iface.type === 'ethernet' ? '🔌' : '📡'; + const wolBadge = iface.wake_on_lan === true + ? 'WoL ✓' + : 'WoL ✗'; + + return ` +
+
+ ${typeIcon} ${escapeHtml(iface.name)} (${iface.type})${wolBadge} +
+
+ IP: ${iface.ip || 'N/A'} • MAC: ${iface.mac || 'N/A'}
+ Vitesse: ${iface.speed_mbps ? iface.speed_mbps + ' Mbps' : 'N/A'} + ${iface.driver ? ` • Driver: ${iface.driver}` : ''} +
+ `; + }).join(''); + } catch (e) { + networkHtml = '

Erreur de parsing JSON

'; + } + } + + // Network benchmark results (iperf3) + let netBenchHtml = ''; + if (bench?.network_results_json) { + try { + const netResults = JSON.parse(bench.network_results_json); + netBenchHtml = ` +
+
📈 Résultats Benchmark Réseau (iperf3)
+
+
+
+ ↑ ${netResults.upload_mbps?.toFixed(2) || 'N/A'} +
+
Upload Mbps
+
+
+
+ ↓ ${netResults.download_mbps?.toFixed(2) || 'N/A'} +
+
Download Mbps
+
+
+
+ ${netResults.ping_ms?.toFixed(2) || 'N/A'} +
+
Ping ms
+
+
+
+ ${netResults.score?.toFixed(2) || 'N/A'} +
+
Score
+
+
+
+ `; + } catch (e) { + console.error('Error parsing network results:', e); + } + } + + detailsContainer.innerHTML = ` + +
+
+
+

${escapeHtml(device.hostname)}

+

+ ${escapeHtml(device.description || 'Aucune description')} +

+ ${device.location ? `

📍 ${escapeHtml(device.location)}

` : ''} + ${device.tags ? `
${formatTags(device.tags)}
` : ''}
${globalScoreHtml}
+
-
- ${device.location ? `📍 ${escapeHtml(device.location)}` : ''} - ${bench?.run_at ? `⏱️ ${formatRelativeTime(runAt)}` : ''} + + ${bench ? ` +
+

📊 Scores de Benchmark

+
+ ${createScoreCard(cpuScore, 'CPU', '🔧')} + ${createScoreCard(memScore, 'Mémoire', '💾')} + ${createScoreCard(diskScore, 'Disque', '💿')} + ${createScoreCard(netScore, 'Réseau', '🌐')} + ${createScoreCard(gpuScore, 'GPU', '🎮')}
+
+ ⏱️ Dernier benchmark: ${bench.run_at ? formatRelativeTime(bench.run_at) : 'N/A'} +
+
+ ` : '
⚠️ Aucun benchmark disponible
'} - ${device.tags ? `
${formatTags(device.tags)}
` : ''} + +
+

🖥️ Résumé Matériel

+
+ ${createInfoCard('🔧 CPU', `${escapeHtml(cpuModel)}
${cpuCores} cores / ${cpuThreads} threads`)} + ${createInfoCard('💾 RAM', ramUsageHtml)} + ${createInfoCard('🎮 GPU', escapeHtml(gpuSummary))} + ${createInfoCard('💿 Storage', escapeHtml(storage))} + ${createInfoCard('🐧 OS', `${escapeHtml(osName)}
Kernel: ${escapeHtml(kernelVersion)}`)} + ${createInfoCard('⏰ Créé le', new Date(device.created_at).toLocaleDateString('fr-FR'))} +
+
-
- ${createScoreBadge(cpuScore, 'CPU')} - ${createScoreBadge(memScore, 'MEM')} - ${createScoreBadge(diskScore, 'DISK')} - ${createScoreBadge(netScore, 'NET')} - ${createScoreBadge(gpuScore, 'GPU')} + + ${networkHtml || netBenchHtml ? ` +
+

🌐 Détails Réseau

+ ${networkHtml} + ${netBenchHtml} +
+ ` : ''} + + + + `; +} + +// Create score card for display +function createScoreCard(score, label, icon) { + const scoreValue = score !== null && score !== undefined ? Math.round(score) : 'N/A'; + const badgeClass = score !== null && score !== undefined + ? window.BenchUtils.getScoreBadgeClass(score) + : 'badge'; + + return ` +
+
${icon}
+
${label}
+
+ ${scoreValue}
`; } -// Render pagination -function renderPagination(totalItems) { - const container = document.getElementById('paginationContainer'); - - if (totalItems <= pageSize) { - container.innerHTML = ''; - return; - } - - const totalPages = Math.ceil(totalItems / pageSize); - - container.innerHTML = ` -