This commit is contained in:
2025-12-07 13:52:48 +01:00
parent 496a7854b0
commit 919629b3c1
4 changed files with 1375 additions and 0 deletions

121
01_vision_fonctionnelle.md Normal file
View File

@@ -0,0 +1,121 @@
# 01 Vision fonctionnelle et objectifs
## Nom de lapplication
Nom provisoire : **Linux BenchTools**
Objectif : application self-hosted permettant de :
- Recenser les machines (physiques, VM, SBC type Raspberry Pi).
- Collecter des informations matérielles (CPU, RAM, GPU, stockage, réseau, carte mère, OS).
- Lancer des benchmarks standardisés via un **script client** exécuté sur les machines.
- Recevoir et stocker les résultats de bench sur un **serveur central**.
- Calculer une **note globale** et des sous-scores (CPU, mémoire, disque, réseau, GPU).
- Afficher un **dashboard** triant les machines par performance globale.
- Associer des liens constructeurs et des documents (PDF / images) à chaque machine.
Lapplication doit être :
- **Self-hosted** sur une machine Linux (Debian) au format Docker.
- Légère (SQLite, pas de dépendance à une grosse base).
- Accessible sur le réseau local (via reverse proxy existant).
- Exploitable autant pour des serveurs que des PC ou des VMs.
---
## Rôles et usages
### Rôle principal : utilisateur/admin unique (Gilles)
Actions possibles :
- Ajouter/modifier une machine (device) et ses métadonnées (description, localisation, tags).
- Visualiser les informations hardware associées à une machine.
- Lancer un bench sur une machine en copiant une commande fournie par linterface.
- Visualiser lhistorique des benchmarks dune machine.
- Consulter les notes globales et par catégorie (CPU, RAM, disque, réseau, GPU).
- Ajouter/éditer des liens vers les sites constructeurs.
- Uploader des notices PDF, factures, schémas, photos.
---
## Fonctionnalités principales (MVP)
1. **Gestion des machines (devices)**
- CRUD basique :
- Créer un device (ou auto-création à la réception dun bench).
- Lire les infos dun device.
- Modifier la description, la localisation, les tags.
- Un device représente une machine logique (hostname ou nom choisi).
2. **Snapshots hardware**
- À chaque bench, un snapshot de létat matériel et OS est enregistré :
- CPU (modèle, cœurs, threads, fréquences, cache, vendor).
- RAM (total, slots, type, capacités).
- GPU (modèle, mémoire, vendor).
- Disques (liste, type, taille, interface, SMART résumé).
- Réseau (interfaces, vitesses, IPs).
- Carte mère (vendor, modèle, BIOS).
- OS (nom, version, kernel, architecture, type machine : bare metal / VM).
- Ces infos sont liées à un `hardware_snapshot` et référencées par un `benchmark`.
3. **Benchmarks**
- Réception dun JSON produit par un script Bash exécuté sur la machine.
- Enregistrement dun `benchmark` avec :
- Date/heure du run.
- Version du script client.
- Scores par catégorie : `cpu_score`, `memory_score`, `disk_score`, `network_score`, `gpu_score`.
- `global_score` calculé selon une formule configurable (par ex. moyenne pondérée).
- Détails bruts en JSON (`details_json`) pour audit.
4. **Liens constructeur**
- Attacher à un device des liens HTTP :
- Fiche produit constructeur.
- Page support / BIOS / drivers.
- Documentation technique.
5. **Documents**
- Upload de fichiers (surtout PDF) associés à un device :
- Notices / manuels.
- Factures.
- Photos / schémas.
- Stockage sur disque, références dans SQLite.
6. **Dashboard Web**
- Page daccueil :
- Statistiques globales (nombre de devices, nombre total de benchmarks, date dernier bench, score moyen).
- Tableau trié par `global_score` décroissant.
- Indicateur compact par ligne :
- Hostname + description.
- Dernier `global_score`.
- Détail CPU/Mem/Disk/Net/GPU du dernier bench.
- Section "Quick bench script" :
- Affichage de la commande shell à copier/coller pour exécuter le script client.
---
## Non-objectifs (MVP)
- Pas de multi-tenant ni de gestion avancée de droits utilisateurs.
- Pas de monitoring temps réel (ce nest pas un outil de supervision).
- Pas de support Windows/macOS dans la première version (Linux seulement).
- Pas dagent resident/daemon permanent sur les clients (simple script à exécuter ponctuellement).
---
## Contraintes techniques
- Backend en **Python** (FastAPI recommandé).
- Base de données : **SQLite** (fichier sur disque).
- WebUI simple (HTML/CSS/JS minimal, éventuellement HTMX).
- Déploiement via **Docker** + `docker-compose`.
- Script client en **Bash** (priorité Debian/Ubuntu, extensible ensuite).
---
## Critères de réussite (MVP)
- Être capable de :
- Déployer lapp via `docker-compose up -d`.
- Copier une commande depuis le Dashboard, lexécuter sur une machine Linux, et voir :
- Le device apparaître (ou se mettre à jour).
- Un nouveau benchmark visible dans linterface.
- Voir la machine triée correctement dans le dashboard par `global_score`.
- Pouvoir attacher au moins un PDF à un device et le télécharger depuis linterface.

