blob: 60b7273b87b721cf2fc60c2044c6e9ea5ea5bb16 [file] [log] [blame]
khenaidoo6e55d9e2019-12-12 18:26:26 -05001/*
2* 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.
15 */
Kent Hagerman2b216042020-04-03 18:28:56 -040016package device
khenaidoo6e55d9e2019-12-12 18:26:26 -050017
18import (
19 "context"
20 "github.com/gogo/protobuf/proto"
Kent Hagerman2b216042020-04-03 18:28:56 -040021 "github.com/opencord/voltha-go/db/model"
khenaidoo6e55d9e2019-12-12 18:26:26 -050022 "github.com/opencord/voltha-go/rw_core/config"
Kent Hagerman2b216042020-04-03 18:28:56 -040023 "github.com/opencord/voltha-go/rw_core/core/adapter"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080024 com "github.com/opencord/voltha-lib-go/v3/pkg/adapters/common"
Kent Hagerman2b216042020-04-03 18:28:56 -040025 "github.com/opencord/voltha-lib-go/v3/pkg/db"
26 "github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080027 "github.com/opencord/voltha-lib-go/v3/pkg/kafka"
Scott Baker504b4802020-04-17 10:12:20 -070028 "github.com/opencord/voltha-lib-go/v3/pkg/log"
Matteo Scandolod525ae32020-04-02 17:27:29 -070029 mock_etcd "github.com/opencord/voltha-lib-go/v3/pkg/mocks/etcd"
30 mock_kafka "github.com/opencord/voltha-lib-go/v3/pkg/mocks/kafka"
serkant.uluderya2ae470f2020-01-21 11:13:09 -080031 ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
32 "github.com/opencord/voltha-protos/v3/go/voltha"
khenaidoo6e55d9e2019-12-12 18:26:26 -050033 "github.com/phayes/freeport"
34 "github.com/stretchr/testify/assert"
Kent Hagerman2b216042020-04-03 18:28:56 -040035 "google.golang.org/grpc/codes"
36 "google.golang.org/grpc/status"
khenaidoo6e55d9e2019-12-12 18:26:26 -050037 "math/rand"
khenaidoob2121e52019-12-16 17:17:22 -050038 "sort"
Kent Hagerman2b216042020-04-03 18:28:56 -040039 "strconv"
khenaidoo6e55d9e2019-12-12 18:26:26 -050040 "strings"
41 "sync"
42 "testing"
43 "time"
44)
45
46type DATest struct {
Kent Hagerman2b216042020-04-03 18:28:56 -040047 etcdServer *mock_etcd.EtcdServer
48 deviceMgr *Manager
49 logicalDeviceMgr *LogicalManager
50 kmp kafka.InterContainerProxy
51 kClient kafka.Client
52 kvClientPort int
53 oltAdapterName string
54 onuAdapterName string
55 coreInstanceID string
56 defaultTimeout time.Duration
57 maxTimeout time.Duration
58 device *voltha.Device
59 done chan int
khenaidoo6e55d9e2019-12-12 18:26:26 -050060}
61
62func newDATest() *DATest {
63 test := &DATest{}
64 // Start the embedded etcd server
65 var err error
66 test.etcdServer, test.kvClientPort, err = startEmbeddedEtcdServer("voltha.rwcore.da.test", "voltha.rwcore.da.etcd", "error")
67 if err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +000068 logger.Fatal(err)
khenaidoo6e55d9e2019-12-12 18:26:26 -050069 }
70 // Create the kafka client
Matteo Scandolod525ae32020-04-02 17:27:29 -070071 test.kClient = mock_kafka.NewKafkaClient()
khenaidoo6e55d9e2019-12-12 18:26:26 -050072 test.oltAdapterName = "olt_adapter_mock"
73 test.onuAdapterName = "onu_adapter_mock"
74 test.coreInstanceID = "rw-da-test"
75 test.defaultTimeout = 5 * time.Second
76 test.maxTimeout = 20 * time.Second
77 test.done = make(chan int)
78 parentID := com.GetRandomString(10)
79 test.device = &voltha.Device{
80 Type: "onu_adapter_mock",
81 ParentId: parentID,
82 ParentPortNo: 1,
83 VendorId: "onu_adapter_mock",
84 Adapter: "onu_adapter_mock",
85 Vlan: 100,
86 Address: nil,
87 ProxyAddress: &voltha.Device_ProxyAddress{
88 DeviceId: parentID,
89 DeviceType: "olt_adapter_mock",
90 ChannelId: 100,
91 ChannelGroupId: 0,
92 ChannelTermination: "",
93 OnuId: 2,
94 },
95 AdminState: voltha.AdminState_PREPROVISIONED,
96 OperStatus: voltha.OperStatus_UNKNOWN,
97 Reason: "All good",
98 ConnectStatus: voltha.ConnectStatus_UNKNOWN,
99 Custom: nil,
100 Ports: []*voltha.Port{
101 {PortNo: 1, Label: "pon-1", Type: voltha.Port_PON_ONU, AdminState: voltha.AdminState_ENABLED,
102 OperStatus: voltha.OperStatus_ACTIVE, Peers: []*voltha.Port_PeerPort{{DeviceId: parentID, PortNo: 1}}},
103 {PortNo: 100, Label: "uni-100", Type: voltha.Port_ETHERNET_UNI, AdminState: voltha.AdminState_ENABLED,
104 OperStatus: voltha.OperStatus_ACTIVE},
105 },
106 }
khenaidoo6e55d9e2019-12-12 18:26:26 -0500107 return test
108}
109
Kent Hagerman2b216042020-04-03 18:28:56 -0400110type fakeEventCallbacks struct{}
111
112func (fakeEventCallbacks) SendChangeEvent(_ string, _ *ofp.OfpPortStatus) {}
113func (fakeEventCallbacks) SendPacketIn(_ string, _ string, _ *ofp.OfpPacketIn) {}
114
khenaidoo6e55d9e2019-12-12 18:26:26 -0500115func (dat *DATest) startCore(inCompeteMode bool) {
116 cfg := config.NewRWCoreFlags()
117 cfg.CorePairTopic = "rw_core"
khenaidoo442e7c72020-03-10 16:13:48 -0400118 cfg.DefaultRequestTimeout = dat.defaultTimeout
khenaidoo6e55d9e2019-12-12 18:26:26 -0500119 cfg.KVStorePort = dat.kvClientPort
120 cfg.InCompetingMode = inCompeteMode
121 grpcPort, err := freeport.GetFreePort()
122 if err != nil {
Girish Kumarf56a4682020-03-20 20:07:46 +0000123 logger.Fatal("Cannot get a freeport for grpc")
khenaidoo6e55d9e2019-12-12 18:26:26 -0500124 }
125 cfg.GrpcPort = grpcPort
126 cfg.GrpcHost = "127.0.0.1"
khenaidoo6e55d9e2019-12-12 18:26:26 -0500127 client := setupKVClient(cfg, dat.coreInstanceID)
Kent Hagerman2b216042020-04-03 18:28:56 -0400128 backend := &db.Backend{
129 Client: client,
130 StoreType: cfg.KVStoreType,
131 Host: cfg.KVStoreHost,
132 Port: cfg.KVStorePort,
133 Timeout: cfg.KVStoreTimeout,
134 LivenessChannelInterval: cfg.LiveProbeInterval / 2,
135 PathPrefix: cfg.KVStoreDataPrefix}
136 dat.kmp = kafka.NewInterContainerProxy(
137 kafka.InterContainerHost(cfg.KafkaAdapterHost),
138 kafka.InterContainerPort(cfg.KafkaAdapterPort),
139 kafka.MsgClient(dat.kClient),
140 kafka.DefaultTopic(&kafka.Topic{Name: cfg.CoreTopic}),
141 kafka.DeviceDiscoveryTopic(&kafka.Topic{Name: cfg.AffinityRouterTopic}))
142
143 endpointMgr := kafka.NewEndpointManager(backend)
144 proxy := model.NewProxy(backend, "/")
145 adapterMgr := adapter.NewAdapterManager(proxy, dat.coreInstanceID, dat.kClient)
146
147 dat.deviceMgr, dat.logicalDeviceMgr = NewDeviceManagers(proxy, adapterMgr, dat.kmp, endpointMgr, cfg.CorePairTopic, dat.coreInstanceID, cfg.DefaultCoreTimeout)
148 dat.logicalDeviceMgr.SetEventCallbacks(fakeEventCallbacks{})
149 if err = dat.kmp.Start(); err != nil {
150 logger.Fatal("Cannot start InterContainerProxy")
Thomas Lee Se5a44012019-11-07 20:32:24 +0530151 }
Kent Hagerman2b216042020-04-03 18:28:56 -0400152 if err = adapterMgr.Start(context.Background()); err != nil {
153 logger.Fatal("Cannot start adapterMgr")
154 }
155 dat.deviceMgr.Start(context.Background())
156 dat.logicalDeviceMgr.Start(context.Background())
khenaidoo6e55d9e2019-12-12 18:26:26 -0500157}
158
159func (dat *DATest) stopAll() {
160 if dat.kClient != nil {
161 dat.kClient.Stop()
162 }
Kent Hagerman2b216042020-04-03 18:28:56 -0400163 if dat.logicalDeviceMgr != nil {
164 dat.logicalDeviceMgr.Stop(context.Background())
165 }
166 if dat.deviceMgr != nil {
167 dat.deviceMgr.Stop(context.Background())
168 }
169 if dat.kmp != nil {
170 dat.kmp.Stop()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500171 }
172 if dat.etcdServer != nil {
173 stopEmbeddedEtcdServer(dat.etcdServer)
174 }
175}
176
Kent Hagerman2b216042020-04-03 18:28:56 -0400177//startEmbeddedEtcdServer creates and starts an Embedded etcd server locally.
178func startEmbeddedEtcdServer(configName, storageDir, logLevel string) (*mock_etcd.EtcdServer, int, error) {
179 kvClientPort, err := freeport.GetFreePort()
180 if err != nil {
181 return nil, 0, err
182 }
183 peerPort, err := freeport.GetFreePort()
184 if err != nil {
185 return nil, 0, err
186 }
187 etcdServer := mock_etcd.StartEtcdServer(mock_etcd.MKConfig(configName, kvClientPort, peerPort, storageDir, logLevel))
188 if etcdServer == nil {
189 return nil, 0, status.Error(codes.Internal, "Embedded server failed to start")
190 }
191 return etcdServer, kvClientPort, nil
192}
193
194func stopEmbeddedEtcdServer(server *mock_etcd.EtcdServer) {
195 if server != nil {
196 server.Stop()
197 }
198}
199
200func setupKVClient(cf *config.RWCoreFlags, coreInstanceID string) kvstore.Client {
201 addr := cf.KVStoreHost + ":" + strconv.Itoa(cf.KVStorePort)
Scott Baker504b4802020-04-17 10:12:20 -0700202 client, err := kvstore.NewEtcdClient(addr, cf.KVStoreTimeout, log.FatalLevel)
Kent Hagerman2b216042020-04-03 18:28:56 -0400203 if err != nil {
204 panic("no kv client")
205 }
206 return client
207}
208
209func (dat *DATest) createDeviceAgent(t *testing.T) *Agent {
210 deviceMgr := dat.deviceMgr
khenaidoo6e55d9e2019-12-12 18:26:26 -0500211 clonedDevice := proto.Clone(dat.device).(*voltha.Device)
Kent Hagerman2b216042020-04-03 18:28:56 -0400212 deviceAgent := newAgent(deviceMgr.adapterProxy, clonedDevice, deviceMgr, deviceMgr.clusterDataProxy, deviceMgr.defaultTimeout)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500213 d, err := deviceAgent.start(context.TODO(), clonedDevice)
214 assert.Nil(t, err)
215 assert.NotNil(t, d)
216 deviceMgr.addDeviceAgentToMap(deviceAgent)
217 return deviceAgent
218}
219
Kent Hagerman2b216042020-04-03 18:28:56 -0400220func (dat *DATest) updateDeviceConcurrently(t *testing.T, da *Agent, globalWG *sync.WaitGroup) {
khenaidoo442e7c72020-03-10 16:13:48 -0400221 originalDevice, err := da.getDevice(context.Background())
222 assert.Nil(t, err)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500223 assert.NotNil(t, originalDevice)
224 var localWG sync.WaitGroup
225
226 // Update device routine
227 var (
228 root = false
229 vendor = "onu_adapter_mock"
230 model = "go-mock"
231 serialNumber = com.GetRandomSerialNumber()
232 macAddress = strings.ToUpper(com.GetRandomMacAddress())
233 vlan = rand.Uint32()
234 reason = "testing concurrent device update"
235 portToAdd = &voltha.Port{PortNo: 101, Label: "uni-101", Type: voltha.Port_ETHERNET_UNI, AdminState: voltha.AdminState_ENABLED,
236 OperStatus: voltha.OperStatus_ACTIVE}
237 )
238 localWG.Add(1)
239 go func() {
240 deviceToUpdate := proto.Clone(originalDevice).(*voltha.Device)
241 deviceToUpdate.Root = root
242 deviceToUpdate.Vendor = vendor
243 deviceToUpdate.Model = model
244 deviceToUpdate.SerialNumber = serialNumber
245 deviceToUpdate.MacAddress = macAddress
246 deviceToUpdate.Vlan = vlan
247 deviceToUpdate.Reason = reason
npujar467fe752020-01-16 20:17:45 +0530248 err := da.updateDeviceUsingAdapterData(context.Background(), deviceToUpdate)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500249 assert.Nil(t, err)
250 localWG.Done()
251 }()
252
253 // Update the device status routine
254 localWG.Add(1)
255 go func() {
npujar467fe752020-01-16 20:17:45 +0530256 err := da.updateDeviceStatus(context.Background(), voltha.OperStatus_ACTIVE, voltha.ConnectStatus_REACHABLE)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500257 assert.Nil(t, err)
258 localWG.Done()
259 }()
260
261 // Add a port routine
262 localWG.Add(1)
263 go func() {
npujar467fe752020-01-16 20:17:45 +0530264 err := da.addPort(context.Background(), portToAdd)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500265 assert.Nil(t, err)
266 localWG.Done()
267 }()
268
269 // wait for go routines to be done
270 localWG.Wait()
271
272 expectedChange := proto.Clone(originalDevice).(*voltha.Device)
273 expectedChange.OperStatus = voltha.OperStatus_ACTIVE
274 expectedChange.ConnectStatus = voltha.ConnectStatus_REACHABLE
275 expectedChange.Ports = append(expectedChange.Ports, portToAdd)
276 expectedChange.Root = root
277 expectedChange.Vendor = vendor
278 expectedChange.Model = model
279 expectedChange.SerialNumber = serialNumber
280 expectedChange.MacAddress = macAddress
281 expectedChange.Vlan = vlan
282 expectedChange.Reason = reason
283
khenaidoo442e7c72020-03-10 16:13:48 -0400284 updatedDevice, _ := da.getDevice(context.Background())
khenaidoo6e55d9e2019-12-12 18:26:26 -0500285 assert.NotNil(t, updatedDevice)
286 assert.True(t, proto.Equal(expectedChange, updatedDevice))
287
288 globalWG.Done()
289}
290
291func TestConcurrentDevices(t *testing.T) {
khenaidoo442e7c72020-03-10 16:13:48 -0400292 for i := 0; i < 2; i++ {
293 da := newDATest()
294 assert.NotNil(t, da)
295 defer da.stopAll()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500296
khenaidoo442e7c72020-03-10 16:13:48 -0400297 // Start the Core
298 da.startCore(false)
khenaidoo6e55d9e2019-12-12 18:26:26 -0500299
khenaidoo442e7c72020-03-10 16:13:48 -0400300 var wg sync.WaitGroup
301 numConCurrentDeviceAgents := 20
302 for i := 0; i < numConCurrentDeviceAgents; i++ {
303 wg.Add(1)
304 a := da.createDeviceAgent(t)
305 go da.updateDeviceConcurrently(t, a, &wg)
306 }
307
308 wg.Wait()
khenaidoo6e55d9e2019-12-12 18:26:26 -0500309 }
khenaidoo6e55d9e2019-12-12 18:26:26 -0500310}
khenaidoob2121e52019-12-16 17:17:22 -0500311
312func isFlowSliceEqual(a, b []*ofp.OfpFlowStats) bool {
313 if len(a) != len(b) {
314 return false
315 }
316 sort.Slice(a, func(i, j int) bool {
317 return a[i].Id < a[j].Id
318 })
319 sort.Slice(b, func(i, j int) bool {
320 return b[i].Id < b[j].Id
321 })
322 for idx := range a {
323 if !proto.Equal(a[idx], b[idx]) {
324 return false
325 }
326 }
327 return true
328}
329
330func isGroupSliceEqual(a, b []*ofp.OfpGroupEntry) bool {
331 if len(a) != len(b) {
332 return false
333 }
334 sort.Slice(a, func(i, j int) bool {
335 return a[i].Desc.GroupId < a[j].Desc.GroupId
336 })
337 sort.Slice(b, func(i, j int) bool {
338 return b[i].Desc.GroupId < b[j].Desc.GroupId
339 })
340 for idx := range a {
341 if !proto.Equal(a[idx], b[idx]) {
342 return false
343 }
344 }
345 return true
346}
347
348func TestFlowsToUpdateToDelete_EmptySlices(t *testing.T) {
349 newFlows := []*ofp.OfpFlowStats{}
350 existingFlows := []*ofp.OfpFlowStats{}
351 expectedNewFlows := []*ofp.OfpFlowStats{}
352 expectedFlowsToDelete := []*ofp.OfpFlowStats{}
353 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{}
354 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
355 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
356 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
357 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
358}
359
360func TestFlowsToUpdateToDelete_NoExistingFlows(t *testing.T) {
361 newFlows := []*ofp.OfpFlowStats{
362 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
363 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
364 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
365 }
366 existingFlows := []*ofp.OfpFlowStats{}
367 expectedNewFlows := []*ofp.OfpFlowStats{
368 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
369 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
370 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
371 }
372 expectedFlowsToDelete := []*ofp.OfpFlowStats{}
373 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{
374 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
375 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
376 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
377 }
378 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
379 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
380 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
381 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
382}
383
384func TestFlowsToUpdateToDelete_UpdateNoDelete(t *testing.T) {
385 newFlows := []*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 }
390 existingFlows := []*ofp.OfpFlowStats{
391 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
392 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
393 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
394 }
395 expectedNewFlows := []*ofp.OfpFlowStats{
396 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
397 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
398 }
399 expectedFlowsToDelete := []*ofp.OfpFlowStats{}
400 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{
401 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
402 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
403 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
404 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
405 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
406 }
407 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
408 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
409 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
410 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
411}
412
413func TestFlowsToUpdateToDelete_UpdateAndDelete(t *testing.T) {
414 newFlows := []*ofp.OfpFlowStats{
415 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 20},
416 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
417 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 10, Flags: 0, Cookie: 1250000, PacketCount: 0},
418 {Id: 126, TableId: 1260, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1260000, PacketCount: 0},
419 {Id: 127, TableId: 1270, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1270000, PacketCount: 0},
420 }
421 existingFlows := []*ofp.OfpFlowStats{
422 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
423 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
424 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
425 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
426 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
427 }
428 expectedNewFlows := []*ofp.OfpFlowStats{
429 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 20},
430 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 10, Flags: 0, Cookie: 1250000, PacketCount: 0},
431 {Id: 126, TableId: 1260, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1260000, PacketCount: 0},
432 {Id: 127, TableId: 1270, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1270000, PacketCount: 0},
433 }
434 expectedFlowsToDelete := []*ofp.OfpFlowStats{
435 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
436 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
437 }
438 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{
439 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
440 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
441 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 20},
442 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
443 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 10, Flags: 0, Cookie: 1250000, PacketCount: 0},
444 {Id: 126, TableId: 1260, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1260000, PacketCount: 0},
445 {Id: 127, TableId: 1270, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1270000, PacketCount: 0},
446 }
447 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
448 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
449 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
450 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
451}
452
453func TestGroupsToUpdateToDelete_EmptySlices(t *testing.T) {
454 newGroups := []*ofp.OfpGroupEntry{}
455 existingGroups := []*ofp.OfpGroupEntry{}
456 expectedNewGroups := []*ofp.OfpGroupEntry{}
457 expectedGroupsToDelete := []*ofp.OfpGroupEntry{}
458 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{}
459 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
460 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
461 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
462 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
463}
464
465func TestGroupsToUpdateToDelete_NoExistingGroups(t *testing.T) {
466 newGroups := []*ofp.OfpGroupEntry{
467 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
468 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
469 }
470 existingGroups := []*ofp.OfpGroupEntry{}
471 expectedNewGroups := []*ofp.OfpGroupEntry{
472 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
473 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
474 }
475 expectedGroupsToDelete := []*ofp.OfpGroupEntry{}
476 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{
477 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
478 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
479 }
480 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
481 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
482 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
483 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
484}
485
486func TestGroupsToUpdateToDelete_UpdateNoDelete(t *testing.T) {
487 newGroups := []*ofp.OfpGroupEntry{
488 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
489 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
490 }
491 existingGroups := []*ofp.OfpGroupEntry{
492 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
493 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
494 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
495 }
496 expectedNewGroups := []*ofp.OfpGroupEntry{
497 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
498 }
499 expectedGroupsToDelete := []*ofp.OfpGroupEntry{}
500 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{
501 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
502 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
503 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
504 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
505 }
506 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
507 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
508 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
509 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
510}
511
512func TestGroupsToUpdateToDelete_UpdateWithDelete(t *testing.T) {
513 newGroups := []*ofp.OfpGroupEntry{
514 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
515 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: []*ofp.OfpBucket{{WatchPort: 10}}}},
516 }
517 existingGroups := []*ofp.OfpGroupEntry{
518 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
519 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
520 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
521 }
522 expectedNewGroups := []*ofp.OfpGroupEntry{
523 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
524 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: []*ofp.OfpBucket{{WatchPort: 10}}}},
525 }
526 expectedGroupsToDelete := []*ofp.OfpGroupEntry{
527 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
528 }
529 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{
530 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
531 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: []*ofp.OfpBucket{{WatchPort: 10}}}},
532 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
533 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
534 }
535 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
536 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
537 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
538 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
539}