@@ -751,8 +751,148 @@ var _ = Describe("[rfe_id:27363][performance] CPU Management", Ordered, func() {
751
751
})
752
752
})
753
753
754
+ Context ("Runc cpus isolation" , func () {
755
+ var guaranteedPod , secondPod * corev1.Pod
756
+
757
+ AfterEach (func () {
758
+ cmd := []string {"rm" , "-f" , "/rootfs/var/roothome/create" }
759
+ nodes .ExecCommand (ctx , workerRTNode , cmd )
760
+ deletePod (ctx , guaranteedPod )
761
+ deletePod (ctx , secondPod )
762
+ })
763
+
764
+ It ("Verify that runc excludes the cpus used by guaranteed pod" , func () {
765
+ guaranteedPod = getGuaranteedPod (ctx , workerRTNode )
766
+ secondPod = getNonGuaranteedPod (ctx , workerRTNode )
767
+ containerIDs := make ([]string , 2 )
768
+
769
+ var err error
770
+ containerIDs [0 ], err = pods .GetContainerIDByName (guaranteedPod , "test" )
771
+ Expect (err ).ToNot (HaveOccurred ())
772
+ containerIDs [1 ], err = pods .GetContainerIDByName (secondPod , "test" )
773
+ Expect (err ).ToNot (HaveOccurred ())
774
+ for _ , containerID := range containerIDs {
775
+ path := fmt .Sprintf ("/rootfs/var/run/containers/storage/overlay-containers/%s/userdata/config.json" , containerID )
776
+ cmd := []string {"/bin/bash" , "-c" , fmt .Sprintf ("cat %s >> /rootfs/var/roothome/create" , path )}
777
+ nodes .ExecCommand (ctx , workerRTNode , cmd )
778
+ }
779
+ cmd := []string {"cat" , "/rootfs/var/roothome/create" }
780
+ output , err := nodes .ExecCommand (context .TODO (), workerRTNode , cmd )
781
+ Expect (err ).ToNot (HaveOccurred ())
782
+
783
+ out := testutils .ToString (output )
784
+ hostnameRe := regexp .MustCompile (`"hostname":\s+"([^"]+)"` )
785
+ cpusRe := regexp .MustCompile (`"cpus":\s+"([^"]+)"` )
786
+
787
+ hostnameMatches := hostnameRe .FindAllStringSubmatch (out , - 1 )
788
+ cpusMatches := cpusRe .FindAllStringSubmatch (out , - 1 )
789
+ if len (hostnameMatches ) == 0 || len (cpusMatches ) == 0 {
790
+ Fail ("Failed to extract hostname or cpus information" )
791
+ }
792
+ uniqueCombinations := make (map [string ]struct {})
793
+ zippedMatches := make ([]map [string ]string , 0 )
794
+ for i := 0 ; i < len (hostnameMatches ) && i < len (cpusMatches ); i ++ {
795
+ combination := fmt .Sprintf ("%s-%s" , hostnameMatches [i ][1 ], cpusMatches [i ][1 ])
796
+ if _ , exists := uniqueCombinations [combination ]; ! exists {
797
+ uniqueCombinations [combination ] = struct {}{}
798
+ zippedMatches = append (zippedMatches , map [string ]string {
799
+ "hostname" : hostnameMatches [i ][1 ],
800
+ "cpus" : cpusMatches [i ][1 ],
801
+ })
802
+ }
803
+ }
804
+
805
+ parseCPUs := func (cpuStr string ) []int {
806
+ var cpus []int
807
+ for _ , part := range strings .Split (cpuStr , "," ) {
808
+ if strings .Contains (part , "-" ) {
809
+ bounds := strings .Split (part , "-" )
810
+ min , _ := strconv .Atoi (bounds [0 ])
811
+ max , _ := strconv .Atoi (bounds [1 ])
812
+ for i := min ; i <= max ; i ++ {
813
+ cpus = append (cpus , i )
814
+ }
815
+ } else {
816
+ val , _ := strconv .Atoi (part )
817
+ cpus = append (cpus , val )
818
+ }
819
+ }
820
+ return cpus
821
+ }
822
+
823
+ guaranteedPodCpus := parseCPUs (zippedMatches [0 ]["cpus" ])
824
+ runcCpus := parseCPUs (zippedMatches [1 ]["cpus" ])
825
+ overlapFound := false
826
+ for _ , guaranteedCpu := range guaranteedPodCpus {
827
+ for _ , runcCpu := range runcCpus {
828
+ if guaranteedCpu == runcCpu {
829
+ overlapFound = true
830
+ break
831
+ }
832
+ }
833
+ if overlapFound {
834
+ break
835
+ }
836
+ }
837
+ Expect (overlapFound ).ToNot (BeTrue (), "Overlap of cpus found, not expected behaviour" )
838
+ })
839
+ })
840
+
754
841
})
755
842
843
+ func getGuaranteedPod (ctx context.Context , workerRTNode * corev1.Node ) * corev1.Pod {
844
+ var err error
845
+ testpod1 := pods .GetTestPod ()
846
+ testpod1 .Namespace = testutils .NamespaceTesting
847
+ testpod1 .Spec .Containers [0 ].Resources = corev1.ResourceRequirements {
848
+ Limits : corev1.ResourceList {
849
+ corev1 .ResourceCPU : resource .MustParse ("2" ),
850
+ corev1 .ResourceMemory : resource .MustParse ("200Mi" ),
851
+ },
852
+ }
853
+ testpod1 .Spec .NodeSelector = map [string ]string {testutils .LabelHostname : workerRTNode .Name }
854
+
855
+ profile , _ := profiles .GetByNodeLabels (testutils .NodeSelectorLabels )
856
+ runtimeClass := components .GetComponentName (profile .Name , components .ComponentNamePrefix )
857
+ testpod1 .Spec .RuntimeClassName = & runtimeClass
858
+
859
+ err = testclient .Client .Create (ctx , testpod1 )
860
+ Expect (err ).ToNot (HaveOccurred ())
861
+ _ , err = pods .WaitForCondition (ctx , client .ObjectKeyFromObject (testpod1 ), corev1 .PodReady , corev1 .ConditionTrue , 5 * time .Minute )
862
+ Expect (err ).ToNot (HaveOccurred ())
863
+ Expect (testpod1 .Status .QOSClass ).To (Equal (corev1 .PodQOSGuaranteed ))
864
+ return testpod1
865
+ }
866
+
867
+ func getNonGuaranteedPod (ctx context.Context , workerRTNode * corev1.Node ) * corev1.Pod {
868
+ var err error
869
+ testpod2 := pods .GetTestPod ()
870
+ testpod2 .Namespace = testutils .NamespaceTesting
871
+ testpod2 .Spec .NodeSelector = map [string ]string {testutils .LabelHostname : workerRTNode .Name }
872
+
873
+ profile , _ := profiles .GetByNodeLabels (testutils .NodeSelectorLabels )
874
+ runtimeClass := components .GetComponentName (profile .Name , components .ComponentNamePrefix )
875
+ testpod2 .Spec .RuntimeClassName = & runtimeClass
876
+
877
+ err = testclient .Client .Create (ctx , testpod2 )
878
+ Expect (err ).ToNot (HaveOccurred ())
879
+ _ , err = pods .WaitForCondition (ctx , client .ObjectKeyFromObject (testpod2 ), corev1 .PodReady , corev1 .ConditionTrue , 5 * time .Minute )
880
+ Expect (err ).ToNot (HaveOccurred ())
881
+ return testpod2
882
+ }
883
+
884
+ func deletePod (ctx context.Context , pod * corev1.Pod ) {
885
+ err := testclient .Client .Delete (ctx , pod )
886
+ if err != nil {
887
+ testlog .Errorf ("Failed to delete Pod %s/%s: %v" , pod .Namespace , pod .Name , err )
888
+ }
889
+
890
+ err = pods .WaitForDeletion (ctx , pod , pods .DefaultDeletionTimeout * time .Second )
891
+ if err != nil {
892
+ testlog .Errorf ("Timed out waiting for deletion of Pod %s/%s: %v" , pod .Namespace , pod .Name , err )
893
+ }
894
+ }
895
+
756
896
func checkForWorkloadPartitioning (ctx context.Context ) bool {
757
897
// Look for the correct Workload Partition annotation in
758
898
// a crio configuration file on the target node
@@ -1064,3 +1204,4 @@ func busyCpuImageEnv() string {
1064
1204
}
1065
1205
return fmt .Sprintf ("%s%s" , qeImageRegistry , busyCpusImage )
1066
1206
}
1207
+
0 commit comments