blob: 8a345758f7c8b966e364f59780c58706a556f659 [file] [log] [blame]
Scott Bakere7144bc2019-10-01 14:16:47 -07001/*
2 * Portions copyright 2019-present Open Networking Foundation
3 * Original copyright 2019-present Ciena Corporation
4 *
5 * Licensed under the Apache License, Version 2.0 (the"github.com/stretchr/testify/assert" "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package afrouter
18
19import (
20 "fmt"
21 "github.com/golang/protobuf/proto"
Scott Bakere7144bc2019-10-01 14:16:47 -070022 common_pb "github.com/opencord/voltha-protos/go/common"
23 voltha_pb "github.com/opencord/voltha-protos/go/voltha"
24 "github.com/stretchr/testify/assert"
25 "google.golang.org/grpc"
26 "testing"
27)
28
Scott Bakere7144bc2019-10-01 14:16:47 -070029// Build an affinity router configuration
30func MakeAffinityTestConfig(numBackends int, numConnections int) (*RouteConfig, *RouterConfig) {
31
32 var backends []BackendConfig
33 for backendIndex := 0; backendIndex < numBackends; backendIndex++ {
34 var connections []ConnectionConfig
35 for connectionIndex := 0; connectionIndex < numConnections; connectionIndex++ {
36 connectionConfig := ConnectionConfig{
37 Name: fmt.Sprintf("rw_vcore%d%d", backendIndex, connectionIndex+1),
38 Addr: "foo",
39 Port: "123",
40 }
41 connections = append(connections, connectionConfig)
42 }
43
44 backendConfig := BackendConfig{
45 Name: fmt.Sprintf("rw_vcore%d", backendIndex),
46 Type: BackendSingleServer,
47 Connections: connections,
48 }
49
50 backends = append(backends, backendConfig)
51 }
52
53 backendClusterConfig := BackendClusterConfig{
54 Name: "vcore",
55 Backends: backends,
56 }
57
58 routeConfig := RouteConfig{
59 Name: "dev_manager",
60 Type: RouteTypeRpcAffinityMessage,
61 Association: AssociationRoundRobin,
62 BackendCluster: "vcore",
63 backendCluster: &backendClusterConfig,
64 RouteField: "id",
65 Methods: []string{"CreateDevice", "EnableDevice"},
66 NbBindingMethods: []string{"CreateDevice"},
67 }
68
69 routerConfig := RouterConfig{
70 Name: "vcore",
71 ProtoService: "VolthaService",
72 ProtoPackage: "voltha",
73 Routes: []RouteConfig{routeConfig},
Scott Baker4989fe92019-10-09 17:03:06 -070074 ProtoFile: TEST_PROTOFILE,
Scott Bakere7144bc2019-10-01 14:16:47 -070075 }
76 return &routeConfig, &routerConfig
77}
78
79// Route() requires an open connection, so pretend we have one.
80func PretendAffinityOpenConnection(router Router, clusterName string, backendIndex int, connectionName string) {
81 cluster := router.FindBackendCluster(clusterName)
82
83 // Route Method expects an open connection
84 conn := cluster.backends[backendIndex].connections[connectionName]
85 cluster.backends[backendIndex].openConns[conn] = &grpc.ClientConn{}
86}
87
88// Common setup to run before each unit test
89func AffinityTestSetup() {
90 // reset globals that need to be clean for each unit test
91
92 clusters = make(map[string]*cluster)
93 allRouters = make(map[string]Router)
94}
95
96// Test creation of a new AffinityRouter, and the Service(), Name(), FindBackendCluster(), and
97// methods.
98func TestAffinityRouterInit(t *testing.T) {
99 AffinityTestSetup()
100
101 routeConfig, routerConfig := MakeAffinityTestConfig(1, 1)
102
103 router, err := newAffinityRouter(routerConfig, routeConfig)
104
105 assert.NotNil(t, router)
106 assert.Nil(t, err)
107
108 assert.Equal(t, router.Service(), "VolthaService")
109 assert.Equal(t, router.Name(), "dev_manager")
110
111 cluster, err := router.BackendCluster("foo", "bar")
112 assert.Equal(t, cluster, clusters["vcore"])
113 assert.Nil(t, err)
114
115 assert.Equal(t, router.FindBackendCluster("vcore"), clusters["vcore"])
116}
117
118// Should throw error if no name in configuration
119func TestAffinityRouterInitNoName(t *testing.T) {
120 AffinityTestSetup()
121
122 routeConfig, routerConfig := MakeAffinityTestConfig(1, 1)
123 routeConfig.Name = ""
124
125 _, err := newAffinityRouter(routerConfig, routeConfig)
126
127 assert.EqualError(t, err, "Failed to create a new router ''")
128}
129
130// Should thow error if now ProtoPackage in configuration
131func TestAffinityRouterInitNoProtoPackage(t *testing.T) {
132 AffinityTestSetup()
133
134 routeConfig, routerConfig := MakeAffinityTestConfig(1, 1)
135 routerConfig.ProtoPackage = ""
136
137 _, err := newAffinityRouter(routerConfig, routeConfig)
138
139 assert.EqualError(t, err, "Failed to create a new router 'dev_manager'")
140}
141
142// Should throw error if no ProtoServer in configuration
143func TestAffinityRouterInitNoProtoService(t *testing.T) {
144 AffinityTestSetup()
145
146 routeConfig, routerConfig := MakeAffinityTestConfig(1, 1)
147 routerConfig.ProtoService = ""
148
149 _, err := newAffinityRouter(routerConfig, routeConfig)
150
151 assert.EqualError(t, err, "Failed to create a new router 'dev_manager'")
152}
153
154// Tests a cluster with only one Backend
155func TestAffinityRouteOne(t *testing.T) {
156 AffinityTestSetup()
157
158 _, routerConfig := MakeAffinityTestConfig(1, 1)
159
160 router, err := newRouter(routerConfig)
161 assert.Nil(t, err)
162
163 PretendAffinityOpenConnection(router, "vcore", 0, "rw_vcore01")
164
165 idMessage := &common_pb.ID{Id: "1234"}
166
167 idData, err := proto.Marshal(idMessage)
168 assert.Nil(t, err)
169
170 sel := &requestFrame{payload: idData,
171 err: nil,
172 metaKey: NoMeta,
173 methodInfo: newMethodDetails("/voltha.VolthaService/EnableDevice")}
174
175 backend, connection := router.Route(sel)
176
177 assert.Nil(t, sel.err)
178 assert.NotNil(t, backend)
179 assert.Equal(t, "rw_vcore0", backend.name)
180 assert.Nil(t, connection)
181
182 // Since we only have one backend, calling Route a second time should return the same one
183
184 backend, connection = router.Route(sel)
185
186 assert.Nil(t, sel.err)
187 assert.NotNil(t, backend)
188 assert.Equal(t, "rw_vcore0", backend.name)
189 assert.Nil(t, connection)
190}
191
192// Tests a cluster with two Backends
193func TestAffinityRouteTwo(t *testing.T) {
194 AffinityTestSetup()
195
196 _, routerConfig := MakeAffinityTestConfig(2, 1)
197
198 router, err := newRouter(routerConfig)
199 assert.Nil(t, err)
200
201 PretendAffinityOpenConnection(router, "vcore", 0, "rw_vcore01")
202 PretendAffinityOpenConnection(router, "vcore", 1, "rw_vcore11")
203
204 idMessage := &common_pb.ID{Id: "1234"}
205 idData, err := proto.Marshal(idMessage)
206 assert.Nil(t, err)
207
208 sel := &requestFrame{payload: idData,
209 err: nil,
210 metaKey: NoMeta,
211 methodInfo: newMethodDetails("/voltha.VolthaService/EnableDevice")}
212
213 // We should Route to the first core and bind affinity to it
214
215 backend, connection := router.Route(sel)
216
217 assert.Nil(t, sel.err)
218 assert.NotNil(t, backend)
219 assert.Equal(t, "rw_vcore0", backend.name)
220 assert.Nil(t, connection)
221
222 // We should have established affinity, and trying Route again should return the same core
223
224 backend, connection = router.Route(sel)
225
226 assert.Nil(t, sel.err)
227 assert.NotNil(t, backend)
228 assert.Equal(t, "rw_vcore0", backend.name)
229 assert.Nil(t, connection)
230
231 // Make up a message with a different id
232 idMessage = &common_pb.ID{Id: "1235"}
233 idData, err = proto.Marshal(idMessage)
234 assert.Nil(t, err)
235
236 sel = &requestFrame{payload: idData,
237 err: nil,
238 metaKey: NoMeta,
239 methodInfo: newMethodDetails("/voltha.VolthaService/EnableDevice")}
240
241 // Calling Route with the new ID should cause it to bind affinity to the second core
242
243 backend, connection = router.Route(sel)
244
245 assert.Nil(t, sel.err)
246 assert.NotNil(t, backend)
247 assert.Equal(t, "rw_vcore1", backend.name)
248 assert.Nil(t, connection)
249}
250
251// Tests a cluster with one backend but no open connections
252func TestAffinityRouteOneNoOpenConnection(t *testing.T) {
253 AffinityTestSetup()
254
255 _, routerConfig := MakeAffinityTestConfig(1, 1)
256
257 router, err := newRouter(routerConfig)
258 assert.Nil(t, err)
259
260 idMessage := &common_pb.ID{Id: "1234"}
261
262 idData, err := proto.Marshal(idMessage)
263 assert.Nil(t, err)
264
265 sel := &requestFrame{payload: idData,
266 err: nil,
267 metaKey: NoMeta,
268 methodInfo: newMethodDetails("/voltha.VolthaService/EnableDevice")}
269
270 backend, connection := router.Route(sel)
271
272 assert.EqualError(t, sel.err, "No backend with open connections found")
273 assert.Nil(t, backend)
274 assert.Nil(t, connection)
275}
276
277// Tests binding on reply
278func TestAffinityRouteReply(t *testing.T) {
279 AffinityTestSetup()
280
281 _, routerConfig := MakeAffinityTestConfig(2, 1)
282
283 router, err := newRouter(routerConfig)
284 assert.Nil(t, err)
285
286 // Get the created AffinityRouter so we can inspect its state
287 aRouter := allRouters["vcoredev_manager"].(AffinityRouter)
288
289 PretendAffinityOpenConnection(router, "vcore", 0, "rw_vcore01")
290 PretendAffinityOpenConnection(router, "vcore", 1, "rw_vcore11")
291
292 idMessage := &voltha_pb.Device{Id: "1234"}
293 idData, err := proto.Marshal(idMessage)
294 assert.Nil(t, err)
295
296 // Note that sel.backend must be set. As this is a response, it must
297 // have come from a backend and that backend must be known.
298
299 sel := &responseFrame{payload: idData,
300 metaKey: NoMeta,
301 backend: router.FindBackendCluster("vcore").backends[0],
302 method: "CreateDevice",
303 }
304
305 // affinity should be unset as we have not routed yet
306 assert.Empty(t, aRouter.affinity)
307
308 err = aRouter.ReplyHandler(sel)
309 assert.Nil(t, err)
310
311 // affinity should now be set
312 assert.NotEmpty(t, aRouter.affinity)
313 assert.Equal(t, router.FindBackendCluster("vcore").backends[0], aRouter.affinity["1234"])
314}
315
316// Tests binding on reply, with incorrect frame type
317func TestAffinityRouteReplyIncorrectFrame(t *testing.T) {
318 AffinityTestSetup()
319
320 _, routerConfig := MakeAffinityTestConfig(2, 1)
321
322 router, err := newRouter(routerConfig)
323 assert.Nil(t, err)
324
325 // Get the created AffinityRouter so we can inspect its state
326 aRouter := allRouters["vcoredev_manager"].(AffinityRouter)
327
328 PretendAffinityOpenConnection(router, "vcore", 0, "rw_vcore01")
329 PretendAffinityOpenConnection(router, "vcore", 1, "rw_vcore11")
330
331 idMessage := &voltha_pb.Device{Id: "1234"}
332 idData, err := proto.Marshal(idMessage)
333 assert.Nil(t, err)
334
335 sel := &requestFrame{payload: idData,
336 err: nil,
337 metaKey: NoMeta,
338 methodInfo: newMethodDetails("/voltha.VolthaService/EnableDevice"),
339 }
340
341 // ReplyHandler expects a replyFrame and we're giving it a requestFrame instead
342
343 err = aRouter.ReplyHandler(sel)
344 assert.EqualError(t, err, "Internal: invalid data type in ReplyHander call &{[10 4 49 50 51 52] <nil> <nil> <nil> <nil> {/voltha.VolthaService/EnableDevice voltha VolthaService EnableDevice} nometa }")
345}
346
347func TestAffinityRouterDecodeProtoField(t *testing.T) {
348 AffinityTestSetup()
349
350 _, routerConfig := MakeAffinityTestConfig(2, 1)
351
352 _, err := newRouter(routerConfig)
353 assert.Nil(t, err)
354
355 // Get the created AffinityRouter so we can inspect its state
356 aRouter := allRouters["vcoredev_manager"].(AffinityRouter)
357
358 // Pick something to test with lots of field types. Port is a good candidate.
359 portMessage := &voltha_pb.Port{PortNo: 123,
360 Label: "testlabel",
361 Type: 3,
362 DeviceId: "5678",
363 RxPackets: 9876,
364 }
365
366 portData, err := proto.Marshal(portMessage)
367 assert.Nil(t, err)
368
369 /*
370 * Decode various fields in the protobuf. Decoding each subsequent
371 * field implies skipfield() is called on its predecessor.
372 */
373
374 s, err := aRouter.decodeProtoField(portData, 1) // field 1 is PortNo
Scott Baker4989fe92019-10-09 17:03:06 -0700375 assert.Nil(t, err)
Scott Bakere7144bc2019-10-01 14:16:47 -0700376 assert.Equal(t, "123", s)
377
378 // Test VOL-1882, skipping of varint field. Note: May cause infinite loop if not fixed!
379 s, err = aRouter.decodeProtoField(portData, 2) // field 2 is Label
Scott Baker4989fe92019-10-09 17:03:06 -0700380 assert.Nil(t, err)
Scott Bakere7144bc2019-10-01 14:16:47 -0700381 assert.Equal(t, "testlabel", s)
382
383 s, err = aRouter.decodeProtoField(portData, 3) // field 3 is PortType
Scott Baker4989fe92019-10-09 17:03:06 -0700384 assert.Nil(t, err)
Scott Bakere7144bc2019-10-01 14:16:47 -0700385 assert.Equal(t, "3", s)
386
387 s, err = aRouter.decodeProtoField(portData, 7) // field 7 is DeviceId
Scott Baker4989fe92019-10-09 17:03:06 -0700388 assert.Nil(t, err)
Scott Bakere7144bc2019-10-01 14:16:47 -0700389 assert.Equal(t, "5678", s)
390
391 // TODO: Seems like an int64 ought to be allowed...
Scott Baker4989fe92019-10-09 17:03:06 -0700392 _, err = aRouter.decodeProtoField(portData, 9) // field 7 is RxPackets
Scott Bakere7144bc2019-10-01 14:16:47 -0700393 assert.EqualError(t, err, "Only integer and string route selectors are permitted")
394}
395
Scott Baker8f26db32019-10-22 15:08:21 -0700396func TestAffinityRouterDecodeProtoFieldMissingField(t *testing.T) {
397 AffinityTestSetup()
398
399 _, routerConfig := MakeAffinityTestConfig(2, 1)
400
401 _, err := newRouter(routerConfig)
402 assert.Nil(t, err)
403
404 // Get the created AffinityRouter so we can inspect its state
405 aRouter := allRouters["vcoredev_manager"].(AffinityRouter)
406
407 // Pick something to test with lots of field types. Port is a good candidate.
408 portMessage := &voltha_pb.Port{PortNo: 123,
409 Label: "testlabel",
410 Type: 3,
411 DeviceId: "5678",
412 RxPackets: 9876,
413 }
414
415 portData, err := proto.Marshal(portMessage)
416 assert.Nil(t, err)
417
418 // Field #5 is not present.
419 // See VOL-1952. This used to cause a panic.
420
421 _, err = aRouter.decodeProtoField(portData, 5) // field 5 is PortNo
422 assert.EqualError(t, err, "At end of message, attribute 5 not found")
423}
424
Scott Bakere7144bc2019-10-01 14:16:47 -0700425// Test setting affinity for a key to a backend
426func TestAffinitySetAffinity(t *testing.T) {
427 AffinityTestSetup()
428
429 _, routerConfig := MakeAffinityTestConfig(2, 1)
430
431 router, err := newRouter(routerConfig)
432 assert.Nil(t, err)
433
434 // Get the created AffinityRouter so we can inspect its state
435 aRouter := allRouters["vcoredev_manager"].(AffinityRouter)
436
437 backend := router.FindBackendCluster("vcore").backends[0]
438 err = aRouter.setAffinity("1234", backend)
439
440 assert.Nil(t, err)
441}
442
443// Trying to set affinity when it has already been set should fail.
444func TestAffinitySetAffinityChange(t *testing.T) {
445 AffinityTestSetup()
446
447 _, routerConfig := MakeAffinityTestConfig(2, 1)
448
449 router, err := newRouter(routerConfig)
450 assert.Nil(t, err)
451
452 // Get the created AffinityRouter so we can inspect its state
453 aRouter := allRouters["vcoredev_manager"].(AffinityRouter)
454
455 backend := router.FindBackendCluster("vcore").backends[0]
456 err = aRouter.setAffinity("1234", backend)
457
458 assert.Nil(t, err)
459
460 // Now pick a different backend
461 backend = router.FindBackendCluster("vcore").backends[1]
462 err = aRouter.setAffinity("1234", backend)
463
464 assert.EqualError(t, err, "Attempting multiple sets of affinity for key 1234 to backend rw_vcore1 from rw_vcore0 on router dev_manager")
465}