blob: ffc2a3b43eaf9533faf0180d9fdd72e6fec05f15 [file] [log] [blame]
khenaidoo6e55d9e2019-12-12 18:26:26 -05001/*
Kent Hagerman45a13e42020-04-13 12:23:50 -04002 * Copyright 2019-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.
khenaidoo6e55d9e2019-12-12 18:26:26 -050015 */
Kent Hagerman45a13e42020-04-13 12:23:50 -040016
Kent Hagerman2b216042020-04-03 18:28:56 -040017package device
khenaidoo6e55d9e2019-12-12 18:26:26 -050018
19import (
20 "context"
21 "github.com/gogo/protobuf/proto"
Kent Hagerman2b216042020-04-03 18:28:56 -040022 "github.com/opencord/voltha-go/db/model"
khenaidoo6e55d9e2019-12-12 18:26:26 -050023 "github.com/opencord/voltha-go/rw_core/config"
Kent Hagerman2b216042020-04-03 18:28:56 -040024 "github.com/opencord/voltha-go/rw_core/core/adapter"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080025 com "github.com/opencord/voltha-lib-go/v3/pkg/adapters/common"
Kent Hagerman2b216042020-04-03 18:28:56 -040026 "github.com/opencord/voltha-lib-go/v3/pkg/db"
27 "github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080028 "github.com/opencord/voltha-lib-go/v3/pkg/kafka"
Scott Baker504b4802020-04-17 10:12:20 -070029 "github.com/opencord/voltha-lib-go/v3/pkg/log"
Matteo Scandolod525ae32020-04-02 17:27:29 -070030 mock_etcd "github.com/opencord/voltha-lib-go/v3/pkg/mocks/etcd"
31 mock_kafka "github.com/opencord/voltha-lib-go/v3/pkg/mocks/kafka"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080032 ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
33 "github.com/opencord/voltha-protos/v3/go/voltha"
khenaidoo6e55d9e2019-12-12 18:26:26 -050034 "github.com/phayes/freeport"
35 "github.com/stretchr/testify/assert"
Kent Hagerman2b216042020-04-03 18:28:56 -040036 "google.golang.org/grpc/codes"
37 "google.golang.org/grpc/status"
khenaidoo6e55d9e2019-12-12 18:26:26 -050038 "math/rand"
khenaidoob2121e52019-12-16 17:17:22 -050039 "sort"
Kent Hagerman2b216042020-04-03 18:28:56 -040040 "strconv"
khenaidoo6e55d9e2019-12-12 18:26:26 -050041 "strings"
42 "sync"
43 "testing"
44 "time"
45)
46
47type DATest struct {
Kent Hagerman2b216042020-04-03 18:28:56 -040048 etcdServer *mock_etcd.EtcdServer
49 deviceMgr *Manager
50 logicalDeviceMgr *LogicalManager
51 kmp kafka.InterContainerProxy
52 kClient kafka.Client
53 kvClientPort int
54 oltAdapterName string
55 onuAdapterName string
56 coreInstanceID string
57 defaultTimeout time.Duration
58 maxTimeout time.Duration
59 device *voltha.Device
60 done chan int
khenaidoo6e55d9e2019-12-12 18:26:26 -050061}
62
63func newDATest() *DATest {
64 test := &DATest{}
65 // Start the embedded etcd server
66 var err error
67 test.etcdServer, test.kvClientPort, err = startEmbeddedEtcdServer("voltha.rwcore.da.test", "voltha.rwcore.da.etcd", "error")
68 if err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +000069 logger.Fatal(err)
khenaidoo6e55d9e2019-12-12 18:26:26 -050070 }
71 // Create the kafka client
Matteo Scandolod525ae32020-04-02 17:27:29 -070072 test.kClient = mock_kafka.NewKafkaClient()
khenaidoo6e55d9e2019-12-12 18:26:26 -050073 test.oltAdapterName = "olt_adapter_mock"
74 test.onuAdapterName = "onu_adapter_mock"
75 test.coreInstanceID = "rw-da-test"
76 test.defaultTimeout = 5 * time.Second
77 test.maxTimeout = 20 * time.Second
78 test.done = make(chan int)
79 parentID := com.GetRandomString(10)
80 test.device = &voltha.Device{
81 Type: "onu_adapter_mock",
82 ParentId: parentID,
83 ParentPortNo: 1,
84 VendorId: "onu_adapter_mock",
85 Adapter: "onu_adapter_mock",
86 Vlan: 100,
87 Address: nil,
88 ProxyAddress: &voltha.Device_ProxyAddress{
89 DeviceId: parentID,
90 DeviceType: "olt_adapter_mock",
91 ChannelId: 100,
92 ChannelGroupId: 0,
93 ChannelTermination: "",
94 OnuId: 2,
95 },
96 AdminState: voltha.AdminState_PREPROVISIONED,
97 OperStatus: voltha.OperStatus_UNKNOWN,
98 Reason: "All good",
99 ConnectStatus: voltha.ConnectStatus_UNKNOWN,
100 Custom: nil,
101 Ports: []*voltha.Port{
102 {PortNo: 1, Label: "pon-1", Type: voltha.Port_PON_ONU, AdminState: voltha.AdminState_ENABLED,
103 OperStatus: voltha.OperStatus_ACTIVE, Peers: []*voltha.Port_PeerPort{{DeviceId: parentID, PortNo: 1}}},
104 {PortNo: 100, Label: "uni-100", Type: voltha.Port_ETHERNET_UNI, AdminState: voltha.AdminState_ENABLED,
105 OperStatus: voltha.OperStatus_ACTIVE},
106 },
107 }
khenaidoo6e55d9e2019-12-12 18:26:26 -0500108 return test
109}
110
111func (dat *DATest) startCore(inCompeteMode bool) {
112 cfg := config.NewRWCoreFlags()
113 cfg.CorePairTopic = "rw_core"
khenaidoo442e7c72020-03-10 16:13:48 -0400114 cfg.DefaultRequestTimeout = dat.defaultTimeout
khenaidoo6e55d9e2019-12-12 18:26:26 -0500115 cfg.KVStorePort = dat.kvClientPort
116 cfg.InCompetingMode = inCompeteMode
117 grpcPort, err := freeport.GetFreePort()
118 if err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000119 logger.Fatal("Cannot get a freeport for grpc")
khenaidoo6e55d9e2019-12-12 18:26:26 -0500120 }
121 cfg.GrpcPort = grpcPort
122 cfg.GrpcHost = "127.0.0.1"
khenaidoo6e55d9e2019-12-12 18:26:26 -0500123 client := setupKVClient(cfg, dat.coreInstanceID)
Kent Hagerman2b216042020-04-03 18:28:56 -0400124 backend := &db.Backend{
125 Client: client,
126 StoreType: cfg.KVStoreType,
127 Host: cfg.KVStoreHost,
128 Port: cfg.KVStorePort,
129 Timeout: cfg.KVStoreTimeout,
130 LivenessChannelInterval: cfg.LiveProbeInterval / 2,
131 PathPrefix: cfg.KVStoreDataPrefix}
132 dat.kmp = kafka.NewInterContainerProxy(
133 kafka.InterContainerHost(cfg.KafkaAdapterHost),
134 kafka.InterContainerPort(cfg.KafkaAdapterPort),
135 kafka.MsgClient(dat.kClient),
136 kafka.DefaultTopic(&kafka.Topic{Name: cfg.CoreTopic}),
137 kafka.DeviceDiscoveryTopic(&kafka.Topic{Name: cfg.AffinityRouterTopic}))
138
139 endpointMgr := kafka.NewEndpointManager(backend)
Kent Hagermanf5a67352020-04-30 15:15:26 -0400140 proxy := model.NewDBPath(backend)
Kent Hagerman2b216042020-04-03 18:28:56 -0400141 adapterMgr := adapter.NewAdapterManager(proxy, dat.coreInstanceID, dat.kClient)
142
Kent Hagerman45a13e42020-04-13 12:23:50 -0400143 dat.deviceMgr, dat.logicalDeviceMgr = NewManagers(proxy, adapterMgr, dat.kmp, endpointMgr, cfg.CorePairTopic, dat.coreInstanceID, cfg.DefaultCoreTimeout)
Kent Hagerman2b216042020-04-03 18:28:56 -0400144 if err = dat.kmp.Start(); err != nil {
145 logger.Fatal("Cannot start InterContainerProxy")
Thomas Lee Se5a44012019-11-07 20:32:24 +0530146 }
Kent Hagerman2f0d0552020-04-23 17:28:52 -0400147 adapterMgr.Start(context.Background())
khenaidoo6e55d9e2019-12-12 18:26:26 -0500148}
149
150func (dat *DATest) stopAll() {
151 if dat.kClient != nil {
152 dat.kClient.Stop()
153 }
Kent Hagerman2b216042020-04-03 18:28:56 -0400154 if dat.kmp != nil {
155 dat.kmp.Stop()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500156 }
157 if dat.etcdServer != nil {
158 stopEmbeddedEtcdServer(dat.etcdServer)
159 }
160}
161
Kent Hagerman2b216042020-04-03 18:28:56 -0400162//startEmbeddedEtcdServer creates and starts an Embedded etcd server locally.
163func startEmbeddedEtcdServer(configName, storageDir, logLevel string) (*mock_etcd.EtcdServer, int, error) {
164 kvClientPort, err := freeport.GetFreePort()
165 if err != nil {
166 return nil, 0, err
167 }
168 peerPort, err := freeport.GetFreePort()
169 if err != nil {
170 return nil, 0, err
171 }
172 etcdServer := mock_etcd.StartEtcdServer(mock_etcd.MKConfig(configName, kvClientPort, peerPort, storageDir, logLevel))
173 if etcdServer == nil {
174 return nil, 0, status.Error(codes.Internal, "Embedded server failed to start")
175 }
176 return etcdServer, kvClientPort, nil
177}
178
179func stopEmbeddedEtcdServer(server *mock_etcd.EtcdServer) {
180 if server != nil {
181 server.Stop()
182 }
183}
184
185func setupKVClient(cf *config.RWCoreFlags, coreInstanceID string) kvstore.Client {
186 addr := cf.KVStoreHost + ":" + strconv.Itoa(cf.KVStorePort)
Scott Baker504b4802020-04-17 10:12:20 -0700187 client, err := kvstore.NewEtcdClient(addr, cf.KVStoreTimeout, log.FatalLevel)
Kent Hagerman2b216042020-04-03 18:28:56 -0400188 if err != nil {
189 panic("no kv client")
190 }
191 return client
192}
193
194func (dat *DATest) createDeviceAgent(t *testing.T) *Agent {
195 deviceMgr := dat.deviceMgr
khenaidoo6e55d9e2019-12-12 18:26:26 -0500196 clonedDevice := proto.Clone(dat.device).(*voltha.Device)
Kent Hagermanf5a67352020-04-30 15:15:26 -0400197 deviceAgent := newAgent(deviceMgr.adapterProxy, clonedDevice, deviceMgr, deviceMgr.dProxy, deviceMgr.defaultTimeout)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500198 d, err := deviceAgent.start(context.TODO(), clonedDevice)
199 assert.Nil(t, err)
200 assert.NotNil(t, d)
201 deviceMgr.addDeviceAgentToMap(deviceAgent)
202 return deviceAgent
203}
204
Kent Hagerman2b216042020-04-03 18:28:56 -0400205func (dat *DATest) updateDeviceConcurrently(t *testing.T, da *Agent, globalWG *sync.WaitGroup) {
khenaidoo442e7c72020-03-10 16:13:48 -0400206 originalDevice, err := da.getDevice(context.Background())
207 assert.Nil(t, err)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500208 assert.NotNil(t, originalDevice)
209 var localWG sync.WaitGroup
210
211 // Update device routine
212 var (
213 root = false
214 vendor = "onu_adapter_mock"
215 model = "go-mock"
216 serialNumber = com.GetRandomSerialNumber()
217 macAddress = strings.ToUpper(com.GetRandomMacAddress())
218 vlan = rand.Uint32()
219 reason = "testing concurrent device update"
220 portToAdd = &voltha.Port{PortNo: 101, Label: "uni-101", Type: voltha.Port_ETHERNET_UNI, AdminState: voltha.AdminState_ENABLED,
221 OperStatus: voltha.OperStatus_ACTIVE}
222 )
223 localWG.Add(1)
224 go func() {
225 deviceToUpdate := proto.Clone(originalDevice).(*voltha.Device)
226 deviceToUpdate.Root = root
227 deviceToUpdate.Vendor = vendor
228 deviceToUpdate.Model = model
229 deviceToUpdate.SerialNumber = serialNumber
230 deviceToUpdate.MacAddress = macAddress
231 deviceToUpdate.Vlan = vlan
232 deviceToUpdate.Reason = reason
npujar467fe752020-01-16 20:17:45 +0530233 err := da.updateDeviceUsingAdapterData(context.Background(), deviceToUpdate)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500234 assert.Nil(t, err)
235 localWG.Done()
236 }()
237
238 // Update the device status routine
239 localWG.Add(1)
240 go func() {
npujar467fe752020-01-16 20:17:45 +0530241 err := da.updateDeviceStatus(context.Background(), voltha.OperStatus_ACTIVE, voltha.ConnectStatus_REACHABLE)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500242 assert.Nil(t, err)
243 localWG.Done()
244 }()
245
246 // Add a port routine
247 localWG.Add(1)
248 go func() {
npujar467fe752020-01-16 20:17:45 +0530249 err := da.addPort(context.Background(), portToAdd)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500250 assert.Nil(t, err)
251 localWG.Done()
252 }()
253
254 // wait for go routines to be done
255 localWG.Wait()
256
257 expectedChange := proto.Clone(originalDevice).(*voltha.Device)
258 expectedChange.OperStatus = voltha.OperStatus_ACTIVE
259 expectedChange.ConnectStatus = voltha.ConnectStatus_REACHABLE
260 expectedChange.Ports = append(expectedChange.Ports, portToAdd)
261 expectedChange.Root = root
262 expectedChange.Vendor = vendor
263 expectedChange.Model = model
264 expectedChange.SerialNumber = serialNumber
265 expectedChange.MacAddress = macAddress
266 expectedChange.Vlan = vlan
267 expectedChange.Reason = reason
268
khenaidoo442e7c72020-03-10 16:13:48 -0400269 updatedDevice, _ := da.getDevice(context.Background())
khenaidoo6e55d9e2019-12-12 18:26:26 -0500270 assert.NotNil(t, updatedDevice)
271 assert.True(t, proto.Equal(expectedChange, updatedDevice))
272
273 globalWG.Done()
274}
275
276func TestConcurrentDevices(t *testing.T) {
khenaidoo442e7c72020-03-10 16:13:48 -0400277 for i := 0; i < 2; i++ {
278 da := newDATest()
279 assert.NotNil(t, da)
280 defer da.stopAll()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500281
khenaidoo442e7c72020-03-10 16:13:48 -0400282 // Start the Core
283 da.startCore(false)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500284
khenaidoo442e7c72020-03-10 16:13:48 -0400285 var wg sync.WaitGroup
286 numConCurrentDeviceAgents := 20
287 for i := 0; i < numConCurrentDeviceAgents; i++ {
288 wg.Add(1)
289 a := da.createDeviceAgent(t)
290 go da.updateDeviceConcurrently(t, a, &wg)
291 }
292
293 wg.Wait()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500294 }
khenaidoo6e55d9e2019-12-12 18:26:26 -0500295}
khenaidoob2121e52019-12-16 17:17:22 -0500296
297func isFlowSliceEqual(a, b []*ofp.OfpFlowStats) bool {
298 if len(a) != len(b) {
299 return false
300 }
301 sort.Slice(a, func(i, j int) bool {
302 return a[i].Id < a[j].Id
303 })
304 sort.Slice(b, func(i, j int) bool {
305 return b[i].Id < b[j].Id
306 })
307 for idx := range a {
308 if !proto.Equal(a[idx], b[idx]) {
309 return false
310 }
311 }
312 return true
313}
314
315func isGroupSliceEqual(a, b []*ofp.OfpGroupEntry) bool {
316 if len(a) != len(b) {
317 return false
318 }
319 sort.Slice(a, func(i, j int) bool {
320 return a[i].Desc.GroupId < a[j].Desc.GroupId
321 })
322 sort.Slice(b, func(i, j int) bool {
323 return b[i].Desc.GroupId < b[j].Desc.GroupId
324 })
325 for idx := range a {
326 if !proto.Equal(a[idx], b[idx]) {
327 return false
328 }
329 }
330 return true
331}
332
333func TestFlowsToUpdateToDelete_EmptySlices(t *testing.T) {
334 newFlows := []*ofp.OfpFlowStats{}
335 existingFlows := []*ofp.OfpFlowStats{}
336 expectedNewFlows := []*ofp.OfpFlowStats{}
337 expectedFlowsToDelete := []*ofp.OfpFlowStats{}
338 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{}
339 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
340 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
341 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
342 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
343}
344
345func TestFlowsToUpdateToDelete_NoExistingFlows(t *testing.T) {
346 newFlows := []*ofp.OfpFlowStats{
347 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
348 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
349 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
350 }
351 existingFlows := []*ofp.OfpFlowStats{}
352 expectedNewFlows := []*ofp.OfpFlowStats{
353 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
354 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
355 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
356 }
357 expectedFlowsToDelete := []*ofp.OfpFlowStats{}
358 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{
359 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
360 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
361 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
362 }
363 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
364 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
365 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
366 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
367}
368
369func TestFlowsToUpdateToDelete_UpdateNoDelete(t *testing.T) {
370 newFlows := []*ofp.OfpFlowStats{
371 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
372 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
373 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
374 }
375 existingFlows := []*ofp.OfpFlowStats{
376 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
377 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
378 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
379 }
380 expectedNewFlows := []*ofp.OfpFlowStats{
381 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
382 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
383 }
384 expectedFlowsToDelete := []*ofp.OfpFlowStats{}
385 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{
386 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
387 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
388 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
389 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
390 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
391 }
392 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
393 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
394 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
395 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
396}
397
398func TestFlowsToUpdateToDelete_UpdateAndDelete(t *testing.T) {
399 newFlows := []*ofp.OfpFlowStats{
400 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 20},
401 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
402 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 10, Flags: 0, Cookie: 1250000, PacketCount: 0},
403 {Id: 126, TableId: 1260, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1260000, PacketCount: 0},
404 {Id: 127, TableId: 1270, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1270000, PacketCount: 0},
405 }
406 existingFlows := []*ofp.OfpFlowStats{
407 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
408 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
409 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
410 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
411 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
412 }
413 expectedNewFlows := []*ofp.OfpFlowStats{
414 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 20},
415 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 10, Flags: 0, Cookie: 1250000, PacketCount: 0},
416 {Id: 126, TableId: 1260, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1260000, PacketCount: 0},
417 {Id: 127, TableId: 1270, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1270000, PacketCount: 0},
418 }
419 expectedFlowsToDelete := []*ofp.OfpFlowStats{
420 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
421 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
422 }
423 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{
424 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
425 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
426 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 20},
427 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
428 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 10, Flags: 0, Cookie: 1250000, PacketCount: 0},
429 {Id: 126, TableId: 1260, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1260000, PacketCount: 0},
430 {Id: 127, TableId: 1270, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1270000, PacketCount: 0},
431 }
432 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
433 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
434 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
435 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
436}
437
438func TestGroupsToUpdateToDelete_EmptySlices(t *testing.T) {
439 newGroups := []*ofp.OfpGroupEntry{}
440 existingGroups := []*ofp.OfpGroupEntry{}
441 expectedNewGroups := []*ofp.OfpGroupEntry{}
442 expectedGroupsToDelete := []*ofp.OfpGroupEntry{}
443 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{}
444 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
445 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
446 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
447 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
448}
449
450func TestGroupsToUpdateToDelete_NoExistingGroups(t *testing.T) {
451 newGroups := []*ofp.OfpGroupEntry{
452 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
453 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
454 }
455 existingGroups := []*ofp.OfpGroupEntry{}
456 expectedNewGroups := []*ofp.OfpGroupEntry{
457 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
458 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
459 }
460 expectedGroupsToDelete := []*ofp.OfpGroupEntry{}
461 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{
462 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
463 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
464 }
465 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
466 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
467 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
468 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
469}
470
471func TestGroupsToUpdateToDelete_UpdateNoDelete(t *testing.T) {
472 newGroups := []*ofp.OfpGroupEntry{
473 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
474 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
475 }
476 existingGroups := []*ofp.OfpGroupEntry{
477 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
478 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
479 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
480 }
481 expectedNewGroups := []*ofp.OfpGroupEntry{
482 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
483 }
484 expectedGroupsToDelete := []*ofp.OfpGroupEntry{}
485 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{
486 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
487 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
488 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
489 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
490 }
491 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
492 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
493 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
494 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
495}
496
497func TestGroupsToUpdateToDelete_UpdateWithDelete(t *testing.T) {
498 newGroups := []*ofp.OfpGroupEntry{
499 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
500 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: []*ofp.OfpBucket{{WatchPort: 10}}}},
501 }
502 existingGroups := []*ofp.OfpGroupEntry{
503 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
504 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
505 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
506 }
507 expectedNewGroups := []*ofp.OfpGroupEntry{
508 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
509 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: []*ofp.OfpBucket{{WatchPort: 10}}}},
510 }
511 expectedGroupsToDelete := []*ofp.OfpGroupEntry{
512 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
513 }
514 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{
515 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
516 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: []*ofp.OfpBucket{{WatchPort: 10}}}},
517 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
518 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
519 }
520 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
521 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
522 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
523 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
524}