blob: f43bb114ad4747c9d78b93116f8dadb480d47a9f [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
34var logger, _ = log.AddPackage(log.JSON, log.DebugLevel, nil)
David K. Bainbridge9cb404e2020-01-28 14:32:29 -080035var NoVolthaConnectionError = errors.New("no-voltha-connection")
David K. Bainbridge157bdab2020-01-16 14:38:05 -080036
37type ofcEvent byte
38type ofcState byte
39
40const (
41 ofcEventStart = ofcEvent(iota)
David K. Bainbridge55376262020-01-22 23:28:27 -080042 ofcEventConnect
43 ofcEventDisconnect
44 ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -080045
David K. Bainbridge55376262020-01-22 23:28:27 -080046 ofcStateCreated = ofcState(iota)
47 ofcStateStarted
48 ofcStateConnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -080049 ofcStateDisconnected
David K. Bainbridge55376262020-01-22 23:28:27 -080050 ofcStateStopped
David K. Bainbridge157bdab2020-01-16 14:38:05 -080051)
52
David K. Bainbridge55376262020-01-22 23:28:27 -080053func (e ofcEvent) String() string {
54 switch e {
55 case ofcEventStart:
56 return "ofc-event-start"
57 case ofcEventConnect:
58 return "ofc-event-connected"
59 case ofcEventDisconnect:
60 return "ofc-event-disconnected"
61 case ofcEventStop:
62 return "ofc-event-stop"
63 default:
64 return "ofc-event-unknown"
65 }
66}
67
68func (s ofcState) String() string {
69 switch s {
70 case ofcStateCreated:
71 return "ofc-state-created"
72 case ofcStateStarted:
73 return "ofc-state-started"
74 case ofcStateConnected:
75 return "ofc-state-connected"
76 case ofcStateDisconnected:
77 return "ofc-state-disconnected"
78 case ofcStateStopped:
79 return "ofc-state-stopped"
80 default:
81 return "ofc-state-unknown"
82 }
83}
84
85// OFClient the configuration and operational state of a connection to an
86// openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -080087type OFClient struct {
88 OFControllerEndPoint string
89 Port uint16
90 DeviceID string
David K. Bainbridge157bdab2020-01-16 14:38:05 -080091 VolthaClient voltha.VolthaServiceClient
92 PacketOutChannel chan *voltha.PacketOut
93 ConnectionMaxRetries int
94 ConnectionRetryDelay time.Duration
95 conn net.Conn
96
97 // expirimental
98 events chan ofcEvent
99 sendChannel chan Message
100 lastUnsentMessage Message
101}
102
David K. Bainbridge55376262020-01-22 23:28:27 -0800103// NewClient returns an initialized OFClient instance based on the configuration
104// specified
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800105func NewOFClient(config *OFClient) *OFClient {
106
107 ofc := OFClient{
108 DeviceID: config.DeviceID,
109 OFControllerEndPoint: config.OFControllerEndPoint,
110 VolthaClient: config.VolthaClient,
111 PacketOutChannel: config.PacketOutChannel,
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800112 ConnectionMaxRetries: config.ConnectionMaxRetries,
113 ConnectionRetryDelay: config.ConnectionRetryDelay,
114 events: make(chan ofcEvent, 10),
115 sendChannel: make(chan Message, 100),
116 }
117
118 if ofc.ConnectionRetryDelay <= 0 {
119 logger.Warnw("connection retry delay not valid, setting to default",
120 log.Fields{
121 "device-id": ofc.DeviceID,
122 "value": ofc.ConnectionRetryDelay.String(),
123 "default": (3 * time.Second).String()})
124 ofc.ConnectionRetryDelay = 3 * time.Second
125 }
126 return &ofc
127}
128
David K. Bainbridge55376262020-01-22 23:28:27 -0800129// Stop initiates a shutdown of the OFClient
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800130func (ofc *OFClient) Stop() {
David K. Bainbridge55376262020-01-22 23:28:27 -0800131 ofc.events <- ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800132}
133
134func (ofc *OFClient) peekAtOFHeader(buf []byte) (ofp.IHeader, error) {
135 header := ofp.Header{}
136 header.Version = uint8(buf[0])
137 header.Type = uint8(buf[1])
138 header.Length = binary.BigEndian.Uint16(buf[2:4])
139 header.Xid = binary.BigEndian.Uint32(buf[4:8])
140
141 // TODO: add minimal validation of version and type
142
143 return &header, nil
144}
145
146func (ofc *OFClient) establishConnectionToController() error {
147 if ofc.conn != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800148 logger.Debugw("closing-of-connection-to-reconnect",
149 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800150 ofc.conn.Close()
151 ofc.conn = nil
152 }
153 try := 1
154 for ofc.ConnectionMaxRetries == 0 || try < ofc.ConnectionMaxRetries {
155 if raddr, err := net.ResolveTCPAddr("tcp", ofc.OFControllerEndPoint); err != nil {
156 logger.Debugw("openflow-client unable to resolve endpoint",
157 log.Fields{
158 "device-id": ofc.DeviceID,
159 "endpoint": ofc.OFControllerEndPoint})
160 } else {
161 if connection, err := net.DialTCP("tcp", nil, raddr); err == nil {
162 ofc.conn = connection
163 ofc.sayHello()
David K. Bainbridge55376262020-01-22 23:28:27 -0800164 ofc.events <- ofcEventConnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800165 return nil
166 } else {
167 logger.Warnw("openflow-client-connect-error",
168 log.Fields{
169 "device-id": ofc.DeviceID,
170 "endpoint": ofc.OFControllerEndPoint})
171 }
172 }
173 if ofc.ConnectionMaxRetries == 0 || try < ofc.ConnectionMaxRetries {
174 if ofc.ConnectionMaxRetries != 0 {
175 try += 1
176 }
177 time.Sleep(ofc.ConnectionRetryDelay)
178 }
179 }
180 return errors.New("failed-to-connect-to-of-controller")
181}
182
David K. Bainbridge55376262020-01-22 23:28:27 -0800183// Run implementes the state machine for the OF client reacting to state change
184// events and invoking actions as a reaction to those state changes
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800185func (ofc *OFClient) Run(ctx context.Context) {
186
187 var ofCtx context.Context
188 var ofDone func()
David K. Bainbridge55376262020-01-22 23:28:27 -0800189 state := ofcStateCreated
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800190 ofc.events <- ofcEventStart
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800191top:
192 for {
193 select {
194 case <-ctx.Done():
David K. Bainbridge55376262020-01-22 23:28:27 -0800195 state = ofcStateStopped
196 logger.Debugw("state-transition-context-done",
197 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800198 break top
199 case event := <-ofc.events:
David K. Bainbridge55376262020-01-22 23:28:27 -0800200 previous := state
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800201 switch event {
202 case ofcEventStart:
David K. Bainbridge55376262020-01-22 23:28:27 -0800203 logger.Debugw("ofc-event-start",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800204 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge55376262020-01-22 23:28:27 -0800205 if state == ofcStateCreated {
206 state = ofcStateStarted
207 logger.Debug("STARTED MORE THAN ONCE")
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800208 go func() {
209 if err := ofc.establishConnectionToController(); err != nil {
210 logger.Errorw("controller-connection-failed", log.Fields{"error": err})
211 panic(err)
212 }
213 }()
David K. Bainbridge55376262020-01-22 23:28:27 -0800214 } else {
215 logger.Errorw("illegal-state-transition",
216 log.Fields{
217 "device-id": ofc.DeviceID,
218 "current-state": state.String(),
219 "event": event.String()})
220 }
221 case ofcEventConnect:
222 logger.Debugw("ofc-event-connected",
223 log.Fields{"device-id": ofc.DeviceID})
224 if state == ofcStateStarted || state == ofcStateDisconnected {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800225 state = ofcStateConnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800226 ofCtx, ofDone = context.WithCancel(context.Background())
227 go ofc.messageSender(ofCtx)
228 go ofc.processOFStream(ofCtx)
David K. Bainbridge55376262020-01-22 23:28:27 -0800229 } else {
230 logger.Errorw("illegal-state-transition",
231 log.Fields{
232 "device-id": ofc.DeviceID,
233 "current-state": state.String(),
234 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800235 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800236 case ofcEventDisconnect:
237 logger.Debugw("ofc-event-disconnected",
238 log.Fields{
239 "device-id": ofc.DeviceID,
240 "state": state.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800241 if state == ofcStateConnected {
242 state = ofcStateDisconnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800243 if ofDone != nil {
244 ofDone()
245 ofDone = nil
246 }
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800247 go func() {
248 if err := ofc.establishConnectionToController(); err != nil {
249 log.Errorw("controller-connection-failed", log.Fields{"error": err})
250 panic(err)
251 }
252 }()
David K. Bainbridge55376262020-01-22 23:28:27 -0800253 } else {
254 logger.Errorw("illegal-state-transition",
255 log.Fields{
256 "device-id": ofc.DeviceID,
257 "current-state": state.String(),
258 "event": event.String()})
259 }
260 case ofcEventStop:
261 logger.Debugw("ofc-event-stop",
262 log.Fields{"device-id": ofc.DeviceID})
263 if state == ofcStateCreated || state == ofcStateConnected || state == ofcStateDisconnected {
264 state = ofcStateStopped
265 break top
266 } else {
267 logger.Errorw("illegal-state-transition",
268 log.Fields{
269 "device-id": ofc.DeviceID,
270 "current-state": state.String(),
271 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800272 }
273 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800274 logger.Debugw("state-transition",
275 log.Fields{
276 "device-id": ofc.DeviceID,
277 "previous-state": previous.String(),
278 "current-state": state.String(),
279 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800280 }
281 }
282
David K. Bainbridge55376262020-01-22 23:28:27 -0800283 // If the child context exists, then cancel it
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800284 if ofDone != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800285 log.Debugw("closing-child-processes",
286 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800287 ofDone()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800288 }
289
David K. Bainbridge55376262020-01-22 23:28:27 -0800290 // If the connection is open, then close it
291 if ofc.conn != nil {
292 log.Debugw("closing-of-connection",
293 log.Fields{"device-id": ofc.DeviceID})
294 ofc.conn.Close()
295 ofc.conn = nil
296 }
297 log.Debugw("state-machine-finished",
298 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800299}
300
David K. Bainbridge55376262020-01-22 23:28:27 -0800301// processOFStream processes the OF connection from the controller and invokes
302// the appropriate handler methods for each message.
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800303func (ofc *OFClient) processOFStream(ctx context.Context) {
David K. Bainbridge55376262020-01-22 23:28:27 -0800304 fromController := bufio.NewReader(ofc.conn)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800305
306 /*
David K. Bainbridge55376262020-01-22 23:28:27 -0800307 * We have a read buffer of a max size of 4096, so if we ever have
308 * a message larger than this then we will have issues
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800309 */
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800310 headerBuf := make([]byte, 8)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800311
312top:
313 // Continue until we are told to stop
David K. Bainbridge55376262020-01-22 23:28:27 -0800314 for {
315 select {
316 case <-ctx.Done():
317 logger.Error("of-loop-ending-context-done")
318 break top
319 default:
320 // Read 8 bytes, the standard OF header
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800321 read, err := io.ReadFull(fromController, headerBuf)
David K. Bainbridge55376262020-01-22 23:28:27 -0800322 if err != nil {
323 logger.Errorw("bad-of-header",
324 log.Fields{
325 "byte-count": read,
326 "device-id": ofc.DeviceID,
327 "error": err})
328 break top
329 }
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800330
David K. Bainbridge55376262020-01-22 23:28:27 -0800331 // Decode the header
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800332 peek, err := ofc.peekAtOFHeader(headerBuf)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800333 if err != nil {
334 /*
335 * Header is bad, assume stream is corrupted
336 * and needs to be restarted
337 */
338 logger.Errorw("bad-of-packet",
339 log.Fields{
340 "device-id": ofc.DeviceID,
341 "error": err})
342 break top
343 }
344
David K. Bainbridge55376262020-01-22 23:28:27 -0800345 // Calculate the size of the rest of the packet and read it
346 need := int(peek.GetLength())
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800347 messageBuf := make([]byte, need)
348 copy(messageBuf, headerBuf)
349 read, err = io.ReadFull(fromController, messageBuf[8:])
David K. Bainbridge55376262020-01-22 23:28:27 -0800350 if err != nil {
351 logger.Errorw("bad-of-packet",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800352 log.Fields{
David K. Bainbridge55376262020-01-22 23:28:27 -0800353 "byte-count": read,
354 "device-id": ofc.DeviceID,
355 "error": err})
356 break top
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800357 }
358
359 // Decode and process the packet
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800360 decoder := goloxi.NewDecoder(messageBuf)
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800361 msg, err := ofp.DecodeHeader(decoder)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800362 if err != nil {
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800363 // nolint: staticcheck
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800364 js, _ := json.Marshal(decoder)
365 logger.Errorw("failed-to-decode",
366 log.Fields{
367 "device-id": ofc.DeviceID,
368 "decoder": js,
369 "error": err})
370 break top
371 }
372 if logger.V(log.DebugLevel) {
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800373 js, _ := json.Marshal(msg)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800374 logger.Debugw("packet-header",
375 log.Fields{
376 "device-id": ofc.DeviceID,
377 "header": js})
378 }
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800379 ofc.parseHeader(msg)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800380 }
381 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800382 logger.Debugw("end-of-stream",
383 log.Fields{"device-id": ofc.DeviceID})
384 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800385}
386
387func (ofc *OFClient) sayHello() {
388 hello := ofp.NewHello()
389 hello.Xid = uint32(GetXid())
390 elem := ofp.NewHelloElemVersionbitmap()
391 elem.SetType(ofp.OFPHETVersionbitmap)
392 elem.SetLength(8)
Kent Hagerman3243ee52020-02-26 12:11:55 -0500393 elem.SetBitmaps([]*ofp.Uint32{{Value: 16}})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800394 hello.SetElements([]ofp.IHelloElem{elem})
395 if logger.V(log.DebugLevel) {
396 js, _ := json.Marshal(hello)
397 logger.Debugw("sayHello Called",
398 log.Fields{
399 "device-id": ofc.DeviceID,
400 "hello-message": js})
401 }
402 if err := ofc.SendMessage(hello); err != nil {
403 logger.Fatalw("Failed saying hello to Openflow Server, unable to proceed",
404 log.Fields{
405 "device-id": ofc.DeviceID,
406 "error": err})
407 }
408}
409
410func (ofc *OFClient) parseHeader(header ofp.IHeader) {
Andrea Campanella91c4e4e2020-03-05 16:52:06 +0100411 headerType := header.GetType()
412 logger.Debugw("packet-header-type",
413 log.Fields{
414 "header-type": ofp.Type(headerType).String()})
415 switch headerType {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800416 case ofp.OFPTHello:
417 //x := header.(*ofp.Hello)
418 case ofp.OFPTError:
419 go ofc.handleErrMsg(header.(*ofp.ErrorMsg))
420 case ofp.OFPTEchoRequest:
421 go ofc.handleEchoRequest(header.(*ofp.EchoRequest))
422 case ofp.OFPTEchoReply:
423 case ofp.OFPTExperimenter:
424 case ofp.OFPTFeaturesRequest:
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800425 go func() {
426 if err := ofc.handleFeatureRequest(header.(*ofp.FeaturesRequest)); err != nil {
427 logger.Errorw("handle-feature-request", log.Fields{"error": err})
428 }
429 }()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800430 case ofp.OFPTFeaturesReply:
431 case ofp.OFPTGetConfigRequest:
432 go ofc.handleGetConfigRequest(header.(*ofp.GetConfigRequest))
433 case ofp.OFPTGetConfigReply:
434 case ofp.OFPTSetConfig:
435 go ofc.handleSetConfig(header.(*ofp.SetConfig))
436 case ofp.OFPTPacketIn:
437 case ofp.OFPTFlowRemoved:
438 case ofp.OFPTPortStatus:
439 case ofp.OFPTPacketOut:
440 go ofc.handlePacketOut(header.(*ofp.PacketOut))
441 case ofp.OFPTFlowMod:
442 /*
443 * Not using go routine to handle flow* messages or barrier requests
444 * onos typically issues barrier requests just before a flow* message.
445 * by handling in this thread I ensure all flow* are handled when barrier
446 * request is issued.
447 */
448 switch header.(ofp.IFlowMod).GetCommand() {
449 case ofp.OFPFCAdd:
450 ofc.handleFlowAdd(header.(*ofp.FlowAdd))
451 case ofp.OFPFCModify:
452 ofc.handleFlowMod(header.(*ofp.FlowMod))
453 case ofp.OFPFCModifyStrict:
454 ofc.handleFlowModStrict(header.(*ofp.FlowModifyStrict))
455 case ofp.OFPFCDelete:
456 ofc.handleFlowDelete(header.(*ofp.FlowDelete))
457 case ofp.OFPFCDeleteStrict:
458 ofc.handleFlowDeleteStrict(header.(*ofp.FlowDeleteStrict))
459 }
460 case ofp.OFPTStatsRequest:
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800461 go func() {
462 if err := ofc.handleStatsRequest(header, header.(ofp.IStatsRequest).GetStatsType()); err != nil {
463 logger.Errorw("ofpt-stats-request", log.Fields{"error": err})
464 }
465 }()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800466 case ofp.OFPTBarrierRequest:
467 /* See note above at case ofp.OFPTFlowMod:*/
468 ofc.handleBarrierRequest(header.(*ofp.BarrierRequest))
469 case ofp.OFPTRoleRequest:
470 go ofc.handleRoleRequest(header.(*ofp.RoleRequest))
471 case ofp.OFPTMeterMod:
Don Newtonac0455d2020-01-23 11:52:26 -0500472 ofc.handleMeterModRequest(header.(*ofp.MeterMod))
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800473 }
474}
475
David K. Bainbridge55376262020-01-22 23:28:27 -0800476// Message interface that represents an open flow message and enables for a
477// unified implementation of SendMessage
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800478type Message interface {
479 Serialize(encoder *goloxi.Encoder) error
480}
481
482func (ofc *OFClient) doSend(msg Message) error {
483 if ofc.conn == nil {
484 return errors.New("no-connection")
485 }
486 enc := goloxi.NewEncoder()
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800487 if err := msg.Serialize(enc); err != nil {
488 return err
489 }
490
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800491 bytes := enc.Bytes()
492 if _, err := ofc.conn.Write(bytes); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800493 logger.Errorw("unable-to-send-message-to-controller",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800494 log.Fields{
495 "device-id": ofc.DeviceID,
496 "message": msg,
497 "error": err})
498 return err
499 }
500 return nil
501}
502
503func (ofc *OFClient) messageSender(ctx context.Context) {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800504 // first process last fail if it exists
505 if ofc.lastUnsentMessage != nil {
506 if err := ofc.doSend(ofc.lastUnsentMessage); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800507 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800508 return
509 }
510 ofc.lastUnsentMessage = nil
511 }
512top:
513 for {
514 select {
515 case <-ctx.Done():
516 break top
517 case msg := <-ofc.sendChannel:
David K. Bainbridge55376262020-01-22 23:28:27 -0800518 if err := ofc.doSend(msg); err != nil {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800519 ofc.lastUnsentMessage = msg
David K. Bainbridge55376262020-01-22 23:28:27 -0800520 ofc.events <- ofcEventDisconnect
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800521 logger.Debugw("message-sender-error",
522 log.Fields{
523 "device-id": ofc.DeviceID,
524 "error": err.Error()})
525 break top
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800526 }
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800527 logger.Debugw("message-sender-send",
528 log.Fields{
529 "device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800530 ofc.lastUnsentMessage = nil
531 }
532 }
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800533
534 logger.Debugw("message-sender-finished",
535 log.Fields{
536 "device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800537}
538
David K. Bainbridge55376262020-01-22 23:28:27 -0800539// SendMessage queues a message to be sent to the openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800540func (ofc *OFClient) SendMessage(message Message) error {
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800541 logger.Debug("queuing-message")
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800542 ofc.sendChannel <- message
543 return nil
544}