blob: 941cb3b6cc4b384da7eda19d1d07f7a6fa6352e0 [file] [log] [blame]
khenaidoob9203542018-09-17 22:56:37 -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 */
16package core
17
18import (
19 "context"
khenaidoo19d7b632018-10-30 10:49:50 -040020 "errors"
21 "fmt"
khenaidoob9203542018-09-17 22:56:37 -040022 "github.com/gogo/protobuf/proto"
23 "github.com/opencord/voltha-go/common/log"
24 "github.com/opencord/voltha-go/db/model"
khenaidoo89b0e942018-10-21 21:11:33 -040025 fd "github.com/opencord/voltha-go/rw_core/flow_decomposition"
26 "github.com/opencord/voltha-go/rw_core/graph"
27 fu "github.com/opencord/voltha-go/rw_core/utils"
khenaidoo8f474192019-04-03 17:20:44 -040028 ic "github.com/opencord/voltha-protos/go/inter_container"
29 ofp "github.com/opencord/voltha-protos/go/openflow_13"
30 "github.com/opencord/voltha-protos/go/voltha"
khenaidoob9203542018-09-17 22:56:37 -040031 "google.golang.org/grpc/codes"
32 "google.golang.org/grpc/status"
khenaidoo19d7b632018-10-30 10:49:50 -040033 "reflect"
khenaidoo92e62c52018-10-03 14:02:54 -040034 "sync"
Stephane Barbarieef6650d2019-07-18 12:15:09 -040035 "time"
khenaidoob9203542018-09-17 22:56:37 -040036)
37
38type LogicalDeviceAgent struct {
khenaidoo3306c992019-05-24 16:57:35 -040039 logicalDeviceId string
40 rootDeviceId string
41 deviceMgr *DeviceManager
42 ldeviceMgr *LogicalDeviceManager
43 clusterDataProxy *model.Proxy
44 exitChannel chan int
45 deviceGraph *graph.DeviceGraph
46 flowProxy *model.Proxy
47 groupProxy *model.Proxy
48 ldProxy *model.Proxy
49 portProxies map[string]*model.Proxy
50 portProxiesLock sync.RWMutex
51 lockLogicalDevice sync.RWMutex
52 logicalPortsNo map[uint32]bool //value is true for NNI port
53 lockLogicalPortsNo sync.RWMutex
54 flowDecomposer *fd.FlowDecomposer
55 defaultTimeout int64
khenaidoob9203542018-09-17 22:56:37 -040056}
57
Stephane Barbarie1ab43272018-12-08 21:42:13 -050058func newLogicalDeviceAgent(id string, deviceId string, ldeviceMgr *LogicalDeviceManager,
59 deviceMgr *DeviceManager,
khenaidoo2c6a0992019-04-29 13:46:56 -040060 cdProxy *model.Proxy, timeout int64) *LogicalDeviceAgent {
khenaidoob9203542018-09-17 22:56:37 -040061 var agent LogicalDeviceAgent
62 agent.exitChannel = make(chan int, 1)
63 agent.logicalDeviceId = id
Stephane Barbarie1ab43272018-12-08 21:42:13 -050064 agent.rootDeviceId = deviceId
khenaidoob9203542018-09-17 22:56:37 -040065 agent.deviceMgr = deviceMgr
khenaidoo9a468962018-09-19 15:33:13 -040066 agent.clusterDataProxy = cdProxy
khenaidoob9203542018-09-17 22:56:37 -040067 agent.ldeviceMgr = ldeviceMgr
khenaidoo19d7b632018-10-30 10:49:50 -040068 agent.flowDecomposer = fd.NewFlowDecomposer(agent.deviceMgr)
khenaidoo92e62c52018-10-03 14:02:54 -040069 agent.lockLogicalDevice = sync.RWMutex{}
khenaidoofc1314d2019-03-14 09:34:21 -040070 agent.portProxies = make(map[string]*model.Proxy)
71 agent.portProxiesLock = sync.RWMutex{}
khenaidoo2c6a0992019-04-29 13:46:56 -040072 agent.lockLogicalPortsNo = sync.RWMutex{}
73 agent.logicalPortsNo = make(map[uint32]bool)
khenaidoo2c6a0992019-04-29 13:46:56 -040074 agent.defaultTimeout = timeout
khenaidoob9203542018-09-17 22:56:37 -040075 return &agent
76}
77
khenaidoo4d4802d2018-10-04 21:59:49 -040078// start creates the logical device and add it to the data model
khenaidoo297cd252019-02-07 22:10:23 -050079func (agent *LogicalDeviceAgent) start(ctx context.Context, loadFromdB bool) error {
80 log.Infow("starting-logical_device-agent", log.Fields{"logicaldeviceId": agent.logicalDeviceId, "loadFromdB": loadFromdB})
81 var ld *voltha.LogicalDevice
82 if !loadFromdB {
khenaidoo297cd252019-02-07 22:10:23 -050083 var err error
khenaidooba6b6c42019-08-02 09:11:56 -040084 // First create and store the logical device
khenaidoo297cd252019-02-07 22:10:23 -050085 ld = &voltha.LogicalDevice{Id: agent.logicalDeviceId, RootDeviceId: agent.rootDeviceId}
86
87 // Create the datapath ID (uint64) using the logical device ID (based on the MAC Address)
88 var datapathID uint64
89 if datapathID, err = CreateDataPathId(agent.logicalDeviceId); err != nil {
90 log.Errorw("error-creating-datapath-id", log.Fields{"error": err})
91 return err
92 }
93 ld.DatapathId = datapathID
khenaidoo297cd252019-02-07 22:10:23 -050094 ld.Flows = &ofp.Flows{Items: nil}
95 ld.FlowGroups = &ofp.FlowGroups{Items: nil}
96
khenaidoo297cd252019-02-07 22:10:23 -050097 agent.lockLogicalDevice.Lock()
khenaidoo297cd252019-02-07 22:10:23 -050098 // Save the logical device
Stephane Barbarieef6650d2019-07-18 12:15:09 -040099 if added := agent.clusterDataProxy.AddWithID(ctx, "/logical_devices", ld.Id, ld, ""); added == nil {
khenaidoo297cd252019-02-07 22:10:23 -0500100 log.Errorw("failed-to-add-logical-device", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
101 } else {
102 log.Debugw("logicaldevice-created", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
103 }
104 agent.lockLogicalDevice.Unlock()
khenaidoofc1314d2019-03-14 09:34:21 -0400105
khenaidooba6b6c42019-08-02 09:11:56 -0400106 //Retrieve the switch capability from the device adapter
107 var switchCap *ic.SwitchCapability
108 if switchCap, err = agent.deviceMgr.getSwitchCapability(ctx, agent.rootDeviceId); err != nil {
109 log.Errorw("error-creating-logical-device", log.Fields{"error": err})
110 return err
111 }
112
113 // Save the data
114 agent.lockLogicalDevice.Lock()
115 if ld, err = agent.getLogicalDeviceWithoutLock(); err != nil {
116 log.Warnw("failed-to-load-logical-device", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
117 return err
118 }
119 clonedLd := (proto.Clone(ld)).(*voltha.LogicalDevice)
120 clonedLd.Desc = (proto.Clone(switchCap.Desc)).(*ofp.OfpDesc)
121 log.Debugw("Switch-capability", log.Fields{"Desc": clonedLd.Desc})
122 clonedLd.SwitchFeatures = (proto.Clone(switchCap.SwitchFeatures)).(*ofp.OfpSwitchFeatures)
123 agent.lockLogicalDevice.Unlock()
124
khenaidoo3d3b8c22019-05-22 18:10:39 -0400125 // TODO: Set the logical ports in a separate call once the port update issue is fixed.
126 go agent.setupLogicalPorts(ctx)
127
khenaidoo297cd252019-02-07 22:10:23 -0500128 } else {
129 // load from dB - the logical may not exist at this time. On error, just return and the calling function
130 // will destroy this agent.
131 var err error
132 if ld, err = agent.GetLogicalDevice(); err != nil {
133 log.Warnw("failed-to-load-logical-device", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
134 return err
135 }
khenaidoo3d3b8c22019-05-22 18:10:39 -0400136
khenaidoo8c3303d2019-02-13 14:59:39 -0500137 // Update the root device Id
138 agent.rootDeviceId = ld.RootDeviceId
khenaidoo3d3b8c22019-05-22 18:10:39 -0400139
140 // Setup the local list of logical ports
141 agent.addLogicalPortsToMap(ld.Ports)
142
143 // Setup the device graph
144 agent.generateDeviceGraph()
khenaidoob9203542018-09-17 22:56:37 -0400145 }
khenaidoo92e62c52018-10-03 14:02:54 -0400146 agent.lockLogicalDevice.Lock()
khenaidoo3d3b8c22019-05-22 18:10:39 -0400147 defer agent.lockLogicalDevice.Unlock()
khenaidoofc1314d2019-03-14 09:34:21 -0400148
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400149 agent.flowProxy = agent.clusterDataProxy.CreateProxy(
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400150 ctx,
khenaidoo19d7b632018-10-30 10:49:50 -0400151 fmt.Sprintf("/logical_devices/%s/flows", agent.logicalDeviceId),
152 false)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400153 agent.groupProxy = agent.clusterDataProxy.CreateProxy(
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400154 ctx,
khenaidoo19d7b632018-10-30 10:49:50 -0400155 fmt.Sprintf("/logical_devices/%s/flow_groups", agent.logicalDeviceId),
156 false)
Stephane Barbarie40fd3b22019-04-23 21:50:47 -0400157 agent.ldProxy = agent.clusterDataProxy.CreateProxy(
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400158 ctx,
khenaidoofc1314d2019-03-14 09:34:21 -0400159 fmt.Sprintf("/logical_devices/%s", agent.logicalDeviceId),
160 false)
khenaidoo19d7b632018-10-30 10:49:50 -0400161
khenaidoofc1314d2019-03-14 09:34:21 -0400162 // TODO: Use a port proxy once the POST_ADD is fixed
khenaidoo3d3b8c22019-05-22 18:10:39 -0400163 if agent.ldProxy != nil {
164 agent.ldProxy.RegisterCallback(model.POST_UPDATE, agent.portUpdated)
165 } else {
166 log.Errorw("logical-device-proxy-null", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
167 return status.Error(codes.Internal, "logical-device-proxy-null")
168 }
khenaidoobcf205b2019-01-25 22:21:14 -0500169
khenaidoob9203542018-09-17 22:56:37 -0400170 return nil
171}
172
khenaidoo4d4802d2018-10-04 21:59:49 -0400173// stop stops the logical devuce agent. This removes the logical device from the data model.
174func (agent *LogicalDeviceAgent) stop(ctx context.Context) {
175 log.Info("stopping-logical_device-agent")
176 agent.lockLogicalDevice.Lock()
177 defer agent.lockLogicalDevice.Unlock()
khenaidoo8c3303d2019-02-13 14:59:39 -0500178
khenaidoo4d4802d2018-10-04 21:59:49 -0400179 //Remove the logical device from the model
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400180 if removed := agent.clusterDataProxy.Remove(ctx, "/logical_devices/"+agent.logicalDeviceId, ""); removed == nil {
khenaidoo4d4802d2018-10-04 21:59:49 -0400181 log.Errorw("failed-to-remove-logical-device", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
182 } else {
183 log.Debugw("logicaldevice-removed", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
184 }
185 agent.exitChannel <- 1
186 log.Info("logical_device-agent-stopped")
187}
188
khenaidoo19d7b632018-10-30 10:49:50 -0400189// GetLogicalDevice locks the logical device model and then retrieves the latest logical device information
190func (agent *LogicalDeviceAgent) GetLogicalDevice() (*voltha.LogicalDevice, error) {
191 log.Debug("GetLogicalDevice")
khenaidoo1ce37ad2019-03-24 22:07:24 -0400192 agent.lockLogicalDevice.RLock()
193 defer agent.lockLogicalDevice.RUnlock()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400194 logicalDevice := agent.clusterDataProxy.Get(context.Background(), "/logical_devices/"+agent.logicalDeviceId, 0, false, "")
khenaidoo92e62c52018-10-03 14:02:54 -0400195 if lDevice, ok := logicalDevice.(*voltha.LogicalDevice); ok {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500196 return lDevice, nil
khenaidoo92e62c52018-10-03 14:02:54 -0400197 }
198 return nil, status.Errorf(codes.NotFound, "logical_device-%s", agent.logicalDeviceId)
199}
200
khenaidoodd237172019-05-27 16:37:17 -0400201func (agent *LogicalDeviceAgent) ListLogicalDeviceFlows() (*ofp.Flows, error) {
202 log.Debug("ListLogicalDeviceFlows")
203 agent.lockLogicalDevice.RLock()
204 defer agent.lockLogicalDevice.RUnlock()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400205 logicalDevice := agent.clusterDataProxy.Get(context.Background(), "/logical_devices/"+agent.logicalDeviceId, 0, false, "")
khenaidoodd237172019-05-27 16:37:17 -0400206 if lDevice, ok := logicalDevice.(*voltha.LogicalDevice); ok {
207 cFlows := (proto.Clone(lDevice.Flows)).(*ofp.Flows)
208 return cFlows, nil
209 }
210 return nil, status.Errorf(codes.NotFound, "logical_device-%s", agent.logicalDeviceId)
211}
212
213func (agent *LogicalDeviceAgent) ListLogicalDeviceFlowGroups() (*ofp.FlowGroups, error) {
214 log.Debug("ListLogicalDeviceFlowGroups")
215 agent.lockLogicalDevice.RLock()
216 defer agent.lockLogicalDevice.RUnlock()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400217 logicalDevice := agent.clusterDataProxy.Get(context.Background(), "/logical_devices/"+agent.logicalDeviceId, 0, false, "")
khenaidoodd237172019-05-27 16:37:17 -0400218 if lDevice, ok := logicalDevice.(*voltha.LogicalDevice); ok {
219 cFlowGroups := (proto.Clone(lDevice.FlowGroups)).(*ofp.FlowGroups)
220 return cFlowGroups, nil
221 }
222 return nil, status.Errorf(codes.NotFound, "logical_device-%s", agent.logicalDeviceId)
223}
224
khenaidoo19d7b632018-10-30 10:49:50 -0400225func (agent *LogicalDeviceAgent) ListLogicalDevicePorts() (*voltha.LogicalPorts, error) {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400226 log.Debug("ListLogicalDevicePorts")
227 agent.lockLogicalDevice.RLock()
228 defer agent.lockLogicalDevice.RUnlock()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400229 logicalDevice := agent.clusterDataProxy.Get(context.Background(), "/logical_devices/"+agent.logicalDeviceId, 0, false, "")
khenaidoo19d7b632018-10-30 10:49:50 -0400230 if lDevice, ok := logicalDevice.(*voltha.LogicalDevice); ok {
231 lPorts := make([]*voltha.LogicalPort, 0)
232 for _, port := range lDevice.Ports {
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500233 lPorts = append(lPorts, port)
khenaidoo19d7b632018-10-30 10:49:50 -0400234 }
235 return &voltha.LogicalPorts{Items: lPorts}, nil
236 }
237 return nil, status.Errorf(codes.NotFound, "logical_device-%s", agent.logicalDeviceId)
238}
239
240// listFlows locks the logical device model and then retrieves the latest flow information
241func (agent *LogicalDeviceAgent) listFlows() []*ofp.OfpFlowStats {
242 log.Debug("listFlows")
khenaidoo1ce37ad2019-03-24 22:07:24 -0400243 agent.lockLogicalDevice.RLock()
244 defer agent.lockLogicalDevice.RUnlock()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400245 logicalDevice := agent.clusterDataProxy.Get(context.Background(), "/logical_devices/"+agent.logicalDeviceId, 0, false, "")
khenaidoo19d7b632018-10-30 10:49:50 -0400246 if lDevice, ok := logicalDevice.(*voltha.LogicalDevice); ok {
247 return lDevice.Flows.Items
248 }
249 return nil
250}
251
252// listFlowGroups locks the logical device model and then retrieves the latest flow groups information
253func (agent *LogicalDeviceAgent) listFlowGroups() []*ofp.OfpGroupEntry {
254 log.Debug("listFlowGroups")
khenaidoo1ce37ad2019-03-24 22:07:24 -0400255 agent.lockLogicalDevice.RLock()
256 defer agent.lockLogicalDevice.RUnlock()
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400257 logicalDevice := agent.clusterDataProxy.Get(context.Background(), "/logical_devices/"+agent.logicalDeviceId, 0, false, "")
khenaidoo19d7b632018-10-30 10:49:50 -0400258 if lDevice, ok := logicalDevice.(*voltha.LogicalDevice); ok {
259 return lDevice.FlowGroups.Items
260 }
261 return nil
262}
263
khenaidoo43c82122018-11-22 18:38:28 -0500264//updateLogicalDeviceWithoutLock updates the model with the logical device. It clones the logicaldevice before saving it
265func (agent *LogicalDeviceAgent) updateLogicalDeviceFlowsWithoutLock(flows *ofp.Flows) error {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400266 updateCtx := context.WithValue(context.Background(), model.RequestTimestamp, time.Now().UnixNano())
267 afterUpdate := agent.flowProxy.Update(updateCtx, "/", flows, false, "")
khenaidoo43c82122018-11-22 18:38:28 -0500268 if afterUpdate == nil {
269 return status.Errorf(codes.Internal, "failed-updating-logical-device-flows:%s", agent.logicalDeviceId)
270 }
khenaidoo43c82122018-11-22 18:38:28 -0500271 return nil
272}
273
274//updateLogicalDeviceWithoutLock updates the model with the logical device. It clones the logicaldevice before saving it
275func (agent *LogicalDeviceAgent) updateLogicalDeviceFlowGroupsWithoutLock(flowGroups *ofp.FlowGroups) error {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400276 updateCtx := context.WithValue(context.Background(), model.RequestTimestamp, time.Now().UnixNano())
277 afterUpdate := agent.groupProxy.Update(updateCtx, "/", flowGroups, false, "")
khenaidoo43c82122018-11-22 18:38:28 -0500278 if afterUpdate == nil {
279 return status.Errorf(codes.Internal, "failed-updating-logical-device-flow-groups:%s", agent.logicalDeviceId)
280 }
khenaidoo43c82122018-11-22 18:38:28 -0500281 return nil
282}
283
khenaidoo4d4802d2018-10-04 21:59:49 -0400284// getLogicalDeviceWithoutLock retrieves a logical device from the model without locking it. This is used only by
285// functions that have already acquired the logical device lock to the model
khenaidoo92e62c52018-10-03 14:02:54 -0400286func (agent *LogicalDeviceAgent) getLogicalDeviceWithoutLock() (*voltha.LogicalDevice, error) {
287 log.Debug("getLogicalDeviceWithoutLock")
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400288 logicalDevice := agent.clusterDataProxy.Get(context.Background(), "/logical_devices/"+agent.logicalDeviceId, 0, false, "")
khenaidoo92e62c52018-10-03 14:02:54 -0400289 if lDevice, ok := logicalDevice.(*voltha.LogicalDevice); ok {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400290 //log.Debug("getLogicalDeviceWithoutLock", log.Fields{"ldevice": lDevice})
Stephane Barbarie1ab43272018-12-08 21:42:13 -0500291 return lDevice, nil
khenaidoo92e62c52018-10-03 14:02:54 -0400292 }
293 return nil, status.Errorf(codes.NotFound, "logical_device-%s", agent.logicalDeviceId)
294}
295
khenaidoo2c6a0992019-04-29 13:46:56 -0400296func (agent *LogicalDeviceAgent) updateLogicalPort(device *voltha.Device, port *voltha.Port) error {
297 log.Debugw("updateLogicalPort", log.Fields{"deviceId": device.Id, "port": port})
298 var err error
299 if port.Type == voltha.Port_ETHERNET_NNI {
300 if _, err = agent.addNNILogicalPort(device, port); err != nil {
301 return err
302 }
303 agent.addLogicalPortToMap(port.PortNo, true)
304 } else if port.Type == voltha.Port_ETHERNET_UNI {
305 if _, err = agent.addUNILogicalPort(device, port); err != nil {
306 return err
307 }
308 agent.addLogicalPortToMap(port.PortNo, false)
309 } else {
310 // Update the device graph to ensure all routes on the logical device have been calculated
311 if err = agent.updateRoutes(device, port); err != nil {
312 log.Errorw("failed-to-update-routes", log.Fields{"deviceId": device.Id, "port": port, "error": err})
313 return err
314 }
315 }
316 return nil
317}
318
khenaidoo910204f2019-04-08 17:56:40 -0400319func (agent *LogicalDeviceAgent) addLogicalPort(device *voltha.Device, port *voltha.Port) error {
khenaidoo8f474192019-04-03 17:20:44 -0400320 log.Debugw("addLogicalPort", log.Fields{"deviceId": device.Id, "port": port})
khenaidoo8f474192019-04-03 17:20:44 -0400321 var err error
khenaidoofc1314d2019-03-14 09:34:21 -0400322 if port.Type == voltha.Port_ETHERNET_NNI {
khenaidoo910204f2019-04-08 17:56:40 -0400323 if _, err = agent.addNNILogicalPort(device, port); err != nil {
khenaidoofc1314d2019-03-14 09:34:21 -0400324 return err
325 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400326 agent.addLogicalPortToMap(port.PortNo, true)
khenaidoofc1314d2019-03-14 09:34:21 -0400327 } else if port.Type == voltha.Port_ETHERNET_UNI {
khenaidoo910204f2019-04-08 17:56:40 -0400328 if _, err = agent.addUNILogicalPort(device, port); err != nil {
khenaidoofc1314d2019-03-14 09:34:21 -0400329 return err
330 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400331 agent.addLogicalPortToMap(port.PortNo, false)
khenaidoofc1314d2019-03-14 09:34:21 -0400332 } else {
333 log.Debugw("invalid-port-type", log.Fields{"deviceId": device.Id, "port": port})
334 return nil
335 }
khenaidoofc1314d2019-03-14 09:34:21 -0400336 return nil
337}
338
khenaidoo3d3b8c22019-05-22 18:10:39 -0400339// setupLogicalPorts is invoked once the logical device has been created and is ready to get ports
340// added to it. While the logical device was being created we could have received requests to add
341// NNI and UNI ports which were discarded. Now is the time to add them if needed
342func (agent *LogicalDeviceAgent) setupLogicalPorts(ctx context.Context) error {
343 log.Infow("setupLogicalPorts", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
344 // First add any NNI ports which could have been missing
345 if err := agent.setupNNILogicalPorts(nil, agent.rootDeviceId); err != nil {
346 log.Errorw("error-setting-up-NNI-ports", log.Fields{"error": err, "deviceId": agent.rootDeviceId})
347 return err
348 }
349
350 // Now, set up the UNI ports if needed.
351 if children, err := agent.deviceMgr.getAllChildDevices(agent.rootDeviceId); err != nil {
352 log.Errorw("error-getting-child-devices", log.Fields{"error": err, "deviceId": agent.rootDeviceId})
353 return err
354 } else {
355 chnlsList := make([]chan interface{}, 0)
356 for _, child := range children.Items {
357 ch := make(chan interface{})
358 chnlsList = append(chnlsList, ch)
359 go func(device *voltha.Device, ch chan interface{}) {
360 if err = agent.setupUNILogicalPorts(nil, device); err != nil {
361 log.Error("setting-up-UNI-ports-failed", log.Fields{"deviceID": device.Id})
362 ch <- status.Errorf(codes.Internal, "UNI-ports-setup-failed: %s", device.Id)
363 }
364 ch <- nil
365 }(child, ch)
366 }
367 // Wait for completion
368 if res := fu.WaitForNilOrErrorResponses(agent.defaultTimeout, chnlsList...); res != nil {
369 return status.Errorf(codes.Aborted, "errors-%s", res)
370 }
371 }
372 return nil
373}
374
khenaidoofc1314d2019-03-14 09:34:21 -0400375// setupNNILogicalPorts creates an NNI port on the logical device that represents an NNI interface on a root device
376func (agent *LogicalDeviceAgent) setupNNILogicalPorts(ctx context.Context, deviceId string) error {
377 log.Infow("setupNNILogicalPorts-start", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
khenaidoob9203542018-09-17 22:56:37 -0400378 // Build the logical device based on information retrieved from the device adapter
khenaidoofc1314d2019-03-14 09:34:21 -0400379 var err error
380
381 var device *voltha.Device
382 if device, err = agent.deviceMgr.GetDevice(deviceId); err != nil {
khenaidoo2c6a0992019-04-29 13:46:56 -0400383 log.Errorw("error-retrieving-device", log.Fields{"error": err, "deviceId": deviceId})
khenaidoofc1314d2019-03-14 09:34:21 -0400384 return err
385 }
386
387 //Get UNI port number
khenaidoofc1314d2019-03-14 09:34:21 -0400388 for _, port := range device.Ports {
389 if port.Type == voltha.Port_ETHERNET_NNI {
khenaidoo910204f2019-04-08 17:56:40 -0400390 if _, err = agent.addNNILogicalPort(device, port); err != nil {
khenaidoofc1314d2019-03-14 09:34:21 -0400391 log.Errorw("error-adding-UNI-port", log.Fields{"error": err})
khenaidoofc1314d2019-03-14 09:34:21 -0400392 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400393 agent.addLogicalPortToMap(port.PortNo, true)
khenaidoofc1314d2019-03-14 09:34:21 -0400394 }
395 }
khenaidoofc1314d2019-03-14 09:34:21 -0400396 return err
397}
398
khenaidoo3ab34882019-05-02 21:33:30 -0400399// updatePortsState updates the ports state related to the device
400func (agent *LogicalDeviceAgent) updatePortsState(device *voltha.Device, state voltha.AdminState_AdminState) error {
401 log.Infow("updatePortsState-start", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
402 agent.lockLogicalDevice.Lock()
403 defer agent.lockLogicalDevice.Unlock()
404 // Get the latest logical device info
405 if ld, err := agent.getLogicalDeviceWithoutLock(); err != nil {
406 log.Warnw("logical-device-unknown", log.Fields{"ldeviceId": agent.logicalDeviceId, "error": err})
407 return err
408 } else {
409 cloned := (proto.Clone(ld)).(*voltha.LogicalDevice)
410 for _, lport := range cloned.Ports {
411 if lport.DeviceId == device.Id {
412 switch state {
413 case voltha.AdminState_ENABLED:
414 lport.OfpPort.Config = lport.OfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
khenaidoo0a822f92019-05-08 15:15:57 -0400415 lport.OfpPort.State = uint32(ofp.OfpPortState_OFPPS_LIVE)
khenaidoo3ab34882019-05-02 21:33:30 -0400416 case voltha.AdminState_DISABLED:
417 lport.OfpPort.Config = lport.OfpPort.Config | uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
khenaidoo0a822f92019-05-08 15:15:57 -0400418 lport.OfpPort.State = uint32(ofp.OfpPortState_OFPPS_LINK_DOWN)
khenaidoo3ab34882019-05-02 21:33:30 -0400419 default:
420 log.Warnw("unsupported-state-change", log.Fields{"deviceId": device.Id, "state": state})
421 }
422 }
423 }
424 // Updating the logical device will trigger the poprt change events to be populated to the controller
425 if err := agent.updateLogicalDeviceWithoutLock(cloned); err != nil {
426 log.Warnw("logical-device-update-failed", log.Fields{"ldeviceId": agent.logicalDeviceId, "error": err})
427 return err
428 }
429 }
430 return nil
431}
432
khenaidoofc1314d2019-03-14 09:34:21 -0400433// setupUNILogicalPorts creates a UNI port on the logical device that represents a child UNI interface
434func (agent *LogicalDeviceAgent) setupUNILogicalPorts(ctx context.Context, childDevice *voltha.Device) error {
khenaidoo3d3b8c22019-05-22 18:10:39 -0400435 log.Infow("setupUNILogicalPort", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
khenaidoofc1314d2019-03-14 09:34:21 -0400436 // Build the logical device based on information retrieved from the device adapter
khenaidoob9203542018-09-17 22:56:37 -0400437 var err error
khenaidoo59ef7be2019-06-21 12:40:28 -0400438 var added bool
khenaidoo19d7b632018-10-30 10:49:50 -0400439 //Get UNI port number
khenaidoo19d7b632018-10-30 10:49:50 -0400440 for _, port := range childDevice.Ports {
441 if port.Type == voltha.Port_ETHERNET_UNI {
khenaidoo59ef7be2019-06-21 12:40:28 -0400442 if added, err = agent.addUNILogicalPort(childDevice, port); err != nil {
khenaidoofc1314d2019-03-14 09:34:21 -0400443 log.Errorw("error-adding-UNI-port", log.Fields{"error": err})
khenaidoofc1314d2019-03-14 09:34:21 -0400444 }
khenaidoo59ef7be2019-06-21 12:40:28 -0400445 if added {
446 agent.addLogicalPortToMap(port.PortNo, false)
447 }
khenaidoo19d7b632018-10-30 10:49:50 -0400448 }
449 }
khenaidoofc1314d2019-03-14 09:34:21 -0400450 return err
khenaidoo92e62c52018-10-03 14:02:54 -0400451}
452
khenaidoo0a822f92019-05-08 15:15:57 -0400453// deleteAllLogicalPorts deletes all logical ports associated with this device
454func (agent *LogicalDeviceAgent) deleteAllLogicalPorts(device *voltha.Device) error {
455 log.Infow("updatePortsState-start", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
456 agent.lockLogicalDevice.Lock()
457 defer agent.lockLogicalDevice.Unlock()
458 // Get the latest logical device info
459 if ld, err := agent.getLogicalDeviceWithoutLock(); err != nil {
460 log.Warnw("logical-device-unknown", log.Fields{"ldeviceId": agent.logicalDeviceId, "error": err})
461 return err
462 } else {
463 cloned := (proto.Clone(ld)).(*voltha.LogicalDevice)
464 updateLogicalPorts := []*voltha.LogicalPort{}
465 for _, lport := range cloned.Ports {
466 if lport.DeviceId != device.Id {
467 updateLogicalPorts = append(updateLogicalPorts, lport)
468 }
469 }
470 if len(updateLogicalPorts) < len(cloned.Ports) {
471 cloned.Ports = updateLogicalPorts
472 // Updating the logical device will trigger the poprt change events to be populated to the controller
473 if err := agent.updateLogicalDeviceWithoutLock(cloned); err != nil {
474 log.Warnw("logical-device-update-failed", log.Fields{"ldeviceId": agent.logicalDeviceId, "error": err})
475 return err
476 }
477 } else {
478 log.Debugw("no-change-required", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
479 }
480 }
481 return nil
482}
483
khenaidoo92e62c52018-10-03 14:02:54 -0400484//updateLogicalDeviceWithoutLock updates the model with the logical device. It clones the logicaldevice before saving it
485func (agent *LogicalDeviceAgent) updateLogicalDeviceWithoutLock(logicalDevice *voltha.LogicalDevice) error {
Stephane Barbarieef6650d2019-07-18 12:15:09 -0400486 updateCtx := context.WithValue(context.Background(), model.RequestTimestamp, time.Now().UnixNano())
487 afterUpdate := agent.clusterDataProxy.Update(updateCtx, "/logical_devices/"+agent.logicalDeviceId, logicalDevice, false, "")
khenaidoo92e62c52018-10-03 14:02:54 -0400488 if afterUpdate == nil {
489 return status.Errorf(codes.Internal, "failed-updating-logical-device:%s", agent.logicalDeviceId)
490 }
491 return nil
492}
493
khenaidoo19d7b632018-10-30 10:49:50 -0400494//updateFlowTable updates the flow table of that logical device
495func (agent *LogicalDeviceAgent) updateFlowTable(ctx context.Context, flow *ofp.OfpFlowMod) error {
496 log.Debug("updateFlowTable")
497 if flow == nil {
498 return nil
499 }
500 switch flow.GetCommand() {
501 case ofp.OfpFlowModCommand_OFPFC_ADD:
502 return agent.flowAdd(flow)
503 case ofp.OfpFlowModCommand_OFPFC_DELETE:
504 return agent.flowDelete(flow)
505 case ofp.OfpFlowModCommand_OFPFC_DELETE_STRICT:
506 return agent.flowDeleteStrict(flow)
507 case ofp.OfpFlowModCommand_OFPFC_MODIFY:
508 return agent.flowModify(flow)
509 case ofp.OfpFlowModCommand_OFPFC_MODIFY_STRICT:
510 return agent.flowModifyStrict(flow)
511 }
512 return status.Errorf(codes.Internal,
513 "unhandled-command: lDeviceId:%s, command:%s", agent.logicalDeviceId, flow.GetCommand())
514}
515
516//updateGroupTable updates the group table of that logical device
517func (agent *LogicalDeviceAgent) updateGroupTable(ctx context.Context, groupMod *ofp.OfpGroupMod) error {
518 log.Debug("updateGroupTable")
519 if groupMod == nil {
520 return nil
521 }
522 switch groupMod.GetCommand() {
523 case ofp.OfpGroupModCommand_OFPGC_ADD:
524 return agent.groupAdd(groupMod)
525 case ofp.OfpGroupModCommand_OFPGC_DELETE:
526 return agent.groupDelete(groupMod)
527 case ofp.OfpGroupModCommand_OFPGC_MODIFY:
528 return agent.groupModify(groupMod)
529 }
530 return status.Errorf(codes.Internal,
531 "unhandled-command: lDeviceId:%s, command:%s", agent.logicalDeviceId, groupMod.GetCommand())
532}
533
khenaidoo19d7b632018-10-30 10:49:50 -0400534//flowAdd adds a flow to the flow table of that logical device
535func (agent *LogicalDeviceAgent) flowAdd(mod *ofp.OfpFlowMod) error {
536 log.Debug("flowAdd")
537 if mod == nil {
538 return nil
539 }
khenaidoo92e62c52018-10-03 14:02:54 -0400540 agent.lockLogicalDevice.Lock()
541 defer agent.lockLogicalDevice.Unlock()
khenaidoo19d7b632018-10-30 10:49:50 -0400542
543 var lDevice *voltha.LogicalDevice
544 var err error
545 if lDevice, err = agent.getLogicalDeviceWithoutLock(); err != nil {
546 log.Errorw("no-logical-device-present", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
547 return errors.New(fmt.Sprintf("no-logical-device-present:%s", agent.logicalDeviceId))
548 }
549
550 var flows []*ofp.OfpFlowStats
551 if lDevice.Flows != nil && lDevice.Flows.Items != nil {
552 flows = lDevice.Flows.Items
553 }
554
khenaidoo2c6a0992019-04-29 13:46:56 -0400555 updatedFlows := make([]*ofp.OfpFlowStats, 0)
khenaidoo19d7b632018-10-30 10:49:50 -0400556 changed := false
557 checkOverlap := (mod.Flags & uint32(ofp.OfpFlowModFlags_OFPFF_CHECK_OVERLAP)) != 0
558 if checkOverlap {
559 if overlapped := fu.FindOverlappingFlows(flows, mod); len(overlapped) != 0 {
560 // TODO: should this error be notified other than being logged?
561 log.Warnw("overlapped-flows", log.Fields{"logicaldeviceId": agent.logicalDeviceId})
562 } else {
563 // Add flow
khenaidoo68c930b2019-05-13 11:46:51 -0400564 flow := fu.FlowStatsEntryFromFlowModMessage(mod)
khenaidoo19d7b632018-10-30 10:49:50 -0400565 flows = append(flows, flow)
khenaidoo2c6a0992019-04-29 13:46:56 -0400566 updatedFlows = append(updatedFlows, flow)
khenaidoo19d7b632018-10-30 10:49:50 -0400567 changed = true
568 }
569 } else {
khenaidoo68c930b2019-05-13 11:46:51 -0400570 flow := fu.FlowStatsEntryFromFlowModMessage(mod)
khenaidoo19d7b632018-10-30 10:49:50 -0400571 idx := fu.FindFlows(flows, flow)
572 if idx >= 0 {
573 oldFlow := flows[idx]
574 if (mod.Flags & uint32(ofp.OfpFlowModFlags_OFPFF_RESET_COUNTS)) != 0 {
575 flow.ByteCount = oldFlow.ByteCount
576 flow.PacketCount = oldFlow.PacketCount
577 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400578 if !reflect.DeepEqual(oldFlow, flow) {
579 flows[idx] = flow
580 updatedFlows = append(updatedFlows, flow)
581 changed = true
582 }
khenaidoo19d7b632018-10-30 10:49:50 -0400583 } else {
584 flows = append(flows, flow)
khenaidoo2c6a0992019-04-29 13:46:56 -0400585 updatedFlows = append(updatedFlows, flow)
586 changed = true
khenaidoo19d7b632018-10-30 10:49:50 -0400587 }
khenaidoo19d7b632018-10-30 10:49:50 -0400588 }
589 if changed {
khenaidoo0458db62019-06-20 08:50:36 -0400590 deviceRules := agent.flowDecomposer.DecomposeRules(agent, ofp.Flows{Items: updatedFlows}, *lDevice.FlowGroups)
591 log.Debugw("rules", log.Fields{"rules": deviceRules.String()})
592
593 if err := agent.addDeviceFlowsAndGroups(deviceRules); err != nil {
594 log.Errorw("failure-updating-device-flows", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "error": err})
khenaidoo2c6a0992019-04-29 13:46:56 -0400595 return err
596 }
597
khenaidoo19d7b632018-10-30 10:49:50 -0400598 // Update model
khenaidoo0458db62019-06-20 08:50:36 -0400599 if err := agent.updateLogicalDeviceFlowsWithoutLock(&ofp.Flows{Items: flows}); err != nil {
khenaidoo2c6a0992019-04-29 13:46:56 -0400600 log.Errorw("db-flow-update-failed", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
khenaidoo19d7b632018-10-30 10:49:50 -0400601 return err
602 }
603 }
khenaidoo19d7b632018-10-30 10:49:50 -0400604 return nil
605}
606
607//flowDelete deletes a flow from the flow table of that logical device
608func (agent *LogicalDeviceAgent) flowDelete(mod *ofp.OfpFlowMod) error {
609 log.Debug("flowDelete")
610 if mod == nil {
611 return nil
612 }
613 agent.lockLogicalDevice.Lock()
614 defer agent.lockLogicalDevice.Unlock()
615
616 var lDevice *voltha.LogicalDevice
617 var err error
618 if lDevice, err = agent.getLogicalDeviceWithoutLock(); err != nil {
619 log.Errorw("no-logical-device-present", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
620 return errors.New(fmt.Sprintf("no-logical-device-present:%s", agent.logicalDeviceId))
621 }
622 flows := lDevice.Flows.Items
623
624 //build a list of what to keep vs what to delete
625 toKeep := make([]*ofp.OfpFlowStats, 0)
khenaidoo0458db62019-06-20 08:50:36 -0400626 toDelete := make([]*ofp.OfpFlowStats, 0)
khenaidoo19d7b632018-10-30 10:49:50 -0400627 for _, f := range flows {
khenaidoo0458db62019-06-20 08:50:36 -0400628 // Check whether the flow and the flowmod matches
629 if fu.FlowMatch(f, fu.FlowStatsEntryFromFlowModMessage(mod)) {
630 toDelete = append(toDelete, f)
631 continue
632 }
633 // Check wild card match
khenaidoo19d7b632018-10-30 10:49:50 -0400634 if !fu.FlowMatchesMod(f, mod) {
635 toKeep = append(toKeep, f)
khenaidoo0458db62019-06-20 08:50:36 -0400636 } else {
637 toDelete = append(toDelete, f)
khenaidoo19d7b632018-10-30 10:49:50 -0400638 }
639 }
640
khenaidoo0458db62019-06-20 08:50:36 -0400641 log.Debugw("flowDelete", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "toKeep": len(toKeep), "toDelete": toDelete})
642
khenaidoo19d7b632018-10-30 10:49:50 -0400643 //Update flows
khenaidoo0458db62019-06-20 08:50:36 -0400644 if len(toDelete) > 0 {
645 deviceRules := agent.flowDecomposer.DecomposeRules(agent, ofp.Flows{Items: toDelete}, ofp.FlowGroups{})
646 log.Debugw("rules", log.Fields{"rules": deviceRules.String()})
647
648 if err := agent.deleteDeviceFlowsAndGroups(deviceRules); err != nil {
649 log.Errorw("failure-updating-device-flows", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "error": err})
650 return err
651 }
652
khenaidoo43c82122018-11-22 18:38:28 -0500653 if err := agent.updateLogicalDeviceFlowsWithoutLock(&ofp.Flows{Items: toKeep}); err != nil {
654 log.Errorw("Cannot-update-flows", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
khenaidoo19d7b632018-10-30 10:49:50 -0400655 return err
656 }
657 }
658
659 //TODO: send announcement on delete
660 return nil
661}
662
khenaidoo0458db62019-06-20 08:50:36 -0400663func (agent *LogicalDeviceAgent) addDeviceFlowsAndGroups(deviceRules *fu.DeviceRules) error {
664 log.Debugw("addDeviceFlowsAndGroups", log.Fields{"logicalDeviceID": agent.logicalDeviceId})
khenaidoo19d7b632018-10-30 10:49:50 -0400665
khenaidoo0458db62019-06-20 08:50:36 -0400666 chnlsList := make([]chan interface{}, 0)
667 for deviceId, value := range deviceRules.GetRules() {
668 ch := make(chan interface{})
669 chnlsList = append(chnlsList, ch)
670 go func(deviceId string, flows []*ofp.OfpFlowStats, groups []*ofp.OfpGroupEntry) {
671 if err := agent.deviceMgr.addFlowsAndGroups(deviceId, flows, groups); err != nil {
672 log.Errorw("flow-add-failed", log.Fields{"deviceID": deviceId, "error": err})
673 ch <- status.Errorf(codes.Internal, "flow-add-failed: %s", deviceId)
674 }
675 ch <- nil
676 }(deviceId, value.ListFlows(), value.ListGroups())
khenaidoo19d7b632018-10-30 10:49:50 -0400677 }
khenaidoo0458db62019-06-20 08:50:36 -0400678 // Wait for completion
679 if res := fu.WaitForNilOrErrorResponses(agent.defaultTimeout, chnlsList...); res != nil {
680 return status.Errorf(codes.Aborted, "errors-%s", res)
khenaidoo19d7b632018-10-30 10:49:50 -0400681 }
khenaidoo0458db62019-06-20 08:50:36 -0400682 return nil
683}
khenaidoo19d7b632018-10-30 10:49:50 -0400684
khenaidoo0458db62019-06-20 08:50:36 -0400685func (agent *LogicalDeviceAgent) deleteDeviceFlowsAndGroups(deviceRules *fu.DeviceRules) error {
686 log.Debugw("deleteDeviceFlowsAndGroups", log.Fields{"logicalDeviceID": agent.logicalDeviceId})
687
688 chnlsList := make([]chan interface{}, 0)
689 for deviceId, value := range deviceRules.GetRules() {
690 ch := make(chan interface{})
691 chnlsList = append(chnlsList, ch)
692 go func(deviceId string, flows []*ofp.OfpFlowStats, groups []*ofp.OfpGroupEntry) {
693 if err := agent.deviceMgr.deleteFlowsAndGroups(deviceId, flows, groups); err != nil {
694 log.Error("flow-delete-failed", log.Fields{"deviceID": deviceId, "error": err})
695 ch <- status.Errorf(codes.Internal, "flow-delete-failed: %s", deviceId)
696 }
697 ch <- nil
698 }(deviceId, value.ListFlows(), value.ListGroups())
699 }
700 // Wait for completion
701 if res := fu.WaitForNilOrErrorResponses(agent.defaultTimeout, chnlsList...); res != nil {
702 return status.Errorf(codes.Aborted, "errors-%s", res)
703 }
704 return nil
705}
706
707func (agent *LogicalDeviceAgent) updateDeviceFlowsAndGroups(deviceRules *fu.DeviceRules) error {
708 log.Debugw("updateDeviceFlowsAndGroups", log.Fields{"logicalDeviceID": agent.logicalDeviceId})
709
710 chnlsList := make([]chan interface{}, 0)
711 for deviceId, value := range deviceRules.GetRules() {
712 ch := make(chan interface{})
713 chnlsList = append(chnlsList, ch)
714 go func(deviceId string, flows []*ofp.OfpFlowStats, groups []*ofp.OfpGroupEntry) {
715 if err := agent.deviceMgr.updateFlowsAndGroups(deviceId, flows, groups); err != nil {
716 log.Error("flow-update-failed", log.Fields{"deviceID": deviceId, "error": err})
717 ch <- status.Errorf(codes.Internal, "flow-update-failed: %s", deviceId)
718 }
719 ch <- nil
720 }(deviceId, value.ListFlows(), value.ListGroups())
721 }
722 // Wait for completion
723 if res := fu.WaitForNilOrErrorResponses(agent.defaultTimeout, chnlsList...); res != nil {
724 return status.Errorf(codes.Aborted, "errors-%s", res)
khenaidoo19d7b632018-10-30 10:49:50 -0400725 }
726 return nil
727}
728
729//flowDeleteStrict deletes a flow from the flow table of that logical device
730func (agent *LogicalDeviceAgent) flowDeleteStrict(mod *ofp.OfpFlowMod) error {
731 log.Debug("flowDeleteStrict")
732 if mod == nil {
733 return nil
734 }
735 agent.lockLogicalDevice.Lock()
736 defer agent.lockLogicalDevice.Unlock()
737
738 var lDevice *voltha.LogicalDevice
739 var err error
740 if lDevice, err = agent.getLogicalDeviceWithoutLock(); err != nil {
741 log.Errorw("no-logical-device-present", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
742 return errors.New(fmt.Sprintf("no-logical-device-present:%s", agent.logicalDeviceId))
743 }
744 flows := lDevice.Flows.Items
745 changed := false
khenaidoo68c930b2019-05-13 11:46:51 -0400746 flow := fu.FlowStatsEntryFromFlowModMessage(mod)
khenaidoo19d7b632018-10-30 10:49:50 -0400747 idx := fu.FindFlows(flows, flow)
748 if idx >= 0 {
749 flows = append(flows[:idx], flows[idx+1:]...)
750 changed = true
751 } else {
752 return errors.New(fmt.Sprintf("Cannot delete flow - %s", flow))
753 }
754
755 if changed {
khenaidoo0458db62019-06-20 08:50:36 -0400756 deviceRules := agent.flowDecomposer.DecomposeRules(agent, ofp.Flows{Items: []*ofp.OfpFlowStats{flow}}, ofp.FlowGroups{})
757 log.Debugw("rules", log.Fields{"rules": deviceRules.String()})
758
759 if err := agent.deleteDeviceFlowsAndGroups(deviceRules); err != nil {
760 log.Errorw("failure-deleting-device-flows", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "error": err})
761 return err
762 }
763
khenaidoo43c82122018-11-22 18:38:28 -0500764 if err := agent.updateLogicalDeviceFlowsWithoutLock(&ofp.Flows{Items: flows}); err != nil {
khenaidoo0458db62019-06-20 08:50:36 -0400765 log.Errorw("Cannot-update-flows", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
khenaidoo19d7b632018-10-30 10:49:50 -0400766 return err
767 }
768 }
khenaidoo19d7b632018-10-30 10:49:50 -0400769 return nil
770}
771
772//flowModify modifies a flow from the flow table of that logical device
773func (agent *LogicalDeviceAgent) flowModify(mod *ofp.OfpFlowMod) error {
774 return errors.New("flowModify not implemented")
775}
776
777//flowModifyStrict deletes a flow from the flow table of that logical device
778func (agent *LogicalDeviceAgent) flowModifyStrict(mod *ofp.OfpFlowMod) error {
779 return errors.New("flowModifyStrict not implemented")
780}
781
782func (agent *LogicalDeviceAgent) groupAdd(groupMod *ofp.OfpGroupMod) error {
783 log.Debug("groupAdd")
784 if groupMod == nil {
785 return nil
786 }
787 agent.lockLogicalDevice.Lock()
788 defer agent.lockLogicalDevice.Unlock()
789
790 var lDevice *voltha.LogicalDevice
791 var err error
792 if lDevice, err = agent.getLogicalDeviceWithoutLock(); err != nil {
793 log.Errorw("no-logical-device-present", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
794 return errors.New(fmt.Sprintf("no-logical-device-present:%s", agent.logicalDeviceId))
795 }
796 groups := lDevice.FlowGroups.Items
khenaidoo19d7b632018-10-30 10:49:50 -0400797 if fu.FindGroup(groups, groupMod.GroupId) == -1 {
khenaidoo68c930b2019-05-13 11:46:51 -0400798 groups = append(groups, fu.GroupEntryFromGroupMod(groupMod))
khenaidoo0458db62019-06-20 08:50:36 -0400799
800 deviceRules := agent.flowDecomposer.DecomposeRules(agent, *lDevice.Flows, ofp.FlowGroups{Items: groups})
801 log.Debugw("rules", log.Fields{"rules": deviceRules.String()})
802
803 if err := agent.addDeviceFlowsAndGroups(deviceRules); err != nil {
804 log.Errorw("failure-updating-device-flows", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "error": err})
805 return err
806 }
807
khenaidoo43c82122018-11-22 18:38:28 -0500808 if err := agent.updateLogicalDeviceFlowGroupsWithoutLock(&ofp.FlowGroups{Items: groups}); err != nil {
809 log.Errorw("Cannot-update-group", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
khenaidoo19d7b632018-10-30 10:49:50 -0400810 return err
811 }
812 } else {
813 return errors.New(fmt.Sprintf("Groups %d already present", groupMod.GroupId))
814 }
khenaidoo19d7b632018-10-30 10:49:50 -0400815 return nil
816}
817
818func (agent *LogicalDeviceAgent) groupDelete(groupMod *ofp.OfpGroupMod) error {
819 log.Debug("groupDelete")
820 if groupMod == nil {
821 return nil
822 }
823 agent.lockLogicalDevice.Lock()
824 defer agent.lockLogicalDevice.Unlock()
825
826 var lDevice *voltha.LogicalDevice
827 var err error
828 if lDevice, err = agent.getLogicalDeviceWithoutLock(); err != nil {
829 log.Errorw("no-logical-device-present", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
830 return errors.New(fmt.Sprintf("no-logical-device-present:%s", agent.logicalDeviceId))
831 }
832 groups := lDevice.FlowGroups.Items
833 flows := lDevice.Flows.Items
834 groupsChanged := false
835 flowsChanged := false
836 groupId := groupMod.GroupId
837 if groupId == uint32(ofp.OfpGroup_OFPG_ALL) {
838 //TODO we must delete all flows that point to this group and
839 //signal controller as requested by flow's flag
840 groups = []*ofp.OfpGroupEntry{}
841 groupsChanged = true
842 } else {
843 if idx := fu.FindGroup(groups, groupId); idx == -1 {
844 return nil // Valid case
845 } else {
846 flowsChanged, flows = fu.FlowsDeleteByGroupId(flows, groupId)
847 groups = append(groups[:idx], groups[idx+1:]...)
848 groupsChanged = true
849 }
850 }
khenaidoo0458db62019-06-20 08:50:36 -0400851 if flowsChanged || groupsChanged {
852 deviceRules := agent.flowDecomposer.DecomposeRules(agent, ofp.Flows{Items: flows}, ofp.FlowGroups{Items: groups})
853 log.Debugw("rules", log.Fields{"rules": deviceRules.String()})
854
855 if err := agent.updateDeviceFlowsAndGroups(deviceRules); err != nil {
856 log.Errorw("failure-updating-device-flows-groups", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "error": err})
857 return err
858 }
859 }
860
khenaidoo43c82122018-11-22 18:38:28 -0500861 if groupsChanged {
862 if err := agent.updateLogicalDeviceFlowGroupsWithoutLock(&ofp.FlowGroups{Items: groups}); err != nil {
863 log.Errorw("Cannot-update-group", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
khenaidoo19d7b632018-10-30 10:49:50 -0400864 return err
865 }
866 }
khenaidoo43c82122018-11-22 18:38:28 -0500867 if flowsChanged {
868 if err := agent.updateLogicalDeviceFlowsWithoutLock(&ofp.Flows{Items: flows}); err != nil {
869 log.Errorw("Cannot-update-flow", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
870 return err
871 }
872 }
khenaidoo19d7b632018-10-30 10:49:50 -0400873 return nil
874}
875
876func (agent *LogicalDeviceAgent) groupModify(groupMod *ofp.OfpGroupMod) error {
877 log.Debug("groupModify")
878 if groupMod == nil {
879 return nil
880 }
881 agent.lockLogicalDevice.Lock()
882 defer agent.lockLogicalDevice.Unlock()
883
884 var lDevice *voltha.LogicalDevice
885 var err error
886 if lDevice, err = agent.getLogicalDeviceWithoutLock(); err != nil {
887 log.Errorw("no-logical-device-present", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
888 return errors.New(fmt.Sprintf("no-logical-device-present:%s", agent.logicalDeviceId))
889 }
890 groups := lDevice.FlowGroups.Items
891 groupsChanged := false
892 groupId := groupMod.GroupId
893 if idx := fu.FindGroup(groups, groupId); idx == -1 {
khenaidooca301322019-01-09 23:06:32 -0500894 return errors.New(fmt.Sprintf("group-absent:%d", groupId))
khenaidoo19d7b632018-10-30 10:49:50 -0400895 } else {
896 //replace existing group entry with new group definition
khenaidoo68c930b2019-05-13 11:46:51 -0400897 groupEntry := fu.GroupEntryFromGroupMod(groupMod)
khenaidoo19d7b632018-10-30 10:49:50 -0400898 groups[idx] = groupEntry
899 groupsChanged = true
900 }
901 if groupsChanged {
khenaidoo0458db62019-06-20 08:50:36 -0400902 deviceRules := agent.flowDecomposer.DecomposeRules(agent, ofp.Flows{Items: lDevice.Flows.Items}, ofp.FlowGroups{Items: groups})
903 log.Debugw("rules", log.Fields{"rules": deviceRules.String()})
904
905 if err := agent.updateDeviceFlowsAndGroups(deviceRules); err != nil {
906 log.Errorw("failure-updating-device-flows-groups", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "error": err})
907 return err
908 }
909
khenaidoo43c82122018-11-22 18:38:28 -0500910 //lDevice.FlowGroups.Items = groups
911 if err := agent.updateLogicalDeviceFlowGroupsWithoutLock(&ofp.FlowGroups{Items: groups}); err != nil {
khenaidoo19d7b632018-10-30 10:49:50 -0400912 log.Errorw("Cannot-update-logical-group", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
913 return err
914 }
915 }
916 return nil
917}
918
919// deleteLogicalPort removes the logical port
920func (agent *LogicalDeviceAgent) deleteLogicalPort(lPort *voltha.LogicalPort) error {
921 agent.lockLogicalDevice.Lock()
922 defer agent.lockLogicalDevice.Unlock()
923
khenaidoo92e62c52018-10-03 14:02:54 -0400924 // Get the most up to date logical device
925 var logicaldevice *voltha.LogicalDevice
926 if logicaldevice, _ = agent.getLogicalDeviceWithoutLock(); logicaldevice == nil {
khenaidoo19d7b632018-10-30 10:49:50 -0400927 log.Debugw("no-logical-device", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "logicalPortId": lPort.Id})
khenaidoob9203542018-09-17 22:56:37 -0400928 return nil
929 }
khenaidoo92e62c52018-10-03 14:02:54 -0400930 index := -1
931 for i, logicalPort := range logicaldevice.Ports {
khenaidoo19d7b632018-10-30 10:49:50 -0400932 if logicalPort.Id == lPort.Id {
khenaidoo92e62c52018-10-03 14:02:54 -0400933 index = i
934 break
935 }
936 }
937 if index >= 0 {
938 copy(logicaldevice.Ports[index:], logicaldevice.Ports[index+1:])
939 logicaldevice.Ports[len(logicaldevice.Ports)-1] = nil
940 logicaldevice.Ports = logicaldevice.Ports[:len(logicaldevice.Ports)-1]
941 log.Debugw("logical-port-deleted", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
khenaidoo0a822f92019-05-08 15:15:57 -0400942 if err := agent.updateLogicalDeviceWithoutLock(logicaldevice); err != nil {
943 log.Errorw("logical-device-update-failed", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
944 return err
945 }
946 // Reset the logical device graph
khenaidoo3d3b8c22019-05-22 18:10:39 -0400947 go agent.generateDeviceGraph()
khenaidoo92e62c52018-10-03 14:02:54 -0400948 }
949 return nil
khenaidoob9203542018-09-17 22:56:37 -0400950}
951
khenaidoo0a822f92019-05-08 15:15:57 -0400952// deleteLogicalPorts removes the logical ports associated with that deviceId
953func (agent *LogicalDeviceAgent) deleteLogicalPorts(deviceId string) error {
954 agent.lockLogicalDevice.Lock()
955 defer agent.lockLogicalDevice.Unlock()
956
957 // Get the most up to date logical device
958 var logicaldevice *voltha.LogicalDevice
959 if logicaldevice, _ = agent.getLogicalDeviceWithoutLock(); logicaldevice == nil {
960 log.Debugw("no-logical-device", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
961 return nil
962 }
963 updatedLPorts := []*voltha.LogicalPort{}
964 for _, logicalPort := range logicaldevice.Ports {
965 if logicalPort.DeviceId != deviceId {
966 updatedLPorts = append(updatedLPorts, logicalPort)
967 }
968 }
969 logicaldevice.Ports = updatedLPorts
970 log.Debugw("updated-logical-ports", log.Fields{"ports": updatedLPorts})
971 if err := agent.updateLogicalDeviceWithoutLock(logicaldevice); err != nil {
972 log.Errorw("logical-device-update-failed", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
973 return err
974 }
975 // Reset the logical device graph
khenaidoo3d3b8c22019-05-22 18:10:39 -0400976 go agent.generateDeviceGraph()
khenaidoo0a822f92019-05-08 15:15:57 -0400977
978 return nil
979}
980
khenaidoo19d7b632018-10-30 10:49:50 -0400981// enableLogicalPort enables the logical port
982func (agent *LogicalDeviceAgent) enableLogicalPort(lPort *voltha.LogicalPort) error {
983 agent.lockLogicalDevice.Lock()
984 defer agent.lockLogicalDevice.Unlock()
985
986 // Get the most up to date logical device
987 var logicaldevice *voltha.LogicalDevice
988 if logicaldevice, _ = agent.getLogicalDeviceWithoutLock(); logicaldevice == nil {
989 log.Debugw("no-logical-device", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "logicalPortId": lPort.Id})
990 return nil
991 }
992 index := -1
993 for i, logicalPort := range logicaldevice.Ports {
994 if logicalPort.Id == lPort.Id {
995 index = i
996 break
997 }
998 }
999 if index >= 0 {
1000 logicaldevice.Ports[index].OfpPort.Config = logicaldevice.Ports[index].OfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
1001 return agent.updateLogicalDeviceWithoutLock(logicaldevice)
1002 }
1003 //TODO: Trigger subsequent actions on the device
1004 return nil
1005}
1006
1007// disableLogicalPort disabled the logical port
1008func (agent *LogicalDeviceAgent) disableLogicalPort(lPort *voltha.LogicalPort) error {
1009 agent.lockLogicalDevice.Lock()
1010 defer agent.lockLogicalDevice.Unlock()
1011
1012 // Get the most up to date logical device
1013 var logicaldevice *voltha.LogicalDevice
1014 if logicaldevice, _ = agent.getLogicalDeviceWithoutLock(); logicaldevice == nil {
1015 log.Debugw("no-logical-device", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "logicalPortId": lPort.Id})
1016 return nil
1017 }
1018 index := -1
1019 for i, logicalPort := range logicaldevice.Ports {
1020 if logicalPort.Id == lPort.Id {
1021 index = i
1022 break
1023 }
1024 }
1025 if index >= 0 {
1026 logicaldevice.Ports[index].OfpPort.Config = (logicaldevice.Ports[index].OfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)) | uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
1027 return agent.updateLogicalDeviceWithoutLock(logicaldevice)
1028 }
1029 //TODO: Trigger subsequent actions on the device
1030 return nil
1031}
1032
khenaidoo89b0e942018-10-21 21:11:33 -04001033func (agent *LogicalDeviceAgent) getPreCalculatedRoute(ingress, egress uint32) []graph.RouteHop {
khenaidoo19d7b632018-10-30 10:49:50 -04001034 log.Debugw("ROUTE", log.Fields{"len": len(agent.deviceGraph.Routes)})
khenaidoo89b0e942018-10-21 21:11:33 -04001035 for routeLink, route := range agent.deviceGraph.Routes {
khenaidoo19d7b632018-10-30 10:49:50 -04001036 log.Debugw("ROUTELINKS", log.Fields{"ingress": ingress, "egress": egress, "routelink": routeLink})
khenaidoo89b0e942018-10-21 21:11:33 -04001037 if ingress == routeLink.Ingress && egress == routeLink.Egress {
1038 return route
1039 }
1040 }
1041 log.Warnw("no-route", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "ingress": ingress, "egress": egress})
1042 return nil
1043}
1044
khenaidoo19d7b632018-10-30 10:49:50 -04001045func (agent *LogicalDeviceAgent) GetRoute(ingressPortNo uint32, egressPortNo uint32) []graph.RouteHop {
khenaidoo89b0e942018-10-21 21:11:33 -04001046 log.Debugw("getting-route", log.Fields{"ingress-port": ingressPortNo, "egress-port": egressPortNo})
khenaidoo89b0e942018-10-21 21:11:33 -04001047 routes := make([]graph.RouteHop, 0)
khenaidoo2c6a0992019-04-29 13:46:56 -04001048
khenaidoo19d7b632018-10-30 10:49:50 -04001049 // Note: A port value of 0 is equivalent to a nil port
1050
khenaidoo89b0e942018-10-21 21:11:33 -04001051 // Consider different possibilities
khenaidoo19d7b632018-10-30 10:49:50 -04001052 if egressPortNo != 0 && ((egressPortNo & 0x7fffffff) == uint32(ofp.OfpPortNo_OFPP_CONTROLLER)) {
khenaidoo2c6a0992019-04-29 13:46:56 -04001053 log.Debugw("controller-flow", log.Fields{"ingressPortNo": ingressPortNo, "egressPortNo": egressPortNo, "logicalPortsNo": agent.logicalPortsNo})
1054 if agent.isNNIPort(ingressPortNo) {
khenaidoo89b0e942018-10-21 21:11:33 -04001055 log.Debug("returning-half-route")
1056 //This is a trap on the NNI Port
khenaidoo8f474192019-04-03 17:20:44 -04001057 if len(agent.deviceGraph.Routes) == 0 {
1058 // If there are no routes set (usually when the logical device has only NNI port(s), then just return an
1059 // internal route
khenaidoo2c6a0992019-04-29 13:46:56 -04001060 hop := graph.RouteHop{DeviceID: agent.rootDeviceId, Ingress: ingressPortNo, Egress: egressPortNo}
khenaidoo8f474192019-04-03 17:20:44 -04001061 routes = append(routes, hop)
1062 routes = append(routes, hop)
1063 return routes
1064 }
khenaidoo89b0e942018-10-21 21:11:33 -04001065 //Return a 'half' route to make the flow decomposer logic happy
1066 for routeLink, route := range agent.deviceGraph.Routes {
khenaidoo2c6a0992019-04-29 13:46:56 -04001067 if agent.isNNIPort(routeLink.Egress) {
khenaidoo89b0e942018-10-21 21:11:33 -04001068 routes = append(routes, graph.RouteHop{}) // first hop is set to empty
1069 routes = append(routes, route[1])
1070 return routes
1071 }
1072 }
khenaidoo2c6a0992019-04-29 13:46:56 -04001073 log.Warnw("no-upstream-route", log.Fields{"ingressPortNo": ingressPortNo, "egressPortNo": egressPortNo, "logicalPortsNo": agent.logicalPortsNo})
khenaidoo89b0e942018-10-21 21:11:33 -04001074 return nil
1075 }
1076 //treat it as if the output port is the first NNI of the OLT
khenaidoo2c6a0992019-04-29 13:46:56 -04001077 var err error
1078 if egressPortNo, err = agent.getFirstNNIPort(); err != nil {
1079 log.Warnw("no-nni-port", log.Fields{"error": err})
1080 return nil
1081 }
khenaidoo89b0e942018-10-21 21:11:33 -04001082 }
1083 //If ingress port is not specified (nil), it may be a wildcarded
1084 //route if egress port is OFPP_CONTROLLER or a nni logical port,
1085 //in which case we need to create a half-route where only the egress
1086 //hop is filled, the first hop is nil
khenaidoo2c6a0992019-04-29 13:46:56 -04001087 if ingressPortNo == 0 && agent.isNNIPort(egressPortNo) {
khenaidoo89b0e942018-10-21 21:11:33 -04001088 // We can use the 2nd hop of any upstream route, so just find the first upstream:
1089 for routeLink, route := range agent.deviceGraph.Routes {
khenaidoo2c6a0992019-04-29 13:46:56 -04001090 if agent.isNNIPort(routeLink.Egress) {
khenaidoo89b0e942018-10-21 21:11:33 -04001091 routes = append(routes, graph.RouteHop{}) // first hop is set to empty
1092 routes = append(routes, route[1])
1093 return routes
1094 }
1095 }
khenaidoo2c6a0992019-04-29 13:46:56 -04001096 log.Warnw("no-upstream-route", log.Fields{"ingressPortNo": ingressPortNo, "egressPortNo": egressPortNo, "logicalPortsNo": agent.logicalPortsNo})
khenaidoo89b0e942018-10-21 21:11:33 -04001097 return nil
1098 }
1099 //If egress port is not specified (nil), we can also can return a "half" route
khenaidoo19d7b632018-10-30 10:49:50 -04001100 if egressPortNo == 0 {
khenaidoo89b0e942018-10-21 21:11:33 -04001101 for routeLink, route := range agent.deviceGraph.Routes {
khenaidoo19d7b632018-10-30 10:49:50 -04001102 if routeLink.Ingress == ingressPortNo {
khenaidoo89b0e942018-10-21 21:11:33 -04001103 routes = append(routes, route[0])
1104 routes = append(routes, graph.RouteHop{})
1105 return routes
1106 }
1107 }
khenaidoo2c6a0992019-04-29 13:46:56 -04001108 log.Warnw("no-downstream-route", log.Fields{"ingressPortNo": ingressPortNo, "egressPortNo": egressPortNo, "logicalPortsNo": agent.logicalPortsNo})
khenaidoo89b0e942018-10-21 21:11:33 -04001109 return nil
1110 }
khenaidoo89b0e942018-10-21 21:11:33 -04001111 // Return the pre-calculated route
khenaidoo19d7b632018-10-30 10:49:50 -04001112 return agent.getPreCalculatedRoute(ingressPortNo, egressPortNo)
khenaidoo89b0e942018-10-21 21:11:33 -04001113}
1114
khenaidoo3d3b8c22019-05-22 18:10:39 -04001115//GetWildcardInputPorts filters out the logical port number from the set of logical ports on the device and
1116//returns their port numbers. This function is invoked only during flow decomposition where the lock on the logical
1117//device is already held. Therefore it is safe to retrieve the logical device without lock.
khenaidoo89b0e942018-10-21 21:11:33 -04001118func (agent *LogicalDeviceAgent) GetWildcardInputPorts(excludePort ...uint32) []uint32 {
1119 lPorts := make([]uint32, 0)
1120 var exclPort uint32
1121 if len(excludePort) == 1 {
1122 exclPort = excludePort[0]
1123 }
khenaidoo3d3b8c22019-05-22 18:10:39 -04001124 if lDevice, _ := agent.getLogicalDeviceWithoutLock(); lDevice != nil {
khenaidoo89b0e942018-10-21 21:11:33 -04001125 for _, port := range lDevice.Ports {
1126 if port.OfpPort.PortNo != exclPort {
1127 lPorts = append(lPorts, port.OfpPort.PortNo)
1128 }
1129 }
1130 }
1131 return lPorts
1132}
khenaidoo19d7b632018-10-30 10:49:50 -04001133
1134func (agent *LogicalDeviceAgent) GetDeviceGraph() *graph.DeviceGraph {
1135 return agent.deviceGraph
1136}
1137
khenaidoo3306c992019-05-24 16:57:35 -04001138//updateRoutes rebuilds the device graph if not done already
khenaidoo2c6a0992019-04-29 13:46:56 -04001139func (agent *LogicalDeviceAgent) updateRoutes(device *voltha.Device, port *voltha.Port) error {
1140 log.Debugf("updateRoutes", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "device": device.Id, "port": port})
khenaidoo910204f2019-04-08 17:56:40 -04001141 agent.lockLogicalDevice.Lock()
1142 defer agent.lockLogicalDevice.Unlock()
khenaidoo19d7b632018-10-30 10:49:50 -04001143 if agent.deviceGraph == nil {
khenaidoo910204f2019-04-08 17:56:40 -04001144 agent.deviceGraph = graph.NewDeviceGraph(agent.logicalDeviceId, agent.deviceMgr.GetDevice)
khenaidoo2c6a0992019-04-29 13:46:56 -04001145 }
1146 // Get all the logical ports on that logical device
1147 if lDevice, err := agent.getLogicalDeviceWithoutLock(); err != nil {
manikkaraj k259a6f72019-05-06 09:55:44 -04001148 log.Errorw("unknown-logical-device", log.Fields{"error": err, "logicalDeviceId": agent.logicalDeviceId})
khenaidoo2c6a0992019-04-29 13:46:56 -04001149 return err
1150 } else {
1151 //TODO: Find a better way to refresh only missing routes
1152 agent.deviceGraph.ComputeRoutes(lDevice.Ports)
1153 }
khenaidoo2c6a0992019-04-29 13:46:56 -04001154 agent.deviceGraph.Print()
1155 return nil
khenaidoo19d7b632018-10-30 10:49:50 -04001156}
1157
khenaidoo2c6a0992019-04-29 13:46:56 -04001158//updateDeviceGraph updates the device graph if not done already and setup the default rules as well
khenaidoo910204f2019-04-08 17:56:40 -04001159func (agent *LogicalDeviceAgent) updateDeviceGraph(lp *voltha.LogicalPort) {
khenaidoo2c6a0992019-04-29 13:46:56 -04001160 log.Debugf("updateDeviceGraph", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
1161 agent.lockLogicalDevice.Lock()
1162 defer agent.lockLogicalDevice.Unlock()
khenaidoo910204f2019-04-08 17:56:40 -04001163 if agent.deviceGraph == nil {
1164 agent.deviceGraph = graph.NewDeviceGraph(agent.logicalDeviceId, agent.deviceMgr.GetDevice)
1165 }
1166 agent.deviceGraph.AddPort(lp)
khenaidoo2c6a0992019-04-29 13:46:56 -04001167 agent.deviceGraph.Print()
khenaidoo19d7b632018-10-30 10:49:50 -04001168}
khenaidoofdbad6e2018-11-06 22:26:38 -05001169
khenaidoo3d3b8c22019-05-22 18:10:39 -04001170//generateDeviceGraph regenerates the device graph
1171func (agent *LogicalDeviceAgent) generateDeviceGraph() {
1172 log.Debugf("generateDeviceGraph", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
khenaidoo0a822f92019-05-08 15:15:57 -04001173 agent.lockLogicalDevice.Lock()
1174 defer agent.lockLogicalDevice.Unlock()
1175 // Get the latest logical device
1176 if ld, err := agent.getLogicalDeviceWithoutLock(); err != nil {
1177 log.Errorw("logical-device-not-present", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "error": err})
1178 } else {
khenaidoo3d3b8c22019-05-22 18:10:39 -04001179 log.Debugw("generating-graph", log.Fields{"lDeviceId": agent.logicalDeviceId, "deviceGraph": agent.deviceGraph, "lPorts": len(ld.Ports)})
1180 if agent.deviceGraph == nil {
1181 agent.deviceGraph = graph.NewDeviceGraph(agent.logicalDeviceId, agent.deviceMgr.GetDevice)
1182 }
khenaidoo0a822f92019-05-08 15:15:57 -04001183 agent.deviceGraph.ComputeRoutes(ld.Ports)
khenaidoo3d3b8c22019-05-22 18:10:39 -04001184 agent.deviceGraph.Print()
khenaidoo0a822f92019-05-08 15:15:57 -04001185 }
1186}
1187
khenaidoofc1314d2019-03-14 09:34:21 -04001188// portAdded is a callback invoked when a port is added to the logical device.
1189// TODO: To use when POST_ADD is fixed.
1190func (agent *LogicalDeviceAgent) portAdded(args ...interface{}) interface{} {
1191 log.Debugw("portAdded-callback", log.Fields{"argsLen": len(args)})
1192
1193 var port *voltha.LogicalPort
1194
1195 // Sanity check
1196 if args[0] != nil {
1197 log.Warnw("previous-data-not-nil", log.Fields{"args0": args[0]})
1198 }
1199 var ok bool
1200 if port, ok = args[1].(*voltha.LogicalPort); !ok {
1201 log.Errorw("invalid-args", log.Fields{"args1": args[1]})
1202 return nil
1203 }
1204
1205 // Set the proxy and callback for that port
1206 agent.portProxiesLock.Lock()
Stephane Barbarie40fd3b22019-04-23 21:50:47 -04001207 agent.portProxies[port.Id] = agent.clusterDataProxy.CreateProxy(
Stephane Barbarieef6650d2019-07-18 12:15:09 -04001208 context.Background(),
khenaidoofc1314d2019-03-14 09:34:21 -04001209 fmt.Sprintf("/logical_devices/%s/ports/%s", agent.logicalDeviceId, port.Id),
1210 false)
1211 agent.portProxies[port.Id].RegisterCallback(model.POST_UPDATE, agent.portUpdated)
1212 agent.portProxiesLock.Unlock()
1213
1214 // Send the port change event to the OF controller
1215 agent.ldeviceMgr.grpcNbiHdlr.sendChangeEvent(agent.logicalDeviceId,
khenaidoo910204f2019-04-08 17:56:40 -04001216 &ofp.OfpPortStatus{Reason: ofp.OfpPortReason_OFPPR_ADD, Desc: port.OfpPort})
khenaidoofc1314d2019-03-14 09:34:21 -04001217
1218 return nil
1219}
1220
1221// portRemoved is a callback invoked when a port is removed from the logical device.
1222// TODO: To use when POST_ADD is fixed.
1223func (agent *LogicalDeviceAgent) portRemoved(args ...interface{}) interface{} {
1224 log.Debugw("portRemoved-callback", log.Fields{"argsLen": len(args)})
1225
1226 var port *voltha.LogicalPort
1227
1228 // Sanity check
1229 if args[1] != nil {
1230 log.Warnw("data-not-nil", log.Fields{"args1": args[1]})
1231 }
1232 var ok bool
1233 if port, ok = args[0].(*voltha.LogicalPort); !ok {
1234 log.Errorw("invalid-args", log.Fields{"args0": args[0]})
1235 return nil
1236 }
1237
1238 // Remove the proxy and callback for that port
1239 agent.portProxiesLock.Lock()
1240 agent.portProxies[port.Id].UnregisterCallback(model.POST_UPDATE, agent.portUpdated)
1241 delete(agent.portProxies, port.Id)
1242 agent.portProxiesLock.Unlock()
1243
1244 // Send the port change event to the OF controller
1245 agent.ldeviceMgr.grpcNbiHdlr.sendChangeEvent(agent.logicalDeviceId,
khenaidoo910204f2019-04-08 17:56:40 -04001246 &ofp.OfpPortStatus{Reason: ofp.OfpPortReason_OFPPR_DELETE, Desc: port.OfpPort})
khenaidoofc1314d2019-03-14 09:34:21 -04001247
1248 return nil
1249}
1250
1251// diff go over two lists of logical ports and return what's new, what's changed and what's removed.
khenaidoo910204f2019-04-08 17:56:40 -04001252func diff(oldList, newList []*voltha.LogicalPort) (newPorts, changedPorts, deletedPorts []*voltha.LogicalPort) {
khenaidoofc1314d2019-03-14 09:34:21 -04001253 newPorts = make([]*voltha.LogicalPort, 0)
1254 changedPorts = make([]*voltha.LogicalPort, 0)
1255 deletedPorts = make([]*voltha.LogicalPort, 0)
1256 for _, o := range oldList {
1257 found := false
khenaidoofc1314d2019-03-14 09:34:21 -04001258 for _, n := range newList {
1259 if o.Id == n.Id {
khenaidoofc1314d2019-03-14 09:34:21 -04001260 found = true
1261 break
1262 }
1263 }
1264 if !found {
1265 deletedPorts = append(deletedPorts, o)
1266 }
khenaidoofc1314d2019-03-14 09:34:21 -04001267 }
1268 for _, n := range newList {
1269 found := false
khenaidoo2bc48282019-07-16 18:13:46 -04001270 changed := false
khenaidoofc1314d2019-03-14 09:34:21 -04001271 for _, o := range oldList {
1272 if o.Id == n.Id {
khenaidoo2bc48282019-07-16 18:13:46 -04001273 changed = !reflect.DeepEqual(o, n)
khenaidoofc1314d2019-03-14 09:34:21 -04001274 found = true
1275 break
1276 }
1277 }
1278 if !found {
1279 newPorts = append(newPorts, n)
1280 }
khenaidoo2bc48282019-07-16 18:13:46 -04001281 if changed {
1282 changedPorts = append(changedPorts, n)
1283 }
khenaidoofc1314d2019-03-14 09:34:21 -04001284 }
1285 return
1286}
1287
1288// portUpdated is invoked when a port is updated on the logical device. Until
1289// the POST_ADD notification is fixed, we will use the logical device to
1290// update that data.
1291func (agent *LogicalDeviceAgent) portUpdated(args ...interface{}) interface{} {
1292 log.Debugw("portUpdated-callback", log.Fields{"argsLen": len(args)})
1293
1294 var oldLD *voltha.LogicalDevice
1295 var newlD *voltha.LogicalDevice
1296
1297 var ok bool
1298 if oldLD, ok = args[0].(*voltha.LogicalDevice); !ok {
1299 log.Errorw("invalid-args", log.Fields{"args0": args[0]})
1300 return nil
1301 }
1302 if newlD, ok = args[1].(*voltha.LogicalDevice); !ok {
1303 log.Errorw("invalid-args", log.Fields{"args1": args[1]})
1304 return nil
1305 }
1306
1307 if reflect.DeepEqual(oldLD.Ports, newlD.Ports) {
1308 log.Debug("ports-have-not-changed")
1309 return nil
1310 }
1311
1312 // Get the difference between the two list
1313 newPorts, changedPorts, deletedPorts := diff(oldLD.Ports, newlD.Ports)
1314
1315 // Send the port change events to the OF controller
khenaidoo2c6a0992019-04-29 13:46:56 -04001316 for _, newP := range newPorts {
khenaidoofc1314d2019-03-14 09:34:21 -04001317 go agent.ldeviceMgr.grpcNbiHdlr.sendChangeEvent(agent.logicalDeviceId,
khenaidoo2c6a0992019-04-29 13:46:56 -04001318 &ofp.OfpPortStatus{Reason: ofp.OfpPortReason_OFPPR_ADD, Desc: newP.OfpPort})
khenaidoofc1314d2019-03-14 09:34:21 -04001319 }
1320 for _, change := range changedPorts {
1321 go agent.ldeviceMgr.grpcNbiHdlr.sendChangeEvent(agent.logicalDeviceId,
khenaidoo910204f2019-04-08 17:56:40 -04001322 &ofp.OfpPortStatus{Reason: ofp.OfpPortReason_OFPPR_MODIFY, Desc: change.OfpPort})
khenaidoofc1314d2019-03-14 09:34:21 -04001323 }
1324 for _, del := range deletedPorts {
1325 go agent.ldeviceMgr.grpcNbiHdlr.sendChangeEvent(agent.logicalDeviceId,
khenaidoo910204f2019-04-08 17:56:40 -04001326 &ofp.OfpPortStatus{Reason: ofp.OfpPortReason_OFPPR_DELETE, Desc: del.OfpPort})
khenaidoofc1314d2019-03-14 09:34:21 -04001327 }
1328
1329 return nil
1330}
1331
khenaidoo8f474192019-04-03 17:20:44 -04001332// addNNILogicalPort adds an NNI port to the logical device. It returns a bool representing whether a port has been
1333// added and an eror in case a valid error is encountered. If the port was successfully added it will return
1334// (true, nil). If the device is not in the correct state it will return (false, nil) as this is a valid
1335// scenario. This also applies to the case where the port was already added.
khenaidoo910204f2019-04-08 17:56:40 -04001336func (agent *LogicalDeviceAgent) addNNILogicalPort(device *voltha.Device, port *voltha.Port) (bool, error) {
khenaidoo1ce37ad2019-03-24 22:07:24 -04001337 log.Debugw("addNNILogicalPort", log.Fields{"NNI": port})
khenaidoo8f474192019-04-03 17:20:44 -04001338 if device.AdminState != voltha.AdminState_ENABLED || device.OperStatus != voltha.OperStatus_ACTIVE {
1339 log.Infow("device-not-ready", log.Fields{"deviceId": device.Id, "admin": device.AdminState, "oper": device.OperStatus})
1340 return false, nil
khenaidoofc1314d2019-03-14 09:34:21 -04001341 }
khenaidoo1ce37ad2019-03-24 22:07:24 -04001342 agent.lockLogicalDevice.RLock()
1343 if agent.portExist(device, port) {
1344 log.Debugw("port-already-exist", log.Fields{"port": port})
1345 agent.lockLogicalDevice.RUnlock()
khenaidoo8f474192019-04-03 17:20:44 -04001346 return false, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -04001347 }
1348 agent.lockLogicalDevice.RUnlock()
1349
khenaidoofc1314d2019-03-14 09:34:21 -04001350 var portCap *ic.PortCapability
1351 var err error
1352 // First get the port capability
1353 if portCap, err = agent.deviceMgr.getPortCapability(nil, device.Id, port.PortNo); err != nil {
1354 log.Errorw("error-retrieving-port-capabilities", log.Fields{"error": err})
khenaidoo8f474192019-04-03 17:20:44 -04001355 return false, err
khenaidoofc1314d2019-03-14 09:34:21 -04001356 }
khenaidoo1ce37ad2019-03-24 22:07:24 -04001357
1358 agent.lockLogicalDevice.Lock()
1359 defer agent.lockLogicalDevice.Unlock()
1360 // Double check again if this port has been already added since the getPortCapability could have taken a long time
1361 if agent.portExist(device, port) {
1362 log.Debugw("port-already-exist", log.Fields{"port": port})
khenaidoo8f474192019-04-03 17:20:44 -04001363 return false, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -04001364 }
1365
khenaidoofc1314d2019-03-14 09:34:21 -04001366 portCap.Port.RootPort = true
1367 lp := (proto.Clone(portCap.Port)).(*voltha.LogicalPort)
1368 lp.DeviceId = device.Id
1369 lp.Id = fmt.Sprintf("nni-%d", port.PortNo)
1370 lp.OfpPort.PortNo = port.PortNo
1371 lp.OfpPort.Name = lp.Id
1372 lp.DevicePortNo = port.PortNo
1373
khenaidoofc1314d2019-03-14 09:34:21 -04001374 var ld *voltha.LogicalDevice
1375 if ld, err = agent.getLogicalDeviceWithoutLock(); err != nil {
1376 log.Errorw("error-retrieving-logical-device", log.Fields{"error": err})
khenaidoo8f474192019-04-03 17:20:44 -04001377 return false, err
khenaidoofc1314d2019-03-14 09:34:21 -04001378 }
1379 cloned := (proto.Clone(ld)).(*voltha.LogicalDevice)
1380 if cloned.Ports == nil {
1381 cloned.Ports = make([]*voltha.LogicalPort, 0)
1382 }
1383 cloned.Ports = append(cloned.Ports, lp)
1384
1385 if err = agent.updateLogicalDeviceWithoutLock(cloned); err != nil {
1386 log.Errorw("error-updating-logical-device", log.Fields{"error": err})
khenaidoo8f474192019-04-03 17:20:44 -04001387 return false, err
khenaidoofc1314d2019-03-14 09:34:21 -04001388 }
khenaidoo910204f2019-04-08 17:56:40 -04001389
1390 // Update the device graph with this new logical port
1391 clonedLP := (proto.Clone(lp)).(*voltha.LogicalPort)
1392 go agent.updateDeviceGraph(clonedLP)
1393
khenaidoo8f474192019-04-03 17:20:44 -04001394 return true, nil
khenaidoofc1314d2019-03-14 09:34:21 -04001395}
1396
khenaidoo910204f2019-04-08 17:56:40 -04001397func (agent *LogicalDeviceAgent) portExist(device *voltha.Device, port *voltha.Port) bool {
khenaidoo54544ae2019-03-18 13:22:39 -04001398 if ldevice, _ := agent.getLogicalDeviceWithoutLock(); ldevice != nil {
khenaidoofc1314d2019-03-14 09:34:21 -04001399 for _, lPort := range ldevice.Ports {
khenaidoo54544ae2019-03-18 13:22:39 -04001400 if lPort.DeviceId == device.Id && lPort.DevicePortNo == port.PortNo && lPort.Id == port.Label {
khenaidoofc1314d2019-03-14 09:34:21 -04001401 return true
1402 }
1403 }
1404 }
1405 return false
1406}
1407
khenaidoo8f474192019-04-03 17:20:44 -04001408// addUNILogicalPort adds an UNI port to the logical device. It returns a bool representing whether a port has been
1409// added and an eror in case a valid error is encountered. If the port was successfully added it will return
1410// (true, nil). If the device is not in the correct state it will return (false, nil) as this is a valid
1411// scenario. This also applies to the case where the port was already added.
khenaidoo910204f2019-04-08 17:56:40 -04001412func (agent *LogicalDeviceAgent) addUNILogicalPort(childDevice *voltha.Device, port *voltha.Port) (bool, error) {
khenaidoofc1314d2019-03-14 09:34:21 -04001413 log.Debugw("addUNILogicalPort", log.Fields{"port": port})
khenaidoo8f474192019-04-03 17:20:44 -04001414 if childDevice.AdminState != voltha.AdminState_ENABLED || childDevice.OperStatus != voltha.OperStatus_ACTIVE {
1415 log.Infow("device-not-ready", log.Fields{"deviceId": childDevice.Id, "admin": childDevice.AdminState, "oper": childDevice.OperStatus})
1416 return false, nil
khenaidoofc1314d2019-03-14 09:34:21 -04001417 }
khenaidoo1ce37ad2019-03-24 22:07:24 -04001418 agent.lockLogicalDevice.RLock()
1419 if agent.portExist(childDevice, port) {
1420 log.Debugw("port-already-exist", log.Fields{"port": port})
1421 agent.lockLogicalDevice.RUnlock()
khenaidoo8f474192019-04-03 17:20:44 -04001422 return false, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -04001423 }
1424 agent.lockLogicalDevice.RUnlock()
khenaidoofc1314d2019-03-14 09:34:21 -04001425 var portCap *ic.PortCapability
1426 var err error
1427 // First get the port capability
1428 if portCap, err = agent.deviceMgr.getPortCapability(nil, childDevice.Id, port.PortNo); err != nil {
1429 log.Errorw("error-retrieving-port-capabilities", log.Fields{"error": err})
khenaidoo8f474192019-04-03 17:20:44 -04001430 return false, err
khenaidoofc1314d2019-03-14 09:34:21 -04001431 }
khenaidoo1ce37ad2019-03-24 22:07:24 -04001432 agent.lockLogicalDevice.Lock()
1433 defer agent.lockLogicalDevice.Unlock()
1434 // Double check again if this port has been already added since the getPortCapability could have taken a long time
1435 if agent.portExist(childDevice, port) {
1436 log.Debugw("port-already-exist", log.Fields{"port": port})
khenaidoo8f474192019-04-03 17:20:44 -04001437 return false, nil
khenaidoo1ce37ad2019-03-24 22:07:24 -04001438 }
khenaidoofc1314d2019-03-14 09:34:21 -04001439 // Get stored logical device
1440 if ldevice, err := agent.getLogicalDeviceWithoutLock(); err != nil {
khenaidoo8f474192019-04-03 17:20:44 -04001441 return false, status.Error(codes.NotFound, agent.logicalDeviceId)
khenaidoofc1314d2019-03-14 09:34:21 -04001442 } else {
1443 log.Debugw("adding-uni", log.Fields{"deviceId": childDevice.Id})
1444 portCap.Port.RootPort = false
Matt Jeanneret3815e322019-03-12 19:15:49 -04001445 portCap.Port.Id = port.Label
1446 portCap.Port.OfpPort.PortNo = port.PortNo
khenaidoofc1314d2019-03-14 09:34:21 -04001447 portCap.Port.DeviceId = childDevice.Id
1448 portCap.Port.DevicePortNo = port.PortNo
1449 cloned := (proto.Clone(ldevice)).(*voltha.LogicalDevice)
1450 if cloned.Ports == nil {
1451 cloned.Ports = make([]*voltha.LogicalPort, 0)
1452 }
1453 cloned.Ports = append(cloned.Ports, portCap.Port)
khenaidoo910204f2019-04-08 17:56:40 -04001454 if err := agent.updateLogicalDeviceWithoutLock(cloned); err != nil {
1455 return false, err
1456 }
khenaidoo910204f2019-04-08 17:56:40 -04001457 // Update the device graph with this new logical port
1458 clonedLP := (proto.Clone(portCap.Port)).(*voltha.LogicalPort)
1459 go agent.updateDeviceGraph(clonedLP)
1460 return true, nil
khenaidoofc1314d2019-03-14 09:34:21 -04001461 }
1462}
1463
khenaidoo43c82122018-11-22 18:38:28 -05001464func (agent *LogicalDeviceAgent) packetOut(packet *ofp.OfpPacketOut) {
khenaidoofdbad6e2018-11-06 22:26:38 -05001465 log.Debugw("packet-out", log.Fields{"packet": packet.GetInPort()})
khenaidoo68c930b2019-05-13 11:46:51 -04001466 outPort := fu.GetPacketOutPort(packet)
khenaidoofdbad6e2018-11-06 22:26:38 -05001467 //frame := packet.GetData()
1468 //TODO: Use a channel between the logical agent and the device agent
khenaidooca301322019-01-09 23:06:32 -05001469 if err := agent.deviceMgr.packetOut(agent.rootDeviceId, outPort, packet); err != nil {
khenaidoo910204f2019-04-08 17:56:40 -04001470 log.Error("packetout-failed", log.Fields{"logicalDeviceID": agent.rootDeviceId})
khenaidooca301322019-01-09 23:06:32 -05001471 }
khenaidoofdbad6e2018-11-06 22:26:38 -05001472}
1473
khenaidoo297cd252019-02-07 22:10:23 -05001474func (agent *LogicalDeviceAgent) packetIn(port uint32, transactionId string, packet []byte) {
1475 log.Debugw("packet-in", log.Fields{"port": port, "packet": packet, "transactionId": transactionId})
khenaidoo68c930b2019-05-13 11:46:51 -04001476 packetIn := fu.MkPacketIn(port, packet)
khenaidoo297cd252019-02-07 22:10:23 -05001477 agent.ldeviceMgr.grpcNbiHdlr.sendPacketIn(agent.logicalDeviceId, transactionId, packetIn)
khenaidooca301322019-01-09 23:06:32 -05001478 log.Debugw("sending-packet-in", log.Fields{"packet-in": packetIn})
khenaidoofdbad6e2018-11-06 22:26:38 -05001479}
khenaidoo2c6a0992019-04-29 13:46:56 -04001480
1481func (agent *LogicalDeviceAgent) addLogicalPortToMap(portNo uint32, nniPort bool) {
1482 agent.lockLogicalPortsNo.Lock()
1483 defer agent.lockLogicalPortsNo.Unlock()
1484 if exist := agent.logicalPortsNo[portNo]; !exist {
1485 agent.logicalPortsNo[portNo] = nniPort
1486 }
1487}
1488
khenaidoo3d3b8c22019-05-22 18:10:39 -04001489func (agent *LogicalDeviceAgent) addLogicalPortsToMap(lps []*voltha.LogicalPort) {
1490 agent.lockLogicalPortsNo.Lock()
1491 defer agent.lockLogicalPortsNo.Unlock()
1492 for _, lp := range lps {
1493 if exist := agent.logicalPortsNo[lp.DevicePortNo]; !exist {
1494 agent.logicalPortsNo[lp.DevicePortNo] = lp.RootPort
1495 }
1496 }
1497}
1498
khenaidoo2c6a0992019-04-29 13:46:56 -04001499func (agent *LogicalDeviceAgent) deleteLogicalPortFromMap(portNo uint32) {
1500 agent.lockLogicalPortsNo.Lock()
1501 defer agent.lockLogicalPortsNo.Unlock()
1502 if exist := agent.logicalPortsNo[portNo]; exist {
1503 delete(agent.logicalPortsNo, portNo)
1504 }
1505}
1506
1507func (agent *LogicalDeviceAgent) isNNIPort(portNo uint32) bool {
1508 agent.lockLogicalPortsNo.RLock()
1509 defer agent.lockLogicalPortsNo.RUnlock()
1510 if exist := agent.logicalPortsNo[portNo]; exist {
1511 return agent.logicalPortsNo[portNo]
1512 }
1513 return false
1514}
1515
1516func (agent *LogicalDeviceAgent) getFirstNNIPort() (uint32, error) {
1517 agent.lockLogicalPortsNo.RLock()
1518 defer agent.lockLogicalPortsNo.RUnlock()
1519 for portNo, nni := range agent.logicalPortsNo {
1520 if nni {
1521 return portNo, nil
1522 }
1523 }
1524 return 0, status.Error(codes.NotFound, "No NNI port found")
1525}