blob: 86bb6b9addb95811ae228c999b856779ed4495d5 [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
56 ofcFlowsChunkSize = 450 // this amount of flows is around 57KB
57 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) {
205
Jonathan Hart4b110f62020-03-13 17:36:19 -0700206 for _, endpoint := range ofc.OFControllerEndPoints {
207 connection := &OFConnection{
208 OFControllerEndPoint: endpoint,
209 DeviceID: ofc.DeviceID,
210 VolthaClient: ofc.VolthaClient,
211 PacketOutChannel: ofc.PacketOutChannel,
212 ConnectionMaxRetries: ofc.ConnectionMaxRetries,
213 ConnectionRetryDelay: ofc.ConnectionRetryDelay,
214 role: ofcRoleNone,
215 roleManager: ofc,
216 events: make(chan ofcEvent, 10),
217 sendChannel: make(chan Message, 100),
Matteo Scandolo256266d2020-06-01 13:44:07 -0700218 flowsChunkSize: ofc.flowsChunkSize,
219 portsChunkSize: ofc.portsChunkSize,
220 portsDescChunkSize: ofc.portsDescChunkSize,
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800221 }
Jonathan Hart4b110f62020-03-13 17:36:19 -0700222
223 ofc.connections[endpoint] = connection
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800224 }
225
Jonathan Hart4b110f62020-03-13 17:36:19 -0700226 for _, connection := range ofc.connections {
227 go connection.Run(ctx)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800228 }
229}
230
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000231func (ofc *OFClient) SendMessage(ctx context.Context, message Message) error {
Jonathan Hart4b110f62020-03-13 17:36:19 -0700232 for _, connection := range ofc.connections {
233 if connection.role == ofcRoleMaster || connection.role == ofcRoleEqual {
Rohan Agrawalc32d9932020-06-15 11:01:47 +0000234 err := connection.SendMessage(ctx, message)
Jonathan Hart4b110f62020-03-13 17:36:19 -0700235 if err != nil {
236 return err
237 }
238 }
239 }
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800240 return nil
241}