cpu_manager_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package e2enode
  14. import (
  15. "fmt"
  16. "os/exec"
  17. "strconv"
  18. "strings"
  19. "time"
  20. v1 "k8s.io/api/core/v1"
  21. "k8s.io/apimachinery/pkg/api/resource"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
  24. kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
  25. "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager"
  26. cpumanagerstate "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state"
  27. "k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
  28. "k8s.io/kubernetes/pkg/kubelet/types"
  29. "k8s.io/kubernetes/test/e2e/framework"
  30. e2enode "k8s.io/kubernetes/test/e2e/framework/node"
  31. e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
  32. "github.com/onsi/ginkgo"
  33. "github.com/onsi/gomega"
  34. )
  35. // Helper for makeCPUManagerPod().
  36. type ctnAttribute struct {
  37. ctnName string
  38. cpuRequest string
  39. cpuLimit string
  40. }
  41. // makeCPUMangerPod returns a pod with the provided ctnAttributes.
  42. func makeCPUManagerPod(podName string, ctnAttributes []ctnAttribute) *v1.Pod {
  43. var containers []v1.Container
  44. for _, ctnAttr := range ctnAttributes {
  45. cpusetCmd := fmt.Sprintf("grep Cpus_allowed_list /proc/self/status | cut -f2 && sleep 1d")
  46. ctn := v1.Container{
  47. Name: ctnAttr.ctnName,
  48. Image: busyboxImage,
  49. Resources: v1.ResourceRequirements{
  50. Requests: v1.ResourceList{
  51. v1.ResourceName(v1.ResourceCPU): resource.MustParse(ctnAttr.cpuRequest),
  52. v1.ResourceName(v1.ResourceMemory): resource.MustParse("100Mi"),
  53. },
  54. Limits: v1.ResourceList{
  55. v1.ResourceName(v1.ResourceCPU): resource.MustParse(ctnAttr.cpuLimit),
  56. v1.ResourceName(v1.ResourceMemory): resource.MustParse("100Mi"),
  57. },
  58. },
  59. Command: []string{"sh", "-c", cpusetCmd},
  60. }
  61. containers = append(containers, ctn)
  62. }
  63. return &v1.Pod{
  64. ObjectMeta: metav1.ObjectMeta{
  65. Name: podName,
  66. },
  67. Spec: v1.PodSpec{
  68. RestartPolicy: v1.RestartPolicyNever,
  69. Containers: containers,
  70. },
  71. }
  72. }
  73. func deletePods(f *framework.Framework, podNames []string) {
  74. for _, podName := range podNames {
  75. gp := int64(0)
  76. delOpts := metav1.DeleteOptions{
  77. GracePeriodSeconds: &gp,
  78. }
  79. f.PodClient().DeleteSync(podName, &delOpts, framework.DefaultPodDeletionTimeout)
  80. }
  81. }
  82. func getLocalNodeCPUDetails(f *framework.Framework) (cpuCapVal int64, cpuAllocVal int64, cpuResVal int64) {
  83. localNodeCap := getLocalNode(f).Status.Capacity
  84. cpuCap := localNodeCap[v1.ResourceCPU]
  85. localNodeAlloc := getLocalNode(f).Status.Allocatable
  86. cpuAlloc := localNodeAlloc[v1.ResourceCPU]
  87. cpuRes := cpuCap.DeepCopy()
  88. cpuRes.Sub(cpuAlloc)
  89. // RoundUp reserved CPUs to get only integer cores.
  90. cpuRes.RoundUp(0)
  91. return cpuCap.Value(), (cpuCap.Value() - cpuRes.Value()), cpuRes.Value()
  92. }
  93. func waitForContainerRemoval(containerName, podName, podNS string) {
  94. rs, _, err := getCRIClient()
  95. framework.ExpectNoError(err)
  96. gomega.Eventually(func() bool {
  97. containers, err := rs.ListContainers(&runtimeapi.ContainerFilter{
  98. LabelSelector: map[string]string{
  99. types.KubernetesPodNameLabel: podName,
  100. types.KubernetesPodNamespaceLabel: podNS,
  101. types.KubernetesContainerNameLabel: containerName,
  102. },
  103. })
  104. if err != nil {
  105. return false
  106. }
  107. return len(containers) == 0
  108. }, 2*time.Minute, 1*time.Second).Should(gomega.BeTrue())
  109. }
  110. func waitForStateFileCleanedUp() {
  111. gomega.Eventually(func() bool {
  112. restoredState, err := cpumanagerstate.NewCheckpointState("/var/lib/kubelet", "cpu_manager_state", "static", nil)
  113. framework.ExpectNoError(err, "failed to create testing cpumanager state instance")
  114. assignments := restoredState.GetCPUAssignments()
  115. if len(assignments) == 0 {
  116. return true
  117. }
  118. return false
  119. }, 2*time.Minute, 1*time.Second).Should(gomega.BeTrue())
  120. }
  121. func isHTEnabled() bool {
  122. outData, err := exec.Command("/bin/sh", "-c", "lscpu | grep \"Thread(s) per core:\" | cut -d \":\" -f 2").Output()
  123. framework.ExpectNoError(err)
  124. threadsPerCore, err := strconv.Atoi(strings.TrimSpace(string(outData)))
  125. framework.ExpectNoError(err)
  126. return threadsPerCore > 1
  127. }
  128. func isMultiNUMA() bool {
  129. outData, err := exec.Command("/bin/sh", "-c", "lscpu | grep \"NUMA node(s):\" | cut -d \":\" -f 2").Output()
  130. framework.ExpectNoError(err)
  131. numaNodes, err := strconv.Atoi(strings.TrimSpace(string(outData)))
  132. framework.ExpectNoError(err)
  133. return numaNodes > 1
  134. }
  135. func getCPUSiblingList(cpuRes int64) string {
  136. out, err := exec.Command("/bin/sh", "-c", fmt.Sprintf("cat /sys/devices/system/cpu/cpu%d/topology/thread_siblings_list | tr -d \"\n\r\"", cpuRes)).Output()
  137. framework.ExpectNoError(err)
  138. return string(out)
  139. }
  140. func getCoreSiblingList(cpuRes int64) string {
  141. out, err := exec.Command("/bin/sh", "-c", fmt.Sprintf("cat /sys/devices/system/cpu/cpu%d/topology/core_siblings_list | tr -d \"\n\r\"", cpuRes)).Output()
  142. framework.ExpectNoError(err)
  143. return string(out)
  144. }
  145. func deleteStateFile() {
  146. err := exec.Command("/bin/sh", "-c", "rm -f /var/lib/kubelet/cpu_manager_state").Run()
  147. framework.ExpectNoError(err, "error deleting state file")
  148. }
  149. func setOldKubeletConfig(f *framework.Framework, oldCfg *kubeletconfig.KubeletConfiguration) {
  150. // Delete the CPU Manager state file so that the old Kubelet configuration
  151. // can take effect.i
  152. deleteStateFile()
  153. if oldCfg != nil {
  154. framework.ExpectNoError(setKubeletConfiguration(f, oldCfg))
  155. }
  156. }
  157. func disableCPUManagerInKubelet(f *framework.Framework) (oldCfg *kubeletconfig.KubeletConfiguration) {
  158. // Disable CPU Manager in Kubelet.
  159. oldCfg, err := getCurrentKubeletConfig()
  160. framework.ExpectNoError(err)
  161. newCfg := oldCfg.DeepCopy()
  162. if newCfg.FeatureGates == nil {
  163. newCfg.FeatureGates = make(map[string]bool)
  164. }
  165. newCfg.FeatureGates["CPUManager"] = false
  166. // Update the Kubelet configuration.
  167. framework.ExpectNoError(setKubeletConfiguration(f, newCfg))
  168. // Wait for the Kubelet to be ready.
  169. gomega.Eventually(func() bool {
  170. nodes, err := e2enode.TotalReady(f.ClientSet)
  171. framework.ExpectNoError(err)
  172. return nodes == 1
  173. }, time.Minute, time.Second).Should(gomega.BeTrue())
  174. return oldCfg
  175. }
  176. func enableCPUManagerInKubelet(f *framework.Framework, cleanStateFile bool) (oldCfg *kubeletconfig.KubeletConfiguration) {
  177. // Enable CPU Manager in Kubelet with static policy.
  178. oldCfg, err := getCurrentKubeletConfig()
  179. framework.ExpectNoError(err)
  180. newCfg := oldCfg.DeepCopy()
  181. if newCfg.FeatureGates == nil {
  182. newCfg.FeatureGates = make(map[string]bool)
  183. } else {
  184. newCfg.FeatureGates["CPUManager"] = true
  185. }
  186. // After graduation of the CPU Manager feature to Beta, the CPU Manager
  187. // "none" policy is ON by default. But when we set the CPU Manager policy to
  188. // "static" in this test and the Kubelet is restarted so that "static"
  189. // policy can take effect, there will always be a conflict with the state
  190. // checkpointed in the disk (i.e., the policy checkpointed in the disk will
  191. // be "none" whereas we are trying to restart Kubelet with "static"
  192. // policy). Therefore, we delete the state file so that we can proceed
  193. // with the tests.
  194. // Only delete the state file at the begin of the tests.
  195. if cleanStateFile {
  196. deleteStateFile()
  197. }
  198. // Set the CPU Manager policy to static.
  199. newCfg.CPUManagerPolicy = string(cpumanager.PolicyStatic)
  200. // Set the CPU Manager reconcile period to 1 second.
  201. newCfg.CPUManagerReconcilePeriod = metav1.Duration{Duration: 1 * time.Second}
  202. // The Kubelet panics if either kube-reserved or system-reserved is not set
  203. // when CPU Manager is enabled. Set cpu in kube-reserved > 0 so that
  204. // kubelet doesn't panic.
  205. if newCfg.KubeReserved == nil {
  206. newCfg.KubeReserved = map[string]string{}
  207. }
  208. if _, ok := newCfg.KubeReserved["cpu"]; !ok {
  209. newCfg.KubeReserved["cpu"] = "200m"
  210. }
  211. // Update the Kubelet configuration.
  212. framework.ExpectNoError(setKubeletConfiguration(f, newCfg))
  213. // Wait for the Kubelet to be ready.
  214. gomega.Eventually(func() bool {
  215. nodes, err := e2enode.TotalReady(f.ClientSet)
  216. framework.ExpectNoError(err)
  217. return nodes == 1
  218. }, time.Minute, time.Second).Should(gomega.BeTrue())
  219. return oldCfg
  220. }
  221. func runCPUManagerTests(f *framework.Framework) {
  222. var cpuCap, cpuAlloc int64
  223. var oldCfg *kubeletconfig.KubeletConfiguration
  224. var cpuListString, expAllowedCPUsListRegex string
  225. var cpuList []int
  226. var cpu1, cpu2 int
  227. var cset cpuset.CPUSet
  228. var err error
  229. var ctnAttrs []ctnAttribute
  230. var pod, pod1, pod2 *v1.Pod
  231. ginkgo.It("should assign CPUs as expected based on the Pod spec", func() {
  232. cpuCap, cpuAlloc, _ = getLocalNodeCPUDetails(f)
  233. // Skip CPU Manager tests altogether if the CPU capacity < 2.
  234. if cpuCap < 2 {
  235. e2eskipper.Skipf("Skipping CPU Manager tests since the CPU capacity < 2")
  236. }
  237. // Enable CPU Manager in the kubelet.
  238. oldCfg = enableCPUManagerInKubelet(f, true)
  239. ginkgo.By("running a non-Gu pod")
  240. ctnAttrs = []ctnAttribute{
  241. {
  242. ctnName: "non-gu-container",
  243. cpuRequest: "100m",
  244. cpuLimit: "200m",
  245. },
  246. }
  247. pod = makeCPUManagerPod("non-gu-pod", ctnAttrs)
  248. pod = f.PodClient().CreateSync(pod)
  249. ginkgo.By("checking if the expected cpuset was assigned")
  250. expAllowedCPUsListRegex = fmt.Sprintf("^0-%d\n$", cpuCap-1)
  251. err = f.PodClient().MatchContainerOutput(pod.Name, pod.Spec.Containers[0].Name, expAllowedCPUsListRegex)
  252. framework.ExpectNoError(err, "expected log not found in container [%s] of pod [%s]",
  253. pod.Spec.Containers[0].Name, pod.Name)
  254. ginkgo.By("by deleting the pods and waiting for container removal")
  255. deletePods(f, []string{pod.Name})
  256. waitForContainerRemoval(pod.Spec.Containers[0].Name, pod.Name, pod.Namespace)
  257. ginkgo.By("running a Gu pod")
  258. ctnAttrs = []ctnAttribute{
  259. {
  260. ctnName: "gu-container",
  261. cpuRequest: "1000m",
  262. cpuLimit: "1000m",
  263. },
  264. }
  265. pod = makeCPUManagerPod("gu-pod", ctnAttrs)
  266. pod = f.PodClient().CreateSync(pod)
  267. ginkgo.By("checking if the expected cpuset was assigned")
  268. cpu1 = 1
  269. if isHTEnabled() {
  270. cpuList = cpuset.MustParse(getCPUSiblingList(0)).ToSlice()
  271. cpu1 = cpuList[1]
  272. } else if isMultiNUMA() {
  273. cpuList = cpuset.MustParse(getCoreSiblingList(0)).ToSlice()
  274. cpu1 = cpuList[1]
  275. }
  276. expAllowedCPUsListRegex = fmt.Sprintf("^%d\n$", cpu1)
  277. err = f.PodClient().MatchContainerOutput(pod.Name, pod.Spec.Containers[0].Name, expAllowedCPUsListRegex)
  278. framework.ExpectNoError(err, "expected log not found in container [%s] of pod [%s]",
  279. pod.Spec.Containers[0].Name, pod.Name)
  280. ginkgo.By("by deleting the pods and waiting for container removal")
  281. deletePods(f, []string{pod.Name})
  282. waitForContainerRemoval(pod.Spec.Containers[0].Name, pod.Name, pod.Namespace)
  283. ginkgo.By("running multiple Gu and non-Gu pods")
  284. ctnAttrs = []ctnAttribute{
  285. {
  286. ctnName: "gu-container",
  287. cpuRequest: "1000m",
  288. cpuLimit: "1000m",
  289. },
  290. }
  291. pod1 = makeCPUManagerPod("gu-pod", ctnAttrs)
  292. pod1 = f.PodClient().CreateSync(pod1)
  293. ctnAttrs = []ctnAttribute{
  294. {
  295. ctnName: "non-gu-container",
  296. cpuRequest: "200m",
  297. cpuLimit: "300m",
  298. },
  299. }
  300. pod2 = makeCPUManagerPod("non-gu-pod", ctnAttrs)
  301. pod2 = f.PodClient().CreateSync(pod2)
  302. ginkgo.By("checking if the expected cpuset was assigned")
  303. cpu1 = 1
  304. if isHTEnabled() {
  305. cpuList = cpuset.MustParse(getCPUSiblingList(0)).ToSlice()
  306. cpu1 = cpuList[1]
  307. } else if isMultiNUMA() {
  308. cpuList = cpuset.MustParse(getCoreSiblingList(0)).ToSlice()
  309. cpu1 = cpuList[1]
  310. }
  311. expAllowedCPUsListRegex = fmt.Sprintf("^%d\n$", cpu1)
  312. err = f.PodClient().MatchContainerOutput(pod1.Name, pod1.Spec.Containers[0].Name, expAllowedCPUsListRegex)
  313. framework.ExpectNoError(err, "expected log not found in container [%s] of pod [%s]",
  314. pod1.Spec.Containers[0].Name, pod1.Name)
  315. cpuListString = "0"
  316. if cpuAlloc > 2 {
  317. cset = cpuset.MustParse(fmt.Sprintf("0-%d", cpuCap-1))
  318. cpuListString = fmt.Sprintf("%s", cset.Difference(cpuset.NewCPUSet(cpu1)))
  319. }
  320. expAllowedCPUsListRegex = fmt.Sprintf("^%s\n$", cpuListString)
  321. err = f.PodClient().MatchContainerOutput(pod2.Name, pod2.Spec.Containers[0].Name, expAllowedCPUsListRegex)
  322. framework.ExpectNoError(err, "expected log not found in container [%s] of pod [%s]",
  323. pod2.Spec.Containers[0].Name, pod2.Name)
  324. ginkgo.By("by deleting the pods and waiting for container removal")
  325. deletePods(f, []string{pod1.Name, pod2.Name})
  326. waitForContainerRemoval(pod1.Spec.Containers[0].Name, pod1.Name, pod1.Namespace)
  327. waitForContainerRemoval(pod2.Spec.Containers[0].Name, pod2.Name, pod2.Namespace)
  328. // Skip rest of the tests if CPU capacity < 3.
  329. if cpuCap < 3 {
  330. e2eskipper.Skipf("Skipping rest of the CPU Manager tests since CPU capacity < 3")
  331. }
  332. ginkgo.By("running a Gu pod requesting multiple CPUs")
  333. ctnAttrs = []ctnAttribute{
  334. {
  335. ctnName: "gu-container",
  336. cpuRequest: "2000m",
  337. cpuLimit: "2000m",
  338. },
  339. }
  340. pod = makeCPUManagerPod("gu-pod", ctnAttrs)
  341. pod = f.PodClient().CreateSync(pod)
  342. ginkgo.By("checking if the expected cpuset was assigned")
  343. cpuListString = "1-2"
  344. if isMultiNUMA() {
  345. cpuList = cpuset.MustParse(getCoreSiblingList(0)).ToSlice()
  346. if !isHTEnabled() {
  347. cset = cpuset.MustParse(fmt.Sprintf("%d,%d", cpuList[1], cpuList[2]))
  348. } else {
  349. cset = cpuset.MustParse(getCPUSiblingList(int64(cpuList[1])))
  350. }
  351. cpuListString = fmt.Sprintf("%s", cset)
  352. } else if isHTEnabled() {
  353. cpuListString = "2-3"
  354. cpuList = cpuset.MustParse(getCPUSiblingList(0)).ToSlice()
  355. if cpuList[1] != 1 {
  356. cset = cpuset.MustParse(getCPUSiblingList(1))
  357. cpuListString = fmt.Sprintf("%s", cset)
  358. }
  359. }
  360. expAllowedCPUsListRegex = fmt.Sprintf("^%s\n$", cpuListString)
  361. err = f.PodClient().MatchContainerOutput(pod.Name, pod.Spec.Containers[0].Name, expAllowedCPUsListRegex)
  362. framework.ExpectNoError(err, "expected log not found in container [%s] of pod [%s]",
  363. pod.Spec.Containers[0].Name, pod.Name)
  364. ginkgo.By("by deleting the pods and waiting for container removal")
  365. deletePods(f, []string{pod.Name})
  366. waitForContainerRemoval(pod.Spec.Containers[0].Name, pod.Name, pod.Namespace)
  367. ginkgo.By("running a Gu pod with multiple containers requesting integer CPUs")
  368. ctnAttrs = []ctnAttribute{
  369. {
  370. ctnName: "gu-container1",
  371. cpuRequest: "1000m",
  372. cpuLimit: "1000m",
  373. },
  374. {
  375. ctnName: "gu-container2",
  376. cpuRequest: "1000m",
  377. cpuLimit: "1000m",
  378. },
  379. }
  380. pod = makeCPUManagerPod("gu-pod", ctnAttrs)
  381. pod = f.PodClient().CreateSync(pod)
  382. ginkgo.By("checking if the expected cpuset was assigned")
  383. cpu1, cpu2 = 1, 2
  384. if isHTEnabled() {
  385. cpuList = cpuset.MustParse(getCPUSiblingList(0)).ToSlice()
  386. if cpuList[1] != 1 {
  387. cpu1, cpu2 = cpuList[1], 1
  388. }
  389. if isMultiNUMA() {
  390. cpuList = cpuset.MustParse(getCoreSiblingList(0)).ToSlice()
  391. cpu2 = cpuList[1]
  392. }
  393. } else if isMultiNUMA() {
  394. cpuList = cpuset.MustParse(getCoreSiblingList(0)).ToSlice()
  395. cpu1, cpu2 = cpuList[1], cpuList[2]
  396. }
  397. expAllowedCPUsListRegex = fmt.Sprintf("^%d|%d\n$", cpu1, cpu2)
  398. err = f.PodClient().MatchContainerOutput(pod.Name, pod.Spec.Containers[0].Name, expAllowedCPUsListRegex)
  399. framework.ExpectNoError(err, "expected log not found in container [%s] of pod [%s]",
  400. pod.Spec.Containers[0].Name, pod.Name)
  401. err = f.PodClient().MatchContainerOutput(pod.Name, pod.Spec.Containers[1].Name, expAllowedCPUsListRegex)
  402. framework.ExpectNoError(err, "expected log not found in container [%s] of pod [%s]",
  403. pod.Spec.Containers[1].Name, pod.Name)
  404. ginkgo.By("by deleting the pods and waiting for container removal")
  405. deletePods(f, []string{pod.Name})
  406. waitForContainerRemoval(pod.Spec.Containers[0].Name, pod.Name, pod.Namespace)
  407. waitForContainerRemoval(pod.Spec.Containers[1].Name, pod.Name, pod.Namespace)
  408. ginkgo.By("running multiple Gu pods")
  409. ctnAttrs = []ctnAttribute{
  410. {
  411. ctnName: "gu-container1",
  412. cpuRequest: "1000m",
  413. cpuLimit: "1000m",
  414. },
  415. }
  416. pod1 = makeCPUManagerPod("gu-pod1", ctnAttrs)
  417. pod1 = f.PodClient().CreateSync(pod1)
  418. ctnAttrs = []ctnAttribute{
  419. {
  420. ctnName: "gu-container2",
  421. cpuRequest: "1000m",
  422. cpuLimit: "1000m",
  423. },
  424. }
  425. pod2 = makeCPUManagerPod("gu-pod2", ctnAttrs)
  426. pod2 = f.PodClient().CreateSync(pod2)
  427. ginkgo.By("checking if the expected cpuset was assigned")
  428. cpu1, cpu2 = 1, 2
  429. if isHTEnabled() {
  430. cpuList = cpuset.MustParse(getCPUSiblingList(0)).ToSlice()
  431. if cpuList[1] != 1 {
  432. cpu1, cpu2 = cpuList[1], 1
  433. }
  434. if isMultiNUMA() {
  435. cpuList = cpuset.MustParse(getCoreSiblingList(0)).ToSlice()
  436. cpu2 = cpuList[1]
  437. }
  438. } else if isMultiNUMA() {
  439. cpuList = cpuset.MustParse(getCoreSiblingList(0)).ToSlice()
  440. cpu1, cpu2 = cpuList[1], cpuList[2]
  441. }
  442. expAllowedCPUsListRegex = fmt.Sprintf("^%d\n$", cpu1)
  443. err = f.PodClient().MatchContainerOutput(pod1.Name, pod1.Spec.Containers[0].Name, expAllowedCPUsListRegex)
  444. framework.ExpectNoError(err, "expected log not found in container [%s] of pod [%s]",
  445. pod1.Spec.Containers[0].Name, pod1.Name)
  446. expAllowedCPUsListRegex = fmt.Sprintf("^%d\n$", cpu2)
  447. err = f.PodClient().MatchContainerOutput(pod2.Name, pod2.Spec.Containers[0].Name, expAllowedCPUsListRegex)
  448. framework.ExpectNoError(err, "expected log not found in container [%s] of pod [%s]",
  449. pod2.Spec.Containers[0].Name, pod2.Name)
  450. ginkgo.By("by deleting the pods and waiting for container removal")
  451. deletePods(f, []string{pod1.Name, pod2.Name})
  452. waitForContainerRemoval(pod1.Spec.Containers[0].Name, pod1.Name, pod1.Namespace)
  453. waitForContainerRemoval(pod2.Spec.Containers[0].Name, pod2.Name, pod2.Namespace)
  454. ginkgo.By("test for automatically remove inactive pods from cpumanager state file.")
  455. // First running a Gu Pod,
  456. // second disable cpu manager in kubelet,
  457. // then delete the Gu Pod,
  458. // then enable cpu manager in kubelet,
  459. // at last wait for the reconcile process cleaned up the state file, if the assignments map is empty,
  460. // it proves that the automatic cleanup in the reconcile process is in effect.
  461. ginkgo.By("running a Gu pod for test remove")
  462. ctnAttrs = []ctnAttribute{
  463. {
  464. ctnName: "gu-container-testremove",
  465. cpuRequest: "1000m",
  466. cpuLimit: "1000m",
  467. },
  468. }
  469. pod = makeCPUManagerPod("gu-pod-testremove", ctnAttrs)
  470. pod = f.PodClient().CreateSync(pod)
  471. ginkgo.By("checking if the expected cpuset was assigned")
  472. cpu1 = 1
  473. if isHTEnabled() {
  474. cpuList = cpuset.MustParse(getCPUSiblingList(0)).ToSlice()
  475. cpu1 = cpuList[1]
  476. } else if isMultiNUMA() {
  477. cpuList = cpuset.MustParse(getCoreSiblingList(0)).ToSlice()
  478. cpu1 = cpuList[1]
  479. }
  480. expAllowedCPUsListRegex = fmt.Sprintf("^%d\n$", cpu1)
  481. err = f.PodClient().MatchContainerOutput(pod.Name, pod.Spec.Containers[0].Name, expAllowedCPUsListRegex)
  482. framework.ExpectNoError(err, "expected log not found in container [%s] of pod [%s]",
  483. pod.Spec.Containers[0].Name, pod.Name)
  484. ginkgo.By("disable cpu manager in kubelet")
  485. disableCPUManagerInKubelet(f)
  486. ginkgo.By("by deleting the pod and waiting for container removal")
  487. deletePods(f, []string{pod.Name})
  488. waitForContainerRemoval(pod.Spec.Containers[0].Name, pod.Name, pod.Namespace)
  489. ginkgo.By("enable cpu manager in kubelet without delete state file")
  490. enableCPUManagerInKubelet(f, false)
  491. ginkgo.By("wait for the deleted pod to be cleaned up from the state file")
  492. waitForStateFileCleanedUp()
  493. ginkgo.By("the deleted pod has already been deleted from the state file")
  494. setOldKubeletConfig(f, oldCfg)
  495. })
  496. }
  497. // Serial because the test updates kubelet configuration.
  498. var _ = SIGDescribe("CPU Manager [Serial] [Feature:CPUManager][NodeAlphaFeature:CPUManager]", func() {
  499. f := framework.NewDefaultFramework("cpu-manager-test")
  500. ginkgo.Context("With kubeconfig updated with static CPU Manager policy run the CPU Manager tests", func() {
  501. runCPUManagerTests(f)
  502. })
  503. })