blob: 630fb2f793dc616d26bcb12b5d3247ab850b46cd [file] [log] [blame]
khenaidoo89b0e942018-10-21 21:11:33 -04001/*
2 * Copyright 2018-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 graph
18
19import (
npujar467fe752020-01-16 20:17:45 +053020 "context"
khenaidoo89b0e942018-10-21 21:11:33 -040021 "fmt"
khenaidoo89b0e942018-10-21 21:11:33 -040022 "strconv"
23 "strings"
Stephane Barbariec53a2752019-03-08 17:50:10 -050024 "sync"
npujar1d86a522019-11-14 17:11:16 +053025
26 "github.com/gyuho/goraph"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080027 "github.com/opencord/voltha-lib-go/v3/pkg/log"
28 "github.com/opencord/voltha-protos/v3/go/voltha"
khenaidoo89b0e942018-10-21 21:11:33 -040029)
30
31func init() {
npujar1d86a522019-11-14 17:11:16 +053032 _, err := log.AddPackage(log.JSON, log.WarnLevel, nil)
33 if err != nil {
34 log.Errorw("unable-to-register-package-to-the-log-map", log.Fields{"error": err})
35 }
khenaidoo89b0e942018-10-21 21:11:33 -040036}
37
npujar1d86a522019-11-14 17:11:16 +053038// RouteHop represent route hop attributes
khenaidoo89b0e942018-10-21 21:11:33 -040039type RouteHop struct {
40 DeviceID string
41 Ingress uint32
42 Egress uint32
43}
44
npujar1d86a522019-11-14 17:11:16 +053045// OFPortLink represent of port link attributes
khenaidoo89b0e942018-10-21 21:11:33 -040046type OFPortLink struct {
47 Ingress uint32
48 Egress uint32
49}
50
khenaidoo910204f2019-04-08 17:56:40 -040051type ofPortLinkToPath struct {
52 link OFPortLink
53 path []RouteHop
54}
55
npujar1d86a522019-11-14 17:11:16 +053056// GetDeviceFunc returns device function
npujar467fe752020-01-16 20:17:45 +053057type GetDeviceFunc func(ctx context.Context, id string) (*voltha.Device, error)
khenaidoo89b0e942018-10-21 21:11:33 -040058
npujar1d86a522019-11-14 17:11:16 +053059// DeviceGraph represent device graph attributes
khenaidoo89b0e942018-10-21 21:11:33 -040060type DeviceGraph struct {
npujar1d86a522019-11-14 17:11:16 +053061 logicalDeviceID string
khenaidoo910204f2019-04-08 17:56:40 -040062 GGraph goraph.Graph
63 getDeviceFromModel GetDeviceFunc
64 logicalPorts []*voltha.LogicalPort
65 rootPortsString map[string]uint32
66 nonRootPortsString map[string]uint32
67 RootPorts map[uint32]uint32
68 rootPortsLock sync.RWMutex
69 Routes map[OFPortLink][]RouteHop
70 graphBuildLock sync.RWMutex
71 boundaryPorts map[string]uint32
72 boundaryPortsLock sync.RWMutex
73 cachedDevices map[string]*voltha.Device
74 cachedDevicesLock sync.RWMutex
75 devicesAdded map[string]string
76 portsAdded map[string]string
khenaidoo89b0e942018-10-21 21:11:33 -040077}
78
npujar1d86a522019-11-14 17:11:16 +053079// NewDeviceGraph creates device graph instance
80func NewDeviceGraph(logicalDeviceID string, getDevice GetDeviceFunc) *DeviceGraph {
khenaidoo89b0e942018-10-21 21:11:33 -040081 var dg DeviceGraph
npujar1d86a522019-11-14 17:11:16 +053082 dg.logicalDeviceID = logicalDeviceID
khenaidoo89b0e942018-10-21 21:11:33 -040083 dg.GGraph = goraph.NewGraph()
khenaidoo910204f2019-04-08 17:56:40 -040084 dg.getDeviceFromModel = getDevice
khenaidoo1ce37ad2019-03-24 22:07:24 -040085 dg.graphBuildLock = sync.RWMutex{}
khenaidoo910204f2019-04-08 17:56:40 -040086 dg.cachedDevicesLock = sync.RWMutex{}
87 dg.rootPortsLock = sync.RWMutex{}
88 dg.devicesAdded = make(map[string]string)
89 dg.portsAdded = make(map[string]string)
90 dg.rootPortsString = make(map[string]uint32)
91 dg.nonRootPortsString = make(map[string]uint32)
92 dg.RootPorts = make(map[uint32]uint32)
93 dg.boundaryPorts = make(map[string]uint32)
94 dg.Routes = make(map[OFPortLink][]RouteHop)
95 dg.cachedDevices = make(map[string]*voltha.Device)
96 log.Debug("new device graph created ...")
khenaidoo89b0e942018-10-21 21:11:33 -040097 return &dg
98}
99
khenaidoo910204f2019-04-08 17:56:40 -0400100//IsRootPort returns true if the port is a root port on a logical device
101func (dg *DeviceGraph) IsRootPort(port uint32) bool {
102 dg.rootPortsLock.RLock()
103 defer dg.rootPortsLock.RUnlock()
104 _, exist := dg.RootPorts[port]
105 return exist
106}
107
108//GetDeviceNodeIds retrieves all the nodes in the device graph
109func (dg *DeviceGraph) GetDeviceNodeIds() map[string]string {
110 dg.graphBuildLock.RLock()
111 defer dg.graphBuildLock.RUnlock()
112 nodeIds := make(map[string]string)
113 nodesMap := dg.GGraph.GetNodes()
114 for id, node := range nodesMap {
115 if len(strings.Split(node.String(), ":")) != 2 { // not port node
116 nodeIds[id.String()] = id.String()
117 }
118 }
119 return nodeIds
120}
121
122//ComputeRoutes creates a device graph from the logical ports and then calculates all the routes
123//between the logical ports. This will clear up the graph and routes if there were any.
npujar467fe752020-01-16 20:17:45 +0530124func (dg *DeviceGraph) ComputeRoutes(ctx context.Context, lps []*voltha.LogicalPort) {
khenaidoo93d5a3d2020-01-15 12:37:05 -0500125 if dg == nil || len(lps) == 0 {
Stephane Barbariec53a2752019-03-08 17:50:10 -0500126 return
127 }
khenaidoo1ce37ad2019-03-24 22:07:24 -0400128 dg.graphBuildLock.Lock()
129 defer dg.graphBuildLock.Unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500130
khenaidoo910204f2019-04-08 17:56:40 -0400131 // Clear the graph
132 dg.reset()
133
134 dg.logicalPorts = lps
135
136 // Set the root, non-root ports and boundary ports
khenaidoo89b0e942018-10-21 21:11:33 -0400137 for _, lp := range lps {
npujar1d86a522019-11-14 17:11:16 +0530138 portID := concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)
khenaidoo910204f2019-04-08 17:56:40 -0400139 if lp.RootPort {
npujar1d86a522019-11-14 17:11:16 +0530140 dg.rootPortsString[portID] = lp.OfpPort.PortNo
khenaidoo910204f2019-04-08 17:56:40 -0400141 dg.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
142 } else {
npujar1d86a522019-11-14 17:11:16 +0530143 dg.nonRootPortsString[portID] = lp.OfpPort.PortNo
khenaidoo910204f2019-04-08 17:56:40 -0400144 }
npujar1d86a522019-11-14 17:11:16 +0530145 dg.boundaryPorts[portID] = lp.OfpPort.PortNo
khenaidoo89b0e942018-10-21 21:11:33 -0400146 }
khenaidoo89b0e942018-10-21 21:11:33 -0400147
148 // Build the graph
149 var device *voltha.Device
khenaidoo89b0e942018-10-21 21:11:33 -0400150 for _, logicalPort := range dg.logicalPorts {
npujar467fe752020-01-16 20:17:45 +0530151 device, _ = dg.getDevice(ctx, logicalPort.DeviceId, false)
152 dg.GGraph = dg.addDevice(ctx, device, dg.GGraph, &dg.devicesAdded, &dg.portsAdded, dg.boundaryPorts)
khenaidoo89b0e942018-10-21 21:11:33 -0400153 }
khenaidoo910204f2019-04-08 17:56:40 -0400154
khenaidoo89b0e942018-10-21 21:11:33 -0400155 dg.Routes = dg.buildRoutes()
156}
157
khenaidoo910204f2019-04-08 17:56:40 -0400158// AddPort adds a port to the graph. If the graph is empty it will just invoke ComputeRoutes function
npujar467fe752020-01-16 20:17:45 +0530159func (dg *DeviceGraph) AddPort(ctx context.Context, lp *voltha.LogicalPort) {
khenaidoo2c6a0992019-04-29 13:46:56 -0400160 log.Debugw("Addport", log.Fields{"logicalPort": lp})
khenaidoo910204f2019-04-08 17:56:40 -0400161 // If the graph does not exist invoke ComputeRoutes.
162 if len(dg.boundaryPorts) == 0 {
npujar467fe752020-01-16 20:17:45 +0530163 dg.ComputeRoutes(ctx, []*voltha.LogicalPort{lp})
khenaidoo910204f2019-04-08 17:56:40 -0400164 return
165 }
166
167 dg.graphBuildLock.Lock()
168 defer dg.graphBuildLock.Unlock()
169
npujar1d86a522019-11-14 17:11:16 +0530170 portID := concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)
khenaidoo910204f2019-04-08 17:56:40 -0400171
172 // If the port is already part of the boundary ports, do nothing
npujar1d86a522019-11-14 17:11:16 +0530173 if dg.portExist(portID) {
khenaidoo910204f2019-04-08 17:56:40 -0400174 return
175 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400176 // Add the port to the set of boundary ports
npujar1d86a522019-11-14 17:11:16 +0530177 dg.boundaryPorts[portID] = lp.OfpPort.PortNo
khenaidoo2c6a0992019-04-29 13:46:56 -0400178
khenaidoo910204f2019-04-08 17:56:40 -0400179 // Add the device where this port is located to the device graph. If the device is already added then
180 // only the missing port will be added
npujar467fe752020-01-16 20:17:45 +0530181 device, _ := dg.getDevice(ctx, lp.DeviceId, false)
182 dg.GGraph = dg.addDevice(ctx, device, dg.GGraph, &dg.devicesAdded, &dg.portsAdded, dg.boundaryPorts)
khenaidoo910204f2019-04-08 17:56:40 -0400183
184 if lp.RootPort {
185 // Compute the route from this root port to all non-root ports
npujar1d86a522019-11-14 17:11:16 +0530186 dg.rootPortsString[portID] = lp.OfpPort.PortNo
khenaidoo910204f2019-04-08 17:56:40 -0400187 dg.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
188 dg.Routes = dg.buildPathsToAllNonRootPorts(lp)
189 } else {
190 // Compute the route from this port to all root ports
npujar1d86a522019-11-14 17:11:16 +0530191 dg.nonRootPortsString[portID] = lp.OfpPort.PortNo
khenaidoo910204f2019-04-08 17:56:40 -0400192 dg.Routes = dg.buildPathsToAllRootPorts(lp)
193 }
194
195 dg.Print()
196}
197
npujar1d86a522019-11-14 17:11:16 +0530198// Print prints routes
khenaidoo910204f2019-04-08 17:56:40 -0400199func (dg *DeviceGraph) Print() error {
npujar1d86a522019-11-14 17:11:16 +0530200 log.Debugw("Print", log.Fields{"graph": dg.logicalDeviceID, "boundaryPorts": dg.boundaryPorts})
khenaidoo910204f2019-04-08 17:56:40 -0400201 if level, err := log.GetPackageLogLevel(); err == nil && level == log.DebugLevel {
202 output := ""
203 routeNumber := 1
204 for k, v := range dg.Routes {
205 key := fmt.Sprintf("LP:%d->LP:%d", k.Ingress, k.Egress)
206 val := ""
207 for _, i := range v {
208 val += fmt.Sprintf("{%d->%s->%d},", i.Ingress, i.DeviceID, i.Egress)
209 }
210 val = val[:len(val)-1]
211 output += fmt.Sprintf("%d:{%s=>%s} ", routeNumber, key, fmt.Sprintf("[%s]", val))
npujar1d86a522019-11-14 17:11:16 +0530212 routeNumber++
khenaidoo910204f2019-04-08 17:56:40 -0400213 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400214 if len(dg.Routes) == 0 {
npujar1d86a522019-11-14 17:11:16 +0530215 log.Debugw("no-routes-found", log.Fields{"lDeviceId": dg.logicalDeviceID, "Graph": dg.GGraph.String()})
khenaidoo2c6a0992019-04-29 13:46:56 -0400216 } else {
npujar1d86a522019-11-14 17:11:16 +0530217 log.Debugw("graph_routes", log.Fields{"lDeviceId": dg.logicalDeviceID, "Routes": output})
khenaidoo2c6a0992019-04-29 13:46:56 -0400218 }
khenaidoo910204f2019-04-08 17:56:40 -0400219 }
220 return nil
221}
222
npujar1d86a522019-11-14 17:11:16 +0530223// IsUpToDate returns true if device is up to date
khenaidoo4c9e5592019-09-09 16:20:41 -0400224func (dg *DeviceGraph) IsUpToDate(ld *voltha.LogicalDevice) bool {
225 if ld != nil {
226 if len(dg.boundaryPorts) != len(ld.Ports) {
227 return false
228 }
npujar1d86a522019-11-14 17:11:16 +0530229 var portID string
khenaidoo4c9e5592019-09-09 16:20:41 -0400230 var val uint32
231 var exist bool
232 for _, lp := range ld.Ports {
npujar1d86a522019-11-14 17:11:16 +0530233 portID = concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)
234 if val, exist = dg.boundaryPorts[portID]; !exist || val != lp.OfpPort.PortNo {
khenaidoo4c9e5592019-09-09 16:20:41 -0400235 return false
236 }
237 }
238 return true
239 }
240 return len(dg.boundaryPorts) == 0
241}
242
khenaidoo910204f2019-04-08 17:56:40 -0400243//getDevice returns the device either from the local cache (default) or from the model.
244//TODO: Set a cache timeout such that we do not use invalid data. The full device lifecycle should also
245//be taken in consideration
npujar467fe752020-01-16 20:17:45 +0530246func (dg *DeviceGraph) getDevice(ctx context.Context, id string, useCache bool) (*voltha.Device, error) {
khenaidoo2c6a0992019-04-29 13:46:56 -0400247 if useCache {
248 dg.cachedDevicesLock.RLock()
249 if d, exist := dg.cachedDevices[id]; exist {
250 dg.cachedDevicesLock.RUnlock()
251 //log.Debugw("getDevice - returned from cache", log.Fields{"deviceId": id})
252 return d, nil
253 }
khenaidoo910204f2019-04-08 17:56:40 -0400254 dg.cachedDevicesLock.RUnlock()
khenaidoo910204f2019-04-08 17:56:40 -0400255 }
khenaidoo910204f2019-04-08 17:56:40 -0400256 // Not cached
npujar467fe752020-01-16 20:17:45 +0530257 d, err := dg.getDeviceFromModel(ctx, id)
npujar1d86a522019-11-14 17:11:16 +0530258 if err != nil {
khenaidoo910204f2019-04-08 17:56:40 -0400259 log.Errorw("device-not-found", log.Fields{"deviceId": id, "error": err})
260 return nil, err
khenaidoo910204f2019-04-08 17:56:40 -0400261 }
npujar1d86a522019-11-14 17:11:16 +0530262 // cache it
263 dg.cachedDevicesLock.Lock()
264 dg.cachedDevices[id] = d
265 dg.cachedDevicesLock.Unlock()
266 //log.Debugw("getDevice - returned from model", log.Fields{"deviceId": id})
267 return d, nil
khenaidoo910204f2019-04-08 17:56:40 -0400268}
269
270// addDevice adds a device to a device graph and setup edges that represent the device connections to its peers
npujar467fe752020-01-16 20:17:45 +0530271func (dg *DeviceGraph) addDevice(ctx context.Context, device *voltha.Device, g goraph.Graph, devicesAdded *map[string]string, portsAdded *map[string]string,
khenaidoo910204f2019-04-08 17:56:40 -0400272 boundaryPorts map[string]uint32) goraph.Graph {
khenaidoo89b0e942018-10-21 21:11:33 -0400273
274 if device == nil {
275 return g
276 }
277
khenaidoo3d3b8c22019-05-22 18:10:39 -0400278 log.Debugw("Adding-device", log.Fields{"deviceId": device.Id, "ports": device.Ports})
279
khenaidoo910204f2019-04-08 17:56:40 -0400280 if _, exist := (*devicesAdded)[device.Id]; !exist {
281 g.AddNode(goraph.NewNode(device.Id))
282 (*devicesAdded)[device.Id] = device.Id
khenaidoo89b0e942018-10-21 21:11:33 -0400283 }
khenaidoo89b0e942018-10-21 21:11:33 -0400284
npujar1d86a522019-11-14 17:11:16 +0530285 var portID string
286 var peerPortID string
khenaidoo89b0e942018-10-21 21:11:33 -0400287 for _, port := range device.Ports {
npujar1d86a522019-11-14 17:11:16 +0530288 portID = concatDeviceIDPortID(device.Id, port.PortNo)
289 if _, exist := (*portsAdded)[portID]; !exist {
290 (*portsAdded)[portID] = portID
291 g.AddNode(goraph.NewNode(portID))
292 err := g.AddEdge(goraph.StringID(device.Id), goraph.StringID(portID), 1)
293 if err != nil {
294 log.Errorw("unable-to-add-edge", log.Fields{"error": err})
295 }
296 err = g.AddEdge(goraph.StringID(portID), goraph.StringID(device.Id), 1)
297 if err != nil {
298 log.Errorw("unable-to-add-edge", log.Fields{"error": err})
299 }
khenaidoo89b0e942018-10-21 21:11:33 -0400300 }
301 for _, peer := range port.Peers {
302 if _, exist := (*devicesAdded)[peer.DeviceId]; !exist {
npujar467fe752020-01-16 20:17:45 +0530303 d, _ := dg.getDevice(ctx, peer.DeviceId, true)
304 g = dg.addDevice(ctx, d, g, devicesAdded, portsAdded, boundaryPorts)
khenaidoo89b0e942018-10-21 21:11:33 -0400305 }
npujar1d86a522019-11-14 17:11:16 +0530306 peerPortID = concatDeviceIDPortID(peer.DeviceId, peer.PortNo)
307 err := g.AddEdge(goraph.StringID(portID), goraph.StringID(peerPortID), 1)
308 if err != nil {
309 log.Errorw("unable-to-add-edge", log.Fields{"error": err})
310 }
311 err = g.AddEdge(goraph.StringID(peerPortID), goraph.StringID(portID), 1)
312 if err != nil {
313 log.Errorw("unable-to-add-edge", log.Fields{"error": err})
314 }
khenaidoo89b0e942018-10-21 21:11:33 -0400315 }
316 }
317 return g
318}
319
khenaidoo910204f2019-04-08 17:56:40 -0400320//portExist returns true if the port ID is already part of the boundary ports map.
321func (dg *DeviceGraph) portExist(id string) bool {
322 dg.boundaryPortsLock.RLock()
323 defer dg.boundaryPortsLock.RUnlock()
324 _, exist := dg.boundaryPorts[id]
khenaidoo89b0e942018-10-21 21:11:33 -0400325 return exist
326}
327
khenaidoo910204f2019-04-08 17:56:40 -0400328// buildPathsToAllRootPorts builds all the paths from the non-root logical port to all root ports
329// on the logical device
330func (dg *DeviceGraph) buildPathsToAllRootPorts(lp *voltha.LogicalPort) map[OFPortLink][]RouteHop {
331 paths := dg.Routes
npujar1d86a522019-11-14 17:11:16 +0530332 source := concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)
khenaidoo910204f2019-04-08 17:56:40 -0400333 sourcePort := lp.OfpPort.PortNo
334 ch := make(chan *ofPortLinkToPath)
335 numBuildRequest := 0
336 for target, targetPort := range dg.rootPortsString {
337 go dg.buildRoute(source, target, sourcePort, targetPort, ch)
npujar1d86a522019-11-14 17:11:16 +0530338 numBuildRequest++
khenaidoo910204f2019-04-08 17:56:40 -0400339 }
340 responseReceived := 0
341forloop:
342 for {
343 if responseReceived == numBuildRequest {
344 break
345 }
npujar1d86a522019-11-14 17:11:16 +0530346 res, ok := <-ch
347 if !ok {
348 log.Debug("channel closed")
349 break forloop
khenaidoo910204f2019-04-08 17:56:40 -0400350 }
npujar1d86a522019-11-14 17:11:16 +0530351 if res != nil && len(res.path) > 0 {
352 paths[res.link] = res.path
353 paths[OFPortLink{Ingress: res.link.Egress, Egress: res.link.Ingress}] = getReverseRoute(res.path)
354 }
355 responseReceived++
khenaidoo910204f2019-04-08 17:56:40 -0400356 }
khenaidoo89b0e942018-10-21 21:11:33 -0400357 return paths
358}
359
khenaidoo910204f2019-04-08 17:56:40 -0400360// buildPathsToAllNonRootPorts builds all the paths from the root logical port to all non-root ports
361// on the logical device
362func (dg *DeviceGraph) buildPathsToAllNonRootPorts(lp *voltha.LogicalPort) map[OFPortLink][]RouteHop {
363 paths := dg.Routes
npujar1d86a522019-11-14 17:11:16 +0530364 source := concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)
khenaidoo910204f2019-04-08 17:56:40 -0400365 sourcePort := lp.OfpPort.PortNo
366 ch := make(chan *ofPortLinkToPath)
367 numBuildRequest := 0
368 for target, targetPort := range dg.nonRootPortsString {
369 go dg.buildRoute(source, target, sourcePort, targetPort, ch)
npujar1d86a522019-11-14 17:11:16 +0530370 numBuildRequest++
khenaidoo910204f2019-04-08 17:56:40 -0400371 }
372 responseReceived := 0
373forloop:
374 for {
375 if responseReceived == numBuildRequest {
376 break
377 }
npujar1d86a522019-11-14 17:11:16 +0530378 res, ok := <-ch
379 if !ok {
380 log.Debug("channel closed")
381 break forloop
khenaidoo910204f2019-04-08 17:56:40 -0400382 }
npujar1d86a522019-11-14 17:11:16 +0530383 if res != nil && len(res.path) > 0 {
384 paths[res.link] = res.path
385 paths[OFPortLink{Ingress: res.link.Egress, Egress: res.link.Ingress}] = getReverseRoute(res.path)
386 }
387 responseReceived++
khenaidoo910204f2019-04-08 17:56:40 -0400388 }
389 return paths
390}
391
392//buildRoute builds a route between a source and a target logical port
npujar1d86a522019-11-14 17:11:16 +0530393func (dg *DeviceGraph) buildRoute(sourceID, targetID string, sourcePort, targetPort uint32, ch chan *ofPortLinkToPath) {
khenaidoo910204f2019-04-08 17:56:40 -0400394 var pathIds []goraph.ID
395 path := make([]RouteHop, 0)
396 var err error
397 var hop RouteHop
398 var result *ofPortLinkToPath
399
npujar1d86a522019-11-14 17:11:16 +0530400 if sourceID == targetID {
khenaidoo910204f2019-04-08 17:56:40 -0400401 ch <- result
402 return
403 }
404 //Ignore Root - Root Routes
405 if dg.IsRootPort(sourcePort) && dg.IsRootPort(targetPort) {
406 ch <- result
407 return
408 }
409
410 //Ignore non-Root - non-Root Routes
411 if !dg.IsRootPort(sourcePort) && !dg.IsRootPort(targetPort) {
412 ch <- result
413 return
414 }
415
npujar1d86a522019-11-14 17:11:16 +0530416 if pathIds, _, err = goraph.Dijkstra(dg.GGraph, goraph.StringID(sourceID), goraph.StringID(targetID)); err != nil {
417 log.Errorw("no-path", log.Fields{"sourceId": sourceID, "targetId": targetID, "error": err})
khenaidoo910204f2019-04-08 17:56:40 -0400418 ch <- result
419 return
420 }
421 if len(pathIds)%3 != 0 {
422 ch <- result
423 return
424 }
npujar1d86a522019-11-14 17:11:16 +0530425 var deviceID string
khenaidoo910204f2019-04-08 17:56:40 -0400426 var ingressPort uint32
427 var egressPort uint32
428 for i := 0; i < len(pathIds); i = i + 3 {
npujar1d86a522019-11-14 17:11:16 +0530429 if deviceID, ingressPort, err = splitIntoDeviceIDPortID(pathIds[i].String()); err != nil {
430 log.Errorw("id-error", log.Fields{"sourceId": sourceID, "targetId": targetID, "error": err})
khenaidoo910204f2019-04-08 17:56:40 -0400431 break
432 }
npujar1d86a522019-11-14 17:11:16 +0530433 if _, egressPort, err = splitIntoDeviceIDPortID(pathIds[i+2].String()); err != nil {
434 log.Errorw("id-error", log.Fields{"sourceId": sourceID, "targetId": targetID, "error": err})
khenaidoo910204f2019-04-08 17:56:40 -0400435 break
436 }
npujar1d86a522019-11-14 17:11:16 +0530437 hop = RouteHop{Ingress: ingressPort, DeviceID: deviceID, Egress: egressPort}
khenaidoo910204f2019-04-08 17:56:40 -0400438 path = append(path, hop)
439 }
440 result = &ofPortLinkToPath{link: OFPortLink{Ingress: sourcePort, Egress: targetPort}, path: path}
441 ch <- result
442}
443
444//buildRoutes build all routes between all the ports on the logical device
445func (dg *DeviceGraph) buildRoutes() map[OFPortLink][]RouteHop {
446 paths := make(map[OFPortLink][]RouteHop)
447 ch := make(chan *ofPortLinkToPath)
448 numBuildRequest := 0
449 for source, sourcePort := range dg.boundaryPorts {
450 for target, targetPort := range dg.boundaryPorts {
451 go dg.buildRoute(source, target, sourcePort, targetPort, ch)
npujar1d86a522019-11-14 17:11:16 +0530452 numBuildRequest++
khenaidoo89b0e942018-10-21 21:11:33 -0400453 }
454 }
khenaidoo910204f2019-04-08 17:56:40 -0400455 responseReceived := 0
456forloop:
457 for {
458 if responseReceived == numBuildRequest {
459 break
460 }
npujar1d86a522019-11-14 17:11:16 +0530461 res, ok := <-ch
462 if !ok {
463 log.Debug("channel closed")
464 break forloop
khenaidoo910204f2019-04-08 17:56:40 -0400465 }
npujar1d86a522019-11-14 17:11:16 +0530466 if res != nil && len(res.path) > 0 {
467 paths[res.link] = res.path
468 }
469 responseReceived++
khenaidoo910204f2019-04-08 17:56:40 -0400470 }
471 return paths
472}
473
474// reset cleans up the device graph
475func (dg *DeviceGraph) reset() {
476 dg.devicesAdded = make(map[string]string)
477 dg.portsAdded = make(map[string]string)
478 dg.rootPortsString = make(map[string]uint32)
479 dg.nonRootPortsString = make(map[string]uint32)
480 dg.RootPorts = make(map[uint32]uint32)
481 dg.boundaryPorts = make(map[string]uint32)
482 dg.Routes = make(map[OFPortLink][]RouteHop)
483 dg.cachedDevices = make(map[string]*voltha.Device)
484}
485
486//concatDeviceIdPortId formats a portid using the device id and the port number
npujar1d86a522019-11-14 17:11:16 +0530487func concatDeviceIDPortID(deviceID string, portNo uint32) string {
488 return fmt.Sprintf("%s:%d", deviceID, portNo)
khenaidoo910204f2019-04-08 17:56:40 -0400489}
490
491// splitIntoDeviceIdPortId extracts the device id and port number from the portId
npujar1d86a522019-11-14 17:11:16 +0530492func splitIntoDeviceIDPortID(id string) (string, uint32, error) {
khenaidoo910204f2019-04-08 17:56:40 -0400493 result := strings.Split(id, ":")
494 if len(result) != 2 {
npujar1d86a522019-11-14 17:11:16 +0530495 return "", 0, fmt.Errorf("invalid-id-%s", id)
khenaidoo910204f2019-04-08 17:56:40 -0400496 }
npujar1d86a522019-11-14 17:11:16 +0530497 temp, err := strconv.ParseInt(result[1], 10, 32)
498 if err != nil {
499 return "", 0, fmt.Errorf("invalid-id-%s-%s", id, err.Error())
khenaidoo910204f2019-04-08 17:56:40 -0400500 }
npujar1d86a522019-11-14 17:11:16 +0530501 return result[0], uint32(temp), nil
khenaidoo910204f2019-04-08 17:56:40 -0400502}
503
khenaidoocfe03b92019-06-03 20:06:31 -0400504//getReverseRoute returns the reverse of the route
khenaidoo910204f2019-04-08 17:56:40 -0400505func getReverseRoute(route []RouteHop) []RouteHop {
506 reverse := make([]RouteHop, len(route))
khenaidoocfe03b92019-06-03 20:06:31 -0400507 for i, j := 0, len(route)-1; j >= 0; i, j = i+1, j-1 {
508 reverse[i].DeviceID, reverse[i].Ingress, reverse[i].Egress = route[j].DeviceID, route[j].Egress, route[j].Ingress
khenaidoo910204f2019-04-08 17:56:40 -0400509 }
510 return reverse
khenaidoo89b0e942018-10-21 21:11:33 -0400511}