diff --git a/api/agent.yaml b/api/agent.yaml index e75601d..95a0339 100644 --- a/api/agent.yaml +++ b/api/agent.yaml @@ -294,17 +294,13 @@ components: VPCCreateRequest: type: object - required: [name, cidr] + required: [name] properties: name: type: string description: Unique name for the VPC, must follow the format vp-[id] pattern: '^vp-.+' example: vp-00001 - cidr: - type: string - description: CIDR block for the entire VPC address space - example: "10.0.0.0/16" VPC: type: object @@ -316,13 +312,10 @@ components: type: string enum: [creating, created, deleting, deleted] example: created - cidr: - type: string - example: "10.0.0.0/16" SubnetCreateRequest: type: object - required: [name, vpc, interface_ip, cidr] + required: [name, vpc, gateway_ip, cidr] properties: name: type: string @@ -349,7 +342,7 @@ components: type: string description: Interface type key defined in the agent config (e.g. vms, internet, admin). Falls back to default_interface if omitted or unknown. example: vms - interface_ip: + gateway_ip: type: string format: ipv4 description: Gateway IP for the subnet @@ -358,12 +351,6 @@ 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,23 +377,23 @@ components: type: string description: Resolved interface name from agent config example: br-000000 - interface_ip: + gateway_ip: type: string example: "10.10.10.1" cidr: type: string example: "10.10.10.0/24" - default_route: - type: boolean - example: false VMCreateRequest: type: object - required: [name, interfaces, storage] + required: [name, metadata_port, interfaces, storage] properties: name: type: string example: vm-00001 + metadata_port: + type: string + example: "80" memory: type: integer description: Memory in MB (default 512) diff --git a/internal/api/agent/models.go b/internal/api/agent/models.go index dc0c47f..9622ef9 100644 --- a/internal/api/agent/models.go +++ b/internal/api/agent/models.go @@ -2,36 +2,32 @@ package agentapi type VPCCreateRequest struct { Name string `json:"name"` - CIDR string `json:"cidr"` } type VPC struct { Name string `json:"name"` State string `json:"state"` - CIDR string `json:"cidr"` } 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"` - DefaultRoute bool `json:"default_route"` + Name string `json:"name"` + VPC string `json:"vpc"` + Mode string `json:"mode"` + VxlanID int `json:"vxlan_id"` + IfaceType string `json:"iface_type"` + GatewayIP string `json:"gateway_ip"` + CIDR string `json:"cidr"` } 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"` - DefaultRoute bool `json:"default_route"` + 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"` + GatewayIP string `json:"gateway_ip"` + CIDR string `json:"cidr"` } type VMInterface struct { @@ -46,13 +42,14 @@ type VMStorage struct { } type VMCreateRequest struct { - Name string `json:"name"` - Memory int `json:"memory"` - CPUs int `json:"cpus"` - Password string `json:"password"` - SSHKey string `json:"sshkey"` - Interfaces []VMInterface `json:"interfaces"` - Storage []VMStorage `json:"storage"` + Name string `json:"name"` + MetadataPort string `json:"metadata_port"` + Memory int `json:"memory"` + CPUs int `json:"cpus"` + Password string `json:"password"` + SSHKey string `json:"sshkey"` + Interfaces []VMInterface `json:"interfaces"` + Storage []VMStorage `json:"storage"` } type VM struct { diff --git a/internal/api/agent/subnet.go b/internal/api/agent/subnet.go index 36ee9d7..21bebf8 100644 --- a/internal/api/agent/subnet.go +++ b/internal/api/agent/subnet.go @@ -53,12 +53,10 @@ func (s *Server) getSubnet(w http.ResponseWriter, _ *http.Request, name string) sub.VxlanID, _ = strconv.Atoi(value) case "local_iface": sub.LocalIface = value - case "interface_ip": - sub.InterfaceIP = value + case "gateway_ip": + sub.GatewayIP = value case "cidr": sub.CIDR = value - case "default_route": - sub.DefaultRoute = value == "true" } } w.WriteHeader(http.StatusOK) diff --git a/internal/api/agent/subnet_test.go b/internal/api/agent/subnet_test.go index 36e42f7..23745a0 100644 --- a/internal/api/agent/subnet_test.go +++ b/internal/api/agent/subnet_test.go @@ -60,7 +60,7 @@ func TestPostSubnet_Created(t *testing.T) { Name: "sn-new", VPC: "vpc-1", IfaceType: "vms", - InterfaceIP: "10.0.0.1", + GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", } body, _ := json.Marshal(req) @@ -95,7 +95,7 @@ func TestPostSubnet_IfaceTypeOptional(t *testing.T) { req := SubnetCreateRequest{ Name: "sn-opt", VPC: "vpc-1", - InterfaceIP: "10.0.0.1", + GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", // IfaceType omis — doit utiliser default_interface } @@ -113,7 +113,7 @@ func TestPostSubnet_VPCNotFound(t *testing.T) { Name: "sn-1", VPC: "vpc-inexistant", IfaceType: "vms", - InterfaceIP: "10.0.0.1", + GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", } body, _ := json.Marshal(req) @@ -132,7 +132,7 @@ func TestPostSubnet_Duplicate(t *testing.T) { Name: "sn-exist", VPC: "vpc-1", IfaceType: "vms", - InterfaceIP: "10.0.0.1", + GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", } body, _ := json.Marshal(req) @@ -150,52 +150,7 @@ func TestPostSubnet_VPCDeleting(t *testing.T) { Name: "sn-1", VPC: "vpc-dying", IfaceType: "vms", - InterfaceIP: "10.0.0.1", - CIDR: "10.0.0.0/24", - } - body, _ := json.Marshal(req) - w := httptest.NewRecorder() - s.SubnetsHandler(w, httptest.NewRequest(http.MethodPost, "/subnets", bytes.NewReader(body))) - if w.Code != http.StatusUnprocessableEntity { - t.Errorf("attendu 422, obtenu %d", w.Code) - } -} - -func TestPostSubnet_BridgeMode_Success(t *testing.T) { - s, db := newTestServer(t) - kv.AddInDB(db, "vpc/vpc-1/state", "created") - req := SubnetCreateRequest{ - Name: "sn-br", - VPC: "vpc-1", - Mode: "bridge", - IfaceType: "vms", - InterfaceIP: "10.0.0.1", - CIDR: "10.0.0.0/24", - } - body, _ := json.Marshal(req) - w := httptest.NewRecorder() - s.SubnetsHandler(w, httptest.NewRequest(http.MethodPost, "/subnets", bytes.NewReader(body))) - if w.Code != http.StatusAccepted { - t.Fatalf("attendu 202, obtenu %d: %s", w.Code, w.Body.String()) - } - var result Subnet - json.NewDecoder(w.Body).Decode(&result) - if result.Mode != "bridge" { - t.Errorf("mode attendu bridge, obtenu %q", result.Mode) - } - if result.VxlanID != 0 { - t.Errorf("vxlan_id devrait être 0 en mode bridge, obtenu %d", result.VxlanID) - } -} - -func TestPostSubnet_UnknownMode(t *testing.T) { - s, db := newTestServer(t) - kv.AddInDB(db, "vpc/vpc-1/state", "created") - req := SubnetCreateRequest{ - Name: "sn-1", - VPC: "vpc-1", - Mode: "vlan", - InterfaceIP: "10.0.0.1", + GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", } body, _ := json.Marshal(req) @@ -222,7 +177,7 @@ func TestGetSubnet_Found(t *testing.T) { kv.AddInDB(db, "subnet/sn-1/state", "created") kv.AddInDB(db, "subnet/sn-1/vpc", "vpc-1") kv.AddInDB(db, "subnet/sn-1/cidr", "10.0.0.0/24") - kv.AddInDB(db, "subnet/sn-1/interface_ip", "10.0.0.1") + kv.AddInDB(db, "subnet/sn-1/gateway_ip", "10.0.0.1") req := httptest.NewRequest(http.MethodGet, "/subnets/sn-1", nil) w := httptest.NewRecorder() s.SubnetByNameHandler(w, req) diff --git a/internal/api/agent/subnets.go b/internal/api/agent/subnets.go index 3cea247..61082f1 100644 --- a/internal/api/agent/subnets.go +++ b/internal/api/agent/subnets.go @@ -50,12 +50,10 @@ func (s *Server) listSubnets(w http.ResponseWriter, _ *http.Request) { subnets[name].VxlanID, _ = strconv.Atoi(value) case "local_iface": subnets[name].LocalIface = value - case "interface_ip": - subnets[name].InterfaceIP = value + case "gateway_ip": + subnets[name].GatewayIP = value case "cidr": subnets[name].CIDR = value - case "default_route": - subnets[name].DefaultRoute = value == "true" } } result := make([]Subnet, 0, len(subnets)) @@ -73,20 +71,19 @@ func (s *Server) postSubnet(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(ErrorResponse{Error: "invalid request body"}) return } - if req.Name == "" || req.VPC == "" || req.InterfaceIP == "" || req.CIDR == "" { + if req.Name == "" || req.VPC == "" || req.GatewayIP == "" || req.CIDR == "" { w.WriteHeader(http.StatusBadRequest) - json.NewEncoder(w).Encode(ErrorResponse{Error: "name, vpc, interface_ip and cidr are required"}) + json.NewEncoder(w).Encode(ErrorResponse{Error: "name, vpc, gateway_ip and cidr are required"}) 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, - DefaultRoute: req.DefaultRoute, + Name: req.Name, + VPC: req.VPC, + Mode: req.Mode, + VxlanID: req.VxlanID, + IfaceType: req.IfaceType, + GatewayIP: req.GatewayIP, + CIDR: req.CIDR, } if err := s.dispatcher.Prepare(cmd); err != nil { if _, dbErr := kv.GetFromDB(s.db, "subnet/"+req.Name+"/state"); dbErr == nil { @@ -121,12 +118,10 @@ func (s *Server) postSubnet(w http.ResponseWriter, r *http.Request) { sub.VxlanID, _ = strconv.Atoi(value) case "local_iface": sub.LocalIface = value - case "interface_ip": - sub.InterfaceIP = value + case "gateway_ip": + sub.GatewayIP = value case "cidr": sub.CIDR = value - case "default_route": - sub.DefaultRoute = value == "true" } } w.WriteHeader(http.StatusAccepted) diff --git a/internal/api/agent/vms.go b/internal/api/agent/vms.go index 95f2c85..841c429 100644 --- a/internal/api/agent/vms.go +++ b/internal/api/agent/vms.go @@ -58,9 +58,9 @@ func (s *Server) startVM(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(ErrorResponse{Error: "invalid request body"}) return } - if req.Name == "" || len(req.Interfaces) == 0 || len(req.Storage) == 0 { + if req.Name == "" || req.MetadataPort == "" || len(req.Interfaces) == 0 || len(req.Storage) == 0 { w.WriteHeader(http.StatusBadRequest) - json.NewEncoder(w).Encode(ErrorResponse{Error: "name, interfaces and storage are required"}) + json.NewEncoder(w).Encode(ErrorResponse{Error: "name, metadata_port, interfaces and storage are required"}) return } @@ -78,14 +78,15 @@ func (s *Server) startVM(w http.ResponseWriter, r *http.Request) { } cmd := dispatcher.StartVMCommand{ - Name: req.Name, - Subnet: primary.Subnet, - IP: primary.IP, - VolumePath: req.Storage[0].Path, - Memory: req.Memory, - CPUs: req.CPUs, - Password: req.Password, - SSHKey: req.SSHKey, + Name: req.Name, + Subnet: primary.Subnet, + IP: primary.IP, + MetadataPort: req.MetadataPort, + VolumePath: req.Storage[0].Path, + Memory: req.Memory, + CPUs: req.CPUs, + Password: req.Password, + SSHKey: req.SSHKey, } if err := s.dispatcher.Prepare(cmd); err != nil { diff --git a/internal/api/agent/vpc.go b/internal/api/agent/vpc.go index bb3f5d7..43cc33c 100644 --- a/internal/api/agent/vpc.go +++ b/internal/api/agent/vpc.go @@ -35,9 +35,8 @@ func (s *Server) getVpc(w http.ResponseWriter, _ *http.Request, name string) { json.NewEncoder(w).Encode(ErrorResponse{Error: "vpc not found"}) return } - cidr, _ := kv.GetFromDB(s.db, "vpc/"+name+"/cidr") w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(VPC{Name: name, State: state, CIDR: cidr}) + json.NewEncoder(w).Encode(VPC{Name: name, State: state}) } func (s *Server) deleteVpc(w http.ResponseWriter, _ *http.Request, name string) { diff --git a/internal/api/agent/vpc_test.go b/internal/api/agent/vpc_test.go index 3516305..01308a5 100644 --- a/internal/api/agent/vpc_test.go +++ b/internal/api/agent/vpc_test.go @@ -53,7 +53,7 @@ func TestListVpcs_InvalidMethod(t *testing.T) { func TestPostVpc_Created(t *testing.T) { s, _ := newTestServer(t) - body, _ := json.Marshal(VPCCreateRequest{Name: "vpc-new", CIDR: "10.0.0.0/16"}) + body, _ := json.Marshal(VPCCreateRequest{Name: "vpc-new"}) w := httptest.NewRecorder() s.VpcsHandler(w, httptest.NewRequest(http.MethodPost, "/vpcs", bytes.NewReader(body))) if w.Code != http.StatusAccepted { @@ -67,34 +67,11 @@ func TestPostVpc_Created(t *testing.T) { if result.State != "creating" { t.Errorf("state attendu creating, obtenu %q", result.State) } - if result.CIDR != "10.0.0.0/16" { - t.Errorf("cidr attendu 10.0.0.0/16, obtenu %q", result.CIDR) - } } func TestPostVpc_MissingName(t *testing.T) { s, _ := newTestServer(t) - body, _ := json.Marshal(VPCCreateRequest{CIDR: "10.0.0.0/16"}) - w := httptest.NewRecorder() - s.VpcsHandler(w, httptest.NewRequest(http.MethodPost, "/vpcs", bytes.NewReader(body))) - if w.Code != http.StatusBadRequest { - t.Errorf("attendu 400, obtenu %d", w.Code) - } -} - -func TestPostVpc_MissingCIDR(t *testing.T) { - s, _ := newTestServer(t) - body, _ := json.Marshal(VPCCreateRequest{Name: "vpc-new"}) - w := httptest.NewRecorder() - s.VpcsHandler(w, httptest.NewRequest(http.MethodPost, "/vpcs", bytes.NewReader(body))) - if w.Code != http.StatusBadRequest { - t.Errorf("attendu 400, obtenu %d", w.Code) - } -} - -func TestPostVpc_InvalidCIDR(t *testing.T) { - s, _ := newTestServer(t) - body, _ := json.Marshal(VPCCreateRequest{Name: "vpc-new", CIDR: "not-a-cidr"}) + body, _ := json.Marshal(VPCCreateRequest{}) w := httptest.NewRecorder() s.VpcsHandler(w, httptest.NewRequest(http.MethodPost, "/vpcs", bytes.NewReader(body))) if w.Code != http.StatusBadRequest { @@ -105,7 +82,7 @@ func TestPostVpc_InvalidCIDR(t *testing.T) { func TestPostVpc_Duplicate(t *testing.T) { s, db := newTestServer(t) kv.AddInDB(db, "vpc/vpc-exist/state", "created") - body, _ := json.Marshal(VPCCreateRequest{Name: "vpc-exist", CIDR: "10.0.0.0/16"}) + body, _ := json.Marshal(VPCCreateRequest{Name: "vpc-exist"}) w := httptest.NewRecorder() s.VpcsHandler(w, httptest.NewRequest(http.MethodPost, "/vpcs", bytes.NewReader(body))) if w.Code != http.StatusConflict { diff --git a/internal/api/agent/vpcs.go b/internal/api/agent/vpcs.go index 905cdee..b062d1c 100644 --- a/internal/api/agent/vpcs.go +++ b/internal/api/agent/vpcs.go @@ -2,7 +2,6 @@ package agentapi import ( "encoding/json" - "net" "net/http" "strings" @@ -39,11 +38,8 @@ func (s *Server) listVpcs(w http.ResponseWriter, _ *http.Request) { if _, ok := vpcs[name]; !ok { vpcs[name] = &VPC{Name: name} } - switch parts[2] { - case "state": + if parts[2] == "state" { vpcs[name].State = value - case "cidr": - vpcs[name].CIDR = value } } result := make([]VPC, 0, len(vpcs)) @@ -66,17 +62,7 @@ func (s *Server) postVpc(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(ErrorResponse{Error: "name is required"}) return } - if req.CIDR == "" { - w.WriteHeader(http.StatusBadRequest) - json.NewEncoder(w).Encode(ErrorResponse{Error: "cidr is required"}) - return - } - if _, _, err := net.ParseCIDR(req.CIDR); err != nil { - w.WriteHeader(http.StatusBadRequest) - json.NewEncoder(w).Encode(ErrorResponse{Error: "invalid cidr"}) - return - } - cmd := dispatcher.CreateVPCCommand{Name: req.Name, CIDR: req.CIDR} + cmd := dispatcher.CreateVPCCommand{Name: req.Name} if err := s.dispatcher.Prepare(cmd); err != nil { w.WriteHeader(http.StatusConflict) json.NewEncoder(w).Encode(ErrorResponse{Error: err.Error()}) @@ -90,5 +76,5 @@ func (s *Server) postVpc(w http.ResponseWriter, r *http.Request) { return } w.WriteHeader(http.StatusAccepted) - json.NewEncoder(w).Encode(VPC{Name: req.Name, State: state, CIDR: req.CIDR}) + json.NewEncoder(w).Encode(VPC{Name: req.Name, State: state}) } diff --git a/internal/dhcp/dhcp_test.go b/internal/dhcp/dhcp_test.go index a2d5e60..1681fec 100644 --- a/internal/dhcp/dhcp_test.go +++ b/internal/dhcp/dhcp_test.go @@ -51,15 +51,11 @@ func TestIncrementIP_Carry(t *testing.T) { func newConf(t *testing.T, cidr string) Config { t.Helper() _, network, _ := net.ParseCIDR(cidr) - _, vpcNet, _ := net.ParseCIDR("10.0.0.0/16") - gw := net.ParseIP("192.168.1.1").To4() return Config{ - Network: network, - VPCGateway: gw, - VPCRoute: vpcNet, - DefaultGateway: gw, - Name: "test", - ConfDir: t.TempDir(), + Network: network, + Gateway: net.ParseIP("192.168.1.1").To4(), + Name: "test", + ConfDir: t.TempDir(), } } @@ -88,45 +84,13 @@ func TestGenerateConfig_FilenameMatchesName(t *testing.T) { } } -func TestGenerateConfig_ContainsDefaultGateway(t *testing.T) { +func TestGenerateConfig_ContainsGateway(t *testing.T) { conf := newConf(t, "192.168.1.0/29") path, _, _ := GenerateConfig(conf) content, _ := os.ReadFile(path) if !strings.Contains(string(content), "dhcp-option=3,192.168.1.1") { - t.Errorf("dhcp-option=3 absente du fichier généré :\n%s", content) - } -} - -func TestGenerateConfig_NoDefaultGateway(t *testing.T) { - conf := newConf(t, "192.168.1.0/29") - conf.DefaultGateway = nil - path, _, _ := GenerateConfig(conf) - content, _ := os.ReadFile(path) - - if strings.Contains(string(content), "dhcp-option=3,") { - t.Errorf("dhcp-option=3 présente alors que DefaultGateway=nil :\n%s", content) - } -} - -func TestGenerateConfig_ContainsVPCRoute(t *testing.T) { - conf := newConf(t, "192.168.1.0/29") - path, _, _ := GenerateConfig(conf) - content, _ := os.ReadFile(path) - - if !strings.Contains(string(content), "dhcp-option=121,10.0.0.0/16,192.168.1.1") { - t.Errorf("dhcp-option=121 absente ou incorrecte :\n%s", content) - } -} - -func TestGenerateConfig_NoVPCRoute(t *testing.T) { - conf := newConf(t, "192.168.1.0/29") - conf.VPCRoute = nil - path, _, _ := GenerateConfig(conf) - content, _ := os.ReadFile(path) - - if strings.Contains(string(content), "dhcp-option=121,") { - t.Errorf("dhcp-option=121 présente alors que VPCRoute=nil :\n%s", content) + t.Errorf("gateway absente du fichier généré :\n%s", content) } } @@ -134,6 +98,7 @@ func TestGenerateConfig_ContainsDhcpRange(t *testing.T) { _, network, _ := net.ParseCIDR("10.10.0.0/24") conf := Config{ Network: network, + Gateway: net.ParseIP("10.10.0.1").To4(), Name: "vpc1", ConfDir: t.TempDir(), } @@ -179,6 +144,7 @@ func TestGenerateConfig_CreatesConfDir(t *testing.T) { _, network, _ := net.ParseCIDR("10.0.0.0/30") conf := Config{ Network: network, + Gateway: net.ParseIP("10.0.0.1").To4(), Name: "net", ConfDir: dir, } diff --git a/internal/dhcp/generate.go b/internal/dhcp/generate.go index 2b6c72f..3c46fe2 100644 --- a/internal/dhcp/generate.go +++ b/internal/dhcp/generate.go @@ -14,12 +14,7 @@ func GenerateConfig(c Config) (string, map[string]string, error) { var sb strings.Builder fmt.Fprintf(&sb, "no-resolv\n") fmt.Fprintf(&sb, "dhcp-range=%s,static,%s,12h\n", c.Network.IP.String(), mask) - if c.VPCRoute != nil { - fmt.Fprintf(&sb, "dhcp-option=121,%s,%s\n", c.VPCRoute.String(), c.VPCGateway.String()) - } - if c.DefaultGateway != nil { - fmt.Fprintf(&sb, "dhcp-option=3,%s\n", c.DefaultGateway.String()) - } + fmt.Fprintf(&sb, "dhcp-option=3,%s\n", c.Gateway.String()) fmt.Fprintf(&sb, "dhcp-option=6,1.1.1.1,8.8.8.8\n\n") entries := make(map[string]string) diff --git a/internal/dhcp/struct.go b/internal/dhcp/struct.go index 316667c..4c69b9c 100644 --- a/internal/dhcp/struct.go +++ b/internal/dhcp/struct.go @@ -5,10 +5,8 @@ import ( ) type Config struct { - Network *net.IPNet - VPCGateway net.IP // next-hop for VPCRoute (option 121) - VPCRoute *net.IPNet // if non-nil, emit dhcp-option=121,VPCRoute,VPCGateway - DefaultGateway net.IP // if non-nil, emit dhcp-option=3,DefaultGateway - Name string - ConfDir string + Network *net.IPNet + Gateway net.IP + Name string + ConfDir string } diff --git a/internal/dispatcher/agent/subnet_commands.go b/internal/dispatcher/agent/subnet_commands.go index e488842..b454974 100644 --- a/internal/dispatcher/agent/subnet_commands.go +++ b/internal/dispatcher/agent/subnet_commands.go @@ -12,14 +12,13 @@ import ( ) type CreateSubnetCommand struct { - Name string - VPC string - Mode string - VxlanID int - IfaceType string - InterfaceIP string - CIDR string - DefaultRoute bool + Name string + VPC string + Mode string + VxlanID int + IfaceType string + GatewayIP string + CIDR string } func (c CreateSubnetCommand) Prepare(db *badger.DB, cfg *configuration.Config) error { @@ -47,9 +46,8 @@ func (c CreateSubnetCommand) Prepare(db *badger.DB, cfg *configuration.Config) e kv.AddInDB(db, "subnet/"+c.Name+"/vpc", c.VPC) kv.AddInDB(db, "subnet/"+c.Name+"/mode", c.Mode) 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+"/gateway_ip", c.GatewayIP) 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 b6aacee..3b118bc 100644 --- a/internal/dispatcher/agent/subnet_commands_test.go +++ b/internal/dispatcher/agent/subnet_commands_test.go @@ -20,7 +20,7 @@ func TestCreateSubnetCommand_Prepare_Success(t *testing.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", + IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", } if err := cmd.Prepare(db, testCfg()); err != nil { t.Fatalf("Prepare a échoué : %v", err) @@ -40,7 +40,7 @@ func TestCreateSubnetCommand_Prepare_UsesIfaceTypeMapping(t *testing.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", + IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", } cmd.Prepare(db, testCfg()) iface, _ := kv.GetFromDB(db, "subnet/sn-1/local_iface") @@ -54,7 +54,7 @@ func TestCreateSubnetCommand_Prepare_UsesDefaultIfaceWhenTypeUnknown(t *testing. kv.AddInDB(db, "vpc/vpc-1/state", "created") cmd := CreateSubnetCommand{ Name: "sn-1", VPC: "vpc-1", VxlanID: 100, - IfaceType: "inconnu", InterfaceIP: "10.0.0.1", CIDR: "10.0.0.0/24", + IfaceType: "inconnu", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", } cmd.Prepare(db, testCfg()) iface, _ := kv.GetFromDB(db, "subnet/sn-1/local_iface") @@ -69,7 +69,7 @@ func TestCreateSubnetCommand_Prepare_Duplicate(t *testing.T) { kv.AddInDB(db, "subnet/sn-exist/state", "created") cmd := CreateSubnetCommand{ Name: "sn-exist", VPC: "vpc-1", VxlanID: 100, - IfaceType: "vms", InterfaceIP: "10.0.0.1", CIDR: "10.0.0.0/24", + IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", } if err := cmd.Prepare(db, testCfg()); err == nil { t.Error("Prepare devrait échouer sur un subnet déjà existant") @@ -80,7 +80,7 @@ func TestCreateSubnetCommand_Prepare_VPCNotFound(t *testing.T) { _, db := newTestDispatcher(t) cmd := CreateSubnetCommand{ Name: "sn-1", VPC: "vpc-inexistant", VxlanID: 100, - IfaceType: "vms", InterfaceIP: "10.0.0.1", CIDR: "10.0.0.0/24", + IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", } if err := cmd.Prepare(db, testCfg()); err == nil { t.Error("Prepare devrait échouer si le VPC n'existe pas") @@ -92,7 +92,7 @@ func TestCreateSubnetCommand_Prepare_VPCDeleting(t *testing.T) { kv.AddInDB(db, "vpc/vpc-dying/state", "deleting") cmd := CreateSubnetCommand{ Name: "sn-1", VPC: "vpc-dying", VxlanID: 100, - IfaceType: "vms", InterfaceIP: "10.0.0.1", CIDR: "10.0.0.0/24", + IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", } if err := cmd.Prepare(db, testCfg()); err == nil { t.Error("Prepare devrait échouer si le VPC est en cours de suppression") @@ -104,109 +104,13 @@ func TestCreateSubnetCommand_Prepare_VPCDeleted(t *testing.T) { kv.AddInDB(db, "vpc/vpc-gone/state", "deleted") cmd := CreateSubnetCommand{ Name: "sn-1", VPC: "vpc-gone", VxlanID: 100, - IfaceType: "vms", InterfaceIP: "10.0.0.1", CIDR: "10.0.0.0/24", + IfaceType: "vms", GatewayIP: "10.0.0.1", CIDR: "10.0.0.0/24", } if err := cmd.Prepare(db, testCfg()); err == nil { t.Error("Prepare devrait échouer si le VPC est supprimé") } } -func TestCreateSubnetCommand_Prepare_DefaultsToVxlanMode(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()) - mode, _ := kv.GetFromDB(db, "subnet/sn-1/mode") - if mode != "vxlan" { - t.Errorf("mode attendu vxlan, obtenu %q", mode) - } - if _, err := kv.GetFromDB(db, "subnet/sn-1/vxlan_id"); err != nil { - t.Error("vxlan_id devrait être écrit en mode vxlan") - } -} - -func TestCreateSubnetCommand_Prepare_BridgeMode_Success(t *testing.T) { - _, db := newTestDispatcher(t) - kv.AddInDB(db, "vpc/vpc-1/state", "created") - cmd := CreateSubnetCommand{ - Name: "sn-1", VPC: "vpc-1", Mode: "bridge", - IfaceType: "vms", InterfaceIP: "10.0.0.1", CIDR: "10.0.0.0/24", - } - if err := cmd.Prepare(db, testCfg()); err != nil { - t.Fatalf("Prepare a échoué : %v", err) - } - mode, _ := kv.GetFromDB(db, "subnet/sn-1/mode") - if mode != "bridge" { - t.Errorf("mode attendu bridge, obtenu %q", mode) - } - iface, _ := kv.GetFromDB(db, "subnet/sn-1/local_iface") - if iface != "br-vms" { - t.Errorf("local_iface attendu br-vms, obtenu %q", iface) - } -} - -func TestCreateSubnetCommand_Prepare_BridgeMode_NoVxlanID(t *testing.T) { - _, db := newTestDispatcher(t) - kv.AddInDB(db, "vpc/vpc-1/state", "created") - cmd := CreateSubnetCommand{ - Name: "sn-1", VPC: "vpc-1", Mode: "bridge", - IfaceType: "vms", InterfaceIP: "10.0.0.1", CIDR: "10.0.0.0/24", - } - cmd.Prepare(db, testCfg()) - if _, err := kv.GetFromDB(db, "subnet/sn-1/vxlan_id"); err == nil { - t.Error("vxlan_id ne devrait pas être écrit en mode bridge") - } -} - -func TestCreateSubnetCommand_Prepare_UnknownMode(t *testing.T) { - _, db := newTestDispatcher(t) - kv.AddInDB(db, "vpc/vpc-1/state", "created") - cmd := CreateSubnetCommand{ - Name: "sn-1", VPC: "vpc-1", Mode: "vlan", - IfaceType: "vms", InterfaceIP: "10.0.0.1", CIDR: "10.0.0.0/24", - } - if err := cmd.Prepare(db, testCfg()); err == nil { - t.Error("Prepare devrait échouer pour un mode inconnu") - } -} - -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/dispatcher/agent/vm_commands.go b/internal/dispatcher/agent/vm_commands.go index 1bb55d4..aade998 100644 --- a/internal/dispatcher/agent/vm_commands.go +++ b/internal/dispatcher/agent/vm_commands.go @@ -2,9 +2,7 @@ package dispatcher import ( "fmt" - "math/rand" "strconv" - "strings" "time" configuration "git.g3e.fr/syonad/two/internal/config/agent" @@ -14,14 +12,15 @@ import ( ) type StartVMCommand struct { - Name string - Subnet string - IP string - VolumePath string - Memory int - CPUs int - Password string - SSHKey string + Name string + Subnet string + IP string + MetadataPort string + VolumePath string + Memory int + CPUs int + Password string + SSHKey string } func (c StartVMCommand) Prepare(db *badger.DB, _ *configuration.Config) error { @@ -35,14 +34,10 @@ func (c StartVMCommand) Prepare(db *badger.DB, _ *configuration.Config) error { if subnetState == "deleting" || subnetState == "deleted" { return fmt.Errorf("subnet %q is %s", c.Subnet, subnetState) } - port, err := allocateMetadataPort(db) - if err != nil { - return fmt.Errorf("allocate metadata port: %w", err) - } kv.AddInDB(db, "vm/"+c.Name+"/state", "starting") kv.AddInDB(db, "vm/"+c.Name+"/subnet", c.Subnet) kv.AddInDB(db, "vm/"+c.Name+"/ip", c.IP) - kv.AddInDB(db, "vm/"+c.Name+"/metadata_port", strconv.Itoa(port)) + kv.AddInDB(db, "vm/"+c.Name+"/metadata_port", c.MetadataPort) kv.AddInDB(db, "vm/"+c.Name+"/volume_path", c.VolumePath) kv.AddInDB(db, "vm/"+c.Name+"/memory", strconv.Itoa(c.Memory)) kv.AddInDB(db, "vm/"+c.Name+"/cpus", strconv.Itoa(c.CPUs)) @@ -55,28 +50,6 @@ func (c StartVMCommand) Prepare(db *badger.DB, _ *configuration.Config) error { return nil } -func allocateMetadataPort(db *badger.DB) (int, error) { - entries, err := kv.ListByPrefix(db, "vm/") - if err != nil { - return 0, err - } - used := make(map[int]struct{}) - for key, value := range entries { - if strings.HasSuffix(key, "/metadata_port") { - if p, err := strconv.Atoi(value); err == nil { - used[p] = struct{}{} - } - } - } - for range 100 { - p := rand.Intn(9000) + 1000 - if _, taken := used[p]; !taken { - return p, nil - } - } - return 0, fmt.Errorf("no free metadata port available in [1000, 9999]") -} - func (c StartVMCommand) Execute(db *badger.DB, cfg *configuration.Config) error { timeout := time.After(time.Duration(cfg.Dispatcher.TimeoutSeconds) * time.Second) for { diff --git a/internal/dispatcher/agent/vpc_commands.go b/internal/dispatcher/agent/vpc_commands.go index a129d88..c03dd77 100644 --- a/internal/dispatcher/agent/vpc_commands.go +++ b/internal/dispatcher/agent/vpc_commands.go @@ -2,7 +2,6 @@ package dispatcher import ( "fmt" - "net" "strings" "time" @@ -14,19 +13,12 @@ import ( type CreateVPCCommand struct { Name string - CIDR string } func (c CreateVPCCommand) Prepare(db *badger.DB, _ *configuration.Config) error { if _, err := kv.GetFromDB(db, "vpc/"+c.Name+"/state"); err == nil { return fmt.Errorf("vpc %q already exists", c.Name) } - if _, _, err := net.ParseCIDR(c.CIDR); err != nil { - return fmt.Errorf("invalid cidr %q: %w", c.CIDR, err) - } - if err := kv.AddInDB(db, "vpc/"+c.Name+"/cidr", c.CIDR); err != nil { - return err - } return kv.AddInDB(db, "vpc/"+c.Name+"/state", "creating") } diff --git a/internal/dispatcher/agent/vpc_commands_test.go b/internal/dispatcher/agent/vpc_commands_test.go index 9633733..e2f2162 100644 --- a/internal/dispatcher/agent/vpc_commands_test.go +++ b/internal/dispatcher/agent/vpc_commands_test.go @@ -10,7 +10,7 @@ import ( func TestCreateVPCCommand_Prepare_NewVPC(t *testing.T) { _, db := newTestDispatcher(t) - cmd := CreateVPCCommand{Name: "vpc-1", CIDR: "10.0.0.0/16"} + cmd := CreateVPCCommand{Name: "vpc-1"} if err := cmd.Prepare(db, nil); err != nil { t.Fatalf("Prepare a échoué : %v", err) } @@ -21,32 +21,17 @@ func TestCreateVPCCommand_Prepare_NewVPC(t *testing.T) { if state != "creating" { t.Errorf("state attendu creating, obtenu %q", state) } - cidr, err := kv.GetFromDB(db, "vpc/vpc-1/cidr") - if err != nil { - t.Fatalf("cidr non écrit en DB : %v", err) - } - if cidr != "10.0.0.0/16" { - t.Errorf("cidr attendu 10.0.0.0/16, obtenu %q", cidr) - } } func TestCreateVPCCommand_Prepare_Duplicate(t *testing.T) { _, db := newTestDispatcher(t) kv.AddInDB(db, "vpc/vpc-exist/state", "created") - cmd := CreateVPCCommand{Name: "vpc-exist", CIDR: "10.0.0.0/16"} + cmd := CreateVPCCommand{Name: "vpc-exist"} if err := cmd.Prepare(db, nil); err == nil { t.Error("Prepare devrait échouer sur un VPC déjà existant") } } -func TestCreateVPCCommand_Prepare_InvalidCIDR(t *testing.T) { - _, db := newTestDispatcher(t) - cmd := CreateVPCCommand{Name: "vpc-bad", CIDR: "not-a-cidr"} - if err := cmd.Prepare(db, nil); err == nil { - t.Error("Prepare devrait échouer avec un CIDR invalide") - } -} - // --- DeleteVPCCommand.Prepare --- func TestDeleteVPCCommand_Prepare_Success(t *testing.T) { diff --git a/internal/ebtables/ebtables.go b/internal/ebtables/ebtables.go index 40ea0f0..31e276b 100644 --- a/internal/ebtables/ebtables.go +++ b/internal/ebtables/ebtables.go @@ -13,48 +13,46 @@ func deleteRule(args ...string) error { return exec.Command("ebtables", append([]string{"-D"}, args...)...).Run() } -func DropARPToGateway(iface, ip string) error { +func DropARPToGateway(bridge, gatewayIP string) error { if err := addRule("FORWARD", - "--out-interface", iface, + "--out-interface", bridge, "-p", "arp", "--arp-op", "Request", - "--arp-ip-dst", ip, + "--arp-ip-dst", gatewayIP, "-j", "DROP"); err != nil { return fmt.Errorf("ebtables arp rule: %w", err) } return nil } -func DropDHCP(iface, ip string) error { +func DropDHCP(bridge string) error { if err := addRule("FORWARD", - "--out-interface", iface, + "--out-interface", bridge, "-p", "IPv4", "--ip-protocol", "udp", "--ip-source-port", "67:68", "--ip-destination-port", "67:68", - "--ip-source", ip, "-j", "DROP"); err != nil { return fmt.Errorf("ebtables dhcp rule: %w", err) } return nil } -func DeleteARPToGateway(iface, ip string) error { +func DeleteARPToGateway(bridge, gatewayIP string) error { return deleteRule("FORWARD", - "--out-interface", iface, + "--out-interface", bridge, "-p", "arp", "--arp-op", "Request", - "--arp-ip-dst", ip, + "--arp-ip-dst", gatewayIP, "-j", "DROP") } -func DeleteDHCP(iface, ip string) error { +func DeleteDHCP(bridge string) error { return deleteRule("FORWARD", - "--out-interface", iface, + "--out-interface", bridge, "-p", "IPv4", "--ip-protocol", "udp", "--ip-source-port", "67:68", "--ip-destination-port", "67:68", - "--ip-source", ip, "-j", "DROP") } diff --git a/internal/netif/gateway_linux.go b/internal/netif/gateway_linux.go deleted file mode 100644 index 7636b7a..0000000 --- a/internal/netif/gateway_linux.go +++ /dev/null @@ -1,23 +0,0 @@ -//go:build linux - -package netif - -import ( - "fmt" - "net" - - "github.com/vishvananda/netlink" -) - -func GetDefaultGateway() (net.IP, error) { - routes, err := netlink.RouteList(nil, netlink.FAMILY_V4) - if err != nil { - return nil, fmt.Errorf("list routes: %w", err) - } - for _, r := range routes { - if r.Dst == nil && r.Gw != nil { - return r.Gw, nil - } - } - return nil, fmt.Errorf("no default gateway found") -} diff --git a/internal/netif/gateway_other.go b/internal/netif/gateway_other.go deleted file mode 100644 index 764867c..0000000 --- a/internal/netif/gateway_other.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build !linux - -package netif - -import ( - "fmt" - "net" -) - -func GetDefaultGateway() (net.IP, error) { - return nil, fmt.Errorf("not supported on this platform") -} diff --git a/internal/subnet/create.go b/internal/subnet/create.go index a3faf46..53349ca 100644 --- a/internal/subnet/create.go +++ b/internal/subnet/create.go @@ -81,30 +81,32 @@ func createSubnet(db *badger.DB, subnetName string, d subnetData) error { switch d.mode { case "vxlan": if err := netns.Call(d.vpc, func() error { - if err := netif.AddrAdd(d.bridge, d.interfaceIP); err != nil { - return fmt.Errorf("add addr: %w", err) - } - if err := netif.RouteAdd(d.bridge, d.cidr); err != nil { - return fmt.Errorf("add route: %w", err) - } - if err := ebtables.DropARPToGateway(vethI, d.interfaceIP.String()); err != nil { - return err - } - return ebtables.DropDHCP(vethI, d.interfaceIP.String()) + return netif.AddrAdd(d.bridge, d.gatewayIP) }); err != nil { - return fmt.Errorf("configure netns: %w", err) + return fmt.Errorf("add addr to bridge in netns: %w", err) + } + if err := netns.Call(d.vpc, func() error { + return netif.RouteAdd(d.bridge, d.cidr) + }); err != nil { + return fmt.Errorf("add route in netns: %w", err) } case "bridge": - if err := netns.Call(d.vpc, func() error { - if err := netif.AddrAdd(d.bridge, d.interfaceIP); err != nil { - return fmt.Errorf("add addr: %w", err) - } - if err := netif.RouteAdd(d.bridge, d.cidr); err != nil { - return fmt.Errorf("add route: %w", err) - } - return ebtables.DropDHCP(vethI, d.interfaceIP.String()) - }); err != nil { - return fmt.Errorf("configure netns: %w", err) + } + + applyEbtables := func() error { + if err := ebtables.DropARPToGateway(d.bridge, d.gatewayIP.String()); err != nil { + return err + } + return ebtables.DropDHCP(d.bridge) + } + switch d.mode { + case "vxlan": + if err := applyEbtables(); err != nil { + return err + } + case "bridge": + if err := netns.Call(d.vpc, applyEbtables); err != nil { + return fmt.Errorf("set ebtables in netns: %w", err) } } @@ -137,22 +139,10 @@ func setupVxlanHost(d subnetData, vethE string) error { func startDHCP(db *badger.DB, subnetName string, d subnetData) error { conf := dhcp.Config{ Network: d.cidr, + Gateway: d.gatewayIP, Name: d.vpc + "_" + d.bridge, ConfDir: "/etc/dnsmasq.d", } - switch d.mode { - case "vxlan": - conf.VPCGateway = d.interfaceIP - conf.VPCRoute = d.vpcCIDR - case "bridge": - if d.defaultRoute { - gw, err := netif.GetDefaultGateway() - if err != nil { - return fmt.Errorf("get default gateway: %w", err) - } - conf.DefaultGateway = gw - } - } _, entries, err := dhcp.GenerateConfig(conf) if err != nil { return fmt.Errorf("generate dhcp config: %w", err) diff --git a/internal/subnet/data.go b/internal/subnet/data.go index 0f1f62a..2936e21 100644 --- a/internal/subnet/data.go +++ b/internal/subnet/data.go @@ -11,16 +11,14 @@ 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 - defaultRoute bool + vpc string + subnetID string + bridge string + mode string + vxlanID int + localIface string + gatewayIP net.IP + cidr *net.IPNet } func loadSubnet(db *badger.DB, name string) (subnetData, error) { @@ -59,15 +57,15 @@ func loadSubnet(db *badger.DB, name string) (subnetData, error) { } d.localIface = localIface - interfaceIPStr, err := kv.GetFromDB(db, "subnet/"+name+"/interface_ip") + gatewayIPStr, err := kv.GetFromDB(db, "subnet/"+name+"/gateway_ip") if err != nil { - return d, fmt.Errorf("get interface_ip: %w", err) + return d, fmt.Errorf("get gateway_ip: %w", err) } - interfaceIP := net.ParseIP(interfaceIPStr) - if interfaceIP == nil { - return d, fmt.Errorf("invalid interface_ip: %s", interfaceIPStr) + gatewayIP := net.ParseIP(gatewayIPStr) + if gatewayIP == nil { + return d, fmt.Errorf("invalid gateway_ip: %s", gatewayIPStr) } - d.interfaceIP = interfaceIP + d.gatewayIP = gatewayIP cidrStr, err := kv.GetFromDB(db, "subnet/"+name+"/cidr") if err != nil { @@ -79,21 +77,5 @@ 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) - } - _, vpcIPNet, err := net.ParseCIDR(vpcCIDRStr) - if err != nil { - return d, fmt.Errorf("parse vpc cidr: %w", err) - } - d.vpcCIDR = vpcIPNet - return d, nil } diff --git a/internal/subnet/delete.go b/internal/subnet/delete.go index 0ae30b5..bc62af7 100644 --- a/internal/subnet/delete.go +++ b/internal/subnet/delete.go @@ -70,18 +70,18 @@ func stopDHCP(db *badger.DB, subnetName string, d subnetData) error { func deleteSubnetVxlan(d subnetData) error { vxlanIface := fmt.Sprintf("vxlan-%d", d.vxlanID) - vethI := "v-" + d.subnetID + "-i" + + if err := ebtables.DeleteARPToGateway(d.bridge, d.gatewayIP.String()); err != nil { + return fmt.Errorf("delete ebtables arp rule: %w", err) + } + if err := ebtables.DeleteDHCP(d.bridge); err != nil { + return fmt.Errorf("delete ebtables dhcp rule: %w", err) + } if err := netns.Call(d.vpc, func() error { - if err := ebtables.DeleteARPToGateway(vethI, d.interfaceIP.String()); err != nil { - return fmt.Errorf("delete ebtables arp rule: %w", err) - } - if err := ebtables.DeleteDHCP(vethI, d.interfaceIP.String()); err != nil { - return fmt.Errorf("delete ebtables dhcp rule: %w", err) - } return netif.DeleteLink(d.bridge) }); err != nil { - return fmt.Errorf("delete netns resources: %w", err) + return fmt.Errorf("delete bridge in netns: %w", err) } if err := netif.DeleteLink(vxlanIface); err != nil { @@ -99,15 +99,19 @@ func deleteSubnetVxlan(d subnetData) error { } func deleteSubnetBridge(d subnetData) error { - vethI := "v-" + d.subnetID + "-i" + if err := netns.Call(d.vpc, func() error { + if err := ebtables.DeleteARPToGateway(d.bridge, d.gatewayIP.String()); err != nil { + return fmt.Errorf("delete ebtables arp rule: %w", err) + } + return ebtables.DeleteDHCP(d.bridge) + }); err != nil { + return fmt.Errorf("delete ebtables in netns: %w", err) + } if err := netns.Call(d.vpc, func() error { - if err := ebtables.DeleteDHCP(vethI, d.interfaceIP.String()); err != nil { - return fmt.Errorf("delete ebtables dhcp rule: %w", err) - } return netif.DeleteLink(d.bridge) }); err != nil { - return fmt.Errorf("delete netns resources: %w", err) + return fmt.Errorf("delete bridge in netns: %w", err) } if err := netif.DeleteLink("v-" + d.subnetID + "-e"); err != nil { diff --git a/internal/vm/create.go b/internal/vm/create.go index f3e9d34..53e2e96 100644 --- a/internal/vm/create.go +++ b/internal/vm/create.go @@ -33,7 +33,7 @@ func StartVM(db *badger.DB, name string, cfg *configuration.Config) error { } if err := netns.Call(d.vpcName, func() error { - return iptables.AddMetadataRedirect(d.ip, d.interfaceIP, d.metadataPort) + return iptables.AddMetadataRedirect(d.ip, d.gatewayIP, d.metadataPort) }); err != nil { return fmt.Errorf("add metadata redirect: %w", err) } @@ -41,7 +41,7 @@ func StartVM(db *badger.DB, name string, cfg *configuration.Config) error { if err := metadata.StartMetadata(metadata.NoCloudConfig{ Name: name, VpcName: d.vpcName, - BindIP: d.interfaceIP, + BindIP: d.gatewayIP, BindPort: d.metadataPort, Password: d.password, SSHKEY: d.sshkey, diff --git a/internal/vm/data.go b/internal/vm/data.go index df5c051..0563fc0 100644 --- a/internal/vm/data.go +++ b/internal/vm/data.go @@ -14,7 +14,7 @@ import ( type vmData struct { subnetName string vpcName string - interfaceIP string + gatewayIP string bridge string tapID int ip string @@ -43,11 +43,11 @@ func loadVM(db *badger.DB, name string) (vmData, error) { } d.vpcName = vpcName - interfaceIP, err := kv.GetFromDB(db, "subnet/"+subnetName+"/interface_ip") + gatewayIP, err := kv.GetFromDB(db, "subnet/"+subnetName+"/gateway_ip") if err != nil { - return d, fmt.Errorf("get interface_ip: %w", err) + return d, fmt.Errorf("get gateway_ip: %w", err) } - d.interfaceIP = interfaceIP + d.gatewayIP = gatewayIP tapIDStr, err := kv.GetFromDB(db, "vm/"+name+"/tap_id") if err != nil { diff --git a/internal/vm/delete.go b/internal/vm/delete.go index 79a4c72..5030339 100644 --- a/internal/vm/delete.go +++ b/internal/vm/delete.go @@ -52,7 +52,7 @@ func StopVM(db *badger.DB, name string, cfg *configuration.Config) error { } if err := netns.Call(d.vpcName, func() error { - return iptables.DeleteMetadataRedirect(d.ip, d.interfaceIP, d.metadataPort) + return iptables.DeleteMetadataRedirect(d.ip, d.gatewayIP, d.metadataPort) }); err != nil { return fmt.Errorf("delete metadata redirect: %w", err) }