blob: 7df2ec7a4cf771bc59c642b37db3a28cc6161753 [file] [log] [blame]
khenaidoo820197c2020-02-13 16:35:33 -05001/*
Joey Armstrong7a9af442024-01-03 19:26:36 -05002 * Copyright 2020-2024 Open Networking Foundation (ONF) and the ONF Contributors
khenaidoo820197c2020-02-13 16:35:33 -05003 *
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
khenaidood948f772021-08-11 17:49:24 -040025 "github.com/opencord/voltha-lib-go/v7/pkg/log"
26 "github.com/opencord/voltha-protos/v5/go/voltha"
khenaidoo820197c2020-02-13 16:35:33 -050027 "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
Kent Hagerman2a07b862020-06-19 15:23:07 -040051// listDevicePortsFunc returns device ports
52type listDevicePortsFunc func(ctx context.Context, id string) (map[uint32]*voltha.Port, error)
khenaidoo820197c2020-02-13 16:35:33 -050053
54// DeviceRoutes represent the set of routes between logical ports of a logical device
55type DeviceRoutes struct {
Kent Hagerman2a07b862020-06-19 15:23:07 -040056 listDevicePorts listDevicePortsFunc
khenaidoo0db4c812020-05-27 15:27:30 -040057 logicalPorts map[uint32]*voltha.LogicalPort
khenaidoo820197c2020-02-13 16:35:33 -050058 RootPorts map[uint32]uint32
khenaidoo820197c2020-02-13 16:35:33 -050059 Routes map[PathID][]Hop
khenaidoo820197c2020-02-13 16:35:33 -050060 devicesPonPorts map[string][]*voltha.Port
khenaidoo0db4c812020-05-27 15:27:30 -040061 childConnectionPort map[string]uint32
Akash Reddy Kankanala929cc002025-04-08 15:05:21 +053062 logicalDeviceID string
63 rootDeviceID string
64 rootPortsLock sync.RWMutex
65 routeBuildLock sync.RWMutex
khenaidoo820197c2020-02-13 16:35:33 -050066}
67
68// NewDeviceRoutes creates device graph instance
Kent Hagerman2a07b862020-06-19 15:23:07 -040069func NewDeviceRoutes(logicalDeviceID, rootDeviceID string, deviceMgr listDevicePortsFunc) *DeviceRoutes {
70 return &DeviceRoutes{
71 logicalDeviceID: logicalDeviceID,
72 rootDeviceID: rootDeviceID,
73 listDevicePorts: deviceMgr,
74 RootPorts: make(map[uint32]uint32),
75 Routes: make(map[PathID][]Hop),
76 devicesPonPorts: make(map[string][]*voltha.Port),
77 childConnectionPort: make(map[string]uint32),
78 logicalPorts: make(map[uint32]*voltha.LogicalPort),
79 }
khenaidoo820197c2020-02-13 16:35:33 -050080}
81
Joey Armstrong393daca2023-07-06 08:47:54 -040082// IsRootPort returns true if the port is a root port on a logical device
khenaidoo820197c2020-02-13 16:35:33 -050083func (dr *DeviceRoutes) IsRootPort(port uint32) bool {
84 dr.rootPortsLock.RLock()
85 defer dr.rootPortsLock.RUnlock()
86 _, exist := dr.RootPorts[port]
87 return exist
88}
89
khenaidoo0db4c812020-05-27 15:27:30 -040090func (dr *DeviceRoutes) GetRoute(ctx context.Context, ingress, egress uint32) ([]Hop, error) {
91 dr.routeBuildLock.Lock()
92 defer dr.routeBuildLock.Unlock()
93
94 if route, exist := dr.Routes[PathID{Ingress: ingress, Egress: egress}]; exist {
95 return route, nil
96 }
97
98 uniPort, nniPort, err := dr.getLogicalPorts(ingress, egress)
99 if err != nil {
Andrea Campanella832cff62021-11-05 17:05:18 +0100100 return nil, fmt.Errorf("no route from:%d to:%d for %s %w", ingress, egress, err.Error(), ErrNoRoute)
khenaidoo0db4c812020-05-27 15:27:30 -0400101 }
102
103 childPonPort, err := dr.getChildPonPort(ctx, uniPort.DeviceId)
104 if err != nil {
105 return nil, err
106 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400107 rootDevicePonPort, err := dr.getParentPonPort(ctx, uniPort.DeviceId)
khenaidoo0db4c812020-05-27 15:27:30 -0400108 if err != nil {
109 return nil, err
110 }
111
112 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: uniPort.DevicePortNo}] = []Hop{
113 {DeviceID: nniPort.DeviceId, Ingress: nniPort.DevicePortNo, Egress: rootDevicePonPort},
114 {DeviceID: uniPort.DeviceId, Ingress: childPonPort, Egress: uniPort.DevicePortNo},
115 }
116 dr.Routes[PathID{Ingress: uniPort.DevicePortNo, Egress: nniPort.OfpPort.PortNo}] = getReverseRoute(
117 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: uniPort.DevicePortNo}])
118
119 return dr.Routes[PathID{Ingress: ingress, Egress: egress}], nil
120
121}
122
khenaidood948f772021-08-11 17:49:24 -0400123func (dr *DeviceRoutes) RemoveRoutes() {
124 dr.reset()
125}
126
Joey Armstrong393daca2023-07-06 08:47:54 -0400127// ComputeRoutes calculates all the routes between the logical ports. This will clear up any existing route
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400128func (dr *DeviceRoutes) ComputeRoutes(ctx context.Context, lps map[uint32]*voltha.LogicalPort) error {
khenaidoo820197c2020-02-13 16:35:33 -0500129 dr.routeBuildLock.Lock()
130 defer dr.routeBuildLock.Unlock()
131
Rohan Agrawal31f21802020-06-12 05:38:46 +0000132 logger.Debugw(ctx, "computing-all-routes", log.Fields{"len-logical-ports": len(lps)})
khenaidoo820197c2020-02-13 16:35:33 -0500133 var err error
134 defer func() {
135 // On error, clear the routes - any flow request or a port add/delete will trigger the rebuild
136 if err != nil {
137 dr.reset()
138 }
139 }()
140
141 if len(lps) < 2 {
khenaidoo787224a2020-04-16 18:08:47 -0400142 return fmt.Errorf("not enough logical port :%w", ErrNoRoute)
khenaidoo820197c2020-02-13 16:35:33 -0500143 }
144
145 dr.reset()
khenaidoo820197c2020-02-13 16:35:33 -0500146
147 // Setup the physical ports to logical ports map, the nni ports as well as the root ports map
148 physPortToLogicalPortMap := make(map[string]uint32)
149 nniPorts := make([]*voltha.LogicalPort, 0)
150 for _, lp := range lps {
151 physPortToLogicalPortMap[concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)] = lp.OfpPort.PortNo
152 if lp.RootPort {
153 nniPorts = append(nniPorts, lp)
154 dr.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
155 }
khenaidoo0db4c812020-05-27 15:27:30 -0400156 dr.logicalPorts[lp.OfpPort.PortNo] = lp
khenaidoo820197c2020-02-13 16:35:33 -0500157 }
khenaidoo0db4c812020-05-27 15:27:30 -0400158
khenaidoo820197c2020-02-13 16:35:33 -0500159 if len(nniPorts) == 0 {
khenaidoo787224a2020-04-16 18:08:47 -0400160 return fmt.Errorf("no nni port :%w", ErrNoRoute)
khenaidoo820197c2020-02-13 16:35:33 -0500161 }
khenaidoo820197c2020-02-13 16:35:33 -0500162 var copyFromNNIPort *voltha.LogicalPort
163 for idx, nniPort := range nniPorts {
164 if idx == 0 {
165 copyFromNNIPort = nniPort
166 } else if len(dr.Routes) > 0 {
167 dr.copyFromExistingNNIRoutes(nniPort, copyFromNNIPort)
168 return nil
169 }
170 // Get root device
Kent Hagerman2a07b862020-06-19 15:23:07 -0400171 rootDeviceID := nniPort.DeviceId
172 rootDevicePorts, err := dr.getDeviceWithCacheUpdate(ctx, nniPort.DeviceId)
khenaidoo820197c2020-02-13 16:35:33 -0500173 if err != nil {
174 return err
175 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400176 if len(rootDevicePorts) == 0 {
177 err = status.Errorf(codes.FailedPrecondition, "no-port-%s", rootDeviceID)
khenaidoo820197c2020-02-13 16:35:33 -0500178 return err
179 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400180 for _, rootDevicePort := range rootDevicePorts {
khenaidoo820197c2020-02-13 16:35:33 -0500181 if rootDevicePort.Type == voltha.Port_PON_OLT {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400182 logger.Debugw(ctx, "peers", log.Fields{"root-device-id": rootDeviceID, "port-no": rootDevicePort.PortNo, "len-peers": len(rootDevicePort.Peers)})
khenaidoo820197c2020-02-13 16:35:33 -0500183 for _, rootDevicePeer := range rootDevicePort.Peers {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400184 childDeviceID := rootDevicePeer.DeviceId
185 childDevicePorts, err := dr.getDeviceWithCacheUpdate(ctx, rootDevicePeer.DeviceId)
khenaidoo820197c2020-02-13 16:35:33 -0500186 if err != nil {
balaji.nagarajanf1cea712026-02-10 17:59:31 +0530187 logger.Warnw(ctx, "child-device-ports-not-found", log.Fields{"device-id": rootDevicePeer.DeviceId, "rootDevicePort": rootDevicePort.PortNo, "rootPortPeer": rootDevicePeer.PortNo})
188 continue
khenaidoo820197c2020-02-13 16:35:33 -0500189 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400190 childPonPort, err := dr.getChildPonPort(ctx, childDeviceID)
khenaidoo0db4c812020-05-27 15:27:30 -0400191 if err != nil {
balaji.nagarajanf1cea712026-02-10 17:59:31 +0530192 logger.Warnw(ctx, "child-pon-port-not-found", log.Fields{"device-id": childDeviceID, "rootDevicePort": rootDevicePort.PortNo, "rootPortPeer": rootDevicePeer.PortNo})
193 continue
khenaidoo820197c2020-02-13 16:35:33 -0500194 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400195 for _, childDevicePort := range childDevicePorts {
khenaidoo820197c2020-02-13 16:35:33 -0500196 if childDevicePort.Type == voltha.Port_ETHERNET_UNI {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400197 childLogicalPort, exist := physPortToLogicalPortMap[concatDeviceIDPortID(childDeviceID, childDevicePort.PortNo)]
khenaidoo820197c2020-02-13 16:35:33 -0500198 if !exist {
199 // This can happen if this logical port has not been created yet for that device
200 continue
201 }
202 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: childLogicalPort}] = []Hop{
Kent Hagerman2a07b862020-06-19 15:23:07 -0400203 {DeviceID: rootDeviceID, Ingress: nniPort.DevicePortNo, Egress: rootDevicePort.PortNo},
204 {DeviceID: childDeviceID, Ingress: childPonPort, Egress: childDevicePort.PortNo},
khenaidoo820197c2020-02-13 16:35:33 -0500205 }
206 dr.Routes[PathID{Ingress: childLogicalPort, Egress: nniPort.OfpPort.PortNo}] = getReverseRoute(
207 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: childLogicalPort}])
208 }
209 }
210 }
211 }
212 }
213 }
214 return nil
215}
216
khenaidoo0db4c812020-05-27 15:27:30 -0400217// AddPort augments the current set of routes with new routes corresponding to the logical port "lp". If the routes have
218// not been built yet then use logical port "lps" to compute all current routes (lps includes lp)
Kent Hagerman2a07b862020-06-19 15:23:07 -0400219func (dr *DeviceRoutes) AddPort(ctx context.Context, lp *voltha.LogicalPort, deviceID string, devicePorts map[uint32]*voltha.Port, lps map[uint32]*voltha.LogicalPort) error {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000220 logger.Debugw(ctx, "add-port-to-routes", log.Fields{"port": lp, "count-logical-ports": len(lps)})
khenaidoo0db4c812020-05-27 15:27:30 -0400221
222 // Adding NNI port
223 if lp.RootPort {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400224 return dr.AddNNIPort(ctx, lp, deviceID, devicePorts, lps)
khenaidoo820197c2020-02-13 16:35:33 -0500225 }
226
khenaidoo0db4c812020-05-27 15:27:30 -0400227 // Adding UNI port
Kent Hagerman2a07b862020-06-19 15:23:07 -0400228 return dr.AddUNIPort(ctx, lp, deviceID, devicePorts, lps)
khenaidoo0db4c812020-05-27 15:27:30 -0400229}
230
231// AddUNIPort setup routes between the logical UNI port lp and all registered NNI ports
Kent Hagerman2a07b862020-06-19 15:23:07 -0400232func (dr *DeviceRoutes) AddUNIPort(ctx context.Context, lp *voltha.LogicalPort, deviceID string, devicePorts map[uint32]*voltha.Port, lps map[uint32]*voltha.LogicalPort) error {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000233 logger.Debugw(ctx, "add-uni-port-to-routes", log.Fields{"port": lp, "count-logical-ports": len(lps)})
khenaidoo0db4c812020-05-27 15:27:30 -0400234
235 dr.routeBuildLock.Lock()
236 defer dr.routeBuildLock.Unlock()
237
238 // Add port to logical ports
239 dr.logicalPorts[lp.OfpPort.PortNo] = lp
240
241 // Update internal structures with device data
Kent Hagerman2a07b862020-06-19 15:23:07 -0400242 dr.updateCache(deviceID, devicePorts)
khenaidoo0db4c812020-05-27 15:27:30 -0400243
244 // Adding a UNI port
245 childPonPort, err := dr.getChildPonPort(ctx, lp.DeviceId)
246 if err != nil {
247 return err
248 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400249 rootDevicePonPort, err := dr.getParentPonPort(ctx, deviceID)
khenaidoo0db4c812020-05-27 15:27:30 -0400250 if err != nil {
251 return err
252 }
253
254 // Adding a UNI port
255 for _, lPort := range lps {
256 if lPort.RootPort {
257 dr.Routes[PathID{Ingress: lPort.OfpPort.PortNo, Egress: lp.OfpPort.PortNo}] = []Hop{
258 {DeviceID: lPort.DeviceId, Ingress: lPort.DevicePortNo, Egress: rootDevicePonPort},
259 {DeviceID: lp.DeviceId, Ingress: childPonPort, Egress: lp.DevicePortNo},
260 }
261 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: lPort.OfpPort.PortNo}] = getReverseRoute(
262 dr.Routes[PathID{Ingress: lPort.OfpPort.PortNo, Egress: lp.OfpPort.PortNo}])
263 }
khenaidoo820197c2020-02-13 16:35:33 -0500264 }
265 return nil
266}
267
khenaidoo0db4c812020-05-27 15:27:30 -0400268// AddNNIPort setup routes between the logical NNI port lp and all registered UNI ports
Kent Hagerman2a07b862020-06-19 15:23:07 -0400269func (dr *DeviceRoutes) AddNNIPort(ctx context.Context, lp *voltha.LogicalPort, deviceID string, devicePorts map[uint32]*voltha.Port, lps map[uint32]*voltha.LogicalPort) error {
270 logger.Debugw(ctx, "add-port-to-routes", log.Fields{"port": lp, "logical-ports-count": len(lps), "device-id": deviceID})
khenaidoo820197c2020-02-13 16:35:33 -0500271
272 dr.routeBuildLock.Lock()
khenaidoo820197c2020-02-13 16:35:33 -0500273 defer dr.routeBuildLock.Unlock()
khenaidoo0db4c812020-05-27 15:27:30 -0400274
275 // Update internal structures with device data
Kent Hagerman2a07b862020-06-19 15:23:07 -0400276 dr.updateCache(deviceID, devicePorts)
khenaidoo0db4c812020-05-27 15:27:30 -0400277
278 // Setup the physical ports to logical ports map, the nni ports as well as the root ports map
279 physPortToLogicalPortMap := make(map[string]uint32)
280 for _, lp := range lps {
281 physPortToLogicalPortMap[concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)] = lp.OfpPort.PortNo
282 if lp.RootPort {
283 dr.rootPortsLock.Lock()
284 dr.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
285 dr.rootPortsLock.Unlock()
286 }
287 dr.logicalPorts[lp.OfpPort.PortNo] = lp
khenaidoo820197c2020-02-13 16:35:33 -0500288 }
289
Kent Hagerman2a07b862020-06-19 15:23:07 -0400290 for _, rootDevicePort := range devicePorts {
khenaidoo0db4c812020-05-27 15:27:30 -0400291 if rootDevicePort.Type == voltha.Port_PON_OLT {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400292 logger.Debugw(ctx, "peers", log.Fields{"root-device-id": deviceID, "port-no": rootDevicePort.PortNo, "len-peers": len(rootDevicePort.Peers)})
khenaidoo0db4c812020-05-27 15:27:30 -0400293 for _, rootDevicePeer := range rootDevicePort.Peers {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400294 childDeviceID := rootDevicePeer.DeviceId
295 childDevicePorts, err := dr.getDeviceWithCacheUpdate(ctx, rootDevicePeer.DeviceId)
khenaidoo0db4c812020-05-27 15:27:30 -0400296 if err != nil {
297 continue
298 }
299
Kent Hagerman2a07b862020-06-19 15:23:07 -0400300 childPonPort, err := dr.getChildPonPort(ctx, childDeviceID)
khenaidoo0db4c812020-05-27 15:27:30 -0400301 if err != nil {
302 continue
303 }
304
Kent Hagerman2a07b862020-06-19 15:23:07 -0400305 for _, childDevicePort := range childDevicePorts {
306 childLogicalPort, exist := physPortToLogicalPortMap[concatDeviceIDPortID(childDeviceID, childDevicePort.PortNo)]
khenaidoo0db4c812020-05-27 15:27:30 -0400307 if !exist {
308 // This can happen if this logical port has not been created yet for that device
309 continue
310 }
311
312 if childDevicePort.Type == voltha.Port_ETHERNET_UNI {
313 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: childLogicalPort}] = []Hop{
Kent Hagerman2a07b862020-06-19 15:23:07 -0400314 {DeviceID: deviceID, Ingress: lp.DevicePortNo, Egress: rootDevicePort.PortNo},
315 {DeviceID: childDeviceID, Ingress: childPonPort, Egress: childDevicePort.PortNo},
khenaidoo0db4c812020-05-27 15:27:30 -0400316 }
317 dr.Routes[PathID{Ingress: childLogicalPort, Egress: lp.OfpPort.PortNo}] = getReverseRoute(
318 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: childLogicalPort}])
319 }
320 }
khenaidoo820197c2020-02-13 16:35:33 -0500321 }
322 }
323 }
khenaidoo0db4c812020-05-27 15:27:30 -0400324 return nil
325}
khenaidoo820197c2020-02-13 16:35:33 -0500326
khenaidoo0db4c812020-05-27 15:27:30 -0400327// AddAllPorts setups up new routes using all ports on the device. lps includes the device's logical port
Kent Hagerman2a07b862020-06-19 15:23:07 -0400328func (dr *DeviceRoutes) AddAllPorts(ctx context.Context, deviceID string, devicePorts map[uint32]*voltha.Port, lps map[uint32]*voltha.LogicalPort) error {
329 logger.Debugw(ctx, "add-all-port-to-routes", log.Fields{"logical-ports-count": len(lps), "device-id": deviceID})
khenaidoo0db4c812020-05-27 15:27:30 -0400330 for _, lp := range lps {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400331 if lp.DeviceId == deviceID {
332 if err := dr.AddPort(ctx, lp, deviceID, devicePorts, lps); err != nil {
khenaidoo820197c2020-02-13 16:35:33 -0500333 return err
334 }
khenaidoo820197c2020-02-13 16:35:33 -0500335 }
khenaidoo820197c2020-02-13 16:35:33 -0500336 }
337 return nil
338}
339
340// Print prints routes
Rohan Agrawal31f21802020-06-12 05:38:46 +0000341func (dr *DeviceRoutes) Print(ctx context.Context) error {
khenaidoo292ab522020-06-05 18:17:59 -0400342 dr.routeBuildLock.RLock()
343 defer dr.routeBuildLock.RUnlock()
Rohan Agrawal31f21802020-06-12 05:38:46 +0000344 logger.Debugw(ctx, "Print", log.Fields{"logical-device-id": dr.logicalDeviceID, "logical-ports": dr.logicalPorts})
Girish Kumarf56a4682020-03-20 20:07:46 +0000345 if logger.V(log.DebugLevel) {
khenaidoo820197c2020-02-13 16:35:33 -0500346 output := ""
347 routeNumber := 1
348 for k, v := range dr.Routes {
349 key := fmt.Sprintf("LP:%d->LP:%d", k.Ingress, k.Egress)
350 val := ""
351 for _, i := range v {
352 val += fmt.Sprintf("{%d->%s->%d},", i.Ingress, i.DeviceID, i.Egress)
353 }
354 val = val[:len(val)-1]
355 output += fmt.Sprintf("%d:{%s=>%s} ", routeNumber, key, fmt.Sprintf("[%s]", val))
356 routeNumber++
357 }
358 if len(dr.Routes) == 0 {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000359 logger.Debugw(ctx, "no-routes-found", log.Fields{"logical-device-id": dr.logicalDeviceID})
khenaidoo820197c2020-02-13 16:35:33 -0500360 } else {
divyadesaicb8b59d2020-08-18 09:55:47 +0000361 logger.Debugw(ctx, "graph_routes", log.Fields{"logical-device-id": dr.logicalDeviceID, "Routes": output})
khenaidoo820197c2020-02-13 16:35:33 -0500362 }
363 }
364 return nil
365}
366
khenaidoo0db4c812020-05-27 15:27:30 -0400367// isUpToDate returns true if device is up to date
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400368func (dr *DeviceRoutes) isUpToDate(ldPorts map[uint32]*voltha.LogicalPort) bool {
khenaidoo820197c2020-02-13 16:35:33 -0500369 dr.routeBuildLock.Lock()
370 defer dr.routeBuildLock.Unlock()
371 numNNI, numUNI := 0, 0
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400372 if ldPorts != nil {
373 if len(dr.logicalPorts) != len(ldPorts) {
khenaidoo820197c2020-02-13 16:35:33 -0500374 return false
375 }
376 numNNI = len(dr.RootPorts)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400377 numUNI = len(ldPorts) - numNNI
khenaidoo820197c2020-02-13 16:35:33 -0500378 }
379 return len(dr.Routes) == numNNI*numUNI*2
380}
381
khenaidoo0db4c812020-05-27 15:27:30 -0400382// IsRoutesEmpty returns true if there are no routes
383func (dr *DeviceRoutes) IsRoutesEmpty() bool {
384 dr.routeBuildLock.RLock()
385 defer dr.routeBuildLock.RUnlock()
386 return len(dr.Routes) == 0
387}
388
389// GetHalfRoute returns a half route that has only the egress hop set or the ingress hop set
390func (dr *DeviceRoutes) GetHalfRoute(nniAsEgress bool, ingress, egress uint32) ([]Hop, error) {
391 dr.routeBuildLock.RLock()
392 defer dr.routeBuildLock.RUnlock()
393 routes := make([]Hop, 0)
394 for routeLink, path := range dr.Routes {
395 // If nniAsEgress is set then the half route will only have the egress hop set where the egress port needs to be
396 // an NNI port
397 if nniAsEgress {
398 // Prioritize a specific egress NNI port if set
399 if egress != 0 && dr.IsRootPort(egress) && routeLink.Egress == egress {
400 routes = append(routes, Hop{})
401 routes = append(routes, path[1])
402 return routes, nil
403 }
404 if egress == 0 && dr.IsRootPort(routeLink.Egress) {
405 routes = append(routes, Hop{})
406 routes = append(routes, path[1])
407 return routes, nil
408 }
Andrey Pozolotin34dd63f2021-05-31 21:26:40 +0300409 } else if ingress != 0 && routeLink.Ingress == ingress {
khenaidoo0db4c812020-05-27 15:27:30 -0400410 // Here we use the first route whose ingress port matches the ingress input parameter
Andrey Pozolotin34dd63f2021-05-31 21:26:40 +0300411 routes = append(routes, path[0])
412 routes = append(routes, Hop{})
413 return routes, nil
khenaidoo820197c2020-02-13 16:35:33 -0500414 }
415 }
khenaidoo0db4c812020-05-27 15:27:30 -0400416 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 -0500417}
418
Joey Armstrong393daca2023-07-06 08:47:54 -0400419// getDeviceWithCacheUpdate returns the from the model and updates the PON ports map of that device.
Kent Hagerman2a07b862020-06-19 15:23:07 -0400420func (dr *DeviceRoutes) getDeviceWithCacheUpdate(ctx context.Context, deviceID string) (map[uint32]*voltha.Port, error) {
421 devicePorts, err := dr.listDevicePorts(ctx, deviceID)
khenaidoo820197c2020-02-13 16:35:33 -0500422 if err != nil {
khenaidoo820197c2020-02-13 16:35:33 -0500423 return nil, err
424 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400425 dr.updateCache(deviceID, devicePorts)
426 return devicePorts, nil
khenaidoo820197c2020-02-13 16:35:33 -0500427}
428
Joey Armstrong393daca2023-07-06 08:47:54 -0400429// copyFromExistingNNIRoutes copies routes from an existing set of NNI routes
khenaidoo820197c2020-02-13 16:35:33 -0500430func (dr *DeviceRoutes) copyFromExistingNNIRoutes(newNNIPort *voltha.LogicalPort, copyFromNNIPort *voltha.LogicalPort) {
431 updatedRoutes := make(map[PathID][]Hop)
432 for key, val := range dr.Routes {
433 if key.Ingress == copyFromNNIPort.OfpPort.PortNo {
434 updatedRoutes[PathID{Ingress: newNNIPort.OfpPort.PortNo, Egress: key.Egress}] = []Hop{
435 {DeviceID: newNNIPort.DeviceId, Ingress: newNNIPort.DevicePortNo, Egress: val[0].Egress},
436 val[1],
437 }
438 }
439 if key.Egress == copyFromNNIPort.OfpPort.PortNo {
440 updatedRoutes[PathID{Ingress: key.Ingress, Egress: newNNIPort.OfpPort.PortNo}] = []Hop{
441 val[0],
442 {DeviceID: newNNIPort.DeviceId, Ingress: val[1].Ingress, Egress: newNNIPort.DevicePortNo},
443 }
444 }
445 updatedRoutes[key] = val
446 }
447 dr.Routes = updatedRoutes
448}
449
450// reset cleans up the device graph
451func (dr *DeviceRoutes) reset() {
452 dr.rootPortsLock.Lock()
453 dr.RootPorts = make(map[uint32]uint32)
454 dr.rootPortsLock.Unlock()
khenaidoo820197c2020-02-13 16:35:33 -0500455 dr.Routes = make(map[PathID][]Hop)
khenaidoo0db4c812020-05-27 15:27:30 -0400456 dr.logicalPorts = make(map[uint32]*voltha.LogicalPort)
khenaidoo820197c2020-02-13 16:35:33 -0500457 dr.devicesPonPorts = make(map[string][]*voltha.Port)
khenaidoo0db4c812020-05-27 15:27:30 -0400458 dr.childConnectionPort = make(map[string]uint32)
khenaidoo820197c2020-02-13 16:35:33 -0500459}
460
Joey Armstrong393daca2023-07-06 08:47:54 -0400461// concatDeviceIdPortId formats a portid using the device id and the port number
khenaidoo820197c2020-02-13 16:35:33 -0500462func concatDeviceIDPortID(deviceID string, portNo uint32) string {
463 return fmt.Sprintf("%s:%d", deviceID, portNo)
464}
465
Joey Armstrong393daca2023-07-06 08:47:54 -0400466// getReverseRoute returns the reverse of the route
khenaidoo820197c2020-02-13 16:35:33 -0500467func getReverseRoute(route []Hop) []Hop {
468 reverse := make([]Hop, len(route))
469 for i, j := 0, len(route)-1; j >= 0; i, j = i+1, j-1 {
470 reverse[i].DeviceID, reverse[i].Ingress, reverse[i].Egress = route[j].DeviceID, route[j].Egress, route[j].Ingress
471 }
472 return reverse
473}
khenaidoo0db4c812020-05-27 15:27:30 -0400474
475// getChildPonPort returns the child PON port number either from cache or from the model. If it is from the model then
476// it updates the PON ports map of that device.
477func (dr *DeviceRoutes) getChildPonPort(ctx context.Context, deviceID string) (uint32, error) {
478 if port, exist := dr.devicesPonPorts[deviceID]; exist {
479 // Return only the first PON port of that child device
480 return port[0].PortNo, nil
481 }
482
483 // Get child device from model
484 if _, err := dr.getDeviceWithCacheUpdate(ctx, deviceID); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000485 logger.Errorw(ctx, "device-not-found", log.Fields{"device-id": deviceID, "error": err})
khenaidoo0db4c812020-05-27 15:27:30 -0400486 return 0, err
487 }
488
489 // Try again
490 if port, exist := dr.devicesPonPorts[deviceID]; exist {
491 // Return only the first PON port of that child device
492 return port[0].PortNo, nil
493 }
494
495 return 0, fmt.Errorf("pon port not found %s", deviceID)
496}
497
498// getParentPonPort returns the parent PON port of the child device
Kent Hagerman2a07b862020-06-19 15:23:07 -0400499func (dr *DeviceRoutes) getParentPonPort(ctx context.Context, childDeviceID string) (uint32, error) {
khenaidoo0db4c812020-05-27 15:27:30 -0400500 if pNo, exist := dr.childConnectionPort[childDeviceID]; exist {
501 return pNo, nil
502 }
503
504 // Get parent device from the model
Kent Hagerman2a07b862020-06-19 15:23:07 -0400505 if _, err := dr.getDeviceWithCacheUpdate(ctx, dr.rootDeviceID); err != nil {
divyadesaicb8b59d2020-08-18 09:55:47 +0000506 logger.Errorw(ctx, "device-not-found", log.Fields{"device-id": dr.rootDeviceID, "error": err})
khenaidoo0db4c812020-05-27 15:27:30 -0400507 return 0, err
508 }
509 // Try again
510 if pNo, exist := dr.childConnectionPort[childDeviceID]; exist {
511 return pNo, nil
512 }
513 return 0, fmt.Errorf("pon port associated with child device %s not found", childDeviceID)
514}
515
Kent Hagerman2a07b862020-06-19 15:23:07 -0400516func (dr *DeviceRoutes) updateCache(deviceID string, devicePorts map[uint32]*voltha.Port) {
517 for _, port := range devicePorts {
khenaidoo0db4c812020-05-27 15:27:30 -0400518 if port.Type == voltha.Port_PON_ONU || port.Type == voltha.Port_PON_OLT {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400519 dr.devicesPonPorts[deviceID] = append(dr.devicesPonPorts[deviceID], port)
khenaidoo0db4c812020-05-27 15:27:30 -0400520 for _, peer := range port.Peers {
521 if port.Type == voltha.Port_PON_ONU {
522 dr.childConnectionPort[port.DeviceId] = peer.PortNo
523 } else {
524 dr.childConnectionPort[peer.DeviceId] = port.PortNo
525 }
526 }
527 }
528 }
529}
530
531func (dr *DeviceRoutes) getLogicalPorts(ingress, egress uint32) (uniPort, nniPort *voltha.LogicalPort, err error) {
532 inPort, exist := dr.logicalPorts[ingress]
533 if !exist {
534 err = fmt.Errorf("ingress port %d not found", ingress)
535 return
536 }
537 outPort, exist := dr.logicalPorts[egress]
538 if !exist {
539 err = fmt.Errorf("egress port %d not found", egress)
540 return
541 }
542
543 if inPort.RootPort {
544 nniPort = inPort
545 uniPort = outPort
546 } else {
547 nniPort = outPort
548 uniPort = inPort
549 }
550
551 return
552}