242
02_model_donnees.md Normal file
View File

@@ -0,0 +1,242 @@
# 02 Modèle de données (SQLite)
Objectif : définir le schéma relationnel pour SQLite, exploité par SQLAlchemy (ou équivalent).
## Vue densemble
Tables principales :
- `devices`
- `hardware_snapshots`
- `benchmarks`
- `manufacturer_links`
- `documents`
Optionnel (plus tard) :
- `users` (si auth interne)
- `settings` (config globale de lapp)
---
## Table devices
Représente une machine (physique ou virtuelle).
Champs :
- `id` (INTEGER, PK, autoincrement)
- `hostname` (TEXT, non nul)
- `fqdn` (TEXT, nullable)
- `description` (TEXT, nullable)
- `asset_tag` (TEXT, nullable) identifiant interne/inventaire
- `location` (TEXT, nullable)
- `owner` (TEXT, nullable)
- `tags` (TEXT, nullable) liste séparée par virgules ou JSON
- `created_at` (DATETIME, non nul)
- `updated_at` (DATETIME, non nul)
Contraintes :
- `hostname` peut être utilisé comme identifiant logique si `device_identifier` nest pas fourni par le client.
- Index recommandé : `idx_devices_hostname`.
---
## Table hardware_snapshots
Snapshot détaillé de la configuration matérielle et OS au moment dun bench.
Champs :
- `id` (INTEGER, PK)
- `device_id` (INTEGER, FK -> devices.id)
- `captured_at` (DATETIME, non nul)
### CPU
- `cpu_vendor` (TEXT)
- `cpu_model` (TEXT)
- `cpu_microarchitecture` (TEXT, nullable)
- `cpu_cores` (INTEGER)
- `cpu_threads` (INTEGER)
- `cpu_base_freq_ghz` (REAL, nullable)
- `cpu_max_freq_ghz` (REAL, nullable)
- `cpu_cache_l1_kb` (INTEGER, nullable)
- `cpu_cache_l2_kb` (INTEGER, nullable)
- `cpu_cache_l3_kb` (INTEGER, nullable)
- `cpu_flags` (TEXT, nullable) chaîne ou JSON (AVX, AES, etc.)
- `cpu_tdp_w` (REAL, nullable)
### RAM
- `ram_total_mb` (INTEGER)
- `ram_slots_total` (INTEGER, nullable)
- `ram_slots_used` (INTEGER, nullable)
- `ram_ecc` (BOOLEAN, nullable)
- `ram_layout_json` (TEXT, nullable)
Exemple JSON :
```json
[
{ "slot": "DIMM_A1", "size_mb": 16384, "type": "DDR4", "speed_mhz": 2133, "vendor": "Corsair", "part_number": "XXXX" }
]
```
### GPU
- `gpu_summary` (TEXT, nullable) ex: "Intel UHD 630"
- `gpu_vendor` (TEXT, nullable)
- `gpu_model` (TEXT, nullable)
- `gpu_driver_version` (TEXT, nullable)
- `gpu_memory_dedicated_mb` (INTEGER, nullable)
- `gpu_memory_shared_mb` (INTEGER, nullable)
- `gpu_api_support` (TEXT, nullable) ex: "OpenGL 4.6, Vulkan"
### Stockage
- `storage_summary` (TEXT, nullable) résumé human readable
- `storage_devices_json` (TEXT, nullable)
Exemple JSON :
```json
[
{
"name": "/dev/nvme0n1",
"type": "NVMe",
"interface": "PCIe 3.0 x4",
"capacity_gb": 1000,
"vendor": "Samsung",
"model": "970 EVO Plus",
"smart_health": "PASSED",
"temperature_c": 42
}
]
```
- `partitions_json` (TEXT, nullable)
### Réseau
- `network_interfaces_json` (TEXT, nullable)
Exemple :
```json
[
{
"name": "eth0",
"type": "ethernet",
"mac": "aa:bb:cc:dd:ee:ff",
"ip": "10.0.0.10",
"speed_mbps": 1000,
"driver": "e1000e"
}
]
```
### OS / Carte mère
- `os_name` (TEXT)
- `os_version` (TEXT)
- `kernel_version` (TEXT)
- `architecture` (TEXT) "x86_64", "arm64"
- `virtualization_type` (TEXT, nullable) "kvm", "vmware", "none", etc.
- `motherboard_vendor` (TEXT, nullable)
- `motherboard_model` (TEXT, nullable)
- `bios_version` (TEXT, nullable)
- `bios_date` (TEXT, nullable)
### Divers
- `sensors_json` (TEXT, nullable) valeurs de températures, etc.
- `raw_info_json` (TEXT, nullable) dump brut de commandes (inxi, lshw, etc.)
Index recommandés :
- `idx_hw_snapshots_device_id`
- `idx_hw_snapshots_captured_at`
---
## Table benchmarks
Représente un run de bench.
Champs :
- `id` (INTEGER, PK)
- `device_id` (INTEGER, FK -> devices.id)
- `hardware_snapshot_id` (INTEGER, FK -> hardware_snapshots.id)
- `run_at` (DATETIME, non nul)
- `bench_script_version` (TEXT, non nul)
### Scores
- `global_score` (REAL, non nul)
- `cpu_score` (REAL, nullable)
- `memory_score` (REAL, nullable)
- `disk_score` (REAL, nullable)
- `network_score` (REAL, nullable)
- `gpu_score` (REAL, nullable)
### Détails
- `details_json` (TEXT, non nul)
Contient toutes les valeurs brutes :
- CPU events/sec, temps dexécution, etc.
- Throughput mémoire.
- Read/write MB/s, IOPS.
- Vitesse iperf3, ping.
- Score GPU, etc.
- `notes` (TEXT, nullable)
Index :
- `idx_benchmarks_device_id`
- `idx_benchmarks_run_at`
---
## Table manufacturer_links
Liens vers les ressources en ligne du constructeur.
Champs :
- `id` (INTEGER, PK)
- `device_id` (INTEGER, FK -> devices.id)
- `label` (TEXT, non nul) ex: "Support HP", "Page produit Lenovo"
- `url` (TEXT, non nul)
Index :
- `idx_links_device_id`
---
## Table documents
Fichiers (PDF, images, etc.) associés à un device.
Champs :
- `id` (INTEGER, PK)
- `device_id` (INTEGER, FK -> devices.id)
- `doc_type` (TEXT, non nul) `manual`, `warranty`, `invoice`, `photo`, `other`
- `filename` (TEXT, non nul) nom original
- `stored_path` (TEXT, non nul) chemin relatif sur le serveur
- `mime_type` (TEXT, non nul)
- `size_bytes` (INTEGER, non nul)
- `uploaded_at` (DATETIME, non nul)
Index :
- `idx_documents_device_id`
---
## Éventuelle table settings (optionnelle)
Pour stocker des paramètres globaux de lapplication.
Champs :
- `id` (INTEGER, PK)
- `key` (TEXT, unique)
- `value` (TEXT)
Exemples :
- `default_bench_server_url`
- `default_iperf_server`
- `score_weights_json` (pondération CPU/Mem/Disque/Réseau/GPU)

