go.ligato.io/vpp-agent/v3@v3.5.0/plugins/govppmux/client_binapi.go (about) 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 govppmux 16 17 import ( 18 "context" 19 "runtime/trace" 20 "time" 21 22 govppapi "go.fd.io/govpp/api" 23 "go.fd.io/govpp/core" 24 "go.ligato.io/cn-infra/v2/logging" 25 ) 26 27 func (p *Plugin) NewStream(ctx context.Context, options ...govppapi.StreamOption) (govppapi.Stream, error) { 28 return p.vppConn.NewStream(ctx, options...) 29 } 30 31 func (p *Plugin) Invoke(ctx context.Context, req govppapi.Message, reply govppapi.Message) error { 32 return p.vppConn.Invoke(ctx, req, reply) 33 } 34 35 func (p *Plugin) WatchEvent(ctx context.Context, event govppapi.Message) (govppapi.Watcher, error) { 36 return p.vppConn.WatchEvent(ctx, event) 37 } 38 39 // NewAPIChannel returns a new API channel for communication with VPP via govpp core. 40 // It uses default buffer sizes for the request and reply Go channels. 41 func (p *Plugin) NewAPIChannel() (govppapi.Channel, error) { 42 ch, err := p.vppConn.NewAPIChannel() 43 if err != nil { 44 return nil, err 45 } 46 retryCfg := retryConfig{ 47 p.config.RetryRequestCount, 48 p.config.RetryRequestTimeout, 49 } 50 return newGovppChan(ch, retryCfg), nil 51 } 52 53 // NewAPIChannelBuffered returns a new API channel for communication with VPP via govpp core. 54 // It allows to specify custom buffer sizes for the request and reply Go channels. 55 func (p *Plugin) NewAPIChannelBuffered(reqChanBufSize, replyChanBufSize int) (govppapi.Channel, error) { 56 ch, err := p.vppConn.NewAPIChannelBuffered(reqChanBufSize, replyChanBufSize) 57 if err != nil { 58 return nil, err 59 } 60 retryCfg := retryConfig{ 61 p.config.RetryRequestCount, 62 p.config.RetryRequestTimeout, 63 } 64 return newGovppChan(ch, retryCfg), nil 65 } 66 67 // goVppChan implements govpp channel interface. Instance is returned by NewAPIChannel() or NewAPIChannelBuffered(), 68 // and contains *govpp.channel dynamic type (vppChan field). Implemented methods allow custom handling of low-level 69 // govpp. 70 type goVppChan struct { 71 govppapi.Channel 72 // Retry data 73 retry retryConfig 74 } 75 76 func newGovppChan(ch govppapi.Channel, retryCfg retryConfig) *goVppChan { 77 govppChan := &goVppChan{ 78 Channel: ch, 79 retry: retryCfg, 80 } 81 reportChannelsOpened() 82 return govppChan 83 } 84 85 func (c *goVppChan) Close() { 86 c.Channel.Close() 87 reportChannelsClosed() 88 } 89 90 // helper struct holding info about retry configuration 91 type retryConfig struct { 92 attempts int 93 timeout time.Duration 94 } 95 96 // govppRequestCtx is custom govpp RequestCtx. 97 type govppRequestCtx struct { 98 ctx context.Context 99 task *trace.Task 100 101 // Original request context 102 requestCtx govppapi.RequestCtx 103 // Function allowing to re-send request in case it's granted by the config file 104 sendRequest func(govppapi.Message) govppapi.RequestCtx 105 // Parameter for sendRequest 106 requestMsg govppapi.Message 107 108 retry retryConfig 109 start time.Time 110 } 111 112 // govppMultirequestCtx is custom govpp MultiRequestCtx. 113 type govppMultirequestCtx struct { 114 ctx context.Context 115 task *trace.Task 116 117 // Original multi request context 118 requestCtx govppapi.MultiRequestCtx 119 // Parameter for sendRequest 120 requestMsg govppapi.Message 121 122 start time.Time 123 } 124 125 // SendRequest sends asynchronous request to the vpp and receives context used to receive reply. 126 // Plugin govppmux allows to re-send retry which failed because of disconnected vpp, if enabled. 127 func (c *goVppChan) SendRequest(request govppapi.Message) govppapi.RequestCtx { 128 ctx, task := trace.NewTask(context.Background(), "govpp.SendRequest") 129 trace.Log(ctx, "messageName", request.GetMessageName()) 130 131 start := time.Now() 132 // Send request now and wait for context 133 requestCtx := c.Channel.SendRequest(request) 134 135 reportRequestSent(request) 136 137 // Return context with value and function which allows to send request again if needed 138 return &govppRequestCtx{ 139 ctx: ctx, 140 task: task, 141 requestCtx: requestCtx, 142 sendRequest: c.Channel.SendRequest, 143 requestMsg: request, 144 retry: c.retry, 145 start: start, 146 } 147 } 148 149 // ReceiveReply handles request and returns error if occurred. Also does retry if this option is available. 150 func (r *govppRequestCtx) ReceiveReply(reply govppapi.Message) error { 151 defer r.task.End() 152 153 var timeout time.Duration 154 attempts := r.retry.attempts 155 if r.retry.timeout > 0 { // Default value is 500ms 156 timeout = r.retry.timeout 157 } 158 // Receive reply from original send 159 err := r.requestCtx.ReceiveReply(reply) 160 for retry := 1; err == core.ErrNotConnected; retry++ { 161 if retry > attempts { 162 break // max attempts exceeded 163 } 164 logging.Warnf("govppmux: request retry (%d/%d), message %s in %v", 165 retry, attempts, r.requestMsg.GetMessageName(), timeout) 166 // Wait before next attempt 167 time.Sleep(timeout) 168 // Retry request 169 trace.Logf(r.ctx, "requestRetry", "%d/%d", retry, attempts) 170 err = r.sendRequest(r.requestMsg).ReceiveReply(reply) 171 } 172 if err != nil { 173 reportRequestFailed(reply, err) 174 } else { 175 reportRequestSuccess(r.requestMsg, r.start) 176 reportRepliesReceived(reply) 177 } 178 return err 179 } 180 181 // SendMultiRequest sends asynchronous request to the vpp and receives context used to receive reply. 182 func (c *goVppChan) SendMultiRequest(request govppapi.Message) govppapi.MultiRequestCtx { 183 ctx, task := trace.NewTask(context.Background(), "govpp.SendMultiRequest") 184 trace.Log(ctx, "msgName", request.GetMessageName()) 185 186 start := time.Now() 187 // Send request now and wait for context 188 requestCtx := c.Channel.SendMultiRequest(request) 189 190 reportRequestSent(request) 191 192 // Return context with value and function which allows to send request again if needed 193 return &govppMultirequestCtx{ 194 ctx: ctx, 195 task: task, 196 requestCtx: requestCtx, 197 requestMsg: request, 198 start: start, 199 } 200 } 201 202 // ReceiveReply handles request and returns error if occurred. 203 func (r *govppMultirequestCtx) ReceiveReply(reply govppapi.Message) (bool, error) { 204 // Receive reply from original send 205 last, err := r.requestCtx.ReceiveReply(reply) 206 if last || err != nil { 207 defer r.task.End() 208 if err != nil { 209 reportRequestFailed(r.requestMsg, err) 210 } else { 211 reportRequestSuccess(r.requestMsg, r.start) 212 } 213 } else { 214 reportRepliesReceived(reply) 215 } 216 return last, err 217 }