fea: support list/add/del/save output (#48)

This commit is contained in:
buliangjun
2024-04-01 11:21:04 +08:00
committed by GitHub
parent bb611dad29
commit dbe58aa126
13 changed files with 320 additions and 47 deletions

View File

@@ -47,4 +47,5 @@ func Commands(app *api.App) {
Log{}.Commands(app) Log{}.Commands(app)
Guest{}.Commands(app) Guest{}.Commands(app)
Knock{}.Commands(app) Knock{}.Commands(app)
Output{}.Commands(app)
} }

View File

@@ -144,6 +144,21 @@ func (u Config) Check(c *cli.Context) error {
} }
} }
} }
// Check output config
out.Info("%15s: %s", "check", "output")
pattern = filepath.Join(dir, "switch", "output", "*.json")
if files, err := filepath.Glob(pattern); err == nil {
for _, file := range files {
var obj []config.Output
if err := libol.UnmarshalLoad(&obj, file); err != nil {
out.Warn("%15s: %s", filepath.Base(file), err)
} else {
out.Info("%15s: %s", filepath.Base(file), "success")
}
}
}
return nil return nil
} }

135
cmd/api/v5/output.go Normal file
View File

@@ -0,0 +1,135 @@
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"
)
type Output struct {
Cmd
}
func (o Output) Url(prefix, name string) string {
return prefix + "/api/network/" + name + "/output"
}
func (o Output) Add(c *cli.Context) error {
network := c.String("network")
if len(network) == 0 {
return libol.NewErr("invalid network")
}
output := &schema.Output{
Network: network,
Remote: c.String("remote"),
Segment: c.Int("segment"),
Protocol: c.String("protocol"),
}
url := o.Url(c.String("url"), network)
clt := o.NewHttp(c.String("token"))
if err := clt.PostJSON(url, output, nil); err != nil {
return err
}
return nil
}
func (o Output) Remove(c *cli.Context) error {
network := c.String("network")
if len(network) == 0 {
return libol.NewErr("invalid network")
}
output := &schema.Output{
Network: network,
Device: c.String("device"),
}
url := o.Url(c.String("url"), network)
clt := o.NewHttp(c.String("token"))
if err := clt.DeleteJSON(url, output, nil); err != nil {
return err
}
return nil
}
func (o Output) Save(c *cli.Context) error {
network := c.String("network")
url := o.Url(c.String("url"), network)
clt := o.NewHttp(c.String("token"))
if err := clt.PutJSON(url, nil, nil); err != nil {
return err
}
return nil
}
func (o Output) Tmpl() string {
return `# total {{ len . }}
{{ps -24 "network"}} {{ps -15 "protocol"}} {{ps -15 "Remote"}} {{ps -15 "segment"}} {{ps -15 "device"}}
{{- range . }}
{{ps -24 .Network}} {{ps -15 .Protocol}} {{ps -15 .Remote}} {{pi -15 .Segment }} {{ps -15 .Device}}
{{- end }}
`
}
func (o Output) List(c *cli.Context) error {
url := o.Url(c.String("url"), c.String("network"))
clt := o.NewHttp(c.String("token"))
var items []schema.Output
if err := clt.GetJSON(url, &items); err != nil {
return err
}
return o.Out(items, c.String("format"), o.Tmpl())
}
func (o Output) Commands(app *api.App) {
app.Command(&cli.Command{
Name: "output",
Aliases: []string{"op"},
Usage: "Output configuration",
Subcommands: []*cli.Command{
{
Name: "add",
Usage: "Add an output for the network",
Flags: []cli.Flag{
&cli.StringFlag{Name: "network"},
&cli.StringFlag{Name: "remote"},
&cli.IntFlag{Name: "segment"},
&cli.StringFlag{Name: "protocol"},
//&cli.StringFlag{Name: "connection"},
//&cli.StringFlag{Name: "secret"},
//&cli.StringFlag{Name: "auth"},
},
Action: o.Add,
},
{
Name: "remove",
Usage: "Remove an output from the network",
Aliases: []string{"rm"},
Flags: []cli.Flag{
&cli.StringFlag{Name: "network"},
&cli.StringFlag{Name: "device"},
},
Action: o.Remove,
},
{
Name: "list",
Usage: "Display all outputs of the network",
Aliases: []string{"ls"},
Flags: []cli.Flag{
&cli.StringFlag{Name: "network", Required: true},
},
Action: o.List,
},
{
Name: "save",
Usage: "Save all outputs",
Aliases: []string{"sa"},
Flags: []cli.Flag{
&cli.StringFlag{Name: "network", Required: true},
},
Action: o.Save,
},
},
})
}

