github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/qrm-plugins/cpu/util/util.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 util
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"math"
    23  	"sort"
    24  
    25  	v1 "k8s.io/api/core/v1"
    26  	pluginapi "k8s.io/kubelet/pkg/apis/resourceplugin/v1alpha1"
    27  
    28  	"github.com/kubewharf/katalyst-core/pkg/agent/qrm-plugins/cpu/dynamicpolicy/calculator"
    29  	"github.com/kubewharf/katalyst-core/pkg/agent/qrm-plugins/cpu/dynamicpolicy/state"
    30  	"github.com/kubewharf/katalyst-core/pkg/config"
    31  	"github.com/kubewharf/katalyst-core/pkg/metaserver"
    32  	"github.com/kubewharf/katalyst-core/pkg/util/general"
    33  	utilkubeconfig "github.com/kubewharf/katalyst-core/pkg/util/kubelet/config"
    34  	"github.com/kubewharf/katalyst-core/pkg/util/machine"
    35  )
    36  
    37  func GetCoresReservedForSystem(conf *config.Configuration, metaServer *metaserver.MetaServer, machineInfo *machine.KatalystMachineInfo, allCPUs machine.CPUSet) (machine.CPUSet, error) {
    38  	if conf == nil {
    39  		return machine.NewCPUSet(), fmt.Errorf("nil conf")
    40  	} else if metaServer == nil {
    41  		return machine.NewCPUSet(), fmt.Errorf("nil metaServer")
    42  	} else if machineInfo == nil {
    43  		return machine.NewCPUSet(), fmt.Errorf("nil machineInfo")
    44  	}
    45  
    46  	var reservedQuantityInt int
    47  	if conf.UseKubeletReservedConfig {
    48  		klConfig, err := metaServer.GetKubeletConfig(context.TODO())
    49  		if err != nil {
    50  			return machine.NewCPUSet(), fmt.Errorf("failed to get kubelet config: %v", err)
    51  		}
    52  
    53  		reservedQuantity, found, err := utilkubeconfig.GetReservedQuantity(klConfig, string(v1.ResourceCPU))
    54  		if err != nil {
    55  			return machine.NewCPUSet(), fmt.Errorf("GetKubeletReservedQuantity failed with error: %v", err)
    56  		} else {
    57  			reservedQuantityFloat := float64(reservedQuantity.MilliValue()) / 1000
    58  			reservedQuantityInt = int(math.Ceil(reservedQuantityFloat))
    59  
    60  			general.Infof("get reservedQuantityInt: %d from kubelet config, found: %v", reservedQuantityInt, found)
    61  		}
    62  	} else {
    63  		reservedQuantityInt = conf.ReservedCPUCores
    64  		general.Infof("get reservedQuantityInt: %d from ReservedCPUCores configuration", reservedQuantityInt)
    65  	}
    66  
    67  	reservedCPUs, _, reserveErr := calculator.TakeHTByNUMABalance(machineInfo, allCPUs, reservedQuantityInt)
    68  	if reserveErr != nil {
    69  		return reservedCPUs, fmt.Errorf("takeByNUMABalance for reservedCPUsNum: %d failed with error: %v",
    70  			reservedQuantityInt, reserveErr)
    71  	}
    72  
    73  	general.Infof("take reservedCPUs: %s by reservedCPUsNum: %d", reservedCPUs.String(), reservedQuantityInt)
    74  	return reservedCPUs, nil
    75  }
    76  
    77  // RegenerateHints regenerates hints for container that'd already been allocated cpu,
    78  // and regenerateHints will assemble hints based on already-existed AllocationInfo,
    79  // without any calculation logics at all
    80  func RegenerateHints(allocationInfo *state.AllocationInfo, reqInt int) map[string]*pluginapi.ListOfTopologyHints {
    81  	hints := map[string]*pluginapi.ListOfTopologyHints{}
    82  
    83  	if allocationInfo.OriginalAllocationResult.Size() < reqInt {
    84  		general.ErrorS(nil, "cpus already allocated with smaller quantity than requested",
    85  			"podUID", allocationInfo.PodUid,
    86  			"containerName", allocationInfo.ContainerName,
    87  			"requestedResource", reqInt,
    88  			"allocatedSize", allocationInfo.OriginalAllocationResult.Size())
    89  
    90  		return nil
    91  	}
    92  
    93  	allocatedNumaNodes := make([]uint64, 0, len(allocationInfo.TopologyAwareAssignments))
    94  	for numaNode, cset := range allocationInfo.TopologyAwareAssignments {
    95  		if cset.Size() > 0 {
    96  			allocatedNumaNodes = append(allocatedNumaNodes, uint64(numaNode))
    97  		}
    98  	}
    99  
   100  	sort.Slice(allocatedNumaNodes, func(i, j int) bool {
   101  		return allocatedNumaNodes[i] < allocatedNumaNodes[j]
   102  	})
   103  
   104  	general.InfoS("regenerating machineInfo hints, cpus was already allocated to pod",
   105  		"podNamespace", allocationInfo.PodNamespace,
   106  		"podName", allocationInfo.PodName,
   107  		"containerName", allocationInfo.ContainerName,
   108  		"hint", allocatedNumaNodes)
   109  	hints[string(v1.ResourceCPU)] = &pluginapi.ListOfTopologyHints{
   110  		Hints: []*pluginapi.TopologyHint{
   111  			{
   112  				Nodes:     allocatedNumaNodes,
   113  				Preferred: true,
   114  			},
   115  		},
   116  	}
   117  	return hints
   118  }
   119  
   120  // PackAllocationResponse fills pluginapi.ResourceAllocationResponse with information from AllocationInfo and pluginapi.ResourceRequest
   121  func PackAllocationResponse(allocationInfo *state.AllocationInfo, resourceName, ociPropertyName string,
   122  	isNodeResource, isScalarResource bool, req *pluginapi.ResourceRequest,
   123  ) (*pluginapi.ResourceAllocationResponse, error) {
   124  	if allocationInfo == nil {
   125  		return nil, fmt.Errorf("packAllocationResponse got nil allocationInfo")
   126  	} else if req == nil {
   127  		return nil, fmt.Errorf("packAllocationResponse got nil request")
   128  	}
   129  
   130  	return &pluginapi.ResourceAllocationResponse{
   131  		PodUid:         req.PodUid,
   132  		PodNamespace:   req.PodNamespace,
   133  		PodName:        req.PodName,
   134  		ContainerName:  req.ContainerName,
   135  		ContainerType:  req.ContainerType,
   136  		ContainerIndex: req.ContainerIndex,
   137  		PodRole:        req.PodRole,
   138  		PodType:        req.PodType,
   139  		ResourceName:   resourceName,
   140  		AllocationResult: &pluginapi.ResourceAllocation{
   141  			ResourceAllocation: map[string]*pluginapi.ResourceAllocationInfo{
   142  				resourceName: {
   143  					OciPropertyName:   ociPropertyName,
   144  					IsNodeResource:    isNodeResource,
   145  					IsScalarResource:  isScalarResource,
   146  					AllocatedQuantity: float64(allocationInfo.AllocationResult.Size()),
   147  					AllocationResult:  allocationInfo.AllocationResult.String(),
   148  					ResourceHints: &pluginapi.ListOfTopologyHints{
   149  						Hints: []*pluginapi.TopologyHint{
   150  							req.Hint,
   151  						},
   152  					},
   153  				},
   154  			},
   155  		},
   156  		Labels:         general.DeepCopyMap(req.Labels),
   157  		Annotations:    general.DeepCopyMap(req.Annotations),
   158  		NativeQosClass: req.NativeQosClass,
   159  	}, nil
   160  }