1 // Copyright (c) 2020 Cisco and/or its affiliates.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
32 "git.fd.io/govpp.git/adapter/socketclient"
33 "git.fd.io/govpp.git/binapi/vpe"
34 "git.fd.io/govpp.git/binapigen"
35 "git.fd.io/govpp.git/binapigen/vppapi"
41 apifiles, err := vppapi.Parse()
46 switch cmd := flag.Arg(0); cmd {
48 runServer(apifiles, ":7777")
50 showVPPAPI(os.Stdout, apifiles)
53 writeAsJSON(os.Stdout, apifiles)
57 for _, apifile := range apifiles {
58 if apifile.Name == f {
59 writeAsJSON(os.Stdout, apifile)
65 log.Fatalf("VPP API file %q not found", f)
77 log.Fatalf("invalid command: %q", cmd)
82 func writeAsJSON(w io.Writer, data interface{}) {
83 b, err := json.MarshalIndent(data, "", " ")
88 if _, err := w.Write(b); err != nil {
93 func showRPC(apifiles []*vppapi.File) {
94 for _, apifile := range apifiles {
95 fmt.Printf("%s.api\n", apifile.Name)
96 if apifile.Service == nil {
99 for _, rpc := range apifile.Service.RPCs {
103 reply = "stream " + reply
105 fmt.Printf(" rpc (%s) --> (%s)\n", req, reply)
110 func showVPPAPI(out io.Writer, apifiles []*vppapi.File) {
111 binapigen.SortFilesByImports(apifiles)
114 w := tabwriter.NewWriter(&buf, 0, 0, 3, ' ', 0)
115 fmt.Fprintf(w, "API\tOPTIONS\tCRC\tPATH\tIMPORTED\tTYPES\t\n")
117 for _, apifile := range apifiles {
118 importedTypes := binapigen.ListImportedTypes(apifiles, apifile)
120 for k, v := range apifile.Options {
121 options = append(options, fmt.Sprintf("%s=%v", k, v))
123 imports := fmt.Sprintf("%d apis, %2d types", len(apifile.Imports), len(importedTypes))
124 path := strings.TrimPrefix(apifile.Path, vppapi.DefaultDir+"/")
125 types := fmt.Sprintf("%2d enum, %2d alias, %2d struct, %2d union, %2d msg",
126 len(apifile.EnumTypes), len(apifile.AliasTypes), len(apifile.StructTypes), len(apifile.UnionTypes), len(apifile.Messages))
127 fmt.Fprintf(w, " %s\t%s\t%s\t%s\t%v\t%s\t\n",
128 apifile.Name, strings.Join(options, " "), apifile.CRC, path, imports, types)
131 if err := w.Flush(); err != nil {
134 fmt.Fprint(out, buf.String())
137 func sendCLI(args []string) {
138 cmd := strings.Join(args, " ")
139 fmt.Printf("# %s\n", cmd)
141 conn, err := govpp.Connect("/run/vpp/api.sock")
145 defer conn.Disconnect()
147 ch, err := conn.NewAPIChannel()
153 if err := ch.CheckCompatiblity(vpe.AllMessages()...); err != nil {
157 client := vpe.NewServiceClient(conn)
158 reply, err := client.CliInband(context.Background(), &vpe.CliInband{
165 fmt.Print(reply.Reply)
168 func runServer(apifiles []*vppapi.File, addr string) {
169 apiRoutes(apifiles, http.DefaultServeMux)
171 conn, err := govpp.Connect(socketclient.DefaultSocketName)
176 vpeRPC := vpe.NewServiceClient(conn)
177 c := vpe.RESTHandler(vpeRPC)
181 log.Printf("listening on %v", addr)
183 if err := http.ListenAndServe(addr, nil); err != nil {
188 func apiRoutes(apifiles []*vppapi.File, mux *http.ServeMux) {
189 for _, apifile := range apifiles {
191 mux.HandleFunc("/vppapi/"+name, apiFileHandler(apifile))
192 mux.HandleFunc("/raw/"+name, rawHandler(apifile))
193 mux.HandleFunc("/rpc/"+name, rpcHandler(apifile))
195 mux.HandleFunc("/vppapi", apiHandler(apifiles))
198 func rpcHandler(apifile *vppapi.File) func(http.ResponseWriter, *http.Request) {
199 return func(w http.ResponseWriter, req *http.Request) {
200 msgName := strings.TrimPrefix(req.URL.Path, "/rpc/"+apifile.Name+"/")
202 http.Error(w, "no message name", 500)
206 input, err := ioutil.ReadAll(req.Body)
208 http.Error(w, err.Error(), 500)
212 msgReq := make(map[string]interface{})
213 err = json.Unmarshal(input, &msgReq)
215 http.Error(w, err.Error(), 500)
219 var msg *vppapi.Message
220 for _, m := range apifile.Messages {
221 if m.Name == msgName {
227 http.Error(w, "unknown message name: "+msgName, 500)
234 func apiHandler(apifiles []*vppapi.File) func(http.ResponseWriter, *http.Request) {
235 return func(w http.ResponseWriter, req *http.Request) {
236 b, err := json.MarshalIndent(apifiles, "", " ")
238 http.Error(w, err.Error(), 500)
245 func apiFileHandler(apifile *vppapi.File) func(http.ResponseWriter, *http.Request) {
246 return func(w http.ResponseWriter, req *http.Request) {
247 b, err := json.MarshalIndent(apifile, "", " ")
249 http.Error(w, err.Error(), 500)
256 func rawHandler(apifile *vppapi.File) func(http.ResponseWriter, *http.Request) {
257 return func(w http.ResponseWriter, req *http.Request) {
258 b, err := ioutil.ReadFile(apifile.Path)
260 http.Error(w, err.Error(), 500)