blob: 94ad64592ffdd726f5eb6c72f703891c65fdc517 [file] [log] [blame]
David K. Bainbridge157bdab2020-01-16 14:38:05 -08001/*
2 Copyright 2020 the original author or authors.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17package openflow
18
19import (
David K. Bainbridge55376262020-01-22 23:28:27 -080020 "bufio"
David K. Bainbridge157bdab2020-01-16 14:38:05 -080021 "context"
22 "encoding/binary"
23 "encoding/json"
24 "errors"
25 "github.com/donNewtonAlpha/goloxi"
26 ofp "github.com/donNewtonAlpha/goloxi/of13"
David K. Bainbridgeaea73cd2020-01-27 10:44:50 -080027 "github.com/opencord/voltha-lib-go/v3/pkg/log"
28 "github.com/opencord/voltha-protos/v3/go/voltha"
David K. Bainbridge157bdab2020-01-16 14:38:05 -080029 "io"
30 "net"
31 "time"
32)
33
David K. Bainbridge9cb404e2020-01-28 14:32:29 -080034var NoVolthaConnectionError = errors.New("no-voltha-connection")
David K. Bainbridge157bdab2020-01-16 14:38:05 -080035
36type ofcEvent byte
37type ofcState byte
38
39const (
40 ofcEventStart = ofcEvent(iota)
David K. Bainbridge55376262020-01-22 23:28:27 -080041 ofcEventConnect
42 ofcEventDisconnect
43 ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -080044
David K. Bainbridge55376262020-01-22 23:28:27 -080045 ofcStateCreated = ofcState(iota)
46 ofcStateStarted
47 ofcStateConnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -080048 ofcStateDisconnected
David K. Bainbridge55376262020-01-22 23:28:27 -080049 ofcStateStopped
David K. Bainbridge157bdab2020-01-16 14:38:05 -080050)
51
David K. Bainbridge55376262020-01-22 23:28:27 -080052func (e ofcEvent) String() string {
53 switch e {
54 case ofcEventStart:
55 return "ofc-event-start"
56 case ofcEventConnect:
57 return "ofc-event-connected"
58 case ofcEventDisconnect:
59 return "ofc-event-disconnected"
60 case ofcEventStop:
61 return "ofc-event-stop"
62 default:
63 return "ofc-event-unknown"
64 }
65}
66
67func (s ofcState) String() string {
68 switch s {
69 case ofcStateCreated:
70 return "ofc-state-created"
71 case ofcStateStarted:
72 return "ofc-state-started"
73 case ofcStateConnected:
74 return "ofc-state-connected"
75 case ofcStateDisconnected:
76 return "ofc-state-disconnected"
77 case ofcStateStopped:
78 return "ofc-state-stopped"
79 default:
80 return "ofc-state-unknown"
81 }
82}
83
84// OFClient the configuration and operational state of a connection to an
85// openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -080086type OFClient struct {
87 OFControllerEndPoint string
88 Port uint16
89 DeviceID string
David K. Bainbridge157bdab2020-01-16 14:38:05 -080090 VolthaClient voltha.VolthaServiceClient
91 PacketOutChannel chan *voltha.PacketOut
92 ConnectionMaxRetries int
93 ConnectionRetryDelay time.Duration
94 conn net.Conn
95
96 // expirimental
97 events chan ofcEvent
98 sendChannel chan Message
99 lastUnsentMessage Message
100}
101
David K. Bainbridge55376262020-01-22 23:28:27 -0800102// NewClient returns an initialized OFClient instance based on the configuration
103// specified
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800104func NewOFClient(config *OFClient) *OFClient {
105
106 ofc := OFClient{
107 DeviceID: config.DeviceID,
108 OFControllerEndPoint: config.OFControllerEndPoint,
109 VolthaClient: config.VolthaClient,
110 PacketOutChannel: config.PacketOutChannel,
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800111 ConnectionMaxRetries: config.ConnectionMaxRetries,
112 ConnectionRetryDelay: config.ConnectionRetryDelay,
113 events: make(chan ofcEvent, 10),
114 sendChannel: make(chan Message, 100),
115 }
116
117 if ofc.ConnectionRetryDelay <= 0 {
118 logger.Warnw("connection retry delay not valid, setting to default",
119 log.Fields{
120 "device-id": ofc.DeviceID,
121 "value": ofc.ConnectionRetryDelay.String(),
122 "default": (3 * time.Second).String()})
123 ofc.ConnectionRetryDelay = 3 * time.Second
124 }
125 return &ofc
126}
127
David K. Bainbridge55376262020-01-22 23:28:27 -0800128// Stop initiates a shutdown of the OFClient
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800129func (ofc *OFClient) Stop() {
David K. Bainbridge55376262020-01-22 23:28:27 -0800130 ofc.events <- ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800131}
132
133func (ofc *OFClient) peekAtOFHeader(buf []byte) (ofp.IHeader, error) {
134 header := ofp.Header{}
135 header.Version = uint8(buf[0])
136 header.Type = uint8(buf[1])
137 header.Length = binary.BigEndian.Uint16(buf[2:4])
138 header.Xid = binary.BigEndian.Uint32(buf[4:8])
139
140 // TODO: add minimal validation of version and type
141
142 return &header, nil
143}
144
145func (ofc *OFClient) establishConnectionToController() error {
146 if ofc.conn != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800147 logger.Debugw("closing-of-connection-to-reconnect",
148 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800149 ofc.conn.Close()
150 ofc.conn = nil
151 }
152 try := 1
153 for ofc.ConnectionMaxRetries == 0 || try < ofc.ConnectionMaxRetries {
154 if raddr, err := net.ResolveTCPAddr("tcp", ofc.OFControllerEndPoint); err != nil {
155 logger.Debugw("openflow-client unable to resolve endpoint",
156 log.Fields{
157 "device-id": ofc.DeviceID,
158 "endpoint": ofc.OFControllerEndPoint})
159 } else {
160 if connection, err := net.DialTCP("tcp", nil, raddr); err == nil {
161 ofc.conn = connection
162 ofc.sayHello()
David K. Bainbridge55376262020-01-22 23:28:27 -0800163 ofc.events <- ofcEventConnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800164 return nil
165 } else {
166 logger.Warnw("openflow-client-connect-error",
167 log.Fields{
168 "device-id": ofc.DeviceID,
169 "endpoint": ofc.OFControllerEndPoint})
170 }
171 }
172 if ofc.ConnectionMaxRetries == 0 || try < ofc.ConnectionMaxRetries {
173 if ofc.ConnectionMaxRetries != 0 {
174 try += 1
175 }
176 time.Sleep(ofc.ConnectionRetryDelay)
177 }
178 }
179 return errors.New("failed-to-connect-to-of-controller")
180}
181
David K. Bainbridge55376262020-01-22 23:28:27 -0800182// Run implementes the state machine for the OF client reacting to state change
183// events and invoking actions as a reaction to those state changes
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800184func (ofc *OFClient) Run(ctx context.Context) {
185
186 var ofCtx context.Context
187 var ofDone func()
David K. Bainbridge55376262020-01-22 23:28:27 -0800188 state := ofcStateCreated
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800189 ofc.events <- ofcEventStart
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800190top:
191 for {
192 select {
193 case <-ctx.Done():
David K. Bainbridge55376262020-01-22 23:28:27 -0800194 state = ofcStateStopped
195 logger.Debugw("state-transition-context-done",
196 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800197 break top
198 case event := <-ofc.events:
David K. Bainbridge55376262020-01-22 23:28:27 -0800199 previous := state
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800200 switch event {
201 case ofcEventStart:
David K. Bainbridge55376262020-01-22 23:28:27 -0800202 logger.Debugw("ofc-event-start",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800203 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge55376262020-01-22 23:28:27 -0800204 if state == ofcStateCreated {
205 state = ofcStateStarted
206 logger.Debug("STARTED MORE THAN ONCE")
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800207 go func() {
208 if err := ofc.establishConnectionToController(); err != nil {
209 logger.Errorw("controller-connection-failed", log.Fields{"error": err})
210 panic(err)
211 }
212 }()
David K. Bainbridge55376262020-01-22 23:28:27 -0800213 } else {
214 logger.Errorw("illegal-state-transition",
215 log.Fields{
216 "device-id": ofc.DeviceID,
217 "current-state": state.String(),
218 "event": event.String()})
219 }
220 case ofcEventConnect:
221 logger.Debugw("ofc-event-connected",
222 log.Fields{"device-id": ofc.DeviceID})
223 if state == ofcStateStarted || state == ofcStateDisconnected {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800224 state = ofcStateConnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800225 ofCtx, ofDone = context.WithCancel(context.Background())
226 go ofc.messageSender(ofCtx)
227 go ofc.processOFStream(ofCtx)
David K. Bainbridge55376262020-01-22 23:28:27 -0800228 } else {
229 logger.Errorw("illegal-state-transition",
230 log.Fields{
231 "device-id": ofc.DeviceID,
232 "current-state": state.String(),
233 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800234 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800235 case ofcEventDisconnect:
236 logger.Debugw("ofc-event-disconnected",
237 log.Fields{
238 "device-id": ofc.DeviceID,
239 "state": state.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800240 if state == ofcStateConnected {
241 state = ofcStateDisconnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800242 if ofDone != nil {
243 ofDone()
244 ofDone = nil
245 }
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800246 go func() {
247 if err := ofc.establishConnectionToController(); err != nil {
Girish Kumare9d76172020-03-20 20:26:04 +0000248 logger.Errorw("controller-connection-failed", log.Fields{"error": err})
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800249 panic(err)
250 }
251 }()
David K. Bainbridge55376262020-01-22 23:28:27 -0800252 } else {
253 logger.Errorw("illegal-state-transition",
254 log.Fields{
255 "device-id": ofc.DeviceID,
256 "current-state": state.String(),
257 "event": event.String()})
258 }
259 case ofcEventStop:
260 logger.Debugw("ofc-event-stop",
261 log.Fields{"device-id": ofc.DeviceID})
262 if state == ofcStateCreated || state == ofcStateConnected || state == ofcStateDisconnected {
263 state = ofcStateStopped
264 break top
265 } else {
266 logger.Errorw("illegal-state-transition",
267 log.Fields{
268 "device-id": ofc.DeviceID,
269 "current-state": state.String(),
270 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800271 }
272 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800273 logger.Debugw("state-transition",
274 log.Fields{
275 "device-id": ofc.DeviceID,
276 "previous-state": previous.String(),
277 "current-state": state.String(),
278 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800279 }
280 }
281
David K. Bainbridge55376262020-01-22 23:28:27 -0800282 // If the child context exists, then cancel it
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800283 if ofDone != nil {
Girish Kumare9d76172020-03-20 20:26:04 +0000284 logger.Debugw("closing-child-processes",
David K. Bainbridge55376262020-01-22 23:28:27 -0800285 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800286 ofDone()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800287 }
288
David K. Bainbridge55376262020-01-22 23:28:27 -0800289 // If the connection is open, then close it
290 if ofc.conn != nil {
Girish Kumare9d76172020-03-20 20:26:04 +0000291 logger.Debugw("closing-of-connection",
David K. Bainbridge55376262020-01-22 23:28:27 -0800292 log.Fields{"device-id": ofc.DeviceID})
293 ofc.conn.Close()
294 ofc.conn = nil
295 }
Girish Kumare9d76172020-03-20 20:26:04 +0000296 logger.Debugw("state-machine-finished",
David K. Bainbridge55376262020-01-22 23:28:27 -0800297 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800298}
299
David K. Bainbridge55376262020-01-22 23:28:27 -0800300// processOFStream processes the OF connection from the controller and invokes
301// the appropriate handler methods for each message.
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800302func (ofc *OFClient) processOFStream(ctx context.Context) {
David K. Bainbridge55376262020-01-22 23:28:27 -0800303 fromController := bufio.NewReader(ofc.conn)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800304
305 /*
David K. Bainbridge55376262020-01-22 23:28:27 -0800306 * We have a read buffer of a max size of 4096, so if we ever have
307 * a message larger than this then we will have issues
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800308 */
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800309 headerBuf := make([]byte, 8)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800310
311top:
312 // Continue until we are told to stop
David K. Bainbridge55376262020-01-22 23:28:27 -0800313 for {
314 select {
315 case <-ctx.Done():
316 logger.Error("of-loop-ending-context-done")
317 break top
318 default:
319 // Read 8 bytes, the standard OF header
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800320 read, err := io.ReadFull(fromController, headerBuf)
David K. Bainbridge55376262020-01-22 23:28:27 -0800321 if err != nil {
322 logger.Errorw("bad-of-header",
323 log.Fields{
324 "byte-count": read,
325 "device-id": ofc.DeviceID,
326 "error": err})
327 break top
328 }
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800329
David K. Bainbridge55376262020-01-22 23:28:27 -0800330 // Decode the header
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800331 peek, err := ofc.peekAtOFHeader(headerBuf)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800332 if err != nil {
333 /*
334 * Header is bad, assume stream is corrupted
335 * and needs to be restarted
336 */
337 logger.Errorw("bad-of-packet",
338 log.Fields{
339 "device-id": ofc.DeviceID,
340 "error": err})
341 break top
342 }
343
David K. Bainbridge55376262020-01-22 23:28:27 -0800344 // Calculate the size of the rest of the packet and read it
345 need := int(peek.GetLength())
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800346 messageBuf := make([]byte, need)
347 copy(messageBuf, headerBuf)
348 read, err = io.ReadFull(fromController, messageBuf[8:])
David K. Bainbridge55376262020-01-22 23:28:27 -0800349 if err != nil {
350 logger.Errorw("bad-of-packet",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800351 log.Fields{
David K. Bainbridge55376262020-01-22 23:28:27 -0800352 "byte-count": read,
353 "device-id": ofc.DeviceID,
354 "error": err})
355 break top
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800356 }
357
358 // Decode and process the packet
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800359 decoder := goloxi.NewDecoder(messageBuf)
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800360 msg, err := ofp.DecodeHeader(decoder)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800361 if err != nil {
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800362 // nolint: staticcheck
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800363 js, _ := json.Marshal(decoder)
364 logger.Errorw("failed-to-decode",
365 log.Fields{
366 "device-id": ofc.DeviceID,
367 "decoder": js,
368 "error": err})
369 break top
370 }
371 if logger.V(log.DebugLevel) {
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800372 js, _ := json.Marshal(msg)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800373 logger.Debugw("packet-header",
374 log.Fields{
375 "device-id": ofc.DeviceID,
376 "header": js})
377 }
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800378 ofc.parseHeader(msg)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800379 }
380 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800381 logger.Debugw("end-of-stream",
382 log.Fields{"device-id": ofc.DeviceID})
383 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800384}
385
386func (ofc *OFClient) sayHello() {
387 hello := ofp.NewHello()
388 hello.Xid = uint32(GetXid())
389 elem := ofp.NewHelloElemVersionbitmap()
390 elem.SetType(ofp.OFPHETVersionbitmap)
391 elem.SetLength(8)
Kent Hagerman3243ee52020-02-26 12:11:55 -0500392 elem.SetBitmaps([]*ofp.Uint32{{Value: 16}})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800393 hello.SetElements([]ofp.IHelloElem{elem})
394 if logger.V(log.DebugLevel) {
395 js, _ := json.Marshal(hello)
396 logger.Debugw("sayHello Called",
397 log.Fields{
398 "device-id": ofc.DeviceID,
399 "hello-message": js})
400 }
401 if err := ofc.SendMessage(hello); err != nil {
402 logger.Fatalw("Failed saying hello to Openflow Server, unable to proceed",
403 log.Fields{
404 "device-id": ofc.DeviceID,
405 "error": err})
406 }
407}
408
409func (ofc *OFClient) parseHeader(header ofp.IHeader) {
Andrea Campanella91c4e4e2020-03-05 16:52:06 +0100410 headerType := header.GetType()
411 logger.Debugw("packet-header-type",
412 log.Fields{
413 "header-type": ofp.Type(headerType).String()})
414 switch headerType {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800415 case ofp.OFPTHello:
416 //x := header.(*ofp.Hello)
417 case ofp.OFPTError:
418 go ofc.handleErrMsg(header.(*ofp.ErrorMsg))
419 case ofp.OFPTEchoRequest:
420 go ofc.handleEchoRequest(header.(*ofp.EchoRequest))
421 case ofp.OFPTEchoReply:
422 case ofp.OFPTExperimenter:
423 case ofp.OFPTFeaturesRequest:
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800424 go func() {
425 if err := ofc.handleFeatureRequest(header.(*ofp.FeaturesRequest)); err != nil {
426 logger.Errorw("handle-feature-request", log.Fields{"error": err})
427 }
428 }()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800429 case ofp.OFPTFeaturesReply:
430 case ofp.OFPTGetConfigRequest:
431 go ofc.handleGetConfigRequest(header.(*ofp.GetConfigRequest))
432 case ofp.OFPTGetConfigReply:
433 case ofp.OFPTSetConfig:
434 go ofc.handleSetConfig(header.(*ofp.SetConfig))
435 case ofp.OFPTPacketIn:
436 case ofp.OFPTFlowRemoved:
437 case ofp.OFPTPortStatus:
438 case ofp.OFPTPacketOut:
439 go ofc.handlePacketOut(header.(*ofp.PacketOut))
440 case ofp.OFPTFlowMod:
441 /*
442 * Not using go routine to handle flow* messages or barrier requests
443 * onos typically issues barrier requests just before a flow* message.
444 * by handling in this thread I ensure all flow* are handled when barrier
445 * request is issued.
446 */
447 switch header.(ofp.IFlowMod).GetCommand() {
448 case ofp.OFPFCAdd:
449 ofc.handleFlowAdd(header.(*ofp.FlowAdd))
450 case ofp.OFPFCModify:
451 ofc.handleFlowMod(header.(*ofp.FlowMod))
452 case ofp.OFPFCModifyStrict:
453 ofc.handleFlowModStrict(header.(*ofp.FlowModifyStrict))
454 case ofp.OFPFCDelete:
455 ofc.handleFlowDelete(header.(*ofp.FlowDelete))
456 case ofp.OFPFCDeleteStrict:
457 ofc.handleFlowDeleteStrict(header.(*ofp.FlowDeleteStrict))
458 }
459 case ofp.OFPTStatsRequest:
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800460 go func() {
461 if err := ofc.handleStatsRequest(header, header.(ofp.IStatsRequest).GetStatsType()); err != nil {
462 logger.Errorw("ofpt-stats-request", log.Fields{"error": err})
463 }
464 }()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800465 case ofp.OFPTBarrierRequest:
466 /* See note above at case ofp.OFPTFlowMod:*/
467 ofc.handleBarrierRequest(header.(*ofp.BarrierRequest))
468 case ofp.OFPTRoleRequest:
469 go ofc.handleRoleRequest(header.(*ofp.RoleRequest))
470 case ofp.OFPTMeterMod:
Don Newtonac0455d2020-01-23 11:52:26 -0500471 ofc.handleMeterModRequest(header.(*ofp.MeterMod))
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800472 }
473}
474
David K. Bainbridge55376262020-01-22 23:28:27 -0800475// Message interface that represents an open flow message and enables for a
476// unified implementation of SendMessage
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800477type Message interface {
478 Serialize(encoder *goloxi.Encoder) error
479}
480
481func (ofc *OFClient) doSend(msg Message) error {
482 if ofc.conn == nil {
483 return errors.New("no-connection")
484 }
485 enc := goloxi.NewEncoder()
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800486 if err := msg.Serialize(enc); err != nil {
487 return err
488 }
489
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800490 bytes := enc.Bytes()
491 if _, err := ofc.conn.Write(bytes); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800492 logger.Errorw("unable-to-send-message-to-controller",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800493 log.Fields{
494 "device-id": ofc.DeviceID,
495 "message": msg,
496 "error": err})
497 return err
498 }
499 return nil
500}
501
502func (ofc *OFClient) messageSender(ctx context.Context) {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800503 // first process last fail if it exists
504 if ofc.lastUnsentMessage != nil {
505 if err := ofc.doSend(ofc.lastUnsentMessage); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800506 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800507 return
508 }
509 ofc.lastUnsentMessage = nil
510 }
511top:
512 for {
513 select {
514 case <-ctx.Done():
515 break top
516 case msg := <-ofc.sendChannel:
David K. Bainbridge55376262020-01-22 23:28:27 -0800517 if err := ofc.doSend(msg); err != nil {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800518 ofc.lastUnsentMessage = msg
David K. Bainbridge55376262020-01-22 23:28:27 -0800519 ofc.events <- ofcEventDisconnect
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800520 logger.Debugw("message-sender-error",
521 log.Fields{
522 "device-id": ofc.DeviceID,
523 "error": err.Error()})
524 break top
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800525 }
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800526 logger.Debugw("message-sender-send",
527 log.Fields{
528 "device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800529 ofc.lastUnsentMessage = nil
530 }
531 }
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800532
533 logger.Debugw("message-sender-finished",
534 log.Fields{
535 "device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800536}
537
David K. Bainbridge55376262020-01-22 23:28:27 -0800538// SendMessage queues a message to be sent to the openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800539func (ofc *OFClient) SendMessage(message Message) error {
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800540 logger.Debug("queuing-message")
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800541 ofc.sendChannel <- message
542 return nil
543}