1
16
17 package topologymanager
18
19 import (
20 "fmt"
21 "reflect"
22 "strings"
23 "testing"
24
25 cadvisorapi "github.com/google/cadvisor/info/v1"
26 "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
27 )
28
29 func TestNUMAInfo(t *testing.T) {
30 tcases := []struct {
31 name string
32 topology []cadvisorapi.Node
33 expectedNUMAInfo *NUMAInfo
34 expectedErr error
35 opts PolicyOptions
36 }{
37 {
38 name: "positive test 1 node",
39 topology: []cadvisorapi.Node{
40 {
41 Id: 0,
42 },
43 },
44 expectedNUMAInfo: &NUMAInfo{
45 Nodes: []int{0},
46 NUMADistances: NUMADistances{
47 0: nil,
48 },
49 },
50 opts: PolicyOptions{},
51 },
52 {
53 name: "positive test 1 node, with PreferClosestNUMA",
54 topology: []cadvisorapi.Node{
55 {
56 Id: 0,
57 Distances: []uint64{
58 10,
59 11,
60 12,
61 12,
62 },
63 },
64 },
65 expectedNUMAInfo: &NUMAInfo{
66 Nodes: []int{0},
67 NUMADistances: NUMADistances{
68 0: {
69 10,
70 11,
71 12,
72 12,
73 },
74 },
75 },
76 opts: PolicyOptions{
77 PreferClosestNUMA: true,
78 },
79 },
80 {
81 name: "positive test 2 nodes",
82 topology: []cadvisorapi.Node{
83 {
84 Id: 0,
85 },
86 {
87 Id: 1,
88 },
89 },
90 expectedNUMAInfo: &NUMAInfo{
91 Nodes: []int{0, 1},
92 NUMADistances: NUMADistances{
93 0: nil,
94 1: nil,
95 },
96 },
97 },
98 {
99 name: "positive test 2 nodes, with PreferClosestNUMA",
100 topology: []cadvisorapi.Node{
101 {
102 Id: 0,
103 Distances: []uint64{
104 10,
105 11,
106 12,
107 12,
108 },
109 },
110 {
111 Id: 1,
112 Distances: []uint64{
113 11,
114 10,
115 12,
116 12,
117 },
118 },
119 },
120 expectedNUMAInfo: &NUMAInfo{
121 Nodes: []int{0, 1},
122 NUMADistances: NUMADistances{
123 0: {
124 10,
125 11,
126 12,
127 12,
128 },
129 1: {
130 11,
131 10,
132 12,
133 12,
134 },
135 },
136 },
137 opts: PolicyOptions{
138 PreferClosestNUMA: true,
139 },
140 },
141 {
142 name: "positive test 3 nodes",
143 topology: []cadvisorapi.Node{
144 {
145 Id: 0,
146 },
147 {
148 Id: 1,
149 },
150 {
151 Id: 2,
152 },
153 },
154 expectedNUMAInfo: &NUMAInfo{
155 Nodes: []int{0, 1, 2},
156 NUMADistances: NUMADistances{
157 0: nil,
158 1: nil,
159 2: nil,
160 },
161 },
162 },
163 {
164 name: "positive test 3 nodes, with PreferClosestNUMA",
165 topology: []cadvisorapi.Node{
166 {
167 Id: 0,
168 Distances: []uint64{
169 10,
170 11,
171 12,
172 12,
173 },
174 },
175 {
176 Id: 1,
177 Distances: []uint64{
178 11,
179 10,
180 12,
181 12,
182 },
183 },
184 {
185 Id: 2,
186 Distances: []uint64{
187 12,
188 12,
189 10,
190 11,
191 },
192 },
193 },
194 expectedNUMAInfo: &NUMAInfo{
195 Nodes: []int{0, 1, 2},
196 NUMADistances: NUMADistances{
197 0: {
198 10,
199 11,
200 12,
201 12,
202 },
203 1: {
204 11,
205 10,
206 12,
207 12,
208 },
209 2: {
210 12,
211 12,
212 10,
213 11,
214 },
215 },
216 },
217 opts: PolicyOptions{
218 PreferClosestNUMA: true,
219 },
220 },
221 {
222 name: "positive test 4 nodes",
223 topology: []cadvisorapi.Node{
224 {
225 Id: 0,
226 },
227 {
228 Id: 1,
229 },
230 {
231 Id: 2,
232 },
233 {
234 Id: 3,
235 },
236 },
237 expectedNUMAInfo: &NUMAInfo{
238 Nodes: []int{0, 1, 2, 3},
239 NUMADistances: NUMADistances{
240 0: nil,
241 1: nil,
242 2: nil,
243 3: nil,
244 },
245 },
246 },
247 {
248 name: "positive test 4 nodes, with PreferClosestNUMA",
249 topology: []cadvisorapi.Node{
250 {
251 Id: 0,
252 Distances: []uint64{
253 10,
254 11,
255 12,
256 12,
257 },
258 },
259 {
260 Id: 1,
261 Distances: []uint64{
262 11,
263 10,
264 12,
265 12,
266 },
267 },
268 {
269 Id: 2,
270 Distances: []uint64{
271 12,
272 12,
273 10,
274 11,
275 },
276 },
277 {
278 Id: 3,
279 Distances: []uint64{
280 12,
281 12,
282 11,
283 10,
284 },
285 },
286 },
287 expectedNUMAInfo: &NUMAInfo{
288 Nodes: []int{0, 1, 2, 3},
289 NUMADistances: NUMADistances{
290 0: {
291 10,
292 11,
293 12,
294 12,
295 },
296 1: {
297 11,
298 10,
299 12,
300 12,
301 },
302 2: {
303 12,
304 12,
305 10,
306 11,
307 },
308 3: {
309 12,
310 12,
311 11,
312 10,
313 },
314 },
315 },
316 opts: PolicyOptions{
317 PreferClosestNUMA: true,
318 },
319 },
320 {
321 name: "negative test 1 node, no distance file with PreferClosestNUMA",
322 topology: []cadvisorapi.Node{
323 {
324 Id: 9,
325 },
326 },
327 expectedNUMAInfo: nil,
328 expectedErr: fmt.Errorf("error getting NUMA distances from cadvisor"),
329 opts: PolicyOptions{
330 PreferClosestNUMA: true,
331 },
332 },
333 {
334 name: "one node and its id is 1",
335 topology: []cadvisorapi.Node{
336 {
337 Id: 1,
338 },
339 },
340 expectedNUMAInfo: &NUMAInfo{
341 Nodes: []int{1},
342 NUMADistances: NUMADistances{
343 1: nil,
344 },
345 },
346 },
347 {
348 name: "one node and its id is 1, with PreferClosestNUMA",
349 topology: []cadvisorapi.Node{
350 {
351 Id: 1,
352 Distances: []uint64{
353 11,
354 10,
355 12,
356 12,
357 },
358 },
359 },
360 expectedNUMAInfo: &NUMAInfo{
361 Nodes: []int{1},
362 NUMADistances: NUMADistances{
363 1: {
364 11,
365 10,
366 12,
367 12,
368 },
369 },
370 },
371 opts: PolicyOptions{
372 PreferClosestNUMA: true,
373 },
374 },
375 {
376 name: "two nodes not sequential",
377 topology: []cadvisorapi.Node{
378 {
379 Id: 0,
380 Distances: []uint64{
381 10,
382 11,
383 12,
384 12,
385 },
386 },
387 {
388 Id: 2,
389 Distances: []uint64{
390 12,
391 12,
392 10,
393 11,
394 },
395 },
396 },
397 expectedNUMAInfo: &NUMAInfo{
398 Nodes: []int{0, 2},
399 NUMADistances: NUMADistances{
400 0: nil,
401 2: nil,
402 },
403 },
404 },
405 {
406 name: "two nodes not sequential, with PreferClosestNUMA",
407 topology: []cadvisorapi.Node{
408 {
409 Id: 0,
410 Distances: []uint64{
411 10,
412 11,
413 12,
414 12,
415 },
416 },
417 {
418 Id: 2,
419 Distances: []uint64{
420 12,
421 12,
422 10,
423 11,
424 },
425 },
426 },
427 expectedNUMAInfo: &NUMAInfo{
428 Nodes: []int{0, 2},
429 NUMADistances: NUMADistances{
430 0: {
431 10,
432 11,
433 12,
434 12,
435 },
436 2: {
437 12,
438 12,
439 10,
440 11,
441 },
442 },
443 },
444 opts: PolicyOptions{
445 PreferClosestNUMA: true,
446 },
447 },
448 }
449
450 for _, tcase := range tcases {
451 topology, err := NewNUMAInfo(tcase.topology, tcase.opts)
452 if tcase.expectedErr == nil && err != nil {
453 t.Fatalf("Expected err to equal nil, not %v", err)
454 } else if tcase.expectedErr != nil && err == nil {
455 t.Fatalf("Expected err to equal %v, not nil", tcase.expectedErr)
456 } else if tcase.expectedErr != nil {
457 if !strings.Contains(err.Error(), tcase.expectedErr.Error()) {
458 t.Errorf("Unexpected error message. Have: %s wants %s", err.Error(), tcase.expectedErr.Error())
459 }
460 }
461
462 if !reflect.DeepEqual(topology, tcase.expectedNUMAInfo) {
463 t.Fatalf("Expected topology to equal %v, not %v", tcase.expectedNUMAInfo, topology)
464 }
465
466 }
467 }
468
469 func TestCalculateAvgDistanceFor(t *testing.T) {
470 tcases := []struct {
471 name string
472 bm []int
473 distance NUMADistances
474 expectedAvg float64
475 }{
476 {
477 name: "1 NUMA node",
478 bm: []int{
479 0,
480 },
481 distance: NUMADistances{
482 0: {
483 10,
484 },
485 },
486 expectedAvg: 10,
487 },
488 {
489 name: "2 NUMA node, 1 set in bitmask",
490 bm: []int{
491 0,
492 },
493 distance: NUMADistances{
494 0: {
495 10,
496 11,
497 },
498 1: {
499 11,
500 10,
501 },
502 },
503 expectedAvg: 10,
504 },
505 {
506 name: "2 NUMA node, 2 set in bitmask",
507 bm: []int{
508 0,
509 1,
510 },
511 distance: NUMADistances{
512 0: {
513 10,
514 11,
515 },
516 1: {
517 11,
518 10,
519 },
520 },
521 expectedAvg: 10.5,
522 },
523 {
524 name: "4 NUMA node, 2 set in bitmask",
525 bm: []int{
526 0,
527 2,
528 },
529 distance: NUMADistances{
530 0: {
531 10,
532 11,
533 12,
534 12,
535 },
536 1: {
537 11,
538 10,
539 12,
540 12,
541 },
542 2: {
543 12,
544 12,
545 10,
546 11,
547 },
548 3: {
549 12,
550 12,
551 11,
552 10,
553 },
554 },
555 expectedAvg: 11,
556 },
557 {
558 name: "4 NUMA node, 3 set in bitmask",
559 bm: []int{
560 0,
561 2,
562 3,
563 },
564 distance: NUMADistances{
565 0: {
566 10,
567 11,
568 12,
569 12,
570 },
571 1: {
572 11,
573 10,
574 12,
575 12,
576 },
577 2: {
578 12,
579 12,
580 10,
581 11,
582 },
583 3: {
584 12,
585 12,
586 11,
587 10,
588 },
589 },
590 expectedAvg: 11.11111111111111,
591 },
592 {
593 name: "0 NUMA node, 0 set in bitmask",
594 bm: []int{},
595 distance: NUMADistances{},
596 expectedAvg: 0,
597 },
598 }
599
600 for _, tcase := range tcases {
601 bm, err := bitmask.NewBitMask(tcase.bm...)
602 if err != nil {
603 t.Errorf("no error expected got %v", err)
604 }
605
606 numaInfo := NUMAInfo{
607 Nodes: tcase.bm,
608 NUMADistances: tcase.distance,
609 }
610
611 result := numaInfo.NUMADistances.CalculateAverageFor(bm)
612 if result != tcase.expectedAvg {
613 t.Errorf("Expected result to equal %g, not %g", tcase.expectedAvg, result)
614 }
615 }
616
617 }
618
619 func TestClosest(t *testing.T) {
620 tcases := []struct {
621 description string
622 current bitmask.BitMask
623 candidate bitmask.BitMask
624 expected string
625 numaInfo *NUMAInfo
626 }{
627 {
628 description: "current and candidate length is not the same, current narrower",
629 current: NewTestBitMask(0),
630 candidate: NewTestBitMask(0, 2),
631 expected: "current",
632 numaInfo: &NUMAInfo{},
633 },
634 {
635 description: "current and candidate length is the same, distance is the same, current more lower bits set",
636 current: NewTestBitMask(0, 1),
637 candidate: NewTestBitMask(0, 2),
638 expected: "current",
639 numaInfo: &NUMAInfo{
640 NUMADistances: NUMADistances{
641 0: {10, 10, 10},
642 1: {10, 10, 10},
643 2: {10, 10, 10},
644 },
645 },
646 },
647 {
648 description: "current and candidate length is the same, distance is the same, candidate more lower bits set",
649 current: NewTestBitMask(0, 3),
650 candidate: NewTestBitMask(0, 2),
651 expected: "candidate",
652 numaInfo: &NUMAInfo{
653 NUMADistances: NUMADistances{
654 0: {10, 10, 10, 10},
655 1: {10, 10, 10, 10},
656 2: {10, 10, 10, 10},
657 3: {10, 10, 10, 10},
658 },
659 },
660 },
661 {
662 description: "current and candidate length is the same, candidate average distance is smaller",
663 current: NewTestBitMask(0, 3),
664 candidate: NewTestBitMask(0, 1),
665 expected: "candidate",
666 numaInfo: &NUMAInfo{
667 NUMADistances: NUMADistances{
668 0: {10, 11, 12, 12},
669 1: {11, 10, 12, 12},
670 2: {12, 12, 10, 11},
671 3: {12, 12, 11, 10},
672 },
673 },
674 },
675 {
676 description: "current and candidate length is the same, current average distance is smaller",
677 current: NewTestBitMask(2, 3),
678 candidate: NewTestBitMask(0, 3),
679 expected: "current",
680 numaInfo: &NUMAInfo{
681 NUMADistances: NUMADistances{
682 0: {10, 11, 12, 12},
683 1: {11, 10, 12, 12},
684 2: {12, 12, 10, 11},
685 3: {12, 12, 11, 10},
686 },
687 },
688 },
689 }
690
691 for _, tc := range tcases {
692 t.Run(tc.description, func(t *testing.T) {
693
694 result := tc.numaInfo.Closest(tc.candidate, tc.current)
695 if result != tc.current && result != tc.candidate {
696 t.Errorf("Expected result to be either 'current' or 'candidate' hint")
697 }
698 if tc.expected == "current" && result != tc.current {
699 t.Errorf("Expected result to be %v, got %v", tc.current, result)
700 }
701 if tc.expected == "candidate" && result != tc.candidate {
702 t.Errorf("Expected result to be %v, got %v", tc.candidate, result)
703 }
704 })
705 }
706 }
707
View as plain text