Skip to content

Commit 8b508dd

Browse files
Merge pull request #1405 from mrniranjan/ctrl_plane_schedule_testcases
OCPBUGS-62839: E2E: Add test cases related to schedulable control plane nodes
2 parents 899ff33 + 8822a92 commit 8b508dd

File tree

2 files changed

+131
-1
lines changed

2 files changed

+131
-1
lines changed

test/e2e/performanceprofile/functests/1_performance/cpu_management.go

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,14 @@ var _ = Describe("[rfe_id:27363][performance] CPU Management", Ordered, func() {
9393
ctx context.Context = context.Background()
9494
getter cgroup.ControllersGetter
9595
cgroupV2 bool
96+
workerRTNodes []corev1.Node
9697
)
9798

9899
testutils.CustomBeforeAll(func() {
99100
isSNO, err := cluster.IsSingleNode()
100101
Expect(err).ToNot(HaveOccurred())
101102
RunningOnSingleNode = isSNO
102-
workerRTNodes, err := nodes.GetByLabels(testutils.NodeSelectorLabels)
103+
workerRTNodes, err = nodes.GetByLabels(testutils.NodeSelectorLabels)
103104
Expect(err).ToNot(HaveOccurred())
104105
workerRTNodes, err = nodes.MatchingOptionalSelector(workerRTNodes)
105106
Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("error looking for the optional selector: %v", err))
@@ -1067,6 +1068,89 @@ var _ = Describe("[rfe_id:27363][performance] CPU Management", Ordered, func() {
10671068
})
10681069
})
10691070

1071+
Context("With Control plane schedule enabled", Label(string(label.CtrlPlaneSchedulable), string(label.OpenShift)), func() {
1072+
var (
1073+
profile *performancev2.PerformanceProfile
1074+
reservedCpus string
1075+
expectedCpuset cpuset.CPUSet
1076+
platformServices []string
1077+
)
1078+
1079+
BeforeAll(func() {
1080+
By("Checking if control plane is schedulable")
1081+
ok, err := cluster.IsControlPlaneSchedulable(context.TODO())
1082+
Expect(err).ToNot(HaveOccurred(), "Unable to check if control plane is schedulable")
1083+
if !ok {
1084+
Skip("Skipping tests: Control planes are not schedulable")
1085+
}
1086+
1087+
By("Fetching performance profile")
1088+
profile, err = profiles.GetByNodeLabels(testutils.NodeSelectorLabels)
1089+
Expect(err).ToNot(HaveOccurred(), "Unable to fetch profile")
1090+
Expect(profile.Spec.CPU.Reserved).ToNot(BeNil(), "Profile CPU Reserved field is nil")
1091+
reservedCpus = string(*profile.Spec.CPU.Reserved)
1092+
1093+
expectedCpuset, err = cpuset.Parse(reservedCpus)
1094+
Expect(err).ToNot(HaveOccurred(), "Unable to parse reserved CPU set %s", reservedCpus)
1095+
1096+
platformServices = []string{"systemd", "crio", "kubelet", "ovs-vswitchd"}
1097+
})
1098+
1099+
It("[test_id: 83851] verify platform services are restricted to reserved cpus", Label(string(label.Tier0)), func() {
1100+
By("Verifying platform services are restricted to reserved CPUs")
1101+
for _, service := range platformServices {
1102+
By(fmt.Sprintf("Checking CPU affinity for service: %s", service))
1103+
verifyServiceCPUAffinity(service, expectedCpuset, workerRTNodes)
1104+
}
1105+
})
1106+
1107+
It("[test_id: 83856] Verify cpu affinity of burstable pods are adjusted when guaranteed pods are created and removed on control plane node", Label(string(label.Tier0)), func() {
1108+
var guPod, buPod *corev1.Pod
1109+
1110+
By("Creating and starting guaranteed pod")
1111+
guPod = makePod(ctx, &workerRTNodes[0], true)
1112+
err = testclient.DataPlaneClient.Create(ctx, guPod)
1113+
Expect(err).ToNot(HaveOccurred(), "Unable to create guaranteed pod")
1114+
guPod, err = pods.WaitForCondition(ctx, client.ObjectKeyFromObject(guPod), corev1.PodReady, corev1.ConditionTrue, 10*time.Minute)
1115+
Expect(err).ToNot(HaveOccurred())
1116+
1117+
By("Getting cpuset configuration for guaranteed pod")
1118+
guaranteedPodCpuset, err := getPodCpusetConfiguration(ctx, guPod, getter)
1119+
Expect(err).ToNot(HaveOccurred())
1120+
1121+
By("Creating and starting burstable pod on the same node")
1122+
buPod = makePod(ctx, &workerRTNodes[0], false)
1123+
buPod.Spec.NodeSelector = map[string]string{testutils.LabelHostname: guPod.Spec.NodeName}
1124+
buPod.Spec.Containers[0].Resources = corev1.ResourceRequirements{
1125+
Limits: corev1.ResourceList{
1126+
corev1.ResourceMemory: resource.MustParse("200Mi"),
1127+
corev1.ResourceCPU: resource.MustParse("500m"),
1128+
},
1129+
}
1130+
err = testclient.DataPlaneClient.Create(ctx, buPod)
1131+
Expect(err).ToNot(HaveOccurred(), "Unable to create burstable pod")
1132+
buPod, err = pods.WaitForCondition(ctx, client.ObjectKeyFromObject(buPod), corev1.PodReady, corev1.ConditionTrue, 10*time.Minute)
1133+
Expect(err).ToNot(HaveOccurred())
1134+
1135+
By("Getting cpuset configuration for burstable pod")
1136+
burstablePodCpuset, err := getPodCpusetConfiguration(ctx, buPod, getter)
1137+
Expect(err).ToNot(HaveOccurred())
1138+
1139+
By("Verifying that guaranteed pod cpuset is not a subset of burstable pod cpuset")
1140+
Expect(guaranteedPodCpuset.IsSubsetOf(burstablePodCpuset)).ToNot(BeTrue())
1141+
1142+
defer func() {
1143+
if guPod != nil {
1144+
testlog.Infof("deleting pod %q", guPod.Name)
1145+
deleteTestPod(ctx, guPod)
1146+
}
1147+
if buPod != nil {
1148+
testlog.Infof("deleting pod %q", buPod.Name)
1149+
deleteTestPod(ctx, buPod)
1150+
}
1151+
}()
1152+
})
1153+
})
10701154
})
10711155

