blob: b7d5a289ad175ec9eb1685b83b96b400b1e56ded [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 (
20 "context"
David K. Bainbridge157bdab2020-01-16 14:38:05 -080021 "errors"
Jonathan Hart4b110f62020-03-13 17:36:19 -070022 "sync"
David K. Bainbridge157bdab2020-01-16 14:38:05 -080023 "time"
David Bainbridgef8ce7d22020-04-08 12:49:41 -070024
Jonathan Hart828908c2020-04-15 14:23:45 -070025 ofp "github.com/opencord/goloxi/of13"
David Bainbridgef8ce7d22020-04-08 12:49:41 -070026 "github.com/opencord/ofagent-go/internal/pkg/holder"
27 "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)
30
David K. Bainbridge9cb404e2020-01-28 14:32:29 -080031var NoVolthaConnectionError = errors.New("no-voltha-connection")
David K. Bainbridge157bdab2020-01-16 14:38:05 -080032
33type ofcEvent byte
34type ofcState byte
Jonathan Hart4b110f62020-03-13 17:36:19 -070035type ofcRole byte
David K. Bainbridge157bdab2020-01-16 14:38:05 -080036
37const (
38 ofcEventStart = ofcEvent(iota)
David K. Bainbridge55376262020-01-22 23:28:27 -080039 ofcEventConnect
40 ofcEventDisconnect
41 ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -080042
David K. Bainbridge55376262020-01-22 23:28:27 -080043 ofcStateCreated = ofcState(iota)
44 ofcStateStarted
45 ofcStateConnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -080046 ofcStateDisconnected
David K. Bainbridge55376262020-01-22 23:28:27 -080047 ofcStateStopped
Jonathan Hart4b110f62020-03-13 17:36:19 -070048
49 ofcRoleNone = ofcRole(iota)
50 ofcRoleEqual
51 ofcRoleMaster
52 ofcRoleSlave
Matteo Scandolo256266d2020-06-01 13:44:07 -070053
54 // according to testing this is the maximum content of an
55 // openflow message to remain under 64KB
Andrea Campanellaf2cf13e2020-09-03 16:22:27 +020056 ofcFlowsChunkSize = 400 // this amount of flows is around 40KB for DT, 47KB ATT and 61KB for TT
Matteo Scandolo256266d2020-06-01 13:44:07 -070057 ofcPortsChunkSize = 550 // this amount of port stats is around 61KB
58 ofcPortsDescChunkSize = 900 // this amount of port desc is around 57KB
David K. Bainbridge157bdab2020-01-16 14:38:05 -080059)
60
David K. Bainbridge55376262020-01-22 23:28:27 -080061func (e ofcEvent) String() string {
62 switch e {
63 case ofcEventStart:
64 return "ofc-event-start"
65 case ofcEventConnect:
66 return "ofc-event-connected"
67 case ofcEventDisconnect:
68 return "ofc-event-disconnected"
69 case ofcEventStop:
70 return "ofc-event-stop"
71 default:
72 return "ofc-event-unknown"
73 }
74}
75
76func (s ofcState) String() string {
77 switch s {
78 case ofcStateCreated:
79 return "ofc-state-created"
80 case ofcStateStarted:
81 return "ofc-state-started"
82 case ofcStateConnected:
83 return "ofc-state-connected"
84 case ofcStateDisconnected:
85 return "ofc-state-disconnected"
86 case ofcStateStopped:
87 return "ofc-state-stopped"
88 default:
89 return "ofc-state-unknown"
90 }
91}
92
93// OFClient the configuration and operational state of a connection to an
94// openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -080095type OFClient struct {
Jonathan Hart4b110f62020-03-13 17:36:19 -070096 OFControllerEndPoints []string
97 DeviceID string
David Bainbridgef8ce7d22020-04-08 12:49:41 -070098 VolthaClient *holder.VolthaServiceClientHolder
Jonathan Hart4b110f62020-03-13 17:36:19 -070099 PacketOutChannel chan *voltha.PacketOut
100 ConnectionMaxRetries int
101 ConnectionRetryDelay time.Duration
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800102
Jonathan Hart4b110f62020-03-13 17:36:19 -0700103 // map of endpoint to OF connection
104 connections map[string]*OFConnection
105
106 // global role state for device
107 generationIsDefined bool
108 generationID uint64
109 roleLock sync.Mutex
Matteo Scandolo256266d2020-06-01 13:44:07 -0700110
111 flowsChunkSize int
112 portsChunkSize int
113 portsDescChunkSize int
Jonathan Hart4b110f62020-03-13 17:36:19 -0700114}
115
116type RoleManager interface {
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000117 UpdateRoles(ctx context.Context, from string, request *ofp.RoleRequest) bool
Jonathan Hart4b110f62020-03-13 17:36:19 -0700118}
119
120func distance(a uint64, b uint64) int64 {
121 return (int64)(a - b)
122}
123
124// UpdateRoles validates a role request and updates role state for connections where it changed
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000125func (ofc *OFClient) UpdateRoles(ctx context.Context, from string, request *ofp.RoleRequest) bool {
126 logger.Debug(ctx, "updating role", log.Fields{
Jonathan Hart4b110f62020-03-13 17:36:19 -0700127 "from": from,
128 "to": request.Role,
129 "id": request.GenerationId})
130
131 ofc.roleLock.Lock()
132 defer ofc.roleLock.Unlock()
133
134 if request.Role == ofp.OFPCRRoleEqual {
135 // equal request doesn't care about generation ID and always succeeds
136 connection := ofc.connections[from]
137 connection.role = ofcRoleEqual
138 return true
139 }
140
141 if ofc.generationIsDefined && distance(request.GenerationId, ofc.generationID) < 0 {
142 // generation ID is not valid
143 return false
144 } else {
145 ofc.generationID = request.GenerationId
146 ofc.generationIsDefined = true
147
148 if request.Role == ofp.OFPCRRoleMaster {
149 // master is potentially changing, find the existing master and set it to slave
150 for endpoint, connection := range ofc.connections {
151 if endpoint == from {
152 connection.role = ofcRoleMaster
153 } else if connection.role == ofcRoleMaster {
154 // the old master should be set to slave
155 connection.role = ofcRoleSlave
156 }
157 }
158 return true
159 } else if request.Role == ofp.OFPCRRoleSlave {
160 connection := ofc.connections[from]
161 connection.role = ofcRoleSlave
162 return true
163 }
164 }
165
166 return false
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800167}
168
David K. Bainbridge55376262020-01-22 23:28:27 -0800169// NewClient returns an initialized OFClient instance based on the configuration
170// specified
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000171func NewOFClient(ctx context.Context, config *OFClient) *OFClient {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800172
173 ofc := OFClient{
Jonathan Hart4b110f62020-03-13 17:36:19 -0700174 DeviceID: config.DeviceID,
175 OFControllerEndPoints: config.OFControllerEndPoints,
176 VolthaClient: config.VolthaClient,
177 PacketOutChannel: config.PacketOutChannel,
178 ConnectionMaxRetries: config.ConnectionMaxRetries,
179 ConnectionRetryDelay: config.ConnectionRetryDelay,
180 connections: make(map[string]*OFConnection),
Matteo Scandolo256266d2020-06-01 13:44:07 -0700181 flowsChunkSize: ofcFlowsChunkSize,
182 portsChunkSize: ofcPortsChunkSize,
183 portsDescChunkSize: ofcPortsDescChunkSize,
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800184 }
185
186 if ofc.ConnectionRetryDelay <= 0 {
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000187 logger.Warnw(ctx, "connection retry delay not valid, setting to default",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800188 log.Fields{
189 "device-id": ofc.DeviceID,
190 "value": ofc.ConnectionRetryDelay.String(),
191 "default": (3 * time.Second).String()})
192 ofc.ConnectionRetryDelay = 3 * time.Second
193 }
194 return &ofc
195}
196
David K. Bainbridge55376262020-01-22 23:28:27 -0800197// Stop initiates a shutdown of the OFClient
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800198func (ofc *OFClient) Stop() {
Jonathan Hart4b110f62020-03-13 17:36:19 -0700199 for _, connection := range ofc.connections {
200 connection.events <- ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800201 }
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800202}
203
204func (ofc *OFClient) Run(ctx context.Context) {
Jonathan Hart4b110f62020-03-13 17:36:19 -0700205 for _, endpoint := range ofc.OFControllerEndPoints {
206 connection := &OFConnection{
207 OFControllerEndPoint: endpoint,
208 DeviceID: ofc.DeviceID,
209 VolthaClient: ofc.VolthaClient,
210 PacketOutChannel: ofc.PacketOutChannel,
211 ConnectionMaxRetries: ofc.ConnectionMaxRetries,
212 ConnectionRetryDelay: ofc.ConnectionRetryDelay,
213 role: ofcRoleNone,
214 roleManager: ofc,
215 events: make(chan ofcEvent, 10),
216 sendChannel: make(chan Message, 100),
Matteo Scandolo256266d2020-06-01 13:44:07 -0700217 flowsChunkSize: ofc.flowsChunkSize,
218 portsChunkSize: ofc.portsChunkSize,
219 portsDescChunkSize: ofc.portsDescChunkSize,
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800220 }
Jonathan Hart4b110f62020-03-13 17:36:19 -0700221
222 ofc.connections[endpoint] = connection
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800223 }
224
Jonathan Hart4b110f62020-03-13 17:36:19 -0700225 for _, connection := range ofc.connections {
226 go connection.Run(ctx)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800227 }
228}
229
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000230func (ofc *OFClient) SendMessage(ctx context.Context, message Message) error {
Jonathan Hart4b110f62020-03-13 17:36:19 -0700231 for _, connection := range ofc.connections {
232 if connection.role == ofcRoleMaster || connection.role == ofcRoleEqual {
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000233 err := connection.SendMessage(ctx, message)
Jonathan Hart4b110f62020-03-13 17:36:19 -0700234 if err != nil {
235 return err
236 }
237 }
238 }
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800239 return nil
240}