blob: c37cfd0b2cf9fae0fde220371e3796f77af3739a [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 (
khenaidoo89b0e942018-10-21 21:11:33 -040020 "fmt"
khenaidoo89b0e942018-10-21 21:11:33 -040021 "strconv"
22 "strings"
Stephane Barbariec53a2752019-03-08 17:50:10 -050023 "sync"
npujar1d86a522019-11-14 17:11:16 +053024
25 "github.com/gyuho/goraph"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080026 "github.com/opencord/voltha-lib-go/v3/pkg/log"
27 "github.com/opencord/voltha-protos/v3/go/voltha"
khenaidoo89b0e942018-10-21 21:11:33 -040028)
29
30func init() {
npujar1d86a522019-11-14 17:11:16 +053031 _, err := log.AddPackage(log.JSON, log.WarnLevel, nil)
32 if err != nil {
33 log.Errorw("unable-to-register-package-to-the-log-map", log.Fields{"error": err})
34 }
khenaidoo89b0e942018-10-21 21:11:33 -040035}
36
npujar1d86a522019-11-14 17:11:16 +053037// RouteHop represent route hop attributes
khenaidoo89b0e942018-10-21 21:11:33 -040038type RouteHop struct {
39 DeviceID string
40 Ingress uint32
41 Egress uint32
42}
43
npujar1d86a522019-11-14 17:11:16 +053044// OFPortLink represent of port link attributes
khenaidoo89b0e942018-10-21 21:11:33 -040045type OFPortLink struct {
46 Ingress uint32
47 Egress uint32
48}
49
khenaidoo910204f2019-04-08 17:56:40 -040050type ofPortLinkToPath struct {
51 link OFPortLink
52 path []RouteHop
53}
54
npujar1d86a522019-11-14 17:11:16 +053055// GetDeviceFunc returns device function
khenaidoo89b0e942018-10-21 21:11:33 -040056type GetDeviceFunc func(id string) (*voltha.Device, error)
57
npujar1d86a522019-11-14 17:11:16 +053058// DeviceGraph represent device graph attributes
khenaidoo89b0e942018-10-21 21:11:33 -040059type DeviceGraph struct {
npujar1d86a522019-11-14 17:11:16 +053060 logicalDeviceID string
khenaidoo910204f2019-04-08 17:56:40 -040061 GGraph goraph.Graph
62 getDeviceFromModel GetDeviceFunc
63 logicalPorts []*voltha.LogicalPort
64 rootPortsString map[string]uint32
65 nonRootPortsString map[string]uint32
66 RootPorts map[uint32]uint32
67 rootPortsLock sync.RWMutex
68 Routes map[OFPortLink][]RouteHop
69 graphBuildLock sync.RWMutex
70 boundaryPorts map[string]uint32
71 boundaryPortsLock sync.RWMutex
72 cachedDevices map[string]*voltha.Device
73 cachedDevicesLock sync.RWMutex
74 devicesAdded map[string]string
75 portsAdded map[string]string
khenaidoo89b0e942018-10-21 21:11:33 -040076}
77
npujar1d86a522019-11-14 17:11:16 +053078// NewDeviceGraph creates device graph instance
79func NewDeviceGraph(logicalDeviceID string, getDevice GetDeviceFunc) *DeviceGraph {
khenaidoo89b0e942018-10-21 21:11:33 -040080 var dg DeviceGraph
npujar1d86a522019-11-14 17:11:16 +053081 dg.logicalDeviceID = logicalDeviceID
khenaidoo89b0e942018-10-21 21:11:33 -040082 dg.GGraph = goraph.NewGraph()
khenaidoo910204f2019-04-08 17:56:40 -040083 dg.getDeviceFromModel = getDevice
khenaidoo1ce37ad2019-03-24 22:07:24 -040084 dg.graphBuildLock = sync.RWMutex{}
khenaidoo910204f2019-04-08 17:56:40 -040085 dg.cachedDevicesLock = sync.RWMutex{}
86 dg.rootPortsLock = sync.RWMutex{}
87 dg.devicesAdded = make(map[string]string)
88 dg.portsAdded = make(map[string]string)
89 dg.rootPortsString = make(map[string]uint32)
90 dg.nonRootPortsString = make(map[string]uint32)
91 dg.RootPorts = make(map[uint32]uint32)
92 dg.boundaryPorts = make(map[string]uint32)
93 dg.Routes = make(map[OFPortLink][]RouteHop)
94 dg.cachedDevices = make(map[string]*voltha.Device)
95 log.Debug("new device graph created ...")
khenaidoo89b0e942018-10-21 21:11:33 -040096 return &dg
97}
98
khenaidoo910204f2019-04-08 17:56:40 -040099//IsRootPort returns true if the port is a root port on a logical device
100func (dg *DeviceGraph) IsRootPort(port uint32) bool {
101 dg.rootPortsLock.RLock()
102 defer dg.rootPortsLock.RUnlock()
103 _, exist := dg.RootPorts[port]
104 return exist
105}
106
107//GetDeviceNodeIds retrieves all the nodes in the device graph
108func (dg *DeviceGraph) GetDeviceNodeIds() map[string]string {
109 dg.graphBuildLock.RLock()
110 defer dg.graphBuildLock.RUnlock()
111 nodeIds := make(map[string]string)
112 nodesMap := dg.GGraph.GetNodes()
113 for id, node := range nodesMap {
114 if len(strings.Split(node.String(), ":")) != 2 { // not port node
115 nodeIds[id.String()] = id.String()
116 }
117 }
118 return nodeIds
119}
120
121//ComputeRoutes creates a device graph from the logical ports and then calculates all the routes
122//between the logical ports. This will clear up the graph and routes if there were any.
khenaidoo89b0e942018-10-21 21:11:33 -0400123func (dg *DeviceGraph) ComputeRoutes(lps []*voltha.LogicalPort) {
khenaidoo93d5a3d2020-01-15 12:37:05 -0500124 if dg == nil || len(lps) == 0 {
Stephane Barbariec53a2752019-03-08 17:50:10 -0500125 return
126 }
khenaidoo1ce37ad2019-03-24 22:07:24 -0400127 dg.graphBuildLock.Lock()
128 defer dg.graphBuildLock.Unlock()
Stephane Barbariec53a2752019-03-08 17:50:10 -0500129
khenaidoo910204f2019-04-08 17:56:40 -0400130 // Clear the graph
131 dg.reset()
132
133 dg.logicalPorts = lps
134
135 // Set the root, non-root ports and boundary ports
khenaidoo89b0e942018-10-21 21:11:33 -0400136 for _, lp := range lps {
npujar1d86a522019-11-14 17:11:16 +0530137 portID := concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)
khenaidoo910204f2019-04-08 17:56:40 -0400138 if lp.RootPort {
npujar1d86a522019-11-14 17:11:16 +0530139 dg.rootPortsString[portID] = lp.OfpPort.PortNo
khenaidoo910204f2019-04-08 17:56:40 -0400140 dg.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
141 } else {
npujar1d86a522019-11-14 17:11:16 +0530142 dg.nonRootPortsString[portID] = lp.OfpPort.PortNo
khenaidoo910204f2019-04-08 17:56:40 -0400143 }
npujar1d86a522019-11-14 17:11:16 +0530144 dg.boundaryPorts[portID] = lp.OfpPort.PortNo
khenaidoo89b0e942018-10-21 21:11:33 -0400145 }
khenaidoo89b0e942018-10-21 21:11:33 -0400146
147 // Build the graph
148 var device *voltha.Device
khenaidoo89b0e942018-10-21 21:11:33 -0400149 for _, logicalPort := range dg.logicalPorts {
khenaidoo2c6a0992019-04-29 13:46:56 -0400150 device, _ = dg.getDevice(logicalPort.DeviceId, false)
khenaidoo910204f2019-04-08 17:56:40 -0400151 dg.GGraph = dg.addDevice(device, dg.GGraph, &dg.devicesAdded, &dg.portsAdded, dg.boundaryPorts)
khenaidoo89b0e942018-10-21 21:11:33 -0400152 }
khenaidoo910204f2019-04-08 17:56:40 -0400153
khenaidoo89b0e942018-10-21 21:11:33 -0400154 dg.Routes = dg.buildRoutes()
155}
156
khenaidoo910204f2019-04-08 17:56:40 -0400157// AddPort adds a port to the graph. If the graph is empty it will just invoke ComputeRoutes function
158func (dg *DeviceGraph) AddPort(lp *voltha.LogicalPort) {
khenaidoo2c6a0992019-04-29 13:46:56 -0400159 log.Debugw("Addport", log.Fields{"logicalPort": lp})
khenaidoo910204f2019-04-08 17:56:40 -0400160 // If the graph does not exist invoke ComputeRoutes.
161 if len(dg.boundaryPorts) == 0 {
162 dg.ComputeRoutes([]*voltha.LogicalPort{lp})
163 return
164 }
165
166 dg.graphBuildLock.Lock()
167 defer dg.graphBuildLock.Unlock()
168
npujar1d86a522019-11-14 17:11:16 +0530169 portID := concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)
khenaidoo910204f2019-04-08 17:56:40 -0400170
171 // If the port is already part of the boundary ports, do nothing
npujar1d86a522019-11-14 17:11:16 +0530172 if dg.portExist(portID) {
khenaidoo910204f2019-04-08 17:56:40 -0400173 return
174 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400175 // Add the port to the set of boundary ports
npujar1d86a522019-11-14 17:11:16 +0530176 dg.boundaryPorts[portID] = lp.OfpPort.PortNo
khenaidoo2c6a0992019-04-29 13:46:56 -0400177
khenaidoo910204f2019-04-08 17:56:40 -0400178 // Add the device where this port is located to the device graph. If the device is already added then
179 // only the missing port will be added
khenaidoo2c6a0992019-04-29 13:46:56 -0400180 device, _ := dg.getDevice(lp.DeviceId, false)
khenaidoo910204f2019-04-08 17:56:40 -0400181 dg.GGraph = dg.addDevice(device, dg.GGraph, &dg.devicesAdded, &dg.portsAdded, dg.boundaryPorts)
182
183 if lp.RootPort {
184 // Compute the route from this root port to all non-root ports
npujar1d86a522019-11-14 17:11:16 +0530185 dg.rootPortsString[portID] = lp.OfpPort.PortNo
khenaidoo910204f2019-04-08 17:56:40 -0400186 dg.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
187 dg.Routes = dg.buildPathsToAllNonRootPorts(lp)
188 } else {
189 // Compute the route from this port to all root ports
npujar1d86a522019-11-14 17:11:16 +0530190 dg.nonRootPortsString[portID] = lp.OfpPort.PortNo
khenaidoo910204f2019-04-08 17:56:40 -0400191 dg.Routes = dg.buildPathsToAllRootPorts(lp)
192 }
193
194 dg.Print()
195}
196
npujar1d86a522019-11-14 17:11:16 +0530197// Print prints routes
khenaidoo910204f2019-04-08 17:56:40 -0400198func (dg *DeviceGraph) Print() error {
npujar1d86a522019-11-14 17:11:16 +0530199 log.Debugw("Print", log.Fields{"graph": dg.logicalDeviceID, "boundaryPorts": dg.boundaryPorts})
khenaidoo910204f2019-04-08 17:56:40 -0400200 if level, err := log.GetPackageLogLevel(); err == nil && level == log.DebugLevel {
201 output := ""
202 routeNumber := 1
203 for k, v := range dg.Routes {
204 key := fmt.Sprintf("LP:%d->LP:%d", k.Ingress, k.Egress)
205 val := ""
206 for _, i := range v {
207 val += fmt.Sprintf("{%d->%s->%d},", i.Ingress, i.DeviceID, i.Egress)
208 }
209 val = val[:len(val)-1]
210 output += fmt.Sprintf("%d:{%s=>%s} ", routeNumber, key, fmt.Sprintf("[%s]", val))
npujar1d86a522019-11-14 17:11:16 +0530211 routeNumber++
khenaidoo910204f2019-04-08 17:56:40 -0400212 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400213 if len(dg.Routes) == 0 {
npujar1d86a522019-11-14 17:11:16 +0530214 log.Debugw("no-routes-found", log.Fields{"lDeviceId": dg.logicalDeviceID, "Graph": dg.GGraph.String()})
khenaidoo2c6a0992019-04-29 13:46:56 -0400215 } else {
npujar1d86a522019-11-14 17:11:16 +0530216 log.Debugw("graph_routes", log.Fields{"lDeviceId": dg.logicalDeviceID, "Routes": output})
khenaidoo2c6a0992019-04-29 13:46:56 -0400217 }
khenaidoo910204f2019-04-08 17:56:40 -0400218 }
219 return nil
220}
221
npujar1d86a522019-11-14 17:11:16 +0530222// IsUpToDate returns true if device is up to date
khenaidoo4c9e5592019-09-09 16:20:41 -0400223func (dg *DeviceGraph) IsUpToDate(ld *voltha.LogicalDevice) bool {
224 if ld != nil {
225 if len(dg.boundaryPorts) != len(ld.Ports) {
226 return false
227 }
npujar1d86a522019-11-14 17:11:16 +0530228 var portID string
khenaidoo4c9e5592019-09-09 16:20:41 -0400229 var val uint32
230 var exist bool
231 for _, lp := range ld.Ports {
npujar1d86a522019-11-14 17:11:16 +0530232 portID = concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)
233 if val, exist = dg.boundaryPorts[portID]; !exist || val != lp.OfpPort.PortNo {
khenaidoo4c9e5592019-09-09 16:20:41 -0400234 return false
235 }
236 }
237 return true
238 }
239 return len(dg.boundaryPorts) == 0
240}
241
khenaidoo910204f2019-04-08 17:56:40 -0400242//getDevice returns the device either from the local cache (default) or from the model.
243//TODO: Set a cache timeout such that we do not use invalid data. The full device lifecycle should also
244//be taken in consideration
khenaidoo2c6a0992019-04-29 13:46:56 -0400245func (dg *DeviceGraph) getDevice(id string, useCache bool) (*voltha.Device, error) {
246 if useCache {
247 dg.cachedDevicesLock.RLock()
248 if d, exist := dg.cachedDevices[id]; exist {
249 dg.cachedDevicesLock.RUnlock()
250 //log.Debugw("getDevice - returned from cache", log.Fields{"deviceId": id})
251 return d, nil
252 }
khenaidoo910204f2019-04-08 17:56:40 -0400253 dg.cachedDevicesLock.RUnlock()
khenaidoo910204f2019-04-08 17:56:40 -0400254 }
khenaidoo910204f2019-04-08 17:56:40 -0400255 // Not cached
npujar1d86a522019-11-14 17:11:16 +0530256 d, err := dg.getDeviceFromModel(id)
257 if err != nil {
khenaidoo910204f2019-04-08 17:56:40 -0400258 log.Errorw("device-not-found", log.Fields{"deviceId": id, "error": err})
259 return nil, err
khenaidoo910204f2019-04-08 17:56:40 -0400260 }
npujar1d86a522019-11-14 17:11:16 +0530261 // cache it
262 dg.cachedDevicesLock.Lock()
263 dg.cachedDevices[id] = d
264 dg.cachedDevicesLock.Unlock()
265 //log.Debugw("getDevice - returned from model", log.Fields{"deviceId": id})
266 return d, nil
khenaidoo910204f2019-04-08 17:56:40 -0400267}
268
269// addDevice adds a device to a device graph and setup edges that represent the device connections to its peers
khenaidoo89b0e942018-10-21 21:11:33 -0400270func (dg *DeviceGraph) addDevice(device *voltha.Device, g goraph.Graph, devicesAdded *map[string]string, portsAdded *map[string]string,
khenaidoo910204f2019-04-08 17:56:40 -0400271 boundaryPorts map[string]uint32) goraph.Graph {
khenaidoo89b0e942018-10-21 21:11:33 -0400272
273 if device == nil {
274 return g
275 }
276
khenaidoo3d3b8c22019-05-22 18:10:39 -0400277 log.Debugw("Adding-device", log.Fields{"deviceId": device.Id, "ports": device.Ports})
278
khenaidoo910204f2019-04-08 17:56:40 -0400279 if _, exist := (*devicesAdded)[device.Id]; !exist {
280 g.AddNode(goraph.NewNode(device.Id))
281 (*devicesAdded)[device.Id] = device.Id
khenaidoo89b0e942018-10-21 21:11:33 -0400282 }
khenaidoo89b0e942018-10-21 21:11:33 -0400283
npujar1d86a522019-11-14 17:11:16 +0530284 var portID string
285 var peerPortID string
khenaidoo89b0e942018-10-21 21:11:33 -0400286 for _, port := range device.Ports {
npujar1d86a522019-11-14 17:11:16 +0530287 portID = concatDeviceIDPortID(device.Id, port.PortNo)
288 if _, exist := (*portsAdded)[portID]; !exist {
289 (*portsAdded)[portID] = portID
290 g.AddNode(goraph.NewNode(portID))
291 err := g.AddEdge(goraph.StringID(device.Id), goraph.StringID(portID), 1)
292 if err != nil {
293 log.Errorw("unable-to-add-edge", log.Fields{"error": err})
294 }
295 err = g.AddEdge(goraph.StringID(portID), goraph.StringID(device.Id), 1)
296 if err != nil {
297 log.Errorw("unable-to-add-edge", log.Fields{"error": err})
298 }
khenaidoo89b0e942018-10-21 21:11:33 -0400299 }
300 for _, peer := range port.Peers {
301 if _, exist := (*devicesAdded)[peer.DeviceId]; !exist {
khenaidoo2c6a0992019-04-29 13:46:56 -0400302 d, _ := dg.getDevice(peer.DeviceId, true)
khenaidoo89b0e942018-10-21 21:11:33 -0400303 g = dg.addDevice(d, g, devicesAdded, portsAdded, boundaryPorts)
khenaidoo89b0e942018-10-21 21:11:33 -0400304 }
npujar1d86a522019-11-14 17:11:16 +0530305 peerPortID = concatDeviceIDPortID(peer.DeviceId, peer.PortNo)
306 err := g.AddEdge(goraph.StringID(portID), goraph.StringID(peerPortID), 1)
307 if err != nil {
308 log.Errorw("unable-to-add-edge", log.Fields{"error": err})
309 }
310 err = g.AddEdge(goraph.StringID(peerPortID), goraph.StringID(portID), 1)
311 if err != nil {
312 log.Errorw("unable-to-add-edge", log.Fields{"error": err})
313 }
khenaidoo89b0e942018-10-21 21:11:33 -0400314 }
315 }
316 return g
317}
318
khenaidoo910204f2019-04-08 17:56:40 -0400319//portExist returns true if the port ID is already part of the boundary ports map.
320func (dg *DeviceGraph) portExist(id string) bool {
321 dg.boundaryPortsLock.RLock()
322 defer dg.boundaryPortsLock.RUnlock()
323 _, exist := dg.boundaryPorts[id]
khenaidoo89b0e942018-10-21 21:11:33 -0400324 return exist
325}
326
khenaidoo910204f2019-04-08 17:56:40 -0400327// buildPathsToAllRootPorts builds all the paths from the non-root logical port to all root ports
328// on the logical device
329func (dg *DeviceGraph) buildPathsToAllRootPorts(lp *voltha.LogicalPort) map[OFPortLink][]RouteHop {
330 paths := dg.Routes
npujar1d86a522019-11-14 17:11:16 +0530331 source := concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)
khenaidoo910204f2019-04-08 17:56:40 -0400332 sourcePort := lp.OfpPort.PortNo
333 ch := make(chan *ofPortLinkToPath)
334 numBuildRequest := 0
335 for target, targetPort := range dg.rootPortsString {
336 go dg.buildRoute(source, target, sourcePort, targetPort, ch)
npujar1d86a522019-11-14 17:11:16 +0530337 numBuildRequest++
khenaidoo910204f2019-04-08 17:56:40 -0400338 }
339 responseReceived := 0
340forloop:
341 for {
342 if responseReceived == numBuildRequest {
343 break
344 }
npujar1d86a522019-11-14 17:11:16 +0530345 res, ok := <-ch
346 if !ok {
347 log.Debug("channel closed")
348 break forloop
khenaidoo910204f2019-04-08 17:56:40 -0400349 }
npujar1d86a522019-11-14 17:11:16 +0530350 if res != nil && len(res.path) > 0 {
351 paths[res.link] = res.path
352 paths[OFPortLink{Ingress: res.link.Egress, Egress: res.link.Ingress}] = getReverseRoute(res.path)
353 }
354 responseReceived++
khenaidoo910204f2019-04-08 17:56:40 -0400355 }
khenaidoo89b0e942018-10-21 21:11:33 -0400356 return paths
357}
358
khenaidoo910204f2019-04-08 17:56:40 -0400359// buildPathsToAllNonRootPorts builds all the paths from the root logical port to all non-root ports
360// on the logical device
361func (dg *DeviceGraph) buildPathsToAllNonRootPorts(lp *voltha.LogicalPort) map[OFPortLink][]RouteHop {
362 paths := dg.Routes
npujar1d86a522019-11-14 17:11:16 +0530363 source := concatDeviceIDPortID(lp.DeviceId, lp.DevicePortNo)
khenaidoo910204f2019-04-08 17:56:40 -0400364 sourcePort := lp.OfpPort.PortNo
365 ch := make(chan *ofPortLinkToPath)
366 numBuildRequest := 0
367 for target, targetPort := range dg.nonRootPortsString {
368 go dg.buildRoute(source, target, sourcePort, targetPort, ch)
npujar1d86a522019-11-14 17:11:16 +0530369 numBuildRequest++
khenaidoo910204f2019-04-08 17:56:40 -0400370 }
371 responseReceived := 0
372forloop:
373 for {
374 if responseReceived == numBuildRequest {
375 break
376 }
npujar1d86a522019-11-14 17:11:16 +0530377 res, ok := <-ch
378 if !ok {
379 log.Debug("channel closed")
380 break forloop
khenaidoo910204f2019-04-08 17:56:40 -0400381 }
npujar1d86a522019-11-14 17:11:16 +0530382 if res != nil && len(res.path) > 0 {
383 paths[res.link] = res.path
384 paths[OFPortLink{Ingress: res.link.Egress, Egress: res.link.Ingress}] = getReverseRoute(res.path)
385 }
386 responseReceived++
khenaidoo910204f2019-04-08 17:56:40 -0400387 }
388 return paths
389}
390
391//buildRoute builds a route between a source and a target logical port
npujar1d86a522019-11-14 17:11:16 +0530392func (dg *DeviceGraph) buildRoute(sourceID, targetID string, sourcePort, targetPort uint32, ch chan *ofPortLinkToPath) {
khenaidoo910204f2019-04-08 17:56:40 -0400393 var pathIds []goraph.ID
394 path := make([]RouteHop, 0)
395 var err error
396 var hop RouteHop
397 var result *ofPortLinkToPath
398
npujar1d86a522019-11-14 17:11:16 +0530399 if sourceID == targetID {
khenaidoo910204f2019-04-08 17:56:40 -0400400 ch <- result
401 return
402 }
403 //Ignore Root - Root Routes
404 if dg.IsRootPort(sourcePort) && dg.IsRootPort(targetPort) {
405 ch <- result
406 return
407 }
408
409 //Ignore non-Root - non-Root Routes
410 if !dg.IsRootPort(sourcePort) && !dg.IsRootPort(targetPort) {
411 ch <- result
412 return
413 }
414
npujar1d86a522019-11-14 17:11:16 +0530415 if pathIds, _, err = goraph.Dijkstra(dg.GGraph, goraph.StringID(sourceID), goraph.StringID(targetID)); err != nil {
416 log.Errorw("no-path", log.Fields{"sourceId": sourceID, "targetId": targetID, "error": err})
khenaidoo910204f2019-04-08 17:56:40 -0400417 ch <- result
418 return
419 }
420 if len(pathIds)%3 != 0 {
421 ch <- result
422 return
423 }
npujar1d86a522019-11-14 17:11:16 +0530424 var deviceID string
khenaidoo910204f2019-04-08 17:56:40 -0400425 var ingressPort uint32
426 var egressPort uint32
427 for i := 0; i < len(pathIds); i = i + 3 {
npujar1d86a522019-11-14 17:11:16 +0530428 if deviceID, ingressPort, err = splitIntoDeviceIDPortID(pathIds[i].String()); err != nil {
429 log.Errorw("id-error", log.Fields{"sourceId": sourceID, "targetId": targetID, "error": err})
khenaidoo910204f2019-04-08 17:56:40 -0400430 break
431 }
npujar1d86a522019-11-14 17:11:16 +0530432 if _, egressPort, err = splitIntoDeviceIDPortID(pathIds[i+2].String()); err != nil {
433 log.Errorw("id-error", log.Fields{"sourceId": sourceID, "targetId": targetID, "error": err})
khenaidoo910204f2019-04-08 17:56:40 -0400434 break
435 }
npujar1d86a522019-11-14 17:11:16 +0530436 hop = RouteHop{Ingress: ingressPort, DeviceID: deviceID, Egress: egressPort}
khenaidoo910204f2019-04-08 17:56:40 -0400437 path = append(path, hop)
438 }
439 result = &ofPortLinkToPath{link: OFPortLink{Ingress: sourcePort, Egress: targetPort}, path: path}
440 ch <- result
441}
442
443//buildRoutes build all routes between all the ports on the logical device
444func (dg *DeviceGraph) buildRoutes() map[OFPortLink][]RouteHop {
445 paths := make(map[OFPortLink][]RouteHop)
446 ch := make(chan *ofPortLinkToPath)
447 numBuildRequest := 0
448 for source, sourcePort := range dg.boundaryPorts {
449 for target, targetPort := range dg.boundaryPorts {
450 go dg.buildRoute(source, target, sourcePort, targetPort, ch)
npujar1d86a522019-11-14 17:11:16 +0530451 numBuildRequest++
khenaidoo89b0e942018-10-21 21:11:33 -0400452 }
453 }
khenaidoo910204f2019-04-08 17:56:40 -0400454 responseReceived := 0
455forloop:
456 for {
457 if responseReceived == numBuildRequest {
458 break
459 }
npujar1d86a522019-11-14 17:11:16 +0530460 res, ok := <-ch
461 if !ok {
462 log.Debug("channel closed")
463 break forloop
khenaidoo910204f2019-04-08 17:56:40 -0400464 }
npujar1d86a522019-11-14 17:11:16 +0530465 if res != nil && len(res.path) > 0 {
466 paths[res.link] = res.path
467 }
468 responseReceived++
khenaidoo910204f2019-04-08 17:56:40 -0400469 }
470 return paths
471}
472
473// reset cleans up the device graph
474func (dg *DeviceGraph) reset() {
475 dg.devicesAdded = make(map[string]string)
476 dg.portsAdded = make(map[string]string)
477 dg.rootPortsString = make(map[string]uint32)
478 dg.nonRootPortsString = make(map[string]uint32)
479 dg.RootPorts = make(map[uint32]uint32)
480 dg.boundaryPorts = make(map[string]uint32)
481 dg.Routes = make(map[OFPortLink][]RouteHop)
482 dg.cachedDevices = make(map[string]*voltha.Device)
483}
484
485//concatDeviceIdPortId formats a portid using the device id and the port number
npujar1d86a522019-11-14 17:11:16 +0530486func concatDeviceIDPortID(deviceID string, portNo uint32) string {
487 return fmt.Sprintf("%s:%d", deviceID, portNo)
khenaidoo910204f2019-04-08 17:56:40 -0400488}
489
490// splitIntoDeviceIdPortId extracts the device id and port number from the portId
npujar1d86a522019-11-14 17:11:16 +0530491func splitIntoDeviceIDPortID(id string) (string, uint32, error) {
khenaidoo910204f2019-04-08 17:56:40 -0400492 result := strings.Split(id, ":")
493 if len(result) != 2 {
npujar1d86a522019-11-14 17:11:16 +0530494 return "", 0, fmt.Errorf("invalid-id-%s", id)
khenaidoo910204f2019-04-08 17:56:40 -0400495 }
npujar1d86a522019-11-14 17:11:16 +0530496 temp, err := strconv.ParseInt(result[1], 10, 32)
497 if err != nil {
498 return "", 0, fmt.Errorf("invalid-id-%s-%s", id, err.Error())
khenaidoo910204f2019-04-08 17:56:40 -0400499 }
npujar1d86a522019-11-14 17:11:16 +0530500 return result[0], uint32(temp), nil
khenaidoo910204f2019-04-08 17:56:40 -0400501}
502
khenaidoocfe03b92019-06-03 20:06:31 -0400503//getReverseRoute returns the reverse of the route
khenaidoo910204f2019-04-08 17:56:40 -0400504func getReverseRoute(route []RouteHop) []RouteHop {
505 reverse := make([]RouteHop, len(route))
khenaidoocfe03b92019-06-03 20:06:31 -0400506 for i, j := 0, len(route)-1; j >= 0; i, j = i+1, j-1 {
507 reverse[i].DeviceID, reverse[i].Ingress, reverse[i].Egress = route[j].DeviceID, route[j].Egress, route[j].Ingress
khenaidoo910204f2019-04-08 17:56:40 -0400508 }
509 return reverse
khenaidoo89b0e942018-10-21 21:11:33 -0400510}