f-50: lab: scénarios E5, configuration de l'agent par hyperviseur, release 0.2.0rc003 #50

Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
GnomeZworc 2026-10-04 20:43:53 +02:00
commit b4660c95d3
Signed by: nicolas.boufideline
GPG key ID: 4406BBBF8845D632
25 changed files with 760 additions and 27 deletions

View file

@ -22,15 +22,15 @@ func (p *Plan) Write(w io.Writer) error {
var extras []NodePlan
for _, n := range p.Nodes {
if len(n.Secondary) > 0 || n.Loopback.IsValid() || n.FRR != "" || n.Release != "" {
if len(n.Secondary) > 0 || n.Loopback.IsValid() || n.FRR != "" || n.Release != "" || n.Agent != "" {
extras = append(extras, n)
}
}
if len(extras) > 0 {
fmt.Fprintf(tw, "\nroles\n")
fmt.Fprintf(tw, " name\tloopback\tsecondary\tfrr\trelease\n")
fmt.Fprintf(tw, " name\tloopback\tsecondary\tfrr\trelease\tagent\n")
for _, n := range extras {
fmt.Fprintf(tw, " %s\t%s\t%s\t%s\t%s\n", n.Name, orDash(loopback(n)), orDash(secondary(n)), orDash(filepath.Base(n.FRR)), orDash(n.Release))
fmt.Fprintf(tw, " %s\t%s\t%s\t%s\t%s\t%s\n", n.Name, orDash(loopback(n)), orDash(secondary(n)), orDash(filepath.Base(n.FRR)), orDash(n.Release), orDash(filepath.Base(n.Agent)))
}
}

View file

@ -44,6 +44,7 @@ type NodePlan struct {
Loopback netip.Prefix
FRR string
Release string
Agent string
}
type Cable struct {
@ -78,6 +79,7 @@ func Compute(t *Topology) (*Plan, error) {
SSHPort: SSHBasePort + i,
FRR: n.FRR,
Release: n.Release,
Agent: n.Agent,
}
for segment, raws := range n.Secondary {
for _, raw := range raws {

View file

@ -315,9 +315,9 @@ nodes
hv2 hypervisor deb 4 16384 MiB 127.0.0.1:2203
roles
name loopback secondary frr release
hv1 - - - 0.2.0rc002
hv2 - - - 0.2.0rc002
name loopback secondary frr release agent
hv1 - - - 0.2.0rc002 -
hv2 - - - 0.2.0rc002 -
segment underlay: 10.250.0.0/24, mtu 9000, switch sw1, bridge br-underlay, gateway 10.250.0.1
node interface address mac udp switch port mac udp

View file

@ -142,10 +142,10 @@ func TestWrite_ShowsTheRoles(t *testing.T) {
}
want := `
roles
name loopback secondary frr release
sw1 - underlay 169.254.0.1/28 sw1.conf -
rr1 lo1 10.255.255.1/32 underlay 169.254.0.3/28 rr1.conf -
hv1 - - hv1.conf 0.2.0rc002
name loopback secondary frr release agent
sw1 - underlay 169.254.0.1/28 sw1.conf - -
rr1 lo1 10.255.255.1/32 underlay 169.254.0.3/28 rr1.conf - -
hv1 - - hv1.conf 0.2.0rc002 -
`
if !bytes.Contains(buf.Bytes(), []byte(want)) {
t.Errorf("plan:\n%s\ndoes not contain:\n%s", buf.String(), want)
@ -214,3 +214,52 @@ func TestCompute_CarriesTheRelease(t *testing.T) {
t.Errorf("rr1 release = %q", got)
}
}
func TestValidate_AgentOnlyForHypervisors(t *testing.T) {
doc := header + `
segments:
red: { switch: sw, cidr: 10.1.0.0/24 }
nodes:
sw: { role: switch, image: deb, cpus: 1, memory: 512 }
rr: { role: rr, image: deb, cpus: 1, memory: 512, segments: [red], agent: agent.yml }
hv: { role: hypervisor, image: deb, cpus: 1, memory: 512, segments: [red], release: 0.2.0rc003, agent: agent.yml }
`
msg := validationError(t, doc)
requireContains(t, msg, "node rr: agent is only for hypervisors")
if bytes.Contains([]byte(msg), []byte("node hv: agent")) {
t.Errorf("a hypervisor agent was refused:\n%s", msg)
}
}
func TestLoad_ResolvesTheAgentPathAgainstTheTopologyFile(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "lab.yml")
doc := header + `
segments:
red: { switch: sw, cidr: 10.1.0.0/24 }
nodes:
sw: { role: switch, image: deb, cpus: 1, memory: 512 }
hv1: { role: hypervisor, image: deb, cpus: 1, memory: 512, segments: [red], release: 0.2.0rc003, agent: agent/two.yml }
hv2: { role: hypervisor, image: deb, cpus: 1, memory: 512, segments: [red], release: 0.2.0rc003, agent: /etc/lab/agent.yml }
`
if err := os.WriteFile(path, []byte(doc), 0o600); err != nil {
t.Fatal(err)
}
topo, err := Load(path)
if err != nil {
t.Fatalf("Load: %v", err)
}
want := map[string]string{"sw": "", "hv1": filepath.Join(dir, "agent", "two.yml"), "hv2": "/etc/lab/agent.yml"}
for _, n := range topo.Nodes {
if n.Agent != want[n.Name] {
t.Errorf("%s agent = %q, want %q", n.Name, n.Agent, want[n.Name])
}
}
p, err := Compute(topo)
if err != nil {
t.Fatal(err)
}
if got := nodeOf(t, p, "hv1").Agent; got != filepath.Join(dir, "agent", "two.yml") {
t.Errorf("plan hv1 agent = %q", got)
}
}

View file

@ -50,6 +50,7 @@ type Node struct {
Loopback string
FRR string
Release string
Agent string
}
type fileImage struct {
@ -74,6 +75,7 @@ type fileNode struct {
Loopback string `yaml:"loopback"`
FRR string `yaml:"frr"`
Release string `yaml:"release"`
Agent string `yaml:"agent"`
}
type file struct {
@ -96,6 +98,9 @@ func Load(path string) (*Topology, error) {
if n.FRR != "" && !filepath.IsAbs(n.FRR) {
t.Nodes[i].FRR = filepath.Join(filepath.Dir(path), n.FRR)
}
if n.Agent != "" && !filepath.IsAbs(n.Agent) {
t.Nodes[i].Agent = filepath.Join(filepath.Dir(path), n.Agent)
}
}
return t, nil
}
@ -144,6 +149,7 @@ func Parse(data []byte) (*Topology, error) {
Loopback: n.Loopback,
FRR: n.FRR,
Release: n.Release,
Agent: n.Agent,
})
}
return t, nil

View file

@ -215,6 +215,9 @@ func validateExtras(n Node, segments map[string]Segment, add func(string, ...any
case n.Role != RoleHypervisor && n.Release != "":
add("node %s: release is only for hypervisors", n.Name)
}
if n.Role != RoleHypervisor && n.Agent != "" {
add("node %s: agent is only for hypervisors", n.Name)
}
if n.Loopback != "" {
prefix, err := netip.ParsePrefix(n.Loopback)
switch {