blob: 298911d8757e288dc09670db23731e783880f229 [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 ofagent
18
19import (
20 "context"
David Bainbridgef8ce7d22020-04-08 12:49:41 -070021 "sync"
22 "time"
23
24 "github.com/opencord/ofagent-go/internal/pkg/holder"
David K. Bainbridge157bdab2020-01-16 14:38:05 -080025 "github.com/opencord/ofagent-go/internal/pkg/openflow"
Andrea Campanella18448bc2021-07-08 18:47:22 +020026 "github.com/opencord/voltha-lib-go/v5/pkg/log"
27 "github.com/opencord/voltha-lib-go/v5/pkg/probe"
Maninder12b909f2020-10-23 14:23:36 +053028 "github.com/opencord/voltha-protos/v4/go/openflow_13"
29 "github.com/opencord/voltha-protos/v4/go/voltha"
David K. Bainbridge157bdab2020-01-16 14:38:05 -080030 "google.golang.org/grpc"
David K. Bainbridge157bdab2020-01-16 14:38:05 -080031)
32
David K. Bainbridge157bdab2020-01-16 14:38:05 -080033type ofaEvent byte
34type ofaState byte
35
36const (
37 ofaEventStart = ofaEvent(iota)
38 ofaEventVolthaConnected
39 ofaEventVolthaDisconnected
40 ofaEventError
41
42 ofaStateConnected = ofaState(iota)
David K. Bainbridge9cb404e2020-01-28 14:32:29 -080043 ofaStateConnecting
David K. Bainbridge157bdab2020-01-16 14:38:05 -080044 ofaStateDisconnected
45)
46
47type OFAgent struct {
48 VolthaApiEndPoint string
Jonathan Hart4b110f62020-03-13 17:36:19 -070049 OFControllerEndPoints []string
David K. Bainbridge157bdab2020-01-16 14:38:05 -080050 DeviceListRefreshInterval time.Duration
51 ConnectionMaxRetries int
52 ConnectionRetryDelay time.Duration
53
54 volthaConnection *grpc.ClientConn
David Bainbridgef8ce7d22020-04-08 12:49:41 -070055 volthaClient *holder.VolthaServiceClientHolder
David K. Bainbridge157bdab2020-01-16 14:38:05 -080056 mapLock sync.Mutex
57 clientMap map[string]*openflow.OFClient
58 events chan ofaEvent
59
60 packetInChannel chan *voltha.PacketIn
61 packetOutChannel chan *voltha.PacketOut
Maninder12b909f2020-10-23 14:23:36 +053062 changeEventChannel chan *openflow_13.ChangeEvent
David K. Bainbridge157bdab2020-01-16 14:38:05 -080063}
64
Rohan Agrawalc32d9932020-06-15 11:01:47 +000065func NewOFAgent(ctx context.Context, config *OFAgent) (*OFAgent, error) {
David K. Bainbridge157bdab2020-01-16 14:38:05 -080066 ofa := OFAgent{
67 VolthaApiEndPoint: config.VolthaApiEndPoint,
Jonathan Hart4b110f62020-03-13 17:36:19 -070068 OFControllerEndPoints: config.OFControllerEndPoints,
David K. Bainbridge157bdab2020-01-16 14:38:05 -080069 DeviceListRefreshInterval: config.DeviceListRefreshInterval,
70 ConnectionMaxRetries: config.ConnectionMaxRetries,
71 ConnectionRetryDelay: config.ConnectionRetryDelay,
David Bainbridgef8ce7d22020-04-08 12:49:41 -070072 volthaClient: &holder.VolthaServiceClientHolder{},
David K. Bainbridge157bdab2020-01-16 14:38:05 -080073 packetInChannel: make(chan *voltha.PacketIn),
74 packetOutChannel: make(chan *voltha.PacketOut),
Maninder12b909f2020-10-23 14:23:36 +053075 changeEventChannel: make(chan *openflow_13.ChangeEvent),
David K. Bainbridge157bdab2020-01-16 14:38:05 -080076 clientMap: make(map[string]*openflow.OFClient),
77 events: make(chan ofaEvent, 100),
78 }
79
80 if ofa.DeviceListRefreshInterval <= 0 {
Rohan Agrawalc32d9932020-06-15 11:01:47 +000081 logger.Warnw(ctx, "device list refresh internal not valid, setting to default",
David K. Bainbridge157bdab2020-01-16 14:38:05 -080082 log.Fields{
83 "value": ofa.DeviceListRefreshInterval.String(),
84 "default": (1 * time.Minute).String()})
85 ofa.DeviceListRefreshInterval = 1 * time.Minute
86 }
87
88 if ofa.ConnectionRetryDelay <= 0 {
Rohan Agrawalc32d9932020-06-15 11:01:47 +000089 logger.Warnw(ctx, "connection retry delay not value, setting to default",
David K. Bainbridge157bdab2020-01-16 14:38:05 -080090 log.Fields{
91 "value": ofa.ConnectionRetryDelay.String(),
92 "default": (3 * time.Second).String()})
93 ofa.ConnectionRetryDelay = 3 * time.Second
94 }
95
96 return &ofa, nil
97}
98
99// Run - make the inital connection to voltha and kicks off io streams
100func (ofa *OFAgent) Run(ctx context.Context) {
101
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000102 logger.Debugw(ctx, "Starting GRPC - VOLTHA client",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800103 log.Fields{
104 "voltha-endpoint": ofa.VolthaApiEndPoint,
Jonathan Hart4b110f62020-03-13 17:36:19 -0700105 "controller-endpoint": ofa.OFControllerEndPoints})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800106
107 // If the context contains a k8s probe then register services
108 p := probe.GetProbeFromContext(ctx)
109 if p != nil {
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000110 p.RegisterService(ctx, "voltha")
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800111 }
divyadesai81bb7ba2020-03-11 11:45:23 +0000112
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800113 ofa.events <- ofaEventStart
114
115 /*
116 * Two sub-contexts are created here for different purposes so we can
117 * control the lifecyle of processing loops differently.
118 *
119 * volthaCtx - controls those processes that rely on the GRPC
120 * GRPCconnection to voltha and will be restarted when the
121 * GRPC connection is interrupted.
122 * hdlCtx - controls those processes that listen to channels and
123 * process each message. these will likely never be
124 * stopped until the ofagent is stopped.
125 */
126 var volthaCtx, hdlCtx context.Context
127 var volthaDone, hdlDone func()
128 state := ofaStateDisconnected
129
130 for {
131 select {
132 case <-ctx.Done():
133 if volthaDone != nil {
134 volthaDone()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800135 }
136 if hdlDone != nil {
137 hdlDone()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800138 }
139 return
140 case event := <-ofa.events:
141 switch event {
142 case ofaEventStart:
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000143 logger.Debug(ctx, "ofagent-voltha-start-event")
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800144
145 // Start the loops that process messages
146 hdlCtx, hdlDone = context.WithCancel(context.Background())
147 go ofa.handlePacketsIn(hdlCtx)
148 go ofa.handleChangeEvents(hdlCtx)
149
150 // Kick off process to attempt to establish
151 // connection to voltha
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800152 state = ofaStateConnecting
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800153 go func() {
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000154 if err := ofa.establishConnectionToVoltha(ctx, p); err != nil {
155 logger.Errorw(ctx, "voltha-connection-failed", log.Fields{"error": err})
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800156 panic(err)
157 }
158 }()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800159
160 case ofaEventVolthaConnected:
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000161 logger.Debug(ctx, "ofagent-voltha-connect-event")
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800162
163 // Start the loops that poll from voltha
164 if state != ofaStateConnected {
165 state = ofaStateConnected
166 volthaCtx, volthaDone = context.WithCancel(context.Background())
167 go ofa.receiveChangeEvents(volthaCtx)
168 go ofa.receivePacketsIn(volthaCtx)
169 go ofa.streamPacketOut(volthaCtx)
170 go ofa.synchronizeDeviceList(volthaCtx)
171 }
172
173 case ofaEventVolthaDisconnected:
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800174 if p != nil {
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000175 p.UpdateStatus(ctx, "voltha", probe.ServiceStatusNotReady)
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800176 }
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000177 logger.Debug(ctx, "ofagent-voltha-disconnect-event")
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800178 if state == ofaStateConnected {
179 state = ofaStateDisconnected
khenaidoo927391f2021-06-18 17:06:52 -0400180 // Clear all the OF connections to the OF controller. These will be recreated when the
181 // connection to voltha is established
182 ofa.clearAllOFClient()
David Bainbridgef8ce7d22020-04-08 12:49:41 -0700183 ofa.volthaClient.Clear()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800184 volthaDone()
185 volthaDone = nil
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800186 }
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800187 if state != ofaStateConnecting {
188 state = ofaStateConnecting
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800189 go func() {
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000190 if err := ofa.establishConnectionToVoltha(ctx, p); err != nil {
191 logger.Errorw(ctx, "voltha-connection-failed", log.Fields{"error": err})
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800192 panic(err)
193 }
194 }()
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800195 }
196
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800197 case ofaEventError:
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000198 logger.Debug(ctx, "ofagent-error-event")
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800199 default:
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000200 logger.Fatalw(ctx, "ofagent-unknown-event",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800201 log.Fields{"event": event})
202 }
203 }
204 }
205}