@@ -29,6 +29,14 @@ import (
29
29
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/socketmask"
30
30
)
31
31
32
+ type mockAffinityStore struct {
33
+ hint topologymanager.TopologyHint
34
+ }
35
+
36
+ func (m * mockAffinityStore ) GetAffinity (podUID string , containerName string ) topologymanager.TopologyHint {
37
+ return m .hint
38
+ }
39
+
32
40
func makeNUMADevice (id string , numa int ) pluginapi.Device {
33
41
return pluginapi.Device {
34
42
ID : id ,
@@ -261,3 +269,211 @@ func TestGetTopologyHints(t *testing.T) {
261
269
}
262
270
}
263
271
}
272
+
273
+ func TestTopologyAlignedAllocation (t * testing.T ) {
274
+ tcases := []struct {
275
+ description string
276
+ resource string
277
+ request int
278
+ devices []pluginapi.Device
279
+ hint topologymanager.TopologyHint
280
+ expectedAllocation int
281
+ expectedAlignment map [int ]int
282
+ }{
283
+ {
284
+ description : "Single Request, no alignment" ,
285
+ resource : "resource" ,
286
+ request : 1 ,
287
+ devices : []pluginapi.Device {
288
+ {ID : "Dev1" },
289
+ {ID : "Dev2" },
290
+ },
291
+ hint : topologymanager.TopologyHint {
292
+ NUMANodeAffinity : makeSocketMask (0 , 1 ),
293
+ Preferred : true ,
294
+ },
295
+ expectedAllocation : 1 ,
296
+ expectedAlignment : map [int ]int {},
297
+ },
298
+ {
299
+ description : "Request for 1, partial alignment" ,
300
+ resource : "resource" ,
301
+ request : 1 ,
302
+ devices : []pluginapi.Device {
303
+ {ID : "Dev1" },
304
+ makeNUMADevice ("Dev2" , 1 ),
305
+ },
306
+ hint : topologymanager.TopologyHint {
307
+ NUMANodeAffinity : makeSocketMask (1 ),
308
+ Preferred : true ,
309
+ },
310
+ expectedAllocation : 1 ,
311
+ expectedAlignment : map [int ]int {1 : 1 },
312
+ },
313
+ {
314
+ description : "Single Request, socket 0" ,
315
+ resource : "resource" ,
316
+ request : 1 ,
317
+ devices : []pluginapi.Device {
318
+ makeNUMADevice ("Dev1" , 0 ),
319
+ makeNUMADevice ("Dev2" , 1 ),
320
+ },
321
+ hint : topologymanager.TopologyHint {
322
+ NUMANodeAffinity : makeSocketMask (0 ),
323
+ Preferred : true ,
324
+ },
325
+ expectedAllocation : 1 ,
326
+ expectedAlignment : map [int ]int {0 : 1 },
327
+ },
328
+ {
329
+ description : "Single Request, socket 1" ,
330
+ resource : "resource" ,
331
+ request : 1 ,
332
+ devices : []pluginapi.Device {
333
+ makeNUMADevice ("Dev1" , 0 ),
334
+ makeNUMADevice ("Dev2" , 1 ),
335
+ },
336
+ hint : topologymanager.TopologyHint {
337
+ NUMANodeAffinity : makeSocketMask (1 ),
338
+ Preferred : true ,
339
+ },
340
+ expectedAllocation : 1 ,
341
+ expectedAlignment : map [int ]int {1 : 1 },
342
+ },
343
+ {
344
+ description : "Request for 2, socket 0" ,
345
+ resource : "resource" ,
346
+ request : 2 ,
347
+ devices : []pluginapi.Device {
348
+ makeNUMADevice ("Dev1" , 0 ),
349
+ makeNUMADevice ("Dev2" , 1 ),
350
+ makeNUMADevice ("Dev3" , 0 ),
351
+ makeNUMADevice ("Dev4" , 1 ),
352
+ },
353
+ hint : topologymanager.TopologyHint {
354
+ NUMANodeAffinity : makeSocketMask (0 ),
355
+ Preferred : true ,
356
+ },
357
+ expectedAllocation : 2 ,
358
+ expectedAlignment : map [int ]int {0 : 2 },
359
+ },
360
+ {
361
+ description : "Request for 2, socket 1" ,
362
+ resource : "resource" ,
363
+ request : 2 ,
364
+ devices : []pluginapi.Device {
365
+ makeNUMADevice ("Dev1" , 0 ),
366
+ makeNUMADevice ("Dev2" , 1 ),
367
+ makeNUMADevice ("Dev3" , 0 ),
368
+ makeNUMADevice ("Dev4" , 1 ),
369
+ },
370
+ hint : topologymanager.TopologyHint {
371
+ NUMANodeAffinity : makeSocketMask (1 ),
372
+ Preferred : true ,
373
+ },
374
+ expectedAllocation : 2 ,
375
+ expectedAlignment : map [int ]int {1 : 2 },
376
+ },
377
+ {
378
+ description : "Request for 4, unsatisfiable, prefer socket 0" ,
379
+ resource : "resource" ,
380
+ request : 4 ,
381
+ devices : []pluginapi.Device {
382
+ makeNUMADevice ("Dev1" , 0 ),
383
+ makeNUMADevice ("Dev2" , 1 ),
384
+ makeNUMADevice ("Dev3" , 0 ),
385
+ makeNUMADevice ("Dev4" , 1 ),
386
+ makeNUMADevice ("Dev5" , 0 ),
387
+ makeNUMADevice ("Dev6" , 1 ),
388
+ },
389
+ hint : topologymanager.TopologyHint {
390
+ NUMANodeAffinity : makeSocketMask (0 ),
391
+ Preferred : true ,
392
+ },
393
+ expectedAllocation : 4 ,
394
+ expectedAlignment : map [int ]int {0 : 3 , 1 : 1 },
395
+ },
396
+ {
397
+ description : "Request for 4, unsatisfiable, prefer socket 1" ,
398
+ resource : "resource" ,
399
+ request : 4 ,
400
+ devices : []pluginapi.Device {
401
+ makeNUMADevice ("Dev1" , 0 ),
402
+ makeNUMADevice ("Dev2" , 1 ),
403
+ makeNUMADevice ("Dev3" , 0 ),
404
+ makeNUMADevice ("Dev4" , 1 ),
405
+ makeNUMADevice ("Dev5" , 0 ),
406
+ makeNUMADevice ("Dev6" , 1 ),
407
+ },
408
+ hint : topologymanager.TopologyHint {
409
+ NUMANodeAffinity : makeSocketMask (1 ),
410
+ Preferred : true ,
411
+ },
412
+ expectedAllocation : 4 ,
413
+ expectedAlignment : map [int ]int {0 : 1 , 1 : 3 },
414
+ },
415
+ {
416
+ description : "Request for 4, multisocket" ,
417
+ resource : "resource" ,
418
+ request : 4 ,
419
+ devices : []pluginapi.Device {
420
+ makeNUMADevice ("Dev1" , 0 ),
421
+ makeNUMADevice ("Dev2" , 1 ),
422
+ makeNUMADevice ("Dev3" , 2 ),
423
+ makeNUMADevice ("Dev4" , 3 ),
424
+ makeNUMADevice ("Dev5" , 0 ),
425
+ makeNUMADevice ("Dev6" , 1 ),
426
+ makeNUMADevice ("Dev7" , 2 ),
427
+ makeNUMADevice ("Dev8" , 3 ),
428
+ },
429
+ hint : topologymanager.TopologyHint {
430
+ NUMANodeAffinity : makeSocketMask (1 , 3 ),
431
+ Preferred : true ,
432
+ },
433
+ expectedAllocation : 4 ,
434
+ expectedAlignment : map [int ]int {1 : 2 , 3 : 2 },
435
+ },
436
+ }
437
+ for _ , tc := range tcases {
438
+ m := ManagerImpl {
439
+ allDevices : make (map [string ]map [string ]pluginapi.Device ),
440
+ healthyDevices : make (map [string ]sets.String ),
441
+ allocatedDevices : make (map [string ]sets.String ),
442
+ podDevices : make (podDevices ),
443
+ sourcesReady : & sourcesReadyStub {},
444
+ activePods : func () []* v1.Pod { return []* v1.Pod {} },
445
+ topologyAffinityStore : & mockAffinityStore {tc .hint },
446
+ }
447
+
448
+ m .allDevices [tc .resource ] = make (map [string ]pluginapi.Device )
449
+ m .healthyDevices [tc .resource ] = sets .NewString ()
450
+
451
+ for _ , d := range tc .devices {
452
+ m.allDevices [tc.resource ][d.ID ] = d
453
+ m .healthyDevices [tc .resource ].Insert (d .ID )
454
+ }
455
+
456
+ allocated , err := m .devicesToAllocate ("podUID" , "containerName" , tc .resource , tc .request , sets .NewString ())
457
+ if err != nil {
458
+ t .Errorf ("Unexpected error: %v" , err )
459
+ continue
460
+ }
461
+
462
+ if len (allocated ) != tc .expectedAllocation {
463
+ t .Errorf ("%v. expected allocation: %v but got: %v" , tc .description , tc .expectedAllocation , len (allocated ))
464
+ }
465
+
466
+ alignment := make (map [int ]int )
467
+ if m .deviceHasTopologyAlignment (tc .resource ) {
468
+ for d := range allocated {
469
+ if m.allDevices [tc.resource ][d ].Topology != nil {
470
+ alignment [int (m.allDevices [tc.resource ][d ].Topology .Nodes [0 ].ID )]++
471
+ }
472
+ }
473
+ }
474
+
475
+ if ! reflect .DeepEqual (alignment , tc .expectedAlignment ) {
476
+ t .Errorf ("%v. expected alignment: %v but got: %v" , tc .description , tc .expectedAlignment , alignment )
477
+ }
478
+ }
479
+ }
0 commit comments