From bb5698fddae2333e017192bd9516e165ea025b21 Mon Sep 17 00:00:00 2001 From: GnomeZworc Date: Mon, 18 May 2026 22:49:01 +0200 Subject: [PATCH] f-28: add subnet default_route field Signed-off-by: GnomeZworc --- api/agent.yaml | 9 +++++ internal/api/agent/models.go | 32 +++++++++-------- internal/api/agent/subnet.go | 2 ++ internal/api/agent/subnets.go | 19 +++++++---- internal/dispatcher/agent/subnet_commands.go | 16 +++++---- .../dispatcher/agent/subnet_commands_test.go | 34 +++++++++++++++++++ internal/subnet/data.go | 25 +++++++++----- 7 files changed, 99 insertions(+), 38 deletions(-) diff --git a/api/agent.yaml b/api/agent.yaml index e3bf8b9..1476859 100644 --- a/api/agent.yaml +++ b/api/agent.yaml @@ -358,6 +358,12 @@ components: type: string description: Subnet CIDR block example: "10.10.10.0/24" + default_route: + type: boolean + description: > + If true, advertise a default route via DHCP. For vxlan mode the gateway is the interface IP. + For bridge mode the gateway is read from the host routing table. + default: false Subnet: type: object @@ -390,6 +396,9 @@ components: cidr: type: string example: "10.10.10.0/24" + default_route: + type: boolean + example: false VMCreateRequest: type: object diff --git a/internal/api/agent/models.go b/internal/api/agent/models.go index ffa994a..6535c4c 100644 --- a/internal/api/agent/models.go +++ b/internal/api/agent/models.go @@ -12,24 +12,26 @@ type VPC struct { } type SubnetCreateRequest struct { - Name string `json:"name"` - VPC string `json:"vpc"` - Mode string `json:"mode"` - VxlanID int `json:"vxlan_id"` - IfaceType string `json:"iface_type"` - InterfaceIP string `json:"interface_ip"` - CIDR string `json:"cidr"` + Name string `json:"name"` + VPC string `json:"vpc"` + Mode string `json:"mode"` + VxlanID int `json:"vxlan_id"` + IfaceType string `json:"iface_type"` + InterfaceIP string `json:"interface_ip"` + CIDR string `json:"cidr"` + DefaultRoute bool `json:"default_route"` } type Subnet struct { - Name string `json:"name"` - State string `json:"state"` - VPC string `json:"vpc"` - Mode string `json:"mode"` - VxlanID int `json:"vxlan_id"` - LocalIface string `json:"local_iface"` - InterfaceIP string `json:"interface_ip"` - CIDR string `json:"cidr"` + Name string `json:"name"` + State string `json:"state"` + VPC string `json:"vpc"` + Mode string `json:"mode"` + VxlanID int `json:"vxlan_id"` + LocalIface string `json:"local_iface"` + InterfaceIP string `json:"interface_ip"` + CIDR string `json:"cidr"` + DefaultRoute bool `json:"default_route"` } type VMInterface struct { diff --git a/internal/api/agent/subnet.go b/internal/api/agent/subnet.go index 95b0199..36ee9d7 100644 --- a/internal/api/agent/subnet.go +++ b/internal/api/agent/subnet.go @@ -57,6 +57,8 @@ func (s *Server) getSubnet(w http.ResponseWriter, _ *http.Request, name string) sub.InterfaceIP = value case "cidr": sub.CIDR = value + case "default_route": + sub.DefaultRoute = value == "true" } } w.WriteHeader(http.StatusOK) diff --git a/internal/api/agent/subnets.go b/internal/api/agent/subnets.go index a70e618..3cea247 100644 --- a/internal/api/agent/subnets.go +++ b/internal/api/agent/subnets.go @@ -54,6 +54,8 @@ func (s *Server) listSubnets(w http.ResponseWriter, _ *http.Request) { subnets[name].InterfaceIP = value case "cidr": subnets[name].CIDR = value + case "default_route": + subnets[name].DefaultRoute = value == "true" } } result := make([]Subnet, 0, len(subnets)) @@ -77,13 +79,14 @@ func (s *Server) postSubnet(w http.ResponseWriter, r *http.Request) { return } cmd := dispatcher.CreateSubnetCommand{ - Name: req.Name, - VPC: req.VPC, - Mode: req.Mode, - VxlanID: req.VxlanID, - IfaceType: req.IfaceType, - InterfaceIP: req.InterfaceIP, - CIDR: req.CIDR, + Name: req.Name, + VPC: req.VPC, + Mode: req.Mode, + VxlanID: req.VxlanID, + IfaceType: req.IfaceType, + InterfaceIP: req.InterfaceIP, + CIDR: req.CIDR, + DefaultRoute: req.DefaultRoute, } if err := s.dispatcher.Prepare(cmd); err != nil { if _, dbErr := kv.GetFromDB(s.db, "subnet/"+req.Name+"/state"); dbErr == nil { @@ -122,6 +125,8 @@ func (s *Server) postSubnet(w http.ResponseWriter, r *http.Request) { sub.InterfaceIP = value case "cidr": sub.CIDR = value + case "default_route": + sub.DefaultRoute = value == "true" } } w.WriteHeader(http.StatusAccepted) diff --git a/internal/dispatcher/agent/subnet_commands.go b/internal/dispatcher/agent/subnet_commands.go index db07c6c..e488842 100644 --- a/internal/dispatcher/agent/subnet_commands.go +++ b/internal/dispatcher/agent/subnet_commands.go @@ -12,13 +12,14 @@ import ( ) type CreateSubnetCommand struct { - Name string - VPC string - Mode string - VxlanID int - IfaceType string - InterfaceIP string - CIDR string + Name string + VPC string + Mode string + VxlanID int + IfaceType string + InterfaceIP string + CIDR string + DefaultRoute bool } func (c CreateSubnetCommand) Prepare(db *badger.DB, cfg *configuration.Config) error { @@ -48,6 +49,7 @@ func (c CreateSubnetCommand) Prepare(db *badger.DB, cfg *configuration.Config) e kv.AddInDB(db, "subnet/"+c.Name+"/local_iface", localIface) kv.AddInDB(db, "subnet/"+c.Name+"/interface_ip", c.InterfaceIP) kv.AddInDB(db, "subnet/"+c.Name+"/cidr", c.CIDR) + kv.AddInDB(db, "subnet/"+c.Name+"/default_route", strconv.FormatBool(c.DefaultRoute)) if c.Mode == "vxlan" { kv.AddInDB(db, "subnet/"+c.Name+"/vxlan_id", strconv.Itoa(c.VxlanID)) } diff --git a/internal/dispatcher/agent/subnet_commands_test.go b/internal/dispatcher/agent/subnet_commands_test.go index 94f2d4c..b6aacee 100644 --- a/internal/dispatcher/agent/subnet_commands_test.go +++ b/internal/dispatcher/agent/subnet_commands_test.go @@ -173,6 +173,40 @@ func TestCreateSubnetCommand_Prepare_UnknownMode(t *testing.T) { } } +func TestCreateSubnetCommand_Prepare_DefaultRouteStored(t *testing.T) { + _, db := newTestDispatcher(t) + kv.AddInDB(db, "vpc/vpc-1/state", "created") + cmd := CreateSubnetCommand{ + Name: "sn-1", VPC: "vpc-1", VxlanID: 100, + IfaceType: "vms", InterfaceIP: "10.0.0.1", CIDR: "10.0.0.0/24", + DefaultRoute: true, + } + if err := cmd.Prepare(db, testCfg()); err != nil { + t.Fatalf("Prepare a échoué : %v", err) + } + val, err := kv.GetFromDB(db, "subnet/sn-1/default_route") + if err != nil { + t.Fatalf("default_route non écrit en DB : %v", err) + } + if val != "true" { + t.Errorf("default_route attendu true, obtenu %q", val) + } +} + +func TestCreateSubnetCommand_Prepare_DefaultRouteFalseByDefault(t *testing.T) { + _, db := newTestDispatcher(t) + kv.AddInDB(db, "vpc/vpc-1/state", "created") + cmd := CreateSubnetCommand{ + Name: "sn-1", VPC: "vpc-1", VxlanID: 100, + IfaceType: "vms", InterfaceIP: "10.0.0.1", CIDR: "10.0.0.0/24", + } + cmd.Prepare(db, testCfg()) + val, _ := kv.GetFromDB(db, "subnet/sn-1/default_route") + if val != "false" { + t.Errorf("default_route attendu false, obtenu %q", val) + } +} + // --- DeleteSubnetCommand.Prepare --- func TestDeleteSubnetCommand_Prepare_Success(t *testing.T) { diff --git a/internal/subnet/data.go b/internal/subnet/data.go index fbc45ff..0f1f62a 100644 --- a/internal/subnet/data.go +++ b/internal/subnet/data.go @@ -11,15 +11,16 @@ import ( ) type subnetData struct { - vpc string - subnetID string - bridge string - mode string - vxlanID int - localIface string - interfaceIP net.IP - cidr *net.IPNet - vpcCIDR *net.IPNet + vpc string + subnetID string + bridge string + mode string + vxlanID int + localIface string + interfaceIP net.IP + cidr *net.IPNet + vpcCIDR *net.IPNet + defaultRoute bool } func loadSubnet(db *badger.DB, name string) (subnetData, error) { @@ -78,6 +79,12 @@ func loadSubnet(db *badger.DB, name string) (subnetData, error) { } d.cidr = ipNet + defaultRouteStr, err := kv.GetFromDB(db, "subnet/"+name+"/default_route") + if err != nil { + return d, fmt.Errorf("get default_route: %w", err) + } + d.defaultRoute = defaultRouteStr == "true" + vpcCIDRStr, err := kv.GetFromDB(db, "vpc/"+d.vpc+"/cidr") if err != nil { return d, fmt.Errorf("get vpc cidr: %w", err)