Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"

machineconfigv1 "github.com/openshift/api/machineconfiguration/v1"
"github.com/openshift/cluster-node-tuning-operator/pkg/performanceprofile/controller/performanceprofile/components"
testutils "github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils"
testclient "github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/client"
hypershiftutils "github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/hypershift"
Expand Down Expand Up @@ -61,16 +62,18 @@ var _ = Describe("[rfe_id: 50649] Performance Addon Operator Must Gather", Label
if profile == nil {
Skip("No Performance Profile found")
}
//replace peformance.yaml for profile.Name when data is generated in the node
profileName := profile.Name
kubeletConfigName := components.GetComponentName(profileName, components.ComponentNamePrefix)
tunedName := components.GetComponentName(profileName, components.ProfileNamePerformance)
ClusterSpecificFiles := []string{
"cluster-scoped-resources/performance.openshift.io/performanceprofiles/performance.yaml",
"cluster-scoped-resources/machineconfiguration.openshift.io/kubeletconfigs/performance-performance.yaml",
"namespaces/openshift-cluster-node-tuning-operator/tuned.openshift.io/tuneds/openshift-node-performance-performance.yaml",
fmt.Sprintf("cluster-scoped-resources/performance.openshift.io/performanceprofiles/%s.yaml", profileName),
fmt.Sprintf("cluster-scoped-resources/machineconfiguration.openshift.io/kubeletconfigs/%s.yaml", kubeletConfigName),
fmt.Sprintf("namespaces/openshift-cluster-node-tuning-operator/tuned.openshift.io/tuneds/%s.yaml", tunedName),
}
// On a hypershift env, the tuned file name has an indentifier in the end
if hypershiftutils.IsHypershiftCluster() {
ClusterSpecificFiles = []string{
"namespaces/openshift-cluster-node-tuning-operator/tuned.openshift.io/tuneds/openshift-node-performance-performance-*.yaml",
fmt.Sprintf("namespaces/openshift-cluster-node-tuning-operator/tuned.openshift.io/tuneds/%s-*.yaml", tunedName),
}
}
By(fmt.Sprintf("Checking Folder: %q\n", mgContentFolder))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package __performance_kubelet_node_test
import (
"bytes"
"context"
"encoding/json"
"fmt"
"path/filepath"
"strconv"
Expand Down Expand Up @@ -33,9 +32,7 @@ import (
testlog "github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/log"
"github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/nodes"
"github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/pods"
"github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/poolname"
"github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/profiles"
"github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/profilesupdate"
"github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/systemd"
)

Expand All @@ -58,8 +55,7 @@ var _ = Describe("[performance] Cgroups and affinity", Ordered, Label(string(lab
isolatedCPUSet cpuset.CPUSet
workerRTNode *corev1.Node
workerRTNodes []corev1.Node
profile, initialProfile *performancev2.PerformanceProfile
poolName string
profile *performancev2.PerformanceProfile
ovsSliceCgroup string
ctx context.Context = context.Background()
ovsSystemdServices []string
Expand Down Expand Up @@ -89,8 +85,6 @@ var _ = Describe("[performance] Cgroups and affinity", Ordered, Label(string(lab
profile, err = profiles.GetByNodeLabels(testutils.NodeSelectorLabels)
Expect(err).ToNot(HaveOccurred())

poolName = poolname.GetByProfile(ctx, profile)

isCgroupV2, err = cgroup.IsVersion2(ctx, testclient.DataPlaneClient)
Expect(err).ToNot(HaveOccurred())

Expand Down Expand Up @@ -160,56 +154,19 @@ var _ = Describe("[performance] Cgroups and affinity", Ordered, Label(string(lab

})

Context("[Performance Profile Modified]", Label(string(label.Tier1)), func() {
BeforeEach(func() {
initialProfile = profile.DeepCopy()
})
Context("[Node Reboot]", Label(string(label.Tier1)), func() {
It("[test_id:64099] Activation file doesn't get deleted", func() {
policy := "best-effort"
// Need to make some changes to pp , causing system reboot
// and check if activation files is modified or deleted
profile, err := profiles.GetByNodeLabels(testutils.NodeSelectorLabels)
Expect(err).ToNot(HaveOccurred(), "Unable to fetch latest performance profile")
currentPolicy := profile.Spec.NUMA.TopologyPolicy
if *currentPolicy == "best-effort" {
policy = "restricted"
}
profile.Spec.NUMA = &performancev2.NUMA{
TopologyPolicy: &policy,
}
By("Updating the performance profile")
profiles.UpdateWithRetry(profile)

By(fmt.Sprintf("Applying changes in performance profile and waiting until %s will start updating", poolName))
profilesupdate.WaitForTuningUpdating(ctx, profile)

By(fmt.Sprintf("Waiting when %s finishes updates", poolName))
profilesupdate.WaitForTuningUpdated(ctx, profile)
By(fmt.Sprintf("Rebooting the worker node %q", workerRTNode.Name))
_, _ = nodes.ExecCommand(ctx, workerRTNode, []string{"sh", "-c", "chroot /rootfs systemctl reboot"})
nodes.WaitForNotReadyOrFail("Reboot", workerRTNode.Name, 10*time.Minute, 30*time.Second)
nodes.WaitForReadyOrFail("Reboot", workerRTNode.Name, 10*time.Minute, 30*time.Second)

By("Checking Activation file")
cmd := []string{"ls", activation_file}
for _, node := range workerRTNodes {
output, err := nodes.ExecCommand(context.TODO(), &node, cmd)
Expect(err).ToNot(HaveOccurred(), "file %s doesn't exist ", activation_file)
out := testutils.ToString(output)
Expect(out).To(Equal(activation_file))
}
})
AfterEach(func() {
By("Reverting the Profile")
profile, err := profiles.GetByNodeLabels(testutils.NodeSelectorLabels)
Expect(err).ToNot(HaveOccurred())
currentSpec, _ := json.Marshal(profile.Spec)
spec, _ := json.Marshal(initialProfile.Spec)
if !bytes.Equal(currentSpec, spec) {
profiles.UpdateWithRetry(initialProfile)

By(fmt.Sprintf("Applying changes in performance profile and waiting until %s will start updating", poolName))
profilesupdate.WaitForTuningUpdating(ctx, profile)

By(fmt.Sprintf("Waiting when %s finishes updates", poolName))
profilesupdate.WaitForTuningUpdated(ctx, profile)
}
output, err := nodes.ExecCommand(context.TODO(), workerRTNode, cmd)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Expect(err).ToNot(HaveOccurred(), "file %s doesn't exist", activation_file)
out := testutils.ToString(output)
Expect(out).To(Equal(activation_file))
})
})
})
Expand Down

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit signature already contains AIA, we don't need the:
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in latest commit

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package __performance_kubelet_node_test

import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
Expand All @@ -24,6 +23,7 @@ import (
testutils "github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils"
testclient "github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/client"
"github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/hypershift"
"github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/infrastructure"
"github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/label"
"github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/nodes"
"github.com/openshift/cluster-node-tuning-operator/test/e2e/performanceprofile/functests/utils/poolname"
Expand All @@ -41,14 +41,13 @@ var _ = Describe("[ref_id: 45487][performance]additional kubelet arguments", Ord
)

testutils.CustomBeforeAll(func() {
// TODO: The code here is broken as it masks workerRTNodes defined above
// and the value of workerRTNodes is never used. Keep the linter happy for now
// and remove the ineffectual assignment of workerRTNodes, err = nodes.MatchingOptionalSelector(workerRTNodes) below.
workerRTNodes, err := nodes.GetByLabels(testutils.NodeSelectorLabels)
var err error
workerRTNodes, err = nodes.GetByLabels(testutils.NodeSelectorLabels)
Expect(err).ToNot(HaveOccurred())

_, err = nodes.MatchingOptionalSelector(workerRTNodes)
workerRTNodes, err = nodes.MatchingOptionalSelector(workerRTNodes)
Expect(err).ToNot(HaveOccurred())
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Expect(workerRTNodes).ToNot(BeEmpty(), "no RT worker nodes found")

profile, err = profiles.GetByNodeLabels(testutils.NodeSelectorLabels)
Expect(err).ToNot(HaveOccurred())
Expand All @@ -58,6 +57,12 @@ var _ = Describe("[ref_id: 45487][performance]additional kubelet arguments", Ord

})
Context("Additional kubelet arguments", Label(string(label.Tier2)), func() {
BeforeEach(func() {
var err error
profile, err = profiles.GetByNodeLabels(testutils.NodeSelectorLabels)
Expect(err).ToNot(HaveOccurred())
})

It("[test_id:45488]Test performance profile annotation for changing multiple kubelet settings", func() {
sysctls := "{\"allowedUnsafeSysctls\":[\"net.core.somaxconn\",\"kernel.msg*\"],\"systemReserved\":{\"memory\":\"300Mi\"},\"kubeReserved\":{\"memory\":\"768Mi\"},\"imageMinimumGCAge\":\"3m\"}"
profile.Annotations = updateKubeletConfigOverrideAnnotations(profile.Annotations, sysctls)
Expand All @@ -72,24 +77,25 @@ var _ = Describe("[ref_id: 45487][performance]additional kubelet arguments", Ord
profilesupdate.WaitForTuningUpdated(ctx, profile)

for _, node := range workerRTNodes {
kubeletConfig, err := nodes.GetKubeletConfig(context.TODO(), &node)
kubeletConfig, err := nodes.GetKubeletConfig(ctx, &node)
Expect(err).ToNot(HaveOccurred())
sysctlsValue := kubeletConfig.AllowedUnsafeSysctls
Expect(sysctlsValue).Should(ContainElements("net.core.somaxconn", "kernel.msg*"))
Expect(kubeletConfig.KubeReserved["memory"]).To(Equal("768Mi"))
Expect(kubeletConfig.ImageMinimumGCAge.Seconds()).To(Equal(180))
Expect(kubeletConfig.ImageMinimumGCAge.Seconds()).To(BeNumerically("==", 180))
}
kubeletArguments := []string{"/bin/bash", "-c", "ps -ef | grep kubelet | grep config"}

autoSizingCmd := []string{"cat", "/rootfs/etc/openshift/kubelet.conf.d/20-auto-sizing.conf"}
for _, node := range workerRTNodes {
out, err := nodes.ExecCommand(context.TODO(), &node, kubeletArguments)
out, err := nodes.ExecCommand(ctx, &node, autoSizingCmd)
Expect(err).ToNot(HaveOccurred())
stdout := testutils.ToString(out)
Expect(strings.Contains(stdout, "300Mi")).To(BeTrue())
Expect(stdout).To(ContainSubstring("300Mi"))
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
})
Context("When setting cpu manager related parameters", func() {
It("[test_id:45493]Should not override performance-addon-operator values", func() {
paoValues := "{\"cpuManagerPolicy\":\"static\",\"cpuManagerReconcilePeriod\":\"5s\"}"
paoValues := "{\"cpuManagerPolicy\":\"none\",\"cpuManagerReconcilePeriod\":\"10s\"}"
profile.Annotations = updateKubeletConfigOverrideAnnotations(profile.Annotations, paoValues)

By("updating Performance profile")
Expand All @@ -102,10 +108,10 @@ var _ = Describe("[ref_id: 45487][performance]additional kubelet arguments", Ord
profilesupdate.WaitForTuningUpdated(ctx, profile)

for _, node := range workerRTNodes {
kubeletConfig, err := nodes.GetKubeletConfig(context.TODO(), &node)
kubeletConfig, err := nodes.GetKubeletConfig(ctx, &node)
Expect(err).ToNot(HaveOccurred())
Expect(kubeletConfig.CPUManagerPolicy).Should(Equal("static"))
Expect(kubeletConfig.CPUManagerReconcilePeriod.Seconds()).To(Equal(5))
Expect(kubeletConfig.CPUManagerReconcilePeriod.Seconds()).To(BeNumerically("==", 5))
}
})
})
Expand Down Expand Up @@ -133,7 +139,7 @@ var _ = Describe("[ref_id: 45487][performance]additional kubelet arguments", Ord
Name: components.GetComponentName(profile.Name, components.ComponentNamePrefix),
Namespace: metav1.NamespaceNone,
}
err := testclient.ControlPlaneClient.Get(context.TODO(), configKey, &kubeletConfig)
err := testclient.ControlPlaneClient.Get(ctx, configKey, &kubeletConfig)
if err != nil {
klog.Warningf("Failed to get the KubeletConfig %q", configKey.Name)
}
Expand All @@ -144,10 +150,19 @@ var _ = Describe("[ref_id: 45487][performance]additional kubelet arguments", Ord
Expect(kubeletConfigString).To(ContainSubstring(`"kubeReserved":{"memory":"768Mi"}`))
Expect(kubeletConfigString).To(ContainSubstring(`"systemReserved":{"memory":"300Mi"}`))

for _, node := range workerRTNodes {
kubeletConfig, err := nodes.GetKubeletConfig(context.TODO(), &node)
// Re-fetch nodes to get current allocatable and capacity after
// the tuning update, since workerRTNodes was populated before the
// annotation was applied and its Status values are stale.
updatedNodes, err := nodes.GetByLabels(testutils.NodeSelectorLabels)
Expect(err).ToNot(HaveOccurred())
updatedNodes, err = nodes.MatchingOptionalSelector(updatedNodes)
Expect(err).ToNot(HaveOccurred())
Expect(updatedNodes).ToNot(BeEmpty(), "no RT worker nodes found after update")

for _, node := range updatedNodes {
kubeletConfig, err := nodes.GetKubeletConfig(ctx, &node)
Expect(err).ToNot(HaveOccurred())
totalCapactity := node.Status.Capacity.Memory().MilliValue()
totalCapacity := node.Status.Capacity.Memory().MilliValue()
evictionMemory := kubeletConfig.EvictionHard["memory.available"]
kubeReserved := kubeletConfig.KubeReserved["memory"]
evictionMemoryInt, err := strconv.ParseInt(strings.TrimSuffix(evictionMemory, "Mi"), 10, 64)
Expand All @@ -158,12 +173,32 @@ var _ = Describe("[ref_id: 45487][performance]additional kubelet arguments", Ord
kubeReservedMemoryResource := resource.NewQuantity(kubeReservedMemoryInt*1024*1024, resource.BinarySI)
evictionMemoryResource := resource.NewQuantity(evictionMemoryInt*1024*1024, resource.BinarySI)
totalKubeMemory := systemReservedResource.MilliValue() + kubeReservedMemoryResource.MilliValue() + evictionMemoryResource.MilliValue()
calculatedAllocatable := totalCapactity - totalKubeMemory

// Pre-allocated hugepages are subtracted from allocatable memory by the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no way to reserve hugepages on kubelet:
kubernetes/kubernetes#140544

@mrniranjan mrniranjan Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not reserving hugepages through kubelet, Hugepages are being set using Kernel from the parameters defined in Performance profile.
The hugepages here are pre-allocated by the kernel (via the performance profile's hugepages spec, which sets kernel boot parameters like hugepagesz=2M hugepages=N).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it, it's just a way to tell the kernel to make those HP so other applications can consume those later.

// kubelet but are still included in node capacity. The standard formula
// Allocatable = Capacity - kubeReserved - systemReserved - evictionHard
// does not account for this, so we must subtract hugepages to match the
// actual allocatable reported by the node.
var totalHugepages int64
for resourceName, quantity := range node.Status.Capacity {
if strings.HasPrefix(string(resourceName), corev1.ResourceHugePagesPrefix) {
totalHugepages += quantity.MilliValue()
}
}

calculatedAllocatable := totalCapacity - totalKubeMemory - totalHugepages
currentAllocatable := node.Status.Allocatable.Memory().MilliValue()
Expect(calculatedAllocatable).To(Equal(currentAllocatable))
}
})

It("[test_id:45495] Test setting PAO managed parameters", func() {
isArm, err := infrastructure.IsARM(ctx, &workerRTNodes[0])
Expect(err).ToNot(HaveOccurred())
if isArm {
Skip("Changing topologyManagerPolicy is not supported on ARM architecture")
}

var paoParameters string
if *profile.Spec.NUMA.TopologyPolicy == "single-numa-node" {
paoParameters = "{\"topologyManagerPolicy\":\"restricted\"}"
Expand All @@ -186,7 +221,7 @@ var _ = Describe("[ref_id: 45487][performance]additional kubelet arguments", Ord
profilesupdate.WaitForTuningUpdated(ctx, profile)
}
for _, node := range workerRTNodes {
kubeletConfig, err := nodes.GetKubeletConfig(context.TODO(), &node)
kubeletConfig, err := nodes.GetKubeletConfig(ctx, &node)
Expect(err).ToNot(HaveOccurred())
Expect(kubeletConfig.TopologyManagerPolicy).To(Equal("single-numa-node"))
}
Expand All @@ -196,38 +231,35 @@ var _ = Describe("[ref_id: 45487][performance]additional kubelet arguments", Ord
By("Reverting the Profile")
profiles.UpdateWithRetry(initialProfile)

kubeletArguments := []string{"/bin/bash", "-c", "ps -ef | grep kubelet | grep config"}
By(fmt.Sprintf("Waiting for %s to be updated after revert", poolName))
profilesupdate.WaitForTuningUpdated(ctx, initialProfile)

for _, node := range workerRTNodes {
kubeletConfig, err := nodes.GetKubeletConfig(context.TODO(), &node)
kubeletConfig, err := nodes.GetKubeletConfig(ctx, &node)
Expect(err).ToNot(HaveOccurred())
Expect(kubeletConfig.AllowedUnsafeSysctls).To(Equal(nil))
Expect(kubeletConfig.AllowedUnsafeSysctls).To(BeEmpty())
Expect(kubeletConfig.KubeReserved["memory"]).ToNot(Equal("768Mi"))
Expect(kubeletConfig.ImageMinimumGCAge.Seconds()).ToNot(Equal(180))
}

autoSizingCmd := []string{"cat", "/rootfs/etc/openshift/kubelet.conf.d/20-auto-sizing.conf"}
for _, node := range workerRTNodes {
out, err := nodes.ExecCommand(context.TODO(), &node, kubeletArguments)
out, err := nodes.ExecCommand(ctx, &node, autoSizingCmd)
Expect(err).ToNot(HaveOccurred())
stdout := testutils.ToString(out)
Expect(strings.Contains(stdout, "300Mi")).To(BeTrue())
Expect(stdout).ToNot(ContainSubstring("300Mi"))
}

})
AfterAll(func() {
By("Reverting the Profile")
profile, err := profiles.GetByNodeLabels(testutils.NodeSelectorLabels)
Expect(err).ToNot(HaveOccurred())
currentSpec, _ := json.Marshal(profile.Spec)
spec, _ := json.Marshal(initialProfile.Spec)
// revert only if the profile changes.
if !equality.Semantic.DeepEqual(currentSpec, spec) {
if !equality.Semantic.DeepEqual(profile.Spec, initialProfile.Spec) || !equality.Semantic.DeepEqual(profile.Annotations, initialProfile.Annotations) {
profiles.UpdateWithRetry(initialProfile)

By(fmt.Sprintf("Applying changes in performance profile and waiting until %s will start updating", poolName))
profilesupdate.WaitForTuningUpdating(ctx, initialProfile)

By(fmt.Sprintf("Waiting when %s finishes updates", poolName))
By(fmt.Sprintf("Waiting for %s to be updated after revert", poolName))
profilesupdate.WaitForTuningUpdated(ctx, initialProfile)

}
})

Expand Down