blob: a1f2ae908ba1701faaedbda45a560d687100a68f [file] [log] [blame]
khenaidoo820197c2020-02-13 16:35:33 -05001/*
2 * Copyright 2020-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package route
18
19import (
20 "context"
khenaidoo787224a2020-04-16 18:08:47 -040021 "errors"
khenaidoo820197c2020-02-13 16:35:33 -050022 "fmt"
Kent Hagermanfa9d6d42020-05-25 11:49:40 -040023 "sync"
24
khenaidoo820197c2020-02-13 16:35:33 -050025 "github.com/opencord/voltha-lib-go/v3/pkg/log"
26 "github.com/opencord/voltha-protos/v3/go/voltha"
27 "google.golang.org/grpc/codes"
28 "google.golang.org/grpc/status"
khenaidoo820197c2020-02-13 16:35:33 -050029)
30
khenaidoo787224a2020-04-16 18:08:47 -040031var ErrNoRoute = errors.New("no route")
32
khenaidoo820197c2020-02-13 16:35:33 -050033// Hop represent a route hop
34type Hop struct {
35 DeviceID string
36 Ingress uint32
37 Egress uint32
38}
39
40// PathID is the identification of a route between two logical ports
41type PathID struct {
42 Ingress uint32
43 Egress uint32
44}
45
46type OFPortLink struct {
47 Ingress uint32
48 Egress uint32
49}
50
51// GetDeviceFunc returns device function
52type GetDeviceFunc func(ctx context.Context, id string) (*voltha.Device, error)
53
54// DeviceRoutes represent the set of routes between logical ports of a logical device
55type DeviceRoutes struct {
56 logicalDeviceID string
57 getDeviceFromModel GetDeviceFunc
khenaidoo0db4c812020-05-27 15:27:30 -040058 logicalPorts map[uint32]*voltha.LogicalPort
khenaidoo820197c2020-02-13 16:35:33 -050059 RootPorts map[uint32]uint32
60 rootPortsLock sync.RWMutex
61 Routes map[PathID][]Hop
62 routeBuildLock sync.RWMutex
63 devicesPonPorts map[string][]*voltha.Port
khenaidoo0db4c812020-05-27 15:27:30 -040064 childConnectionPort map[string]uint32
khenaidoo820197c2020-02-13 16:35:33 -050065}
66
67// NewDeviceRoutes creates device graph instance
Rohan Agrawal31f21802020-06-12 05:38:46 +000068func NewDeviceRoutes(ctx context.Context, logicalDeviceID string, getDevice GetDeviceFunc) *DeviceRoutes {
khenaidoo820197c2020-02-13 16:35:33 -050069 var dr DeviceRoutes
70 dr.logicalDeviceID = logicalDeviceID
71 dr.getDeviceFromModel = getDevice
72 dr.RootPorts = make(map[uint32]uint32)
73 dr.Routes = make(map[PathID][]Hop)
74 dr.devicesPonPorts = make(map[string][]*voltha.Port)
khenaidoo0db4c812020-05-27 15:27:30 -040075 dr.childConnectionPort = make(map[string]uint32)
76 dr.logicalPorts = make(map[uint32]*voltha.LogicalPort)
Rohan Agrawal31f21802020-06-12 05:38:46 +000077 logger.Debug(ctx, "new device routes created ...")
khenaidoo820197c2020-02-13 16:35:33 -050078 return &dr
79}
80
81//IsRootPort returns true if the port is a root port on a logical device
82func (dr *DeviceRoutes) IsRootPort(port uint32) bool {
83 dr.rootPortsLock.RLock()
84 defer dr.rootPortsLock.RUnlock()
85 _, exist := dr.RootPorts[port]
86 return exist
87}
88
khenaidoo0db4c812020-05-27 15:27:30 -040089func (dr *DeviceRoutes) GetRoute(ctx context.Context, ingress, egress uint32) ([]Hop, error) {
90 dr.routeBuildLock.Lock()
91 defer dr.routeBuildLock.Unlock()
92
93 if route, exist := dr.Routes[PathID{Ingress: ingress, Egress: egress}]; exist {
94 return route, nil
95 }
96
97 uniPort, nniPort, err := dr.getLogicalPorts(ingress, egress)
98 if err != nil {
99 return nil, fmt.Errorf("no route from:%d to:%d %w", ingress, egress, err)
100 }
101
102 childPonPort, err := dr.getChildPonPort(ctx, uniPort.DeviceId)
103 if err != nil {
104 return nil, err
105 }
106 rootDevicePonPort, err := dr.getParentPonPort(ctx, nniPort.DeviceId, uniPort.DeviceId)
107 if err != nil {
108 return nil, err
109 }
110
111 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: uniPort.DevicePortNo}] = []Hop{
112 {DeviceID: nniPort.DeviceId, Ingress: nniPort.DevicePortNo, Egress: rootDevicePonPort},
113 {DeviceID: uniPort.DeviceId, Ingress: childPonPort, Egress: uniPort.DevicePortNo},
114 }
115 dr.Routes[PathID{Ingress: uniPort.DevicePortNo, Egress: nniPort.OfpPort.PortNo}] = getReverseRoute(
116 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: uniPort.DevicePortNo}])
117
118 return dr.Routes[PathID{Ingress: ingress, Egress: egress}], nil
119
120}
121
khenaidoo820197c2020-02-13 16:35:33 -0500122//ComputeRoutes calculates all the routes between the logical ports. This will clear up any existing route
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400123func (dr *DeviceRoutes) ComputeRoutes(ctx context.Context, lps map[uint32]*voltha.LogicalPort) error {
khenaidoo820197c2020-02-13 16:35:33 -0500124 dr.routeBuildLock.Lock()
125 defer dr.routeBuildLock.Unlock()
126
Rohan Agrawal31f21802020-06-12 05:38:46 +0000127 logger.Debugw(ctx, "computing-all-routes", log.Fields{"len-logical-ports": len(lps)})
khenaidoo820197c2020-02-13 16:35:33 -0500128 var err error
129 defer func() {
130 // On error, clear the routes - any flow request or a port add/delete will trigger the rebuild
131 if err != nil {
132 dr.reset()
133 }
134 }()
135
136 if len(lps) < 2 {
khenaidoo787224a2020-04-16 18:08:47 -0400137 return fmt.Errorf("not enough logical port :%w", ErrNoRoute)
khenaidoo820197c2020-02-13 16:35:33 -0500138 }
139
140 dr.reset()
khenaidoo820197c2020-02-13 16:35:33 -0500141
142 // Setup the physical ports to logical ports map, the nni ports as well as the root ports map
143 physPortToLogicalPortMap := make(map[string]uint32)
144 nniPorts := make([]*voltha.LogicalPort, 0)
145 for _, lp := range lps {
146 physPortToLogicalPortMap[concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)] = lp.OfpPort.PortNo
147 if lp.RootPort {
148 nniPorts = append(nniPorts, lp)
149 dr.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
150 }
khenaidoo0db4c812020-05-27 15:27:30 -0400151 dr.logicalPorts[lp.OfpPort.PortNo] = lp
khenaidoo820197c2020-02-13 16:35:33 -0500152 }
khenaidoo0db4c812020-05-27 15:27:30 -0400153
khenaidoo820197c2020-02-13 16:35:33 -0500154 if len(nniPorts) == 0 {
khenaidoo787224a2020-04-16 18:08:47 -0400155 return fmt.Errorf("no nni port :%w", ErrNoRoute)
khenaidoo820197c2020-02-13 16:35:33 -0500156 }
157 var rootDevice *voltha.Device
158 var childDevice *voltha.Device
159 var copyFromNNIPort *voltha.LogicalPort
160 for idx, nniPort := range nniPorts {
161 if idx == 0 {
162 copyFromNNIPort = nniPort
163 } else if len(dr.Routes) > 0 {
164 dr.copyFromExistingNNIRoutes(nniPort, copyFromNNIPort)
165 return nil
166 }
167 // Get root device
khenaidoo0db4c812020-05-27 15:27:30 -0400168 rootDevice, err = dr.getDeviceWithCacheUpdate(ctx, nniPort.DeviceId)
khenaidoo820197c2020-02-13 16:35:33 -0500169 if err != nil {
170 return err
171 }
172 if len(rootDevice.Ports) == 0 {
173 err = status.Errorf(codes.FailedPrecondition, "no-port-%s", rootDevice.Id)
174 return err
175 }
176 for _, rootDevicePort := range rootDevice.Ports {
177 if rootDevicePort.Type == voltha.Port_PON_OLT {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000178 logger.Debugw(ctx, "peers", log.Fields{"root-device-id": rootDevice.Id, "port-no": rootDevicePort.PortNo, "len-peers": len(rootDevicePort.Peers)})
khenaidoo820197c2020-02-13 16:35:33 -0500179 for _, rootDevicePeer := range rootDevicePort.Peers {
khenaidoo0db4c812020-05-27 15:27:30 -0400180 childDevice, err = dr.getDeviceWithCacheUpdate(ctx, rootDevicePeer.DeviceId)
khenaidoo820197c2020-02-13 16:35:33 -0500181 if err != nil {
182 return err
183 }
khenaidoo0db4c812020-05-27 15:27:30 -0400184 childPonPort, err := dr.getChildPonPort(ctx, childDevice.Id)
185 if err != nil {
khenaidoo820197c2020-02-13 16:35:33 -0500186 return err
187 }
khenaidoo820197c2020-02-13 16:35:33 -0500188 for _, childDevicePort := range childDevice.Ports {
189 if childDevicePort.Type == voltha.Port_ETHERNET_UNI {
190 childLogicalPort, exist := physPortToLogicalPortMap[concatDeviceIDPortID(childDevice.Id, childDevicePort.PortNo)]
191 if !exist {
192 // This can happen if this logical port has not been created yet for that device
193 continue
194 }
195 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: childLogicalPort}] = []Hop{
196 {DeviceID: rootDevice.Id, Ingress: nniPort.DevicePortNo, Egress: rootDevicePort.PortNo},
197 {DeviceID: childDevice.Id, Ingress: childPonPort, Egress: childDevicePort.PortNo},
198 }
199 dr.Routes[PathID{Ingress: childLogicalPort, Egress: nniPort.OfpPort.PortNo}] = getReverseRoute(
200 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: childLogicalPort}])
201 }
202 }
203 }
204 }
205 }
206 }
207 return nil
208}
209
khenaidoo0db4c812020-05-27 15:27:30 -0400210// AddPort augments the current set of routes with new routes corresponding to the logical port "lp". If the routes have
211// not been built yet then use logical port "lps" to compute all current routes (lps includes lp)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400212func (dr *DeviceRoutes) AddPort(ctx context.Context, lp *voltha.LogicalPort, device *voltha.Device, lps map[uint32]*voltha.LogicalPort) error {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000213 logger.Debugw(ctx, "add-port-to-routes", log.Fields{"port": lp, "count-logical-ports": len(lps)})
khenaidoo0db4c812020-05-27 15:27:30 -0400214
215 // Adding NNI port
216 if lp.RootPort {
217 return dr.AddNNIPort(ctx, lp, device, lps)
khenaidoo820197c2020-02-13 16:35:33 -0500218 }
219
khenaidoo0db4c812020-05-27 15:27:30 -0400220 // Adding UNI port
221 return dr.AddUNIPort(ctx, lp, device, lps)
222}
223
224// AddUNIPort setup routes between the logical UNI port lp and all registered NNI ports
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400225func (dr *DeviceRoutes) AddUNIPort(ctx context.Context, lp *voltha.LogicalPort, device *voltha.Device, lps map[uint32]*voltha.LogicalPort) error {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000226 logger.Debugw(ctx, "add-uni-port-to-routes", log.Fields{"port": lp, "count-logical-ports": len(lps)})
khenaidoo0db4c812020-05-27 15:27:30 -0400227
228 dr.routeBuildLock.Lock()
229 defer dr.routeBuildLock.Unlock()
230
231 // Add port to logical ports
232 dr.logicalPorts[lp.OfpPort.PortNo] = lp
233
234 // Update internal structures with device data
235 dr.updateCache(device)
236
237 // Adding a UNI port
238 childPonPort, err := dr.getChildPonPort(ctx, lp.DeviceId)
239 if err != nil {
240 return err
241 }
242 rootDevicePonPort, err := dr.getParentPonPort(ctx, device.ParentId, device.Id)
243 if err != nil {
244 return err
245 }
246
247 // Adding a UNI port
248 for _, lPort := range lps {
249 if lPort.RootPort {
250 dr.Routes[PathID{Ingress: lPort.OfpPort.PortNo, Egress: lp.OfpPort.PortNo}] = []Hop{
251 {DeviceID: lPort.DeviceId, Ingress: lPort.DevicePortNo, Egress: rootDevicePonPort},
252 {DeviceID: lp.DeviceId, Ingress: childPonPort, Egress: lp.DevicePortNo},
253 }
254 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: lPort.OfpPort.PortNo}] = getReverseRoute(
255 dr.Routes[PathID{Ingress: lPort.OfpPort.PortNo, Egress: lp.OfpPort.PortNo}])
256 }
khenaidoo820197c2020-02-13 16:35:33 -0500257 }
258 return nil
259}
260
khenaidoo0db4c812020-05-27 15:27:30 -0400261// AddNNIPort setup routes between the logical NNI port lp and all registered UNI ports
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400262func (dr *DeviceRoutes) AddNNIPort(ctx context.Context, lp *voltha.LogicalPort, device *voltha.Device, lps map[uint32]*voltha.LogicalPort) error {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000263 logger.Debugw(ctx, "add-port-to-routes", log.Fields{"port": lp, "logical-ports-count": len(lps), "device-id": device.Id})
khenaidoo820197c2020-02-13 16:35:33 -0500264
265 dr.routeBuildLock.Lock()
khenaidoo820197c2020-02-13 16:35:33 -0500266 defer dr.routeBuildLock.Unlock()
khenaidoo0db4c812020-05-27 15:27:30 -0400267
268 // Update internal structures with device data
269 dr.updateCache(device)
270
271 // Setup the physical ports to logical ports map, the nni ports as well as the root ports map
272 physPortToLogicalPortMap := make(map[string]uint32)
273 for _, lp := range lps {
274 physPortToLogicalPortMap[concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)] = lp.OfpPort.PortNo
275 if lp.RootPort {
276 dr.rootPortsLock.Lock()
277 dr.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
278 dr.rootPortsLock.Unlock()
279 }
280 dr.logicalPorts[lp.OfpPort.PortNo] = lp
khenaidoo820197c2020-02-13 16:35:33 -0500281 }
282
khenaidoo0db4c812020-05-27 15:27:30 -0400283 for _, rootDevicePort := range device.Ports {
284 if rootDevicePort.Type == voltha.Port_PON_OLT {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000285 logger.Debugw(ctx, "peers", log.Fields{"root-device-id": device.Id, "port-no": rootDevicePort.PortNo, "len-peers": len(rootDevicePort.Peers)})
khenaidoo0db4c812020-05-27 15:27:30 -0400286 for _, rootDevicePeer := range rootDevicePort.Peers {
287 childDevice, err := dr.getDeviceWithCacheUpdate(ctx, rootDevicePeer.DeviceId)
288 if err != nil {
289 continue
290 }
291
292 childPonPort, err := dr.getChildPonPort(ctx, childDevice.Id)
293 if err != nil {
294 continue
295 }
296
297 for _, childDevicePort := range childDevice.Ports {
298 childLogicalPort, exist := physPortToLogicalPortMap[concatDeviceIDPortID(childDevice.Id, childDevicePort.PortNo)]
299 if !exist {
300 // This can happen if this logical port has not been created yet for that device
301 continue
302 }
303
304 if childDevicePort.Type == voltha.Port_ETHERNET_UNI {
305 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: childLogicalPort}] = []Hop{
306 {DeviceID: device.Id, Ingress: lp.DevicePortNo, Egress: rootDevicePort.PortNo},
307 {DeviceID: childDevice.Id, Ingress: childPonPort, Egress: childDevicePort.PortNo},
308 }
309 dr.Routes[PathID{Ingress: childLogicalPort, Egress: lp.OfpPort.PortNo}] = getReverseRoute(
310 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: childLogicalPort}])
311 }
312 }
khenaidoo820197c2020-02-13 16:35:33 -0500313 }
314 }
315 }
khenaidoo0db4c812020-05-27 15:27:30 -0400316 return nil
317}
khenaidoo820197c2020-02-13 16:35:33 -0500318
khenaidoo0db4c812020-05-27 15:27:30 -0400319// AddAllPorts setups up new routes using all ports on the device. lps includes the device's logical port
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400320func (dr *DeviceRoutes) AddAllPorts(ctx context.Context, device *voltha.Device, lps map[uint32]*voltha.LogicalPort) error {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000321 logger.Debugw(ctx, "add-all-port-to-routes", log.Fields{"logical-ports-count": len(lps), "device-id": device.Id})
khenaidoo0db4c812020-05-27 15:27:30 -0400322 for _, lp := range lps {
323 if lp.DeviceId == device.Id {
324 if err := dr.AddPort(ctx, lp, device, lps); err != nil {
khenaidoo820197c2020-02-13 16:35:33 -0500325 return err
326 }
khenaidoo820197c2020-02-13 16:35:33 -0500327 }
khenaidoo820197c2020-02-13 16:35:33 -0500328 }
329 return nil
330}
331
332// Print prints routes
Rohan Agrawal31f21802020-06-12 05:38:46 +0000333func (dr *DeviceRoutes) Print(ctx context.Context) error {
khenaidoo292ab522020-06-05 18:17:59 -0400334 dr.routeBuildLock.RLock()
335 defer dr.routeBuildLock.RUnlock()
Rohan Agrawal31f21802020-06-12 05:38:46 +0000336 logger.Debugw(ctx, "Print", log.Fields{"logical-device-id": dr.logicalDeviceID, "logical-ports": dr.logicalPorts})
Girish Kumarf56a4682020-03-20 20:07:46 +0000337 if logger.V(log.DebugLevel) {
khenaidoo820197c2020-02-13 16:35:33 -0500338 output := ""
339 routeNumber := 1
340 for k, v := range dr.Routes {
341 key := fmt.Sprintf("LP:%d->LP:%d", k.Ingress, k.Egress)
342 val := ""
343 for _, i := range v {
344 val += fmt.Sprintf("{%d->%s->%d},", i.Ingress, i.DeviceID, i.Egress)
345 }
346 val = val[:len(val)-1]
347 output += fmt.Sprintf("%d:{%s=>%s} ", routeNumber, key, fmt.Sprintf("[%s]", val))
348 routeNumber++
349 }
350 if len(dr.Routes) == 0 {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000351 logger.Debugw(ctx, "no-routes-found", log.Fields{"logical-device-id": dr.logicalDeviceID})
khenaidoo820197c2020-02-13 16:35:33 -0500352 } else {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000353 logger.Debugw(ctx, "graph_routes", log.Fields{"lDeviceId": dr.logicalDeviceID, "Routes": output})
khenaidoo820197c2020-02-13 16:35:33 -0500354 }
355 }
356 return nil
357}
358
khenaidoo0db4c812020-05-27 15:27:30 -0400359// isUpToDate returns true if device is up to date
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400360func (dr *DeviceRoutes) isUpToDate(ldPorts map[uint32]*voltha.LogicalPort) bool {
khenaidoo820197c2020-02-13 16:35:33 -0500361 dr.routeBuildLock.Lock()
362 defer dr.routeBuildLock.Unlock()
363 numNNI, numUNI := 0, 0
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400364 if ldPorts != nil {
365 if len(dr.logicalPorts) != len(ldPorts) {
khenaidoo820197c2020-02-13 16:35:33 -0500366 return false
367 }
368 numNNI = len(dr.RootPorts)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400369 numUNI = len(ldPorts) - numNNI
khenaidoo820197c2020-02-13 16:35:33 -0500370 }
371 return len(dr.Routes) == numNNI*numUNI*2
372}
373
khenaidoo0db4c812020-05-27 15:27:30 -0400374// IsRoutesEmpty returns true if there are no routes
375func (dr *DeviceRoutes) IsRoutesEmpty() bool {
376 dr.routeBuildLock.RLock()
377 defer dr.routeBuildLock.RUnlock()
378 return len(dr.Routes) == 0
379}
380
381// GetHalfRoute returns a half route that has only the egress hop set or the ingress hop set
382func (dr *DeviceRoutes) GetHalfRoute(nniAsEgress bool, ingress, egress uint32) ([]Hop, error) {
383 dr.routeBuildLock.RLock()
384 defer dr.routeBuildLock.RUnlock()
385 routes := make([]Hop, 0)
386 for routeLink, path := range dr.Routes {
387 // If nniAsEgress is set then the half route will only have the egress hop set where the egress port needs to be
388 // an NNI port
389 if nniAsEgress {
390 // Prioritize a specific egress NNI port if set
391 if egress != 0 && dr.IsRootPort(egress) && routeLink.Egress == egress {
392 routes = append(routes, Hop{})
393 routes = append(routes, path[1])
394 return routes, nil
395 }
396 if egress == 0 && dr.IsRootPort(routeLink.Egress) {
397 routes = append(routes, Hop{})
398 routes = append(routes, path[1])
399 return routes, nil
400 }
401 } else {
402 // Here we use the first route whose ingress port matches the ingress input parameter
403 if ingress != 0 && routeLink.Ingress == ingress {
404 routes = append(routes, path[0])
405 routes = append(routes, Hop{})
406 return routes, nil
khenaidoo820197c2020-02-13 16:35:33 -0500407 }
408 }
409 }
khenaidoo0db4c812020-05-27 15:27:30 -0400410 return routes, fmt.Errorf("no half route found for ingress port %d, egress port %d and nni as egress %t", ingress, egress, nniAsEgress)
khenaidoo820197c2020-02-13 16:35:33 -0500411}
412
khenaidoo0db4c812020-05-27 15:27:30 -0400413//getDeviceWithCacheUpdate returns the from the model and updates the PON ports map of that device.
414func (dr *DeviceRoutes) getDeviceWithCacheUpdate(ctx context.Context, deviceID string) (*voltha.Device, error) {
khenaidoo820197c2020-02-13 16:35:33 -0500415 device, err := dr.getDeviceFromModel(ctx, deviceID)
416 if err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000417 logger.Errorw(ctx, "device-not-found", log.Fields{"deviceId": deviceID, "error": err})
khenaidoo820197c2020-02-13 16:35:33 -0500418 return nil, err
419 }
khenaidoo0db4c812020-05-27 15:27:30 -0400420 dr.updateCache(device)
khenaidoo820197c2020-02-13 16:35:33 -0500421 return device, nil
422}
423
424//copyFromExistingNNIRoutes copies routes from an existing set of NNI routes
425func (dr *DeviceRoutes) copyFromExistingNNIRoutes(newNNIPort *voltha.LogicalPort, copyFromNNIPort *voltha.LogicalPort) {
426 updatedRoutes := make(map[PathID][]Hop)
427 for key, val := range dr.Routes {
428 if key.Ingress == copyFromNNIPort.OfpPort.PortNo {
429 updatedRoutes[PathID{Ingress: newNNIPort.OfpPort.PortNo, Egress: key.Egress}] = []Hop{
430 {DeviceID: newNNIPort.DeviceId, Ingress: newNNIPort.DevicePortNo, Egress: val[0].Egress},
431 val[1],
432 }
433 }
434 if key.Egress == copyFromNNIPort.OfpPort.PortNo {
435 updatedRoutes[PathID{Ingress: key.Ingress, Egress: newNNIPort.OfpPort.PortNo}] = []Hop{
436 val[0],
437 {DeviceID: newNNIPort.DeviceId, Ingress: val[1].Ingress, Egress: newNNIPort.DevicePortNo},
438 }
439 }
440 updatedRoutes[key] = val
441 }
442 dr.Routes = updatedRoutes
443}
444
445// reset cleans up the device graph
446func (dr *DeviceRoutes) reset() {
447 dr.rootPortsLock.Lock()
448 dr.RootPorts = make(map[uint32]uint32)
449 dr.rootPortsLock.Unlock()
khenaidoo820197c2020-02-13 16:35:33 -0500450 dr.Routes = make(map[PathID][]Hop)
khenaidoo0db4c812020-05-27 15:27:30 -0400451 dr.logicalPorts = make(map[uint32]*voltha.LogicalPort)
khenaidoo820197c2020-02-13 16:35:33 -0500452 dr.devicesPonPorts = make(map[string][]*voltha.Port)
khenaidoo0db4c812020-05-27 15:27:30 -0400453 dr.childConnectionPort = make(map[string]uint32)
khenaidoo820197c2020-02-13 16:35:33 -0500454}
455
456//concatDeviceIdPortId formats a portid using the device id and the port number
457func concatDeviceIDPortID(deviceID string, portNo uint32) string {
458 return fmt.Sprintf("%s:%d", deviceID, portNo)
459}
460
461//getReverseRoute returns the reverse of the route
462func getReverseRoute(route []Hop) []Hop {
463 reverse := make([]Hop, len(route))
464 for i, j := 0, len(route)-1; j >= 0; i, j = i+1, j-1 {
465 reverse[i].DeviceID, reverse[i].Ingress, reverse[i].Egress = route[j].DeviceID, route[j].Egress, route[j].Ingress
466 }
467 return reverse
468}
khenaidoo0db4c812020-05-27 15:27:30 -0400469
470// getChildPonPort returns the child PON port number either from cache or from the model. If it is from the model then
471// it updates the PON ports map of that device.
472func (dr *DeviceRoutes) getChildPonPort(ctx context.Context, deviceID string) (uint32, error) {
473 if port, exist := dr.devicesPonPorts[deviceID]; exist {
474 // Return only the first PON port of that child device
475 return port[0].PortNo, nil
476 }
477
478 // Get child device from model
479 if _, err := dr.getDeviceWithCacheUpdate(ctx, deviceID); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000480 logger.Errorw(ctx, "device-not-found", log.Fields{"device-id": deviceID, "error": err})
khenaidoo0db4c812020-05-27 15:27:30 -0400481 return 0, err
482 }
483
484 // Try again
485 if port, exist := dr.devicesPonPorts[deviceID]; exist {
486 // Return only the first PON port of that child device
487 return port[0].PortNo, nil
488 }
489
490 return 0, fmt.Errorf("pon port not found %s", deviceID)
491}
492
493// getParentPonPort returns the parent PON port of the child device
494func (dr *DeviceRoutes) getParentPonPort(ctx context.Context, deviceID string, childDeviceID string) (uint32, error) {
495 if pNo, exist := dr.childConnectionPort[childDeviceID]; exist {
496 return pNo, nil
497 }
498
499 // Get parent device from the model
500 if _, err := dr.getDeviceWithCacheUpdate(ctx, deviceID); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000501 logger.Errorw(ctx, "device-not-found", log.Fields{"deviceId": deviceID, "error": err})
khenaidoo0db4c812020-05-27 15:27:30 -0400502 return 0, err
503 }
504 // Try again
505 if pNo, exist := dr.childConnectionPort[childDeviceID]; exist {
506 return pNo, nil
507 }
508 return 0, fmt.Errorf("pon port associated with child device %s not found", childDeviceID)
509}
510
511func (dr *DeviceRoutes) updateCache(device *voltha.Device) {
512 for _, port := range device.Ports {
513 if port.Type == voltha.Port_PON_ONU || port.Type == voltha.Port_PON_OLT {
514 dr.devicesPonPorts[device.Id] = append(dr.devicesPonPorts[device.Id], port)
515 for _, peer := range port.Peers {
516 if port.Type == voltha.Port_PON_ONU {
517 dr.childConnectionPort[port.DeviceId] = peer.PortNo
518 } else {
519 dr.childConnectionPort[peer.DeviceId] = port.PortNo
520 }
521 }
522 }
523 }
524}
525
526func (dr *DeviceRoutes) getLogicalPorts(ingress, egress uint32) (uniPort, nniPort *voltha.LogicalPort, err error) {
527 inPort, exist := dr.logicalPorts[ingress]
528 if !exist {
529 err = fmt.Errorf("ingress port %d not found", ingress)
530 return
531 }
532 outPort, exist := dr.logicalPorts[egress]
533 if !exist {
534 err = fmt.Errorf("egress port %d not found", egress)
535 return
536 }
537
538 if inPort.RootPort {
539 nniPort = inPort
540 uniPort = outPort
541 } else {
542 nniPort = outPort
543 uniPort = inPort
544 }
545
546 return
547}