| /* |
| Copyright 2017 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 cpumanager |
| |
| import ( |
| "reflect" |
| "testing" |
| |
| "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology" |
| "k8s.io/kubernetes/pkg/kubelet/cm/cpuset" |
| ) |
| |
| func TestCPUAccumulatorFreeSockets(t *testing.T) { |
| testCases := []struct { |
| description string |
| topo *topology.CPUTopology |
| availableCPUs cpuset.CPUSet |
| expect []int |
| }{ |
| { |
| "single socket HT, 1 socket free", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| []int{0}, |
| }, |
| { |
| "single socket HT, 0 sockets free", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(1, 2, 3, 4, 5, 6, 7), |
| []int{}, |
| }, |
| { |
| "dual socket HT, 2 sockets free", |
| topoDualSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), |
| []int{0, 1}, |
| }, |
| { |
| "dual socket HT, 1 socket free", |
| topoDualSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11), |
| []int{1}, |
| }, |
| { |
| "dual socket HT, 0 sockets free", |
| topoDualSocketHT, |
| cpuset.NewCPUSet(0, 2, 3, 4, 5, 6, 7, 8, 9, 11), |
| []int{}, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0) |
| result := acc.freeSockets() |
| if !reflect.DeepEqual(result, tc.expect) { |
| t.Errorf("[%s] expected %v to equal %v", tc.description, result, tc.expect) |
| } |
| } |
| } |
| |
| func TestCPUAccumulatorFreeCores(t *testing.T) { |
| testCases := []struct { |
| description string |
| topo *topology.CPUTopology |
| availableCPUs cpuset.CPUSet |
| expect []int |
| }{ |
| { |
| "single socket HT, 4 cores free", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| []int{0, 1, 2, 3}, |
| }, |
| { |
| "single socket HT, 3 cores free", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 4, 5, 6), |
| []int{0, 1, 2}, |
| }, |
| { |
| "single socket HT, 3 cores free (1 partially consumed)", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6), |
| []int{0, 1, 2}, |
| }, |
| { |
| "single socket HT, 0 cores free", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(), |
| []int{}, |
| }, |
| { |
| "single socket HT, 0 cores free (4 partially consumed)", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3), |
| []int{}, |
| }, |
| { |
| "dual socket HT, 6 cores free", |
| topoDualSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), |
| []int{0, 2, 4, 1, 3, 5}, |
| }, |
| { |
| "dual socket HT, 5 cores free (1 consumed from socket 0)", |
| topoDualSocketHT, |
| cpuset.NewCPUSet(2, 1, 3, 4, 5, 7, 8, 9, 10, 11), |
| []int{2, 4, 1, 3, 5}, |
| }, |
| { |
| "dual socket HT, 4 cores free (1 consumed from each socket)", |
| topoDualSocketHT, |
| cpuset.NewCPUSet(2, 3, 4, 5, 8, 9, 10, 11), |
| []int{2, 4, 3, 5}, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0) |
| result := acc.freeCores() |
| if !reflect.DeepEqual(result, tc.expect) { |
| t.Errorf("[%s] expected %v to equal %v", tc.description, result, tc.expect) |
| } |
| } |
| } |
| |
| func TestCPUAccumulatorFreeCPUs(t *testing.T) { |
| testCases := []struct { |
| description string |
| topo *topology.CPUTopology |
| availableCPUs cpuset.CPUSet |
| expect []int |
| }{ |
| { |
| "single socket HT, 8 cpus free", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| []int{0, 4, 1, 5, 2, 6, 3, 7}, |
| }, |
| { |
| "single socket HT, 5 cpus free", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(3, 4, 5, 6, 7), |
| []int{4, 5, 6, 3, 7}, |
| }, |
| { |
| "dual socket HT, 12 cpus free", |
| topoDualSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), |
| []int{0, 6, 2, 8, 4, 10, 1, 7, 3, 9, 5, 11}, |
| }, |
| { |
| "dual socket HT, 11 cpus free", |
| topoDualSocketHT, |
| cpuset.NewCPUSet(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), |
| []int{6, 2, 8, 4, 10, 1, 7, 3, 9, 5, 11}, |
| }, |
| { |
| "dual socket HT, 10 cpus free", |
| topoDualSocketHT, |
| cpuset.NewCPUSet(1, 2, 3, 4, 5, 7, 8, 9, 10, 11), |
| []int{2, 8, 4, 10, 1, 7, 3, 9, 5, 11}, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| acc := newCPUAccumulator(tc.topo, tc.availableCPUs, 0) |
| result := acc.freeCPUs() |
| if !reflect.DeepEqual(result, tc.expect) { |
| t.Errorf("[%s] expected %v to equal %v", tc.description, result, tc.expect) |
| } |
| } |
| } |
| |
| func TestCPUAccumulatorTake(t *testing.T) { |
| testCases := []struct { |
| description string |
| topo *topology.CPUTopology |
| availableCPUs cpuset.CPUSet |
| takeCPUs []cpuset.CPUSet |
| numCPUs int |
| expectSatisfied bool |
| expectFailed bool |
| }{ |
| { |
| "take 0 cpus from a single socket HT, require 1", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| []cpuset.CPUSet{cpuset.NewCPUSet()}, |
| 1, |
| false, |
| false, |
| }, |
| { |
| "take 0 cpus from a single socket HT, require 1, none available", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(), |
| []cpuset.CPUSet{cpuset.NewCPUSet()}, |
| 1, |
| false, |
| true, |
| }, |
| { |
| "take 1 cpu from a single socket HT, require 1", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| []cpuset.CPUSet{cpuset.NewCPUSet(0)}, |
| 1, |
| true, |
| false, |
| }, |
| { |
| "take 1 cpu from a single socket HT, require 2", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| []cpuset.CPUSet{cpuset.NewCPUSet(0)}, |
| 2, |
| false, |
| false, |
| }, |
| { |
| "take 2 cpu from a single socket HT, require 4, expect failed", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2), |
| []cpuset.CPUSet{cpuset.NewCPUSet(0), cpuset.NewCPUSet(1)}, |
| 4, |
| false, |
| true, |
| }, |
| { |
| "take all cpus one at a time from a single socket HT, require 8", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| []cpuset.CPUSet{ |
| cpuset.NewCPUSet(0), |
| cpuset.NewCPUSet(1), |
| cpuset.NewCPUSet(2), |
| cpuset.NewCPUSet(3), |
| cpuset.NewCPUSet(4), |
| cpuset.NewCPUSet(5), |
| cpuset.NewCPUSet(6), |
| cpuset.NewCPUSet(7), |
| }, |
| 8, |
| true, |
| false, |
| }, |
| } |
| |
| for _, tc := range testCases { |
| acc := newCPUAccumulator(tc.topo, tc.availableCPUs, tc.numCPUs) |
| totalTaken := 0 |
| for _, cpus := range tc.takeCPUs { |
| acc.take(cpus) |
| totalTaken += cpus.Size() |
| } |
| if tc.expectSatisfied != acc.isSatisfied() { |
| t.Errorf("[%s] expected acc.isSatisfied() to be %t", tc.description, tc.expectSatisfied) |
| } |
| if tc.expectFailed != acc.isFailed() { |
| t.Errorf("[%s] expected acc.isFailed() to be %t", tc.description, tc.expectFailed) |
| } |
| for _, cpus := range tc.takeCPUs { |
| availableCPUs := acc.details.CPUs() |
| if cpus.Intersection(availableCPUs).Size() > 0 { |
| t.Errorf("[%s] expected intersection of taken cpus [%s] and acc.details.CPUs() [%s] to be empty", tc.description, cpus, availableCPUs) |
| } |
| if !cpus.IsSubsetOf(acc.result) { |
| t.Errorf("[%s] expected [%s] to be a subset of acc.result [%s]", tc.description, cpus, acc.result) |
| } |
| } |
| expNumCPUsNeeded := tc.numCPUs - totalTaken |
| if acc.numCPUsNeeded != expNumCPUsNeeded { |
| t.Errorf("[%s] expected acc.numCPUsNeeded to be %d (got %d)", tc.description, expNumCPUsNeeded, acc.numCPUsNeeded) |
| } |
| } |
| } |
| |
| func TestTakeByTopology(t *testing.T) { |
| testCases := []struct { |
| description string |
| topo *topology.CPUTopology |
| availableCPUs cpuset.CPUSet |
| numCPUs int |
| expErr string |
| expResult cpuset.CPUSet |
| }{ |
| { |
| "take more cpus than are available from single socket with HT", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 2, 4, 6), |
| 5, |
| "not enough cpus available to satisfy request", |
| cpuset.NewCPUSet(), |
| }, |
| { |
| "take zero cpus from single socket with HT", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| 0, |
| "", |
| cpuset.NewCPUSet(), |
| }, |
| { |
| "take one cpu from single socket with HT", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| 1, |
| "", |
| cpuset.NewCPUSet(0), |
| }, |
| { |
| "take one cpu from single socket with HT, some cpus are taken", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(1, 3, 5, 6, 7), |
| 1, |
| "", |
| cpuset.NewCPUSet(6), |
| }, |
| { |
| "take two cpus from single socket with HT", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| 2, |
| "", |
| cpuset.NewCPUSet(0, 4), |
| }, |
| { |
| "take all cpus from single socket with HT", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| 8, |
| "", |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7), |
| }, |
| { |
| "take two cpus from single socket with HT, only one core totally free", |
| topoSingleSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 6), |
| 2, |
| "", |
| cpuset.NewCPUSet(2, 6), |
| }, |
| { |
| "take three cpus from dual socket with HT - core from Socket 0", |
| topoDualSocketHT, |
| cpuset.NewCPUSet(1, 2, 3, 4, 5, 7, 8, 9, 10, 11), |
| 1, |
| "", |
| cpuset.NewCPUSet(2), |
| }, |
| { |
| "take a socket of cpus from dual socket with HT", |
| topoDualSocketHT, |
| cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), |
| 6, |
| "", |
| cpuset.NewCPUSet(0, 2, 4, 6, 8, 10), |
| }, |
| } |
| |
| for _, tc := range testCases { |
| result, err := takeByTopology(tc.topo, tc.availableCPUs, tc.numCPUs) |
| if tc.expErr != "" && err.Error() != tc.expErr { |
| t.Errorf("expected error to be [%v] but it was [%v] in test \"%s\"", tc.expErr, err, tc.description) |
| } |
| if !result.Equals(tc.expResult) { |
| t.Errorf("expected result [%s] to equal [%s] in test \"%s\"", result, tc.expResult, tc.description) |
| } |
| } |
| } |