blob: 54fa6208523b822aa45b4db11463ba926c45ae36 [file] [log] [blame]
Kent Hagerman3136fbd2020-05-14 10:30:45 -04001/*
Joey Armstrong5f51f2e2023-01-17 17:06:26 -05002 * Copyright 2018-2023 Open Networking Foundation (ONF) and the ONF Contributors
Kent Hagerman3136fbd2020-05-14 10:30:45 -04003
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"
khenaidood948f772021-08-11 17:49:24 -040025 fu "github.com/opencord/voltha-lib-go/v7/pkg/flows"
26 "github.com/opencord/voltha-lib-go/v7/pkg/log"
27 ofp "github.com/opencord/voltha-protos/v5/go/openflow_13"
28 "github.com/opencord/voltha-protos/v5/go/voltha"
Kent Hagerman3136fbd2020-05-14 10:30:45 -040029 "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
Rohan Agrawal31f21802020-06-12 05:38:46 +000034func (agent *LogicalAgent) listLogicalDevicePorts(ctx context.Context) map[uint32]*voltha.LogicalPort {
Kent Hagermanfa9d6d42020-05-25 11:49:40 -040035 portIDs := agent.portLoader.ListIDs()
khenaidoo7585a962021-06-10 16:15:38 -040036 logger.Debugw(ctx, "list-logical-device-ports", log.Fields{"num-ports": len(portIDs)})
Kent Hagermanfa9d6d42020-05-25 11:49:40 -040037 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
Kent Hagerman2a07b862020-06-19 15:23:07 -040047func (agent *LogicalAgent) updateLogicalPort(ctx context.Context, device *voltha.Device, devicePorts map[uint32]*voltha.Port, port *voltha.Port) error {
Himani Chawlab4c25912020-11-12 17:16:38 +053048 logger.Debugw(ctx, "update-logical-port", log.Fields{"device-id": device.Id, "port": port})
khenaidoo0db4c812020-05-27 15:27:30 -040049 switch port.Type {
50 case voltha.Port_ETHERNET_NNI:
Kent Hagerman2a07b862020-06-19 15:23:07 -040051 if err := agent.addNNILogicalPort(ctx, device.Id, devicePorts, 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 Hagerman2a07b862020-06-19 15:23:07 -040055 if err := agent.addUNILogicalPort(ctx, device.Id, device.AdminState, device.OperStatus, devicePorts, 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
Himani Chawlab4c25912020-11-12 17:16:38 +053060
nikesh.krishnan95142d52023-02-24 15:32:11 +053061 if err := agent.buildRoutes(ctx); err != nil {
62 // Not an error - temporary state
63 logger.Infow(ctx, "failed-to-update-routes-after-adding-parent-pon-port", log.Fields{"device-id": device.Id, "port": port, "ports-count": len(devicePorts), "error": err})
64 }
65
khenaidoo0db4c812020-05-27 15:27:30 -040066 //fallthrough
67 case voltha.Port_PON_ONU:
68 // Add the routes corresponding to that child device
nikesh.krishnan95142d52023-02-24 15:32:11 +053069
70 if err := agent.updateAllRoutes(ctx, device.Id, devicePorts); err != nil {
71 // Not an error - temporary state
72 logger.Infow(ctx, "failed-to-update-routes-after-adding-child-pon-port", log.Fields{"device-id": device.Id, "port": port, "ports-count": len(devicePorts), "error": err})
73 }
74
khenaidoo0db4c812020-05-27 15:27:30 -040075 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 {
Himani Chawlab4c25912020-11-12 17:16:38 +053085 logger.Infow(ctx, "setup-logical-ports", log.Fields{"logical-device-id": agent.logicalDeviceID})
Kent Hagerman3136fbd2020-05-14 10:30:45 -040086 // First add any NNI ports which could have been missing
87 if err := agent.setupNNILogicalPorts(ctx, agent.rootDeviceID); err != nil {
Himani Chawlab4c25912020-11-12 17:16:38 +053088 logger.Errorw(ctx, "error-setting-up-nni-ports", log.Fields{"error": err, "device-id": agent.rootDeviceID})
Kent Hagerman3136fbd2020-05-14 10:30:45 -040089 return err
90 }
91
92 // Now, set up the UNI ports if needed.
khenaidood948f772021-08-11 17:49:24 -040093 children, err := agent.deviceMgr.getAllChildDevices(ctx, agent.rootDeviceID)
Kent Hagerman3136fbd2020-05-14 10:30:45 -040094 if err != nil {
divyadesaicb8b59d2020-08-18 09:55:47 +000095 logger.Errorw(ctx, "error-getting-child-devices", log.Fields{"error": err, "device-id": agent.rootDeviceID})
Kent Hagerman3136fbd2020-05-14 10:30:45 -040096 return err
97 }
98 responses := make([]coreutils.Response, 0)
99 for _, child := range children.Items {
100 response := coreutils.NewResponse()
101 responses = append(responses, response)
Himani Chawlab4c25912020-11-12 17:16:38 +0530102 subCtx := coreutils.WithSpanAndRPCMetadataFromContext(ctx)
Kent Hagerman2a07b862020-06-19 15:23:07 -0400103 go func(ctx context.Context, child *voltha.Device) {
104 defer response.Done()
105
106 childPorts, err := agent.deviceMgr.listDevicePorts(ctx, child.Id)
107 if err != nil {
Himani Chawlab4c25912020-11-12 17:16:38 +0530108 logger.Error(ctx, "setting-up-uni-ports-failed", log.Fields{"device-id": child.Id})
Kent Hagerman2a07b862020-06-19 15:23:07 -0400109 response.Error(status.Errorf(codes.Internal, "UNI-ports-setup-failed: %s", child.Id))
110 return
111 }
112
113 if err = agent.setupUNILogicalPorts(ctx, child, childPorts); err != nil {
Himani Chawlab4c25912020-11-12 17:16:38 +0530114 logger.Error(ctx, "setting-up-uni-ports-failed", log.Fields{"device-id": child.Id})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400115 response.Error(status.Errorf(codes.Internal, "UNI-ports-setup-failed: %s", child.Id))
116 }
Himani Chawlab4c25912020-11-12 17:16:38 +0530117 }(subCtx, child)
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400118 }
119 // Wait for completion
khenaidood948f772021-08-11 17:49:24 -0400120 if res := coreutils.WaitForNilOrErrorResponses(agent.internalTimeout, responses...); res != nil {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400121 return status.Errorf(codes.Aborted, "errors-%s", res)
122 }
123 return nil
124}
125
126// setupNNILogicalPorts creates an NNI port on the logical device that represents an NNI interface on a root device
127func (agent *LogicalAgent) setupNNILogicalPorts(ctx context.Context, deviceID string) error {
Himani Chawlab4c25912020-11-12 17:16:38 +0530128 logger.Infow(ctx, "setup-nni-logical-ports-start", log.Fields{"logical-device-id": agent.logicalDeviceID})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400129 // Build the logical device based on information retrieved from the device adapter
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400130
Kent Hagerman2a07b862020-06-19 15:23:07 -0400131 devicePorts, err := agent.deviceMgr.listDevicePorts(ctx, deviceID)
132 if err != nil {
divyadesaicb8b59d2020-08-18 09:55:47 +0000133 logger.Errorw(ctx, "error-retrieving-device-ports", log.Fields{"error": err, "device-id": deviceID})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400134 return err
135 }
136
137 //Get UNI port number
Kent Hagerman2a07b862020-06-19 15:23:07 -0400138 for _, port := range devicePorts {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400139 if port.Type == voltha.Port_ETHERNET_NNI {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400140 if err = agent.addNNILogicalPort(ctx, deviceID, devicePorts, port); err != nil {
Himani Chawlab4c25912020-11-12 17:16:38 +0530141 logger.Errorw(ctx, "error-adding-nni-port", log.Fields{"error": err})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400142 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400143 }
144 }
145 return err
146}
147
148// updatePortState updates the port state of the device
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400149func (agent *LogicalAgent) updatePortState(ctx context.Context, portNo uint32, operStatus voltha.OperStatus_Types) error {
Himani Chawlab4c25912020-11-12 17:16:38 +0530150 logger.Infow(ctx, "update-port-state-start", log.Fields{"logical-device-id": agent.logicalDeviceID, "port-no": portNo, "state": operStatus})
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400151
152 portHandle, have := agent.portLoader.Lock(portNo)
153 if !have {
154 return status.Errorf(codes.NotFound, "port-%d-not-exist", portNo)
155 }
156 defer portHandle.Unlock()
157
158 newPort := clonePortSetState(portHandle.GetReadOnly(), operStatus)
159 if err := portHandle.Update(ctx, newPort); err != nil {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400160 return err
161 }
Rohan Agrawal31f21802020-06-12 05:38:46 +0000162 agent.orderedEvents.send(ctx, agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_MODIFY, newPort.OfpPort)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400163 return nil
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400164}
165
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400166func clonePortSetState(oldPort *voltha.LogicalPort, state voltha.OperStatus_Types) *voltha.LogicalPort {
167 newPort := *oldPort // only clone the struct(s) that will be changed
168 newOfpPort := *oldPort.OfpPort
169 newPort.OfpPort = &newOfpPort
170
171 if state == voltha.OperStatus_ACTIVE {
Andrey Pozolotin34dd63f2021-05-31 21:26:40 +0300172 newOfpPort.Config &= ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400173 newOfpPort.State = uint32(ofp.OfpPortState_OFPPS_LIVE)
174 } else {
Andrey Pozolotin34dd63f2021-05-31 21:26:40 +0300175 newOfpPort.Config |= uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400176 newOfpPort.State = uint32(ofp.OfpPortState_OFPPS_LINK_DOWN)
177 }
178 return &newPort
179}
180
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400181// setupUNILogicalPorts creates a UNI port on the logical device that represents a child UNI interface
Kent Hagerman2a07b862020-06-19 15:23:07 -0400182func (agent *LogicalAgent) setupUNILogicalPorts(ctx context.Context, childDevice *voltha.Device, childDevicePorts map[uint32]*voltha.Port) error {
Himani Chawlab4c25912020-11-12 17:16:38 +0530183 logger.Infow(ctx, "setup-uni-logical-ports", log.Fields{"logical-device-id": agent.logicalDeviceID})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400184 // Build the logical device based on information retrieved from the device adapter
185 var err error
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400186 //Get UNI port number
Kent Hagerman2a07b862020-06-19 15:23:07 -0400187 for _, port := range childDevicePorts {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400188 if port.Type == voltha.Port_ETHERNET_UNI {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400189 if err = agent.addUNILogicalPort(ctx, childDevice.Id, childDevice.AdminState, childDevice.OperStatus, childDevicePorts, port); err != nil {
Himani Chawlab4c25912020-11-12 17:16:38 +0530190 logger.Errorw(ctx, "error-adding-uni-port", log.Fields{"error": err})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400191 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400192 }
193 }
194 return err
195}
196
197// deleteAllLogicalPorts deletes all logical ports associated with this logical device
198func (agent *LogicalAgent) deleteAllLogicalPorts(ctx context.Context) error {
Himani Chawlab4c25912020-11-12 17:16:38 +0530199 logger.Infow(ctx, "update-ports-state-start", log.Fields{"logical-device-id": agent.logicalDeviceID})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400200
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400201 // for each port
202 for portID := range agent.portLoader.ListIDs() {
203 // TODO: can just call agent.deleteLogicalPort()?
204 if portHandle, have := agent.portLoader.Lock(portID); have {
205 oldPort := portHandle.GetReadOnly()
206 // delete
207 err := portHandle.Delete(ctx)
208 portHandle.Unlock()
209 if err != nil {
210 return err
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400211 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400212 // and send event
Rohan Agrawal31f21802020-06-12 05:38:46 +0000213 agent.orderedEvents.send(ctx, agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_DELETE, oldPort.OfpPort)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400214 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400215 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400216
217 // Reset the logical device routes
nikesh.krishnan95142d52023-02-24 15:32:11 +0530218 if err := agent.removeRoutes(ctx); err != nil {
219 logger.Warnw(ctx, "error-removing-routes", log.Fields{"logical-device-id": agent.logicalDeviceID, "error": err})
220 }
221
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400222 return nil
223}
224
225// deleteLogicalPorts removes the logical ports associated with that deviceId
226func (agent *LogicalAgent) deleteLogicalPorts(ctx context.Context, deviceID string) error {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000227 logger.Debugw(ctx, "deleting-logical-ports", log.Fields{"device-id": deviceID})
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400228
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400229 // for each port
230 for portNo := range agent.portLoader.ListIDsForDevice(deviceID) {
231 if portHandle, have := agent.portLoader.Lock(portNo); have {
232 // if belongs to this device
233 if oldPort := portHandle.GetReadOnly(); oldPort.DeviceId == deviceID {
234 // delete
235 if err := portHandle.Delete(ctx); err != nil {
236 portHandle.Unlock()
237 return err
238 }
239 // and send event
Rohan Agrawal31f21802020-06-12 05:38:46 +0000240 agent.orderedEvents.send(ctx, agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_DELETE, oldPort.OfpPort)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400241 }
242 portHandle.Unlock()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400243 }
244 }
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400245
246 // Reset the logical device routes
nikesh.krishnan95142d52023-02-24 15:32:11 +0530247
248 if err := agent.buildRoutes(ctx); err != nil {
249 logger.Warnw(ctx, "routes-not-ready", log.Fields{"logical-device-id": agent.logicalDeviceID, "error": err})
250 }
251
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400252 return nil
253}
254
255// enableLogicalPort enables the logical port
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400256func (agent *LogicalAgent) enableLogicalPort(ctx context.Context, lPortNo uint32) error {
257 portHandle, have := agent.portLoader.Lock(lPortNo)
258 if !have {
259 return status.Errorf(codes.NotFound, "port-%d-not-exist", lPortNo)
260 }
261 defer portHandle.Unlock()
262
263 oldPort := portHandle.GetReadOnly()
264
265 newPort := *oldPort // only clone the struct(s) that will be changed
266 newOfpPort := *oldPort.OfpPort
267 newPort.OfpPort = &newOfpPort
268
Andrey Pozolotin34dd63f2021-05-31 21:26:40 +0300269 newOfpPort.Config &= ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400270 if err := portHandle.Update(ctx, &newPort); err != nil {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400271 return err
272 }
Rohan Agrawal31f21802020-06-12 05:38:46 +0000273 agent.orderedEvents.send(ctx, agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_MODIFY, newPort.OfpPort)
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400274 return nil
275}
276
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400277// disableLogicalPort disabled the logical port
278func (agent *LogicalAgent) disableLogicalPort(ctx context.Context, lPortNo uint32) error {
279 portHandle, have := agent.portLoader.Lock(lPortNo)
280 if !have {
281 return status.Errorf(codes.NotFound, "port-%d-not-exist", lPortNo)
282 }
283 defer portHandle.Unlock()
284
285 oldPort := portHandle.GetReadOnly()
286
287 newPort := *oldPort // only clone the struct(s) that will be changed
288 newOfpPort := *oldPort.OfpPort
289 newPort.OfpPort = &newOfpPort
290
291 newOfpPort.Config = (newOfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)) | uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
292 if err := portHandle.Update(ctx, &newPort); err != nil {
khenaidoo0db4c812020-05-27 15:27:30 -0400293 return err
294 }
Rohan Agrawal31f21802020-06-12 05:38:46 +0000295 agent.orderedEvents.send(ctx, agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_MODIFY, newPort.OfpPort)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400296 return nil
297}
298
299// addNNILogicalPort adds an NNI port to the logical device. It returns a bool representing whether a port has been
300// added and an error in case a valid error is encountered. If the port was successfully added it will return
301// (true, nil). If the device is not in the correct state it will return (false, nil) as this is a valid
302// scenario. This also applies to the case where the port was already added.
Kent Hagerman2a07b862020-06-19 15:23:07 -0400303func (agent *LogicalAgent) addNNILogicalPort(ctx context.Context, deviceID string, devicePorts map[uint32]*voltha.Port, port *voltha.Port) error {
Himani Chawlab4c25912020-11-12 17:16:38 +0530304 logger.Debugw(ctx, "add-nni-logical-port", log.Fields{"logical-device-id": agent.logicalDeviceID, "nni-port": port})
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400305
306 label := fmt.Sprintf("nni-%d", port.PortNo)
khenaidooc6c7bda2020-06-17 17:20:18 -0400307 ofpPort := *port.OfpPort
308 ofpPort.HwAddr = append([]uint32{}, port.OfpPort.HwAddr...)
309 ofpPort.PortNo = port.PortNo
310 ofpPort.Name = label
311 nniPort := &voltha.LogicalPort{
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400312 RootPort: true,
Kent Hagerman2a07b862020-06-19 15:23:07 -0400313 DeviceId: deviceID,
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400314 Id: label,
315 DevicePortNo: port.PortNo,
khenaidooc6c7bda2020-06-17 17:20:18 -0400316 OfpPort: &ofpPort,
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400317 OfpPortStats: &ofp.OfpPortStats{},
318 }
319
khenaidooc6c7bda2020-06-17 17:20:18 -0400320 portHandle, created, err := agent.portLoader.LockOrCreate(ctx, nniPort)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400321 if err != nil {
322 return err
323 }
324 defer portHandle.Unlock()
325
326 if !created {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000327 logger.Debugw(ctx, "port-already-exist", log.Fields{"port": port})
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400328 return nil
329 }
330
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400331 // ensure that no events will be sent until this one is
332 queuePosition := agent.orderedEvents.assignQueuePosition()
khenaidoo0db4c812020-05-27 15:27:30 -0400333
334 // Setup the routes for this device and then send the port update event to the OF Controller
335 go func() {
336 // First setup the routes
Himani Chawlab4c25912020-11-12 17:16:38 +0530337 subCtx := coreutils.WithSpanAndRPCMetadataFromContext(ctx)
338 if err := agent.updateRoutes(subCtx, deviceID, devicePorts, nniPort, agent.listLogicalDevicePorts(ctx)); err != nil {
khenaidoo0db4c812020-05-27 15:27:30 -0400339 // This is not an error as we may not have enough logical ports to set up routes or some PON ports have not been
340 // created yet.
Rohan Agrawal31f21802020-06-12 05:38:46 +0000341 logger.Infow(ctx, "routes-not-ready", log.Fields{"logical-device-id": agent.logicalDeviceID, "logical-port": nniPort.OfpPort.PortNo, "error": err})
khenaidoo0db4c812020-05-27 15:27:30 -0400342 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400343 // send event, and allow any queued events to be sent as well
Rohan Agrawal31f21802020-06-12 05:38:46 +0000344 queuePosition.send(ctx, agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_ADD, nniPort.OfpPort)
khenaidoo0db4c812020-05-27 15:27:30 -0400345 }()
khenaidoo0db4c812020-05-27 15:27:30 -0400346 return nil
347}
348
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400349// addUNILogicalPort adds an UNI port to the logical device. It returns a bool representing whether a port has been
350// added and an error in case a valid error is encountered. If the port was successfully added it will return
351// (true, nil). If the device is not in the correct state it will return (false, nil) as this is a valid
352// scenario. This also applies to the case where the port was already added.
Kent Hagerman2a07b862020-06-19 15:23:07 -0400353func (agent *LogicalAgent) addUNILogicalPort(ctx context.Context, deviceID string, deviceAdminState voltha.AdminState_Types, deviceOperStatus voltha.OperStatus_Types, devicePorts map[uint32]*voltha.Port, port *voltha.Port) error {
Himani Chawlab4c25912020-11-12 17:16:38 +0530354 logger.Debugw(ctx, "add-uni-logical-port", log.Fields{"port": port})
Kent Hagerman2a07b862020-06-19 15:23:07 -0400355 if deviceAdminState != voltha.AdminState_ENABLED || deviceOperStatus != voltha.OperStatus_ACTIVE {
divyadesaicb8b59d2020-08-18 09:55:47 +0000356 logger.Infow(ctx, "device-not-ready", log.Fields{"device-id": deviceID, "admin": deviceAdminState, "oper": deviceOperStatus})
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400357 return nil
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400358 }
khenaidooc6c7bda2020-06-17 17:20:18 -0400359 ofpPort := *port.OfpPort
360 ofpPort.HwAddr = append([]uint32{}, port.OfpPort.HwAddr...)
361 ofpPort.PortNo = port.PortNo
362 uniPort := &voltha.LogicalPort{
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400363 RootPort: false,
Kent Hagerman2a07b862020-06-19 15:23:07 -0400364 DeviceId: deviceID,
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400365 Id: port.Label,
366 DevicePortNo: port.PortNo,
khenaidooc6c7bda2020-06-17 17:20:18 -0400367 OfpPort: &ofpPort,
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400368 OfpPortStats: &ofp.OfpPortStats{},
369 }
370
khenaidooc6c7bda2020-06-17 17:20:18 -0400371 portHandle, created, err := agent.portLoader.LockOrCreate(ctx, uniPort)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400372 if err != nil {
373 return err
374 }
375 defer portHandle.Unlock()
376
377 if !created {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000378 logger.Debugw(ctx, "port-already-exist", log.Fields{"port": port})
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400379 return nil
380 }
381
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400382 // ensure that no events will be sent until this one is
383 queuePosition := agent.orderedEvents.assignQueuePosition()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400384
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400385 // Setup the routes for this device and then send the port update event to the OF Controller
386 go func() {
Himani Chawlab4c25912020-11-12 17:16:38 +0530387 subCtx := coreutils.WithSpanAndRPCMetadataFromContext(ctx)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400388 // First setup the routes
Himani Chawlab4c25912020-11-12 17:16:38 +0530389 if err := agent.updateRoutes(subCtx, deviceID, devicePorts, uniPort, agent.listLogicalDevicePorts(ctx)); err != nil {
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400390 // This is not an error as we may not have enough logical ports to set up routes or some PON ports have not been
391 // created yet.
Rohan Agrawal31f21802020-06-12 05:38:46 +0000392 logger.Infow(ctx, "routes-not-ready", log.Fields{"logical-device-id": agent.logicalDeviceID, "logical-port": uniPort.OfpPort.PortNo, "error": err})
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400393 }
394 // send event, and allow any queued events to be sent as well
nikesh.krishnan95142d52023-02-24 15:32:11 +0530395 queuePosition.send(ctx, agent, agent.logicalDeviceID, ofp.OfpPortReason_OFPPR_ADD, uniPort.OfpPort)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400396 }()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400397 return nil
398}
Himani Chawla40af2702021-01-27 15:06:30 +0530399func (e *orderedEvents) waitForAllEventsToBeSent(ctx context.Context, cancel context.CancelFunc) error {
400 defer cancel()
401 ch := make(chan struct{})
402 e.sendCompletion(ch)
403 select {
404 case <-ctx.Done():
405 logger.Error(ctx, "timeout-while-waiting-for-event-queue-to-be-cleared")
406 return ctx.Err()
407 case <-ch:
408 logger.Debug(ctx, "event-queue-is-empty")
409 return nil
410 }
411}
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400412
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400413// send is a convenience to avoid calling both assignQueuePosition and qp.send
Rohan Agrawal31f21802020-06-12 05:38:46 +0000414func (e *orderedEvents) send(ctx context.Context, agent *LogicalAgent, deviceID string, reason ofp.OfpPortReason, desc *ofp.OfpPort) {
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400415 qp := e.assignQueuePosition()
Himani Chawlab4c25912020-11-12 17:16:38 +0530416 subCtx := coreutils.WithSpanAndRPCMetadataFromContext(ctx)
417 go qp.send(subCtx, agent, deviceID, reason, desc)
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400418}
419
Himani Chawla40af2702021-01-27 15:06:30 +0530420// sendCompletion will make sure that given channel is notified when queue is empty
421func (e *orderedEvents) sendCompletion(ch chan struct{}) {
422 qp := e.assignQueuePosition()
423 go qp.sendCompletion(ch)
424}
425
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400426// TODO: shouldn't need to guarantee event ordering like this
427// event ordering should really be protected by per-LogicalPort lock
428// once routing uses on-demand calculation only, this should be changed
429// assignQueuePosition ensures that no events will be sent until this thread calls send() on the returned queuePosition
430func (e *orderedEvents) assignQueuePosition() queuePosition {
431 e.mutex.Lock()
432 defer e.mutex.Unlock()
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400433
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400434 prev := e.last
435 next := make(chan struct{})
436 e.last = next
437 return queuePosition{
438 prev: prev,
439 next: next,
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400440 }
441}
442
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400443// orderedEvents guarantees the order that events are sent, while allowing events to back up.
444type orderedEvents struct {
445 mutex sync.Mutex
446 last <-chan struct{}
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400447}
448
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400449type queuePosition struct {
450 prev <-chan struct{}
451 next chan<- struct{}
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400452}
453
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400454// send waits for its turn, then sends the event, then notifies the next in line
Rohan Agrawal31f21802020-06-12 05:38:46 +0000455func (qp queuePosition) send(ctx context.Context, agent *LogicalAgent, deviceID string, reason ofp.OfpPortReason, desc *ofp.OfpPort) {
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400456 if qp.prev != nil {
457 <-qp.prev // wait for turn
458 }
Rohan Agrawal31f21802020-06-12 05:38:46 +0000459 agent.ldeviceMgr.SendChangeEvent(ctx, deviceID, reason, desc)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400460 close(qp.next) // notify next
Himani Chawla40af2702021-01-27 15:06:30 +0530461
462}
463
464// sendCompletion waits for its turn, then notifies the given channel that queue is empty
465func (qp queuePosition) sendCompletion(ch chan struct{}) {
466 if qp.prev != nil {
467 <-qp.prev // wait for turn
468 }
469 close(ch)
470 close(qp.next)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400471}
472
473// GetWildcardInputPorts filters out the logical port number from the set of logical ports on the device and
474// returns their port numbers.
Rohan Agrawal31f21802020-06-12 05:38:46 +0000475func (agent *LogicalAgent) GetWildcardInputPorts(ctx context.Context, excludePort uint32) map[uint32]struct{} {
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400476 portIDs := agent.portLoader.ListIDs()
477 delete(portIDs, excludePort)
478 return portIDs
479}
480
481// isNNIPort return true iff the specified port belongs to the parent (OLT) device
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400482func (agent *LogicalAgent) isNNIPort(portNo uint32) bool {
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400483 portHandle, have := agent.portLoader.Lock(portNo)
484 if !have {
485 return false
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400486 }
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400487 defer portHandle.Unlock()
488
489 // any root-device logical port is an NNI port
490 return portHandle.GetReadOnly().RootPort
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400491}
492
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400493// getAnyNNIPort returns an NNI port
494func (agent *LogicalAgent) getAnyNNIPort() (uint32, error) {
495 for portID := range agent.portLoader.ListIDsForDevice(agent.rootDeviceID) {
496 return portID, nil
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400497 }
498 return 0, status.Error(codes.NotFound, "No NNI port found")
499}
500
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400501//GetNNIPorts returns all NNI ports
502func (agent *LogicalAgent) GetNNIPorts() map[uint32]struct{} {
503 return agent.portLoader.ListIDsForDevice(agent.rootDeviceID)
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400504}
505
506// getUNILogicalPortNo returns the UNI logical port number specified in the flow
507func (agent *LogicalAgent) getUNILogicalPortNo(flow *ofp.OfpFlowStats) (uint32, error) {
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400508 inPortNo := fu.GetInPort(flow)
509 outPortNo := fu.GetOutPort(flow)
khenaidood948f772021-08-11 17:49:24 -0400510 if inPortNo == 0 && outPortNo == 0 {
511 return 0, status.Errorf(codes.NotFound, "no-uni-port: %v", flow)
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400512 }
khenaidood948f772021-08-11 17:49:24 -0400513 if inPortNo != 0 && !agent.isNNIPort(inPortNo) {
514 return inPortNo, nil
515 }
516 if outPortNo != 0 && !agent.isNNIPort(outPortNo) {
517 return outPortNo, nil
Kent Hagerman3136fbd2020-05-14 10:30:45 -0400518 }
519 return 0, status.Errorf(codes.NotFound, "no-uni-port: %v", flow)
520}