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

     1  package cpumem
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"math"
     8  	"strconv"
     9  
    10  	"github.com/cockroachdb/errors"
    11  	"github.com/mitchellh/mapstructure"
    12  	enginetypes "github.com/projecteru2/core/engine/types"
    13  	"github.com/projecteru2/core/log"
    14  	"github.com/projecteru2/core/resource/plugins/cpumem/schedule"
    15  	cpumemtypes "github.com/projecteru2/core/resource/plugins/cpumem/types"
    16  	plugintypes "github.com/projecteru2/core/resource/plugins/types"
    17  	coretypes "github.com/projecteru2/core/types"
    18  	"github.com/projecteru2/core/utils"
    19  	"github.com/sanity-io/litter"
    20  )
    21  
    22  // AddNode .
    23  func (p Plugin) AddNode(ctx context.Context, nodename string, resource plugintypes.NodeResourceRequest, info *enginetypes.Info) (*plugintypes.AddNodeResponse, error) {
    24  	// try to get the node resource
    25  	var err error
    26  	if _, err = p.doGetNodeResourceInfo(ctx, nodename); err == nil {
    27  		return nil, coretypes.ErrNodeExists
    28  	}
    29  
    30  	if !errors.Is(err, coretypes.ErrInvaildCount) {
    31  		log.WithFunc("resource.cpumem.AddNode").WithField("node", nodename).Error(ctx, err, "failed to get resource info of node")
    32  		return nil, err
    33  	}
    34  
    35  	req := &cpumemtypes.NodeResourceRequest{}
    36  	if err := req.Parse(p.config, resource); err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	if info != nil { //nolint
    41  		// extract NodeResource from Resources
    42  		var nodeRes cpumemtypes.NodeResource
    43  		if b, ok := info.Resources[p.Name()]; ok {
    44  			if err := json.Unmarshal(b, &nodeRes); err != nil {
    45  				return nil, err
    46  			}
    47  			// NodeResource has higher priority
    48  			info.NCPU = int(nodeRes.CPU)
    49  			info.MemTotal = nodeRes.Memory
    50  		}
    51  		if len(req.CPUMap) == 0 {
    52  			req.CPUMap = cpumemtypes.CPUMap{}
    53  			for i := 0; i < info.NCPU; i++ {
    54  				req.CPUMap[strconv.Itoa(i)] = p.config.Scheduler.ShareBase
    55  			}
    56  			req.NUMA = nodeRes.NUMA
    57  		}
    58  
    59  		if req.Memory == 0 {
    60  			req.Memory = info.MemTotal * rate / 10 // use 80% of real memory
    61  			req.NUMAMemory = cpumemtypes.NUMAMemory{}
    62  			for k, v := range nodeRes.NUMAMemory {
    63  				req.NUMAMemory[k] = v * rate / 10
    64  			}
    65  		}
    66  	}
    67  
    68  	nodeResourceInfo := &cpumemtypes.NodeResourceInfo{
    69  		Capacity: &cpumemtypes.NodeResource{
    70  			CPU:        float64(len(req.CPUMap)),
    71  			CPUMap:     req.CPUMap,
    72  			Memory:     req.Memory,
    73  			NUMAMemory: req.NUMAMemory,
    74  			NUMA:       req.NUMA,
    75  		},
    76  	}
    77  
    78  	// if NUMA is set but NUMAMemory is not set
    79  	// then divide memory equally according to the number of numa nodes
    80  	if len(req.NUMA) > 0 && (req.NUMAMemory == nil || len(req.NUMAMemory) == 0) {
    81  		averageMemory := req.Memory / int64(len(req.NUMA))
    82  		nodeResourceInfo.Capacity.NUMAMemory = cpumemtypes.NUMAMemory{}
    83  		for _, ID := range req.NUMA {
    84  			nodeResourceInfo.Capacity.NUMAMemory[ID] = averageMemory
    85  		}
    86  	}
    87  
    88  	if err = p.doSetNodeResourceInfo(ctx, nodename, nodeResourceInfo); err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	resp := &plugintypes.AddNodeResponse{}
    93  	return resp, mapstructure.Decode(map[string]any{
    94  		"capacity": nodeResourceInfo.Capacity,
    95  		"usage":    nodeResourceInfo.Usage,
    96  	}, resp)
    97  }
    98  
    99  // RemoveNode .
   100  func (p Plugin) RemoveNode(ctx context.Context, nodename string) (*plugintypes.RemoveNodeResponse, error) {
   101  	var err error
   102  	if _, err = p.store.Delete(ctx, fmt.Sprintf(nodeResourceInfoKey, nodename)); err != nil {
   103  		log.WithFunc("resource.cpumem.RemoveNode").WithField("node", nodename).Error(ctx, err, "faield to delete node")
   104  	}
   105  	return &plugintypes.RemoveNodeResponse{}, err
   106  }
   107  
   108  // GetNodesDeployCapacity returns available nodes and total capacity
   109  func (p Plugin) GetNodesDeployCapacity(ctx context.Context, nodenames []string, resource plugintypes.WorkloadResourceRequest) (*plugintypes.GetNodesDeployCapacityResponse, error) {
   110  	logger := log.WithFunc("resource.cpumem.GetNodesDeployCapacity")
   111  	req := &cpumemtypes.WorkloadResourceRequest{}
   112  	if err := req.Parse(resource); err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	if err := req.Validate(); err != nil {
   117  		logger.Errorf(ctx, err, "invalid resource opts %+v", req)
   118  		return nil, err
   119  	}
   120  
   121  	nodesDeployCapacityMap := map[string]*plugintypes.NodeDeployCapacity{}
   122  	total := 0
   123  
   124  	nodesResourceInfos, err := p.doGetNodesResourceInfo(ctx, nodenames)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	for nodename, nodeResourceInfo := range nodesResourceInfos {
   130  		nodeDeployCapacity := p.doGetNodeDeployCapacity(nodeResourceInfo, req)
   131  		if nodeDeployCapacity.Capacity > 0 {
   132  			nodesDeployCapacityMap[nodename] = nodeDeployCapacity
   133  			if total == math.MaxInt || nodeDeployCapacity.Capacity == math.MaxInt {
   134  				total = math.MaxInt
   135  			} else {
   136  				total += nodeDeployCapacity.Capacity
   137  			}
   138  		}
   139  	}
   140  
   141  	resp := &plugintypes.GetNodesDeployCapacityResponse{}
   142  	return resp, mapstructure.Decode(map[string]any{
   143  		"nodes_deploy_capacity_map": nodesDeployCapacityMap,
   144  		"total":                     total,
   145  	}, resp)
   146  }
   147  
   148  // SetNodeResourceCapacity sets the amount of total resource info
   149  func (p Plugin) SetNodeResourceCapacity(ctx context.Context, nodename string, resource plugintypes.NodeResource, resourceRequest plugintypes.NodeResourceRequest, delta bool, incr bool) (*plugintypes.SetNodeResourceCapacityResponse, error) {
   150  	logger := log.WithFunc("resource.cpumem.SetNodeResourceCapacity").WithField("node", "nodename")
   151  	req, nodeResource, _, nodeResourceInfo, err := p.parseNodeResourceInfos(ctx, nodename, resource, resourceRequest, nil)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	origin := nodeResourceInfo.Capacity
   156  	before := origin.DeepCopy()
   157  
   158  	if !delta && req != nil {
   159  		req.LoadFromOrigin(origin, resourceRequest)
   160  	}
   161  	nodeResourceInfo.Capacity = p.calculateNodeResource(req, nodeResource, origin, nil, delta, incr)
   162  
   163  	// add new cpu
   164  	for cpu := range nodeResourceInfo.Capacity.CPUMap {
   165  		if _, ok := nodeResourceInfo.Usage.CPUMap[cpu]; !ok {
   166  			nodeResourceInfo.Usage.CPUMap[cpu] = 0
   167  		}
   168  	}
   169  	// delete cpus with no pieces
   170  	nodeResourceInfo.RemoveEmptyCores()
   171  
   172  	if err := p.doSetNodeResourceInfo(ctx, nodename, nodeResourceInfo); err != nil {
   173  		logger.Errorf(ctx, err, "node resource info %+v", litter.Sdump(nodeResourceInfo))
   174  		return nil, err
   175  	}
   176  
   177  	resp := &plugintypes.SetNodeResourceCapacityResponse{}
   178  	return resp, mapstructure.Decode(map[string]any{
   179  		"before": before,
   180  		"after":  nodeResourceInfo.Capacity,
   181  	}, resp)
   182  }
   183  
   184  // GetNodeResourceInfo .
   185  func (p Plugin) GetNodeResourceInfo(ctx context.Context, nodename string, workloadsResource []plugintypes.WorkloadResource) (*plugintypes.GetNodeResourceInfoResponse, error) {
   186  	nodeResourceInfo, _, diffs, err := p.getNodeResourceInfo(ctx, nodename, workloadsResource)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	resp := &plugintypes.GetNodeResourceInfoResponse{}
   192  	return resp, mapstructure.Decode(map[string]any{
   193  		"capacity": nodeResourceInfo.Capacity,
   194  		"usage":    nodeResourceInfo.Usage,
   195  		"diffs":    diffs,
   196  	}, resp)
   197  }
   198  
   199  // SetNodeResourceInfo .
   200  func (p Plugin) SetNodeResourceInfo(ctx context.Context, nodename string, capacity plugintypes.NodeResource, usage plugintypes.NodeResource) (*plugintypes.SetNodeResourceInfoResponse, error) {
   201  	capacityResource := &cpumemtypes.NodeResource{}
   202  	usageResource := &cpumemtypes.NodeResource{}
   203  	if err := capacityResource.Parse(capacity); err != nil {
   204  		return nil, err
   205  	}
   206  	if err := usageResource.Parse(usage); err != nil {
   207  		return nil, err
   208  	}
   209  	resourceInfo := &cpumemtypes.NodeResourceInfo{
   210  		Capacity: capacityResource,
   211  		Usage:    usageResource,
   212  	}
   213  
   214  	return &plugintypes.SetNodeResourceInfoResponse{}, p.doSetNodeResourceInfo(ctx, nodename, resourceInfo)
   215  }
   216  
   217  // SetNodeResourceUsage .
   218  func (p Plugin) SetNodeResourceUsage(ctx context.Context, nodename string, resource plugintypes.NodeResource, resourceRequest plugintypes.NodeResourceRequest, workloadsResource []plugintypes.WorkloadResource, delta bool, incr bool) (*plugintypes.SetNodeResourceUsageResponse, error) {
   219  	logger := log.WithFunc("resource.cpumem.SetNodeResourceUsage").WithField("node", "nodename")
   220  	req, nodeResource, wrksResource, nodeResourceInfo, err := p.parseNodeResourceInfos(ctx, nodename, resource, resourceRequest, workloadsResource)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  	origin := nodeResourceInfo.Usage
   225  	before := origin.DeepCopy()
   226  
   227  	nodeResourceInfo.Usage = p.calculateNodeResource(req, nodeResource, origin, wrksResource, delta, incr)
   228  
   229  	if err := p.doSetNodeResourceInfo(ctx, nodename, nodeResourceInfo); err != nil {
   230  		logger.Errorf(ctx, err, "node resource info %+v", litter.Sdump(nodeResourceInfo))
   231  		return nil, err
   232  	}
   233  
   234  	resp := &plugintypes.SetNodeResourceUsageResponse{}
   235  	return resp, mapstructure.Decode(map[string]any{
   236  		"before": before,
   237  		"after":  nodeResourceInfo.Usage,
   238  	}, resp)
   239  }
   240  
   241  // GetMostIdleNode .
   242  func (p Plugin) GetMostIdleNode(ctx context.Context, nodenames []string) (*plugintypes.GetMostIdleNodeResponse, error) {
   243  	var mostIdleNode string
   244  	var minIdle = math.MaxFloat64
   245  
   246  	nodesResourceInfo, err := p.doGetNodesResourceInfo(ctx, nodenames)
   247  	if err != nil {
   248  		return nil, err
   249  	}
   250  
   251  	for nodename, nodeResourceInfo := range nodesResourceInfo {
   252  		idle := float64(nodeResourceInfo.Usage.CPUMap.TotalPieces()) / float64(nodeResourceInfo.Capacity.CPUMap.TotalPieces())
   253  		idle += float64(nodeResourceInfo.Usage.Memory) / float64(nodeResourceInfo.Capacity.Memory)
   254  
   255  		if idle < minIdle {
   256  			mostIdleNode = nodename
   257  			minIdle = idle
   258  		}
   259  	}
   260  
   261  	resp := &plugintypes.GetMostIdleNodeResponse{}
   262  	return resp, mapstructure.Decode(map[string]any{
   263  		"nodename": mostIdleNode,
   264  		"priority": priority,
   265  	}, resp)
   266  }
   267  
   268  // FixNodeResource .
   269  func (p Plugin) FixNodeResource(ctx context.Context, nodename string, workloadsResource []plugintypes.WorkloadResource) (*plugintypes.GetNodeResourceInfoResponse, error) {
   270  	nodeResourceInfo, actuallyWorkloadsUsage, diffs, err := p.getNodeResourceInfo(ctx, nodename, workloadsResource)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  
   275  	if len(diffs) != 0 {
   276  		nodeResourceInfo.Usage = &cpumemtypes.NodeResource{
   277  			CPU:        actuallyWorkloadsUsage.CPURequest,
   278  			CPUMap:     actuallyWorkloadsUsage.CPUMap,
   279  			Memory:     actuallyWorkloadsUsage.MemoryRequest,
   280  			NUMAMemory: actuallyWorkloadsUsage.NUMAMemory,
   281  		}
   282  		if err = p.doSetNodeResourceInfo(ctx, nodename, nodeResourceInfo); err != nil {
   283  			log.WithFunc("resource.cpumem.FixNodeResource").Error(ctx, err)
   284  			diffs = append(diffs, err.Error())
   285  		}
   286  	}
   287  
   288  	resp := &plugintypes.GetNodeResourceInfoResponse{}
   289  	return resp, mapstructure.Decode(map[string]any{
   290  		"capacity": nodeResourceInfo.Capacity,
   291  		"usage":    nodeResourceInfo.Usage,
   292  		"diffs":    diffs,
   293  	}, resp)
   294  }
   295  
   296  func (p Plugin) getNodeResourceInfo(ctx context.Context, nodename string, workloadsResource []plugintypes.WorkloadResource) (*cpumemtypes.NodeResourceInfo, *cpumemtypes.WorkloadResource, []string, error) {
   297  	logger := log.WithFunc("resource.cpumem.getNodeResourceInfo").WithField("node", nodename)
   298  	nodeResourceInfo, err := p.doGetNodeResourceInfo(ctx, nodename)
   299  	if err != nil {
   300  		logger.Error(ctx, err)
   301  		return nil, nil, nil, err
   302  	}
   303  
   304  	actuallyWorkloadsUsage := &cpumemtypes.WorkloadResource{CPUMap: cpumemtypes.CPUMap{}, NUMAMemory: cpumemtypes.NUMAMemory{}}
   305  	for _, workloadResource := range workloadsResource {
   306  		workloadUsage := &cpumemtypes.WorkloadResource{}
   307  		if err := workloadUsage.Parse(workloadResource); err != nil {
   308  			logger.Error(ctx, err)
   309  			return nil, nil, nil, err
   310  		}
   311  		actuallyWorkloadsUsage.Add(workloadUsage)
   312  	}
   313  
   314  	diffs := []string{}
   315  
   316  	actuallyWorkloadsUsage.CPURequest = utils.Round(actuallyWorkloadsUsage.CPURequest)
   317  	totalCPUUsage := utils.Round(nodeResourceInfo.Usage.CPU)
   318  	if actuallyWorkloadsUsage.CPURequest != totalCPUUsage {
   319  		diffs = append(diffs, fmt.Sprintf("node.CPUUsed != sum(workload.CPURequest): %.2f != %.2f", totalCPUUsage, actuallyWorkloadsUsage.CPURequest))
   320  	}
   321  
   322  	for cpu := range nodeResourceInfo.Capacity.CPUMap {
   323  		if actuallyWorkloadsUsage.CPUMap[cpu] != nodeResourceInfo.Usage.CPUMap[cpu] {
   324  			diffs = append(diffs, fmt.Sprintf("node.CPUMap[%+v] != sum(workload.CPUMap[%+v]): %+v != %+v", cpu, cpu, nodeResourceInfo.Usage.CPUMap[cpu], actuallyWorkloadsUsage.CPUMap[cpu]))
   325  		}
   326  	}
   327  
   328  	for numaNodeID := range nodeResourceInfo.Capacity.NUMAMemory {
   329  		if actuallyWorkloadsUsage.NUMAMemory[numaNodeID] != nodeResourceInfo.Usage.NUMAMemory[numaNodeID] {
   330  			diffs = append(diffs, fmt.Sprintf("node.NUMAMemory[%+v] != sum(workload.NUMAMemory[%+v]: %+v != %+v)", numaNodeID, numaNodeID, nodeResourceInfo.Usage.NUMAMemory[numaNodeID], actuallyWorkloadsUsage.NUMAMemory[numaNodeID]))
   331  		}
   332  	}
   333  
   334  	if nodeResourceInfo.Usage.Memory != actuallyWorkloadsUsage.MemoryRequest {
   335  		diffs = append(diffs, fmt.Sprintf("node.MemoryUsed != sum(workload.MemoryRequest): %d != %d", nodeResourceInfo.Usage.Memory, actuallyWorkloadsUsage.MemoryRequest))
   336  	}
   337  
   338  	return nodeResourceInfo, actuallyWorkloadsUsage, diffs, nil
   339  }
   340  
   341  func (p Plugin) doGetNodeResourceInfo(ctx context.Context, nodename string) (*cpumemtypes.NodeResourceInfo, error) {
   342  	resp, err := p.doGetNodesResourceInfo(ctx, []string{nodename})
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  	return resp[nodename], err
   347  }
   348  
   349  func (p Plugin) doGetNodesResourceInfo(ctx context.Context, nodenames []string) (map[string]*cpumemtypes.NodeResourceInfo, error) {
   350  	keys := []string{}
   351  	for _, nodename := range nodenames {
   352  		keys = append(keys, fmt.Sprintf(nodeResourceInfoKey, nodename))
   353  	}
   354  	resps, err := p.store.GetMulti(ctx, keys)
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  
   359  	result := map[string]*cpumemtypes.NodeResourceInfo{}
   360  
   361  	for _, resp := range resps {
   362  		r := &cpumemtypes.NodeResourceInfo{}
   363  		if err := json.Unmarshal(resp.Value, r); err != nil {
   364  			return nil, err
   365  		}
   366  		result[utils.Tail(string(resp.Key))] = r
   367  	}
   368  	return result, nil
   369  }
   370  
   371  func (p Plugin) doSetNodeResourceInfo(ctx context.Context, nodename string, resourceInfo *cpumemtypes.NodeResourceInfo) error {
   372  	if err := resourceInfo.Validate(); err != nil {
   373  		return err
   374  	}
   375  
   376  	data, err := json.Marshal(resourceInfo)
   377  	if err != nil {
   378  		return err
   379  	}
   380  
   381  	_, err = p.store.Put(ctx, fmt.Sprintf(nodeResourceInfoKey, nodename), string(data))
   382  	return err
   383  }
   384  
   385  func (p Plugin) doGetNodeDeployCapacity(nodeResourceInfo *cpumemtypes.NodeResourceInfo, req *cpumemtypes.WorkloadResourceRequest) *plugintypes.NodeDeployCapacity {
   386  	availableResource := nodeResourceInfo.GetAvailableResource()
   387  
   388  	capacityInfo := &plugintypes.NodeDeployCapacity{
   389  		Weight: 1, // TODO why 1?
   390  	}
   391  	// if cpu-bind is not required, then returns capacity by memory
   392  	if !req.CPUBind {
   393  		// check if cpu is enough
   394  		if req.CPURequest > float64(len(nodeResourceInfo.Capacity.CPUMap)) {
   395  			return capacityInfo
   396  		}
   397  
   398  		// calculate by memory request
   399  		if req.MemRequest == 0 {
   400  			capacityInfo.Capacity = math.MaxInt
   401  			capacityInfo.Rate = 0
   402  		} else {
   403  			capacityInfo.Capacity = int(availableResource.Memory / req.MemRequest)
   404  			capacityInfo.Rate = utils.AdvancedDivide(float64(req.MemRequest), float64(nodeResourceInfo.Capacity.Memory))
   405  		}
   406  		capacityInfo.Usage = utils.AdvancedDivide(float64(nodeResourceInfo.Usage.Memory), float64(nodeResourceInfo.Capacity.Memory))
   407  		return capacityInfo
   408  	}
   409  
   410  	// if cpu-bind is required, then returns capacity by cpu scheduling
   411  	cpuPlans := schedule.GetCPUPlans(nodeResourceInfo, nil, p.config.Scheduler.ShareBase, p.config.Scheduler.MaxShare, req)
   412  	capacityInfo.Capacity = len(cpuPlans)
   413  	capacityInfo.Usage = utils.AdvancedDivide(nodeResourceInfo.Usage.CPU, nodeResourceInfo.Capacity.CPU)
   414  	capacityInfo.Rate = utils.AdvancedDivide(req.CPURequest, nodeResourceInfo.Capacity.CPU)
   415  	capacityInfo.Weight = 100 // cpu-bind above all
   416  	return capacityInfo
   417  }
   418  
   419  // calculateNodeResource priority: node resource request > node resource > workload resource args list
   420  func (p Plugin) calculateNodeResource(req *cpumemtypes.NodeResourceRequest, nodeResource *cpumemtypes.NodeResource, origin *cpumemtypes.NodeResource, workloadsResource []*cpumemtypes.WorkloadResource, delta bool, incr bool) *cpumemtypes.NodeResource {
   421  	var resp *cpumemtypes.NodeResource
   422  	if origin == nil || !delta { // no delta means node resource rewrite with whole new data
   423  		resp = (&cpumemtypes.NodeResource{}).DeepCopy() // init nil pointer!
   424  		// 这个接口最诡异的在于,如果 delta 为 false,意味着是全量写入
   425  		// 但这时候 incr 为 false 的话
   426  		// 实际上是 set 进了负值,所以这里 incr 应该强制为 true
   427  		incr = true
   428  	} else {
   429  		resp = origin.DeepCopy()
   430  	}
   431  
   432  	if req != nil {
   433  		nodeResource = &cpumemtypes.NodeResource{
   434  			CPU:        float64(len(req.CPUMap)),
   435  			CPUMap:     req.CPUMap,
   436  			Memory:     req.Memory,
   437  			NUMAMemory: req.NUMAMemory,
   438  			NUMA:       req.NUMA,
   439  		}
   440  	}
   441  
   442  	if nodeResource != nil {
   443  		if incr {
   444  			resp.Add(nodeResource)
   445  		} else {
   446  			resp.Sub(nodeResource)
   447  		}
   448  		return resp
   449  	}
   450  
   451  	for _, workloadResource := range workloadsResource {
   452  		nodeResource = &cpumemtypes.NodeResource{
   453  			CPU:        workloadResource.CPURequest,
   454  			CPUMap:     workloadResource.CPUMap,
   455  			NUMAMemory: workloadResource.NUMAMemory,
   456  			Memory:     workloadResource.MemoryRequest,
   457  		}
   458  		if incr {
   459  			resp.Add(nodeResource)
   460  		} else {
   461  			resp.Sub(nodeResource)
   462  		}
   463  	}
   464  	return resp
   465  }
   466  
   467  func (p Plugin) parseNodeResourceInfos(
   468  	ctx context.Context, nodename string,
   469  	resource plugintypes.NodeResource,
   470  	resourceRequest plugintypes.NodeResourceRequest,
   471  	workloadsResource []plugintypes.WorkloadResource,
   472  ) (
   473  	*cpumemtypes.NodeResourceRequest,
   474  	*cpumemtypes.NodeResource,
   475  	[]*cpumemtypes.WorkloadResource,
   476  	*cpumemtypes.NodeResourceInfo,
   477  	error,
   478  ) {
   479  	var req *cpumemtypes.NodeResourceRequest
   480  	var nodeResource *cpumemtypes.NodeResource
   481  	wrksResource := []*cpumemtypes.WorkloadResource{}
   482  
   483  	if resourceRequest != nil {
   484  		req = &cpumemtypes.NodeResourceRequest{}
   485  		if err := req.Parse(p.config, resourceRequest); err != nil {
   486  			return nil, nil, nil, nil, err
   487  		}
   488  	}
   489  
   490  	if resource != nil {
   491  		nodeResource = &cpumemtypes.NodeResource{}
   492  		if err := nodeResource.Parse(resource); err != nil {
   493  			return nil, nil, nil, nil, err
   494  		}
   495  	}
   496  
   497  	for _, workloadResource := range workloadsResource {
   498  		wrkResource := &cpumemtypes.WorkloadResource{}
   499  		if err := wrkResource.Parse(workloadResource); err != nil {
   500  			return nil, nil, nil, nil, err
   501  		}
   502  		wrksResource = append(wrksResource, wrkResource)
   503  	}
   504  
   505  	nodeResourceInfo, err := p.doGetNodeResourceInfo(ctx, nodename)
   506  	if err != nil {
   507  		return nil, nil, nil, nil, err
   508  	}
   509  	return req, nodeResource, wrksResource, nodeResourceInfo, nil
   510  }