Include UnschedulableAndUnresolvable nodes into the status map

Signed-off-by: Dave Chen <dave.chen@arm.com>
This commit is contained in:
Dave Chen 2021-01-15 15:17:55 +08:00
parent 9d48a44601
commit fe5a266a03
2 changed files with 40 additions and 17 deletions

View File

@ -138,7 +138,7 @@ func (pl *DefaultPreemption) preempt(ctx context.Context, state *framework.Cycle
} }
// 2) Find all preemption candidates. // 2) Find all preemption candidates.
candidates, evaluatedNodeNum, status, err := pl.FindCandidates(ctx, state, pod, m) candidates, nodeToStautsMap, err := pl.FindCandidates(ctx, state, pod, m)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -147,9 +147,9 @@ func (pl *DefaultPreemption) preempt(ctx context.Context, state *framework.Cycle
if len(candidates) == 0 { if len(candidates) == 0 {
return "", &framework.FitError{ return "", &framework.FitError{
Pod: pod, Pod: pod,
NumAllNodes: len(evaluatedNodeNum), NumAllNodes: len(nodeToStautsMap),
Diagnosis: framework.Diagnosis{ Diagnosis: framework.Diagnosis{
NodeToStatusMap: status, NodeToStatusMap: nodeToStautsMap,
// Leave FailedPlugins as nil as it won't be used on moving Pods. // Leave FailedPlugins as nil as it won't be used on moving Pods.
}, },
} }
@ -198,15 +198,15 @@ func (pl *DefaultPreemption) getOffsetAndNumCandidates(numNodes int32) (int32, i
// FindCandidates calculates a slice of preemption candidates. // FindCandidates calculates a slice of preemption candidates.
// Each candidate is executable to make the given <pod> schedulable. // Each candidate is executable to make the given <pod> schedulable.
func (pl *DefaultPreemption) FindCandidates(ctx context.Context, state *framework.CycleState, pod *v1.Pod, m framework.NodeToStatusMap) ([]Candidate, []*framework.NodeInfo, framework.NodeToStatusMap, error) { func (pl *DefaultPreemption) FindCandidates(ctx context.Context, state *framework.CycleState, pod *v1.Pod, m framework.NodeToStatusMap) ([]Candidate, framework.NodeToStatusMap, error) {
allNodes, err := pl.fh.SnapshotSharedLister().NodeInfos().List() allNodes, err := pl.fh.SnapshotSharedLister().NodeInfos().List()
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, err
} }
if len(allNodes) == 0 { if len(allNodes) == 0 {
return nil, nil, nil, fmt.Errorf("no nodes available") return nil, nil, fmt.Errorf("no nodes available")
} }
potentialNodes := nodesWherePreemptionMightHelp(allNodes, m) potentialNodes, unschedulableNodeStatus := nodesWherePreemptionMightHelp(allNodes, m)
if len(potentialNodes) == 0 { if len(potentialNodes) == 0 {
klog.V(3).Infof("Preemption will not help schedule pod %v/%v on any node.", pod.Namespace, pod.Name) klog.V(3).Infof("Preemption will not help schedule pod %v/%v on any node.", pod.Namespace, pod.Name)
// In this case, we should clean-up any existing nominated node name of the pod. // In this case, we should clean-up any existing nominated node name of the pod.
@ -214,12 +214,12 @@ func (pl *DefaultPreemption) FindCandidates(ctx context.Context, state *framewor
klog.Errorf("Cannot clear 'NominatedNodeName' field of pod %v/%v: %v", pod.Namespace, pod.Name, err) klog.Errorf("Cannot clear 'NominatedNodeName' field of pod %v/%v: %v", pod.Namespace, pod.Name, err)
// We do not return as this error is not critical. // We do not return as this error is not critical.
} }
return nil, nil, nil, nil return nil, unschedulableNodeStatus, nil
} }
pdbs, err := getPodDisruptionBudgets(pl.pdbLister) pdbs, err := getPodDisruptionBudgets(pl.pdbLister)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, err
} }
offset, numCandidates := pl.getOffsetAndNumCandidates(int32(len(potentialNodes))) offset, numCandidates := pl.getOffsetAndNumCandidates(int32(len(potentialNodes)))
@ -231,7 +231,10 @@ func (pl *DefaultPreemption) FindCandidates(ctx context.Context, state *framewor
klog.Infof("from a pool of %d nodes (offset: %d, sample %d nodes: %v), ~%d candidates will be chosen", len(potentialNodes), offset, len(sample), sample, numCandidates) klog.Infof("from a pool of %d nodes (offset: %d, sample %d nodes: %v), ~%d candidates will be chosen", len(potentialNodes), offset, len(sample), sample, numCandidates)
} }
candidates, nodeStatuses := dryRunPreemption(ctx, pl.fh, state, pod, potentialNodes, pdbs, offset, numCandidates) candidates, nodeStatuses := dryRunPreemption(ctx, pl.fh, state, pod, potentialNodes, pdbs, offset, numCandidates)
return candidates, potentialNodes, nodeStatuses, nil for node, status := range unschedulableNodeStatus {
nodeStatuses[node] = status
}
return candidates, nodeStatuses, nil
} }
// PodEligibleToPreemptOthers determines whether this pod should be considered // PodEligibleToPreemptOthers determines whether this pod should be considered
@ -268,18 +271,20 @@ func PodEligibleToPreemptOthers(pod *v1.Pod, nodeInfos framework.NodeInfoLister,
// nodesWherePreemptionMightHelp returns a list of nodes with failed predicates // nodesWherePreemptionMightHelp returns a list of nodes with failed predicates
// that may be satisfied by removing pods from the node. // that may be satisfied by removing pods from the node.
func nodesWherePreemptionMightHelp(nodes []*framework.NodeInfo, m framework.NodeToStatusMap) []*framework.NodeInfo { func nodesWherePreemptionMightHelp(nodes []*framework.NodeInfo, m framework.NodeToStatusMap) ([]*framework.NodeInfo, framework.NodeToStatusMap) {
var potentialNodes []*framework.NodeInfo var potentialNodes []*framework.NodeInfo
nodeStatuses := make(framework.NodeToStatusMap)
for _, node := range nodes { for _, node := range nodes {
name := node.Node().Name name := node.Node().Name
// We reply on the status by each plugin - 'Unschedulable' or 'UnschedulableAndUnresolvable' // We rely on the status by each plugin - 'Unschedulable' or 'UnschedulableAndUnresolvable'
// to determine whether preemption may help or not on the node. // to determine whether preemption may help or not on the node.
if m[name].Code() == framework.UnschedulableAndUnresolvable { if m[name].Code() == framework.UnschedulableAndUnresolvable {
nodeStatuses[node.Node().Name] = framework.NewStatus(framework.UnschedulableAndUnresolvable, "Preemption is not helpful for scheduling")
continue continue
} }
potentialNodes = append(potentialNodes, node) potentialNodes = append(potentialNodes, node)
} }
return potentialNodes return potentialNodes, nodeStatuses
} }
type candidateList struct { type candidateList struct {

View File

@ -154,9 +154,7 @@ func TestPostFilter(t *testing.T) {
"node1": framework.NewStatus(framework.UnschedulableAndUnresolvable), "node1": framework.NewStatus(framework.UnschedulableAndUnresolvable),
}, },
wantResult: nil, wantResult: nil,
// node1 is not actually been evaluated, it will not be processed by preemption, the status here is an internal status, the final message that is going to be written in wantStatus: framework.NewStatus(framework.Unschedulable, "0/1 nodes are available: 1 Preemption is not helpful for scheduling."),
// the log will be looks like "Status after running PostFilter plugins for pod" pod="default/p" status=&{code:2 reasons:[0/0 nodes are available: .] err:<nil>}
wantStatus: framework.NewStatus(framework.Unschedulable, "0/0 nodes are available: ."),
}, },
{ {
name: "pod can be made schedulable on one node", name: "pod can be made schedulable on one node",
@ -233,6 +231,26 @@ func TestPostFilter(t *testing.T) {
wantResult: nil, wantResult: nil,
wantStatus: framework.NewStatus(framework.Unschedulable, "0/2 nodes are available: 1 Insufficient cpu, 1 No victims found on node node1 for preemptor pod p."), wantStatus: framework.NewStatus(framework.Unschedulable, "0/2 nodes are available: 1 Insufficient cpu, 1 No victims found on node node1 for preemptor pod p."),
}, },
{
name: "no candidate nodes found with mixed reason, 2 UnschedulableAndUnresolvable nodes and 2 nodes don't have enough CPU resource",
pod: st.MakePod().Name("p").UID("p").Namespace(v1.NamespaceDefault).Priority(highPriority).Req(largeRes).Obj(),
pods: []*v1.Pod{
st.MakePod().Name("p1").UID("p1").Namespace(v1.NamespaceDefault).Node("node1").Obj(),
st.MakePod().Name("p2").UID("p2").Namespace(v1.NamespaceDefault).Node("node2").Obj(),
},
nodes: []*v1.Node{
st.MakeNode().Name("node1").Capacity(nodeRes).Obj(),
st.MakeNode().Name("node2").Capacity(nodeRes).Obj(),
st.MakeNode().Name("node3").Capacity(nodeRes).Obj(),
st.MakeNode().Name("node4").Capacity(nodeRes).Obj(),
},
filteredNodesStatuses: framework.NodeToStatusMap{
"node3": framework.NewStatus(framework.UnschedulableAndUnresolvable),
"node4": framework.NewStatus(framework.UnschedulableAndUnresolvable),
},
wantResult: nil,
wantStatus: framework.NewStatus(framework.Unschedulable, "0/4 nodes are available: 2 Insufficient cpu, 2 Preemption is not helpful for scheduling."),
},
} }
for _, tt := range tests { for _, tt := range tests {
@ -1413,7 +1431,7 @@ func TestNodesWherePreemptionMightHelp(t *testing.T) {
ni.SetNode(st.MakeNode().Name(name).Obj()) ni.SetNode(st.MakeNode().Name(name).Obj())
nodeInfos = append(nodeInfos, ni) nodeInfos = append(nodeInfos, ni)
} }
nodes := nodesWherePreemptionMightHelp(nodeInfos, tt.nodesStatuses) nodes, _ := nodesWherePreemptionMightHelp(nodeInfos, tt.nodesStatuses)
if len(tt.expected) != len(nodes) { if len(tt.expected) != len(nodes) {
t.Errorf("number of nodes is not the same as expected. exptectd: %d, got: %d. Nodes: %v", len(tt.expected), len(nodes), nodes) t.Errorf("number of nodes is not the same as expected. exptectd: %d, got: %d. Nodes: %v", len(tt.expected), len(nodes), nodes)
} }