Skip to content

Commit

Permalink
Merge pull request #202 from openshift-kni/resync-20240529-4.14
Browse files Browse the repository at this point in the history
[release-4.14] resync 20240529
  • Loading branch information
ffromani authored Jun 4, 2024
2 parents 30bdadc + 8acae4b commit 92c3bc7
Show file tree
Hide file tree
Showing 40 changed files with 2,092 additions and 398 deletions.
3 changes: 2 additions & 1 deletion RESYNC.log.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
| Resync Date | Merge With Upstream Tag/Commit | Author |
|-------------|------------------------------------------------------------------------------------------------------|-------------|
| 2024.02.08 | https://github.com/kubernetes-sigs/scheduler-plugins/commit/51aeff68f8ce110a5dd7386ec2cee2275c78a0c7 | ffromani | added cherry-picks on top
| 2024.02.08 | https://github.com/kubernetes-sigs/scheduler-plugins/commit/51aeff68f8ce110a5dd7386ec2cee2275c78a0c7 | ffromani | added cherry-picks on top: e9b8aa4a7513e92cc577dc1d9d7faaeaeb726b67
| 2024.02.08 | https://github.com/kubernetes-sigs/scheduler-plugins/commit/51aeff68f8ce110a5dd7386ec2cee2275c78a0c7 | ffromani | added cherry-picks on top: dfe8fdeaef37ff8014654688c2fec1a6cd676b60
| 2024.01.23 | https://github.com/kubernetes-sigs/scheduler-plugins/commit/51aeff68f8ce110a5dd7386ec2cee2275c78a0c7 | ffromani |
| 2023.06.01 | https://github.com/kubernetes-sigs/scheduler-plugins/commit/cbf2979725a3be327c1a18a74375e06498bd3419 | ffromani |
| 2023.05.19 | https://github.com/kubernetes-sigs/scheduler-plugins/commit/0ab88ad4a346d4e8682240c1f3817816a4298f40 | ffromani |
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ go 1.20
require (
github.com/diktyo-io/appgroup-api v1.0.1-alpha
github.com/diktyo-io/networktopology-api v1.0.1-alpha
github.com/dustin/go-humanize v1.0.0
github.com/go-logr/logr v1.2.4
github.com/google/go-cmp v0.5.9
github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.1
github.com/dustin/go-humanize v1.0.1
github.com/go-logr/logr v1.3.0
github.com/google/go-cmp v0.6.0
github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.2
github.com/k8stopologyawareschedwg/podfingerprint v0.2.2
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/paypal/load-watcher v0.2.3
Expand Down
14 changes: 8 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,9 @@ github.com/diktyo-io/networktopology-api v1.0.1-alpha/go.mod h1:a9YAoBY96ITcSMUT
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
Expand Down Expand Up @@ -156,8 +157,8 @@ github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
Expand Down Expand Up @@ -229,8 +230,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
Expand Down Expand Up @@ -294,8 +296,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.1 h1:BI3L7hNqRvXtB42FO4NI/0ZjDDVRPOMBDFLShhFtf28=
github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.1/go.mod h1:AkACMQGiTgCt0lQw3m7TTU8PLH9lYKNK5e9DqFf5VuM=
github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.2 h1:uAwqOtyrFYggq3pVf3hs1XKkBxrQ8dkgjWz3LCLJsiY=
github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.2/go.mod h1:LBzS4n6GX1C69tzSd5EibZ9cGOXFuHP7GxEMDYVe1sM=
github.com/k8stopologyawareschedwg/podfingerprint v0.2.2 h1:iFHPfZInM9pz2neye5RdmORMp1hPmte1EGJYpOOzZVg=
github.com/k8stopologyawareschedwg/podfingerprint v0.2.2/go.mod h1:C23pM15t06dXg/OihGlqBvnYzLr+MXDXJ7zMfbNAyXI=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
Expand Down
54 changes: 9 additions & 45 deletions pkg/noderesourcetopology/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ import (
"fmt"

v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/klog/v2"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/klog/v2/klogr"
v1qos "k8s.io/kubernetes/pkg/apis/core/v1/helper/qos"
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
bm "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
Expand Down Expand Up @@ -79,7 +78,13 @@ func singleNUMAContainerLevelHandler(pod *v1.Pod, zones topologyv1alpha2.ZoneLis

// subtract the resources requested by the container from the given NUMA.
// this is necessary, so we won't allocate the same resources for the upcoming containers
subtractFromNUMA(nodes, numaID, container)
clh := klogr.New().WithValues("logID", logID, "node", nodeInfo.Node().Name)
err := subtractResourcesFromNUMANodeList(clh, nodes, numaID, qos, container.Resources.Requests, logID)
if err != nil {
// this is an internal error which should never happen
return framework.NewStatus(framework.Error, "inconsistent resource accounting", err.Error())
}
klog.V(4).InfoS("container aligned", "numaCell", numaID)
}
return nil
}
Expand Down Expand Up @@ -133,7 +138,7 @@ func resourcesAvailableInAnyNUMANodes(logID string, numaNodes NUMANodeList, reso

// non-native resources or ephemeral-storage may not expose NUMA affinity,
// but since they are available at node level, this is fine
if !hasNUMAAffinity && (!v1helper.IsNativeResource(resource) || resource == v1.ResourceEphemeralStorage) {
if !hasNUMAAffinity && isHostLevelResource(resource) {
klog.V(6).InfoS("resource available at node level (no NUMA affinity)", "logID", logID, "node", nodeName, "resource", resource)
continue
}
Expand All @@ -156,25 +161,6 @@ func resourcesAvailableInAnyNUMANodes(logID string, numaNodes NUMANodeList, reso
return numaID, ret
}

func isResourceSetSuitable(qos v1.PodQOSClass, resource v1.ResourceName, quantity, numaQuantity resource.Quantity) bool {
// Check for the following:
if qos != v1.PodQOSGuaranteed {
// 1. set numa node as possible node if resource is memory or Hugepages
if resource == v1.ResourceMemory {
return true
}
if v1helper.IsHugePageResourceName(resource) {
return true
}
// 2. set numa node as possible node if resource is CPU
if resource == v1.ResourceCPU {
return true
}
}
// 3. otherwise check amount of resources
return numaQuantity.Cmp(quantity) >= 0
}

func singleNUMAPodLevelHandler(pod *v1.Pod, zones topologyv1alpha2.ZoneList, nodeInfo *framework.NodeInfo) *framework.Status {
klog.V(5).InfoS("Pod Level Resource handler")

Expand Down Expand Up @@ -226,28 +212,6 @@ func (tm *TopologyMatch) Filter(ctx context.Context, cycleState *framework.Cycle
return status
}

// subtractFromNUMA finds the correct NUMA ID's resources and subtract them from `nodes`.
func subtractFromNUMA(nodes NUMANodeList, numaID int, container v1.Container) {
for i := 0; i < len(nodes); i++ {
if nodes[i].NUMAID != numaID {
continue
}

nRes := nodes[i].Resources
for resName, quan := range container.Resources.Requests {
nodeResQuan := nRes[resName]
nodeResQuan.Sub(quan)
// we do not expect a negative value here, since this function only called
// when resourcesAvailableInAnyNUMANodes function is passed
// but let's log here if such unlikely case will occur
if nodeResQuan.Sign() == -1 {
klog.V(4).InfoS("resource quantity should not be a negative value", "resource", resName, "quantity", nodeResQuan.String())
}
nRes[resName] = nodeResQuan
}
}
}

func filterHandlerFromTopologyManagerConfig(conf TopologyManagerConfig) filterFn {
if conf.Policy != kubeletconfig.SingleNumaNodeTopologyManagerPolicy {
return nil
Expand Down
180 changes: 180 additions & 0 deletions pkg/noderesourcetopology/numaresources.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
Copyright 2021 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package noderesourcetopology

import (
"fmt"
"reflect"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
"sigs.k8s.io/scheduler-plugins/pkg/noderesourcetopology/stringify"
)

type NUMANode struct {
NUMAID int
Resources corev1.ResourceList
Costs map[int]int
}

func (n *NUMANode) WithCosts(costs map[int]int) *NUMANode {
n.Costs = costs
return n
}

func (n NUMANode) DeepCopy() NUMANode {
ret := NUMANode{
NUMAID: n.NUMAID,
Resources: n.Resources.DeepCopy(),
}
if len(n.Costs) > 0 {
ret.Costs = make(map[int]int)
for key, val := range n.Costs {
ret.Costs[key] = val
}
}
return ret
}

func (n NUMANode) Equal(o NUMANode) bool {
if n.NUMAID != o.NUMAID {
return false
}
if !reflect.DeepEqual(n.Costs, o.Costs) {
return false
}
return equalResourceList(n.Resources, o.Resources)
}

func equalResourceList(ra, rb corev1.ResourceList) bool {
if len(ra) != len(rb) {
return false
}
for key, valA := range ra {
valB, ok := rb[key]
if !ok {
return false
}
if !valA.Equal(valB) {
return false
}
}
return true
}

type NUMANodeList []NUMANode

func (nnl NUMANodeList) DeepCopy() NUMANodeList {
ret := make(NUMANodeList, 0, len(nnl))
for idx := 0; idx < len(nnl); idx++ {
ret = append(ret, nnl[idx].DeepCopy())
}
return ret
}

func (nnl NUMANodeList) Equal(oth NUMANodeList) bool {
if len(nnl) != len(oth) {
return false
}
for idx := 0; idx < len(nnl); idx++ {
if !nnl[idx].Equal(oth[idx]) {
return false
}
}
return true
}

func isHostLevelResource(resource corev1.ResourceName) bool {
// host-level resources are resources which *may* not be bound to NUMA nodes.
// A Key example is generic [ephemeral] storage which doesn't expose NUMA affinity.
if resource == corev1.ResourceEphemeralStorage {
return true
}
if resource == corev1.ResourceStorage {
return true
}
if !v1helper.IsNativeResource(resource) {
return true
}
return false
}

func isNUMAAffineResource(resource corev1.ResourceName) bool {
// NUMA-affine resources are resources which are required to be bound to NUMA nodes.
// A Key example is CPU and memory, which must expose NUMA affinity.
if resource == corev1.ResourceCPU {
return true
}
if resource == corev1.ResourceMemory {
return true
}
if v1helper.IsHugePageResourceName(resource) {
return true
}
// Devices are *expected* to expose NUMA Affinity, but they are not *required* to do so.
// We can't tell for sure, so we default to "no".
return false
}

func isResourceSetSuitable(qos corev1.PodQOSClass, resource corev1.ResourceName, quantity, numaQuantity resource.Quantity) bool {
if qos != corev1.PodQOSGuaranteed && isNUMAAffineResource(resource) {
return true
}
return numaQuantity.Cmp(quantity) >= 0
}

// subtractResourcesFromNUMANodeList finds the correct NUMA ID's resources and always subtract them from `nodes` in-place.
func subtractResourcesFromNUMANodeList(lh logr.Logger, nodes NUMANodeList, numaID int, qos corev1.PodQOSClass, containerRes corev1.ResourceList, logID string) error {
logEntries := []any{"numaCell", numaID}

for _, node := range nodes {
if node.NUMAID != numaID {
continue
}

lh.V(5).Info("NUMA resources before", append(logEntries, stringify.ResourceListToLoggable(logID, node.Resources)...)...)

for resName, resQty := range containerRes {
isAffine := isNUMAAffineResource(resName)
if qos != corev1.PodQOSGuaranteed && isAffine {
lh.V(4).Info("ignoring QoS-depending exclusive request", "resource", resName, "QoS", qos)
continue
}
if resQty.IsZero() {
lh.V(4).Info("ignoring zero-valued request", "resource", resName)
continue
}
nResQ, ok := node.Resources[resName]
if !ok {
lh.V(4).Info("ignoring missing resource", "resource", resName, "affine", isAffine, "request", resQty.String())
continue
}
nodeResQty := nResQ.DeepCopy()
nodeResQty.Sub(resQty)
if nodeResQty.Sign() < 0 {
lh.V(1).Info("resource quantity should not be a negative value", "numaCell", numaID, "resource", resName, "quantity", nResQ.String(), "request", resQty.String())
return fmt.Errorf("resource %q request %s exceeds NUMA %d availability %s", string(resName), resQty.String(), numaID, nResQ.String())
}
node.Resources[resName] = nodeResQty
}

lh.V(5).Info("NUMA resources after", append(logEntries, stringify.ResourceListToLoggable(logID, node.Resources)...)...)
}
return nil
}
Loading

0 comments on commit 92c3bc7

Please sign in to comment.