blob: 4e5854bc4819fa940a0f46435fff581cb36c34c4 [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
Maninderdfadc982020-10-28 14:04:33 +053025 "github.com/opencord/voltha-lib-go/v4/pkg/log"
26 "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 {
100 return nil, fmt.Errorf("no route from:%d to:%d %w", ingress, egress, err)
101 }
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 }
403 } else {
404 // Here we use the first route whose ingress port matches the ingress input parameter
405 if ingress != 0 && routeLink.Ingress == ingress {
406 routes = append(routes, path[0])
407 routes = append(routes, Hop{})
408 return routes, nil
khenaidoo820197c2020-02-13 16:35:33 -0500409 }
410 }
411 }
khenaidoo0db4c812020-05-27 15:27:30 -0400412 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 -0500413}
414
khenaidoo0db4c812020-05-27 15:27:30 -0400415//getDeviceWithCacheUpdate returns the from the model and updates the PON ports map of that device.
Kent Hagerman2a07b862020-06-19 15:23:07 -0400416func (dr *DeviceRoutes) getDeviceWithCacheUpdate(ctx context.Context, deviceID string) (map[uint32]*voltha.Port, error) {
417 devicePorts, err := dr.listDevicePorts(ctx, deviceID)
khenaidoo820197c2020-02-13 16:35:33 -0500418 if err != nil {
divyadesaicb8b59d2020-08-18 09:55:47 +0000419 logger.Errorw(ctx, "device-not-found", log.Fields{"device-id": deviceID, "error": err})
khenaidoo820197c2020-02-13 16:35:33 -0500420 return nil, err
421 }
Kent Hagerman2a07b862020-06-19 15:23:07 -0400422 dr.updateCache(deviceID, devicePorts)
423 return devicePorts, nil
khenaidoo820197c2020-02-13 16:35:33 -0500424}
425
426//copyFromExistingNNIRoutes copies routes from an existing set of NNI routes
427func (dr *DeviceRoutes) copyFromExistingNNIRoutes(newNNIPort *voltha.LogicalPort, copyFromNNIPort *voltha.LogicalPort) {
428 updatedRoutes := make(map[PathID][]Hop)
429 for key, val := range dr.Routes {
430 if key.Ingress == copyFromNNIPort.OfpPort.PortNo {
431 updatedRoutes[PathID{Ingress: newNNIPort.OfpPort.PortNo, Egress: key.Egress}] = []Hop{
432 {DeviceID: newNNIPort.DeviceId, Ingress: newNNIPort.DevicePortNo, Egress: val[0].Egress},
433 val[1],
434 }
435 }
436 if key.Egress == copyFromNNIPort.OfpPort.PortNo {
437 updatedRoutes[PathID{Ingress: key.Ingress, Egress: newNNIPort.OfpPort.PortNo}] = []Hop{
438 val[0],
439 {DeviceID: newNNIPort.DeviceId, Ingress: val[1].Ingress, Egress: newNNIPort.DevicePortNo},
440 }
441 }
442 updatedRoutes[key] = val
443 }
444 dr.Routes = updatedRoutes
445}
446
447// reset cleans up the device graph
448func (dr *DeviceRoutes) reset() {
449 dr.rootPortsLock.Lock()
450 dr.RootPorts = make(map[uint32]uint32)
451 dr.rootPortsLock.Unlock()
khenaidoo820197c2020-02-13 16:35:33 -0500452 dr.Routes = make(map[PathID][]Hop)
khenaidoo0db4c812020-05-27 15:27:30 -0400453 dr.logicalPorts = make(map[uint32]*voltha.LogicalPort)
khenaidoo820197c2020-02-13 16:35:33 -0500454 dr.devicesPonPorts = make(map[string][]*voltha.Port)
khenaidoo0db4c812020-05-27 15:27:30 -0400455 dr.childConnectionPort = make(map[string]uint32)
khenaidoo820197c2020-02-13 16:35:33 -0500456}
457
458//concatDeviceIdPortId formats a portid using the device id and the port number
459func concatDeviceIDPortID(deviceID string, portNo uint32) string {
460 return fmt.Sprintf("%s:%d", deviceID, portNo)
461}
462
463//getReverseRoute returns the reverse of the route
464func getReverseRoute(route []Hop) []Hop {
465 reverse := make([]Hop, len(route))
466 for i, j := 0, len(route)-1; j >= 0; i, j = i+1, j-1 {
467 reverse[i].DeviceID, reverse[i].Ingress, reverse[i].Egress = route[j].DeviceID, route[j].Egress, route[j].Ingress
468 }
469 return reverse
470}
khenaidoo0db4c812020-05-27 15:27:30 -0400471
472// getChildPonPort returns the child PON port number either from cache or from the model. If it is from the model then
473// it updates the PON ports map of that device.
474func (dr *DeviceRoutes) getChildPonPort(ctx context.Context, deviceID string) (uint32, error) {
475 if port, exist := dr.devicesPonPorts[deviceID]; exist {
476 // Return only the first PON port of that child device
477 return port[0].PortNo, nil
478 }
479
480 // Get child device from model
481 if _, err := dr.getDeviceWithCacheUpdate(ctx, deviceID); err != nil {
Rohan Agrawal31f21802020-06-12 05:38:46 +0000482 logger.Errorw(ctx, "device-not-found", log.Fields{"device-id": deviceID, "error": err})
khenaidoo0db4c812020-05-27 15:27:30 -0400483 return 0, err
484 }
485
486 // Try again
487 if port, exist := dr.devicesPonPorts[deviceID]; exist {
488 // Return only the first PON port of that child device
489 return port[0].PortNo, nil
490 }
491
492 return 0, fmt.Errorf("pon port not found %s", deviceID)
493}
494
495// getParentPonPort returns the parent PON port of the child device
Kent Hagerman2a07b862020-06-19 15:23:07 -0400496func (dr *DeviceRoutes) getParentPonPort(ctx context.Context, childDeviceID string) (uint32, error) {
khenaidoo0db4c812020-05-27 15:27:30 -0400497 if pNo, exist := dr.childConnectionPort[childDeviceID]; exist {
498 return pNo, nil
499 }
500
501 // Get parent device from the model
Kent Hagerman2a07b862020-06-19 15:23:07 -0400502 if _, err := dr.getDeviceWithCacheUpdate(ctx, dr.rootDeviceID); err != nil {
divyadesaicb8b59d2020-08-18 09:55:47 +0000503 logger.Errorw(ctx, "device-not-found", log.Fields{"device-id": dr.rootDeviceID, "error": err})
khenaidoo0db4c812020-05-27 15:27:30 -0400504 return 0, err
505 }
506 // Try again
507 if pNo, exist := dr.childConnectionPort[childDeviceID]; exist {
508 return pNo, nil
509 }
510 return 0, fmt.Errorf("pon port associated with child device %s not found", childDeviceID)
511}
512
Kent Hagerman2a07b862020-06-19 15:23:07 -0400513func (dr *DeviceRoutes) updateCache(deviceID string, devicePorts map[uint32]*voltha.Port) {
514 for _, port := range devicePorts {
khenaidoo0db4c812020-05-27 15:27:30 -0400515 if port.Type == voltha.Port_PON_ONU || port.Type == voltha.Port_PON_OLT {
Kent Hagerman2a07b862020-06-19 15:23:07 -0400516 dr.devicesPonPorts[deviceID] = append(dr.devicesPonPorts[deviceID], port)
khenaidoo0db4c812020-05-27 15:27:30 -0400517 for _, peer := range port.Peers {
518 if port.Type == voltha.Port_PON_ONU {
519 dr.childConnectionPort[port.DeviceId] = peer.PortNo
520 } else {
521 dr.childConnectionPort[peer.DeviceId] = port.PortNo
522 }
523 }
524 }
525 }
526}
527
528func (dr *DeviceRoutes) getLogicalPorts(ingress, egress uint32) (uniPort, nniPort *voltha.LogicalPort, err error) {
529 inPort, exist := dr.logicalPorts[ingress]
530 if !exist {
531 err = fmt.Errorf("ingress port %d not found", ingress)
532 return
533 }
534 outPort, exist := dr.logicalPorts[egress]
535 if !exist {
536 err = fmt.Errorf("egress port %d not found", egress)
537 return
538 }
539
540 if inPort.RootPort {
541 nniPort = inPort
542 uniPort = outPort
543 } else {
544 nniPort = outPort
545 uniPort = inPort
546 }
547
548 return
549}