537
03_api_backend.md Normal file
View File

@@ -0,0 +1,537 @@
# 03 Spécification API Backend (FastAPI)
Objectif : définir précisément les endpoints backend, les schémas JSON échangés et les règles métier associées.
LAPI sera exposée sous `/api`.
---
## 1. Contenu attendu de ce fichier
Ce document doit servir de référence au développeur backend. Il décrit :
1. Le format du payload JSON envoyé par le script de benchmark (`bench.sh`) vers le backend.
2. Les endpoints REST nécessaires :
- Réception dun benchmark.
- Consultation des devices.
- Consultation de lhistorique des benchmarks.
- Gestion des liens constructeur.
- Gestion des documents (PDF, images).
3. Les règles dauthentification (token simple).
4. Les codes derreur principaux et attentes de validation.
---
## 2. Schéma JSON payload dun benchmark
Endpoint cible : `POST /api/benchmark`
Le script client envoie un JSON de la forme :
```json
{
"device_identifier": "elitedesk-800g3",
"bench_script_version": "1.0.0",
"hardware": {
"cpu": {
"vendor": "Intel",
"model": "Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz",
"microarchitecture": "Skylake",
"cores": 4,
"threads": 8,
"base_freq_ghz": 3.4,
"max_freq_ghz": 4.0,
"cache_l1_kb": 256,
"cache_l2_kb": 1024,
"cache_l3_kb": 8192,
"flags": ["avx", "avx2", "aes"],
"tdp_w": 65
},
"ram": {
"total_mb": 65536,
"slots_total": 4,
"slots_used": 4,
"ecc": false,
"layout": [
{
"slot": "DIMM_A1",
"size_mb": 16384,
"type": "DDR4",
"speed_mhz": 2133,
"vendor": "Corsair",
"part_number": "XYZ"
}
]
},
"gpu": {
"vendor": "Intel",
"model": "Intel HD Graphics 530",
"driver_version": "xxx",
"memory_dedicated_mb": null,
"memory_shared_mb": 512,
"api_support": ["OpenGL 4.5"]
},
"storage": {
"devices": [
{
"name": "/dev/nvme0n1",
"type": "NVMe",
"interface": "PCIe 3.0 x4",
"capacity_gb": 1000,
"vendor": "Samsung",
"model": "970 EVO Plus",
"smart_health": "PASSED",
"temperature_c": 42
}
],
"partitions": [
{
"name": "/dev/nvme0n1p1",
"mount_point": "/",
"fs_type": "ext4",
"used_gb": 50,
"total_gb": 100
}
]
},
"network": {
"interfaces": [
{
"name": "eth0",
"type": "ethernet",
"mac": "aa:bb:cc:dd:ee:ff",
"ip": "10.0.0.10",
"speed_mbps": 1000,
"driver": "e1000e"
}
]
},
"motherboard": {
"vendor": "HP",
"model": "HP 8054",
"bios_version": "P01 Ver. 02.48",
"bios_date": "2023-05-10"
},
"os": {
"name": "Debian",
"version": "12 (bookworm)",
"kernel_version": "6.1.0-xx-amd64",
"architecture": "x86_64",
"virtualization_type": "none"
},
"sensors": {
"cpu_temp_c": 45,
"disk_temps_c": {
"/dev/nvme0n1": 42
}
},
"raw_info": {
"lscpu": "raw text…",
"lsblk": "raw text…"
}
},
"results": {
"cpu": {
"events_per_sec": 12000,
"duration_s": 10,
"score": 90
},
"memory": {
"throughput_mib_s": 20000,
"score": 95
},
"disk": {
"read_mb_s": 1200,
"write_mb_s": 1000,
"iops_read": 50000,
"iops_write": 45000,
"latency_ms": 1.2,
"score": 94
},
"network": {
"upload_mbps": 930,
"download_mbps": 940,
"ping_ms": 1.2,
"jitter_ms": 0.3,
"packet_loss_percent": 0.0,
"score": 88
},
"gpu": {
"glmark2_score": null,
"score": null
},
"global_score": 92
}
}
```
Remarques importantes :
- Certains champs peuvent être `null` ou absents en fonction de la machine (ex: pas de GPU dédié).
- Le backend doit être robuste à labsence de certains blocs (ex: pas de `network` si aucun test réseau).
---
## 3. Logique de traitement côté backend
1. **Auth**
- Lecture du header `Authorization: Bearer <TOKEN>`.
- Comparaison avec une valeur stockée dans la config/ENV (`API_TOKEN`).
- Si token invalide ou absent : réponse 401.
2. **Résolution du device**
- Chercher un enregistrement `devices` avec `hostname == device_identifier`.
- Si trouvé :
- Utiliser `device_id` existant.
- Sinon :
- Créer un nouveau `device` minimal avec :
- `hostname = device_identifier`
- `created_at`, `updated_at` = maintenant.
3. **Création dun hardware_snapshot**
- Mapper le contenu `hardware` vers la table `hardware_snapshots` telle que définie dans `02_model_donnees.md`.
- Exemples :
- `hardware.cpu.vendor` -> `cpu_vendor`
- `hardware.ram.total_mb` -> `ram_total_mb`
- `hardware.storage.devices` -> JSON dans `storage_devices_json`
- etc.
- `captured_at` = `now()` (ou `run_at` du benchmark si fourni).
4. **Création dun benchmark**
- `run_at` = `now()` (ou fourni explicitement plus tard).
- `bench_script_version` = champ du payload.
- Scores :
- `cpu_score` = `results.cpu.score` (si présent).
- `memory_score` = `results.memory.score`
- `disk_score` = `results.disk.score`
- `network_score` = `results.network.score`
- `gpu_score` = `results.gpu.score`
- `global_score` = `results.global_score`
- `details_json` = JSON complet de `results` (ou éventuellement de `results` + métriques brutes).
5. **Réponse**
- En cas de succès, renvoyer :
```json
{
"status": "ok",
"device_id": 1,
"benchmark_id": 42
}
```
---
## 4. Endpoints détaillés
### 4.1. POST /api/benchmark
- Rôle : point dentrée unique des résultats du script client.
- Auth : header `Authorization: Bearer <TOKEN>`.
- Body : JSON (voir schéma ci-dessus).
- Codes de réponse :
- `200` succès.
- `400` JSON invalide / validation Pydantic échouée.
- `401` token invalide ou manquant.
- `500` erreur interne.
---
### 4.2. GET /api/devices
Objectif : lister les devices avec un résumé de leur dernier benchmark.
Paramètres query :
- `page` (int, optionnel, défaut 1)
- `page_size` (int, optionnel, défaut 20)
- `search` (string, optionnel filtre sur hostname/description/tags)
Réponse (exemple simplifié) :
```json
{
"items": [
{
"id": 1,
"hostname": "elitedesk-800g3",
"description": "HP EliteDesk 800 G3 SFF",
"location": "Bureau",
"tags": ["lab", "dev"],
"last_benchmark": {
"id": 42,
"run_at": "2025-12-07T10:32:00Z",
"global_score": 92,
"cpu_score": 90,
"memory_score": 95,
"disk_score": 94,
"network_score": 88,
"gpu_score": null
}
}
],
"total": 1,
"page": 1,
"page_size": 20
}
```
---
### 4.3. GET /api/devices/{device_id}
Objectif : récupérer le détail dun device.
Réponse (exemple) :
```json
{
"id": 1,
"hostname": "elitedesk-800g3",
"fqdn": "elitedesk-800g3.maison43",
"description": "HP EliteDesk 800 G3 SFF (serveur dev)",
"asset_tag": "LAB-001",
"location": "Bureau",
"owner": "Gilles",
"tags": ["lab", "proxmox"],
"created_at": "2025-12-01T10:00:00Z",
"updated_at": "2025-12-07T10:32:00Z",
"last_hardware_snapshot": {
"captured_at": "2025-12-07T10:32:00Z",
"cpu_vendor": "Intel",
"cpu_model": "Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz",
"cpu_cores": 4,
"cpu_threads": 8,
"ram_total_mb": 65536,
"storage_summary": "NVMe 1TB + HDD 2TB",
"gpu_summary": "Intel HD Graphics 530",
"os_name": "Debian",
"os_version": "12 (bookworm)",
"kernel_version": "6.1.0-xx-amd64"
},
"last_benchmark": {
"id": 42,
"run_at": "2025-12-07T10:32:00Z",
"global_score": 92,
"cpu_score": 90,
"memory_score": 95,
"disk_score": 94,
"network_score": 88,
"gpu_score": null
}
}
```
---
### 4.4. GET /api/devices/{device_id}/benchmarks
Objectif : récupérer lhistorique de benchmarks dun device.
Paramètres :
- `limit` (int, défaut 20)
- `offset` (int, défaut 0)
Réponse :
```json
{
"items": [
{
"id": 42,
"run_at": "2025-12-07T10:32:00Z",
"global_score": 92,
"cpu_score": 90,
"memory_score": 95,
"disk_score": 94,
"network_score": 88,
"gpu_score": null
},
{
"id": 35,
"run_at": "2025-12-05T09:10:00Z",
"global_score": 89,
"cpu_score": 88,
"memory_score": 92,
"disk_score": 90,
"network_score": 85,
"gpu_score": null
}
],
"total": 2,
"limit": 20,
"offset": 0
}
```
---
### 4.5. GET /api/benchmarks/{benchmark_id}
Objectif : récupérer les détails complets dun benchmark.
Réponse :
```json
{
"id": 42,
"device_id": 1,
"hardware_snapshot_id": 10,
"run_at": "2025-12-07T10:32:00Z",
"bench_script_version": "1.0.0",
"global_score": 92,
"cpu_score": 90,
"memory_score": 95,
"disk_score": 94,
"network_score": 88,
"gpu_score": null,
"details": {
"cpu": {
"events_per_sec": 12000,
"duration_s": 10,
"score": 90
},
"memory": {
"throughput_mib_s": 20000,
"score": 95
},
"disk": {
"read_mb_s": 1200,
"write_mb_s": 1000,
"iops_read": 50000,
"iops_write": 45000,
"latency_ms": 1.2,
"score": 94
},
"network": {
"upload_mbps": 930,
"download_mbps": 940,
"ping_ms": 1.2,
"jitter_ms": 0.3,
"packet_loss_percent": 0,
"score": 88
},
"gpu": {
"glmark2_score": null,
"score": null
},
"global_score": 92
}
}
```
Note : `details` reflète `details_json` de la base.
---
### 4.6. Liens constructeur
#### GET /api/devices/{device_id}/links
Réponse :
```json
[
{
"id": 1,
"label": "Support HP",
"url": "https://support.hp.com/..."
},
{
"id": 2,
"label": "Page produit",
"url": "https://www.hp.com/..."
}
]
```
#### POST /api/devices/{device_id}/links
Body :
```json
{
"label": "Support HP",
"url": "https://support.hp.com/..."
}
```
Réponse : lobjet créé avec `id`.
#### PUT /api/links/{id}
Body idem POST (label + url).
#### DELETE /api/links/{id}
- Réponse : `204 No Content` en cas de succès.
---
### 4.7. Documents
#### GET /api/devices/{device_id}/docs
Réponse :
```json
[
{
"id": 1,
"doc_type": "manual",
"filename": "manual_elitedesk.pdf",
"mime_type": "application/pdf",
"size_bytes": 3456789,
"uploaded_at": "2025-05-01T10:00:00Z"
}
]
```
#### POST /api/devices/{device_id}/docs
- Type : `multipart/form-data`
- Champs :
- `file` (fichier binaire)
- `doc_type` (string)
Réponse : métadonnées du document créé.
#### GET /api/docs/{doc_id}/download
- Sert le fichier binaire avec le bon `Content-Type`.
---
## 5. Authentification
MVP : token simple.
- Variable denv `API_TOKEN` côté backend.
- Chaque requête `POST /api/benchmark` doit inclure :
```http
Authorization: Bearer <API_TOKEN>
```
- Les endpoints de lecture (GET) peuvent être :
- soit publics sur le LAN,
- soit protégés par un autre mécanisme (reverse proxy) hors scope de cette spec.
---
## 6. Gestion des erreurs et validations
- Utiliser des modèles Pydantic pour :
- valider la structure de `hardware` et `results` autant que possible sans être bloquant.
- autoriser des champs optionnels (None / manquants).
- Codes derreur génériques :
- 400 : données invalides (schema).
- 401 : auth.
- 404 : ressources inexistantes (device, benchmark, link, doc).
- 413 : payload trop volumineux (facultatif selon config).
- 500 : erreur interne.
Logs :
- Logguer les erreurs de parsing JSON.
- Logguer lorigine (IP) des appels `POST /api/benchmark`.
---