View File

@@ -45,22 +45,5 @@
}, },
"acl": "acl-100", "acl": "acl-100",
"dhcp": "enable", "dhcp": "enable",
"namespace": "example", "namespace": "example"
"outputs": [
{
"segment": 43,
"remote": "3.3.3.5",
"dstport": 8899,
"protocol": "vxlan"
},
{
"segment": 55,
"remote": "3.3.3.3",
"protocol": "gre"
},
{
"segment": 23,
"remote": "enp2s2"
}
]
} }

View File

@@ -12,21 +12,5 @@
"startAt": "192.168.55.100", "startAt": "192.168.55.100",
"endAt": "192.168.55.130" "endAt": "192.168.55.130"
}, },
"dhcp": "enable", "dhcp": "enable"
"outputs": [
{
"segment": 43,
"remote": "3.3.3.5",
"protocol": "vxlan"
},
{
"segment": 55,
"remote": "3.3.3.3",
"protocol": "gre"
},
{
"segment": 23,
"remote": "enp2s2"
}
]
} }

View File

@@ -0,0 +1,17 @@
[
{
"segment": 43,
"remote": "3.3.3.5",
"dstport": 8899,
"protocol": "vxlan"
},
{
"segment": 55,
"remote": "3.3.3.3",
"protocol": "gre"
},
{
"segment": 23,
"remote": "enp2s2"
}
]

View File

@@ -36,7 +36,7 @@ OpenLAN软件包含下面部分
{ {
"protocol": "tcp", "protocol": "tcp",
"crypt": { "crypt": {
"algo": "aes-128", ## 支持xor,aes-128,aes-192等对称加密算法 "algorithm": "aes-128", ## 支持xor,aes-128,aes-192等对称加密算法
"secret": "ea64d5b0c96c" "secret": "ea64d5b0c96c"
} }
} }

View File