10721156
func extractConfigInfo(output string) (*ContainerConfig, error) {
@@ -1446,6 +1530,49 @@ func busyCpuImageEnv() string {
14461530
return fmt.Sprintf("%s%s", qeImageRegistry, busyCpusImage)
14471531
}
14481532

1533+
// getPodCpusetConfiguration gets the cpuset configuration for a pod
1534+
func getPodCpusetConfiguration(ctx context.Context, pod *corev1.Pod, getter cgroup.ControllersGetter) (cpuset.CPUSet, error) {
1535+
cpusetCfg := &controller.CpuSet{}
1536+
err := getter.Container(ctx, pod, pod.Spec.Containers[0].Name, cpusetCfg)
1537+
if err != nil {
1538+
return cpuset.CPUSet{}, err
1539+
}
1540+
return cpuset.Parse(cpusetCfg.Cpus)
1541+
}
1542+
1543+
// verifyServiceCPUAffinity verifies that a service is restricted to reserved CPUs
1544+
func verifyServiceCPUAffinity(service string, expectedCpuset cpuset.CPUSet, targetNodes []corev1.Node) {
1545+
for _, ctrlPlaneNode := range targetNodes {
1546+
By(fmt.Sprintf("Checking service %s on node %s", service, ctrlPlaneNode.Name))
1547+
1548+
// Get service PID
1549+
cmd := []string{"pidof", service}
1550+
pidInBytes, err := nodes.ExecCommand(context.TODO(), &ctrlPlaneNode, cmd)
1551+
Expect(err).ToNot(HaveOccurred(), "unable to fetch pid of service %s on node %s", service, ctrlPlaneNode.Name)
1552+
pid := strings.TrimSpace(string(pidInBytes))
1553+
Expect(pid).ToNot(BeEmpty(), "PID for service %s on node %s is empty", service, ctrlPlaneNode.Name)
1554+
1555+
// Get CPU affinity
1556+
tasksetCmd := []string{"taskset", "-pc", pid}
1557+
out, err := nodes.ExecCommand(context.TODO(), &ctrlPlaneNode, tasksetCmd)
1558+
Expect(err).ToNot(HaveOccurred(), "unable to get CPU affinity for service %s on node %s", service, ctrlPlaneNode.Name)
1559+
1560+
testlog.TaggedInfof("Info", "Affinity of %s service with pid %s on node %s is %s", service, pid, ctrlPlaneNode.Name, string(out))
1561+
1562+
// Parse CPU affinity
1563+
output := testutils.ToString(out)
1564+
tasksetOutput := strings.Split(strings.TrimSpace(output), ":")
1565+
cpus := strings.TrimSpace(tasksetOutput[1])
1566+
serviceCpuset, err := cpuset.Parse(cpus)
1567+
Expect(err).ToNot(HaveOccurred(), "unable to parse CPU set %s for service %s", cpus, service)
1568+
1569+
// Verify CPU affinity matches expected reserved CPUs
1570+
Expect(serviceCpuset.Equals(expectedCpuset)).To(BeTrue(),
1571+
"Service %s on node %s is not isolated to reserved cpus. Expected: %s, Got: %s",
1572+
service, ctrlPlaneNode.Name, expectedCpuset.String(), serviceCpuset.String())
1573+
}
1574+
}
1575+
14491576
// isPodReady checks if the pod is in ready state
14501577
func isPodReady(pod *corev1.Pod) bool {
14511578
for _, condition := range pod.Status.Conditions {

test/e2e/performanceprofile/functests/utils/label/label.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ const (
6767

6868
// UnCoreCache Feature should be added in tests that test Pod Affinity of Cpu's that share same Last level cache
6969
UnCoreCache Feature = "uncore-cache"
70+
71+
// ControlplaneSched features be added in tests that test when control plane schedulable is enabled
72+
CtrlPlaneSchedulable Feature = "controlplane-schedulable"
7073
)
7174

7275
// Tier is a label to classify tests under specific grade/level

0 commit comments

Comments
 (0)