blob: 4d02a54a01052756b1384e648649c90225d12dd5 [file] [log] [blame]
khenaidoo820197c2020-02-13 16:35:33 -05001/*
Joey Armstrong5f51f2e2023-01-17 17:06:26 -05002 * Copyright 2020-2023 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 {
56 logicalDeviceID string
Kent Hagerman2a07b862020-06-19 15:23:07 -040057 rootDeviceID string
58 listDevicePorts listDevicePortsFunc
khenaidoo0db4c812020-05-27 15:27:30 -040059 logicalPorts map[uint32]*voltha.LogicalPort
khenaidoo820197c2020-02-13 16:35:33 -050060 RootPorts map[uint32]uint32
61 rootPortsLock sync.RWMutex
62 Routes map[PathID][]Hop
63 routeBuildLock sync.RWMutex
64 devicesPonPorts map[string][]*voltha.Port
khenaidoo0db4c812020-05-27 15:27:30 -040065 childConnectionPort map[string]uint32
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 {
187 return err
188 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400189 childPonPort, err := dr.getChildPonPort(ctx, childDeviceID)
khenaidoo0db4c812020-05-27 15:27:30 -0400190 if err != nil {
khenaidoo820197c2020-02-13 16:35:33 -0500191 return err
192 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400193 for _, childDevicePort := range childDevicePorts {
khenaidoo820197c2020-02-13 16:35:33 -0500194 if childDevicePort.Type == voltha.Port_ETHERNET_UNI {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400195 childLogicalPort, exist := physPortToLogicalPortMap[concatDeviceIDPortID(childDeviceID, childDevicePort.PortNo)]
khenaidoo820197c2020-02-13 16:35:33 -0500196 if !exist {
197 // This can happen if this logical port has not been created yet for that device
198 continue
199 }
200 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: childLogicalPort}] = []Hop{
Kent Hagerman2a07b862020-06-19 15:23:07 -0400201 {DeviceID: rootDeviceID, Ingress: nniPort.DevicePortNo, Egress: rootDevicePort.PortNo},
202 {DeviceID: childDeviceID, Ingress: childPonPort, Egress: childDevicePort.PortNo},
khenaidoo820197c2020-02-13 16:35:33 -0500203 }
204 dr.Routes[PathID{Ingress: childLogicalPort, Egress: nniPort.OfpPort.PortNo}] = getReverseRoute(
205 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: childLogicalPort}])
206 }
207 }
208 }
209 }
210 }
211 }
212 return nil
213}
214
khenaidoo0db4c812020-05-27 15:27:30 -0400215// AddPort augments the current set of routes with new routes corresponding to the logical port "lp". If the routes have
216// not been built yet then use logical port "lps" to compute all current routes (lps includes lp)
Kent Hagerman2a07b862020-06-19 15:23:07 -0400217func (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 +0000218 logger.Debugw(ctx, "add-port-to-routes", log.Fields{"port": lp, "count-logical-ports": len(lps)})
khenaidoo0db4c812020-05-27 15:27:30 -0400219
220 // Adding NNI port
221 if lp.RootPort {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400222 return dr.AddNNIPort(ctx, lp, deviceID, devicePorts, lps)
khenaidoo820197c2020-02-13 16:35:33 -0500223 }
224
khenaidoo0db4c812020-05-27 15:27:30 -0400225 // Adding UNI port
Kent Hagerman2a07b862020-06-19 15:23:07 -0400226 return dr.AddUNIPort(ctx, lp, deviceID, devicePorts, lps)
khenaidoo0db4c812020-05-27 15:27:30 -0400227}
228
229// AddUNIPort setup routes between the logical UNI port lp and all registered NNI ports
Kent Hagerman2a07b862020-06-19 15:23:07 -0400230func (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 +0000231 logger.Debugw(ctx, "add-uni-port-to-routes", log.Fields{"port": lp, "count-logical-ports": len(lps)})
khenaidoo0db4c812020-05-27 15:27:30 -0400232
233 dr.routeBuildLock.Lock()
234 defer dr.routeBuildLock.Unlock()
235
236 // Add port to logical ports
237 dr.logicalPorts[lp.OfpPort.PortNo] = lp
238
239 // Update internal structures with device data
Kent Hagerman2a07b862020-06-19 15:23:07 -0400240 dr.updateCache(deviceID, devicePorts)
khenaidoo0db4c812020-05-27 15:27:30 -0400241
242 // Adding a UNI port
243 childPonPort, err := dr.getChildPonPort(ctx, lp.DeviceId)
244 if err != nil {
245 return err
246 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400247 rootDevicePonPort, err := dr.getParentPonPort(ctx, deviceID)
khenaidoo0db4c812020-05-27 15:27:30 -0400248 if err != nil {
249 return err
250 }
251
252 // Adding a UNI port
253 for _, lPort := range lps {
254 if lPort.RootPort {
255 dr.Routes[PathID{Ingress: lPort.OfpPort.PortNo, Egress: lp.OfpPort.PortNo}] = []Hop{
256 {DeviceID: lPort.DeviceId, Ingress: lPort.DevicePortNo, Egress: rootDevicePonPort},
257 {DeviceID: lp.DeviceId, Ingress: childPonPort, Egress: lp.DevicePortNo},
258 }
259 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: lPort.OfpPort.PortNo}] = getReverseRoute(
260 dr.Routes[PathID{Ingress: lPort.OfpPort.PortNo, Egress: lp.OfpPort.PortNo}])
261 }
khenaidoo820197c2020-02-13 16:35:33 -0500262 }
263 return nil
264}
265
khenaidoo0db4c812020-05-27 15:27:30 -0400266// AddNNIPort setup routes between the logical NNI port lp and all registered UNI ports
Kent Hagerman2a07b862020-06-19 15:23:07 -0400267func (dr *DeviceRoutes) AddNNIPort(ctx context.Context, lp *voltha.LogicalPort, deviceID string, devicePorts map[uint32]*voltha.Port, lps map[uint32]*voltha.LogicalPort) error {
268 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 -0500269
270 dr.routeBuildLock.Lock()
khenaidoo820197c2020-02-13 16:35:33 -0500271 defer dr.routeBuildLock.Unlock()
khenaidoo0db4c812020-05-27 15:27:30 -0400272
273 // Update internal structures with device data
Kent Hagerman2a07b862020-06-19 15:23:07 -0400274 dr.updateCache(deviceID, devicePorts)
khenaidoo0db4c812020-05-27 15:27:30 -0400275
276 // Setup the physical ports to logical ports map, the nni ports as well as the root ports map
277 physPortToLogicalPortMap := make(map[string]uint32)
278 for _, lp := range lps {
279 physPortToLogicalPortMap[concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)] = lp.OfpPort.PortNo
280 if lp.RootPort {
281 dr.rootPortsLock.Lock()
282 dr.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
283 dr.rootPortsLock.Unlock()
284 }
285 dr.logicalPorts[lp.OfpPort.PortNo] = lp
khenaidoo820197c2020-02-13 16:35:33 -0500286 }
287
Kent Hagerman2a07b862020-06-19 15:23:07 -0400288 for _, rootDevicePort := range devicePorts {
khenaidoo0db4c812020-05-27 15:27:30 -0400289 if rootDevicePort.Type == voltha.Port_PON_OLT {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400290 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 -0400291 for _, rootDevicePeer := range rootDevicePort.Peers {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400292 childDeviceID := rootDevicePeer.DeviceId
293 childDevicePorts, err := dr.getDeviceWithCacheUpdate(ctx, rootDevicePeer.DeviceId)
khenaidoo0db4c812020-05-27 15:27:30 -0400294 if err != nil {
295 continue
296 }
297
Kent Hagerman2a07b862020-06-19 15:23:07 -0400298 childPonPort, err := dr.getChildPonPort(ctx, childDeviceID)
khenaidoo0db4c812020-05-27 15:27:30 -0400299 if err != nil {
300 continue
301 }
302
Kent Hagerman2a07b862020-06-19 15:23:07 -0400303 for _, childDevicePort := range childDevicePorts {
304 childLogicalPort, exist := physPortToLogicalPortMap[concatDeviceIDPortID(childDeviceID, childDevicePort.PortNo)]
khenaidoo0db4c812020-05-27 15:27:30 -0400305 if !exist {
306 // This can happen if this logical port has not been created yet for that device
307 continue
308 }
309
310 if childDevicePort.Type == voltha.Port_ETHERNET_UNI {
311 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: childLogicalPort}] = []Hop{
Kent Hagerman2a07b862020-06-19 15:23:07 -0400312 {DeviceID: deviceID, Ingress: lp.DevicePortNo, Egress: rootDevicePort.PortNo},
313 {DeviceID: childDeviceID, Ingress: childPonPort, Egress: childDevicePort.PortNo},
khenaidoo0db4c812020-05-27 15:27:30 -0400314 }
315 dr.Routes[PathID{Ingress: childLogicalPort, Egress: lp.OfpPort.PortNo}] = getReverseRoute(
316 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: childLogicalPort}])
317 }
318 }
khenaidoo820197c2020-02-13 16:35:33 -0500319 }
320 }
321 }
khenaidoo0db4c812020-05-27 15:27:30 -0400322 return nil
323}
khenaidoo820197c2020-02-13 16:35:33 -0500324
khenaidoo0db4c812020-05-27 15:27:30 -0400325// 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 -0400326func (dr *DeviceRoutes) AddAllPorts(ctx context.Context, deviceID string, devicePorts map[uint32]*voltha.Port, lps map[uint32]*voltha.LogicalPort) error {
327 logger.Debugw(ctx, "add-all-port-to-routes", log.Fields{"logical-ports-count": len(lps), "device-id": deviceID})
khenaidoo0db4c812020-05-27 15:27:30 -0400328 for _, lp := range lps {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400329 if lp.DeviceId == deviceID {
330 if err := dr.AddPort(ctx, lp, deviceID, devicePorts, lps); err != nil {
khenaidoo820197c2020-02-13 16:35:33 -0500331 return err
332 }
khenaidoo820197c2020-02-13 16:35:33 -0500333 }
khenaidoo820197c2020-02-13 16:35:33 -0500334 }
335 return nil
336}
337
338// Print prints routes
Rohan Agrawal31f21802020-06-12 05:38:46 +0000339func (dr *DeviceRoutes) Print(ctx context.Context) error {
khenaidoo292ab522020-06-05 18:17:59 -0400340 dr.routeBuildLock.RLock()
341 defer dr.routeBuildLock.RUnlock()
Rohan Agrawal31f21802020-06-12 05:38:46 +0000342 logger.Debugw(ctx, "Print", log.Fields{"logical-device-id": dr.logicalDeviceID, "logical-ports": dr.logicalPorts})
Girish Kumarf56a4682020-03-20 20:07:46 +0000343 if logger.V(log.DebugLevel) {
khenaidoo820197c2020-02-13 16:35:33 -0500344 output := ""
345 routeNumber := 1
346 for k, v := range dr.Routes {
347 key := fmt.Sprintf("LP:%d->LP:%d", k.Ingress, k.Egress)
348 val := ""
349 for _, i := range v {
350 val += fmt.Sprintf("{%d->%s->%d},", i.Ingress, i.DeviceID, i.Egress)
351 }
352 val = val[:len(val)-1]
353 output += fmt.Sprintf("%d:{%s=>%s} ", routeNumber, key, fmt.Sprintf("[%s]", val))
354 routeNumber++
355 }
356 if len(dr.Routes) == 0 {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000357 logger.Debugw(ctx, "no-routes-found", log.Fields{"logical-device-id": dr.logicalDeviceID})
khenaidoo820197c2020-02-13 16:35:33 -0500358 } else {
divyadesaicb8b59d2020-08-18 09:55:47 +0000359 logger.Debugw(ctx, "graph_routes", log.Fields{"logical-device-id": dr.logicalDeviceID, "Routes": output})
khenaidoo820197c2020-02-13 16:35:33 -0500360 }
361 }
362 return nil
363}
364
khenaidoo0db4c812020-05-27 15:27:30 -0400365// isUpToDate returns true if device is up to date
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400366func (dr *DeviceRoutes) isUpToDate(ldPorts map[uint32]*voltha.LogicalPort) bool {
khenaidoo820197c2020-02-13 16:35:33 -0500367 dr.routeBuildLock.Lock()
368 defer dr.routeBuildLock.Unlock()
369 numNNI, numUNI := 0, 0
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400370 if ldPorts != nil {
371 if len(dr.logicalPorts) != len(ldPorts) {
khenaidoo820197c2020-02-13 16:35:33 -0500372 return false
373 }
374 numNNI = len(dr.RootPorts)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400375 numUNI = len(ldPorts) - numNNI
khenaidoo820197c2020-02-13 16:35:33 -0500376 }
377 return len(dr.Routes) == numNNI*numUNI*2
378}
379
khenaidoo0db4c812020-05-27 15:27:30 -0400380// IsRoutesEmpty returns true if there are no routes
381func (dr *DeviceRoutes) IsRoutesEmpty() bool {
382 dr.routeBuildLock.RLock()
383 defer dr.routeBuildLock.RUnlock()
384 return len(dr.Routes) == 0
385}
386
387// GetHalfRoute returns a half route that has only the egress hop set or the ingress hop set
388func (dr *DeviceRoutes) GetHalfRoute(nniAsEgress bool, ingress, egress uint32) ([]Hop, error) {
389 dr.routeBuildLock.RLock()
390 defer dr.routeBuildLock.RUnlock()
391 routes := make([]Hop, 0)
392 for routeLink, path := range dr.Routes {
393 // If nniAsEgress is set then the half route will only have the egress hop set where the egress port needs to be
394 // an NNI port
395 if nniAsEgress {
396 // Prioritize a specific egress NNI port if set
397 if egress != 0 && dr.IsRootPort(egress) && routeLink.Egress == egress {
398 routes = append(routes, Hop{})
399 routes = append(routes, path[1])
400 return routes, nil
401 }
402 if egress == 0 && dr.IsRootPort(routeLink.Egress) {
403 routes = append(routes, Hop{})
404 routes = append(routes, path[1])
405 return routes, nil
406 }
Andrey Pozolotin34dd63f2021-05-31 21:26:40 +0300407 } else if ingress != 0 && routeLink.Ingress == ingress {
khenaidoo0db4c812020-05-27 15:27:30 -0400408 // Here we use the first route whose ingress port matches the ingress input parameter
Andrey Pozolotin34dd63f2021-05-31 21:26:40 +0300409 routes = append(routes, path[0])
410 routes = append(routes, Hop{})
411 return routes, nil
khenaidoo820197c2020-02-13 16:35:33 -0500412 }
413 }
khenaidoo0db4c812020-05-27 15:27:30 -0400414 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 -0500415}
416
Joey Armstrong393daca2023-07-06 08:47:54 -0400417// getDeviceWithCacheUpdate returns the from the model and updates the PON ports map of that device.
Kent Hagerman2a07b862020-06-19 15:23:07 -0400418func (dr *DeviceRoutes) getDeviceWithCacheUpdate(ctx context.Context, deviceID string) (map[uint32]*voltha.Port, error) {
419 devicePorts, err := dr.listDevicePorts(ctx, deviceID)
khenaidoo820197c2020-02-13 16:35:33 -0500420 if err != nil {
divyadesaicb8b59d2020-08-18 09:55:47 +0000421 logger.Errorw(ctx, "device-not-found", log.Fields{"device-id": deviceID, "error": err})
khenaidoo820197c2020-02-13 16:35:33 -0500422 return nil, err
423 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400424 dr.updateCache(deviceID, devicePorts)
425 return devicePorts, nil
khenaidoo820197c2020-02-13 16:35:33 -0500426}
427
Joey Armstrong393daca2023-07-06 08:47:54 -0400428// copyFromExistingNNIRoutes copies routes from an existing set of NNI routes
khenaidoo820197c2020-02-13 16:35:33 -0500429func (dr *DeviceRoutes) copyFromExistingNNIRoutes(newNNIPort *voltha.LogicalPort, copyFromNNIPort *voltha.LogicalPort) {
430 updatedRoutes := make(map[PathID][]Hop)
431 for key, val := range dr.Routes {
432 if key.Ingress == copyFromNNIPort.OfpPort.PortNo {
433 updatedRoutes[PathID{Ingress: newNNIPort.OfpPort.PortNo, Egress: key.Egress}] = []Hop{
434 {DeviceID: newNNIPort.DeviceId, Ingress: newNNIPort.DevicePortNo, Egress: val[0].Egress},
435 val[1],
436 }
437 }
438 if key.Egress == copyFromNNIPort.OfpPort.PortNo {
439 updatedRoutes[PathID{Ingress: key.Ingress, Egress: newNNIPort.OfpPort.PortNo}] = []Hop{
440 val[0],
441 {DeviceID: newNNIPort.DeviceId, Ingress: val[1].Ingress, Egress: newNNIPort.DevicePortNo},
442 }
443 }
444 updatedRoutes[key] = val
445 }
446 dr.Routes = updatedRoutes
447}
448
449// reset cleans up the device graph
450func (dr *DeviceRoutes) reset() {
451 dr.rootPortsLock.Lock()
452 dr.RootPorts = make(map[uint32]uint32)
453 dr.rootPortsLock.Unlock()
khenaidoo820197c2020-02-13 16:35:33 -0500454 dr.Routes = make(map[PathID][]Hop)
khenaidoo0db4c812020-05-27 15:27:30 -0400455 dr.logicalPorts = make(map[uint32]*voltha.LogicalPort)
khenaidoo820197c2020-02-13 16:35:33 -0500456 dr.devicesPonPorts = make(map[string][]*voltha.Port)
khenaidoo0db4c812020-05-27 15:27:30 -0400457 dr.childConnectionPort = make(map[string]uint32)
khenaidoo820197c2020-02-13 16:35:33 -0500458}
459
Joey Armstrong393daca2023-07-06 08:47:54 -0400460// concatDeviceIdPortId formats a portid using the device id and the port number
khenaidoo820197c2020-02-13 16:35:33 -0500461func concatDeviceIDPortID(deviceID string, portNo uint32) string {
462 return fmt.Sprintf("%s:%d", deviceID, portNo)
463}
464
Joey Armstrong393daca2023-07-06 08:47:54 -0400465// getReverseRoute returns the reverse of the route
khenaidoo820197c2020-02-13 16:35:33 -0500466func getReverseRoute(route []Hop) []Hop {
467 reverse := make([]Hop, len(route))
468 for i, j := 0, len(route)-1; j >= 0; i, j = i+1, j-1 {
469 reverse[i].DeviceID, reverse[i].Ingress, reverse[i].Egress = route[j].DeviceID, route[j].Egress, route[j].Ingress
470 }
471 return reverse
472}
khenaidoo0db4c812020-05-27 15:27:30 -0400473
474// getChildPonPort returns the child PON port number either from cache or from the model. If it is from the model then
475// it updates the PON ports map of that device.
476func (dr *DeviceRoutes) getChildPonPort(ctx context.Context, deviceID string) (uint32, error) {
477 if port, exist := dr.devicesPonPorts[deviceID]; exist {
478 // Return only the first PON port of that child device
479 return port[0].PortNo, nil
480 }
481
482 // Get child device from model
483 if _, err := dr.getDeviceWithCacheUpdate(ctx, deviceID); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000484 logger.Errorw(ctx, "device-not-found", log.Fields{"device-id": deviceID, "error": err})
khenaidoo0db4c812020-05-27 15:27:30 -0400485 return 0, err
486 }
487
488 // Try again
489 if port, exist := dr.devicesPonPorts[deviceID]; exist {
490 // Return only the first PON port of that child device
491 return port[0].PortNo, nil
492 }
493
494 return 0, fmt.Errorf("pon port not found %s", deviceID)
495}
496
497// getParentPonPort returns the parent PON port of the child device
Kent Hagerman2a07b862020-06-19 15:23:07 -0400498func (dr *DeviceRoutes) getParentPonPort(ctx context.Context, childDeviceID string) (uint32, error) {
khenaidoo0db4c812020-05-27 15:27:30 -0400499 if pNo, exist := dr.childConnectionPort[childDeviceID]; exist {
500 return pNo, nil
501 }
502
503 // Get parent device from the model
Kent Hagerman2a07b862020-06-19 15:23:07 -0400504 if _, err := dr.getDeviceWithCacheUpdate(ctx, dr.rootDeviceID); err != nil {
divyadesaicb8b59d2020-08-18 09:55:47 +0000505 logger.Errorw(ctx, "device-not-found", log.Fields{"device-id": dr.rootDeviceID, "error": err})
khenaidoo0db4c812020-05-27 15:27:30 -0400506 return 0, err
507 }
508 // Try again
509 if pNo, exist := dr.childConnectionPort[childDeviceID]; exist {
510 return pNo, nil
511 }
512 return 0, fmt.Errorf("pon port associated with child device %s not found", childDeviceID)
513}
514
Kent Hagerman2a07b862020-06-19 15:23:07 -0400515func (dr *DeviceRoutes) updateCache(deviceID string, devicePorts map[uint32]*voltha.Port) {
516 for _, port := range devicePorts {
khenaidoo0db4c812020-05-27 15:27:30 -0400517 if port.Type == voltha.Port_PON_ONU || port.Type == voltha.Port_PON_OLT {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400518 dr.devicesPonPorts[deviceID] = append(dr.devicesPonPorts[deviceID], port)
khenaidoo0db4c812020-05-27 15:27:30 -0400519 for _, peer := range port.Peers {
520 if port.Type == voltha.Port_PON_ONU {
521 dr.childConnectionPort[port.DeviceId] = peer.PortNo
522 } else {
523 dr.childConnectionPort[peer.DeviceId] = port.PortNo
524 }
525 }
526 }
527 }
528}
529
530func (dr *DeviceRoutes) getLogicalPorts(ingress, egress uint32) (uniPort, nniPort *voltha.LogicalPort, err error) {
531 inPort, exist := dr.logicalPorts[ingress]
532 if !exist {
533 err = fmt.Errorf("ingress port %d not found", ingress)
534 return
535 }
536 outPort, exist := dr.logicalPorts[egress]
537 if !exist {
538 err = fmt.Errorf("egress port %d not found", egress)
539 return
540 }
541
542 if inPort.RootPort {
543 nniPort = inPort
544 uniPort = outPort
545 } else {
546 nniPort = outPort
547 uniPort = inPort
548 }
549
550 return
551}