blob: 3f48e0c82dada22599c2daa2f2ef6c73497df2b0 [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
25 ofp "github.com/donNewtonAlpha/goloxi/of13"
26 "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
David K. Bainbridge157bdab2020-01-16 14:38:05 -080053)
54
David K. Bainbridge55376262020-01-22 23:28:27 -080055func (e ofcEvent) String() string {
56 switch e {
57 case ofcEventStart:
58 return "ofc-event-start"
59 case ofcEventConnect:
60 return "ofc-event-connected"
61 case ofcEventDisconnect:
62 return "ofc-event-disconnected"
63 case ofcEventStop:
64 return "ofc-event-stop"
65 default:
66 return "ofc-event-unknown"
67 }
68}
69
70func (s ofcState) String() string {
71 switch s {
72 case ofcStateCreated:
73 return "ofc-state-created"
74 case ofcStateStarted:
75 return "ofc-state-started"
76 case ofcStateConnected:
77 return "ofc-state-connected"
78 case ofcStateDisconnected:
79 return "ofc-state-disconnected"
80 case ofcStateStopped:
81 return "ofc-state-stopped"
82 default:
83 return "ofc-state-unknown"
84 }
85}
86
87// OFClient the configuration and operational state of a connection to an
88// openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -080089type OFClient struct {
Jonathan Hart4b110f62020-03-13 17:36:19 -070090 OFControllerEndPoints []string
91 DeviceID string
David Bainbridgef8ce7d22020-04-08 12:49:41 -070092 VolthaClient *holder.VolthaServiceClientHolder
Jonathan Hart4b110f62020-03-13 17:36:19 -070093 PacketOutChannel chan *voltha.PacketOut
94 ConnectionMaxRetries int
95 ConnectionRetryDelay time.Duration
David K. Bainbridge157bdab2020-01-16 14:38:05 -080096
Jonathan Hart4b110f62020-03-13 17:36:19 -070097 // map of endpoint to OF connection
98 connections map[string]*OFConnection
99
100 // global role state for device
101 generationIsDefined bool
102 generationID uint64
103 roleLock sync.Mutex
104}
105
106type RoleManager interface {
107 UpdateRoles(from string, request *ofp.RoleRequest) bool
108}
109
110func distance(a uint64, b uint64) int64 {
111 return (int64)(a - b)
112}
113
114// UpdateRoles validates a role request and updates role state for connections where it changed
115func (ofc *OFClient) UpdateRoles(from string, request *ofp.RoleRequest) bool {
116 log.Debug("updating role", log.Fields{
117 "from": from,
118 "to": request.Role,
119 "id": request.GenerationId})
120
121 ofc.roleLock.Lock()
122 defer ofc.roleLock.Unlock()
123
124 if request.Role == ofp.OFPCRRoleEqual {
125 // equal request doesn't care about generation ID and always succeeds
126 connection := ofc.connections[from]
127 connection.role = ofcRoleEqual
128 return true
129 }
130
131 if ofc.generationIsDefined && distance(request.GenerationId, ofc.generationID) < 0 {
132 // generation ID is not valid
133 return false
134 } else {
135 ofc.generationID = request.GenerationId
136 ofc.generationIsDefined = true
137
138 if request.Role == ofp.OFPCRRoleMaster {
139 // master is potentially changing, find the existing master and set it to slave
140 for endpoint, connection := range ofc.connections {
141 if endpoint == from {
142 connection.role = ofcRoleMaster
143 } else if connection.role == ofcRoleMaster {
144 // the old master should be set to slave
145 connection.role = ofcRoleSlave
146 }
147 }
148 return true
149 } else if request.Role == ofp.OFPCRRoleSlave {
150 connection := ofc.connections[from]
151 connection.role = ofcRoleSlave
152 return true
153 }
154 }
155
156 return false
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800157}
158
David K. Bainbridge55376262020-01-22 23:28:27 -0800159// NewClient returns an initialized OFClient instance based on the configuration
160// specified
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800161func NewOFClient(config *OFClient) *OFClient {
162
163 ofc := OFClient{
Jonathan Hart4b110f62020-03-13 17:36:19 -0700164 DeviceID: config.DeviceID,
165 OFControllerEndPoints: config.OFControllerEndPoints,
166 VolthaClient: config.VolthaClient,
167 PacketOutChannel: config.PacketOutChannel,
168 ConnectionMaxRetries: config.ConnectionMaxRetries,
169 ConnectionRetryDelay: config.ConnectionRetryDelay,
170 connections: make(map[string]*OFConnection),
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800171 }
172
173 if ofc.ConnectionRetryDelay <= 0 {
174 logger.Warnw("connection retry delay not valid, setting to default",
175 log.Fields{
176 "device-id": ofc.DeviceID,
177 "value": ofc.ConnectionRetryDelay.String(),
178 "default": (3 * time.Second).String()})
179 ofc.ConnectionRetryDelay = 3 * time.Second
180 }
181 return &ofc
182}
183
David K. Bainbridge55376262020-01-22 23:28:27 -0800184// Stop initiates a shutdown of the OFClient
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800185func (ofc *OFClient) Stop() {
Jonathan Hart4b110f62020-03-13 17:36:19 -0700186 for _, connection := range ofc.connections {
187 connection.events <- ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800188 }
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800189}
190
191func (ofc *OFClient) Run(ctx context.Context) {
192
Jonathan Hart4b110f62020-03-13 17:36:19 -0700193 for _, endpoint := range ofc.OFControllerEndPoints {
194 connection := &OFConnection{
195 OFControllerEndPoint: endpoint,
196 DeviceID: ofc.DeviceID,
197 VolthaClient: ofc.VolthaClient,
198 PacketOutChannel: ofc.PacketOutChannel,
199 ConnectionMaxRetries: ofc.ConnectionMaxRetries,
200 ConnectionRetryDelay: ofc.ConnectionRetryDelay,
201 role: ofcRoleNone,
202 roleManager: ofc,
203 events: make(chan ofcEvent, 10),
204 sendChannel: make(chan Message, 100),
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800205 }
Jonathan Hart4b110f62020-03-13 17:36:19 -0700206
207 ofc.connections[endpoint] = connection
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800208 }
209
Jonathan Hart4b110f62020-03-13 17:36:19 -0700210 for _, connection := range ofc.connections {
211 go connection.Run(ctx)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800212 }
213}
214
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800215func (ofc *OFClient) SendMessage(message Message) error {
Jonathan Hart4b110f62020-03-13 17:36:19 -0700216 for _, connection := range ofc.connections {
217 if connection.role == ofcRoleMaster || connection.role == ofcRoleEqual {
218 err := connection.SendMessage(message)
219 if err != nil {
220 return err
221 }
222 }
223 }
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800224 return nil
225}