diff --git a/api/agent.yaml b/api/agent.yaml index 1d3a77f..be328d0 100644 --- a/api/agent.yaml +++ b/api/agent.yaml @@ -127,12 +127,6 @@ paths: application/json: schema: $ref: "#/components/schemas/Subnet" - "400": - description: Missing required field or unknown iface_type - content: - application/json: - schema: - $ref: "#/components/schemas/Error" "409": description: Subnet already exists content: @@ -209,16 +203,15 @@ components: properties: name: type: string - description: Unique name for the VPC, must follow the format vp-[id] - pattern: '^vp-.+' - example: vp-00001 + description: Unique name for the VPC + example: vpc1 VPC: type: object properties: name: type: string - example: vp-00001 + example: vpc1 state: type: string enum: [creating, created, deleting, deleted] @@ -226,7 +219,7 @@ components: SubnetCreateRequest: type: object - required: [name, vpc, vxlan_id, gateway_ip, cidr] + required: [name, vpc, vxlan_id, local_ip, gateway_ip, cidr] properties: name: type: string @@ -240,10 +233,11 @@ components: type: integer description: VXLAN VNI identifier example: 100 - iface_type: + local_ip: 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 + format: ipv4 + description: Local VTEP IP address + example: "10.0.0.5" gateway_ip: type: string format: ipv4 @@ -270,10 +264,9 @@ components: vxlan_id: type: integer example: 100 - local_iface: + local_ip: type: string - description: Resolved interface name - example: br-000000 + example: "10.0.0.5" gateway_ip: type: string example: "10.10.10.1" diff --git a/cmd/agent/main.go b/cmd/agent/main.go index dd6fd6d..b61bb6f 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -36,7 +36,7 @@ func main() { apiAddr := fmt.Sprintf("%s:%d", cfg.Api.Address, cfg.Api.Port) promAddr := fmt.Sprintf("%s:%d", cfg.Prometheus.Address, cfg.Prometheus.Port) - d := dispatcher.New(q, db, cfg) + d := dispatcher.New(q, db) go agentapi.New(d, db).Start(apiAddr) go promserver.Start(promAddr, registry) diff --git a/conf/agent/config.exemple.yml b/conf/agent/config.exemple.yml index bc1d437..a2b9f1b 100644 --- a/conf/agent/config.exemple.yml +++ b/conf/agent/config.exemple.yml @@ -1,9 +1,2 @@ database: - path: "/var/lib/two/data/" - -default_interface: br-000000 - -interfaces: - vms: br-000000 - internet: br-000000 - admin: br-000000 + path: "/var/lib/two/data/" \ No newline at end of file diff --git a/internal/api/agent/models.go b/internal/api/agent/models.go index e826082..bd09c76 100644 --- a/internal/api/agent/models.go +++ b/internal/api/agent/models.go @@ -13,19 +13,19 @@ type SubnetCreateRequest struct { Name string `json:"name"` VPC string `json:"vpc"` VxlanID int `json:"vxlan_id"` - IfaceType string `json:"iface_type"` + LocalIP string `json:"local_ip"` GatewayIP string `json:"gateway_ip"` CIDR string `json:"cidr"` } type Subnet struct { - Name string `json:"name"` - State string `json:"state"` - VPC string `json:"vpc"` - VxlanID int `json:"vxlan_id"` - LocalIface string `json:"local_iface"` - GatewayIP string `json:"gateway_ip"` - CIDR string `json:"cidr"` + Name string `json:"name"` + State string `json:"state"` + VPC string `json:"vpc"` + VxlanID int `json:"vxlan_id"` + LocalIP string `json:"local_ip"` + GatewayIP string `json:"gateway_ip"` + CIDR string `json:"cidr"` } type ErrorResponse struct { diff --git a/internal/api/agent/subnets.go b/internal/api/agent/subnets.go index 263c9ce..b251c3c 100644 --- a/internal/api/agent/subnets.go +++ b/internal/api/agent/subnets.go @@ -31,16 +31,16 @@ 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.IfaceType == "" || req.GatewayIP == "" || req.CIDR == "" { + if req.Name == "" || req.VPC == "" || req.LocalIP == "" || req.GatewayIP == "" || req.CIDR == "" { w.WriteHeader(http.StatusBadRequest) - json.NewEncoder(w).Encode(ErrorResponse{Error: "name, vpc, iface_type, gateway_ip and cidr are required"}) + json.NewEncoder(w).Encode(ErrorResponse{Error: "name, vpc, local_ip, gateway_ip and cidr are required"}) return } s.dispatcher.Dispatch(dispatcher.CreateSubnetCommand{ Name: req.Name, VPC: req.VPC, VxlanID: req.VxlanID, - IfaceType: req.IfaceType, + LocalIP: req.LocalIP, GatewayIP: req.GatewayIP, CIDR: req.CIDR, }) @@ -50,7 +50,9 @@ func (s *Server) postSubnet(w http.ResponseWriter, r *http.Request) { State: "creating", VPC: req.VPC, VxlanID: req.VxlanID, + LocalIP: req.LocalIP, GatewayIP: req.GatewayIP, CIDR: req.CIDR, }) } + diff --git a/internal/config/agent/struct.go b/internal/config/agent/struct.go index 1c9fc9f..24ad0f5 100644 --- a/internal/config/agent/struct.go +++ b/internal/config/agent/struct.go @@ -20,8 +20,6 @@ type Config struct { Count int `mapstructure:"count"` BufferSize int `mapstructure:"buffer_size"` } `mapstructure:"worker"` - DefaultInterface string `mapstructure:"default_interface"` - Interfaces map[string]string `mapstructure:"interfaces"` } func LoadConfig(path string) (*Config, error) { @@ -36,7 +34,6 @@ func LoadConfig(path string) (*Config, error) { v.SetDefault("prometheus.port", 9090) v.SetDefault("worker.count", 4) v.SetDefault("worker.buffer_size", 100) - v.SetDefault("default_interface", "br-000000") v.ReadInConfig() diff --git a/internal/dispatcher/dispatcher.go b/internal/dispatcher/dispatcher.go index 8e94827..937adf2 100644 --- a/internal/dispatcher/dispatcher.go +++ b/internal/dispatcher/dispatcher.go @@ -3,28 +3,26 @@ package dispatcher import ( "log" - configuration "git.g3e.fr/syonad/two/internal/config/agent" "git.g3e.fr/syonad/two/pkg/worker" "github.com/dgraph-io/badger/v4" ) type Command interface { - Execute(db *badger.DB, cfg *configuration.Config) error + Execute(db *badger.DB) error } type Dispatcher struct { queue *worker.Queue db *badger.DB - cfg *configuration.Config } -func New(queue *worker.Queue, db *badger.DB, cfg *configuration.Config) *Dispatcher { - return &Dispatcher{queue: queue, db: db, cfg: cfg} +func New(queue *worker.Queue, db *badger.DB) *Dispatcher { + return &Dispatcher{queue: queue, db: db} } func (d *Dispatcher) Dispatch(cmd Command) { d.queue.Submit(func() { - if err := cmd.Execute(d.db, d.cfg); err != nil { + if err := cmd.Execute(d.db); err != nil { log.Printf("command error (%T): %v", cmd, err) } }) diff --git a/internal/dispatcher/subnet_commands.go b/internal/dispatcher/subnet_commands.go index 0d471c3..b23949e 100644 --- a/internal/dispatcher/subnet_commands.go +++ b/internal/dispatcher/subnet_commands.go @@ -5,7 +5,6 @@ import ( "os" "strconv" - configuration "git.g3e.fr/syonad/two/internal/config/agent" "git.g3e.fr/syonad/two/internal/subnet" "git.g3e.fr/syonad/two/pkg/db/kv" "github.com/dgraph-io/badger/v4" @@ -15,20 +14,16 @@ type CreateSubnetCommand struct { Name string VPC string VxlanID int - IfaceType string + LocalIP string GatewayIP string CIDR string } -func (c CreateSubnetCommand) Execute(db *badger.DB, cfg *configuration.Config) error { - localIface, ok := cfg.Interfaces[c.IfaceType] - if !ok { - localIface = cfg.DefaultInterface - } +func (c CreateSubnetCommand) Execute(db *badger.DB) error { kv.AddInDB(db, "subnet/"+c.Name+"/state", "creating") kv.AddInDB(db, "subnet/"+c.Name+"/vpc", c.VPC) kv.AddInDB(db, "subnet/"+c.Name+"/vxlan_id", strconv.Itoa(c.VxlanID)) - kv.AddInDB(db, "subnet/"+c.Name+"/local_iface", localIface) + kv.AddInDB(db, "subnet/"+c.Name+"/local_ip", c.LocalIP) kv.AddInDB(db, "subnet/"+c.Name+"/gateway_ip", c.GatewayIP) kv.AddInDB(db, "subnet/"+c.Name+"/cidr", c.CIDR) return subnet.CreateSubnet(db, c.Name) @@ -38,7 +33,7 @@ type DeleteSubnetCommand struct { Name string } -func (c DeleteSubnetCommand) Execute(db *badger.DB, _ *configuration.Config) error { +func (c DeleteSubnetCommand) Execute(db *badger.DB) error { kv.AddInDB(db, "subnet/"+c.Name+"/state", "deleting") if err := subnet.DeleteSubnet(db, c.Name); err != nil { fmt.Println(err) diff --git a/internal/dispatcher/vpc_commands.go b/internal/dispatcher/vpc_commands.go index b2687fc..195b014 100644 --- a/internal/dispatcher/vpc_commands.go +++ b/internal/dispatcher/vpc_commands.go @@ -1,7 +1,6 @@ package dispatcher import ( - configuration "git.g3e.fr/syonad/two/internal/config/agent" "git.g3e.fr/syonad/two/internal/vpc" "git.g3e.fr/syonad/two/pkg/db/kv" "github.com/dgraph-io/badger/v4" @@ -11,7 +10,7 @@ type CreateVPCCommand struct { Name string } -func (c CreateVPCCommand) Execute(db *badger.DB, _ *configuration.Config) error { +func (c CreateVPCCommand) Execute(db *badger.DB) error { kv.AddInDB(db, "vpc/"+c.Name+"/state", "creating") return vpc.CreateVPC(db, c.Name) } @@ -20,7 +19,7 @@ type DeleteVPCCommand struct { Name string } -func (c DeleteVPCCommand) Execute(db *badger.DB, _ *configuration.Config) error { +func (c DeleteVPCCommand) Execute(db *badger.DB) error { kv.AddInDB(db, "vpc/"+c.Name+"/state", "deleting") if err := vpc.DeleteVPC(db, c.Name); err != nil { return err diff --git a/internal/netif/vxlan.go b/internal/netif/vxlan.go index 6523f37..eacae7c 100644 --- a/internal/netif/vxlan.go +++ b/internal/netif/vxlan.go @@ -1,22 +1,20 @@ package netif import ( + "net" + "github.com/vishvananda/netlink" ) -func CreateVxlan(name string, vxlanID int, localIface string) error { - link, err := netlink.LinkByName(localIface) - if err != nil { - return err - } +func CreateVxlan(name string, vxlanID int, localIP net.IP) error { vxlan := &netlink.Vxlan{ LinkAttrs: netlink.LinkAttrs{ Name: name, }, - VxlanId: vxlanID, - Port: 4789, - VtepDevIndex: link.Attrs().Index, - Learning: false, + VxlanId: vxlanID, + Port: 4789, + SrcAddr: localIP, + Learning: false, } return netlink.LinkAdd(vxlan) } diff --git a/internal/subnet/create.go b/internal/subnet/create.go index 1ed514e..9bfe02a 100644 --- a/internal/subnet/create.go +++ b/internal/subnet/create.go @@ -40,9 +40,13 @@ func CreateSubnet(db *badger.DB, subnetName string) error { return fmt.Errorf("parse vxlan_id: %w", err) } - localIface, err := kv.GetFromDB(db, "subnet/"+subnetName+"/local_iface") + localIPStr, err := kv.GetFromDB(db, "subnet/"+subnetName+"/local_ip") if err != nil { - return fmt.Errorf("get local_iface: %w", err) + return fmt.Errorf("get local_ip: %w", err) + } + localIP := net.ParseIP(localIPStr) + if localIP == nil { + return fmt.Errorf("invalid local_ip: %s", localIPStr) } gatewayIPStr, err := kv.GetFromDB(db, "subnet/"+subnetName+"/gateway_ip") @@ -86,7 +90,7 @@ func CreateSubnet(db *badger.DB, subnetName string) error { } // vxlan - if err := netif.CreateVxlan(vxlanIface, vxlanID, localIface); err != nil { + if err := netif.CreateVxlan(vxlanIface, vxlanID, localIP); err != nil { return fmt.Errorf("create vxlan: %w", err) } diff --git a/internal/vpc/create.go b/internal/vpc/create.go index 148f70c..a6e6aaa 100644 --- a/internal/vpc/create.go +++ b/internal/vpc/create.go @@ -1,8 +1,6 @@ package vpc import ( - "strings" - "git.g3e.fr/syonad/two/internal/netif" "git.g3e.fr/syonad/two/internal/netns" "git.g3e.fr/syonad/two/pkg/db/kv" @@ -11,40 +9,49 @@ import ( ) func CreateVPC(db *badger.DB, name string) error { + // missing + // search data in db + // change state in db + + // create netns if state, err := kv.GetFromDB(db, "vpc/"+name+"/state"); err != nil { return err } else if state == "creating" { - vpcID := strings.SplitN(name, "-", 2)[1] - if err := netns.Create(name); err != nil { return err } - if err := netif.CreateVethToNetns("vp-"+vpcID+"-e", "vp-"+vpcID+"-i", "/var/run/netns/"+name, 9000); err != nil { + // create veth public for this netns + if err := netif.CreateVethToNetns("vp-"+name+"-e", "vp-public-i", "/var/run/netns/"+name, 9000); err != nil { return err } + // create public bridge in netns if err := netns.Call(name, func() error { return netif.CreateBridge("br-public", 1500) }); err != nil { return err } - if err := netif.BridgeSetMaster("vp-"+vpcID+"-e", "br-public"); err != nil { + // set veth to ext public bridge + if err := netif.BridgeSetMaster("vp-"+name+"-e", "br-public"); err != nil { return err } + // set veth to int public bridge if err := netns.Call(name, func() error { - return netif.BridgeSetMaster("vp-"+vpcID+"-i", "br-public") + return netif.BridgeSetMaster("vp-public-i", "br-public") }); err != nil { return err } - if err := netif.LinkSetUp("vp-" + vpcID + "-e"); err != nil { + // set set ext veth up + if err := netif.LinkSetUp("vp-" + name + "-e"); err != nil { return err } + // set set int veth up if err := netns.Call(name, func() error { - return netif.LinkSetUp("vp-" + vpcID + "-i") + return netif.LinkSetUp("vp-public-i") }); err != nil { return err } diff --git a/internal/vpc/delete.go b/internal/vpc/delete.go index dbd3a59..10c863d 100644 --- a/internal/vpc/delete.go +++ b/internal/vpc/delete.go @@ -1,8 +1,6 @@ package vpc import ( - "strings" - "git.g3e.fr/syonad/two/internal/netif" "git.g3e.fr/syonad/two/internal/netns" "git.g3e.fr/syonad/two/pkg/db/kv" @@ -14,9 +12,7 @@ func DeleteVPC(db *badger.DB, name string) error { if state, err := kv.GetFromDB(db, "vpc/"+name+"/state"); err != nil { return err } else if state == "deleting" { - vpcID := strings.SplitN(name, "-", 2)[1] - - if err := netif.DeleteLink("vp-" + vpcID + "-e"); err != nil { + if err := netif.DeleteLink("vp-" + name + "-e"); err != nil { return err }