Réplication de nœuds : réseau et DNS par l'API, multipath, chrony, sysctl par SSH
Deuxième mode de réplication, séparé de la réplication cluster vers cluster
(sélecteur « Clusters (API) | Nœuds (fichiers) » en haut de page) : un profil
réplique la configuration système d'un nœud maître vers des nœuds cibles.
- réseau par /nodes/{node}/network (interfaces.new puis rechargement), adresses
et passerelles propres à chaque nœud, mapping des noms d'interfaces par cible ;
DNS par /nodes/{node}/dns ; multipath.conf, wwids (ajout seul), chrony,
sysctl.d, interfaces.d (hors sdn) par SSH
- découverte des nœuds (/cluster/status), accès SSH par nœud (mot de passe du
cluster ou propre), clé d'hôte épinglée, contrôle du nom du nœud
- dry-run avec diff des fichiers ; apply (admin) avec contrôle SHA-256,
sauvegarde et restore.sh sur le nœud, écriture atomique, rechargements,
restauration si un rechargement échoue, timer systemd de restauration si le
nœud est injoignable après le rechargement du réseau, confirmation tapée
- vue Nœuds et profils, journal séparé par mode, démo avec nœuds simulés
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
dc1a7ff40e
commit
1e667ce8b9
24 files changed
+4973
-65
No files matched your search
@@ -64,6 +64,7 @@ type Server struct {
|
||||
Shadow map[string]string // user without realm -> hash (priv/shadow.cfg)
|
||||
FailOn string // "METHOD path" answering 500, to test rollbacks
|
||||
Calls []string // "METHOD path" of every write, "SSH write" for shadow.cfg
|
||||
Nodes []*Node // nodes of the node replication (AddNode); none: one node "n1"
|
||||
locks int
|
||||
ssh *sshServer
|
||||
}
|
||||
@@ -83,6 +84,9 @@ func New(version string) *Server {
|
||||
// Close stops the API and the SSH server.
|
||||
func (s *Server) Close() {
|
||||
s.ssh.close()
|
||||
for _, n := range s.Nodes {
|
||||
n.ssh.close()
|
||||
}
|
||||
s.Server.Close()
|
||||
}
|
||||
|
||||
@@ -146,14 +150,26 @@ func (s *Server) handle(w http.ResponseWriter, r *http.Request) {
|
||||
if running {
|
||||
cfg = &s.Running
|
||||
}
|
||||
var nd *Node
|
||||
if len(seg) >= 3 && seg[0] == "nodes" {
|
||||
nd = s.node(seg[1])
|
||||
}
|
||||
switch {
|
||||
case nd != nil && r.Method == "GET" && len(seg) == 3 && seg[2] == "version":
|
||||
reply(w, map[string]any{"version": nd.version(), "release": release(nd.version()), "repoid": "abcdef"})
|
||||
case nd != nil && (seg[2] == "network" || seg[2] == "dns"):
|
||||
s.nodeAPI(w, r, nd, seg)
|
||||
case key == "GET version" || (r.Method == "GET" && len(seg) == 3 && seg[0] == "nodes" && seg[2] == "version"):
|
||||
reply(w, map[string]any{"version": s.Version, "release": release(s.Version), "repoid": "abcdef"})
|
||||
case key == "GET cluster/status":
|
||||
reply(w, []any{
|
||||
map[string]any{"type": "cluster", "name": "fake", "quorate": 1},
|
||||
map[string]any{"type": "node", "name": "n1", "online": 1},
|
||||
})
|
||||
st := []any{map[string]any{"type": "cluster", "name": "fake", "quorate": 1}}
|
||||
if len(s.Nodes) == 0 {
|
||||
st = append(st, map[string]any{"type": "node", "name": "n1", "online": 1})
|
||||
}
|
||||
for i, n := range s.Nodes {
|
||||
st = append(st, map[string]any{"type": "node", "name": n.Name, "online": 1, "nodeid": i + 1, "ip": "127.0.0.1"})
|
||||
}
|
||||
reply(w, st)
|
||||
case r.Method == "GET" && len(seg) == 4 && seg[0] == "nodes" && seg[2] == "sdn" && seg[3] == "zones":
|
||||
var out []any
|
||||
for id := range s.Running.Zones {
|
||||
|
||||
Reference in new issue
Block a user