6f29c7191e719c1ad6befe53f988337db282c7a2
[govpp.git] / proxy / client.go
1 //  Copyright (c) 2019 Cisco and/or its affiliates.
2 //
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:
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
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.
14
15 package proxy
16
17 import (
18         "fmt"
19         "net/rpc"
20         "reflect"
21         "time"
22
23         "git.fd.io/govpp.git/api"
24         "git.fd.io/govpp.git/core"
25 )
26
27 type Client struct {
28         serverAddr string
29         rpc        *rpc.Client
30 }
31
32 // Connect dials remote proxy server on given address and
33 // returns new client if successful.
34 func Connect(addr string) (*Client, error) {
35         client, err := rpc.DialHTTP("tcp", addr)
36         if err != nil {
37                 return nil, fmt.Errorf("connection error:%v", err)
38         }
39         c := &Client{
40                 serverAddr: addr,
41                 rpc:        client,
42         }
43         return c, nil
44 }
45
46 // NewStatsClient returns new StatsClient which implements api.StatsProvider.
47 func (c *Client) NewStatsClient() (*StatsClient, error) {
48         stats := &StatsClient{
49                 rpc: c.rpc,
50         }
51         return stats, nil
52 }
53
54 // NewBinapiClient returns new BinapiClient which implements api.Channel.
55 func (c *Client) NewBinapiClient() (*BinapiClient, error) {
56         binapi := &BinapiClient{
57                 rpc:     c.rpc,
58                 timeout: core.DefaultReplyTimeout,
59         }
60         return binapi, nil
61 }
62
63 type StatsClient struct {
64         rpc *rpc.Client
65 }
66
67 func (s *StatsClient) GetSystemStats(sysStats *api.SystemStats) error {
68         // we need to start with a clean, zeroed item before decoding
69         // 'cause if the new values are 'zero' for the type, they will be ignored
70         // by the decoder. (i.e the old values will be left unchanged).
71         req := StatsRequest{StatsType: "system"}
72         resp := StatsResponse{SysStats: new(api.SystemStats)}
73         if err := s.rpc.Call("StatsRPC.GetStats", req, &resp); err != nil {
74                 return err
75         }
76         *sysStats = *resp.SysStats
77         return nil
78 }
79
80 func (s *StatsClient) GetNodeStats(nodeStats *api.NodeStats) error {
81         req := StatsRequest{StatsType: "node"}
82         resp := StatsResponse{NodeStats: new(api.NodeStats)}
83         if err := s.rpc.Call("StatsRPC.GetStats", req, &resp); err != nil {
84                 return err
85         }
86         *nodeStats = *resp.NodeStats
87         return nil
88 }
89
90 func (s *StatsClient) GetInterfaceStats(ifaceStats *api.InterfaceStats) error {
91         req := StatsRequest{StatsType: "interface"}
92         resp := StatsResponse{IfaceStats: new(api.InterfaceStats)}
93         if err := s.rpc.Call("StatsRPC.GetStats", req, &resp); err != nil {
94                 return err
95         }
96         *ifaceStats = *resp.IfaceStats
97         return nil
98 }
99
100 func (s *StatsClient) GetErrorStats(errStats *api.ErrorStats) error {
101         req := StatsRequest{StatsType: "error"}
102         resp := StatsResponse{ErrStats: new(api.ErrorStats)}
103         if err := s.rpc.Call("StatsRPC.GetStats", req, &resp); err != nil {
104                 return err
105         }
106         *errStats = *resp.ErrStats
107         return nil
108 }
109
110 func (s *StatsClient) GetBufferStats(bufStats *api.BufferStats) error {
111         req := StatsRequest{StatsType: "buffer"}
112         resp := StatsResponse{BufStats: new(api.BufferStats)}
113         if err := s.rpc.Call("StatsRPC.GetStats", req, &resp); err != nil {
114                 return err
115         }
116         *bufStats = *resp.BufStats
117         return nil
118 }
119
120 type BinapiClient struct {
121         rpc     *rpc.Client
122         timeout time.Duration
123 }
124
125 func (b *BinapiClient) SendRequest(msg api.Message) api.RequestCtx {
126         req := &requestCtx{
127                 rpc:     b.rpc,
128                 timeout: b.timeout,
129                 req:     msg,
130         }
131         log.Debugf("SendRequest: %T %+v", msg, msg)
132         return req
133 }
134
135 type requestCtx struct {
136         rpc     *rpc.Client
137         req     api.Message
138         timeout time.Duration
139 }
140
141 func (r *requestCtx) ReceiveReply(msg api.Message) error {
142         req := BinapiRequest{
143                 Msg:      r.req,
144                 ReplyMsg: msg,
145                 Timeout:  r.timeout,
146         }
147         resp := BinapiResponse{}
148
149         err := r.rpc.Call("BinapiRPC.Invoke", req, &resp)
150         if err != nil {
151                 return fmt.Errorf("RPC call failed: %v", err)
152         }
153
154         // we set the value of msg to the value from response
155         reflect.ValueOf(msg).Elem().Set(reflect.ValueOf(resp.Msg).Elem())
156
157         return nil
158 }
159
160 func (b *BinapiClient) SendMultiRequest(msg api.Message) api.MultiRequestCtx {
161         req := &multiRequestCtx{
162                 rpc:     b.rpc,
163                 timeout: b.timeout,
164                 req:     msg,
165         }
166         log.Debugf("SendMultiRequest: %T %+v", msg, msg)
167         return req
168 }
169
170 type multiRequestCtx struct {
171         rpc     *rpc.Client
172         req     api.Message
173         timeout time.Duration
174
175         index   int
176         replies []api.Message
177 }
178
179 func (r *multiRequestCtx) ReceiveReply(msg api.Message) (stop bool, err error) {
180         // we call Invoke only on first ReceiveReply
181         if r.index == 0 {
182                 req := BinapiRequest{
183                         Msg:      r.req,
184                         ReplyMsg: msg,
185                         IsMulti:  true,
186                         Timeout:  r.timeout,
187                 }
188                 resp := BinapiResponse{}
189
190                 err := r.rpc.Call("BinapiRPC.Invoke", req, &resp)
191                 if err != nil {
192                         return false, fmt.Errorf("RPC call failed: %v", err)
193                 }
194
195                 r.replies = resp.Msgs
196         }
197
198         if r.index >= len(r.replies) {
199                 return true, nil
200         }
201
202         // we set the value of msg to the value from response
203         reflect.ValueOf(msg).Elem().Set(reflect.ValueOf(r.replies[r.index]).Elem())
204         r.index++
205
206         return false, nil
207 }
208
209 func (b *BinapiClient) SubscribeNotification(notifChan chan api.Message, event api.Message) (api.SubscriptionCtx, error) {
210         panic("implement me")
211 }
212
213 func (b *BinapiClient) SetReplyTimeout(timeout time.Duration) {
214         b.timeout = timeout
215 }
216
217 func (b *BinapiClient) CheckCompatiblity(msgs ...api.Message) error {
218         msgNamesCrscs := make([]string, 0, len(msgs))
219
220         for _, msg := range msgs {
221                 msgNamesCrscs = append(msgNamesCrscs, msg.GetMessageName()+"_"+msg.GetCrcString())
222         }
223
224         req := BinapiCompatibilityRequest{MsgNameCrcs: msgNamesCrscs}
225         resp := BinapiCompatibilityResponse{}
226
227         if err := b.rpc.Call("BinapiRPC.Compatibility", req, &resp); err != nil {
228                 return err
229         }
230
231         return nil
232 }
233
234 func (b *BinapiClient) Close() {
235         b.rpc.Close()
236 }