@@ -55,6 +55,12 @@ type Qoser interface {
Save() Save()
} }
type Outputer interface {
AddOutput(segment int, protocol, Remote string)
DelOutput(device string)
SaveOutput()
}
type Networker interface { type Networker interface {
String() string String() string
ID() string ID() string
@@ -70,6 +76,7 @@ type Networker interface {
Qoser() Qoser Qoser() Qoser
IfAddr() string IfAddr() string
ACLer() ACLer ACLer() ACLer
Outputer
} }
var workers = make(map[string]Networker) var workers = make(map[string]Networker)

View File

@@ -1,6 +1,7 @@
package api package api
import ( import (
"fmt"
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@@ -17,6 +18,8 @@ type Output struct {
func (h Output) Router(router *mux.Router) { func (h Output) Router(router *mux.Router) {
router.HandleFunc("/api/network/{id}/output", h.Get).Methods("GET") router.HandleFunc("/api/network/{id}/output", h.Get).Methods("GET")
router.HandleFunc("/api/network/{id}/output", h.Post).Methods("POST") router.HandleFunc("/api/network/{id}/output", h.Post).Methods("POST")
router.HandleFunc("/api/network/{id}/output", h.Delete).Methods("DELETE")
router.HandleFunc("/api/network/{id}/output", h.Save).Methods("PUT")
} }
func (h Output) Get(w http.ResponseWriter, r *http.Request) { func (h Output) Get(w http.ResponseWriter, r *http.Request) {
@@ -35,5 +38,65 @@ func (h Output) Get(w http.ResponseWriter, r *http.Request) {
} }
func (h Output) Post(w http.ResponseWriter, r *http.Request) { func (h Output) Post(w http.ResponseWriter, r *http.Request) {
ResponseJson(w, "outputs") output := &schema.Output{}
if err := GetData(r, output); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
cs := h.Switcher.Config()
if cs.Network == nil {
http.Error(w, "switch has no network can not add output", http.StatusBadRequest)
return
}
network := cs.GetNetwork(output.Network)
if network == nil {
http.Error(w, fmt.Sprintf("switch has no network with %s can not add output", output.Network), http.StatusBadRequest)
return
}
worker := GetWorker(output.Network)
if worker == nil {
http.Error(w, "Network not found", http.StatusBadRequest)
return
}
worker.AddOutput(output.Segment, output.Protocol, output.Remote)
ResponseMsg(w, 0, "")
}
func (h Output) Delete(w http.ResponseWriter, r *http.Request) {
output := &schema.Output{}
if err := GetData(r, output); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
cs := h.Switcher.Config()
if cs.Network == nil {
http.Error(w, "switch has no network can not del output", http.StatusBadRequest)
return
}
network := cs.GetNetwork(output.Network)
if network == nil {
http.Error(w, fmt.Sprintf("switch has no network with %s can not del output", output.Network), http.StatusBadRequest)
return
}
worker := GetWorker(output.Network)
if worker == nil {
http.Error(w, "Network not found", http.StatusBadRequest)
return
}
worker.DelOutput(output.Device)
ResponseMsg(w, 0, "")
}
func (h Output) Save(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id := vars["id"]
worker := GetWorker(id)
if worker == nil {
http.Error(w, "Network not found", http.StatusBadRequest)
return
}
worker.SaveOutput()
ResponseJson(w, "success")
} }

View File

@@ -24,6 +24,6 @@ func Add(router *mux.Router, switcher Switcher) {
OpenAPI{}.Router(router) OpenAPI{}.Router(router)
ZTrust{}.Router(router) ZTrust{}.Router(router)
QosApi{}.Router(router) QosApi{}.Router(router)
Output{}.Router(router) Output{Switcher: switcher}.Router(router)
ACL{}.Router(router) ACL{}.Router(router)
} }

View File

@@ -22,7 +22,7 @@ type Network struct {
Acl string `json:"acl,omitempty"` Acl string `json:"acl,omitempty"`
Specifies interface{} `json:"specifies,omitempty"` Specifies interface{} `json:"specifies,omitempty"`
Dhcp string `json:"dhcp,omitempty"` Dhcp string `json:"dhcp,omitempty"`
Outputs []Output `json:"outputs"` Outputs []Output `json:"outputs,omitempty"`
ZTrust string `json:"ztrust"` ZTrust string `json:"ztrust"`
Qos string `json:"qos,omitempty"` Qos string `json:"qos,omitempty"`
Namespace string `json:"namespace"` Namespace string `json:"namespace"`
@@ -122,15 +122,26 @@ func (n *Network) LoadRoute() {
} }
} }
func (n *Network) LoadOutput() {
file := n.Dir("output", n.Name+".json")
if err := libol.FileExist(file); err == nil {
if err := libol.UnmarshalLoad(&n.Outputs, file); err != nil {
libol.Error("Network.LoadOutput... %n", err)
}
}
}
func (n *Network) Save() { func (n *Network) Save() {
obj := *n obj := *n
obj.Routes = nil obj.Routes = nil
obj.Links = nil obj.Links = nil
obj.Outputs = nil
if err := libol.MarshalSave(&obj, obj.File, true); err != nil { if err := libol.MarshalSave(&obj, obj.File, true); err != nil {
libol.Error("Network.Save %s %s", obj.Name, err) libol.Error("Network.Save %s %s", obj.Name, err)
} }
n.SaveRoute() n.SaveRoute()
n.SaveLink() n.SaveLink()
n.SaveOutput()
} }
func (n *Network) SaveRoute() { func (n *Network) SaveRoute() {
@@ -153,6 +164,16 @@ func (n *Network) SaveLink() {
} }
} }
func (n *Network) SaveOutput() {
file := n.Dir("output", n.Name+".json")
if n.Outputs == nil {
return
}
if err := libol.MarshalSave(n.Outputs, file, true); err != nil {
libol.Error("Network.SaveOutput %s %s", n.Name, err)
}
}
func (n *Network) Reload() { func (n *Network) Reload() {
switch n.Provider { switch n.Provider {
case "esp": case "esp":

View File

@@ -187,6 +187,7 @@ func (s *Switch) LoadNetwork() {
} }
obj.LoadLink() obj.LoadLink()
obj.LoadRoute() obj.LoadRoute()
obj.LoadOutput()
s.Network = append(s.Network, obj) s.Network = append(s.Network, obj)
} }
s.Format() s.Format()

View File

@@ -153,7 +153,7 @@ func (w *WorkerImpl) AddPhysical(bridge string, output string) {
} }
} }
func (w *WorkerImpl) AddOutput(bridge string, port *LinuxPort) { func (w *WorkerImpl) addOutput(bridge string, port *LinuxPort) {
cfg := port.cfg cfg := port.cfg
out := &models.Output{ out := &models.Output{
Network: w.cfg.Name, Network: w.cfg.Name,
@@ -210,11 +210,11 @@ func (w *WorkerImpl) AddOutput(bridge string, port *LinuxPort) {
} else { } else {
link, err := nl.LinkByName(cfg.Remote) link, err := nl.LinkByName(cfg.Remote)
if link == nil { if link == nil {
w.out.Error("WorkerImpl.AddOutput %s %s", cfg.Remote, err) w.out.Error("WorkerImpl.addOutput %s %s", cfg.Remote, err)
return return
} }
if err := nl.LinkSetUp(link); err != nil { if err := nl.LinkSetUp(link); err != nil {
w.out.Warn("WorkerImpl.AddOutput %s %s", cfg.Remote, err) w.out.Warn("WorkerImpl.addOutput %s %s", cfg.Remote, err)
} }
if cfg.Segment > 0 { if cfg.Segment > 0 {
@@ -246,7 +246,7 @@ func (w *WorkerImpl) AddOutput(bridge string, port *LinuxPort) {
out.Device = port.link out.Device = port.link
cache.Output.Add(port.link, out) cache.Output.Add(port.link, out)
w.out.Info("WorkerImpl.AddOutput %s %s", port.link, port.String()) w.out.Info("WorkerImpl.addOutput %s %s", port.link, port.String())
w.AddPhysical(bridge, port.link) w.AddPhysical(bridge, port.link)
} }
@@ -349,7 +349,7 @@ func (w *WorkerImpl) Start(v api.Switcher) {
port := &LinuxPort{ port := &LinuxPort{
cfg: output, cfg: output,
} }
w.AddOutput(cfg.Bridge.Name, port) w.addOutput(cfg.Bridge.Name, port)
w.outputs = append(w.outputs, port) w.outputs = append(w.outputs, port)
} }
@@ -425,9 +425,9 @@ func (w *WorkerImpl) DelPhysical(bridge string, output string) {
} }
} }
func (w *WorkerImpl) DelOutput(bridge string, port *LinuxPort) { func (w *WorkerImpl) delOutput(bridge string, port *LinuxPort) {
cfg := port.cfg cfg := port.cfg
w.out.Info("WorkerImpl.DelOutput %s %s", port.link, port.String()) w.out.Info("WorkerImpl.delOutput %s %s", port.link, port.String())
cache.Output.Del(port.link) cache.Output.Del(port.link)
w.DelPhysical(bridge, port.link) w.DelPhysical(bridge, port.link)
@@ -513,7 +513,7 @@ func (w *WorkerImpl) Stop() {
} }
for _, output := range w.outputs { for _, output := range w.outputs {
w.DelOutput(w.cfg.Bridge.Name, output) w.delOutput(w.cfg.Bridge.Name, output)
} }
w.outputs = nil w.outputs = nil
@@ -811,3 +811,49 @@ func (w *WorkerImpl) IfAddr() string {
func (w *WorkerImpl) ACLer() api.ACLer { func (w *WorkerImpl) ACLer() api.ACLer {
return w.acl return w.acl
} }
func (w *WorkerImpl) AddOutput(segment int, protocol, Remote string) {
output := co.Output{
Segment: segment,
Protocol: protocol,
Remote: Remote,
}
w.cfg.Outputs = append(w.cfg.Outputs, output)
port := &LinuxPort{
cfg: output,
}
w.addOutput(w.cfg.Bridge.Name, port)
w.outputs = append(w.outputs, port)
}
func (w *WorkerImpl) DelOutput(device string) {
var linuxport *LinuxPort
for _, v := range w.outputs {
if v.link == device {
linuxport = v
break
}
}
if linuxport == nil {
return
}
Outputs := make([]co.Output, 0, len(w.cfg.Outputs))
for _, v := range w.cfg.Outputs {
if v != linuxport.cfg {
Outputs = append(Outputs, v)
}
}
w.cfg.Outputs = Outputs
w.delOutput(w.cfg.Bridge.Name, linuxport)
outputs := make([]*LinuxPort, 0, len(w.outputs))
for _, v := range w.outputs {
if v != linuxport {
outputs = append(outputs, v)
}
}
w.outputs = outputs
}
func (w *WorkerImpl) SaveOutput() {
w.cfg.SaveOutput()
}