Files
serv_benchmark/docs/03_api_backend.md
2025-12-14 10:40:54 +01:00

538 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`.
---