475
04_bench_script_client.md Normal file
View File

@@ -0,0 +1,475 @@
# 04 Spécification du script client de benchmark (bench.sh)
Objectif : définir précisément le comportement du script Bash exécuté sur les machines clientes pour :
- collecter les informations matérielles et système,
- exécuter les benchmarks,
- calculer les scores,
- envoyer un JSON complet au backend via HTTP.
Ce fichier sert de référence pour écrire `scripts/bench.sh` dans le dépôt.
---
## 1. Usage et interface en ligne de commande
Le script doit être exécutable en one-liner depuis une machine cliente, par exemple :
```bash
curl -s https://gitea.maison43.duckdns.org/gilles/linux-benchtools/raw/branch/main/scripts/bench.sh \
| bash -s -- \
--server https://bench.maison43/api/benchmark \
--token "XXXXXXX" \
--device "elitedesk-800g3" \
--iperf-server 10.0.0.10 \
--short
```
### 1.1. Arguments supportés
- `--server <URL>` (obligatoire)
URL de lendpoint backend `POST /api/benchmark`.
- `--token <TOKEN>` (obligatoire)
Token dauthentification à envoyer dans `Authorization: Bearer <TOKEN>`.
- `--device <NAME>` (optionnel)
Identifiant logique de la machine (`device_identifier`).
Si non fourni, utiliser `hostname` de la machine.
- `--iperf-server <HOST>` (optionnel)
Hôte/IP du serveur iperf3 utilisé pour les tests réseau.
- `--skip-cpu`, `--skip-memory`, `--skip-disk`, `--skip-network`, `--skip-gpu` (optionnels)
Permettent de désactiver certains tests.
- `--short` (optionnel)
Version “rapide” des tests (durées/tailles réduites).
- `--help`
Affiche un message daide et quitte.
### 1.2. Variables internes
- `BENCH_SCRIPT_VERSION` (string, ex: `"1.0.0"`)
Doit être mise à jour à chaque changement incompatible du script.
---
## 2. Pré-requis et compatibilité
### 2.1. OS visés (MVP)
- Debian / Ubuntu / Proxmox (base Debian).
Le script doit :
- Lire `/etc/os-release` pour détecter lOS.
- Utiliser `apt-get` pour installer les paquets manquants.
### 2.2. Outils nécessaires
Le script doit vérifier et installer si besoin :
- `curl`
- `jq` (construction JSON)
- `sysbench` (CPU + mémoire)
- `fio` (disque)
- `iperf3` (réseau, si `--iperf-server` fourni)
- `dmidecode` (RAM, carte mère, BIOS)
- `lsblk`
- `lscpu`
- `smartmontools` (optionnel pour SMART disques)
- `lm-sensors` (optionnel pour températures)
- `glmark2` (optionnel pour GPU si dispo)
---
## 3. Structure générale du script
1. Parser les arguments.
2. Vérifier `--server` et `--token` (sinon erreur + exit 1).
3. Déterminer :
- `DEVICE_IDENTIFIER` = `--device` ou `hostname`.
- `BENCH_SCRIPT_VERSION`.
4. Détecter lOS et préparer la commande dinstallation de paquets.
5. Vérifier/installer les outils nécessaires.
6. Collecter les informations hardware et OS.
7. Exécuter les benchmarks (en respectant les flags `--skip-*`).
8. Calculer les scores (CPU, mémoire, disque, réseau, GPU, global).
9. Construire le JSON.
10. Envoyer le JSON au backend.
11. Afficher un récap et le statut HTTP.
---
## 4. Collecte des informations matérielles et système
Toutes les infos doivent ensuite être assemblées dans le bloc `hardware` du JSON.
### 4.1. CPU
Commandes possibles :
- `lscpu`
- `/proc/cpuinfo`
Informations à extraire :
- `vendor` : ligne `Vendor ID` (ou `GenuineIntel`, `AuthenticAMD`, etc.).
- `model` : `Model name`.
- `microarchitecture` : optionnel (peut être déterminé via une table interne si souhaité, sinon laisser vide).
- `cores` : `Core(s) per socket` × `Socket(s)` ou `CPU(s)` minus hyperthreading.
- `threads` : `CPU(s)` (nombre logique).
- `base_freq_ghz` : depuis `lscpu` (MHz -> GHz).
- `max_freq_ghz` : `CPU max MHz` si disponible.
- `cache_l1_kb`, `cache_l2_kb`, `cache_l3_kb` : `L1d cache`, `L2 cache`, `L3 cache`.
- `flags` : liste depuis `Flags` / `Features`.
- `tdp_w` : non triviale à extraire dans un script, peut rester null.
### 4.2. RAM
Commandes :
- `free -m`
- `dmidecode --type memory` (requiert sudo)
Infos :
- `total_mb` : depuis `free -m`.
- `slots_total` : nombre dentrées `Locator` dans `dmidecode` (type DIMM).
- `slots_used` : slots où `Size` nest pas `No Module Installed`.
- `ecc` : champ `Total Width` vs `Data Width` ou `Error Correction Type`.
- `layout` : tableau dobjets avec :
- `slot` (Locator)
- `size_mb`
- `type` (DDR3/DDR4/DDR5/etc.)
- `speed_mhz`
- `vendor`
- `part_number`
### 4.3. GPU
Commandes :
- `lspci | grep -i vga`
- éventuellement `nvidia-smi` si NVIDIA.
Infos :
- `vendor` : Intel, NVIDIA, AMD…
- `model` : texte brut de `lspci`.
- `driver_version` : si récupérable (`nvidia-smi --query-gpu=driver_version`).
- `memory_dedicated_mb` : via `nvidia-smi`/outils spécifiques si possible, sinon null.
- `memory_shared_mb` : éventuellement via `lspci`/`/proc`, sinon null.
- `api_support` : optionnel (OpenGL/Vulkan), peut être laissé vide.
### 4.4. Stockage (disques et partitions)
Commandes :
- `lsblk -o NAME,SIZE,TYPE,MODEL,TRAN,MOUNTPOINT,FSTYPE`
- `smartctl -H /dev/sdX` (si présent)
- éventuellement `nvme list` / `nvme smart-log`.
Infos :
- `devices` : tableau dobjets :
- `name` (ex: `/dev/nvme0n1`)
- `type` (HDD/SSD/NVMe, déduit de `TYPE`/`TRAN`/nom).
- `interface` (SATA, PCIe 3.0 x4, USB, etc. si déductible).
- `capacity_gb` : depuis `SIZE`.
- `vendor` / `model` : `MODEL`.
- `smart_health` : `PASSED` / `FAILED` / null.
- `temperature_c` : si dispo via SMART.
- `partitions` : tableau dobjets :
- `name`
- `mount_point`
- `fs_type`
- `used_gb`
- `total_gb`
### 4.5. Réseau
Commandes :
- `ip addr`
- `ip -o link`
- `ethtool <iface>` (pour vitesse si dispo).
- pour Wi-Fi : `iw dev` / `iwconfig`.
Infos :
- `interfaces` : tableau dobjets :
- `name` (ex: `eth0`, `enp3s0`, `wlan0`)
- `type` (`ethernet`, `wifi`, `other`)
- `mac`
- `ip` (IPv4 principale si existante)
- `speed_mbps` : via `ethtool`.
- `driver` : éventuellement depuis `/sys/class/net/<iface>/device/driver`.
### 4.6. Carte mère / BIOS
Commandes :
- `dmidecode --type baseboard`
- `dmidecode --type bios`
Infos :
- `motherboard.vendor`
- `motherboard.model`
- `bios_version`
- `bios_date`
### 4.7. OS
Commandes :
- `/etc/os-release`
- `uname -r`
- `uname -m`
- `systemd-detect-virt` (si dispo).
Infos :
- `name` : ID ou PRETTY_NAME.
- `version` : VERSION ou VERSION_CODENAME.
- `kernel_version` : `uname -r`.
- `architecture` : `uname -m`.
- `virtualization_type` : sortie de `systemd-detect-virt` (kvm, qemu, none, etc.).
### 4.8. Capteurs (facultatif)
- `sensors` (lm-sensors)
- `smartctl -A` pour température disques.
Infos :
- `sensors.cpu_temp_c` (ou valeur moyenne).
- `sensors.disk_temps_c` : map `{ "/dev/nvme0n1": 42 }`.
---
## 5. Benchmarks à exécuter
Les résultats iront dans le bloc `results` du JSON.
### 5.1. CPU sysbench
Commande par défaut (mode complet) :
```bash
sysbench cpu --cpu-max-prime=20000 --threads="$(nproc)" run
```
Mode `--short` :
```bash
sysbench cpu --cpu-max-prime=10000 --threads="$(nproc)" run
```
Valeurs à extraire :
- `events_per_sec` : ligne `events per second: X`.
- `duration_s` : temps total (`total time:`).
Score CPU :
- Score simple :
- définir une valeur de référence (par ex. 5000 events/s = 50 points).
- `cpu_score = min(100, events_per_sec / ref * 50)` (ajuster plus tard).
- Pour linstant, le script peut :
- soit calculer cette note,
- soit juste envoyer les valeurs brutes et laisser le backend calculer.
### 5.2. Mémoire sysbench
Commande (complet) :
```bash
sysbench memory --memory-total-size=2G --memory-oper=write run
```
Mode `--short` :
```bash
sysbench memory --memory-total-size=512M --memory-oper=write run
```
Valeurs :
- `throughput_mib_s` : ligne `transferred (XXXX MiB/sec)`.
Score mémoire :
- Basé sur `throughput_mib_s` et une référence.
### 5.3. Disque fio
Profil simple (séquentiel read/write 1GiB) :
```bash
fio --name=bench_seq_rw \
--rw=readwrite \
--bs=1M \
--size=1G \
--numjobs=1 \
--iodepth=16 \
--filename=/tmp/fio_benchfile \
--direct=1 \
--group_reporting
```
Mode `--short` :
- Taille 256M.
Valeurs à extraire (via parsing ou `--output-format=json`) :
- `read_mb_s`
- `write_mb_s`
- éventuellement `iops_read`, `iops_write`, `latency_ms`.
Score disque :
- Moyenne pondérée de read/write vs valeurs de référence.
Après test, supprimer `/tmp/fio_benchfile`.
### 5.4. Réseau iperf3
Uniquement si `--iperf-server` fourni.
Download (client -> server, test reverse) :
```bash
iperf3 -c "$IPERF_SERVER" -R -J
```
Upload :
```bash
iperf3 -c "$IPERF_SERVER" -J
```
Utiliser le JSON (`-J`) + `jq` pour extraire :
- `upload_mbps`
- `download_mbps`
- `jitter_ms`
- `packet_loss_percent` (si UDP, option future).
Ping (latence) :
```bash
ping -c 5 "$IPERF_SERVER"
```
Extraire :
- `ping_ms` = moyenne.
Score réseau :
- Combinaison débit (min(up, down)) et latence (ping).
### 5.5. GPU glmark2 (optionnel)
Si `glmark2` disponible :
```bash
glmark2
```
Extraire :
- Score global `glmark2_score`.
Score GPU :
- Normalisation simple vs référence.
---
## 6. Construction du JSON
Le script utilise `jq` pour construire le JSON final :
Structure :
```json
{
"device_identifier": "...",
"bench_script_version": "1.0.0",
"hardware": { ... },
"results": { ... }
}
```
Principes :
- Utiliser `jq -n` et passer les valeurs via `--arg` / `--argjson`.
- Attention aux nombres vs strings (utiliser `--argjson` pour les nombres).
- Gérer proprement les valeurs nulles (par exemple si test GPU non réalisé).
Exemple (simplifié) en shell :
```bash
payload=$(jq -n --arg device_identifier "$DEVICE_IDENTIFIER" --arg bench_script_version "$BENCH_SCRIPT_VERSION" --argjson cpu "$CPU_JSON" --argjson ram "$RAM_JSON" --argjson results "$RESULTS_JSON" '{
device_identifier: $device_identifier,
bench_script_version: $bench_script_version,
hardware: {
cpu: $cpu,
ram: $ram
},
results: $results
}')
```
---
## 7. Envoi au backend
Commandes :
```bash
HTTP_RESPONSE=$(curl -s -o /tmp/bench_response.txt -w "%{http_code}" \
-X POST "$SERVER_URL" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d "$payload")
```
- Si `HTTP_RESPONSE` != `200` :
- Afficher un message derreur (console).
- Optionnel : afficher `/tmp/bench_response.txt`.
- Si succès :
- Afficher un message confirmant lID du benchmark si présent dans la réponse.
---
## 8. Gestion des erreurs à prévoir
- Absence de `--server` ou `--token` -> erreur et exit.
- Outils manquants et impossible à installer -> avertir, sauter le test concerné, transmettre `score = null`.
- Erreurs iperf3 (serveur indisponible) -> ignorer la partie réseau, `network` null.
- Temps de test trop long -> proposer un mode `--short`.
---
## 9. Journalisation locale (optionnelle)
- Possibilité de logger les infos dans `/var/log/linux_benchtools_client.log` ou `/tmp/linux_benchtools_client.log`.
- Log recommandé :
- Date/heure.
- SERVER, DEVICE_IDENTIFIER.
- Résumé des scores.
- Code HTTP de la réponse.
---
## 10. Bonnes pratiques
- Ne jamais supprimer ou modifier des fichiers système.
- Nettoyer les fichiers temporaires (fio, résultats intermédiaires).
- Garder le script idempotent : on peut le relancer sans casser la machine.
- Prévoir un délai total raisonnable pour un run complet (ex. < 510 minutes en mode complet, < 23 minutes en mode `--short`).