blob: ee4e77d030b14b7d744f8d9bed0c27216304108f [file] [log] [blame]
Kent Hagerman3136fbd2020-05-14 10:30:45 -04001/*
2 * Copyright 2018-present Open Networking Foundation
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 device
18
19import (
20 "context"
21 "fmt"
Kent Hagermanfa9d6d42020-05-25 11:49:40 -040022 "sync"
Kent Hagerman3136fbd2020-05-14 10:30:45 -040023
Kent Hagerman3136fbd2020-05-14 10:30:45 -040024 coreutils "github.com/opencord/voltha-go/rw_core/utils"
25 fu "github.com/opencord/voltha-lib-go/v3/pkg/flows"
26 "github.com/opencord/voltha-lib-go/v3/pkg/log"
Kent Hagerman3136fbd2020-05-14 10:30:45 -040027 ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
28 "github.com/opencord/voltha-protos/v3/go/voltha"
29 "google.golang.org/grpc/codes"
30 "google.golang.org/grpc/status"
31)
32
Kent Hagermanfa9d6d42020-05-25 11:49:40 -040033// listLogicalDevicePorts returns logical device ports
34func (agent *LogicalAgent) listLogicalDevicePorts() map[uint32]*voltha.LogicalPort {
35 logger.Debug("listLogicalDevicePorts")
36 portIDs := agent.portLoader.ListIDs()
37 ret := make(map[uint32]*voltha.LogicalPort, len(portIDs))
38 for portID := range portIDs {
39 if portHandle, have := agent.portLoader.Lock(portID); have {
40 ret[portID] = portHandle.GetReadOnly()
41 portHandle.Unlock()
42 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -040043 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -040044 return ret
Kent Hagerman3136fbd2020-05-14 10:30:45 -040045}
46
47func (agent *LogicalAgent) updateLogicalPort(ctx context.Context, device *voltha.Device, port *voltha.Port) error {
48 logger.Debugw("updateLogicalPort", log.Fields{"deviceId": device.Id, "port": port})
khenaidoo0db4c812020-05-27 15:27:30 -040049 switch port.Type {
50 case voltha.Port_ETHERNET_NNI:
Kent Hagermanfa9d6d42020-05-25 11:49:40 -040051 if err := agent.addNNILogicalPort(ctx, device, port); err != nil {
Kent Hagerman3136fbd2020-05-14 10:30:45 -040052 return err
53 }
khenaidoo0db4c812020-05-27 15:27:30 -040054 case voltha.Port_ETHERNET_UNI:
Kent Hagermanfa9d6d42020-05-25 11:49:40 -040055 if err := agent.addUNILogicalPort(ctx, device, port); err != nil {
Kent Hagerman3136fbd2020-05-14 10:30:45 -040056 return err
57 }
khenaidoo0db4c812020-05-27 15:27:30 -040058 case voltha.Port_PON_OLT:
59 // Rebuilt the routes on Parent PON port addition
60 go func() {
Kent Hagermanfa9d6d42020-05-25 11:49:40 -040061 if err := agent.buildRoutes(ctx); err != nil {
khenaidoo0db4c812020-05-27 15:27:30 -040062 // Not an error - temporary state
63 logger.Infow("failed-to-update-routes-after-adding-parent-pon-port", log.Fields{"device-id": device.Id, "port": port, "ports-count": len(device.Ports), "error": err})
64 }
65 }()
66 //fallthrough
67 case voltha.Port_PON_ONU:
68 // Add the routes corresponding to that child device
69 go func() {
Kent Hagermanfa9d6d42020-05-25 11:49:40 -040070 if err := agent.updateAllRoutes(ctx, device); err != nil {
khenaidoo0db4c812020-05-27 15:27:30 -040071 // Not an error - temporary state
72 logger.Infow("failed-to-update-routes-after-adding-child-pon-port", log.Fields{"device-id": device.Id, "port": port, "ports-count": len(device.Ports), "error": err})
73 }
74 }()
75 default:
76 return fmt.Errorf("invalid port type %v", port)
Kent Hagerman3136fbd2020-05-14 10:30:45 -040077 }
78 return nil
79}
80
81// setupLogicalPorts is invoked once the logical device has been created and is ready to get ports
82// added to it. While the logical device was being created we could have received requests to add
83// NNI and UNI ports which were discarded. Now is the time to add them if needed
84func (agent *LogicalAgent) setupLogicalPorts(ctx context.Context) error {
85 logger.Infow("setupLogicalPorts", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
86 // First add any NNI ports which could have been missing
87 if err := agent.setupNNILogicalPorts(ctx, agent.rootDeviceID); err != nil {
88 logger.Errorw("error-setting-up-NNI-ports", log.Fields{"error": err, "deviceId": agent.rootDeviceID})
89 return err
90 }
91
92 // Now, set up the UNI ports if needed.
93 children, err := agent.deviceMgr.GetAllChildDevices(ctx, agent.rootDeviceID)
94 if err != nil {
95 logger.Errorw("error-getting-child-devices", log.Fields{"error": err, "deviceId": agent.rootDeviceID})
96 return err
97 }
98 responses := make([]coreutils.Response, 0)
99 for _, child := range children.Items {
100 response := coreutils.NewResponse()
101 responses = append(responses, response)
102 go func(child *voltha.Device) {
103 if err = agent.setupUNILogicalPorts(context.Background(), child); err != nil {
104 logger.Error("setting-up-UNI-ports-failed", log.Fields{"deviceID": child.Id})
105 response.Error(status.Errorf(codes.Internal, "UNI-ports-setup-failed: %s", child.Id))
106 }
107 response.Done()
108 }(child)
109 }
110 // Wait for completion
111 if res := coreutils.WaitForNilOrErrorResponses(agent.defaultTimeout, responses...); res != nil {
112 return status.Errorf(codes.Aborted, "errors-%s", res)
113 }
114 return nil
115}
116
117// setupNNILogicalPorts creates an NNI port on the logical device that represents an NNI interface on a root device
118func (agent *LogicalAgent) setupNNILogicalPorts(ctx context.Context, deviceID string) error {
119 logger.Infow("setupNNILogicalPorts-start", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
120 // Build the logical device based on information retrieved from the device adapter
121 var err error
122
123 var device *voltha.Device
124 if device, err = agent.deviceMgr.getDevice(ctx, deviceID); err != nil {
125 logger.Errorw("error-retrieving-device", log.Fields{"error": err, "deviceId": deviceID})
126 return err
127 }
128
129 //Get UNI port number
130 for _, port := range device.Ports {
131 if port.Type == voltha.Port_ETHERNET_NNI {
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400132 if err = agent.addNNILogicalPort(ctx, device, port); err != nil {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400133 logger.Errorw("error-adding-UNI-port", log.Fields{"error": err})
134 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400135 }
136 }
137 return err
138}
139
140// updatePortState updates the port state of the device
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400141func (agent *LogicalAgent) updatePortState(ctx context.Context, portNo uint32, operStatus voltha.OperStatus_Types) error {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400142 logger.Infow("updatePortState-start", log.Fields{"logicalDeviceId": agent.logicalDeviceID, "portNo": portNo, "state": operStatus})
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400143
144 portHandle, have := agent.portLoader.Lock(portNo)
145 if !have {
146 return status.Errorf(codes.NotFound, "port-%d-not-exist", portNo)
147 }
148 defer portHandle.Unlock()
149
150 newPort := clonePortSetState(portHandle.GetReadOnly(), operStatus)
151 if err := portHandle.Update(ctx, newPort); err != nil {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400152 return err
153 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400154 agent.orderedEvents.send(agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_MODIFY, newPort.OfpPort)
155 return nil
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400156}
157
158// updatePortsState updates the ports state related to the device
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400159func (agent *LogicalAgent) updatePortsState(ctx context.Context, deviceID string, state voltha.OperStatus_Types) error {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400160 logger.Infow("updatePortsState-start", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400161
162 for portNo := range agent.portLoader.ListIDsForDevice(deviceID) {
163 if portHandle, have := agent.portLoader.Lock(portNo); have {
164 newPort := clonePortSetState(portHandle.GetReadOnly(), state)
165 if err := portHandle.Update(ctx, newPort); err != nil {
166 portHandle.Unlock()
167 return err
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400168 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400169 agent.orderedEvents.send(agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_MODIFY, newPort.OfpPort)
170
171 portHandle.Unlock()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400172 }
173 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400174 return nil
175}
176
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400177func clonePortSetState(oldPort *voltha.LogicalPort, state voltha.OperStatus_Types) *voltha.LogicalPort {
178 newPort := *oldPort // only clone the struct(s) that will be changed
179 newOfpPort := *oldPort.OfpPort
180 newPort.OfpPort = &newOfpPort
181
182 if state == voltha.OperStatus_ACTIVE {
183 newOfpPort.Config = newOfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
184 newOfpPort.State = uint32(ofp.OfpPortState_OFPPS_LIVE)
185 } else {
186 newOfpPort.Config = newOfpPort.Config | uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
187 newOfpPort.State = uint32(ofp.OfpPortState_OFPPS_LINK_DOWN)
188 }
189 return &newPort
190}
191
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400192// setupUNILogicalPorts creates a UNI port on the logical device that represents a child UNI interface
193func (agent *LogicalAgent) setupUNILogicalPorts(ctx context.Context, childDevice *voltha.Device) error {
194 logger.Infow("setupUNILogicalPort", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
195 // Build the logical device based on information retrieved from the device adapter
196 var err error
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400197 //Get UNI port number
198 for _, port := range childDevice.Ports {
199 if port.Type == voltha.Port_ETHERNET_UNI {
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400200 if err = agent.addUNILogicalPort(ctx, childDevice, port); err != nil {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400201 logger.Errorw("error-adding-UNI-port", log.Fields{"error": err})
202 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400203 }
204 }
205 return err
206}
207
208// deleteAllLogicalPorts deletes all logical ports associated with this logical device
209func (agent *LogicalAgent) deleteAllLogicalPorts(ctx context.Context) error {
210 logger.Infow("updatePortsState-start", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400211
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400212 // for each port
213 for portID := range agent.portLoader.ListIDs() {
214 // TODO: can just call agent.deleteLogicalPort()?
215 if portHandle, have := agent.portLoader.Lock(portID); have {
216 oldPort := portHandle.GetReadOnly()
217 // delete
218 err := portHandle.Delete(ctx)
219 portHandle.Unlock()
220 if err != nil {
221 return err
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400222 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400223 // and send event
224 agent.orderedEvents.send(agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_DELETE, oldPort.OfpPort)
225 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400226 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400227
228 // Reset the logical device routes
229 go func() {
230 if err := agent.buildRoutes(context.Background()); err != nil {
231 logger.Warnw("device-routes-not-ready", log.Fields{"logicalDeviceId": agent.logicalDeviceID, "error": err})
232 }
233 }()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400234 return nil
235}
236
237// deleteLogicalPorts removes the logical ports associated with that deviceId
238func (agent *LogicalAgent) deleteLogicalPorts(ctx context.Context, deviceID string) error {
239 logger.Debugw("deleting-logical-ports", log.Fields{"device-id": deviceID})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400240
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400241 // for each port
242 for portNo := range agent.portLoader.ListIDsForDevice(deviceID) {
243 if portHandle, have := agent.portLoader.Lock(portNo); have {
244 // if belongs to this device
245 if oldPort := portHandle.GetReadOnly(); oldPort.DeviceId == deviceID {
246 // delete
247 if err := portHandle.Delete(ctx); err != nil {
248 portHandle.Unlock()
249 return err
250 }
251 // and send event
252 agent.orderedEvents.send(agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_DELETE, oldPort.OfpPort)
253 }
254 portHandle.Unlock()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400255 }
256 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400257
258 // Reset the logical device routes
259 go func() {
260 if err := agent.buildRoutes(context.Background()); err != nil {
261 logger.Warnw("routes-not-ready", log.Fields{"logical-device-id": agent.logicalDeviceID, "error": err})
262 }
263 }()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400264 return nil
265}
266
267// enableLogicalPort enables the logical port
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400268func (agent *LogicalAgent) enableLogicalPort(ctx context.Context, lPortNo uint32) error {
269 portHandle, have := agent.portLoader.Lock(lPortNo)
270 if !have {
271 return status.Errorf(codes.NotFound, "port-%d-not-exist", lPortNo)
272 }
273 defer portHandle.Unlock()
274
275 oldPort := portHandle.GetReadOnly()
276
277 newPort := *oldPort // only clone the struct(s) that will be changed
278 newOfpPort := *oldPort.OfpPort
279 newPort.OfpPort = &newOfpPort
280
281 newOfpPort.Config = newOfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
282 if err := portHandle.Update(ctx, &newPort); err != nil {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400283 return err
284 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400285 agent.orderedEvents.send(agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_MODIFY, newPort.OfpPort)
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400286 return nil
287}
288
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400289// disableLogicalPort disabled the logical port
290func (agent *LogicalAgent) disableLogicalPort(ctx context.Context, lPortNo uint32) error {
291 portHandle, have := agent.portLoader.Lock(lPortNo)
292 if !have {
293 return status.Errorf(codes.NotFound, "port-%d-not-exist", lPortNo)
294 }
295 defer portHandle.Unlock()
296
297 oldPort := portHandle.GetReadOnly()
298
299 newPort := *oldPort // only clone the struct(s) that will be changed
300 newOfpPort := *oldPort.OfpPort
301 newPort.OfpPort = &newOfpPort
302
303 newOfpPort.Config = (newOfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)) | uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
304 if err := portHandle.Update(ctx, &newPort); err != nil {
khenaidoo0db4c812020-05-27 15:27:30 -0400305 return err
306 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400307 agent.orderedEvents.send(agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_MODIFY, newPort.OfpPort)
308 return nil
309}
310
311// addNNILogicalPort adds an NNI port to the logical device. It returns a bool representing whether a port has been
312// added and an error in case a valid error is encountered. If the port was successfully added it will return
313// (true, nil). If the device is not in the correct state it will return (false, nil) as this is a valid
314// scenario. This also applies to the case where the port was already added.
315func (agent *LogicalAgent) addNNILogicalPort(ctx context.Context, device *voltha.Device, port *voltha.Port) error {
316 logger.Debugw("addNNILogicalPort", log.Fields{"NNI": port})
317
318 label := fmt.Sprintf("nni-%d", port.PortNo)
319 tmpPort := &voltha.LogicalPort{
320 RootPort: true,
321 DeviceId: device.Id,
322 Id: label,
323 DevicePortNo: port.PortNo,
324 OfpPort: &voltha.OfpPort{
325 PortNo: port.PortNo,
326 Name: label,
327 },
328 OfpPortStats: &ofp.OfpPortStats{},
329 }
330
331 portHandle, created, err := agent.portLoader.LockOrCreate(ctx, tmpPort)
332 if err != nil {
333 return err
334 }
335 defer portHandle.Unlock()
336
337 if !created {
338 logger.Debugw("port-already-exist", log.Fields{"port": port})
339 return nil
340 }
341
342 // TODO: VOL-3202 Change the port creation logic to include the port capability. This will eliminate
343 // the port capability request that the Core makes following a port create event.
344 // TODO: VOL-3202 the port lock should not be held while getPortCapability() runs (preferably not while *any*
345 // external request runs), this is a temporary hack to avoid updating port state before the port is ready
346
347 // First get the port capability
348 portCap, err := agent.deviceMgr.getPortCapability(ctx, device.Id, port.PortNo)
349 if err != nil {
350 logger.Errorw("error-retrieving-port-capabilities", log.Fields{"error": err})
351 return err
352 }
353
354 newPort := portCap.Port
355 newPort.RootPort = true
356 newPort.DeviceId = device.Id
357 newPort.Id = label
358 newPort.DevicePortNo = port.PortNo
359 newPort.OfpPort.PortNo = port.PortNo
360 newPort.OfpPort.Name = label
361
362 // TODO: VOL-3202 shouldn't create tmp port then update, should prepare complete port first then LockOrCreate()
363 // the use of context.Background() is required to ensure we don't get an inconsistent logical port state
364 // while doing this, and can be removed later.
365 if err := portHandle.Update(ctx, newPort); err != nil {
366 if err := portHandle.Delete(context.Background()); err != nil {
367 return fmt.Errorf("unable-to-delete-%d: %s", port.PortNo, err)
368 }
369 return err
370 }
371
372 // ensure that no events will be sent until this one is
373 queuePosition := agent.orderedEvents.assignQueuePosition()
khenaidoo0db4c812020-05-27 15:27:30 -0400374
375 // Setup the routes for this device and then send the port update event to the OF Controller
376 go func() {
377 // First setup the routes
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400378 if err := agent.updateRoutes(context.Background(), device, newPort, agent.listLogicalDevicePorts()); err != nil {
khenaidoo0db4c812020-05-27 15:27:30 -0400379 // This is not an error as we may not have enough logical ports to set up routes or some PON ports have not been
380 // created yet.
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400381 logger.Infow("routes-not-ready", log.Fields{"logical-device-id": agent.logicalDeviceID, "logical-port": newPort.OfpPort.PortNo, "error": err})
khenaidoo0db4c812020-05-27 15:27:30 -0400382 }
383
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400384 // send event, and allow any queued events to be sent as well
385 queuePosition.send(agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_ADD, newPort.OfpPort)
khenaidoo0db4c812020-05-27 15:27:30 -0400386 }()
khenaidoo0db4c812020-05-27 15:27:30 -0400387 return nil
388}
389
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400390// addUNILogicalPort adds an UNI port to the logical device. It returns a bool representing whether a port has been
391// added and an error in case a valid error is encountered. If the port was successfully added it will return
392// (true, nil). If the device is not in the correct state it will return (false, nil) as this is a valid
393// scenario. This also applies to the case where the port was already added.
394func (agent *LogicalAgent) addUNILogicalPort(ctx context.Context, childDevice *voltha.Device, port *voltha.Port) error {
395 logger.Debugw("addUNILogicalPort", log.Fields{"port": port})
396 if childDevice.AdminState != voltha.AdminState_ENABLED || childDevice.OperStatus != voltha.OperStatus_ACTIVE {
397 logger.Infow("device-not-ready", log.Fields{"deviceId": childDevice.Id, "admin": childDevice.AdminState, "oper": childDevice.OperStatus})
398 return nil
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400399 }
400
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400401 tmpPort := &voltha.LogicalPort{
402 RootPort: false,
403 DeviceId: childDevice.Id,
404 Id: port.Label,
405 DevicePortNo: port.PortNo,
406 OfpPort: &voltha.OfpPort{
407 PortNo: port.PortNo,
408 },
409 OfpPortStats: &ofp.OfpPortStats{},
410 }
411
412 portHandle, created, err := agent.portLoader.LockOrCreate(ctx, tmpPort)
413 if err != nil {
414 return err
415 }
416 defer portHandle.Unlock()
417
418 if !created {
419 logger.Debugw("port-already-exist", log.Fields{"port": port})
420 return nil
421 }
422
423 // TODO: VOL-3202 Change the port creation logic to include the port capability. This will eliminate
424 // the port capability request that the Core makes following a port create event.
425 // TODO: VOL-3202 the port lock should not be held while getPortCapability() runs (preferably not while *any*
426 // external request runs), this is a temporary hack to avoid updating port state before the port is ready
427
428 // First get the port capability
429 portCap, err := agent.deviceMgr.getPortCapability(ctx, childDevice.Id, port.PortNo)
430 if err != nil {
431 logger.Errorw("error-retrieving-port-capabilities", log.Fields{"error": err})
432 return err
433 }
434
435 logger.Debugw("adding-uni", log.Fields{"deviceId": childDevice.Id})
436 newPort := portCap.Port
437 newPort.RootPort = false
438 newPort.DeviceId = childDevice.Id
439 newPort.Id = port.Label
440 newPort.DevicePortNo = port.PortNo
441 newPort.OfpPort.PortNo = port.PortNo
442
443 // TODO: VOL-3202 shouldn't create tmp port then update, should prepare complete port first then LockOrCreate()
444 // the use of context.Background() is required to ensure we don't get an inconsistent logical port state
445 // while doing this, and can be removed later.
446 if err := portHandle.Update(ctx, newPort); err != nil {
447 if err := portHandle.Delete(context.Background()); err != nil {
448 return fmt.Errorf("unable-to-delete-%d: %s", port.PortNo, err)
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400449 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400450 return err
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400451 }
452
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400453 // ensure that no events will be sent until this one is
454 queuePosition := agent.orderedEvents.assignQueuePosition()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400455
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400456 // Setup the routes for this device and then send the port update event to the OF Controller
457 go func() {
458 // First setup the routes
459 if err := agent.updateRoutes(context.Background(), childDevice, newPort, agent.listLogicalDevicePorts()); err != nil {
460 // This is not an error as we may not have enough logical ports to set up routes or some PON ports have not been
461 // created yet.
462 logger.Infow("routes-not-ready", log.Fields{"logical-device-id": agent.logicalDeviceID, "logical-port": newPort.OfpPort.PortNo, "error": err})
463 }
464 // send event, and allow any queued events to be sent as well
465 queuePosition.send(agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_ADD, newPort.OfpPort)
466 }()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400467 return nil
468}
469
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400470// send is a convenience to avoid calling both assignQueuePosition and qp.send
471func (e *orderedEvents) send(agent *LogicalAgent, deviceID string, reason ofp.OfpPortReason, desc *ofp.OfpPort) {
472 qp := e.assignQueuePosition()
473 go qp.send(agent, deviceID, reason, desc)
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400474}
475
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400476// TODO: shouldn't need to guarantee event ordering like this
477// event ordering should really be protected by per-LogicalPort lock
478// once routing uses on-demand calculation only, this should be changed
479// assignQueuePosition ensures that no events will be sent until this thread calls send() on the returned queuePosition
480func (e *orderedEvents) assignQueuePosition() queuePosition {
481 e.mutex.Lock()
482 defer e.mutex.Unlock()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400483
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400484 prev := e.last
485 next := make(chan struct{})
486 e.last = next
487 return queuePosition{
488 prev: prev,
489 next: next,
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400490 }
491}
492
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400493// orderedEvents guarantees the order that events are sent, while allowing events to back up.
494type orderedEvents struct {
495 mutex sync.Mutex
496 last <-chan struct{}
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400497}
498
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400499type queuePosition struct {
500 prev <-chan struct{}
501 next chan<- struct{}
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400502}
503
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400504// send waits for its turn, then sends the event, then notifies the next in line
505func (qp queuePosition) send(agent *LogicalAgent, deviceID string, reason ofp.OfpPortReason, desc *ofp.OfpPort) {
506 if qp.prev != nil {
507 <-qp.prev // wait for turn
508 }
509 agent.ldeviceMgr.SendChangeEvent(deviceID, reason, desc)
510 close(qp.next) // notify next
511}
512
513// GetWildcardInputPorts filters out the logical port number from the set of logical ports on the device and
514// returns their port numbers.
515func (agent *LogicalAgent) GetWildcardInputPorts(excludePort uint32) map[uint32]struct{} {
516 portIDs := agent.portLoader.ListIDs()
517 delete(portIDs, excludePort)
518 return portIDs
519}
520
521// isNNIPort return true iff the specified port belongs to the parent (OLT) device
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400522func (agent *LogicalAgent) isNNIPort(portNo uint32) bool {
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400523 portHandle, have := agent.portLoader.Lock(portNo)
524 if !have {
525 return false
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400526 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400527 defer portHandle.Unlock()
528
529 // any root-device logical port is an NNI port
530 return portHandle.GetReadOnly().RootPort
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400531}
532
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400533// getAnyNNIPort returns an NNI port
534func (agent *LogicalAgent) getAnyNNIPort() (uint32, error) {
535 for portID := range agent.portLoader.ListIDsForDevice(agent.rootDeviceID) {
536 return portID, nil
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400537 }
538 return 0, status.Error(codes.NotFound, "No NNI port found")
539}
540
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400541//GetNNIPorts returns all NNI ports
542func (agent *LogicalAgent) GetNNIPorts() map[uint32]struct{} {
543 return agent.portLoader.ListIDsForDevice(agent.rootDeviceID)
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400544}
545
546// getUNILogicalPortNo returns the UNI logical port number specified in the flow
547func (agent *LogicalAgent) getUNILogicalPortNo(flow *ofp.OfpFlowStats) (uint32, error) {
548 var uniPort uint32
549 inPortNo := fu.GetInPort(flow)
550 outPortNo := fu.GetOutPort(flow)
551 if agent.isNNIPort(inPortNo) {
552 uniPort = outPortNo
553 } else if agent.isNNIPort(outPortNo) {
554 uniPort = inPortNo
555 }
556 if uniPort != 0 {
557 return uniPort, nil
558 }
559 return 0, status.Errorf(codes.NotFound, "no-uni-port: %v", flow)
560}