ajoute le self-healing des VM (sonde de santé, relance automatique, stock écarté) — bloc A, semaine 6
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
b054888f5a
commit
f2649d0f8b
21 changed files with 938 additions and 28 deletions
|
|
@ -2,9 +2,11 @@
|
|||
// vérifications déclarées dans le lab.yaml, par l'agent invité QEMU.
|
||||
//
|
||||
// Il écoute sur un socket Unix accessible au seul groupe www-data (le
|
||||
// portail) et n'accepte que trois identifiants : instance, lab,
|
||||
// vérification. La commande exécutée est toujours lue dans le lab.yaml,
|
||||
// jamais reçue du portail.
|
||||
// portail) et n'accepte que deux formes de requête :
|
||||
// - {instance, lab, verification} : la commande exécutée est toujours lue
|
||||
// dans le lab.yaml, jamais reçue du portail ;
|
||||
// - {instance, sante: true} (bloc A, self-healing) : état libvirt de la VM
|
||||
// et guest-ping de son agent, sans rien exécuter dans la VM.
|
||||
package main
|
||||
|
||||
import (
|
||||
|
|
@ -28,6 +30,8 @@ const (
|
|||
// README du pack de contenus : 10 s maximum par commande.
|
||||
delaiVerification = 10 * time.Second
|
||||
maxSimultanees = 8
|
||||
// Sonde de santé : dumpxml + domstate + guest-ping (3 s).
|
||||
delaiSonde = 8 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -39,11 +43,27 @@ type requete struct {
|
|||
Instance string `json:"instance"`
|
||||
Lab string `json:"lab"`
|
||||
Verification string `json:"verification"`
|
||||
Sante bool `json:"sante"`
|
||||
}
|
||||
|
||||
// valide : une sonde de santé ne porte ni lab ni vérification ; une
|
||||
// vérification porte les deux.
|
||||
func (r requete) valide() bool {
|
||||
if !reInstance.MatchString(r.Instance) {
|
||||
return false
|
||||
}
|
||||
if r.Sante {
|
||||
return r.Lab == "" && r.Verification == ""
|
||||
}
|
||||
return reIdent.MatchString(r.Lab) && reIdent.MatchString(r.Verification)
|
||||
}
|
||||
|
||||
type reponse struct {
|
||||
Reussi bool `json:"reussi"`
|
||||
Erreur string `json:"erreur,omitempty"`
|
||||
// Sonde de santé seulement : état libvirt (« running », « shut off »,
|
||||
// « paused », « crashed »…).
|
||||
Etat string `json:"etat,omitempty"`
|
||||
}
|
||||
|
||||
// Codes d'erreur renvoyés au portail (jamais la sortie de la commande).
|
||||
|
|
@ -123,18 +143,51 @@ func traiter(cfg config, conn *net.UnixConn) {
|
|||
err = dec.Decode(&req)
|
||||
}
|
||||
var rep reponse
|
||||
if err != nil || !reInstance.MatchString(req.Instance) || !reIdent.MatchString(req.Lab) || !reIdent.MatchString(req.Verification) {
|
||||
switch {
|
||||
case err != nil || !req.valide():
|
||||
rep = reponse{Erreur: errRequete}
|
||||
} else {
|
||||
case req.Sante:
|
||||
rep = sonder(cfg, req.Instance)
|
||||
default:
|
||||
rep = verifier(cfg, req)
|
||||
}
|
||||
|
||||
sortie, _ := json.Marshal(rep)
|
||||
_, _ = conn.Write(append(sortie, '\n'))
|
||||
if req.Sante {
|
||||
// Une sonde par VM et par minute : on ne journalise que les échecs.
|
||||
if !rep.Reussi {
|
||||
log.Printf("sante instance=%s etat=%q erreur=%q duree=%s", req.Instance, rep.Etat, rep.Erreur, time.Since(debut).Round(time.Millisecond))
|
||||
}
|
||||
return
|
||||
}
|
||||
log.Printf("instance=%s lab=%s verification=%s reussi=%t erreur=%q duree=%s",
|
||||
req.Instance, req.Lab, req.Verification, rep.Reussi, rep.Erreur, time.Since(debut).Round(time.Millisecond))
|
||||
}
|
||||
|
||||
// sonder : la VM appartient-elle au projet, dans quel état est-elle, et son
|
||||
// agent répond-il ? Reussi = agent qui répond.
|
||||
func sonder(cfg config, instance string) reponse {
|
||||
ctx, annuler := context.WithTimeout(context.Background(), delaiSonde)
|
||||
defer annuler()
|
||||
|
||||
dom, err := lireDomaine(ctx, instance)
|
||||
if err != nil || dom.projet() != cfg.projet {
|
||||
return reponse{Erreur: errMachine}
|
||||
}
|
||||
etat, err := etatDomaine(ctx, instance)
|
||||
if err != nil {
|
||||
return reponse{Erreur: errMachine}
|
||||
}
|
||||
if etat != "running" {
|
||||
return reponse{Etat: etat, Erreur: errAgent}
|
||||
}
|
||||
if err := pingAgent(ctx, instance); err != nil {
|
||||
return reponse{Etat: etat, Erreur: errAgent}
|
||||
}
|
||||
return reponse{Reussi: true, Etat: etat}
|
||||
}
|
||||
|
||||
func verifier(cfg config, req requete) reponse {
|
||||
lab, err := chargerLab(cfg.dossierLabs, req.Lab)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue