Commit c93646e8 authored by Connor Doyle's avatar Connor Doyle

Support opaque integer resource accounting.

- Prevents kubelet from overwriting capacity during sync. - Handles opaque integer resources in the scheduler. - Adds scheduler predicate tests for opaque resources. - Validates opaque int resources: - Ensures supplied opaque int quantities in node capacity, node allocatable, pod request and pod limit are integers. - Adds tests for new validation logic (node update and pod spec). - Added e2e tests for opaque integer resources.
parent 1cba31af
...@@ -120,6 +120,22 @@ func IsStandardContainerResourceName(str string) bool { ...@@ -120,6 +120,22 @@ func IsStandardContainerResourceName(str string) bool {
return standardContainerResources.Has(str) return standardContainerResources.Has(str)
} }
// IsOpaqueIntResourceName returns true if the resource name has the opaque
// integer resource prefix.
func IsOpaqueIntResourceName(name ResourceName) bool {
return strings.HasPrefix(string(name), ResourceOpaqueIntPrefix)
}
// OpaqueIntResourceName returns a ResourceName with the canonical opaque
// integer prefix prepended. If the argument already has the prefix, it is
// returned unmodified.
func OpaqueIntResourceName(name string) ResourceName {
if IsOpaqueIntResourceName(ResourceName(name)) {
return ResourceName(name)
}
return ResourceName(fmt.Sprintf("%s%s", ResourceOpaqueIntPrefix, name))
}
var standardLimitRangeTypes = sets.NewString( var standardLimitRangeTypes = sets.NewString(
string(LimitTypePod), string(LimitTypePod),
string(LimitTypeContainer), string(LimitTypeContainer),
...@@ -193,7 +209,7 @@ var integerResources = sets.NewString( ...@@ -193,7 +209,7 @@ var integerResources = sets.NewString(
// IsIntegerResourceName returns true if the resource is measured in integer values // IsIntegerResourceName returns true if the resource is measured in integer values
func IsIntegerResourceName(str string) bool { func IsIntegerResourceName(str string) bool {
return integerResources.Has(str) return integerResources.Has(str) || IsOpaqueIntResourceName(ResourceName(str))
} }
// NewDeleteOptions returns a DeleteOptions indicating the resource should // NewDeleteOptions returns a DeleteOptions indicating the resource should
......
...@@ -2631,6 +2631,11 @@ const ( ...@@ -2631,6 +2631,11 @@ const (
// Number of Pods that may be running on this Node: see ResourcePods // Number of Pods that may be running on this Node: see ResourcePods
) )
const (
// Namespace prefix for opaque counted resources (alpha).
ResourceOpaqueIntPrefix = "pod.alpha.kubernetes.io/opaque-int-resource-"
)
// ResourceList is a set of (resource name, quantity) pairs. // ResourceList is a set of (resource name, quantity) pairs.
type ResourceList map[ResourceName]resource.Quantity type ResourceList map[ResourceName]resource.Quantity
......
...@@ -2845,6 +2845,17 @@ func ValidateNodeUpdate(node, oldNode *api.Node) field.ErrorList { ...@@ -2845,6 +2845,17 @@ func ValidateNodeUpdate(node, oldNode *api.Node) field.ErrorList {
// allErrs = append(allErrs, field.Invalid("status", node.Status, "must be empty")) // allErrs = append(allErrs, field.Invalid("status", node.Status, "must be empty"))
// } // }
// Validate resource quantities in capacity.
for k, v := range node.Status.Capacity {
resPath := field.NewPath("status", "capacity", string(k))
allErrs = append(allErrs, ValidateResourceQuantityValue(string(k), v, resPath)...)
}
// Validate resource quantities in allocatable.
for k, v := range node.Status.Allocatable {
resPath := field.NewPath("status", "allocatable", string(k))
allErrs = append(allErrs, ValidateResourceQuantityValue(string(k), v, resPath)...)
}
// Validte no duplicate addresses in node status. // Validte no duplicate addresses in node status.
addresses := make(map[api.NodeAddress]bool) addresses := make(map[api.NodeAddress]bool)
for i, address := range node.Status.Addresses { for i, address := range node.Status.Addresses {
...@@ -3236,9 +3247,10 @@ func ValidateResourceRequirements(requirements *api.ResourceRequirements, fldPat ...@@ -3236,9 +3247,10 @@ func ValidateResourceRequirements(requirements *api.ResourceRequirements, fldPat
fldPath := limPath.Key(string(resourceName)) fldPath := limPath.Key(string(resourceName))
// Validate resource name. // Validate resource name.
allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...) allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...)
if api.IsStandardResourceName(string(resourceName)) {
allErrs = append(allErrs, validateBasicResource(quantity, fldPath.Key(string(resourceName)))...) // Validate resource quantity.
} allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...)
// Check that request <= limit. // Check that request <= limit.
requestQuantity, exists := requirements.Requests[resourceName] requestQuantity, exists := requirements.Requests[resourceName]
if exists { if exists {
...@@ -3254,10 +3266,10 @@ func ValidateResourceRequirements(requirements *api.ResourceRequirements, fldPat ...@@ -3254,10 +3266,10 @@ func ValidateResourceRequirements(requirements *api.ResourceRequirements, fldPat
fldPath := reqPath.Key(string(resourceName)) fldPath := reqPath.Key(string(resourceName))
// Validate resource name. // Validate resource name.
allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...) allErrs = append(allErrs, validateContainerResourceName(string(resourceName), fldPath)...)
if api.IsStandardResourceName(string(resourceName)) { // Validate resource quantity.
allErrs = append(allErrs, validateBasicResource(quantity, fldPath.Key(string(resourceName)))...) allErrs = append(allErrs, ValidateResourceQuantityValue(string(resourceName), quantity, fldPath)...)
}
} }
return allErrs return allErrs
} }
......
...@@ -3665,6 +3665,52 @@ func TestValidatePod(t *testing.T) { ...@@ -3665,6 +3665,52 @@ func TestValidatePod(t *testing.T) {
}, },
Spec: validPodSpec, Spec: validPodSpec,
}, },
{ // valid opaque integer resources for init container
ObjectMeta: api.ObjectMeta{Name: "valid-opaque-int", Namespace: "ns"},
Spec: api.PodSpec{
InitContainers: []api.Container{
{
Name: "valid-opaque-int",
Image: "image",
ImagePullPolicy: "IfNotPresent",
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("10"),
},
Limits: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("20"),
},
},
},
},
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
{ // valid opaque integer resources for regular container
ObjectMeta: api.ObjectMeta{Name: "valid-opaque-int", Namespace: "ns"},
Spec: api.PodSpec{
InitContainers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
Containers: []api.Container{
{
Name: "valid-opaque-int",
Image: "image",
ImagePullPolicy: "IfNotPresent",
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("10"),
},
Limits: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("20"),
},
},
},
},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
} }
for _, pod := range successCases { for _, pod := range successCases {
if errs := ValidatePod(&pod); len(errs) != 0 { if errs := ValidatePod(&pod); len(errs) != 0 {
...@@ -4155,6 +4201,112 @@ func TestValidatePod(t *testing.T) { ...@@ -4155,6 +4201,112 @@ func TestValidatePod(t *testing.T) {
}, },
Spec: validPodSpec, Spec: validPodSpec,
}, },
"invalid opaque integer resource requirement: request must be <= limit": {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "invalid",
Image: "image",
ImagePullPolicy: "IfNotPresent",
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("2"),
},
Limits: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("1"),
},
},
},
},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
"invalid fractional opaque integer resource in container request": {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "invalid",
Image: "image",
ImagePullPolicy: "IfNotPresent",
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("500m"),
},
},
},
},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
"invalid fractional opaque integer resource in init container request": {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: api.PodSpec{
InitContainers: []api.Container{
{
Name: "invalid",
Image: "image",
ImagePullPolicy: "IfNotPresent",
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("500m"),
},
},
},
},
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
"invalid fractional opaque integer resource in container limit": {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "invalid",
Image: "image",
ImagePullPolicy: "IfNotPresent",
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("5"),
},
Limits: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("2.5"),
},
},
},
},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
"invalid fractional opaque integer resource in init container limit": {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: api.PodSpec{
InitContainers: []api.Container{
{
Name: "invalid",
Image: "image",
ImagePullPolicy: "IfNotPresent",
Resources: api.ResourceRequirements{
Requests: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("5"),
},
Limits: api.ResourceList{
api.OpaqueIntResourceName("A"): resource.MustParse("2.5"),
},
},
},
},
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
} }
for k, v := range errorCases { for k, v := range errorCases {
if errs := ValidatePod(&v); len(errs) == 0 { if errs := ValidatePod(&v); len(errs) == 0 {
...@@ -6347,6 +6499,60 @@ func TestValidateNodeUpdate(t *testing.T) { ...@@ -6347,6 +6499,60 @@ func TestValidateNodeUpdate(t *testing.T) {
}, },
}, },
}, false}, }, false},
{api.Node{
ObjectMeta: api.ObjectMeta{
Name: "valid-opaque-int-resources",
},
}, api.Node{
ObjectMeta: api.ObjectMeta{
Name: "valid-opaque-int-resources",
},
Status: api.NodeStatus{
Capacity: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
api.OpaqueIntResourceName("A"): resource.MustParse("5"),
api.OpaqueIntResourceName("B"): resource.MustParse("10"),
},
},
}, true},
{api.Node{
ObjectMeta: api.ObjectMeta{
Name: "invalid-fractional-opaque-int-capacity",
},
}, api.Node{
ObjectMeta: api.ObjectMeta{
Name: "invalid-fractional-opaque-int-capacity",
},
Status: api.NodeStatus{
Capacity: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
api.OpaqueIntResourceName("A"): resource.MustParse("500m"),
},
},
}, false},
{api.Node{
ObjectMeta: api.ObjectMeta{
Name: "invalid-fractional-opaque-int-allocatable",
},
}, api.Node{
ObjectMeta: api.ObjectMeta{
Name: "invalid-fractional-opaque-int-allocatable",
},
Status: api.NodeStatus{
Capacity: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
api.OpaqueIntResourceName("A"): resource.MustParse("5"),
},
Allocatable: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
api.OpaqueIntResourceName("A"): resource.MustParse("4.5"),
},
},
}, false},
} }
for i, test := range tests { for i, test := range tests {
test.oldNode.ObjectMeta.ResourceVersion = "1" test.oldNode.ObjectMeta.ResourceVersion = "1"
......
...@@ -436,23 +436,32 @@ func (kl *Kubelet) setNodeAddress(node *api.Node) error { ...@@ -436,23 +436,32 @@ func (kl *Kubelet) setNodeAddress(node *api.Node) error {
} }
func (kl *Kubelet) setNodeStatusMachineInfo(node *api.Node) { func (kl *Kubelet) setNodeStatusMachineInfo(node *api.Node) {
// Note: avoid blindly overwriting the capacity in case opaque
// resources are being advertised.
if node.Status.Capacity == nil {
node.Status.Capacity = api.ResourceList{}
}
// TODO: Post NotReady if we cannot get MachineInfo from cAdvisor. This needs to start // TODO: Post NotReady if we cannot get MachineInfo from cAdvisor. This needs to start
// cAdvisor locally, e.g. for test-cmd.sh, and in integration test. // cAdvisor locally, e.g. for test-cmd.sh, and in integration test.
info, err := kl.GetCachedMachineInfo() info, err := kl.GetCachedMachineInfo()
if err != nil { if err != nil {
// TODO(roberthbailey): This is required for test-cmd.sh to pass. // TODO(roberthbailey): This is required for test-cmd.sh to pass.
// See if the test should be updated instead. // See if the test should be updated instead.
node.Status.Capacity = api.ResourceList{ node.Status.Capacity[api.ResourceCPU] = *resource.NewMilliQuantity(0, resource.DecimalSI)
api.ResourceCPU: *resource.NewMilliQuantity(0, resource.DecimalSI), node.Status.Capacity[api.ResourceMemory] = resource.MustParse("0Gi")
api.ResourceMemory: resource.MustParse("0Gi"), node.Status.Capacity[api.ResourcePods] = *resource.NewQuantity(int64(kl.maxPods), resource.DecimalSI)
api.ResourcePods: *resource.NewQuantity(int64(kl.maxPods), resource.DecimalSI), node.Status.Capacity[api.ResourceNvidiaGPU] = *resource.NewQuantity(int64(kl.nvidiaGPUs), resource.DecimalSI)
api.ResourceNvidiaGPU: *resource.NewQuantity(int64(kl.nvidiaGPUs), resource.DecimalSI),
}
glog.Errorf("Error getting machine info: %v", err) glog.Errorf("Error getting machine info: %v", err)
} else { } else {
node.Status.NodeInfo.MachineID = info.MachineID node.Status.NodeInfo.MachineID = info.MachineID
node.Status.NodeInfo.SystemUUID = info.SystemUUID node.Status.NodeInfo.SystemUUID = info.SystemUUID
node.Status.Capacity = cadvisor.CapacityFromMachineInfo(info)
for rName, rCap := range cadvisor.CapacityFromMachineInfo(info) {
node.Status.Capacity[rName] = rCap
}
if kl.podsPerCore > 0 { if kl.podsPerCore > 0 {
node.Status.Capacity[api.ResourcePods] = *resource.NewQuantity( node.Status.Capacity[api.ResourcePods] = *resource.NewQuantity(
int64(math.Min(float64(info.NumCores*kl.podsPerCore), float64(kl.maxPods))), resource.DecimalSI) int64(math.Min(float64(info.NumCores*kl.podsPerCore), float64(kl.maxPods))), resource.DecimalSI)
......
...@@ -430,19 +430,53 @@ func (c *VolumeZoneChecker) predicate(pod *api.Pod, meta interface{}, nodeInfo * ...@@ -430,19 +430,53 @@ func (c *VolumeZoneChecker) predicate(pod *api.Pod, meta interface{}, nodeInfo *
func GetResourceRequest(pod *api.Pod) *schedulercache.Resource { func GetResourceRequest(pod *api.Pod) *schedulercache.Resource {
result := schedulercache.Resource{} result := schedulercache.Resource{}
for _, container := range pod.Spec.Containers { for _, container := range pod.Spec.Containers {
requests := container.Resources.Requests for rName, rQuantity := range container.Resources.Requests {
result.Memory += requests.Memory().Value() switch rName {
result.MilliCPU += requests.Cpu().MilliValue() case api.ResourceMemory:
result.NvidiaGPU += requests.NvidiaGPU().Value() result.Memory += rQuantity.Value()
case api.ResourceCPU:
result.MilliCPU += rQuantity.MilliValue()
case api.ResourceNvidiaGPU:
result.NvidiaGPU += rQuantity.Value()
default:
if api.IsOpaqueIntResourceName(rName) {
// Lazily allocate this map only if required.
if result.OpaqueIntResources == nil {
result.OpaqueIntResources = map[api.ResourceName]int64{}
}
result.OpaqueIntResources[rName] += rQuantity.Value()
}
}
}
} }
// take max_resource(sum_pod, any_init_container) // take max_resource(sum_pod, any_init_container)
for _, container := range pod.Spec.InitContainers { for _, container := range pod.Spec.InitContainers {
requests := container.Resources.Requests for rName, rQuantity := range container.Resources.Requests {
if mem := requests.Memory().Value(); mem > result.Memory { switch rName {
result.Memory = mem case api.ResourceMemory:
} if mem := rQuantity.Value(); mem > result.Memory {
if cpu := requests.Cpu().MilliValue(); cpu > result.MilliCPU { result.Memory = mem
result.MilliCPU = cpu }
case api.ResourceCPU:
if cpu := rQuantity.MilliValue(); cpu > result.MilliCPU {
result.MilliCPU = cpu
}
case api.ResourceNvidiaGPU:
if gpu := rQuantity.Value(); gpu > result.NvidiaGPU {
result.NvidiaGPU = gpu
}
default:
if api.IsOpaqueIntResourceName(rName) {
// Lazily allocate this map only if required.
if result.OpaqueIntResources == nil {
result.OpaqueIntResources = map[api.ResourceName]int64{}
}
value := rQuantity.Value()
if value > result.OpaqueIntResources[rName] {
result.OpaqueIntResources[rName] = value
}
}
}
} }
} }
return &result return &result
...@@ -471,7 +505,7 @@ func PodFitsResources(pod *api.Pod, meta interface{}, nodeInfo *schedulercache.N ...@@ -471,7 +505,7 @@ func PodFitsResources(pod *api.Pod, meta interface{}, nodeInfo *schedulercache.N
// We couldn't parse metadata - fallback to computing it. // We couldn't parse metadata - fallback to computing it.
podRequest = GetResourceRequest(pod) podRequest = GetResourceRequest(pod)
} }
if podRequest.MilliCPU == 0 && podRequest.Memory == 0 && podRequest.NvidiaGPU == 0 { if podRequest.MilliCPU == 0 && podRequest.Memory == 0 && podRequest.NvidiaGPU == 0 && len(podRequest.OpaqueIntResources) == 0 {
return len(predicateFails) == 0, predicateFails, nil return len(predicateFails) == 0, predicateFails, nil
} }
...@@ -485,6 +519,12 @@ func PodFitsResources(pod *api.Pod, meta interface{}, nodeInfo *schedulercache.N ...@@ -485,6 +519,12 @@ func PodFitsResources(pod *api.Pod, meta interface{}, nodeInfo *schedulercache.N
if allocatable.NvidiaGPU < podRequest.NvidiaGPU+nodeInfo.RequestedResource().NvidiaGPU { if allocatable.NvidiaGPU < podRequest.NvidiaGPU+nodeInfo.RequestedResource().NvidiaGPU {
predicateFails = append(predicateFails, NewInsufficientResourceError(api.ResourceNvidiaGPU, podRequest.NvidiaGPU, nodeInfo.RequestedResource().NvidiaGPU, allocatable.NvidiaGPU)) predicateFails = append(predicateFails, NewInsufficientResourceError(api.ResourceNvidiaGPU, podRequest.NvidiaGPU, nodeInfo.RequestedResource().NvidiaGPU, allocatable.NvidiaGPU))
} }
for rName, rQuant := range podRequest.OpaqueIntResources {
if allocatable.OpaqueIntResources[rName] < rQuant+nodeInfo.RequestedResource().OpaqueIntResources[rName] {
predicateFails = append(predicateFails, NewInsufficientResourceError(rName, podRequest.OpaqueIntResources[rName], nodeInfo.RequestedResource().OpaqueIntResources[rName], allocatable.OpaqueIntResources[rName]))
}
}
if glog.V(10) { if glog.V(10) {
// We explicitly don't do glog.V(10).Infof() to avoid computing all the parameters if this is // We explicitly don't do glog.V(10).Infof() to avoid computing all the parameters if this is
// not logged. There is visible performance gain from it. // not logged. There is visible performance gain from it.
......
...@@ -21,6 +21,7 @@ go_library( ...@@ -21,6 +21,7 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/api:go_default_library", "//pkg/api:go_default_library",
"//pkg/api/resource:go_default_library",
"//pkg/client/cache:go_default_library", "//pkg/client/cache:go_default_library",
"//pkg/labels:go_default_library", "//pkg/labels:go_default_library",
"//pkg/util/wait:go_default_library", "//pkg/util/wait:go_default_library",
......
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
clientcache "k8s.io/kubernetes/pkg/client/cache" clientcache "k8s.io/kubernetes/pkg/client/cache"
priorityutil "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/priorities/util" priorityutil "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/priorities/util"
) )
...@@ -55,9 +56,22 @@ type NodeInfo struct { ...@@ -55,9 +56,22 @@ type NodeInfo struct {
// Resource is a collection of compute resource. // Resource is a collection of compute resource.
type Resource struct { type Resource struct {
MilliCPU int64 MilliCPU int64
Memory int64 Memory int64
NvidiaGPU int64 NvidiaGPU int64
OpaqueIntResources map[api.ResourceName]int64
}
func (r *Resource) ResourceList() api.ResourceList {
result := api.ResourceList{
api.ResourceCPU: *resource.NewMilliQuantity(r.MilliCPU, resource.DecimalSI),
api.ResourceMemory: *resource.NewQuantity(r.Memory, resource.BinarySI),
api.ResourceNvidiaGPU: *resource.NewQuantity(r.NvidiaGPU, resource.DecimalSI),
}
for rName, rQuant := range r.OpaqueIntResources {
result[rName] = *resource.NewQuantity(rQuant, resource.DecimalSI)
}
return result
} }
// NewNodeInfo returns a ready to use empty NodeInfo object. // NewNodeInfo returns a ready to use empty NodeInfo object.
...@@ -169,10 +183,17 @@ func hasPodAffinityConstraints(pod *api.Pod) bool { ...@@ -169,10 +183,17 @@ func hasPodAffinityConstraints(pod *api.Pod) bool {
// addPod adds pod information to this NodeInfo. // addPod adds pod information to this NodeInfo.
func (n *NodeInfo) addPod(pod *api.Pod) { func (n *NodeInfo) addPod(pod *api.Pod) {
cpu, mem, nvidia_gpu, non0_cpu, non0_mem := calculateResource(pod) // cpu, mem, nvidia_gpu, non0_cpu, non0_mem := calculateResource(pod)
n.requestedResource.MilliCPU += cpu res, non0_cpu, non0_mem := calculateResource(pod)
n.requestedResource.Memory += mem n.requestedResource.MilliCPU += res.MilliCPU
n.requestedResource.NvidiaGPU += nvidia_gpu n.requestedResource.Memory += res.Memory
n.requestedResource.NvidiaGPU += res.NvidiaGPU
if n.requestedResource.OpaqueIntResources == nil && len(res.OpaqueIntResources) > 0 {
n.requestedResource.OpaqueIntResources = map[api.ResourceName]int64{}
}
for rName, rQuant := range res.OpaqueIntResources {
n.requestedResource.OpaqueIntResources[rName] += rQuant
}
n.nonzeroRequest.MilliCPU += non0_cpu n.nonzeroRequest.MilliCPU += non0_cpu
n.nonzeroRequest.Memory += non0_mem n.nonzeroRequest.Memory += non0_mem
n.pods = append(n.pods, pod) n.pods = append(n.pods, pod)
...@@ -213,10 +234,17 @@ func (n *NodeInfo) removePod(pod *api.Pod) error { ...@@ -213,10 +234,17 @@ func (n *NodeInfo) removePod(pod *api.Pod) error {
n.pods[i] = n.pods[len(n.pods)-1] n.pods[i] = n.pods[len(n.pods)-1]
n.pods = n.pods[:len(n.pods)-1] n.pods = n.pods[:len(n.pods)-1]
// reduce the resource data // reduce the resource data
cpu, mem, nvidia_gpu, non0_cpu, non0_mem := calculateResource(pod) res, non0_cpu, non0_mem := calculateResource(pod)
n.requestedResource.MilliCPU -= cpu
n.requestedResource.Memory -= mem n.requestedResource.MilliCPU -= res.MilliCPU
n.requestedResource.NvidiaGPU -= nvidia_gpu n.requestedResource.Memory -= res.Memory
n.requestedResource.NvidiaGPU -= res.NvidiaGPU
if len(res.OpaqueIntResources) > 0 && n.requestedResource.OpaqueIntResources == nil {
n.requestedResource.OpaqueIntResources = map[api.ResourceName]int64{}
}
for rName, rQuant := range res.OpaqueIntResources {
n.requestedResource.OpaqueIntResources[rName] -= rQuant
}
n.nonzeroRequest.MilliCPU -= non0_cpu n.nonzeroRequest.MilliCPU -= non0_cpu
n.nonzeroRequest.Memory -= non0_mem n.nonzeroRequest.Memory -= non0_mem
n.generation++ n.generation++
...@@ -226,17 +254,31 @@ func (n *NodeInfo) removePod(pod *api.Pod) error { ...@@ -226,17 +254,31 @@ func (n *NodeInfo) removePod(pod *api.Pod) error {
return fmt.Errorf("no corresponding pod %s in pods of node %s", pod.Name, n.node.Name) return fmt.Errorf("no corresponding pod %s in pods of node %s", pod.Name, n.node.Name)
} }
func calculateResource(pod *api.Pod) (cpu int64, mem int64, nvidia_gpu int64, non0_cpu int64, non0_mem int64) { func calculateResource(pod *api.Pod) (res Resource, non0_cpu int64, non0_mem int64) {
for _, c := range pod.Spec.Containers { for _, c := range pod.Spec.Containers {
req := c.Resources.Requests for rName, rQuant := range c.Resources.Requests {
cpu += req.Cpu().MilliValue() switch rName {
mem += req.Memory().Value() case api.ResourceCPU:
nvidia_gpu += req.NvidiaGPU().Value() res.MilliCPU += rQuant.MilliValue()
case api.ResourceMemory:
res.Memory += rQuant.Value()
case api.ResourceNvidiaGPU:
res.NvidiaGPU += rQuant.Value()
default:
if api.IsOpaqueIntResourceName(rName) {
// Lazily allocate opaque resource map.
if res.OpaqueIntResources == nil {
res.OpaqueIntResources = map[api.ResourceName]int64{}
}
res.OpaqueIntResources[rName] += rQuant.Value()
}
}
}
non0_cpu_req, non0_mem_req := priorityutil.GetNonzeroRequests(&req) non0_cpu_req, non0_mem_req := priorityutil.GetNonzeroRequests(&c.Resources.Requests)
non0_cpu += non0_cpu_req non0_cpu += non0_cpu_req
non0_mem += non0_mem_req non0_mem += non0_mem_req
// No non-zero resources for GPUs // No non-zero resources for GPUs or opaque resources.
} }
return return
} }
...@@ -244,10 +286,26 @@ func calculateResource(pod *api.Pod) (cpu int64, mem int64, nvidia_gpu int64, no ...@@ -244,10 +286,26 @@ func calculateResource(pod *api.Pod) (cpu int64, mem int64, nvidia_gpu int64, no
// Sets the overall node information. // Sets the overall node information.
func (n *NodeInfo) SetNode(node *api.Node) error { func (n *NodeInfo) SetNode(node *api.Node) error {
n.node = node n.node = node
n.allocatableResource.MilliCPU = node.Status.Allocatable.Cpu().MilliValue() for rName, rQuant := range node.Status.Allocatable {
n.allocatableResource.Memory = node.Status.Allocatable.Memory().Value() switch rName {
n.allocatableResource.NvidiaGPU = node.Status.Allocatable.NvidiaGPU().Value() case api.ResourceCPU:
n.allowedPodNumber = int(node.Status.Allocatable.Pods().Value()) n.allocatableResource.MilliCPU = rQuant.MilliValue()
case api.ResourceMemory:
n.allocatableResource.Memory = rQuant.Value()
case api.ResourceNvidiaGPU:
n.allocatableResource.NvidiaGPU = rQuant.Value()
case api.ResourcePods:
n.allowedPodNumber = int(rQuant.Value())
default:
if api.IsOpaqueIntResourceName(rName) {
// Lazily allocate opaque resource map.
if n.allocatableResource.OpaqueIntResources == nil {
n.allocatableResource.OpaqueIntResources = map[api.ResourceName]int64{}
}
n.allocatableResource.OpaqueIntResources[rName] = rQuant.Value()
}
}
}
n.generation++ n.generation++
return nil return nil
} }
......
...@@ -72,6 +72,7 @@ go_library( ...@@ -72,6 +72,7 @@ go_library(
"networking_perf.go", "networking_perf.go",
"node_problem_detector.go", "node_problem_detector.go",
"nodeoutofdisk.go", "nodeoutofdisk.go",
"opaque_resource.go",
"pd.go", "pd.go",
"persistent_volumes.go", "persistent_volumes.go",
"petset.go", "petset.go",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment