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:
cedricandClaude Opus 5.5 committed 2026-10-23 20:29:55 +02:00
1 parent dc1a7ff40e
commit 1e667ce8b9
24 files changed
+4973 -65

No files matched your search

+148
View File
@@ -0,0 +1,148 @@
package nodesync
import (
"strconv"
"strings"
)
// Line is one line of a unified diff: Op is ' ' (context), '-' (target only)
// or '+' (written by the replication). A hunk starts with Op '@' and Text
// "@@ -a,b +c,d @@".
type Line struct {
Op string `json:"op"`
Text string `json:"text"`
}
const diffContext = 3
// maxCells bounds the LCS table; beyond, the diff shows every line replaced.
const maxCells = 4_000_000
// Diff compares the current content of a file with the content to write.
func Diff(oldText, newText []byte) []Line {
a, b := lines(oldText), lines(newText)
ops := editScript(a, b)
// group the changes into hunks with their context
var out []Line
n := len(ops)
i := 0
for i < n {
if ops[i].Op == " " {
i++
continue
}
start := i - diffContext
if start < 0 {
start = 0
}
end := i
for end < n {
if ops[end].Op != " " {
end++
continue
}
// a run of context: the hunk ends when it is longer than 2*context
j := end
for j < n && ops[j].Op == " " {
j++
}
if j == n || j-end > 2*diffContext {
end += diffContext
if end > n {
end = n
}
break
}
end = j
}
oa, ob := ops[start].A, ops[start].B
ca, cb := 0, 0
for _, o := range ops[start:end] {
if o.Op != "+" {
ca++
}
if o.Op != "-" {
cb++
}
}
out = append(out, Line{Op: "@", Text: hunkHeader(oa, ca, ob, cb)})
for _, o := range ops[start:end] {
out = append(out, Line{Op: o.Op, Text: o.Text})
}
i = end
}
return out
}
type editOp struct {
Op string
Text string
A, B int // line numbers (1-based) where this op starts in a and b
}
func editScript(a, b []string) []editOp {
var ops []editOp
if len(a)*len(b) > maxCells {
for i, l := range a {
ops = append(ops, editOp{"-", l, i + 1, 1})
}
for i, l := range b {
ops = append(ops, editOp{"+", l, len(a) + 1, i + 1})
}
return ops
}
// lcs[i][j]: length of the LCS of a[i:] and b[j:]
lcs := make([][]int, len(a)+1)
for i := range lcs {
lcs[i] = make([]int, len(b)+1)
}
for i := len(a) - 1; i >= 0; i-- {
for j := len(b) - 1; j >= 0; j-- {
if a[i] == b[j] {
lcs[i][j] = lcs[i+1][j+1] + 1
} else if lcs[i+1][j] >= lcs[i][j+1] {
lcs[i][j] = lcs[i+1][j]
} else {
lcs[i][j] = lcs[i][j+1]
}
}
}
i, j := 0, 0
for i < len(a) || j < len(b) {
switch {
case i < len(a) && j < len(b) && a[i] == b[j]:
ops = append(ops, editOp{" ", a[i], i + 1, j + 1})
i++
j++
case j < len(b) && (i == len(a) || lcs[i][j+1] > lcs[i+1][j]):
ops = append(ops, editOp{"+", b[j], i + 1, j + 1})
j++
default:
ops = append(ops, editOp{"-", a[i], i + 1, j + 1})
i++
}
}
return ops
}
func lines(b []byte) []string {
s := string(b)
if s == "" {
return nil
}
s = strings.TrimSuffix(s, "\n")
return strings.Split(s, "\n")
}
func hunkHeader(a, ca, b, cb int) string {
r := func(start, n int) string {
if n == 0 {
start--
}
if n == 1 {
return strconv.Itoa(start)
}
return strconv.Itoa(start) + "," + strconv.Itoa(n)
}
return "@@ -" + r(a, ca) + " +" + r(b, cb) + " @@"
}