blob: 7229e05b89f7acd88789a65c28ecf31fbdba7ae4 [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"
22
23 "github.com/gogo/protobuf/proto"
24 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"
27 ic "github.com/opencord/voltha-protos/v3/go/inter_container"
28 ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
29 "github.com/opencord/voltha-protos/v3/go/voltha"
30 "google.golang.org/grpc/codes"
31 "google.golang.org/grpc/status"
32)
33
34// ListLogicalDevicePorts returns logical device ports
35func (agent *LogicalAgent) ListLogicalDevicePorts(ctx context.Context) (*voltha.LogicalPorts, error) {
36 logger.Debug("ListLogicalDevicePorts")
37 logicalDevice, err := agent.GetLogicalDevice(ctx)
38 if err != nil {
39 return nil, err
40 }
41 if logicalDevice == nil {
42 return &voltha.LogicalPorts{}, nil
43 }
44 lPorts := make([]*voltha.LogicalPort, 0)
45 lPorts = append(lPorts, logicalDevice.Ports...)
46 return &voltha.LogicalPorts{Items: lPorts}, nil
47}
48
49func (agent *LogicalAgent) updateLogicalPort(ctx context.Context, device *voltha.Device, port *voltha.Port) error {
50 logger.Debugw("updateLogicalPort", log.Fields{"deviceId": device.Id, "port": port})
51 var err error
khenaidoo0db4c812020-05-27 15:27:30 -040052 switch port.Type {
53 case voltha.Port_ETHERNET_NNI:
Kent Hagerman3136fbd2020-05-14 10:30:45 -040054 if _, err = agent.addNNILogicalPort(ctx, device, port); err != nil {
55 return err
56 }
57 agent.addLogicalPortToMap(port.PortNo, true)
khenaidoo0db4c812020-05-27 15:27:30 -040058 case voltha.Port_ETHERNET_UNI:
Kent Hagerman3136fbd2020-05-14 10:30:45 -040059 if _, err = agent.addUNILogicalPort(ctx, device, port); err != nil {
60 return err
61 }
62 agent.addLogicalPortToMap(port.PortNo, false)
khenaidoo0db4c812020-05-27 15:27:30 -040063 case voltha.Port_PON_OLT:
64 // Rebuilt the routes on Parent PON port addition
65 go func() {
66 if err = agent.buildRoutes(ctx); err != nil {
67 // Not an error - temporary state
68 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})
69 }
70 }()
71 //fallthrough
72 case voltha.Port_PON_ONU:
73 // Add the routes corresponding to that child device
74 go func() {
75 if err = agent.updateAllRoutes(ctx, device); err != nil {
76 // Not an error - temporary state
77 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})
78 }
79 }()
80 default:
81 return fmt.Errorf("invalid port type %v", port)
Kent Hagerman3136fbd2020-05-14 10:30:45 -040082 }
83 return nil
84}
85
86// setupLogicalPorts is invoked once the logical device has been created and is ready to get ports
87// added to it. While the logical device was being created we could have received requests to add
88// NNI and UNI ports which were discarded. Now is the time to add them if needed
89func (agent *LogicalAgent) setupLogicalPorts(ctx context.Context) error {
90 logger.Infow("setupLogicalPorts", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
91 // First add any NNI ports which could have been missing
92 if err := agent.setupNNILogicalPorts(ctx, agent.rootDeviceID); err != nil {
93 logger.Errorw("error-setting-up-NNI-ports", log.Fields{"error": err, "deviceId": agent.rootDeviceID})
94 return err
95 }
96
97 // Now, set up the UNI ports if needed.
98 children, err := agent.deviceMgr.GetAllChildDevices(ctx, agent.rootDeviceID)
99 if err != nil {
100 logger.Errorw("error-getting-child-devices", log.Fields{"error": err, "deviceId": agent.rootDeviceID})
101 return err
102 }
103 responses := make([]coreutils.Response, 0)
104 for _, child := range children.Items {
105 response := coreutils.NewResponse()
106 responses = append(responses, response)
107 go func(child *voltha.Device) {
108 if err = agent.setupUNILogicalPorts(context.Background(), child); err != nil {
109 logger.Error("setting-up-UNI-ports-failed", log.Fields{"deviceID": child.Id})
110 response.Error(status.Errorf(codes.Internal, "UNI-ports-setup-failed: %s", child.Id))
111 }
112 response.Done()
113 }(child)
114 }
115 // Wait for completion
116 if res := coreutils.WaitForNilOrErrorResponses(agent.defaultTimeout, responses...); res != nil {
117 return status.Errorf(codes.Aborted, "errors-%s", res)
118 }
119 return nil
120}
121
122// setupNNILogicalPorts creates an NNI port on the logical device that represents an NNI interface on a root device
123func (agent *LogicalAgent) setupNNILogicalPorts(ctx context.Context, deviceID string) error {
124 logger.Infow("setupNNILogicalPorts-start", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
125 // Build the logical device based on information retrieved from the device adapter
126 var err error
127
128 var device *voltha.Device
129 if device, err = agent.deviceMgr.getDevice(ctx, deviceID); err != nil {
130 logger.Errorw("error-retrieving-device", log.Fields{"error": err, "deviceId": deviceID})
131 return err
132 }
133
134 //Get UNI port number
135 for _, port := range device.Ports {
136 if port.Type == voltha.Port_ETHERNET_NNI {
137 if _, err = agent.addNNILogicalPort(ctx, device, port); err != nil {
138 logger.Errorw("error-adding-UNI-port", log.Fields{"error": err})
139 }
140 agent.addLogicalPortToMap(port.PortNo, true)
141 }
142 }
143 return err
144}
145
146// updatePortState updates the port state of the device
147func (agent *LogicalAgent) updatePortState(ctx context.Context, deviceID string, portNo uint32, operStatus voltha.OperStatus_Types) error {
148 logger.Infow("updatePortState-start", log.Fields{"logicalDeviceId": agent.logicalDeviceID, "portNo": portNo, "state": operStatus})
149 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
150 return err
151 }
152 defer agent.requestQueue.RequestComplete()
153 // Get the latest logical device info
154 original := agent.getLogicalDeviceWithoutLock()
155 updatedPorts := clonePorts(original.Ports)
156 for _, port := range updatedPorts {
157 if port.DeviceId == deviceID && port.DevicePortNo == portNo {
158 if operStatus == voltha.OperStatus_ACTIVE {
159 port.OfpPort.Config = port.OfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
160 port.OfpPort.State = uint32(ofp.OfpPortState_OFPPS_LIVE)
161 } else {
162 port.OfpPort.Config = port.OfpPort.Config | uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
163 port.OfpPort.State = uint32(ofp.OfpPortState_OFPPS_LINK_DOWN)
164 }
165 // Update the logical device
166 if err := agent.updateLogicalDevicePortsWithoutLock(ctx, original, updatedPorts); err != nil {
167 logger.Errorw("error-updating-logical-device", log.Fields{"error": err})
168 return err
169 }
170 return nil
171 }
172 }
173 return status.Errorf(codes.NotFound, "port-%d-not-exist", portNo)
174}
175
176// updatePortsState updates the ports state related to the device
177func (agent *LogicalAgent) updatePortsState(ctx context.Context, device *voltha.Device, state voltha.OperStatus_Types) error {
178 logger.Infow("updatePortsState-start", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
179 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
180 return err
181 }
182 defer agent.requestQueue.RequestComplete()
183 // Get the latest logical device info
184 original := agent.getLogicalDeviceWithoutLock()
185 updatedPorts := clonePorts(original.Ports)
186 for _, port := range updatedPorts {
187 if port.DeviceId == device.Id {
188 if state == voltha.OperStatus_ACTIVE {
189 port.OfpPort.Config = port.OfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
190 port.OfpPort.State = uint32(ofp.OfpPortState_OFPPS_LIVE)
191 } else {
192 port.OfpPort.Config = port.OfpPort.Config | uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
193 port.OfpPort.State = uint32(ofp.OfpPortState_OFPPS_LINK_DOWN)
194 }
195 }
196 }
197 // Updating the logical device will trigger the poprt change events to be populated to the controller
198 if err := agent.updateLogicalDevicePortsWithoutLock(ctx, original, updatedPorts); err != nil {
199 logger.Warnw("logical-device-update-failed", log.Fields{"ldeviceId": agent.logicalDeviceID, "error": err})
200 return err
201 }
202 return nil
203}
204
205// setupUNILogicalPorts creates a UNI port on the logical device that represents a child UNI interface
206func (agent *LogicalAgent) setupUNILogicalPorts(ctx context.Context, childDevice *voltha.Device) error {
207 logger.Infow("setupUNILogicalPort", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
208 // Build the logical device based on information retrieved from the device adapter
209 var err error
210 var added bool
211 //Get UNI port number
212 for _, port := range childDevice.Ports {
213 if port.Type == voltha.Port_ETHERNET_UNI {
214 if added, err = agent.addUNILogicalPort(ctx, childDevice, port); err != nil {
215 logger.Errorw("error-adding-UNI-port", log.Fields{"error": err})
216 }
217 if added {
218 agent.addLogicalPortToMap(port.PortNo, false)
219 }
220 }
221 }
222 return err
223}
224
225// deleteAllLogicalPorts deletes all logical ports associated with this logical device
226func (agent *LogicalAgent) deleteAllLogicalPorts(ctx context.Context) error {
227 logger.Infow("updatePortsState-start", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
228 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
229 return err
230 }
231 defer agent.requestQueue.RequestComplete()
232 // Get the latest logical device info
233 cloned := agent.getLogicalDeviceWithoutLock()
234
235 if err := agent.updateLogicalDevicePortsWithoutLock(ctx, cloned, []*voltha.LogicalPort{}); err != nil {
236 logger.Warnw("logical-device-update-failed", log.Fields{"ldeviceId": agent.logicalDeviceID, "error": err})
237 return err
238 }
239 return nil
240}
241
242// deleteLogicalPort removes the logical port
243func (agent *LogicalAgent) deleteLogicalPort(ctx context.Context, lPort *voltha.LogicalPort) error {
244 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
245 return err
246 }
247 defer agent.requestQueue.RequestComplete()
248
249 logicalDevice := agent.getLogicalDeviceWithoutLock()
250
251 index := -1
252 for i, logicalPort := range logicalDevice.Ports {
253 if logicalPort.Id == lPort.Id {
254 index = i
255 break
256 }
257 }
258 if index >= 0 {
259 clonedPorts := clonePorts(logicalDevice.Ports)
260 if index < len(clonedPorts)-1 {
261 copy(clonedPorts[index:], clonedPorts[index+1:])
262 }
263 clonedPorts[len(clonedPorts)-1] = nil
264 clonedPorts = clonedPorts[:len(clonedPorts)-1]
265 logger.Debugw("logical-port-deleted", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
266 if err := agent.updateLogicalDevicePortsWithoutLock(ctx, logicalDevice, clonedPorts); err != nil {
267 logger.Errorw("logical-device-update-failed", log.Fields{"logicalDeviceId": agent.logicalDeviceID})
268 return err
269 }
270
271 // Remove the logical port from cache
272 agent.deleteLogicalPortsFromMap([]uint32{lPort.DevicePortNo})
273 // Reset the logical device routes
274 go func() {
275 if err := agent.buildRoutes(context.Background()); err != nil {
276 logger.Warnw("device-routes-not-ready", log.Fields{"logicalDeviceId": agent.logicalDeviceID, "error": err})
277 }
278 }()
279 }
280 return nil
281}
282
283// deleteLogicalPorts removes the logical ports associated with that deviceId
284func (agent *LogicalAgent) deleteLogicalPorts(ctx context.Context, deviceID string) error {
285 logger.Debugw("deleting-logical-ports", log.Fields{"device-id": deviceID})
286 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
287 return err
288 }
289 defer agent.requestQueue.RequestComplete()
290
291 logicalDevice := agent.getLogicalDeviceWithoutLock()
292 lPortstoKeep := []*voltha.LogicalPort{}
293 lPortsNoToDelete := []uint32{}
294 for _, logicalPort := range logicalDevice.Ports {
295 if logicalPort.DeviceId != deviceID {
296 lPortstoKeep = append(lPortstoKeep, logicalPort)
297 } else {
298 lPortsNoToDelete = append(lPortsNoToDelete, logicalPort.DevicePortNo)
299 }
300 }
301 logger.Debugw("deleted-logical-ports", log.Fields{"ports": lPortstoKeep})
302 if err := agent.updateLogicalDevicePortsWithoutLock(ctx, logicalDevice, lPortstoKeep); err != nil {
303 logger.Errorw("logical-device-update-failed", log.Fields{"logical-device-id": agent.logicalDeviceID})
304 return err
305 }
306 // Remove the port from the cached logical ports set
307 agent.deleteLogicalPortsFromMap(lPortsNoToDelete)
308
309 // Reset the logical device routes
310 go func() {
311 if err := agent.buildRoutes(context.Background()); err != nil {
312 logger.Warnw("routes-not-ready", log.Fields{"logical-device-id": agent.logicalDeviceID, "error": err})
313 }
314 }()
315
316 return nil
317}
318
319// enableLogicalPort enables the logical port
320func (agent *LogicalAgent) enableLogicalPort(ctx context.Context, lPortID string) error {
321 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
322 return err
323 }
324 defer agent.requestQueue.RequestComplete()
325
326 logicalDevice := agent.getLogicalDeviceWithoutLock()
327
328 index := -1
329 for i, logicalPort := range logicalDevice.Ports {
330 if logicalPort.Id == lPortID {
331 index = i
332 break
333 }
334 }
335 if index >= 0 {
336 clonedPorts := clonePorts(logicalDevice.Ports)
337 clonedPorts[index].OfpPort.Config = clonedPorts[index].OfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
338 return agent.updateLogicalDevicePortsWithoutLock(ctx, logicalDevice, clonedPorts)
339 }
340 return status.Errorf(codes.NotFound, "Port %s on Logical Device %s", lPortID, agent.logicalDeviceID)
341}
342
343// disableLogicalPort disabled the logical port
344func (agent *LogicalAgent) disableLogicalPort(ctx context.Context, lPortID string) error {
345 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
346 return err
347 }
348 defer agent.requestQueue.RequestComplete()
349
350 // Get the most up to date logical device
351 logicalDevice := agent.getLogicalDeviceWithoutLock()
352 index := -1
353 for i, logicalPort := range logicalDevice.Ports {
354 if logicalPort.Id == lPortID {
355 index = i
356 break
357 }
358 }
359 if index >= 0 {
360 clonedPorts := clonePorts(logicalDevice.Ports)
361 clonedPorts[index].OfpPort.Config = (clonedPorts[index].OfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)) | uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
362 return agent.updateLogicalDevicePortsWithoutLock(ctx, logicalDevice, clonedPorts)
363 }
364 return status.Errorf(codes.NotFound, "Port %s on Logical Device %s", lPortID, agent.logicalDeviceID)
365}
366
367// addNNILogicalPort adds an NNI port to the logical device. It returns a bool representing whether a port has been
368// added and an eror in case a valid error is encountered. If the port was successfully added it will return
369// (true, nil). If the device is not in the correct state it will return (false, nil) as this is a valid
370// scenario. This also applies to the case where the port was already added.
371func (agent *LogicalAgent) addNNILogicalPort(ctx context.Context, device *voltha.Device, port *voltha.Port) (bool, error) {
372 logger.Debugw("addNNILogicalPort", log.Fields{"NNI": port})
373
374 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
375 return false, err
376 }
khenaidoo0db4c812020-05-27 15:27:30 -0400377
378 defer agent.requestQueue.RequestComplete()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400379 if agent.portExist(device, port) {
380 logger.Debugw("port-already-exist", log.Fields{"port": port})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400381 return false, nil
382 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400383
khenaidoo0db4c812020-05-27 15:27:30 -0400384 // TODO: Change the port creation logic to include the port capability. This will eliminate the port capability
385 // request that the Core makes following a port create event.
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400386 var portCap *ic.PortCapability
387 var err error
388 // First get the port capability
389 if portCap, err = agent.deviceMgr.getPortCapability(ctx, device.Id, port.PortNo); err != nil {
390 logger.Errorw("error-retrieving-port-capabilities", log.Fields{"error": err})
391 return false, err
392 }
393
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400394 portCap.Port.RootPort = true
395 lp := (proto.Clone(portCap.Port)).(*voltha.LogicalPort)
396 lp.DeviceId = device.Id
397 lp.Id = fmt.Sprintf("nni-%d", port.PortNo)
398 lp.OfpPort.PortNo = port.PortNo
399 lp.OfpPort.Name = lp.Id
400 lp.DevicePortNo = port.PortNo
401
402 ld := agent.getLogicalDeviceWithoutLock()
403
404 clonedPorts := clonePorts(ld.Ports)
405 if clonedPorts == nil {
406 clonedPorts = make([]*voltha.LogicalPort, 0)
407 }
408 clonedPorts = append(clonedPorts, lp)
409
khenaidoo0db4c812020-05-27 15:27:30 -0400410 if err = agent.addLogicalDevicePortsWithoutLock(ctx, ld, clonedPorts, lp, device); err != nil {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400411 logger.Errorw("error-updating-logical-device", log.Fields{"error": err})
412 return false, err
413 }
414
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400415 return true, nil
416}
417
418func (agent *LogicalAgent) portExist(device *voltha.Device, port *voltha.Port) bool {
419 ldevice := agent.getLogicalDeviceWithoutLock()
420 for _, lPort := range ldevice.Ports {
Chaitrashree G S3b3f4a62020-05-21 04:56:33 -0400421 if lPort.DeviceId == device.Id && lPort.DevicePortNo == port.PortNo {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400422 return true
423 }
424 }
425 return false
426}
427
428// addUNILogicalPort adds an UNI port to the logical device. It returns a bool representing whether a port has been
429// added and an eror in case a valid error is encountered. If the port was successfully added it will return
430// (true, nil). If the device is not in the correct state it will return (false, nil) as this is a valid
431// scenario. This also applies to the case where the port was already added.
432func (agent *LogicalAgent) addUNILogicalPort(ctx context.Context, childDevice *voltha.Device, port *voltha.Port) (bool, error) {
433 logger.Debugw("addUNILogicalPort", log.Fields{"port": port})
434 if childDevice.AdminState != voltha.AdminState_ENABLED || childDevice.OperStatus != voltha.OperStatus_ACTIVE {
435 logger.Infow("device-not-ready", log.Fields{"deviceId": childDevice.Id, "admin": childDevice.AdminState, "oper": childDevice.OperStatus})
436 return false, nil
437 }
438 if err := agent.requestQueue.WaitForGreenLight(ctx); err != nil {
439 return false, err
440 }
khenaidoo0db4c812020-05-27 15:27:30 -0400441 defer agent.requestQueue.RequestComplete()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400442
443 if agent.portExist(childDevice, port) {
444 logger.Debugw("port-already-exist", log.Fields{"port": port})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400445 return false, nil
446 }
khenaidoo0db4c812020-05-27 15:27:30 -0400447
448 // TODO: Change the port creation logic to include the port capability. This will eliminate the port capability
449 // request that the Core makes following a port create event.
450
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400451 var portCap *ic.PortCapability
452 var err error
453 // First get the port capability
454 if portCap, err = agent.deviceMgr.getPortCapability(ctx, childDevice.Id, port.PortNo); err != nil {
455 logger.Errorw("error-retrieving-port-capabilities", log.Fields{"error": err})
456 return false, err
457 }
khenaidoo0db4c812020-05-27 15:27:30 -0400458
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400459 // Get stored logical device
460 ldevice := agent.getLogicalDeviceWithoutLock()
461
462 logger.Debugw("adding-uni", log.Fields{"deviceId": childDevice.Id})
463 portCap.Port.RootPort = false
464 portCap.Port.Id = port.Label
465 portCap.Port.OfpPort.PortNo = port.PortNo
466 portCap.Port.DeviceId = childDevice.Id
467 portCap.Port.DevicePortNo = port.PortNo
468 clonedPorts := clonePorts(ldevice.Ports)
469 if clonedPorts == nil {
470 clonedPorts = make([]*voltha.LogicalPort, 0)
471 }
472 clonedPorts = append(clonedPorts, portCap.Port)
khenaidoo0db4c812020-05-27 15:27:30 -0400473
474 if err = agent.addLogicalDevicePortsWithoutLock(ctx, ldevice, clonedPorts, portCap.Port, childDevice); err != nil {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400475 return false, err
476 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400477
478 return true, nil
479}
480
481func clonePorts(ports []*voltha.LogicalPort) []*voltha.LogicalPort {
482 return proto.Clone(&voltha.LogicalPorts{Items: ports}).(*voltha.LogicalPorts).Items
483}
484
485//updateLogicalDevicePortsWithoutLock updates the
486func (agent *LogicalAgent) updateLogicalDevicePortsWithoutLock(ctx context.Context, device *voltha.LogicalDevice, newPorts []*voltha.LogicalPort) error {
487 oldPorts := device.Ports
488 device.Ports = newPorts
489 if err := agent.updateLogicalDeviceWithoutLock(ctx, device); err != nil {
490 return err
491 }
492 agent.portUpdated(oldPorts, newPorts)
493 return nil
494}
495
khenaidoo0db4c812020-05-27 15:27:30 -0400496// addLogicalDevicePortsWithoutLock add the new ports to the logical device, update routes associated with those new
497// ports and send an add port event to the OF controller
498func (agent *LogicalAgent) addLogicalDevicePortsWithoutLock(ctx context.Context, lDevice *voltha.LogicalDevice, newPorts []*voltha.LogicalPort, lp *voltha.LogicalPort, device *voltha.Device) error {
499 oldPorts := lDevice.Ports
500 lDevice.Ports = newPorts
501 if err := agent.updateLogicalDeviceWithoutLock(ctx, lDevice); err != nil {
502 return err
503 }
504
505 // Setup the routes for this device and then send the port update event to the OF Controller
506 go func() {
507 // First setup the routes
508 if err := agent.updateRoutes(context.Background(), device, lp, newPorts); err != nil {
509 // This is not an error as we may not have enough logical ports to set up routes or some PON ports have not been
510 // created yet.
511 logger.Infow("routes-not-ready", log.Fields{"logical-device-id": agent.logicalDeviceID, "logical-port": lp.OfpPort.PortNo, "error": err})
512 }
513
514 // Send a port update event
515 agent.portUpdated(oldPorts, newPorts)
516 }()
517
518 return nil
519}
520
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400521// diff go over two lists of logical ports and return what's new, what's changed and what's removed.
522func diff(oldList, newList []*voltha.LogicalPort) (newPorts, changedPorts, deletedPorts map[string]*voltha.LogicalPort) {
523 newPorts = make(map[string]*voltha.LogicalPort, len(newList))
524 changedPorts = make(map[string]*voltha.LogicalPort, len(oldList))
525 deletedPorts = make(map[string]*voltha.LogicalPort, len(oldList))
526
527 for _, n := range newList {
528 newPorts[n.Id] = n
529 }
530
531 for _, o := range oldList {
532 if n, have := newPorts[o.Id]; have {
533 delete(newPorts, o.Id) // not new
534 if !proto.Equal(n, o) {
535 changedPorts[n.Id] = n // changed
536 }
537 } else {
538 deletedPorts[o.Id] = o // deleted
539 }
540 }
541
542 return newPorts, changedPorts, deletedPorts
543}
544
545// portUpdated is invoked when a port is updated on the logical device
546func (agent *LogicalAgent) portUpdated(prevPorts, currPorts []*voltha.LogicalPort) interface{} {
547 // Get the difference between the two list
548 newPorts, changedPorts, deletedPorts := diff(prevPorts, currPorts)
549
550 // Send the port change events to the OF controller
551 for _, newP := range newPorts {
552 go agent.ldeviceMgr.SendChangeEvent(agent.logicalDeviceID,
553 &ofp.OfpPortStatus{Reason: ofp.OfpPortReason_OFPPR_ADD, Desc: newP.OfpPort})
554 }
555 for _, change := range changedPorts {
556 go agent.ldeviceMgr.SendChangeEvent(agent.logicalDeviceID,
557 &ofp.OfpPortStatus{Reason: ofp.OfpPortReason_OFPPR_MODIFY, Desc: change.OfpPort})
558 }
559 for _, del := range deletedPorts {
560 go agent.ldeviceMgr.SendChangeEvent(agent.logicalDeviceID,
561 &ofp.OfpPortStatus{Reason: ofp.OfpPortReason_OFPPR_DELETE, Desc: del.OfpPort})
562 }
563
564 return nil
565}
566
567//GetWildcardInputPorts filters out the logical port number from the set of logical ports on the device and
568//returns their port numbers. This function is invoked only during flow decomposition where the lock on the logical
569//device is already held. Therefore it is safe to retrieve the logical device without lock.
570func (agent *LogicalAgent) GetWildcardInputPorts(excludePort ...uint32) []uint32 {
571 lPorts := make([]uint32, 0)
572 var exclPort uint32
573 if len(excludePort) == 1 {
574 exclPort = excludePort[0]
575 }
576 lDevice := agent.getLogicalDeviceWithoutLock()
577 for _, port := range lDevice.Ports {
578 if port.OfpPort.PortNo != exclPort {
579 lPorts = append(lPorts, port.OfpPort.PortNo)
580 }
581 }
582 return lPorts
583}
584
585// helpers for agent.logicalPortsNo
586
587func (agent *LogicalAgent) addLogicalPortToMap(portNo uint32, nniPort bool) {
588 agent.lockLogicalPortsNo.Lock()
589 defer agent.lockLogicalPortsNo.Unlock()
590 if exist := agent.logicalPortsNo[portNo]; !exist {
591 agent.logicalPortsNo[portNo] = nniPort
592 }
593}
594
595func (agent *LogicalAgent) addLogicalPortsToMap(lps []*voltha.LogicalPort) {
596 agent.lockLogicalPortsNo.Lock()
597 defer agent.lockLogicalPortsNo.Unlock()
598 for _, lp := range lps {
599 if exist := agent.logicalPortsNo[lp.DevicePortNo]; !exist {
600 agent.logicalPortsNo[lp.DevicePortNo] = lp.RootPort
601 }
602 }
603}
604
605func (agent *LogicalAgent) deleteLogicalPortsFromMap(portsNo []uint32) {
606 agent.lockLogicalPortsNo.Lock()
607 defer agent.lockLogicalPortsNo.Unlock()
608 for _, pNo := range portsNo {
609 delete(agent.logicalPortsNo, pNo)
610 }
611}
612
613func (agent *LogicalAgent) isNNIPort(portNo uint32) bool {
614 agent.lockLogicalPortsNo.RLock()
615 defer agent.lockLogicalPortsNo.RUnlock()
616 if exist := agent.logicalPortsNo[portNo]; exist {
617 return agent.logicalPortsNo[portNo]
618 }
619 return false
620}
621
622func (agent *LogicalAgent) getFirstNNIPort() (uint32, error) {
623 agent.lockLogicalPortsNo.RLock()
624 defer agent.lockLogicalPortsNo.RUnlock()
625 for portNo, nni := range agent.logicalPortsNo {
626 if nni {
627 return portNo, nil
628 }
629 }
630 return 0, status.Error(codes.NotFound, "No NNI port found")
631}
632
633//GetNNIPorts returns NNI ports.
634func (agent *LogicalAgent) GetNNIPorts() []uint32 {
635 agent.lockLogicalPortsNo.RLock()
636 defer agent.lockLogicalPortsNo.RUnlock()
637 nniPorts := make([]uint32, 0)
638 for portNo, nni := range agent.logicalPortsNo {
639 if nni {
640 nniPorts = append(nniPorts, portNo)
641 }
642 }
643 return nniPorts
644}
645
646// getUNILogicalPortNo returns the UNI logical port number specified in the flow
647func (agent *LogicalAgent) getUNILogicalPortNo(flow *ofp.OfpFlowStats) (uint32, error) {
648 var uniPort uint32
649 inPortNo := fu.GetInPort(flow)
650 outPortNo := fu.GetOutPort(flow)
651 if agent.isNNIPort(inPortNo) {
652 uniPort = outPortNo
653 } else if agent.isNNIPort(outPortNo) {
654 uniPort = inPortNo
655 }
656 if uniPort != 0 {
657 return uniPort, nil
658 }
659 return 0, status.Errorf(codes.NotFound, "no-uni-port: %v", flow)
660}