blob: d7192da65b97f7738122d43ea8437b0b0761ac66 [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 (
20 "errors"
21 "fmt"
22 "github.com/gyuho/goraph"
23 "github.com/opencord/voltha-go/common/log"
24 "github.com/opencord/voltha-go/protos/voltha"
25 "strconv"
26 "strings"
Stephane Barbariec53a2752019-03-08 17:50:10 -050027 "sync"
khenaidoo89b0e942018-10-21 21:11:33 -040028)
29
30func init() {
31 log.AddPackage(log.JSON, log.DebugLevel, nil)
32}
33
34type RouteHop struct {
35 DeviceID string
36 Ingress uint32
37 Egress uint32
38}
39
40type OFPortLink struct {
41 Ingress uint32
42 Egress uint32
43}
44
45type GetDeviceFunc func(id string) (*voltha.Device, error)
46
47func concatDeviceIdPortId(deviceId string, portId uint32) string {
48 return fmt.Sprintf("%s:%d", deviceId, portId)
49}
50
51func splitIntoDeviceIdPortId(id string) (string, uint32, error) {
52 result := strings.Split(id, ":")
53 if len(result) != 2 {
54 return "", 0, errors.New(fmt.Sprintf("invalid-id-%s", id))
55 }
56 if temp, err := strconv.ParseInt(result[1], 10, 32); err != nil {
57 return "", 0, errors.New(fmt.Sprintf("invalid-id-%s-%s", id, err.Error()))
58 } else {
59 return result[0], uint32(temp), nil
60 }
61}
62
63type DeviceGraph struct {
64 GGraph goraph.Graph
65 getDevice GetDeviceFunc
66 logicalPorts []*voltha.LogicalPort
67 RootPorts map[uint32]uint32
68 Routes map[OFPortLink][]RouteHop
Stephane Barbariec53a2752019-03-08 17:50:10 -050069 boundaryPorts sync.Map
khenaidoo89b0e942018-10-21 21:11:33 -040070}
71
72func NewDeviceGraph(getDevice GetDeviceFunc) *DeviceGraph {
73 var dg DeviceGraph
74 dg.GGraph = goraph.NewGraph()
75 dg.getDevice = getDevice
76 return &dg
77}
78
79func (dg *DeviceGraph) ComputeRoutes(lps []*voltha.LogicalPort) {
Stephane Barbariec53a2752019-03-08 17:50:10 -050080 if dg == nil {
81 return
82 }
khenaidoo89b0e942018-10-21 21:11:33 -040083 dg.logicalPorts = lps
84 // Set the root ports
85 dg.RootPorts = make(map[uint32]uint32)
86 for _, lp := range lps {
87 if lp.RootPort {
88 dg.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
89 }
90 }
91 // set the boundary ports
Stephane Barbariec53a2752019-03-08 17:50:10 -050092 dg.boundaryPorts.Range(func(key interface{}, value interface{}) bool {
93 dg.boundaryPorts.Delete(key)
94 return true
95 })
96 //dg.boundaryPorts = sync.Map{}
97
khenaidoo89b0e942018-10-21 21:11:33 -040098 for _, lp := range lps {
Stephane Barbariec53a2752019-03-08 17:50:10 -050099 dg.boundaryPorts.Store(fmt.Sprintf("%s:%d", lp.DeviceId, lp.DevicePortNo), lp.OfpPort.PortNo)
khenaidoo89b0e942018-10-21 21:11:33 -0400100 }
101 dg.Routes = make(map[OFPortLink][]RouteHop)
102
103 // Build the graph
104 var device *voltha.Device
105 devicesAdded := make(map[string]string)
106 portsAdded := make(map[string]string)
107 for _, logicalPort := range dg.logicalPorts {
108 device, _ = dg.getDevice(logicalPort.DeviceId)
109 dg.GGraph = dg.addDevice(device, dg.GGraph, &devicesAdded, &portsAdded, dg.boundaryPorts)
110 }
111 dg.Routes = dg.buildRoutes()
112}
113
114func (dg *DeviceGraph) addDevice(device *voltha.Device, g goraph.Graph, devicesAdded *map[string]string, portsAdded *map[string]string,
Stephane Barbariec53a2752019-03-08 17:50:10 -0500115 boundaryPorts sync.Map) goraph.Graph {
khenaidoo89b0e942018-10-21 21:11:33 -0400116
117 if device == nil {
118 return g
119 }
120
121 if _, exist := (*devicesAdded)[device.Id]; exist {
122 return g
123 }
124 g.AddNode(goraph.NewNode(device.Id))
125 (*devicesAdded)[device.Id] = device.Id
126
127 var portId string
128 var peerPortId string
129 for _, port := range device.Ports {
130 portId = concatDeviceIdPortId(device.Id, port.PortNo)
131 if _, exist := (*portsAdded)[portId]; !exist {
132 (*portsAdded)[portId] = portId
133 g.AddNode(goraph.NewNode(portId))
134 g.AddEdge(goraph.StringID(device.Id), goraph.StringID(portId), 1)
135 g.AddEdge(goraph.StringID(portId), goraph.StringID(device.Id), 1)
136 }
137 for _, peer := range port.Peers {
138 if _, exist := (*devicesAdded)[peer.DeviceId]; !exist {
139 d, _ := dg.getDevice(peer.DeviceId)
140 g = dg.addDevice(d, g, devicesAdded, portsAdded, boundaryPorts)
141 } else {
142 peerPortId = concatDeviceIdPortId(peer.DeviceId, peer.PortNo)
143 g.AddEdge(goraph.StringID(portId), goraph.StringID(peerPortId), 1)
144 g.AddEdge(goraph.StringID(peerPortId), goraph.StringID(portId), 1)
145 }
146 }
147 }
148 return g
149}
150
151func (dg *DeviceGraph) IsRootPort(port uint32) bool {
152 _, exist := dg.RootPorts[port]
153 return exist
154}
155
156func (dg *DeviceGraph) buildRoutes() map[OFPortLink][]RouteHop {
157 var pathIds []goraph.ID
158 path := make([]RouteHop, 0)
159 paths := make(map[OFPortLink][]RouteHop)
160 var err error
161 var hop RouteHop
Stephane Barbariec53a2752019-03-08 17:50:10 -0500162
163 dg.boundaryPorts.Range(func(src, srcPort interface{}) bool {
164 source := src.(string)
165 sourcePort := srcPort.(uint32)
166
167 dg.boundaryPorts.Range(func(dst, dstPort interface{}) bool {
168 target := dst.(string)
169 targetPort := dstPort.(uint32)
170
khenaidoo89b0e942018-10-21 21:11:33 -0400171 if source == target {
Stephane Barbariec53a2752019-03-08 17:50:10 -0500172 return true
khenaidoo89b0e942018-10-21 21:11:33 -0400173 }
174 //Ignore NNI - NNI Routes
175 if dg.IsRootPort(sourcePort) && dg.IsRootPort(targetPort) {
Stephane Barbariec53a2752019-03-08 17:50:10 -0500176 return true
khenaidoo89b0e942018-10-21 21:11:33 -0400177 }
178
179 //Ignore UNI - UNI Routes
180 if !dg.IsRootPort(sourcePort) && !dg.IsRootPort(targetPort) {
Stephane Barbariec53a2752019-03-08 17:50:10 -0500181 return true
khenaidoo89b0e942018-10-21 21:11:33 -0400182 }
183
184 if pathIds, _, err = goraph.Dijkstra(dg.GGraph, goraph.StringID(source), goraph.StringID(target)); err != nil {
185 log.Errorw("no-path", log.Fields{"source": source, "target": target, "error": err})
Stephane Barbariec53a2752019-03-08 17:50:10 -0500186 return true
khenaidoo89b0e942018-10-21 21:11:33 -0400187 }
188 if len(pathIds)%3 != 0 {
Stephane Barbariec53a2752019-03-08 17:50:10 -0500189 return true
khenaidoo89b0e942018-10-21 21:11:33 -0400190 }
191 var deviceId string
192 var ingressPort uint32
193 var egressPort uint32
194 for i := 0; i < len(pathIds); i = i + 3 {
195 if deviceId, ingressPort, err = splitIntoDeviceIdPortId(pathIds[i].String()); err != nil {
196 log.Errorw("id-error", log.Fields{"source": source, "target": target, "error": err})
197 break
198 }
199 if _, egressPort, err = splitIntoDeviceIdPortId(pathIds[i+2].String()); err != nil {
200 log.Errorw("id-error", log.Fields{"source": source, "target": target, "error": err})
201 break
202 }
203 hop = RouteHop{Ingress: ingressPort, DeviceID: deviceId, Egress: egressPort}
204 path = append(path, hop)
205 }
206 tmp := make([]RouteHop, len(path))
207 copy(tmp, path)
208 path = nil
209 paths[OFPortLink{Ingress: sourcePort, Egress: targetPort}] = tmp
Stephane Barbariec53a2752019-03-08 17:50:10 -0500210 return true
211 })
212 return true
213 })
khenaidoo89b0e942018-10-21 21:11:33 -0400214 return paths
215}
216
217func (dg *DeviceGraph) GetDeviceNodeIds() map[string]string {
218 nodeIds := make(map[string]string)
219 nodesMap := dg.GGraph.GetNodes()
220 for id, node := range nodesMap {
221 if len(strings.Split(node.String(), ":")) != 2 { // not port node
222 nodeIds[id.String()] = id.String()
223 }
224 }
225 return nodeIds
226}