github.com/projecteru2/core@v0.0.0-20240321043226-06bcc1c23f58/resource/plugins/cpumem/calculate.go (about)

     1  package cpumem
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/cockroachdb/errors"
     7  	"github.com/mitchellh/mapstructure"
     8  	"github.com/projecteru2/core/log"
     9  	"github.com/projecteru2/core/resource/plugins/cpumem/schedule"
    10  	cpumemtypes "github.com/projecteru2/core/resource/plugins/cpumem/types"
    11  	plugintypes "github.com/projecteru2/core/resource/plugins/types"
    12  	coretypes "github.com/projecteru2/core/types"
    13  )
    14  
    15  // CalculateDeploy .
    16  func (p Plugin) CalculateDeploy(ctx context.Context, nodename string, deployCount int, resourceRequest plugintypes.WorkloadResourceRequest) (*plugintypes.CalculateDeployResponse, error) {
    17  	logger := log.WithFunc("resource.cpumem.CalculateDeploy").WithField("node", nodename)
    18  	req := &cpumemtypes.WorkloadResourceRequest{}
    19  	if err := req.Parse(resourceRequest); err != nil {
    20  		return nil, err
    21  	}
    22  	if err := req.Validate(); err != nil {
    23  		logger.Errorf(ctx, err, "invalid resource opts %+v", req)
    24  		return nil, err
    25  	}
    26  
    27  	nodeResourceInfo, err := p.doGetNodeResourceInfo(ctx, nodename)
    28  	if err != nil {
    29  		logger.WithField("node", nodename).Error(ctx, err)
    30  		return nil, err
    31  	}
    32  
    33  	var enginesParams []*cpumemtypes.EngineParams
    34  	var workloadsResource []*cpumemtypes.WorkloadResource
    35  
    36  	if !req.CPUBind {
    37  		enginesParams, workloadsResource, err = p.doAllocByMemory(nodeResourceInfo, deployCount, req)
    38  	} else {
    39  		enginesParams, workloadsResource, err = p.doAllocByCPU(nodeResourceInfo, deployCount, req)
    40  	}
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	resp := &plugintypes.CalculateDeployResponse{}
    46  	return resp, mapstructure.Decode(map[string]any{
    47  		"engines_params":     enginesParams,
    48  		"workloads_resource": workloadsResource,
    49  	}, resp)
    50  }
    51  
    52  // CalculateRealloc .
    53  func (p Plugin) CalculateRealloc(ctx context.Context, nodename string, resource plugintypes.WorkloadResource, resourceRequest plugintypes.WorkloadResourceRequest) (*plugintypes.CalculateReallocResponse, error) {
    54  	req := &cpumemtypes.WorkloadResourceRequest{}
    55  	if err := req.Parse(resourceRequest); err != nil {
    56  		return nil, err
    57  	}
    58  	originResource := &cpumemtypes.WorkloadResource{}
    59  	if err := originResource.Parse(resource); err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	if req.KeepCPUBind {
    64  		req.CPUBind = len(originResource.CPUMap) > 0
    65  	}
    66  
    67  	nodeResourceInfo, err := p.doGetNodeResourceInfo(ctx, nodename)
    68  	if err != nil {
    69  		log.WithFunc("resource.cpumem.CalculateRealloc").WithField("node", nodename).Error(ctx, err, "failed to get resource info of node")
    70  		return nil, err
    71  	}
    72  
    73  	// put resources back into the resource pool
    74  	nodeResourceInfo.Usage.Sub(&cpumemtypes.NodeResource{
    75  		CPU:        originResource.CPURequest,
    76  		CPUMap:     originResource.CPUMap,
    77  		Memory:     originResource.MemoryRequest,
    78  		NUMAMemory: originResource.NUMAMemory,
    79  	})
    80  
    81  	newReq := &cpumemtypes.WorkloadResourceRequest{
    82  		CPUBind:    req.CPUBind,
    83  		CPURequest: req.CPURequest + originResource.CPURequest,
    84  		CPULimit:   req.CPULimit + originResource.CPULimit,
    85  		MemRequest: req.MemRequest + originResource.MemoryRequest,
    86  		MemLimit:   req.MemLimit + originResource.MemoryLimit,
    87  	}
    88  
    89  	if err = newReq.Validate(); err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	// if cpu was specified before, try to ensure cpu affinity
    94  	var cpuMap cpumemtypes.CPUMap
    95  	var numaNodeID string
    96  	var numaMemory cpumemtypes.NUMAMemory
    97  
    98  	if req.CPUBind {
    99  		cpuPlans := schedule.GetCPUPlans(nodeResourceInfo, originResource.CPUMap, p.config.Scheduler.ShareBase, p.config.Scheduler.MaxShare, newReq)
   100  		if len(cpuPlans) == 0 {
   101  			return nil, coretypes.ErrInsufficientResource
   102  		}
   103  
   104  		cpuPlan := cpuPlans[0]
   105  		cpuMap = cpuPlan.CPUMap
   106  		numaNodeID = cpuPlan.NUMANode
   107  		if len(numaNodeID) > 0 {
   108  			numaMemory = cpumemtypes.NUMAMemory{numaNodeID: newReq.MemRequest}
   109  		}
   110  	} else if _, _, err = p.doAllocByMemory(nodeResourceInfo, 1, newReq); err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	engineParams := &cpumemtypes.EngineParams{
   115  		CPU:      newReq.CPULimit,
   116  		CPUMap:   cpuMap,
   117  		NUMANode: numaNodeID,
   118  		Memory:   newReq.MemLimit,
   119  	}
   120  
   121  	newResource := &cpumemtypes.WorkloadResource{
   122  		CPURequest:    newReq.CPURequest,
   123  		CPULimit:      newReq.CPULimit,
   124  		MemoryRequest: newReq.MemRequest,
   125  		MemoryLimit:   newReq.MemLimit,
   126  		CPUMap:        cpuMap,
   127  		NUMAMemory:    numaMemory,
   128  		NUMANode:      numaNodeID,
   129  	}
   130  
   131  	deltaWorkloadResource := newResource.DeepCopy()
   132  	deltaWorkloadResource.Sub(originResource)
   133  
   134  	resp := &plugintypes.CalculateReallocResponse{}
   135  	return resp, mapstructure.Decode(map[string]any{
   136  		"engine_params":     engineParams,
   137  		"delta_resource":    deltaWorkloadResource,
   138  		"workload_resource": newResource,
   139  	}, resp)
   140  }
   141  
   142  // CalculateRemap .
   143  func (p Plugin) CalculateRemap(ctx context.Context, nodename string, workloadsResource map[string]plugintypes.WorkloadResource) (*plugintypes.CalculateRemapResponse, error) {
   144  	resp := &plugintypes.CalculateRemapResponse{}
   145  	engineParamsMap := map[string]*cpumemtypes.EngineParams{}
   146  	if len(workloadsResource) == 0 {
   147  		return resp, mapstructure.Decode(map[string]any{
   148  			"engine_params_map": engineParamsMap,
   149  		}, resp)
   150  	}
   151  
   152  	workloadResourceMap := map[string]*cpumemtypes.WorkloadResource{}
   153  	for ID, workload := range workloadsResource {
   154  		workloadResource := &cpumemtypes.WorkloadResource{}
   155  		if err := workloadResource.Parse(workload); err != nil {
   156  			return nil, err
   157  		}
   158  		workloadResourceMap[ID] = workloadResource
   159  	}
   160  
   161  	nodeResourceInfo, err := p.doGetNodeResourceInfo(ctx, nodename)
   162  	if err != nil {
   163  		log.WithFunc("resource.cpumem.CalculateRemap").WithField("node", nodename).Error(ctx, err)
   164  		return nil, err
   165  	}
   166  
   167  	availableNodeResource := nodeResourceInfo.GetAvailableResource()
   168  
   169  	shareCPUMap := cpumemtypes.CPUMap{}
   170  	for cpu, pieces := range availableNodeResource.CPUMap {
   171  		if pieces >= p.config.Scheduler.ShareBase {
   172  			shareCPUMap[cpu] = p.config.Scheduler.ShareBase
   173  		}
   174  	}
   175  
   176  	if len(shareCPUMap) == 0 {
   177  		for cpu := range nodeResourceInfo.Capacity.CPUMap {
   178  			shareCPUMap[cpu] = p.config.Scheduler.ShareBase
   179  		}
   180  	}
   181  
   182  	for ID, workloadResource := range workloadResourceMap {
   183  		// only process workloads without cpu binding
   184  		if len(workloadResource.CPUMap) == 0 {
   185  			engineParamsMap[ID] = &cpumemtypes.EngineParams{
   186  				CPU:      workloadResource.CPULimit,
   187  				CPUMap:   shareCPUMap,
   188  				NUMANode: workloadResource.NUMANode,
   189  				Memory:   workloadResource.MemoryLimit,
   190  				Remap:    true,
   191  			}
   192  		}
   193  	}
   194  
   195  	return resp, mapstructure.Decode(map[string]any{
   196  		"engine_params_map": engineParamsMap,
   197  	}, resp)
   198  }
   199  
   200  func (p Plugin) doAllocByMemory(resourceInfo *cpumemtypes.NodeResourceInfo, deployCount int, req *cpumemtypes.WorkloadResourceRequest) ([]*cpumemtypes.EngineParams, []*cpumemtypes.WorkloadResource, error) {
   201  	if req.CPURequest > float64(len(resourceInfo.Capacity.CPUMap)) {
   202  		return nil, nil, errors.Wrap(coretypes.ErrInsufficientCapacity, "cpu")
   203  	}
   204  
   205  	availableResource := resourceInfo.GetAvailableResource()
   206  	if req.MemRequest > 0 && availableResource.Memory/req.MemRequest < int64(deployCount) {
   207  		return nil, nil, errors.Wrap(coretypes.ErrInsufficientCapacity, "memory")
   208  	}
   209  
   210  	enginesParams := []*cpumemtypes.EngineParams{}
   211  	workloadsResource := []*cpumemtypes.WorkloadResource{}
   212  
   213  	engineParams := &cpumemtypes.EngineParams{
   214  		CPU:    req.CPULimit,
   215  		Memory: req.MemLimit,
   216  	}
   217  	workloadResource := &cpumemtypes.WorkloadResource{
   218  		CPURequest:    req.CPURequest,
   219  		CPULimit:      req.CPULimit,
   220  		MemoryRequest: req.MemRequest,
   221  		MemoryLimit:   req.MemLimit,
   222  	}
   223  
   224  	for len(enginesParams) < deployCount {
   225  		enginesParams = append(enginesParams, engineParams)
   226  		workloadsResource = append(workloadsResource, workloadResource)
   227  	}
   228  	return enginesParams, workloadsResource, nil
   229  }
   230  
   231  func (p Plugin) doAllocByCPU(resourceInfo *cpumemtypes.NodeResourceInfo, deployCount int, req *cpumemtypes.WorkloadResourceRequest) ([]*cpumemtypes.EngineParams, []*cpumemtypes.WorkloadResource, error) {
   232  	cpuPlans := schedule.GetCPUPlans(resourceInfo, nil, p.config.Scheduler.ShareBase, p.config.Scheduler.MaxShare, req)
   233  	if len(cpuPlans) < deployCount {
   234  		return nil, nil, errors.Wrap(coretypes.ErrInsufficientCapacity, "cpu")
   235  	}
   236  
   237  	cpuPlans = cpuPlans[:deployCount]
   238  	enginesParams := []*cpumemtypes.EngineParams{}
   239  	workloadsResource := []*cpumemtypes.WorkloadResource{}
   240  
   241  	for _, cpuPlan := range cpuPlans {
   242  		enginesParams = append(enginesParams, &cpumemtypes.EngineParams{
   243  			CPU:      req.CPULimit,
   244  			CPUMap:   cpuPlan.CPUMap,
   245  			NUMANode: cpuPlan.NUMANode,
   246  			Memory:   req.MemLimit,
   247  		})
   248  
   249  		workloadResource := &cpumemtypes.WorkloadResource{
   250  			CPURequest:    req.CPURequest,
   251  			CPULimit:      req.CPULimit,
   252  			MemoryRequest: req.MemRequest,
   253  			MemoryLimit:   req.MemLimit,
   254  			CPUMap:        cpuPlan.CPUMap,
   255  			NUMANode:      cpuPlan.NUMANode,
   256  		}
   257  		if len(workloadResource.NUMANode) > 0 {
   258  			workloadResource.NUMAMemory = cpumemtypes.NUMAMemory{workloadResource.NUMANode: workloadResource.MemoryRequest}
   259  		}
   260  
   261  		workloadsResource = append(workloadsResource, workloadResource)
   262  	}
   263  
   264  	return enginesParams, workloadsResource, nil
   265  }