diff --git a/cmd/api/v5/config.go b/cmd/api/v5/config.go index 6630286..c041851 100755 --- a/cmd/api/v5/config.go +++ b/cmd/api/v5/config.go @@ -29,7 +29,7 @@ func (u Config) List(c *cli.Context) error { name := c.String("network") format := c.String("format") if format == "yaml" { - cfg.Format() + cfg.FormatNetwork() } if len(name) > 0 { obj := cfg.GetNetwork(name) diff --git a/cmd/api/v5/network.go b/cmd/api/v5/network.go index 4d6e8bd..3058339 100755 --- a/cmd/api/v5/network.go +++ b/cmd/api/v5/network.go @@ -2,6 +2,7 @@ package v5 import ( "github.com/luscis/openlan/cmd/api" + "github.com/luscis/openlan/pkg/libol" "github.com/luscis/openlan/pkg/schema" "github.com/urfave/cli/v2" ) @@ -11,7 +12,12 @@ type Network struct { } func (u Network) Url(prefix, name string) string { - return prefix + "/api/network" + if name == "" { + return prefix + "/api/network" + } else { + return prefix + "/api/network/" + name + } + } func (u Network) List(c *cli.Context) error { @@ -25,6 +31,46 @@ func (u Network) List(c *cli.Context) error { } } +func (u Network) Add(c *cli.Context) error { + file := c.String("file") + network := &schema.Network{ + File: file, + } + url := u.Url(c.String("url"), "") + clt := u.NewHttp(c.String("token")) + if err := clt.PostJSON(url, network, nil); err != nil { + return err + } + return nil +} + +func (u Network) Remove(c *cli.Context) error { + network := c.String("name") + if len(network) == 0 { + return libol.NewErr("invalid network") + } + url := u.Url(c.String("url"), network) + clt := u.NewHttp(c.String("token")) + if err := clt.DeleteJSON(url, nil, nil); err != nil { + return err + } + return nil +} + +func (u Network) Save(c *cli.Context) error { + name := c.String("name") + network := &schema.Network{ + Name: name, + } + url := u.Url(c.String("url"), "") + clt := u.NewHttp(c.String("token")) + if err := clt.PutJSON(url, network, nil); err != nil { + return err + } + + return nil +} + func (u Network) Commands(app *api.App) { openvpn := OpenVpn{} app.Command(&cli.Command{ @@ -38,6 +84,32 @@ func (u Network) Commands(app *api.App) { Aliases: []string{"ls"}, Action: u.List, }, + { + Name: "add", + Usage: "Add a network", + Flags: []cli.Flag{ + &cli.StringFlag{Name: "file"}, + }, + Action: u.Add, + }, + { + Name: "remove", + Usage: "Remove the network", + Aliases: []string{"rm"}, + Flags: []cli.Flag{ + &cli.StringFlag{Name: "name"}, + }, + Action: u.Remove, + }, + { + Name: "save", + Usage: "Save the network", + Aliases: []string{"sa"}, + Flags: []cli.Flag{ + &cli.StringFlag{Name: "name", Value: ""}, + }, + Action: u.Save, + }, openvpn.Commands(), }, }) diff --git a/pkg/api/api.go b/pkg/api/api.go index 4da9067..87d88d4 100755 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -17,6 +17,9 @@ type Switcher interface { Server() libol.SocketServer Reload() Save() + AddNetwork(network string) + DelNetwork(network string) + SaveNetwork(network string) } func NewWorkerSchema(s Switcher) schema.Worker { diff --git a/pkg/api/network.go b/pkg/api/network.go index 9f17af3..f82b775 100755 --- a/pkg/api/network.go +++ b/pkg/api/network.go @@ -6,16 +6,21 @@ import ( "github.com/gorilla/mux" "github.com/luscis/openlan/pkg/cache" + "github.com/luscis/openlan/pkg/libol" "github.com/luscis/openlan/pkg/models" "github.com/luscis/openlan/pkg/schema" ) type Network struct { + Switcher Switcher } func (h Network) Router(router *mux.Router) { router.HandleFunc("/api/network", h.List).Methods("GET") + router.HandleFunc("/api/network", h.Post).Methods("POST") + router.HandleFunc("/api/network", h.Save).Methods("PUT") router.HandleFunc("/api/network/{id}", h.Get).Methods("GET") + router.HandleFunc("/api/network/{id}", h.Delete).Methods("DELETE") router.HandleFunc("/get/network/{id}/ovpn", h.Profile).Methods("GET") router.HandleFunc("/api/network/{id}/openvpn/restart", h.RestartVPN).Methods("POST") } @@ -41,6 +46,50 @@ func (h Network) Get(w http.ResponseWriter, r *http.Request) { } } +func (h Network) Post(w http.ResponseWriter, r *http.Request) { + network := &schema.Network{} + if err := GetData(r, network); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + cs := h.Switcher.Config() + file := cs.Dir("network", network.File) + if err := libol.FileExist(file); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + cs.AddNetwork(file) + if obj := cs.GetNetworkWithFile(file); obj != nil { + h.Switcher.AddNetwork(obj.Name) + } else { + http.Error(w, "Network not found", http.StatusBadRequest) + return + } + ResponseJson(w, "success") +} + +func (h Network) Delete(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + network := vars["id"] + worker := GetWorker(network) + if worker == nil { + http.Error(w, "network not found", http.StatusBadRequest) + return + } + h.Switcher.DelNetwork(network) + ResponseJson(w, "success") +} + +func (h Network) Save(w http.ResponseWriter, r *http.Request) { + network := &schema.Network{} + if err := GetData(r, network); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + h.Switcher.SaveNetwork(network.Name) + ResponseJson(w, "success") +} + func (h Network) Profile(w http.ResponseWriter, r *http.Request) { server := strings.SplitN(r.Host, ":", 2)[0] vars := mux.Vars(r) diff --git a/pkg/api/output.go b/pkg/api/output.go index e9deac7..2857609 100755 --- a/pkg/api/output.go +++ b/pkg/api/output.go @@ -46,7 +46,7 @@ func (h Output) Post(w http.ResponseWriter, r *http.Request) { return } cs := h.Switcher.Config() - if cs.Network == nil { + if cs.Network[name] == nil { http.Error(w, "network is nil", http.StatusBadRequest) return } @@ -69,7 +69,7 @@ func (h Output) Delete(w http.ResponseWriter, r *http.Request) { return } cs := h.Switcher.Config() - if cs.Network == nil { + if cs.Network[name] == nil { http.Error(w, "network is nil", http.StatusBadRequest) return } diff --git a/pkg/api/url.go b/pkg/api/url.go index 69646b6..e304e5f 100755 --- a/pkg/api/url.go +++ b/pkg/api/url.go @@ -7,7 +7,7 @@ func Add(router *mux.Router, switcher Switcher) { User{}.Router(router) Neighbor{}.Router(router) Point{}.Router(router) - Network{}.Router(router) + Network{Switcher: switcher}.Router(router) OnLine{}.Router(router) Lease{}.Router(router) Server{Switcher: switcher}.Router(router) diff --git a/pkg/config/switch.go b/pkg/config/switch.go index f2e9680..1d0b0d5 100755 --- a/pkg/config/switch.go +++ b/pkg/config/switch.go @@ -50,33 +50,34 @@ func (p *Perf) Correct() { } type Switch struct { - File string `json:"file"` - Alias string `json:"alias"` - Perf Perf `json:"limit,omitempty"` - Protocol string `json:"protocol"` // tcp, tls, udp, kcp, ws and wss. - Listen string `json:"listen"` - Timeout int `json:"timeout"` - Http *Http `json:"http,omitempty"` - Log Log `json:"log"` - Cert *Cert `json:"cert,omitempty"` - Crypt *Crypt `json:"crypt,omitempty"` - Network []*Network `json:"network,omitempty"` - Acl map[string]*ACL `json:"acl,omitempty"` - Qos map[string]*Qos `json:"qos,omitempty"` - FireWall []FlowRule `json:"firewall,omitempty"` - Queue Queue `json:"queue"` - PassFile string `json:"password"` - Ldap *LDAP `json:"ldap,omitempty"` - AddrPool string `json:"pool,omitempty"` - ConfDir string `json:"-"` - TokenFile string `json:"-"` - L2TP *L2TP `json:"l2tp"` + File string `json:"file"` + Alias string `json:"alias"` + Perf Perf `json:"limit,omitempty"` + Protocol string `json:"protocol"` // tcp, tls, udp, kcp, ws and wss. + Listen string `json:"listen"` + Timeout int `json:"timeout"` + Http *Http `json:"http,omitempty"` + Log Log `json:"log"` + Cert *Cert `json:"cert,omitempty"` + Crypt *Crypt `json:"crypt,omitempty"` + Network map[string]*Network `json:"network,omitempty"` + Acl map[string]*ACL `json:"acl,omitempty"` + Qos map[string]*Qos `json:"qos,omitempty"` + FireWall []FlowRule `json:"firewall,omitempty"` + Queue Queue `json:"queue"` + PassFile string `json:"password"` + Ldap *LDAP `json:"ldap,omitempty"` + AddrPool string `json:"pool,omitempty"` + ConfDir string `json:"-"` + TokenFile string `json:"-"` + L2TP *L2TP `json:"l2tp"` } func NewSwitch() *Switch { s := &Switch{ - Acl: make(map[string]*ACL, 32), - Qos: make(map[string]*Qos, 1024), + Acl: make(map[string]*ACL, 32), + Qos: make(map[string]*Qos, 1024), + Network: make(map[string]*Network, 32), } s.Parse() s.Initialize() @@ -155,19 +156,77 @@ func (s *Switch) Dir(elem ...string) string { return filepath.Join(args...) } -func (s *Switch) Format() { - for _, obj := range s.Network { - context := obj.Specifies - obj.NewSpecifies() - if obj.Specifies == nil { - continue - } - if data, err := libol.Marshal(context, true); err != nil { - libol.Warn("Switch.Format %s", err) - } else if err := libol.Unmarshal(obj.Specifies, data); err != nil { - libol.Warn("Switch.Format %s", err) - } +func (s *Switch) formatNetworkWithObj(obj *Network) { + context := obj.Specifies + obj.NewSpecifies() + if obj.Specifies == nil { + return } + if data, err := libol.Marshal(context, true); err != nil { + libol.Warn("Switch.Format %s", err) + } else if err := libol.Unmarshal(obj.Specifies, data); err != nil { + libol.Warn("Switch.Format %s", err) + } +} + +func (s *Switch) FormatNetwork() { + for _, obj := range s.Network { + s.formatNetworkWithObj(obj) + } +} + +func (s *Switch) LoadNetworkWithFile(file string) { + obj := &Network{ + Alias: s.Alias, + File: file, + ConfDir: s.ConfDir, + } + if err := libol.UnmarshalLoad(obj, file); err != nil { + libol.Error("Switch.LoadNetwork %s", err) + return + } + obj.LoadLink() + obj.LoadRoute() + obj.LoadOutput() + s.Network[obj.Name] = obj +} + +func (s *Switch) correctNetworkWithObj(obj *Network) { + for _, link := range obj.Links { + link.Correct() + } + obj.Correct(s) + obj.Alias = s.Alias + if obj.File == "" { + obj.File = s.Dir("network", obj.Name+".json") + } + if _, ok := s.Acl[obj.Name]; !ok { + obj := &ACL{ + Name: obj.Name, + } + obj.Correct(s) + s.Acl[obj.Name] = obj + } + if _, ok := s.Qos[obj.Name]; !ok { + obj := &Qos{ + Name: obj.Name, + } + obj.Correct(s) + s.Qos[obj.Name] = obj + } +} + +func (s *Switch) CorrectNetwork() { + for _, obj := range s.Network { + s.correctNetworkWithObj(obj) + } +} + +func (s *Switch) AddNetwork(file string) { + s.LoadNetworkWithFile(file) + net := s.GetNetworkWithFile(file) + s.formatNetworkWithObj(net) + s.correctNetworkWithObj(net) } func (s *Switch) LoadNetwork() { @@ -176,45 +235,10 @@ func (s *Switch) LoadNetwork() { libol.Error("Switch.LoadNetwork %s", err) } for _, k := range files { - obj := &Network{ - Alias: s.Alias, - File: k, - ConfDir: s.ConfDir, - } - if err := libol.UnmarshalLoad(obj, k); err != nil { - libol.Error("Switch.LoadNetwork %s", err) - continue - } - obj.LoadLink() - obj.LoadRoute() - obj.LoadOutput() - s.Network = append(s.Network, obj) - } - s.Format() - for _, obj := range s.Network { - for _, link := range obj.Links { - link.Correct() - } - obj.Correct(s) - obj.Alias = s.Alias - if obj.File == "" { - obj.File = s.Dir("network", obj.Name+".json") - } - if _, ok := s.Acl[obj.Name]; !ok { - obj := &ACL{ - Name: obj.Name, - } - obj.Correct(s) - s.Acl[obj.Name] = obj - } - if _, ok := s.Qos[obj.Name]; !ok { - obj := &Qos{ - Name: obj.Name, - } - obj.Correct(s) - s.Qos[obj.Name] = obj - } + s.LoadNetworkWithFile(k) } + s.FormatNetwork() + s.CorrectNetwork() } func (s *Switch) LoadQos() { @@ -304,8 +328,12 @@ func (s *Switch) Reload() { } func (s *Switch) GetNetwork(name string) *Network { + return s.Network[name] +} + +func (s *Switch) GetNetworkWithFile(file string) *Network { for _, obj := range s.Network { - if obj.Name == name { + if obj.File == file { return obj } } diff --git a/pkg/schema/network.go b/pkg/schema/network.go index cb28434..23bdd6e 100755 --- a/pkg/schema/network.go +++ b/pkg/schema/network.go @@ -24,6 +24,7 @@ type Subnet struct { } type Network struct { + File string `json:"file,omitempty"` Name string `json:"name"` Subnet Subnet `json:"subnet"` Routes []PrefixRoute `json:"routes"` diff --git a/pkg/switch/switch.go b/pkg/switch/switch.go index 8f5fb59..7aeeb3c 100755 --- a/pkg/switch/switch.go +++ b/pkg/switch/switch.go @@ -2,6 +2,7 @@ package cswitch import ( "encoding/json" + "os" "os/exec" "strconv" "strings" @@ -140,6 +141,46 @@ func (v *Switch) enablePort(protocol, port string) { }) } +func (v *Switch) AddNetwork(network string) { + for _, nCfg := range v.cfg.Network { + name := nCfg.Name + if name == network { + w := NewNetworker(nCfg) + v.worker[name] = w + if w.Provider() != "vxlan" { + w.Initialize() + w.Start(v) + } + } + } +} + +func (v *Switch) DelNetwork(network string) { + worker := v.worker[network] + file := worker.Config().File + if worker.Provider() != "vxlan" { + worker.Stop() + } + cache.Network.Del(network) + delete(v.worker, network) + delete(v.cfg.Network, network) + if err := os.Remove(file); err != nil { + v.out.Error("Error removing file: %s, err: %s", file, err) + } +} + +func (v *Switch) SaveNetwork(network string) { + if network == "" { + for _, obj := range v.cfg.Network { + obj.Save() + } + } else { + if obj := v.cfg.GetNetwork(network); obj != nil { + obj.Save() + } + } +} + func (v *Switch) preNetwork() { for _, nCfg := range v.cfg.Network { name := nCfg.Name