aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/govpp/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/govpp/main.go')
-rw-r--r--cmd/govpp/main.go265
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)
+ }
+}