github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/qrm-plugins/network/staticpolicy/policy_test.go (about) 1 /* 2 Copyright 2022 The Katalyst Authors. 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 */ 16 17 package staticpolicy 18 19 import ( 20 "context" 21 "fmt" 22 "io/ioutil" 23 "net" 24 "os" 25 "sort" 26 "testing" 27 28 info "github.com/google/cadvisor/info/v1" 29 "github.com/stretchr/testify/assert" 30 "github.com/stretchr/testify/require" 31 v1 "k8s.io/api/core/v1" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/runtime" 34 "k8s.io/apimachinery/pkg/util/uuid" 35 pluginapi "k8s.io/kubelet/pkg/apis/resourceplugin/v1alpha1" 36 37 apinode "github.com/kubewharf/katalyst-api/pkg/apis/node/v1alpha1" 38 "github.com/kubewharf/katalyst-api/pkg/consts" 39 katalyst_base "github.com/kubewharf/katalyst-core/cmd/base" 40 "github.com/kubewharf/katalyst-core/cmd/katalyst-agent/app/agent" 41 "github.com/kubewharf/katalyst-core/cmd/katalyst-agent/app/options" 42 "github.com/kubewharf/katalyst-core/pkg/agent/qrm-plugins/network/state" 43 "github.com/kubewharf/katalyst-core/pkg/agent/qrm-plugins/util" 44 "github.com/kubewharf/katalyst-core/pkg/config" 45 "github.com/kubewharf/katalyst-core/pkg/metaserver" 46 metaserveragent "github.com/kubewharf/katalyst-core/pkg/metaserver/agent" 47 "github.com/kubewharf/katalyst-core/pkg/metaserver/agent/pod" 48 "github.com/kubewharf/katalyst-core/pkg/metaserver/external" 49 "github.com/kubewharf/katalyst-core/pkg/metrics" 50 "github.com/kubewharf/katalyst-core/pkg/util/machine" 51 ) 52 53 const ( 54 testSharedNetClsId = "12345" 55 testReclaimedNetClsId = "12346" 56 57 testDefaultSharedNetClsId = 22345 58 testDefaultReclaimedNetClsId = 22346 59 testDefaultDedicatedNetClsId = 22347 60 61 testIPv4ResourceAllocationAnnotationKey = "qrm.katalyst.kubewharf.io/inet_addr" 62 testIPv6ResourceAllocationAnnotationKey = "qrm.katalyst.kubewharf.io/inet_addr_ipv6" 63 testNetNSPathResourceAllocationAnnotationKey = "qrm.katalyst.kubewharf.io/netns_path" 64 testNetInterfaceNameResourceAllocationAnnotationKey = "qrm.katalyst.kubewharf.io/nic_name" 65 testNetClassIDResourceAllocationAnnotationKey = "qrm.katalyst.kubewharf.io/netcls_id" 66 testNetBandwidthResourceAllocationAnnotationKey = "qrm.katalyst.kubewharf.io/net_bandwidth" 67 68 testHostPreferEnhancementValue = "{\"namespace_type\": \"host_ns_preferred\"}" 69 testNotHostPreferEnhancementValue = "{\"namespace_type\": \"anti_host_ns_preferred\"}" 70 testHostEnhancementValue = "{\"namespace_type\": \"host_ns\"}" 71 72 testEth0Name = "eth0" 73 testEth0AffinitiveNUMANode = 0 74 testEth0NSAbsolutePath = "" 75 testEth0NSName = "" 76 77 testEth1Name = "eth1" 78 testEth1AffinitiveNUMANode = 1 79 80 testEth2Name = "eth2" 81 testEth2AffinitiveNUMANode = 2 82 testEth2NSAbsolutePath = "/var/run/ns2" 83 testEth2NSName = "ns2" 84 ) 85 86 var ( 87 testEth0IPv4 = net.ParseIP("1.1.1.1").String() 88 testEth2IPv6 = net.ParseIP("::ffff:192.0.2.1").String() 89 ) 90 91 func generateTestConfiguration(t *testing.T) *config.Configuration { 92 testConfiguration, err := options.NewOptions().Config() 93 require.NoError(t, err) 94 require.NotNil(t, testConfiguration) 95 return testConfiguration 96 } 97 98 func makeMetaServer() *metaserver.MetaServer { 99 cpuTopology, _ := machine.GenerateDummyCPUTopology(16, 2, 4) 100 101 return &metaserver.MetaServer{ 102 MetaAgent: &metaserveragent.MetaAgent{ 103 KatalystMachineInfo: &machine.KatalystMachineInfo{ 104 CPUTopology: cpuTopology, 105 ExtraNetworkInfo: &machine.ExtraNetworkInfo{}, 106 }, 107 }, 108 ExternalManager: external.InitExternalManager(&pod.PodFetcherStub{}), 109 } 110 } 111 112 func makeTestGenericContext(t *testing.T) *agent.GenericContext { 113 genericCtx, err := katalyst_base.GenerateFakeGenericContext([]runtime.Object{}) 114 assert.NoError(t, err) 115 116 return &agent.GenericContext{ 117 GenericContext: genericCtx, 118 MetaServer: makeMetaServer(), 119 PluginManager: nil, 120 } 121 } 122 123 func makeStaticPolicy(t *testing.T, hasNic bool) *StaticPolicy { 124 agentCtx := makeTestGenericContext(t) 125 wrappedEmitter := agentCtx.EmitterPool.GetDefaultMetricsEmitter().WithTags(NetworkResourcePluginPolicyNameStatic, metrics.MetricTag{ 126 Key: util.QRMPluginPolicyTagName, 127 Val: NetworkResourcePluginPolicyNameStatic, 128 }) 129 130 cpuTopology, err := machine.GenerateDummyCPUTopology(16, 2, 4) 131 assert.NoError(t, err) 132 agentCtx.KatalystMachineInfo.CPUTopology = cpuTopology 133 134 mockQrmConfig := generateTestConfiguration(t).QRMPluginsConfiguration 135 mockQrmConfig.ReservedBandwidth = 4000 136 mockQrmConfig.EgressCapacityRate = 0.9 137 mockQrmConfig.IngressCapacityRate = 0.85 138 139 nics := makeNICs(hasNic) 140 availableNICs := filterNICsByAvailability(nics, nil, nil) 141 reservation := make(map[string]uint32) 142 if hasNic { 143 assert.Len(t, availableNICs, 2) 144 145 expectedReservation := map[string]uint32{ 146 testEth0Name: 4000, 147 } 148 var err error 149 reservation, err = getReservedBandwidth(availableNICs, mockQrmConfig.ReservedBandwidth, FirstNIC) 150 assert.NoError(t, err) 151 assert.Equal(t, expectedReservation, reservation) 152 } 153 154 tmpDir, err := ioutil.TempDir("", "checkpoint") 155 assert.NoError(t, err) 156 defer os.RemoveAll(tmpDir) 157 158 stateImpl, err := state.NewCheckpointState(mockQrmConfig, tmpDir, NetworkPluginStateFileName, 159 NetworkResourcePluginPolicyNameStatic, &info.MachineInfo{}, availableNICs, reservation, false) 160 assert.NoError(t, err) 161 162 return &StaticPolicy{ 163 qosConfig: generateTestConfiguration(t).QoSConfiguration, 164 qrmConfig: mockQrmConfig, 165 emitter: wrappedEmitter, 166 metaServer: agentCtx.MetaServer, 167 stopCh: make(chan struct{}), 168 name: fmt.Sprintf("%s_%s", "qrm_network_plugin", NetworkResourcePluginPolicyNameStatic), 169 qosLevelToNetClassMap: map[string]uint32{ 170 consts.PodAnnotationQoSLevelSharedCores: testDefaultSharedNetClsId, 171 consts.PodAnnotationQoSLevelReclaimedCores: testDefaultReclaimedNetClsId, 172 consts.PodAnnotationQoSLevelDedicatedCores: testDefaultDedicatedNetClsId, 173 }, 174 agentCtx: agentCtx, 175 nics: availableNICs, 176 state: stateImpl, 177 podLevelNetClassAnnoKey: consts.PodAnnotationNetClassKey, 178 podLevelNetAttributesAnnoKeys: []string{}, 179 ipv4ResourceAllocationAnnotationKey: testIPv4ResourceAllocationAnnotationKey, 180 ipv6ResourceAllocationAnnotationKey: testIPv6ResourceAllocationAnnotationKey, 181 netNSPathResourceAllocationAnnotationKey: testNetNSPathResourceAllocationAnnotationKey, 182 netInterfaceNameResourceAllocationAnnotationKey: testNetInterfaceNameResourceAllocationAnnotationKey, 183 netClassIDResourceAllocationAnnotationKey: testNetClassIDResourceAllocationAnnotationKey, 184 netBandwidthResourceAllocationAnnotationKey: testNetBandwidthResourceAllocationAnnotationKey, 185 } 186 } 187 188 func makeNICs(hasNics bool) []machine.InterfaceInfo { 189 v4 := net.ParseIP(testEth0IPv4) 190 v6 := net.ParseIP(testEth2IPv6) 191 192 if hasNics { 193 return []machine.InterfaceInfo{ 194 { 195 Iface: testEth0Name, 196 Speed: 25000, 197 NumaNode: testEth0AffinitiveNUMANode, 198 Enable: true, 199 Addr: &machine.IfaceAddr{ 200 IPV4: []*net.IP{&v4}, 201 }, 202 NSAbsolutePath: testEth0NSAbsolutePath, 203 NSName: testEth0NSName, 204 }, 205 { 206 Iface: testEth1Name, 207 Speed: 25000, 208 NumaNode: testEth1AffinitiveNUMANode, 209 Enable: false, 210 Addr: &machine.IfaceAddr{}, 211 }, 212 { 213 Iface: testEth2Name, 214 Speed: 25000, 215 NumaNode: testEth2AffinitiveNUMANode, 216 Enable: true, 217 Addr: &machine.IfaceAddr{ 218 IPV6: []*net.IP{&v6}, 219 }, 220 NSAbsolutePath: testEth2NSAbsolutePath, 221 NSName: testEth2NSName, 222 }, 223 } 224 } else { 225 return []machine.InterfaceInfo{ 226 { 227 Iface: testEth0Name, 228 Speed: 25000, 229 NumaNode: testEth0AffinitiveNUMANode, 230 Enable: false, 231 Addr: &machine.IfaceAddr{ 232 IPV4: []*net.IP{&v4}, 233 }, 234 NSAbsolutePath: testEth0NSAbsolutePath, 235 NSName: testEth0NSName, 236 }, 237 { 238 Iface: testEth1Name, 239 Speed: 25000, 240 NumaNode: testEth1AffinitiveNUMANode, 241 Enable: false, 242 Addr: &machine.IfaceAddr{}, 243 }, 244 { 245 Iface: testEth2Name, 246 Speed: 25000, 247 NumaNode: testEth2AffinitiveNUMANode, 248 Enable: false, 249 Addr: &machine.IfaceAddr{ 250 IPV6: []*net.IP{&v6}, 251 }, 252 NSAbsolutePath: testEth2NSAbsolutePath, 253 NSName: testEth2NSName, 254 }, 255 } 256 } 257 } 258 259 func TestNewStaticPolicy(t *testing.T) { 260 t.Parallel() 261 262 agentCtx := makeTestGenericContext(t) 263 agentCtx.KatalystMachineInfo.ExtraNetworkInfo.Interface = makeNICs(true) 264 agentCtx.MachineInfo = &info.MachineInfo{} 265 266 conf := generateTestConfiguration(t) 267 conf.QRMPluginsConfiguration.ReservedBandwidth = 4000 268 conf.QRMPluginsConfiguration.EgressCapacityRate = 0.9 269 conf.QRMPluginsConfiguration.IngressCapacityRate = 0.85 270 271 tmpDir, err := ioutil.TempDir("", "checkpoint") 272 assert.NoError(t, err) 273 defer os.RemoveAll(tmpDir) 274 conf.GenericQRMPluginConfiguration.StateFileDirectory = tmpDir 275 276 neetToRun, policy, err := NewStaticPolicy(agentCtx, conf, nil, NetworkResourcePluginPolicyNameStatic) 277 assert.NoError(t, err) 278 assert.NotNil(t, policy) 279 assert.True(t, neetToRun) 280 281 // no valid nics on this node 282 agentCtxInvalid := agentCtx 283 agentCtxInvalid.KatalystMachineInfo.ExtraNetworkInfo.Interface = makeNICs(false) 284 neetToRun, policy, err = NewStaticPolicy(agentCtxInvalid, conf, nil, NetworkResourcePluginPolicyNameStatic) 285 assert.NoError(t, err) 286 assert.NotNil(t, policy) 287 assert.True(t, neetToRun) 288 } 289 290 func TestRemovePod(t *testing.T) { 291 t.Parallel() 292 293 policy := makeStaticPolicy(t, true) 294 assert.NotNil(t, policy) 295 296 podID := string(uuid.NewUUID()) 297 testName := "test" 298 var bwReq float64 = 5000 299 300 // create a new Pod with bandwidth request 301 addReq := &pluginapi.ResourceRequest{ 302 PodUid: podID, 303 PodNamespace: testName, 304 PodName: testName, 305 ContainerName: testName, 306 ContainerType: pluginapi.ContainerType_MAIN, 307 ContainerIndex: 0, 308 ResourceName: string(consts.ResourceNetBandwidth), 309 Hint: &pluginapi.TopologyHint{ 310 Nodes: []uint64{0, 1}, 311 Preferred: true, 312 }, 313 ResourceRequests: map[string]float64{ 314 string(consts.ResourceNetBandwidth): bwReq, 315 }, 316 Labels: map[string]string{ 317 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 318 }, 319 Annotations: map[string]string{ 320 consts.PodAnnotationNetClassKey: testSharedNetClsId, 321 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 322 consts.PodAnnotationNetworkEnhancementKey: testHostPreferEnhancementValue, 323 }, 324 } 325 326 resp, err := policy.Allocate(context.Background(), addReq) 327 328 // verify the state 329 allocationInfo := policy.state.GetAllocationInfo(podID, testName) 330 machineState := policy.state.GetMachineState() 331 podEntries := policy.state.GetPodEntries() 332 assert.NoError(t, err) 333 assert.NotNil(t, resp) 334 assert.Equal(t, allocationInfo.IfName, testEth0Name) 335 assert.Equal(t, allocationInfo.Egress, uint32(bwReq)) 336 assert.Equal(t, allocationInfo.Ingress, uint32(bwReq)) 337 assert.Len(t, machineState, 2) 338 assert.Len(t, machineState[testEth0Name].PodEntries, 1) 339 assert.EqualValues(t, machineState[testEth0Name].PodEntries[podID][testName], allocationInfo) 340 assert.Len(t, podEntries, 1) 341 assert.EqualValues(t, podEntries, machineState[testEth0Name].PodEntries) 342 343 // remove the pod 344 delReq := &pluginapi.RemovePodRequest{ 345 PodUid: podID, 346 } 347 348 _, err = policy.RemovePod(context.TODO(), delReq) 349 assert.NoError(t, err) 350 351 // verify the state again 352 allocationInfo = policy.state.GetAllocationInfo(podID, testName) 353 machineState = policy.state.GetMachineState() 354 podEntries = policy.state.GetPodEntries() 355 assert.Nil(t, allocationInfo) 356 assert.Len(t, machineState, 2) 357 assert.Len(t, machineState[testEth0Name].PodEntries, 0) 358 assert.Len(t, podEntries, 0) 359 } 360 361 func TestAllocate(t *testing.T) { 362 t.Parallel() 363 364 testName := "test" 365 366 // common cases 367 testCases := []struct { 368 description string 369 noError bool 370 req *pluginapi.ResourceRequest 371 expectedResp *pluginapi.ResourceAllocationResponse 372 }{ 373 { 374 description: "req for init container", 375 noError: true, 376 req: &pluginapi.ResourceRequest{ 377 PodUid: string(uuid.NewUUID()), 378 PodNamespace: testName, 379 PodName: testName, 380 ContainerName: testName, 381 ContainerType: pluginapi.ContainerType_INIT, 382 ContainerIndex: 0, 383 ResourceName: string(consts.ResourceNetBandwidth), 384 ResourceRequests: map[string]float64{ 385 string(consts.ResourceNetBandwidth): 5000, 386 }, 387 Labels: map[string]string{ 388 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 389 }, 390 Annotations: map[string]string{ 391 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 392 }, 393 }, 394 expectedResp: &pluginapi.ResourceAllocationResponse{ 395 PodNamespace: testName, 396 PodName: testName, 397 ContainerName: testName, 398 ContainerType: pluginapi.ContainerType_INIT, 399 ContainerIndex: 0, 400 ResourceName: string(consts.ResourceNetBandwidth), 401 AllocationResult: nil, 402 Labels: map[string]string{ 403 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 404 }, 405 Annotations: map[string]string{ 406 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 407 }, 408 }, 409 }, 410 { 411 description: "req for shared_cores main container with host netns preference", 412 noError: true, 413 req: &pluginapi.ResourceRequest{ 414 PodUid: string(uuid.NewUUID()), 415 PodNamespace: testName, 416 PodName: testName, 417 ContainerName: testName, 418 ContainerType: pluginapi.ContainerType_MAIN, 419 ContainerIndex: 0, 420 ResourceName: string(consts.ResourceNetBandwidth), 421 Hint: &pluginapi.TopologyHint{ 422 Nodes: []uint64{0, 1}, 423 Preferred: true, 424 }, 425 ResourceRequests: map[string]float64{ 426 string(consts.ResourceNetBandwidth): 5000, 427 }, 428 Labels: map[string]string{ 429 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 430 }, 431 Annotations: map[string]string{ 432 consts.PodAnnotationNetClassKey: testSharedNetClsId, 433 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 434 consts.PodAnnotationNetworkEnhancementKey: testHostPreferEnhancementValue, 435 }, 436 }, 437 expectedResp: &pluginapi.ResourceAllocationResponse{ 438 PodNamespace: testName, 439 PodName: testName, 440 ContainerName: testName, 441 ContainerType: pluginapi.ContainerType_MAIN, 442 ContainerIndex: 0, 443 ResourceName: string(consts.ResourceNetBandwidth), 444 AllocationResult: &pluginapi.ResourceAllocation{ 445 ResourceAllocation: map[string]*pluginapi.ResourceAllocationInfo{ 446 string(consts.ResourceNetBandwidth): { 447 IsNodeResource: true, 448 IsScalarResource: true, 449 AllocatedQuantity: 5000, 450 AllocationResult: machine.NewCPUSet(0, 1).String(), 451 Annotations: map[string]string{ 452 testIPv4ResourceAllocationAnnotationKey: testEth0IPv4, 453 testIPv6ResourceAllocationAnnotationKey: "", 454 testNetInterfaceNameResourceAllocationAnnotationKey: testEth0Name, 455 testNetClassIDResourceAllocationAnnotationKey: testSharedNetClsId, 456 testNetBandwidthResourceAllocationAnnotationKey: "5000", 457 }, 458 ResourceHints: &pluginapi.ListOfTopologyHints{ 459 Hints: []*pluginapi.TopologyHint{ 460 { 461 Nodes: []uint64{0, 1}, 462 Preferred: true, 463 }, 464 }, 465 }, 466 }, 467 }, 468 }, 469 Labels: map[string]string{ 470 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 471 }, 472 Annotations: map[string]string{ 473 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 474 consts.PodAnnotationNetworkEnhancementNamespaceType: consts.PodAnnotationNetworkEnhancementNamespaceTypeHostPrefer, 475 }, 476 }, 477 }, 478 { 479 description: "req for reclaimed_cores main container with not host netns preference", 480 noError: true, 481 req: &pluginapi.ResourceRequest{ 482 PodUid: string(uuid.NewUUID()), 483 PodNamespace: testName, 484 PodName: testName, 485 ContainerName: testName, 486 ContainerType: pluginapi.ContainerType_MAIN, 487 ContainerIndex: 0, 488 ResourceName: string(consts.ResourceNetBandwidth), 489 Hint: &pluginapi.TopologyHint{ 490 Nodes: []uint64{2, 3}, 491 Preferred: true, 492 }, 493 ResourceRequests: map[string]float64{ 494 string(consts.ResourceNetBandwidth): 5000, 495 }, 496 Annotations: map[string]string{ 497 consts.PodAnnotationNetClassKey: testReclaimedNetClsId, 498 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelReclaimedCores, 499 consts.PodAnnotationNetworkEnhancementKey: testNotHostPreferEnhancementValue, 500 }, 501 Labels: map[string]string{ 502 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelReclaimedCores, 503 }, 504 }, 505 expectedResp: &pluginapi.ResourceAllocationResponse{ 506 PodNamespace: testName, 507 PodName: testName, 508 ContainerName: testName, 509 ContainerType: pluginapi.ContainerType_MAIN, 510 ContainerIndex: 0, 511 ResourceName: string(consts.ResourceNetBandwidth), 512 AllocationResult: &pluginapi.ResourceAllocation{ 513 ResourceAllocation: map[string]*pluginapi.ResourceAllocationInfo{ 514 string(consts.ResourceNetBandwidth): { 515 IsNodeResource: true, 516 IsScalarResource: true, 517 AllocatedQuantity: 5000, 518 AllocationResult: machine.NewCPUSet(2, 3).String(), 519 Annotations: map[string]string{ 520 testIPv4ResourceAllocationAnnotationKey: "", 521 testIPv6ResourceAllocationAnnotationKey: testEth2IPv6, 522 testNetNSPathResourceAllocationAnnotationKey: testEth2NSAbsolutePath, 523 testNetInterfaceNameResourceAllocationAnnotationKey: testEth2Name, 524 testNetClassIDResourceAllocationAnnotationKey: testReclaimedNetClsId, 525 testNetBandwidthResourceAllocationAnnotationKey: "5000", 526 }, 527 ResourceHints: &pluginapi.ListOfTopologyHints{ 528 Hints: []*pluginapi.TopologyHint{ 529 { 530 Nodes: []uint64{2, 3}, 531 Preferred: true, 532 }, 533 }, 534 }, 535 }, 536 }, 537 }, 538 Labels: map[string]string{ 539 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelReclaimedCores, 540 }, 541 Annotations: map[string]string{ 542 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelReclaimedCores, 543 consts.PodAnnotationNetworkEnhancementNamespaceType: consts.PodAnnotationNetworkEnhancementNamespaceTypeNotHostPrefer, 544 }, 545 }, 546 }, 547 { 548 description: "req for dedicated_cores main container with host netns guarantee", 549 noError: true, 550 req: &pluginapi.ResourceRequest{ 551 PodUid: string(uuid.NewUUID()), 552 PodNamespace: testName, 553 PodName: testName, 554 ContainerName: testName, 555 ContainerType: pluginapi.ContainerType_MAIN, 556 ContainerIndex: 0, 557 ResourceName: string(consts.ResourceNetBandwidth), 558 Hint: &pluginapi.TopologyHint{ 559 Nodes: []uint64{0, 1}, 560 Preferred: true, 561 }, 562 ResourceRequests: map[string]float64{ 563 string(consts.ResourceNetBandwidth): 5000, 564 }, 565 Annotations: map[string]string{ 566 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 567 consts.PodAnnotationNetworkEnhancementKey: testHostEnhancementValue, 568 }, 569 Labels: map[string]string{ 570 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 571 }, 572 }, 573 expectedResp: &pluginapi.ResourceAllocationResponse{ 574 PodNamespace: testName, 575 PodName: testName, 576 ContainerName: testName, 577 ContainerType: pluginapi.ContainerType_MAIN, 578 ContainerIndex: 0, 579 ResourceName: string(consts.ResourceNetBandwidth), 580 AllocationResult: &pluginapi.ResourceAllocation{ 581 ResourceAllocation: map[string]*pluginapi.ResourceAllocationInfo{ 582 string(consts.ResourceNetBandwidth): { 583 IsNodeResource: true, 584 IsScalarResource: true, 585 AllocatedQuantity: 5000, 586 AllocationResult: machine.NewCPUSet(0, 1).String(), 587 Annotations: map[string]string{ 588 testIPv4ResourceAllocationAnnotationKey: testEth0IPv4, 589 testIPv6ResourceAllocationAnnotationKey: "", 590 testNetInterfaceNameResourceAllocationAnnotationKey: testEth0Name, 591 testNetClassIDResourceAllocationAnnotationKey: fmt.Sprintf("%d", testDefaultDedicatedNetClsId), 592 testNetBandwidthResourceAllocationAnnotationKey: "5000", 593 }, 594 ResourceHints: &pluginapi.ListOfTopologyHints{ 595 Hints: []*pluginapi.TopologyHint{ 596 { 597 Nodes: []uint64{0, 1}, 598 Preferred: true, 599 }, 600 }, 601 }, 602 }, 603 }, 604 }, 605 Labels: map[string]string{ 606 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 607 }, 608 Annotations: map[string]string{ 609 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 610 consts.PodAnnotationNetworkEnhancementNamespaceType: consts.PodAnnotationNetworkEnhancementNamespaceTypeHost, 611 }, 612 }, 613 }, 614 { 615 description: "req for dedicated_cores main container with host netns guarantee and exceeded bandwidth over the 1st NIC", 616 noError: false, 617 req: &pluginapi.ResourceRequest{ 618 PodUid: string(uuid.NewUUID()), 619 PodNamespace: testName, 620 PodName: testName, 621 ContainerName: testName, 622 ContainerType: pluginapi.ContainerType_MAIN, 623 ContainerIndex: 0, 624 ResourceName: string(consts.ResourceNetBandwidth), 625 Hint: &pluginapi.TopologyHint{ 626 Nodes: []uint64{0, 1}, 627 Preferred: true, 628 }, 629 ResourceRequests: map[string]float64{ 630 string(consts.ResourceNetBandwidth): 20000, 631 }, 632 Annotations: map[string]string{ 633 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 634 consts.PodAnnotationNetworkEnhancementKey: testHostEnhancementValue, 635 }, 636 Labels: map[string]string{ 637 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 638 }, 639 }, 640 expectedResp: nil, 641 }, 642 { 643 description: "req for dedicated_cores main container with host netns guarantee and exceeded bandwidth over the 1st NIC which is preferred", 644 noError: false, 645 req: &pluginapi.ResourceRequest{ 646 PodUid: string(uuid.NewUUID()), 647 PodNamespace: testName, 648 PodName: testName, 649 ContainerName: testName, 650 ContainerType: pluginapi.ContainerType_MAIN, 651 ContainerIndex: 0, 652 ResourceName: string(consts.ResourceNetBandwidth), 653 Hint: &pluginapi.TopologyHint{ 654 Nodes: []uint64{0, 1}, 655 Preferred: true, 656 }, 657 ResourceRequests: map[string]float64{ 658 string(consts.ResourceNetBandwidth): 20000, 659 }, 660 Annotations: map[string]string{ 661 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 662 consts.PodAnnotationNetworkEnhancementKey: testHostPreferEnhancementValue, 663 }, 664 Labels: map[string]string{ 665 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 666 }, 667 }, 668 expectedResp: &pluginapi.ResourceAllocationResponse{ 669 PodNamespace: testName, 670 PodName: testName, 671 ContainerName: testName, 672 ContainerType: pluginapi.ContainerType_MAIN, 673 ContainerIndex: 0, 674 ResourceName: string(consts.ResourceNetBandwidth), 675 AllocationResult: &pluginapi.ResourceAllocation{ 676 ResourceAllocation: map[string]*pluginapi.ResourceAllocationInfo{ 677 string(consts.ResourceNetBandwidth): { 678 IsNodeResource: true, 679 IsScalarResource: true, 680 AllocatedQuantity: 20000, 681 AllocationResult: machine.NewCPUSet(2, 3).String(), 682 Annotations: map[string]string{ 683 testIPv4ResourceAllocationAnnotationKey: testEth2IPv6, 684 testIPv6ResourceAllocationAnnotationKey: "", 685 testNetInterfaceNameResourceAllocationAnnotationKey: testEth2Name, 686 testNetClassIDResourceAllocationAnnotationKey: fmt.Sprintf("%d", testDefaultDedicatedNetClsId), 687 testNetBandwidthResourceAllocationAnnotationKey: "20000", 688 }, 689 ResourceHints: &pluginapi.ListOfTopologyHints{ 690 Hints: []*pluginapi.TopologyHint{ 691 { 692 Nodes: []uint64{2, 3}, 693 Preferred: false, 694 }, 695 }, 696 }, 697 }, 698 }, 699 }, 700 Labels: map[string]string{ 701 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 702 }, 703 Annotations: map[string]string{ 704 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 705 consts.PodAnnotationNetworkEnhancementNamespaceType: consts.PodAnnotationNetworkEnhancementNamespaceTypeHostPrefer, 706 }, 707 }, 708 }, 709 } 710 711 for _, tc := range testCases { 712 staticPolicy := makeStaticPolicy(t, true) 713 resp, err := staticPolicy.Allocate(context.Background(), tc.req) 714 if tc.noError { 715 assert.NoError(t, err) 716 assert.NotNil(t, resp) 717 718 tc.expectedResp.PodUid = tc.req.PodUid 719 t.Logf("expect: %v", tc.expectedResp.AllocationResult) 720 t.Logf("actucal: %v", resp.AllocationResult) 721 assert.Equalf(t, tc.expectedResp, resp, "failed in test case: %s", tc.description) 722 } else { 723 assert.Error(t, err) 724 assert.Nil(t, resp) 725 } 726 } 727 728 // no valid nics on this node 729 testCasesNoNic := []struct { 730 description string 731 noError bool 732 req *pluginapi.ResourceRequest 733 expectedResp *pluginapi.ResourceAllocationResponse 734 }{ 735 { 736 description: "req for shared_cores main container with host netns preference when no valid nic on this node", 737 noError: false, 738 req: &pluginapi.ResourceRequest{ 739 PodUid: string(uuid.NewUUID()), 740 PodNamespace: testName, 741 PodName: testName, 742 ContainerName: testName, 743 ContainerType: pluginapi.ContainerType_MAIN, 744 ContainerIndex: 0, 745 ResourceName: string(consts.ResourceNetBandwidth), 746 Hint: &pluginapi.TopologyHint{ 747 Nodes: []uint64{0, 1}, 748 Preferred: true, 749 }, 750 ResourceRequests: map[string]float64{ 751 string(consts.ResourceNetBandwidth): 5000, 752 }, 753 Labels: map[string]string{ 754 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 755 }, 756 Annotations: map[string]string{ 757 consts.PodAnnotationNetClassKey: testSharedNetClsId, 758 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 759 consts.PodAnnotationNetworkEnhancementKey: testHostPreferEnhancementValue, 760 }, 761 }, 762 expectedResp: nil, 763 }, 764 { 765 description: "0 bandwidth req for shared_cores main container with host netns preference when no valid nic on this node", 766 noError: false, 767 req: &pluginapi.ResourceRequest{ 768 PodUid: string(uuid.NewUUID()), 769 PodNamespace: testName, 770 PodName: testName, 771 ContainerName: testName, 772 ContainerType: pluginapi.ContainerType_MAIN, 773 ContainerIndex: 0, 774 ResourceName: string(consts.ResourceNetBandwidth), 775 Hint: &pluginapi.TopologyHint{ 776 Nodes: []uint64{0, 1}, 777 Preferred: true, 778 }, 779 ResourceRequests: map[string]float64{ 780 string(consts.ResourceNetBandwidth): 0, 781 }, 782 Labels: map[string]string{ 783 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 784 }, 785 Annotations: map[string]string{ 786 consts.PodAnnotationNetClassKey: testSharedNetClsId, 787 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 788 consts.PodAnnotationNetworkEnhancementKey: testHostPreferEnhancementValue, 789 }, 790 }, 791 expectedResp: nil, 792 }, 793 } 794 staticPolicy := makeStaticPolicy(t, false) 795 for _, tc := range testCasesNoNic { 796 resp, err := staticPolicy.Allocate(context.Background(), tc.req) 797 if tc.noError { 798 assert.Error(t, err) 799 assert.EqualError(t, err, "failed to meet the bandwidth requirement of 5000 Mbps") 800 assert.Nil(t, resp) 801 } 802 } 803 } 804 805 func TestGetNetClassID(t *testing.T) { 806 t.Parallel() 807 808 staticPolicy := makeStaticPolicy(t, true) 809 staticPolicy.qosLevelToNetClassMap = map[string]uint32{ 810 consts.PodAnnotationQoSLevelReclaimedCores: 10, 811 consts.PodAnnotationQoSLevelSharedCores: 20, 812 consts.PodAnnotationQoSLevelDedicatedCores: 30, 813 consts.PodAnnotationQoSLevelSystemCores: 70, 814 } 815 staticPolicy.podLevelNetClassAnnoKey = consts.PodAnnotationNetClassKey 816 817 testCases := []struct { 818 description string 819 pod *v1.Pod 820 expectedClassID uint32 821 }{ 822 { 823 description: "get net class id for shared_cores", 824 pod: &v1.Pod{ 825 ObjectMeta: metav1.ObjectMeta{ 826 Name: "test-pod-1", 827 Annotations: map[string]string{ 828 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 829 }, 830 Labels: map[string]string{ 831 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 832 }, 833 }, 834 }, 835 expectedClassID: 20, 836 }, 837 { 838 description: "get net class id for reclaimed_cores", 839 pod: &v1.Pod{ 840 ObjectMeta: metav1.ObjectMeta{ 841 Name: "test-pod-1", 842 Annotations: map[string]string{ 843 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelReclaimedCores, 844 }, 845 Labels: map[string]string{ 846 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelReclaimedCores, 847 }, 848 }, 849 }, 850 expectedClassID: 10, 851 }, 852 { 853 description: "get net class id for dedicated_cores", 854 pod: &v1.Pod{ 855 ObjectMeta: metav1.ObjectMeta{ 856 Name: "test-pod-1", 857 Annotations: map[string]string{ 858 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 859 }, 860 Labels: map[string]string{ 861 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 862 }, 863 }, 864 }, 865 expectedClassID: 30, 866 }, 867 { 868 description: "get net class id for system_cores", 869 pod: &v1.Pod{ 870 ObjectMeta: metav1.ObjectMeta{ 871 Name: "test-pod-1", 872 Annotations: map[string]string{ 873 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSystemCores, 874 }, 875 Labels: map[string]string{ 876 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSystemCores, 877 }, 878 }, 879 }, 880 expectedClassID: 70, 881 }, 882 { 883 description: "get pod-level net class id", 884 pod: &v1.Pod{ 885 ObjectMeta: metav1.ObjectMeta{ 886 Name: "test-pod-1", 887 Annotations: map[string]string{ 888 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 889 staticPolicy.podLevelNetClassAnnoKey: fmt.Sprintf("%d", 100), 890 }, 891 Labels: map[string]string{ 892 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 893 }, 894 }, 895 }, 896 expectedClassID: 100, 897 }, 898 } 899 900 for _, tc := range testCases { 901 gotClassID, err := staticPolicy.getNetClassID(tc.pod.Annotations, staticPolicy.podLevelNetClassAnnoKey) 902 assert.NoError(t, err) 903 assert.Equal(t, tc.expectedClassID, gotClassID) 904 } 905 } 906 907 func TestName(t *testing.T) { 908 t.Parallel() 909 910 policy := makeStaticPolicy(t, true) 911 assert.NotNil(t, policy) 912 913 assert.Equal(t, "qrm_network_plugin_static", policy.Name()) 914 } 915 916 func TestResourceName(t *testing.T) { 917 t.Parallel() 918 919 policy := makeStaticPolicy(t, true) 920 assert.NotNil(t, policy) 921 922 assert.Equal(t, string(consts.ResourceNetBandwidth), policy.ResourceName()) 923 } 924 925 func TestGetTopologyHints(t *testing.T) { 926 t.Parallel() 927 928 testName := "test" 929 930 // common cases 931 testCases := []struct { 932 description string 933 req *pluginapi.ResourceRequest 934 expectedResp *pluginapi.ResourceHintsResponse 935 }{ 936 { 937 description: "req for init container", 938 req: &pluginapi.ResourceRequest{ 939 PodUid: string(uuid.NewUUID()), 940 PodNamespace: testName, 941 PodName: testName, 942 ContainerName: testName, 943 ContainerType: pluginapi.ContainerType_INIT, 944 ContainerIndex: 0, 945 ResourceName: string(consts.ResourceNetBandwidth), 946 ResourceRequests: map[string]float64{ 947 string(consts.ResourceNetBandwidth): 5000, 948 }, 949 Labels: map[string]string{ 950 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 951 }, 952 Annotations: map[string]string{ 953 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 954 }, 955 }, 956 expectedResp: &pluginapi.ResourceHintsResponse{ 957 PodNamespace: testName, 958 PodName: testName, 959 ContainerName: testName, 960 ContainerType: pluginapi.ContainerType_INIT, 961 ContainerIndex: 0, 962 ResourceName: string(consts.ResourceNetBandwidth), 963 ResourceHints: map[string]*pluginapi.ListOfTopologyHints{ 964 string(consts.ResourceNetBandwidth): nil, 965 }, 966 Labels: map[string]string{ 967 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 968 }, 969 Annotations: map[string]string{ 970 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 971 }, 972 }, 973 }, 974 { 975 description: "req for shared_cores main container with host netns preference", 976 req: &pluginapi.ResourceRequest{ 977 PodUid: string(uuid.NewUUID()), 978 PodNamespace: testName, 979 PodName: testName, 980 ContainerName: testName, 981 ContainerType: pluginapi.ContainerType_MAIN, 982 ContainerIndex: 0, 983 ResourceName: string(consts.ResourceNetBandwidth), 984 ResourceRequests: map[string]float64{ 985 string(consts.ResourceNetBandwidth): 5000, 986 }, 987 Labels: map[string]string{ 988 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 989 }, 990 Annotations: map[string]string{ 991 consts.PodAnnotationNetClassKey: testSharedNetClsId, 992 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 993 consts.PodAnnotationNetworkEnhancementKey: testHostPreferEnhancementValue, 994 }, 995 }, 996 expectedResp: &pluginapi.ResourceHintsResponse{ 997 PodNamespace: testName, 998 PodName: testName, 999 ContainerName: testName, 1000 ContainerType: pluginapi.ContainerType_MAIN, 1001 ContainerIndex: 0, 1002 ResourceName: string(consts.ResourceNetBandwidth), 1003 ResourceHints: map[string]*pluginapi.ListOfTopologyHints{ 1004 string(consts.ResourceNetBandwidth): { 1005 Hints: []*pluginapi.TopologyHint{ 1006 { 1007 Nodes: []uint64{0, 1}, 1008 Preferred: true, 1009 }, 1010 { 1011 Nodes: []uint64{2, 3}, 1012 Preferred: false, 1013 }, 1014 { 1015 Nodes: []uint64{0, 1, 2, 3}, 1016 Preferred: false, 1017 }, 1018 }, 1019 }, 1020 }, 1021 Labels: map[string]string{ 1022 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 1023 }, 1024 Annotations: map[string]string{ 1025 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 1026 consts.PodAnnotationNetworkEnhancementNamespaceType: consts.PodAnnotationNetworkEnhancementNamespaceTypeHostPrefer, 1027 }, 1028 }, 1029 }, 1030 { 1031 description: "req for reclaimed_cores main container with not host netns preference", 1032 req: &pluginapi.ResourceRequest{ 1033 PodUid: string(uuid.NewUUID()), 1034 PodNamespace: testName, 1035 PodName: testName, 1036 ContainerName: testName, 1037 ContainerType: pluginapi.ContainerType_MAIN, 1038 ContainerIndex: 0, 1039 ResourceName: string(consts.ResourceNetBandwidth), 1040 ResourceRequests: map[string]float64{ 1041 string(consts.ResourceNetBandwidth): 5000, 1042 }, 1043 Annotations: map[string]string{ 1044 consts.PodAnnotationNetClassKey: testReclaimedNetClsId, 1045 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelReclaimedCores, 1046 consts.PodAnnotationNetworkEnhancementKey: testNotHostPreferEnhancementValue, 1047 }, 1048 Labels: map[string]string{ 1049 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelReclaimedCores, 1050 }, 1051 }, 1052 expectedResp: &pluginapi.ResourceHintsResponse{ 1053 PodNamespace: testName, 1054 PodName: testName, 1055 ContainerName: testName, 1056 ContainerType: pluginapi.ContainerType_MAIN, 1057 ContainerIndex: 0, 1058 ResourceName: string(consts.ResourceNetBandwidth), 1059 ResourceHints: map[string]*pluginapi.ListOfTopologyHints{ 1060 string(consts.ResourceNetBandwidth): { 1061 Hints: []*pluginapi.TopologyHint{ 1062 { 1063 Nodes: []uint64{0, 1}, 1064 Preferred: false, 1065 }, 1066 { 1067 Nodes: []uint64{2, 3}, 1068 Preferred: true, 1069 }, 1070 { 1071 Nodes: []uint64{0, 1, 2, 3}, 1072 Preferred: false, 1073 }, 1074 }, 1075 }, 1076 }, 1077 Labels: map[string]string{ 1078 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelReclaimedCores, 1079 }, 1080 Annotations: map[string]string{ 1081 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelReclaimedCores, 1082 consts.PodAnnotationNetworkEnhancementNamespaceType: consts.PodAnnotationNetworkEnhancementNamespaceTypeNotHostPrefer, 1083 }, 1084 }, 1085 }, 1086 { 1087 description: "req for dedicated_cores main container with host netns guarantee", 1088 req: &pluginapi.ResourceRequest{ 1089 PodUid: string(uuid.NewUUID()), 1090 PodNamespace: testName, 1091 PodName: testName, 1092 ContainerName: testName, 1093 ContainerType: pluginapi.ContainerType_MAIN, 1094 ContainerIndex: 0, 1095 ResourceName: string(consts.ResourceNetBandwidth), 1096 ResourceRequests: map[string]float64{ 1097 string(consts.ResourceNetBandwidth): 5000, 1098 }, 1099 Annotations: map[string]string{ 1100 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1101 consts.PodAnnotationNetworkEnhancementKey: testHostEnhancementValue, 1102 }, 1103 Labels: map[string]string{ 1104 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1105 }, 1106 }, 1107 expectedResp: &pluginapi.ResourceHintsResponse{ 1108 PodNamespace: testName, 1109 PodName: testName, 1110 ContainerName: testName, 1111 ContainerType: pluginapi.ContainerType_MAIN, 1112 ContainerIndex: 0, 1113 ResourceName: string(consts.ResourceNetBandwidth), 1114 ResourceHints: map[string]*pluginapi.ListOfTopologyHints{ 1115 string(consts.ResourceNetBandwidth): { 1116 Hints: []*pluginapi.TopologyHint{ 1117 { 1118 Nodes: []uint64{0, 1}, 1119 Preferred: true, 1120 }, 1121 }, 1122 }, 1123 }, 1124 Labels: map[string]string{ 1125 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1126 }, 1127 Annotations: map[string]string{ 1128 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1129 consts.PodAnnotationNetworkEnhancementNamespaceType: consts.PodAnnotationNetworkEnhancementNamespaceTypeHost, 1130 }, 1131 }, 1132 }, 1133 { 1134 description: "req for dedicated_cores main container with exceeded bandwidth over the 1st NIC which is preferred", 1135 req: &pluginapi.ResourceRequest{ 1136 PodUid: string(uuid.NewUUID()), 1137 PodNamespace: testName, 1138 PodName: testName, 1139 ContainerName: testName, 1140 ContainerType: pluginapi.ContainerType_MAIN, 1141 ContainerIndex: 0, 1142 ResourceName: string(consts.ResourceNetBandwidth), 1143 ResourceRequests: map[string]float64{ 1144 string(consts.ResourceNetBandwidth): 20000, 1145 }, 1146 Annotations: map[string]string{ 1147 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1148 consts.PodAnnotationNetworkEnhancementKey: testHostPreferEnhancementValue, 1149 }, 1150 Labels: map[string]string{ 1151 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1152 }, 1153 }, 1154 expectedResp: &pluginapi.ResourceHintsResponse{ 1155 PodNamespace: testName, 1156 PodName: testName, 1157 ContainerName: testName, 1158 ContainerType: pluginapi.ContainerType_MAIN, 1159 ContainerIndex: 0, 1160 ResourceName: string(consts.ResourceNetBandwidth), 1161 ResourceHints: map[string]*pluginapi.ListOfTopologyHints{ 1162 string(consts.ResourceNetBandwidth): { 1163 Hints: []*pluginapi.TopologyHint{ 1164 { 1165 Nodes: []uint64{2, 3}, 1166 Preferred: false, 1167 }, 1168 { 1169 Nodes: []uint64{0, 1, 2, 3}, 1170 Preferred: false, 1171 }, 1172 }, 1173 }, 1174 }, 1175 Labels: map[string]string{ 1176 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1177 }, 1178 Annotations: map[string]string{ 1179 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1180 consts.PodAnnotationNetworkEnhancementNamespaceType: consts.PodAnnotationNetworkEnhancementNamespaceTypeHostPrefer, 1181 }, 1182 }, 1183 }, 1184 { 1185 description: "req for dedicated_cores main container with exceeded bandwidth over the 1st NIC which is required", 1186 req: &pluginapi.ResourceRequest{ 1187 PodUid: string(uuid.NewUUID()), 1188 PodNamespace: testName, 1189 PodName: testName, 1190 ContainerName: testName, 1191 ContainerType: pluginapi.ContainerType_MAIN, 1192 ContainerIndex: 0, 1193 ResourceName: string(consts.ResourceNetBandwidth), 1194 ResourceRequests: map[string]float64{ 1195 string(consts.ResourceNetBandwidth): 20000, 1196 }, 1197 Annotations: map[string]string{ 1198 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1199 consts.PodAnnotationNetworkEnhancementKey: testHostEnhancementValue, 1200 }, 1201 Labels: map[string]string{ 1202 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1203 }, 1204 }, 1205 expectedResp: &pluginapi.ResourceHintsResponse{ 1206 PodNamespace: testName, 1207 PodName: testName, 1208 ContainerName: testName, 1209 ContainerType: pluginapi.ContainerType_MAIN, 1210 ContainerIndex: 0, 1211 ResourceName: string(consts.ResourceNetBandwidth), 1212 ResourceHints: map[string]*pluginapi.ListOfTopologyHints{ 1213 string(consts.ResourceNetBandwidth): { 1214 Hints: []*pluginapi.TopologyHint{}, 1215 }, 1216 }, 1217 Labels: map[string]string{ 1218 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1219 }, 1220 Annotations: map[string]string{ 1221 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelDedicatedCores, 1222 consts.PodAnnotationNetworkEnhancementNamespaceType: consts.PodAnnotationNetworkEnhancementNamespaceTypeHost, 1223 }, 1224 }, 1225 }, 1226 } 1227 1228 for _, tc := range testCases { 1229 staticPolicy := makeStaticPolicy(t, true) 1230 1231 resp, err := staticPolicy.GetTopologyHints(context.Background(), tc.req) 1232 assert.NoError(t, err) 1233 assert.NotNil(t, resp) 1234 1235 tc.expectedResp.PodUid = tc.req.PodUid 1236 1237 compareHint := func(a, b *pluginapi.TopologyHint) bool { 1238 if a == nil { 1239 return true 1240 } else if b == nil { 1241 return false 1242 } 1243 1244 aset, _ := machine.NewCPUSetUint64(a.Nodes...) 1245 bset, _ := machine.NewCPUSetUint64(b.Nodes...) 1246 1247 asetStr := aset.String() 1248 bsetStr := bset.String() 1249 1250 if asetStr < bsetStr { 1251 return true 1252 } else if asetStr > bsetStr { 1253 return false 1254 } else if a.Preferred { 1255 return true 1256 } else { 1257 return false 1258 } 1259 } 1260 1261 if resp.ResourceHints[string(consts.ResourceNetBandwidth)] != nil { 1262 sort.SliceStable(resp.ResourceHints[string(consts.ResourceNetBandwidth)].Hints, func(i, j int) bool { 1263 return compareHint(resp.ResourceHints[string(consts.ResourceNetBandwidth)].Hints[i], 1264 resp.ResourceHints[string(consts.ResourceNetBandwidth)].Hints[j]) 1265 }) 1266 } 1267 1268 if tc.expectedResp.ResourceHints[string(consts.ResourceNetBandwidth)] != nil { 1269 sort.SliceStable(tc.expectedResp.ResourceHints[string(consts.ResourceNetBandwidth)].Hints, func(i, j int) bool { 1270 return compareHint(tc.expectedResp.ResourceHints[string(consts.ResourceNetBandwidth)].Hints[i], 1271 tc.expectedResp.ResourceHints[string(consts.ResourceNetBandwidth)].Hints[j]) 1272 }) 1273 } 1274 1275 assert.Equalf(t, tc.expectedResp, resp, "failed in test case: %s", tc.description) 1276 } 1277 1278 // no valid nics on this node () 1279 staticPolicy := makeStaticPolicy(t, false) 1280 resp, err := staticPolicy.GetTopologyHints(context.Background(), testCases[1].req) 1281 assert.NoError(t, err) 1282 assert.NotNil(t, resp) 1283 assert.Equal(t, map[string]*pluginapi.ListOfTopologyHints{ 1284 resp.ResourceName: { 1285 Hints: []*pluginapi.TopologyHint{}, 1286 }, 1287 }, resp.ResourceHints) 1288 } 1289 1290 func TestGetResourcesAllocation(t *testing.T) { 1291 t.Parallel() 1292 1293 policy := makeStaticPolicy(t, true) 1294 assert.NotNil(t, policy) 1295 1296 _, err := policy.GetResourcesAllocation(context.TODO(), &pluginapi.GetResourcesAllocationRequest{}) 1297 assert.NoError(t, err) 1298 } 1299 1300 func TestGetTopologyAwareResources(t *testing.T) { 1301 t.Parallel() 1302 1303 podID := string(uuid.NewUUID()) 1304 testName := "test" 1305 var bwReq float64 = 5000 1306 1307 testCases := []struct { 1308 description string 1309 hasNic bool 1310 addReq *pluginapi.ResourceRequest 1311 req *pluginapi.GetTopologyAwareResourcesRequest 1312 expectedTopologyAwareQuantity *pluginapi.TopologyAwareQuantity 1313 }{ 1314 { 1315 description: "has valid nics and a valid Pod", 1316 hasNic: true, 1317 addReq: &pluginapi.ResourceRequest{ 1318 PodUid: podID, 1319 PodNamespace: testName, 1320 PodName: testName, 1321 ContainerName: testName, 1322 ContainerType: pluginapi.ContainerType_MAIN, 1323 ContainerIndex: 0, 1324 ResourceName: string(consts.ResourceNetBandwidth), 1325 Hint: &pluginapi.TopologyHint{ 1326 Nodes: []uint64{0, 1}, 1327 Preferred: true, 1328 }, 1329 ResourceRequests: map[string]float64{ 1330 string(consts.ResourceNetBandwidth): bwReq, 1331 }, 1332 Labels: map[string]string{ 1333 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 1334 }, 1335 Annotations: map[string]string{ 1336 consts.PodAnnotationNetClassKey: testSharedNetClsId, 1337 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 1338 consts.PodAnnotationNetworkEnhancementKey: testHostPreferEnhancementValue, 1339 }, 1340 }, 1341 req: &pluginapi.GetTopologyAwareResourcesRequest{ 1342 PodUid: podID, 1343 ContainerName: testName, 1344 }, 1345 expectedTopologyAwareQuantity: &pluginapi.TopologyAwareQuantity{ 1346 ResourceValue: bwReq, 1347 Node: uint64(0), 1348 Name: testEth0Name, 1349 Type: string(apinode.TopologyTypeNIC), 1350 TopologyLevel: pluginapi.TopologyLevel_SOCKET, 1351 Annotations: map[string]string{ 1352 // testEth0NSName is empty, so remove the prefix 1353 consts.ResourceAnnotationKeyResourceIdentifier: testEth0Name, 1354 consts.ResourceAnnotationKeyNICNetNSName: "", 1355 }, 1356 }, 1357 }, 1358 { 1359 description: "has no valid nic", 1360 hasNic: false, 1361 addReq: &pluginapi.ResourceRequest{ 1362 PodUid: podID, 1363 PodNamespace: testName, 1364 PodName: testName, 1365 ContainerName: testName, 1366 ContainerType: pluginapi.ContainerType_MAIN, 1367 ContainerIndex: 0, 1368 ResourceName: string(consts.ResourceNetBandwidth), 1369 Hint: &pluginapi.TopologyHint{ 1370 Nodes: []uint64{0, 1}, 1371 Preferred: true, 1372 }, 1373 ResourceRequests: map[string]float64{ 1374 string(consts.ResourceNetBandwidth): 0, 1375 }, 1376 Labels: map[string]string{ 1377 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 1378 }, 1379 Annotations: map[string]string{ 1380 consts.PodAnnotationNetClassKey: testSharedNetClsId, 1381 consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelSharedCores, 1382 consts.PodAnnotationNetworkEnhancementKey: testHostPreferEnhancementValue, 1383 }, 1384 }, 1385 req: &pluginapi.GetTopologyAwareResourcesRequest{ 1386 PodUid: podID, 1387 ContainerName: testName, 1388 }, 1389 expectedTopologyAwareQuantity: nil, 1390 }, 1391 } 1392 1393 for _, tc := range testCases { 1394 policy := makeStaticPolicy(t, tc.hasNic) 1395 assert.NotNil(t, policy) 1396 1397 _, err := policy.Allocate(context.Background(), tc.addReq) 1398 if tc.hasNic { 1399 assert.NoError(t, err) 1400 } 1401 1402 resp, err := policy.GetTopologyAwareResources(context.TODO(), tc.req) 1403 assert.NoError(t, err) 1404 assert.NotNil(t, resp) 1405 if tc.hasNic { 1406 assert.Len(t, resp.ContainerTopologyAwareResources.AllocatedResources, 1) 1407 assert.Equal(t, resp.ContainerTopologyAwareResources.AllocatedResources[string(consts.ResourceNetBandwidth)].AggregatedQuantity, bwReq) 1408 assert.Len(t, resp.ContainerTopologyAwareResources.AllocatedResources[string(consts.ResourceNetBandwidth)].TopologyAwareQuantityList, 1) 1409 1410 assert.Equal(t, resp.ContainerTopologyAwareResources.AllocatedResources[string(consts.ResourceNetBandwidth)].TopologyAwareQuantityList[0], tc.expectedTopologyAwareQuantity) 1411 } else { 1412 assert.Equal(t, &pluginapi.GetTopologyAwareResourcesResponse{}, resp) 1413 } 1414 } 1415 } 1416 1417 func TestGetTopologyAwareAllocatableResources(t *testing.T) { 1418 t.Parallel() 1419 1420 testCases := []struct { 1421 description string 1422 hasNic bool 1423 expectedAllocatableQuantityList []*pluginapi.TopologyAwareQuantity 1424 expectedCapacityQuantityList []*pluginapi.TopologyAwareQuantity 1425 }{ 1426 { 1427 description: "has valid nics", 1428 hasNic: true, 1429 expectedAllocatableQuantityList: []*pluginapi.TopologyAwareQuantity{ 1430 { 1431 ResourceValue: float64(17250), 1432 Node: uint64(0), 1433 Name: testEth0Name, 1434 Type: string(apinode.TopologyTypeNIC), 1435 TopologyLevel: pluginapi.TopologyLevel_SOCKET, 1436 Annotations: map[string]string{ 1437 // testEth0NSName is empty, so remove the prefix 1438 consts.ResourceAnnotationKeyResourceIdentifier: testEth0Name, 1439 consts.ResourceAnnotationKeyNICNetNSName: "", 1440 }, 1441 }, 1442 { 1443 ResourceValue: float64(21250), 1444 Node: uint64(1), 1445 Name: testEth2Name, 1446 Type: string(apinode.TopologyTypeNIC), 1447 TopologyLevel: pluginapi.TopologyLevel_SOCKET, 1448 Annotations: map[string]string{ 1449 consts.ResourceAnnotationKeyResourceIdentifier: fmt.Sprintf("%s-%s", testEth2NSName, testEth2Name), 1450 consts.ResourceAnnotationKeyNICNetNSName: testEth2NSName, 1451 }, 1452 }, 1453 }, 1454 expectedCapacityQuantityList: []*pluginapi.TopologyAwareQuantity{ 1455 { 1456 ResourceValue: float64(21250), 1457 Node: uint64(0), 1458 Name: testEth0Name, 1459 Type: string(apinode.TopologyTypeNIC), 1460 TopologyLevel: pluginapi.TopologyLevel_SOCKET, 1461 Annotations: map[string]string{ 1462 // testEth0NSName is empty, so remove the prefix 1463 consts.ResourceAnnotationKeyResourceIdentifier: testEth0Name, 1464 consts.ResourceAnnotationKeyNICNetNSName: "", 1465 }, 1466 }, 1467 { 1468 ResourceValue: float64(21250), 1469 Node: uint64(1), 1470 Name: testEth2Name, 1471 Type: string(apinode.TopologyTypeNIC), 1472 TopologyLevel: pluginapi.TopologyLevel_SOCKET, 1473 Annotations: map[string]string{ 1474 consts.ResourceAnnotationKeyResourceIdentifier: fmt.Sprintf("%s-%s", testEth2NSName, testEth2Name), 1475 consts.ResourceAnnotationKeyNICNetNSName: testEth2NSName, 1476 }, 1477 }, 1478 }, 1479 }, 1480 { 1481 description: "has no valid nic", 1482 hasNic: false, 1483 expectedAllocatableQuantityList: make([]*pluginapi.TopologyAwareQuantity, 0), 1484 expectedCapacityQuantityList: make([]*pluginapi.TopologyAwareQuantity, 0), 1485 }, 1486 } 1487 1488 for _, tc := range testCases { 1489 policy := makeStaticPolicy(t, tc.hasNic) 1490 assert.NotNil(t, policy) 1491 1492 resp, err := policy.GetTopologyAwareAllocatableResources(context.TODO(), &pluginapi.GetTopologyAwareAllocatableResourcesRequest{}) 1493 assert.NotNil(t, resp) 1494 assert.NoError(t, err) 1495 1496 if tc.hasNic { 1497 assert.Equal(t, resp.AllocatableResources[string(consts.ResourceNetBandwidth)].AggregatedAllocatableQuantity, tc.expectedAllocatableQuantityList[0].ResourceValue+tc.expectedAllocatableQuantityList[1].ResourceValue) 1498 assert.Equal(t, resp.AllocatableResources[string(consts.ResourceNetBandwidth)].AggregatedCapacityQuantity, tc.expectedCapacityQuantityList[0].ResourceValue+tc.expectedCapacityQuantityList[1].ResourceValue) 1499 assert.Len(t, resp.AllocatableResources[string(consts.ResourceNetBandwidth)].TopologyAwareAllocatableQuantityList, len(tc.expectedAllocatableQuantityList)) 1500 assert.Len(t, resp.AllocatableResources[string(consts.ResourceNetBandwidth)].TopologyAwareCapacityQuantityList, len(tc.expectedCapacityQuantityList)) 1501 1502 assert.Equal(t, resp.AllocatableResources[string(consts.ResourceNetBandwidth)].TopologyAwareAllocatableQuantityList, tc.expectedAllocatableQuantityList) 1503 assert.Equal(t, resp.AllocatableResources[string(consts.ResourceNetBandwidth)].TopologyAwareCapacityQuantityList, tc.expectedCapacityQuantityList) 1504 } else { 1505 assert.Equal(t, float64(0), resp.AllocatableResources[string(consts.ResourceNetBandwidth)].AggregatedAllocatableQuantity) 1506 assert.Equal(t, float64(0), resp.AllocatableResources[string(consts.ResourceNetBandwidth)].AggregatedCapacityQuantity) 1507 assert.Len(t, resp.AllocatableResources[string(consts.ResourceNetBandwidth)].TopologyAwareAllocatableQuantityList, 0) 1508 assert.Len(t, resp.AllocatableResources[string(consts.ResourceNetBandwidth)].TopologyAwareCapacityQuantityList, 0) 1509 } 1510 } 1511 } 1512 1513 func TestGetResourcePluginOptions(t *testing.T) { 1514 t.Parallel() 1515 1516 policy := makeStaticPolicy(t, true) 1517 assert.NotNil(t, policy) 1518 1519 expectedResp := &pluginapi.ResourcePluginOptions{ 1520 PreStartRequired: false, 1521 WithTopologyAlignment: true, 1522 NeedReconcile: false, 1523 } 1524 1525 resp, err := policy.GetResourcePluginOptions(context.TODO(), &pluginapi.Empty{}) 1526 assert.NoError(t, err) 1527 assert.Equal(t, expectedResp, resp) 1528 } 1529 1530 func TestPreStartContainer(t *testing.T) { 1531 t.Parallel() 1532 1533 policy := makeStaticPolicy(t, true) 1534 assert.NotNil(t, policy) 1535 1536 req := &pluginapi.PreStartContainerRequest{ 1537 PodUid: string(uuid.NewUUID()), 1538 PodNamespace: "test-namespace", 1539 PodName: "test-pod-name", 1540 ContainerName: "test-container-name", 1541 } 1542 1543 _, err := policy.PreStartContainer(context.TODO(), req) 1544 assert.NoError(t, err) 1545 }