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