diff options
Diffstat (limited to 'cmd/govpp/main.go')
-rw-r--r-- | cmd/govpp/main.go | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/cmd/govpp/main.go b/cmd/govpp/main.go new file mode 100644 index 0000000..f1ad5d8 --- /dev/null +++ b/cmd/govpp/main.go @@ -0,0 +1,265 @@ +// Copyright (c) 2020 Cisco and/or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "bytes" + "context" + "encoding/json" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "strings" + "text/tabwriter" + + "git.fd.io/govpp.git" + "git.fd.io/govpp.git/adapter/socketclient" + "git.fd.io/govpp.git/binapi/vpe" + "git.fd.io/govpp.git/binapigen" + "git.fd.io/govpp.git/binapigen/vppapi" +) + +func main() { + flag.Parse() + + apifiles, err := vppapi.Parse() + if err != nil { + log.Fatal(err) + } + + switch cmd := flag.Arg(0); cmd { + case "server": + runServer(apifiles, ":7777") + case "vppapi": + showVPPAPI(os.Stdout, apifiles) + case "vppapijson": + if flag.NArg() == 1 { + writeAsJSON(os.Stdout, apifiles) + } else { + f := flag.Arg(1) + var found bool + for _, apifile := range apifiles { + if apifile.Name == f { + writeAsJSON(os.Stdout, apifile) + found = true + break + } + } + if !found { + log.Fatalf("VPP API file %q not found", f) + } + } + case "rpc": + showRPC(apifiles) + case "cli": + args := flag.Args() + if len(args) == 0 { + args = []string{"?"} + } + sendCLI(args[1:]) + default: + log.Fatalf("invalid command: %q", cmd) + } + +} + +func writeAsJSON(w io.Writer, data interface{}) { + b, err := json.MarshalIndent(data, "", " ") + if err != nil { + log.Fatal(err) + return + } + if _, err := w.Write(b); err != nil { + panic(err) + } +} + +func showRPC(apifiles []*vppapi.File) { + for _, apifile := range apifiles { + fmt.Printf("%s.api\n", apifile.Name) + if apifile.Service == nil { + continue + } + for _, rpc := range apifile.Service.RPCs { + req := rpc.Request + reply := rpc.Reply + if rpc.Stream { + reply = "stream " + reply + } + fmt.Printf(" rpc (%s) --> (%s)\n", req, reply) + } + } +} + +func showVPPAPI(out io.Writer, apifiles []*vppapi.File) { + binapigen.SortFilesByImports(apifiles) + + var buf bytes.Buffer + w := tabwriter.NewWriter(&buf, 0, 0, 3, ' ', 0) + fmt.Fprintf(w, "API\tOPTIONS\tCRC\tPATH\tIMPORTED\tTYPES\t\n") + + for _, apifile := range apifiles { + importedTypes := binapigen.ListImportedTypes(apifiles, apifile) + var options []string + for k, v := range apifile.Options { + options = append(options, fmt.Sprintf("%s=%v", k, v)) + } + imports := fmt.Sprintf("%d apis, %2d types", len(apifile.Imports), len(importedTypes)) + path := strings.TrimPrefix(apifile.Path, vppapi.DefaultDir+"/") + types := fmt.Sprintf("%2d enum, %2d alias, %2d struct, %2d union, %2d msg", + len(apifile.EnumTypes), len(apifile.AliasTypes), len(apifile.StructTypes), len(apifile.UnionTypes), len(apifile.Messages)) + fmt.Fprintf(w, " %s\t%s\t%s\t%s\t%v\t%s\t\n", + apifile.Name, strings.Join(options, " "), apifile.CRC, path, imports, types) + } + + if err := w.Flush(); err != nil { + log.Fatal(err) + } + fmt.Fprint(out, buf.String()) +} + +func sendCLI(args []string) { + cmd := strings.Join(args, " ") + fmt.Printf("# %s\n", cmd) + + conn, err := govpp.Connect("/run/vpp/api.sock") + if err != nil { + log.Fatal(err) + } + defer conn.Disconnect() + + ch, err := conn.NewAPIChannel() + if err != nil { + log.Fatal(err) + } + defer ch.Close() + + if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil { + log.Fatal(err) + } + + client := vpe.NewServiceClient(conn) + reply, err := client.CliInband(context.Background(), &vpe.CliInband{ + Cmd: cmd, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Print(reply.Reply) +} + +func runServer(apifiles []*vppapi.File, addr string) { + apiRoutes(apifiles, http.DefaultServeMux) + + conn, err := govpp.Connect(socketclient.DefaultSocketName) + if err != nil { + log.Fatal(err) + } + + vpeRPC := vpe.NewServiceClient(conn) + c := vpe.RESTHandler(vpeRPC) + + http.Handle("/", c) + + log.Printf("listening on %v", addr) + + if err := http.ListenAndServe(addr, nil); err != nil { + log.Fatal(err) + } +} + +func apiRoutes(apifiles []*vppapi.File, mux *http.ServeMux) { + for _, apifile := range apifiles { + name := apifile.Name + mux.HandleFunc("/vppapi/"+name, apiFileHandler(apifile)) + mux.HandleFunc("/raw/"+name, rawHandler(apifile)) + mux.HandleFunc("/rpc/"+name, rpcHandler(apifile)) + } + mux.HandleFunc("/vppapi", apiHandler(apifiles)) +} + +func rpcHandler(apifile *vppapi.File) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, req *http.Request) { + msgName := strings.TrimPrefix(req.URL.Path, "/rpc/"+apifile.Name+"/") + if msgName == "" { + http.Error(w, "no message name", 500) + return + } + + input, err := ioutil.ReadAll(req.Body) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + msgReq := make(map[string]interface{}) + err = json.Unmarshal(input, &msgReq) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + var msg *vppapi.Message + for _, m := range apifile.Messages { + if m.Name == msgName { + msg = &m + break + } + } + if msg == nil { + http.Error(w, "unknown message name: "+msgName, 500) + return + } + + } +} + +func apiHandler(apifiles []*vppapi.File) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, req *http.Request) { + b, err := json.MarshalIndent(apifiles, "", " ") + if err != nil { + http.Error(w, err.Error(), 500) + return + } + w.Write(b) + } +} + +func apiFileHandler(apifile *vppapi.File) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, req *http.Request) { + b, err := json.MarshalIndent(apifile, "", " ") + if err != nil { + http.Error(w, err.Error(), 500) + return + } + w.Write(b) + } +} + +func rawHandler(apifile *vppapi.File) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, req *http.Request) { + b, err := ioutil.ReadFile(apifile.Path) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + w.Write(b) + } +} |