blob: 0ed2748ca2b318520e262fba85921f9edb0a9996 [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"
William Kurkiandaa6bb22019-03-07 12:26:28 -050024 "github.com/opencord/voltha-protos/go/voltha"
khenaidoo89b0e942018-10-21 21:11:33 -040025 "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
khenaidoo1ce37ad2019-03-24 22:07:24 -040069 graphBuildLock sync.RWMutex
Stephane Barbariec53a2752019-03-08 17:50:10 -050070 boundaryPorts sync.Map
khenaidoo89b0e942018-10-21 21:11:33 -040071}
72
73func NewDeviceGraph(getDevice GetDeviceFunc) *DeviceGraph {
74 var dg DeviceGraph
75 dg.GGraph = goraph.NewGraph()
76 dg.getDevice = getDevice
khenaidoo1ce37ad2019-03-24 22:07:24 -040077 dg.graphBuildLock = sync.RWMutex{}
khenaidoo89b0e942018-10-21 21:11:33 -040078 return &dg
79}
80
81func (dg *DeviceGraph) ComputeRoutes(lps []*voltha.LogicalPort) {
Stephane Barbariec53a2752019-03-08 17:50:10 -050082 if dg == nil {
83 return
84 }
khenaidoo1ce37ad2019-03-24 22:07:24 -040085 dg.graphBuildLock.Lock()
86 defer dg.graphBuildLock.Unlock()
khenaidoo89b0e942018-10-21 21:11:33 -040087 dg.logicalPorts = lps
88 // Set the root ports
89 dg.RootPorts = make(map[uint32]uint32)
90 for _, lp := range lps {
91 if lp.RootPort {
92 dg.RootPorts[lp.OfpPort.PortNo] = lp.OfpPort.PortNo
93 }
94 }
95 // set the boundary ports
Stephane Barbariec53a2752019-03-08 17:50:10 -050096 dg.boundaryPorts.Range(func(key interface{}, value interface{}) bool {
97 dg.boundaryPorts.Delete(key)
98 return true
99 })
100 //dg.boundaryPorts = sync.Map{}
101
khenaidoo89b0e942018-10-21 21:11:33 -0400102 for _, lp := range lps {
Stephane Barbariec53a2752019-03-08 17:50:10 -0500103 dg.boundaryPorts.Store(fmt.Sprintf("%s:%d", lp.DeviceId, lp.DevicePortNo), lp.OfpPort.PortNo)
khenaidoo89b0e942018-10-21 21:11:33 -0400104 }
105 dg.Routes = make(map[OFPortLink][]RouteHop)
106
107 // Build the graph
108 var device *voltha.Device
109 devicesAdded := make(map[string]string)
110 portsAdded := make(map[string]string)
111 for _, logicalPort := range dg.logicalPorts {
112 device, _ = dg.getDevice(logicalPort.DeviceId)
113 dg.GGraph = dg.addDevice(device, dg.GGraph, &devicesAdded, &portsAdded, dg.boundaryPorts)
114 }
115 dg.Routes = dg.buildRoutes()
116}
117
118func (dg *DeviceGraph) addDevice(device *voltha.Device, g goraph.Graph, devicesAdded *map[string]string, portsAdded *map[string]string,
Stephane Barbariec53a2752019-03-08 17:50:10 -0500119 boundaryPorts sync.Map) goraph.Graph {
khenaidoo89b0e942018-10-21 21:11:33 -0400120
121 if device == nil {
122 return g
123 }
124
125 if _, exist := (*devicesAdded)[device.Id]; exist {
126 return g
127 }
128 g.AddNode(goraph.NewNode(device.Id))
129 (*devicesAdded)[device.Id] = device.Id
130
131 var portId string
132 var peerPortId string
133 for _, port := range device.Ports {
134 portId = concatDeviceIdPortId(device.Id, port.PortNo)
135 if _, exist := (*portsAdded)[portId]; !exist {
136 (*portsAdded)[portId] = portId
137 g.AddNode(goraph.NewNode(portId))
138 g.AddEdge(goraph.StringID(device.Id), goraph.StringID(portId), 1)
139 g.AddEdge(goraph.StringID(portId), goraph.StringID(device.Id), 1)
140 }
141 for _, peer := range port.Peers {
142 if _, exist := (*devicesAdded)[peer.DeviceId]; !exist {
143 d, _ := dg.getDevice(peer.DeviceId)
144 g = dg.addDevice(d, g, devicesAdded, portsAdded, boundaryPorts)
145 } else {
146 peerPortId = concatDeviceIdPortId(peer.DeviceId, peer.PortNo)
147 g.AddEdge(goraph.StringID(portId), goraph.StringID(peerPortId), 1)
148 g.AddEdge(goraph.StringID(peerPortId), goraph.StringID(portId), 1)
149 }
150 }
151 }
152 return g
153}
154
155func (dg *DeviceGraph) IsRootPort(port uint32) bool {
156 _, exist := dg.RootPorts[port]
157 return exist
158}
159
160func (dg *DeviceGraph) buildRoutes() map[OFPortLink][]RouteHop {
161 var pathIds []goraph.ID
162 path := make([]RouteHop, 0)
163 paths := make(map[OFPortLink][]RouteHop)
164 var err error
165 var hop RouteHop
Stephane Barbariec53a2752019-03-08 17:50:10 -0500166
167 dg.boundaryPorts.Range(func(src, srcPort interface{}) bool {
168 source := src.(string)
169 sourcePort := srcPort.(uint32)
170
171 dg.boundaryPorts.Range(func(dst, dstPort interface{}) bool {
172 target := dst.(string)
173 targetPort := dstPort.(uint32)
174
khenaidoo89b0e942018-10-21 21:11:33 -0400175 if source == target {
Stephane Barbariec53a2752019-03-08 17:50:10 -0500176 return true
khenaidoo89b0e942018-10-21 21:11:33 -0400177 }
178 //Ignore NNI - NNI Routes
179 if dg.IsRootPort(sourcePort) && dg.IsRootPort(targetPort) {
Stephane Barbariec53a2752019-03-08 17:50:10 -0500180 return true
khenaidoo89b0e942018-10-21 21:11:33 -0400181 }
182
183 //Ignore UNI - UNI Routes
184 if !dg.IsRootPort(sourcePort) && !dg.IsRootPort(targetPort) {
Stephane Barbariec53a2752019-03-08 17:50:10 -0500185 return true
khenaidoo89b0e942018-10-21 21:11:33 -0400186 }
187
188 if pathIds, _, err = goraph.Dijkstra(dg.GGraph, goraph.StringID(source), goraph.StringID(target)); err != nil {
189 log.Errorw("no-path", log.Fields{"source": source, "target": target, "error": err})
Stephane Barbariec53a2752019-03-08 17:50:10 -0500190 return true
khenaidoo89b0e942018-10-21 21:11:33 -0400191 }
192 if len(pathIds)%3 != 0 {
Stephane Barbariec53a2752019-03-08 17:50:10 -0500193 return true
khenaidoo89b0e942018-10-21 21:11:33 -0400194 }
195 var deviceId string
196 var ingressPort uint32
197 var egressPort uint32
198 for i := 0; i < len(pathIds); i = i + 3 {
199 if deviceId, ingressPort, err = splitIntoDeviceIdPortId(pathIds[i].String()); err != nil {
200 log.Errorw("id-error", log.Fields{"source": source, "target": target, "error": err})
201 break
202 }
203 if _, egressPort, err = splitIntoDeviceIdPortId(pathIds[i+2].String()); err != nil {
204 log.Errorw("id-error", log.Fields{"source": source, "target": target, "error": err})
205 break
206 }
207 hop = RouteHop{Ingress: ingressPort, DeviceID: deviceId, Egress: egressPort}
208 path = append(path, hop)
209 }
210 tmp := make([]RouteHop, len(path))
211 copy(tmp, path)
212 path = nil
213 paths[OFPortLink{Ingress: sourcePort, Egress: targetPort}] = tmp
Stephane Barbariec53a2752019-03-08 17:50:10 -0500214 return true
215 })
216 return true
217 })
khenaidoo89b0e942018-10-21 21:11:33 -0400218 return paths
219}
220
221func (dg *DeviceGraph) GetDeviceNodeIds() map[string]string {
khenaidoo1ce37ad2019-03-24 22:07:24 -0400222 dg.graphBuildLock.RLock()
223 defer dg.graphBuildLock.RUnlock()
khenaidoo89b0e942018-10-21 21:11:33 -0400224 nodeIds := make(map[string]string)
225 nodesMap := dg.GGraph.GetNodes()
226 for id, node := range nodesMap {
227 if len(strings.Split(node.String(), ":")) != 2 { // not port node
228 nodeIds[id.String()] = id.String()
229 }
230 }
231 return nodeIds
232}