blob: 1f90ecde960b4f5cc4a1b2921357e02f8cd3e9e2 [file] [log] [blame]
khenaidoo820197c2020-02-13 16:35:33 -05001/*
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 */
16package route
17
18import (
19 "context"
20 "errors"
21 "fmt"
22 "github.com/opencord/voltha-protos/v3/go/openflow_13"
23 "github.com/opencord/voltha-protos/v3/go/voltha"
24 "github.com/stretchr/testify/assert"
25 "math/rand"
26 "reflect"
27 "strings"
28 "sync"
29 "testing"
30 "time"
31)
32
33const (
34 logicalDeviceID = "ld"
35 oltDeviceID = "olt"
36)
37
38//portRegistration is a message sent from an OLT device to a logical device to create a logical port
39type portRegistration struct {
40 port *voltha.Port
41 rootPort bool
42}
43
44//onuRegistration is a message sent from an ONU device to an OLT device to register an ONU
45type onuRegistration struct {
46 onu *voltha.Device
47 oltPonNo uint32
48 onuPonNo uint32
49}
50
51type logicalDeviceManager struct {
52 logicalDevice *voltha.LogicalDevice
53 deviceRoutes *DeviceRoutes
54 ldChnl chan portRegistration
55 numLogicalPorts int
56 done chan struct{}
57}
58
59func newLogicalDeviceManager(ld *voltha.LogicalDevice, ch chan portRegistration, totalLogicalPorts int, done chan struct{}) *logicalDeviceManager {
60 return &logicalDeviceManager{
61 logicalDevice: ld,
62 ldChnl: ch,
63 numLogicalPorts: totalLogicalPorts,
64 done: done,
65 }
66}
67
68func (ldM *logicalDeviceManager) start(getDevice GetDeviceFunc, buildRoutes bool) {
69 ldM.deviceRoutes = NewDeviceRoutes(ldM.logicalDevice.Id, getDevice)
70 ofpPortNo := uint32(1)
71 for portReg := range ldM.ldChnl {
72 if portReg.port == nil {
73 // End of registration - exit loop
74 break
75 }
76 lp := &voltha.LogicalPort{
77 Id: portReg.port.Label,
78 OfpPort: &openflow_13.OfpPort{PortNo: ofpPortNo},
79 DeviceId: portReg.port.DeviceId,
80 DevicePortNo: portReg.port.PortNo,
81 RootPort: portReg.rootPort,
82 }
83 ldM.logicalDevice.Ports = append(ldM.logicalDevice.Ports, lp)
84 if buildRoutes {
85 err := ldM.deviceRoutes.AddPort(context.Background(), lp, ldM.logicalDevice.Ports)
86 if err != nil && !strings.Contains(err.Error(), "code = FailedPrecondition") {
87 fmt.Println("(Error when adding port:", lp, len(ldM.logicalDevice.Ports), err)
88 }
89 }
90 ofpPortNo++
91 }
92 // Inform the caller we are now done
93 ldM.done <- struct{}{}
94}
95
96type oltManager struct {
97 olt *voltha.Device
98 logicalDeviceMgr *logicalDeviceManager
99 numNNIPort int
100 numPonPortOnOlt int
101 oltChnl chan onuRegistration
102}
103
104func newOltManager(oltDeviceID string, ldMgr *logicalDeviceManager, numNNIPort int, numPonPortOnOlt int, ch chan onuRegistration) *oltManager {
105 return &oltManager{
106 olt: &voltha.Device{Id: oltDeviceID, ParentId: ldMgr.logicalDevice.Id, Root: true},
107 logicalDeviceMgr: ldMgr,
108 numNNIPort: numNNIPort,
109 numPonPortOnOlt: numPonPortOnOlt,
110 oltChnl: ch,
111 }
112}
113
114func (oltM *oltManager) start() {
115 oltM.olt.Ports = make([]*voltha.Port, 0)
116 // Setup the OLT nni ports and trigger the nni ports creation
117 for nniPort := 1; nniPort < oltM.numNNIPort+1; nniPort++ {
118 p := &voltha.Port{Label: fmt.Sprintf("nni-%d", nniPort), PortNo: uint32(nniPort), DeviceId: oltM.olt.Id, Type: voltha.Port_ETHERNET_NNI}
119 oltM.olt.Ports = append(oltM.olt.Ports, p)
120 oltM.logicalDeviceMgr.ldChnl <- portRegistration{port: p, rootPort: true}
121 }
122
123 // Create OLT pon ports
124 for ponPort := oltM.numNNIPort + 1; ponPort < oltM.numPonPortOnOlt+oltM.numNNIPort+1; ponPort++ {
125 p := voltha.Port{PortNo: uint32(ponPort), DeviceId: oltM.olt.Id, Type: voltha.Port_PON_OLT}
126 oltM.olt.Ports = append(oltM.olt.Ports, &p)
127 }
128
129 // Wait for onu registration
130 for onuReg := range oltM.oltChnl {
131 if onuReg.onu == nil {
132 // All onu has registered - exit the loop
133 break
134 }
135 oltM.registerOnu(onuReg.onu, onuReg.oltPonNo, onuReg.onuPonNo)
136 }
137 // Inform the logical device manager we are done
138 oltM.logicalDeviceMgr.ldChnl <- portRegistration{port: nil}
139}
140
141func (oltM *oltManager) registerOnu(onu *voltha.Device, oltPonNo uint32, onuPonNo uint32) {
142 // Update the olt pon peers
143 for _, port := range oltM.olt.Ports {
144 if port.Type == voltha.Port_PON_OLT && port.PortNo == oltPonNo {
145 port.Peers = append(port.Peers, &voltha.Port_PeerPort{DeviceId: onu.Id, PortNo: onuPonNo})
146 }
147 }
148 // For each uni port on the ONU trigger the creation of a logical port
149 for _, port := range onu.Ports {
150 if port.Type == voltha.Port_ETHERNET_UNI {
151 oltM.logicalDeviceMgr.ldChnl <- portRegistration{port: port, rootPort: false}
152 }
153 }
154}
155
156type onuManager struct {
157 oltMgr *oltManager
158 numOnus int
159 numUnisPerOnu int
160 startingUniPortNo int
161 numGetDeviceInvoked int
162 numGetDeviceInvokedLock sync.RWMutex
163 deviceLock sync.RWMutex
164 onus []*voltha.Device
165}
166
167func newOnuManager(oltMgr *oltManager, numOnus int, numUnisPerOnu int, startingUniPortNo int) *onuManager {
168 return &onuManager{
169 oltMgr: oltMgr,
170 numOnus: numOnus,
171 numUnisPerOnu: numUnisPerOnu,
172 startingUniPortNo: startingUniPortNo,
173 onus: make([]*voltha.Device, 0),
174 }
175}
176
177func (onuM *onuManager) start(startingOltPeerPortNo int, numPonPortOnOlt int) {
178 var wg sync.WaitGroup
179 for oltPonNo := startingOltPeerPortNo; oltPonNo < startingOltPeerPortNo+numPonPortOnOlt; oltPonNo++ {
180 for i := 0; i < onuM.numOnus; i++ {
181 wg.Add(1)
182 go func(idx int, oltPonNum int) {
183 var onu *voltha.Device
184 defer wg.Done()
185 id := fmt.Sprintf("%d-onu-%d", oltPonNum, idx)
186 onu = &voltha.Device{Id: id, ParentId: onuM.oltMgr.olt.Id, ParentPortNo: uint32(oltPonNum)}
187 ponPort := &voltha.Port{Label: fmt.Sprintf("%s:pon-%d", onu.Id, idx), PortNo: 1, DeviceId: onu.Id, Type: voltha.Port_PON_ONU}
188 ponPort.Peers = make([]*voltha.Port_PeerPort, 0)
189 peerPort := voltha.Port_PeerPort{DeviceId: onuM.oltMgr.olt.Id, PortNo: uint32(oltPonNum)}
190 ponPort.Peers = append(ponPort.Peers, &peerPort)
191 onu.Ports = make([]*voltha.Port, 0)
192 onu.Ports = append(onu.Ports, ponPort)
193 for j := onuM.startingUniPortNo; j < onuM.numUnisPerOnu+onuM.startingUniPortNo; j++ {
194 uniPort := &voltha.Port{Label: fmt.Sprintf("%s:uni-%d", onu.Id, j), PortNo: uint32(j), DeviceId: onu.Id, Type: voltha.Port_ETHERNET_UNI}
195 onu.Ports = append(onu.Ports, uniPort)
196 }
197 onuM.deviceLock.Lock()
198 onuM.onus = append(onuM.onus, onu)
199 onuM.deviceLock.Unlock()
200 onuM.oltMgr.oltChnl <- onuRegistration{
201 onu: onu,
202 oltPonNo: uint32(oltPonNum),
203 onuPonNo: 1,
204 }
205 }(i, oltPonNo)
206 }
207 }
208 wg.Wait()
209 //send an empty device to indicate the end of onu registration
210 onuM.oltMgr.oltChnl <- onuRegistration{
211 onu: nil,
212 oltPonNo: 0,
213 onuPonNo: 1,
214 }
215}
216
217func (onuM *onuManager) getOnu(deviceID string) *voltha.Device {
218 onuM.deviceLock.Lock()
219 defer onuM.deviceLock.Unlock()
220 for _, onu := range onuM.onus {
221 if onu.Id == deviceID {
222 return onu
223 }
224 }
225 return nil
226}
227
228func (onuM *onuManager) GetDeviceHelper(_ context.Context, id string) (*voltha.Device, error) {
229 onuM.numGetDeviceInvokedLock.Lock()
230 onuM.numGetDeviceInvoked++
231 onuM.numGetDeviceInvokedLock.Unlock()
232 if id == oltDeviceID {
233 return onuM.oltMgr.olt, nil
234 }
235 if onu := onuM.getOnu(id); onu != nil {
236 return onu, nil
237 }
238 return nil, errors.New("not-found")
239}
240
241func TestDeviceRoutes_ComputeRoutes(t *testing.T) {
242 numNNIPort := 2
243 numPonPortOnOlt := 8
244 numOnuPerOltPonPort := 32
245 numUniPerOnu := 4
246 done := make(chan struct{})
247
248 fmt.Println(fmt.Sprintf("Test: Computing all routes. LogicalPorts:%d, NNI:%d, Pon/OLT:%d, ONU/Pon:%d, Uni/Onu:%d",
249 numNNIPort*numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu, numNNIPort, numPonPortOnOlt, numOnuPerOltPonPort, numUniPerOnu))
250
251 // Create all the devices and logical device before computing the routes in one go
252 ld := &voltha.LogicalDevice{Id: logicalDeviceID}
253 ldMgrChnl := make(chan portRegistration, numNNIPort*numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu)
254 ldMgr := newLogicalDeviceManager(ld, ldMgrChnl, numNNIPort+numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu, done)
255 oltMgrChnl := make(chan onuRegistration, numPonPortOnOlt*numOnuPerOltPonPort)
256 oltMgr := newOltManager(oltDeviceID, ldMgr, numNNIPort, numPonPortOnOlt, oltMgrChnl)
257 onuMgr := newOnuManager(oltMgr, numOnuPerOltPonPort, numUniPerOnu, 2)
258 getDevice := onuMgr.GetDeviceHelper
259 // Start the managers. Only the devices are created. No routes will be built.
260 go ldMgr.start(getDevice, false)
261 go oltMgr.start()
262 go onuMgr.start(numNNIPort+1, numPonPortOnOlt)
263
264 // Wait for all the devices to be created
265 <-done
266 close(oltMgrChnl)
267 close(ldMgrChnl)
268
269 // Computes the routes
270 start := time.Now()
271 err := ldMgr.deviceRoutes.ComputeRoutes(context.TODO(), ldMgr.logicalDevice.Ports)
272 assert.Nil(t, err)
273
274 // Validate the routes are up to date
275 assert.True(t, ldMgr.deviceRoutes.IsUpToDate(ld))
276
277 // Validate the expected number of routes
278 assert.EqualValues(t, 2*numNNIPort*numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu, len(ldMgr.deviceRoutes.Routes))
279
280 // Validate the root ports
281 for _, port := range ldMgr.logicalDevice.Ports {
282 assert.Equal(t, port.RootPort, ldMgr.deviceRoutes.IsRootPort(port.OfpPort.PortNo))
283 }
284 fmt.Println(fmt.Sprintf("Total Time:%dms, Total Routes:%d NumGetDeviceInvoked:%d", time.Since(start)/time.Millisecond, len(ldMgr.deviceRoutes.Routes), onuMgr.numGetDeviceInvoked))
285}
286
287func TestDeviceRoutes_AddPort(t *testing.T) {
288 numNNIPort := 2
289 numPonPortOnOlt := 8
290 numOnuPerOltPonPort := 32
291 numUniPerOnu := 4
292 done := make(chan struct{})
293
294 fmt.Println(fmt.Sprintf("Test: Computing all routes. LogicalPorts:%d, NNI:%d, Pon/OLT:%d, ONU/Pon:%d, Uni/Onu:%d",
295 numNNIPort*numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu, numNNIPort, numPonPortOnOlt, numOnuPerOltPonPort, numUniPerOnu))
296
297 start := time.Now()
298 // Create all the devices and logical device before computing the routes in one go
299 ld := &voltha.LogicalDevice{Id: logicalDeviceID}
300 ldMgrChnl := make(chan portRegistration, numNNIPort*numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu)
301 ldMgr := newLogicalDeviceManager(ld, ldMgrChnl, numNNIPort+numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu, done)
302 oltMgrChnl := make(chan onuRegistration, numPonPortOnOlt*numOnuPerOltPonPort)
303 oltMgr := newOltManager(oltDeviceID, ldMgr, numNNIPort, numPonPortOnOlt, oltMgrChnl)
304 onuMgr := newOnuManager(oltMgr, numOnuPerOltPonPort, numUniPerOnu, 2)
305 getDevice := onuMgr.GetDeviceHelper
306 // Start the managers and trigger the routes to be built as the logical ports become available
307 go ldMgr.start(getDevice, true)
308 go oltMgr.start()
309 go onuMgr.start(numNNIPort+1, numPonPortOnOlt)
310
311 // Wait for all the devices to be created and routes created
312 <-done
313 close(oltMgrChnl)
314 close(ldMgrChnl)
315
316 ldMgr.deviceRoutes.Print()
317
318 // Validate the routes are up to date
319 assert.True(t, ldMgr.deviceRoutes.IsUpToDate(ld))
320
321 // Validate the expected number of routes
322 assert.EqualValues(t, 2*numNNIPort*numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu, len(ldMgr.deviceRoutes.Routes))
323
324 // Validate the root ports
325 for _, port := range ldMgr.logicalDevice.Ports {
326 assert.Equal(t, port.RootPort, ldMgr.deviceRoutes.IsRootPort(port.OfpPort.PortNo))
327 }
328
329 fmt.Println(fmt.Sprintf("Total Time:%dms, Total Routes:%d NumGetDeviceInvoked:%d", time.Since(start)/time.Millisecond, len(ldMgr.deviceRoutes.Routes), onuMgr.numGetDeviceInvoked))
330}
331
332func TestDeviceRoutes_compareRoutesGeneration(t *testing.T) {
333 numNNIPort := 2
334 numPonPortOnOlt := 8
335 numOnuPerOltPonPort := 32
336 numUniPerOnu := 4
337 done := make(chan struct{})
338
339 fmt.Println(fmt.Sprintf("Test: Computing all routes. LogicalPorts:%d, NNI:%d, Pon/OLT:%d, ONU/Pon:%d, Uni/Onu:%d",
340 numNNIPort*numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu, numNNIPort, numPonPortOnOlt, numOnuPerOltPonPort, numUniPerOnu))
341
342 // Create all the devices and logical device before computing the routes in one go
343 ld1 := &voltha.LogicalDevice{Id: logicalDeviceID}
344 ldMgrChnl1 := make(chan portRegistration, numNNIPort*numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu)
345 ldMgr1 := newLogicalDeviceManager(ld1, ldMgrChnl1, numNNIPort+numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu, done)
346 oltMgrChnl1 := make(chan onuRegistration, numPonPortOnOlt*numOnuPerOltPonPort)
347 oltMgr1 := newOltManager(oltDeviceID, ldMgr1, numNNIPort, numPonPortOnOlt, oltMgrChnl1)
348 onuMgr1 := newOnuManager(oltMgr1, numOnuPerOltPonPort, numUniPerOnu, 2)
349 getDevice := onuMgr1.GetDeviceHelper
350 // Start the managers. Only the devices are created. No routes will be built.
351 go ldMgr1.start(getDevice, false)
352 go oltMgr1.start()
353 go onuMgr1.start(numNNIPort+1, numPonPortOnOlt)
354
355 // Wait for all the devices to be created
356 <-done
357 close(oltMgrChnl1)
358 close(ldMgrChnl1)
359
360 err := ldMgr1.deviceRoutes.ComputeRoutes(context.TODO(), ldMgr1.logicalDevice.Ports)
361 assert.Nil(t, err)
362
363 routesGeneratedAllAtOnce := ldMgr1.deviceRoutes.Routes
364
365 done = make(chan struct{})
366 // Create all the devices and logical device before computing the routes in one go
367 ld2 := &voltha.LogicalDevice{Id: logicalDeviceID}
368 ldMgrChnl2 := make(chan portRegistration, numNNIPort*numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu)
369 ldMgr2 := newLogicalDeviceManager(ld2, ldMgrChnl2, numNNIPort+numPonPortOnOlt*numOnuPerOltPonPort*numUniPerOnu, done)
370 oltMgrChnl2 := make(chan onuRegistration, numPonPortOnOlt*numOnuPerOltPonPort)
371 oltMgr2 := newOltManager(oltDeviceID, ldMgr2, numNNIPort, numPonPortOnOlt, oltMgrChnl2)
372 onuMgr2 := newOnuManager(oltMgr2, numOnuPerOltPonPort, numUniPerOnu, 2)
373 // Start the managers. Only the devices are created. No routes will be built.
374 go ldMgr2.start(getDevice, true)
375 go oltMgr2.start()
376 go onuMgr2.start(numNNIPort+1, numPonPortOnOlt)
377
378 // Wait for all the devices to be created
379 <-done
380 close(oltMgrChnl2)
381 close(ldMgrChnl2)
382
383 routesGeneratedPerPort := ldMgr1.deviceRoutes.Routes
384 assert.True(t, isEqual(routesGeneratedAllAtOnce, routesGeneratedPerPort))
385}
386
387func TestDeviceRoutes_reverseRoute(t *testing.T) {
388 // Test the typical use case - 2 hops in a route
389 route := make([]Hop, 2)
390 route[0].DeviceID = "d1"
391 route[0].Ingress = 1
392 route[0].Egress = 2
393 route[1].DeviceID = "d2"
394 route[1].Ingress = 10
395 route[1].Egress = 15
396
397 reverseRoute := getReverseRoute(route)
398 assert.Equal(t, 2, len(reverseRoute))
399 assert.Equal(t, "d2", reverseRoute[0].DeviceID)
400 assert.Equal(t, "d1", reverseRoute[1].DeviceID)
401 assert.Equal(t, uint32(15), reverseRoute[0].Ingress)
402 assert.Equal(t, uint32(10), reverseRoute[0].Egress)
403 assert.Equal(t, uint32(2), reverseRoute[1].Ingress)
404 assert.Equal(t, uint32(1), reverseRoute[1].Egress)
405
406 fmt.Println("Reverse of two hops successful.")
407
408 //Test 3 hops in a route
409 route = make([]Hop, 3)
410 route[0].DeviceID = "d1"
411 route[0].Ingress = 1
412 route[0].Egress = 2
413 route[1].DeviceID = "d2"
414 route[1].Ingress = 10
415 route[1].Egress = 15
416 route[2].DeviceID = "d3"
417 route[2].Ingress = 20
418 route[2].Egress = 25
419 reverseRoute = getReverseRoute(route)
420 assert.Equal(t, 3, len(reverseRoute))
421 assert.Equal(t, "d3", reverseRoute[0].DeviceID)
422 assert.Equal(t, "d2", reverseRoute[1].DeviceID)
423 assert.Equal(t, "d1", reverseRoute[2].DeviceID)
424 assert.Equal(t, uint32(25), reverseRoute[0].Ingress)
425 assert.Equal(t, uint32(20), reverseRoute[0].Egress)
426 assert.Equal(t, uint32(15), reverseRoute[1].Ingress)
427 assert.Equal(t, uint32(10), reverseRoute[1].Egress)
428 assert.Equal(t, uint32(2), reverseRoute[2].Ingress)
429 assert.Equal(t, uint32(1), reverseRoute[2].Egress)
430
431 fmt.Println("Reverse of three hops successful.")
432
433 // Test any number of hops in a route
434 numRoutes := rand.Intn(100)
435 route = make([]Hop, numRoutes)
436 deviceIds := make([]string, numRoutes)
437 ingressNos := make([]uint32, numRoutes)
438 egressNos := make([]uint32, numRoutes)
439 for i := 0; i < numRoutes; i++ {
440 deviceIds[i] = fmt.Sprintf("d-%d", i)
441 ingressNos[i] = rand.Uint32()
442 egressNos[i] = rand.Uint32()
443 }
444 for i := 0; i < numRoutes; i++ {
445 route[i].DeviceID = deviceIds[i]
446 route[i].Ingress = ingressNos[i]
447 route[i].Egress = egressNos[i]
448 }
449 reverseRoute = getReverseRoute(route)
450 assert.Equal(t, numRoutes, len(reverseRoute))
451 for i, j := 0, numRoutes-1; j >= 0; i, j = i+1, j-1 {
452 assert.Equal(t, deviceIds[j], reverseRoute[i].DeviceID)
453 assert.Equal(t, egressNos[j], reverseRoute[i].Ingress)
454 assert.Equal(t, ingressNos[j], reverseRoute[i].Egress)
455 }
456
457 fmt.Println(fmt.Sprintf("Reverse of %d hops successful.", numRoutes))
458
459 reverseOfReverse := getReverseRoute(reverseRoute)
460 assert.Equal(t, route, reverseOfReverse)
461 fmt.Println("Reverse of reverse successful.")
462}
463
464func isEqual(routes1 map[PathID][]Hop, routes2 map[PathID][]Hop) bool {
465 if routes1 == nil && routes2 == nil {
466 return true
467 }
468 if (routes1 == nil && routes2 != nil) || (routes2 == nil && routes1 != nil) {
469 return false
470 }
471 if len(routes1) != len(routes2) {
472 return false
473 }
474 for routeID1, routeHop1 := range routes1 {
475 found := false
476 for routeID2, routeHop2 := range routes2 {
477 if routeID1 == routeID2 {
478 if !reflect.DeepEqual(routeHop1, routeHop2) {
479 return false
480 }
481 found = true
482 break
483 }
484 }
485 if !found {
486 return false
487 }
488 }
489 return true
490}