12 KiB
Executable File
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.
L’API 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 :
- Le format du payload JSON envoyé par le script de benchmark (
bench.sh) vers le backend. - Les endpoints REST nécessaires :
- Réception d’un benchmark.
- Consultation des devices.
- Consultation de l’historique des benchmarks.
- Gestion des liens constructeur.
- Gestion des documents (PDF, images).
- Les règles d’authentification (token simple).
- Les codes d’erreur principaux et attentes de validation.
2. Schéma JSON – payload d’un benchmark
Endpoint cible : POST /api/benchmark
Le script client envoie un JSON de la forme :
{
"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
nullou absents en fonction de la machine (ex: pas de GPU dédié). - Le backend doit être robuste à l’absence de certains blocs (ex: pas de
networksi aucun test réseau).
3. Logique de traitement côté backend
-
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.
- Lecture du header
-
Résolution du device
- Chercher un enregistrement
devicesavechostname == device_identifier. - Si trouvé :
- Utiliser
device_idexistant.
- Utiliser
- Sinon :
- Créer un nouveau
deviceminimal avec :hostname = device_identifiercreated_at,updated_at= maintenant.
- Créer un nouveau
- Chercher un enregistrement
-
Création d’un hardware_snapshot
- Mapper le contenu
hardwarevers la tablehardware_snapshotstelle que définie dans02_model_donnees.md. - Exemples :
hardware.cpu.vendor->cpu_vendorhardware.ram.total_mb->ram_total_mbhardware.storage.devices-> JSON dansstorage_devices_json- etc.
captured_at=now()(ourun_atdu benchmark si fourni).
- Mapper le contenu
-
Création d’un 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.scoredisk_score=results.disk.scorenetwork_score=results.network.scoregpu_score=results.gpu.scoreglobal_score=results.global_score
details_json= JSON complet deresults(ou éventuellement deresults+ métriques brutes).
-
Réponse
- En cas de succès, renvoyer :
{ "status": "ok", "device_id": 1, "benchmark_id": 42 }
- En cas de succès, renvoyer :
4. Endpoints détaillés
4.1. POST /api/benchmark
- Rôle : point d’entré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é) :
{
"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 d’un device.
Réponse (exemple) :
{
"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 l’historique de benchmarks d’un device.
Paramètres :
limit(int, défaut 20)offset(int, défaut 0)
Réponse :
{
"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 d’un benchmark.
Réponse :
{
"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 :
[
{
"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 :
{
"label": "Support HP",
"url": "https://support.hp.com/..."
}
Réponse : l’objet créé avec id.
PUT /api/links/{id}
Body idem POST (label + url).
DELETE /api/links/{id}
- Réponse :
204 No Contenten cas de succès.
4.7. Documents
GET /api/devices/{device_id}/docs
Réponse :
[
{
"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 d’env
API_TOKENcôté backend. - Chaque requête
POST /api/benchmarkdoit inclure :
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
hardwareetresultsautant que possible sans être bloquant. - autoriser des champs optionnels (None / manquants).
- valider la structure de
- Codes d’erreur 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 l’origine (IP) des appels
POST /api/benchmark.