blob: 1a5cd4c9a5e1efa0fba24e48a3ffd3ba5e6dc123 [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
yasin sapli5458a1c2021-06-14 22:24:38 +000025 "github.com/opencord/voltha-lib-go/v5/pkg/log"
Maninderdfadc982020-10-28 14:04:33 +053026 "github.com/opencord/voltha-protos/v4/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
82//IsRootPort returns true if the port is a root port on a logical device
83func (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 Campanella408dec62021-11-10 14:32:10 +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
khenaidoo820197c2020-02-13 16:35:33 -0500123//ComputeRoutes calculates all the routes between the logical ports. This will clear up any existing route
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400124func (dr *DeviceRoutes) ComputeRoutes(ctx context.Context, lps map[uint32]*voltha.LogicalPort) error {
khenaidoo820197c2020-02-13 16:35:33 -0500125 dr.routeBuildLock.Lock()
126 defer dr.routeBuildLock.Unlock()
127
Rohan Agrawal31f21802020-06-12 05:38:46 +0000128 logger.Debugw(ctx, "computing-all-routes", log.Fields{"len-logical-ports": len(lps)})
khenaidoo820197c2020-02-13 16:35:33 -0500129 var err error
130 defer func() {
131 // On error, clear the routes - any flow request or a port add/delete will trigger the rebuild
132 if err != nil {
133 dr.reset()
134 }
135 }()
136
137 if len(lps) < 2 {
khenaidoo787224a2020-04-16 18:08:47 -0400138 return fmt.Errorf("not enough logical port :%w", ErrNoRoute)
khenaidoo820197c2020-02-13 16:35:33 -0500139 }
140
141 dr.reset()
khenaidoo820197c2020-02-13 16:35:33 -0500142
143 // Setup the physical ports to logical ports map, the nni ports as well as the root ports map
144 physPortToLogicalPortMap := make(map[string]uint32)
145 nniPorts := make([]*voltha.LogicalPort, 0)
146 for _, lp := range lps {
147 physPortToLogicalPortMap[concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)] = lp.OfpPort.PortNo
148 if lp.RootPort {
149 nniPorts = append(nniPorts, lp)
150 dr.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
151 }
khenaidoo0db4c812020-05-27 15:27:30 -0400152 dr.logicalPorts[lp.OfpPort.PortNo] = lp
khenaidoo820197c2020-02-13 16:35:33 -0500153 }
khenaidoo0db4c812020-05-27 15:27:30 -0400154
khenaidoo820197c2020-02-13 16:35:33 -0500155 if len(nniPorts) == 0 {
khenaidoo787224a2020-04-16 18:08:47 -0400156 return fmt.Errorf("no nni port :%w", ErrNoRoute)
khenaidoo820197c2020-02-13 16:35:33 -0500157 }
khenaidoo820197c2020-02-13 16:35:33 -0500158 var copyFromNNIPort *voltha.LogicalPort
159 for idx, nniPort := range nniPorts {
160 if idx == 0 {
161 copyFromNNIPort = nniPort
162 } else if len(dr.Routes) > 0 {
163 dr.copyFromExistingNNIRoutes(nniPort, copyFromNNIPort)
164 return nil
165 }
166 // Get root device
Kent Hagerman2a07b862020-06-19 15:23:07 -0400167 rootDeviceID := nniPort.DeviceId
168 rootDevicePorts, err := dr.getDeviceWithCacheUpdate(ctx, nniPort.DeviceId)
khenaidoo820197c2020-02-13 16:35:33 -0500169 if err != nil {
170 return err
171 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400172 if len(rootDevicePorts) == 0 {
173 err = status.Errorf(codes.FailedPrecondition, "no-port-%s", rootDeviceID)
khenaidoo820197c2020-02-13 16:35:33 -0500174 return err
175 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400176 for _, rootDevicePort := range rootDevicePorts {
khenaidoo820197c2020-02-13 16:35:33 -0500177 if rootDevicePort.Type == voltha.Port_PON_OLT {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400178 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 -0500179 for _, rootDevicePeer := range rootDevicePort.Peers {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400180 childDeviceID := rootDevicePeer.DeviceId
181 childDevicePorts, err := dr.getDeviceWithCacheUpdate(ctx, rootDevicePeer.DeviceId)
khenaidoo820197c2020-02-13 16:35:33 -0500182 if err != nil {
183 return err
184 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400185 childPonPort, err := dr.getChildPonPort(ctx, childDeviceID)
khenaidoo0db4c812020-05-27 15:27:30 -0400186 if err != nil {
khenaidoo820197c2020-02-13 16:35:33 -0500187 return err
188 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400189 for _, childDevicePort := range childDevicePorts {
khenaidoo820197c2020-02-13 16:35:33 -0500190 if childDevicePort.Type == voltha.Port_ETHERNET_UNI {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400191 childLogicalPort, exist := physPortToLogicalPortMap[concatDeviceIDPortID(childDeviceID, childDevicePort.PortNo)]
khenaidoo820197c2020-02-13 16:35:33 -0500192 if !exist {
193 // This can happen if this logical port has not been created yet for that device
194 continue
195 }
196 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: childLogicalPort}] = []Hop{
Kent Hagerman2a07b862020-06-19 15:23:07 -0400197 {DeviceID: rootDeviceID, Ingress: nniPort.DevicePortNo, Egress: rootDevicePort.PortNo},
198 {DeviceID: childDeviceID, Ingress: childPonPort, Egress: childDevicePort.PortNo},
khenaidoo820197c2020-02-13 16:35:33 -0500199 }
200 dr.Routes[PathID{Ingress: childLogicalPort, Egress: nniPort.OfpPort.PortNo}] = getReverseRoute(
201 dr.Routes[PathID{Ingress: nniPort.OfpPort.PortNo, Egress: childLogicalPort}])
202 }
203 }
204 }
205 }
206 }
207 }
208 return nil
209}
210
khenaidoo0db4c812020-05-27 15:27:30 -0400211// AddPort augments the current set of routes with new routes corresponding to the logical port "lp". If the routes have
212// not been built yet then use logical port "lps" to compute all current routes (lps includes lp)
Kent Hagerman2a07b862020-06-19 15:23:07 -0400213func (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 +0000214 logger.Debugw(ctx, "add-port-to-routes", log.Fields{"port": lp, "count-logical-ports": len(lps)})
khenaidoo0db4c812020-05-27 15:27:30 -0400215
216 // Adding NNI port
217 if lp.RootPort {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400218 return dr.AddNNIPort(ctx, lp, deviceID, devicePorts, lps)
khenaidoo820197c2020-02-13 16:35:33 -0500219 }
220
khenaidoo0db4c812020-05-27 15:27:30 -0400221 // Adding UNI port
Kent Hagerman2a07b862020-06-19 15:23:07 -0400222 return dr.AddUNIPort(ctx, lp, deviceID, devicePorts, lps)
khenaidoo0db4c812020-05-27 15:27:30 -0400223}
224
225// AddUNIPort setup routes between the logical UNI port lp and all registered NNI ports
Kent Hagerman2a07b862020-06-19 15:23:07 -0400226func (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 +0000227 logger.Debugw(ctx, "add-uni-port-to-routes", log.Fields{"port": lp, "count-logical-ports": len(lps)})
khenaidoo0db4c812020-05-27 15:27:30 -0400228
229 dr.routeBuildLock.Lock()
230 defer dr.routeBuildLock.Unlock()
231
232 // Add port to logical ports
233 dr.logicalPorts[lp.OfpPort.PortNo] = lp
234
235 // Update internal structures with device data
Kent Hagerman2a07b862020-06-19 15:23:07 -0400236 dr.updateCache(deviceID, devicePorts)
khenaidoo0db4c812020-05-27 15:27:30 -0400237
238 // Adding a UNI port
239 childPonPort, err := dr.getChildPonPort(ctx, lp.DeviceId)
240 if err != nil {
241 return err
242 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400243 rootDevicePonPort, err := dr.getParentPonPort(ctx, deviceID)
khenaidoo0db4c812020-05-27 15:27:30 -0400244 if err != nil {
245 return err
246 }
247
248 // Adding a UNI port
249 for _, lPort := range lps {
250 if lPort.RootPort {
251 dr.Routes[PathID{Ingress: lPort.OfpPort.PortNo, Egress: lp.OfpPort.PortNo}] = []Hop{
252 {DeviceID: lPort.DeviceId, Ingress: lPort.DevicePortNo, Egress: rootDevicePonPort},
253 {DeviceID: lp.DeviceId, Ingress: childPonPort, Egress: lp.DevicePortNo},
254 }
255 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: lPort.OfpPort.PortNo}] = getReverseRoute(
256 dr.Routes[PathID{Ingress: lPort.OfpPort.PortNo, Egress: lp.OfpPort.PortNo}])
257 }
khenaidoo820197c2020-02-13 16:35:33 -0500258 }
259 return nil
260}
261
khenaidoo0db4c812020-05-27 15:27:30 -0400262// AddNNIPort setup routes between the logical NNI port lp and all registered UNI ports
Kent Hagerman2a07b862020-06-19 15:23:07 -0400263func (dr *DeviceRoutes) AddNNIPort(ctx context.Context, lp *voltha.LogicalPort, deviceID string, devicePorts map[uint32]*voltha.Port, lps map[uint32]*voltha.LogicalPort) error {
264 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 -0500265
266 dr.routeBuildLock.Lock()
khenaidoo820197c2020-02-13 16:35:33 -0500267 defer dr.routeBuildLock.Unlock()
khenaidoo0db4c812020-05-27 15:27:30 -0400268
269 // Update internal structures with device data
Kent Hagerman2a07b862020-06-19 15:23:07 -0400270 dr.updateCache(deviceID, devicePorts)
khenaidoo0db4c812020-05-27 15:27:30 -0400271
272 // Setup the physical ports to logical ports map, the nni ports as well as the root ports map
273 physPortToLogicalPortMap := make(map[string]uint32)
274 for _, lp := range lps {
275 physPortToLogicalPortMap[concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)] = lp.OfpPort.PortNo
276 if lp.RootPort {
277 dr.rootPortsLock.Lock()
278 dr.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
279 dr.rootPortsLock.Unlock()
280 }
281 dr.logicalPorts[lp.OfpPort.PortNo] = lp
khenaidoo820197c2020-02-13 16:35:33 -0500282 }
283
Kent Hagerman2a07b862020-06-19 15:23:07 -0400284 for _, rootDevicePort := range devicePorts {
khenaidoo0db4c812020-05-27 15:27:30 -0400285 if rootDevicePort.Type == voltha.Port_PON_OLT {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400286 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 -0400287 for _, rootDevicePeer := range rootDevicePort.Peers {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400288 childDeviceID := rootDevicePeer.DeviceId
289 childDevicePorts, err := dr.getDeviceWithCacheUpdate(ctx, rootDevicePeer.DeviceId)
khenaidoo0db4c812020-05-27 15:27:30 -0400290 if err != nil {
291 continue
292 }
293
Kent Hagerman2a07b862020-06-19 15:23:07 -0400294 childPonPort, err := dr.getChildPonPort(ctx, childDeviceID)
khenaidoo0db4c812020-05-27 15:27:30 -0400295 if err != nil {
296 continue
297 }
298
Kent Hagerman2a07b862020-06-19 15:23:07 -0400299 for _, childDevicePort := range childDevicePorts {
300 childLogicalPort, exist := physPortToLogicalPortMap[concatDeviceIDPortID(childDeviceID, childDevicePort.PortNo)]
khenaidoo0db4c812020-05-27 15:27:30 -0400301 if !exist {
302 // This can happen if this logical port has not been created yet for that device
303 continue
304 }
305
306 if childDevicePort.Type == voltha.Port_ETHERNET_UNI {
307 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: childLogicalPort}] = []Hop{
Kent Hagerman2a07b862020-06-19 15:23:07 -0400308 {DeviceID: deviceID, Ingress: lp.DevicePortNo, Egress: rootDevicePort.PortNo},
309 {DeviceID: childDeviceID, Ingress: childPonPort, Egress: childDevicePort.PortNo},
khenaidoo0db4c812020-05-27 15:27:30 -0400310 }
311 dr.Routes[PathID{Ingress: childLogicalPort, Egress: lp.OfpPort.PortNo}] = getReverseRoute(
312 dr.Routes[PathID{Ingress: lp.OfpPort.PortNo, Egress: childLogicalPort}])
313 }
314 }
khenaidoo820197c2020-02-13 16:35:33 -0500315 }
316 }
317 }
khenaidoo0db4c812020-05-27 15:27:30 -0400318 return nil
319}
khenaidoo820197c2020-02-13 16:35:33 -0500320
khenaidoo0db4c812020-05-27 15:27:30 -0400321// 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 -0400322func (dr *DeviceRoutes) AddAllPorts(ctx context.Context, deviceID string, devicePorts map[uint32]*voltha.Port, lps map[uint32]*voltha.LogicalPort) error {
323 logger.Debugw(ctx, "add-all-port-to-routes", log.Fields{"logical-ports-count": len(lps), "device-id": deviceID})
khenaidoo0db4c812020-05-27 15:27:30 -0400324 for _, lp := range lps {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400325 if lp.DeviceId == deviceID {
326 if err := dr.AddPort(ctx, lp, deviceID, devicePorts, lps); err != nil {
khenaidoo820197c2020-02-13 16:35:33 -0500327 return err
328 }
khenaidoo820197c2020-02-13 16:35:33 -0500329 }
khenaidoo820197c2020-02-13 16:35:33 -0500330 }
331 return nil
332}
333
334// Print prints routes
Rohan Agrawal31f21802020-06-12 05:38:46 +0000335func (dr *DeviceRoutes) Print(ctx context.Context) error {
khenaidoo292ab522020-06-05 18:17:59 -0400336 dr.routeBuildLock.RLock()
337 defer dr.routeBuildLock.RUnlock()
Rohan Agrawal31f21802020-06-12 05:38:46 +0000338 logger.Debugw(ctx, "Print", log.Fields{"logical-device-id": dr.logicalDeviceID, "logical-ports": dr.logicalPorts})
Girish Kumarf56a4682020-03-20 20:07:46 +0000339 if logger.V(log.DebugLevel) {
khenaidoo820197c2020-02-13 16:35:33 -0500340 output := ""
341 routeNumber := 1
342 for k, v := range dr.Routes {
343 key := fmt.Sprintf("LP:%d->LP:%d", k.Ingress, k.Egress)
344 val := ""
345 for _, i := range v {
346 val += fmt.Sprintf("{%d->%s->%d},", i.Ingress, i.DeviceID, i.Egress)
347 }
348 val = val[:len(val)-1]
349 output += fmt.Sprintf("%d:{%s=>%s} ", routeNumber, key, fmt.Sprintf("[%s]", val))
350 routeNumber++
351 }
352 if len(dr.Routes) == 0 {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000353 logger.Debugw(ctx, "no-routes-found", log.Fields{"logical-device-id": dr.logicalDeviceID})
khenaidoo820197c2020-02-13 16:35:33 -0500354 } else {
divyadesaicb8b59d2020-08-18 09:55:47 +0000355 logger.Debugw(ctx, "graph_routes", log.Fields{"logical-device-id": dr.logicalDeviceID, "Routes": output})
khenaidoo820197c2020-02-13 16:35:33 -0500356 }
357 }
358 return nil
359}
360
khenaidoo0db4c812020-05-27 15:27:30 -0400361// isUpToDate returns true if device is up to date
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400362func (dr *DeviceRoutes) isUpToDate(ldPorts map[uint32]*voltha.LogicalPort) bool {
khenaidoo820197c2020-02-13 16:35:33 -0500363 dr.routeBuildLock.Lock()
364 defer dr.routeBuildLock.Unlock()
365 numNNI, numUNI := 0, 0
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400366 if ldPorts != nil {
367 if len(dr.logicalPorts) != len(ldPorts) {
khenaidoo820197c2020-02-13 16:35:33 -0500368 return false
369 }
370 numNNI = len(dr.RootPorts)
Kent Hagermanfa9d6d42020-05-25 11:49:40 -0400371 numUNI = len(ldPorts) - numNNI
khenaidoo820197c2020-02-13 16:35:33 -0500372 }
373 return len(dr.Routes) == numNNI*numUNI*2
374}
375
khenaidoo0db4c812020-05-27 15:27:30 -0400376// IsRoutesEmpty returns true if there are no routes
377func (dr *DeviceRoutes) IsRoutesEmpty() bool {
378 dr.routeBuildLock.RLock()
379 defer dr.routeBuildLock.RUnlock()
380 return len(dr.Routes) == 0
381}
382
383// GetHalfRoute returns a half route that has only the egress hop set or the ingress hop set
384func (dr *DeviceRoutes) GetHalfRoute(nniAsEgress bool, ingress, egress uint32) ([]Hop, error) {
385 dr.routeBuildLock.RLock()
386 defer dr.routeBuildLock.RUnlock()
387 routes := make([]Hop, 0)
388 for routeLink, path := range dr.Routes {
389 // If nniAsEgress is set then the half route will only have the egress hop set where the egress port needs to be
390 // an NNI port
391 if nniAsEgress {
392 // Prioritize a specific egress NNI port if set
393 if egress != 0 && dr.IsRootPort(egress) && routeLink.Egress == egress {
394 routes = append(routes, Hop{})
395 routes = append(routes, path[1])
396 return routes, nil
397 }
398 if egress == 0 && dr.IsRootPort(routeLink.Egress) {
399 routes = append(routes, Hop{})
400 routes = append(routes, path[1])
401 return routes, nil
402 }
Andrey Pozolotin34dd63f2021-05-31 21:26:40 +0300403 } else if ingress != 0 && routeLink.Ingress == ingress {
khenaidoo0db4c812020-05-27 15:27:30 -0400404 // Here we use the first route whose ingress port matches the ingress input parameter
Andrey Pozolotin34dd63f2021-05-31 21:26:40 +0300405 routes = append(routes, path[0])
406 routes = append(routes, Hop{})
407 return routes, nil
khenaidoo820197c2020-02-13 16:35:33 -0500408 }
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.
Kent Hagerman2a07b862020-06-19 15:23:07 -0400414func (dr *DeviceRoutes) getDeviceWithCacheUpdate(ctx context.Context, deviceID string) (map[uint32]*voltha.Port, error) {
415 devicePorts, err := dr.listDevicePorts(ctx, deviceID)
khenaidoo820197c2020-02-13 16:35:33 -0500416 if err != nil {
divyadesaicb8b59d2020-08-18 09:55:47 +0000417 logger.Errorw(ctx, "device-not-found", log.Fields{"device-id": deviceID, "error": err})
khenaidoo820197c2020-02-13 16:35:33 -0500418 return nil, err
419 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400420 dr.updateCache(deviceID, devicePorts)
421 return devicePorts, nil
khenaidoo820197c2020-02-13 16:35:33 -0500422}
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
Kent Hagerman2a07b862020-06-19 15:23:07 -0400494func (dr *DeviceRoutes) getParentPonPort(ctx context.Context, childDeviceID string) (uint32, error) {
khenaidoo0db4c812020-05-27 15:27:30 -0400495 if pNo, exist := dr.childConnectionPort[childDeviceID]; exist {
496 return pNo, nil
497 }
498
499 // Get parent device from the model
Kent Hagerman2a07b862020-06-19 15:23:07 -0400500 if _, err := dr.getDeviceWithCacheUpdate(ctx, dr.rootDeviceID); err != nil {
divyadesaicb8b59d2020-08-18 09:55:47 +0000501 logger.Errorw(ctx, "device-not-found", log.Fields{"device-id": dr.rootDeviceID, "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
Kent Hagerman2a07b862020-06-19 15:23:07 -0400511func (dr *DeviceRoutes) updateCache(deviceID string, devicePorts map[uint32]*voltha.Port) {
512 for _, port := range devicePorts {
khenaidoo0db4c812020-05-27 15:27:30 -0400513 if port.Type == voltha.Port_PON_ONU || port.Type == voltha.Port_PON_OLT {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400514 dr.devicesPonPorts[deviceID] = append(dr.devicesPonPorts[deviceID], port)
khenaidoo0db4c812020-05-27 15:27:30 -0400515 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}