// FindUnneededNodes calculates which nodes are not needed, i.e. all pods can be scheduled somewhere else, // and updates unneededNodes map accordingly. It also returns information where pods can be rescheduld and // node utilization level. func FindUnneededNodes( context AutoscalingContext, nodes []*apiv1.Node, unneededNodes map[string]time.Time, pods []*apiv1.Pod, oldHints map[string]string, tracker *simulator.UsageTracker, timestamp time.Time) (unnededTimeMap map[string]time.Time, podReschedulingHints map[string]string, utilizationMap map[string]float64) { currentlyUnneededNodes := make([]*apiv1.Node, 0) nodeNameToNodeInfo := schedulercache.CreateNodeNameToInfoMap(pods, nodes) utilizationMap = make(map[string]float64) // Phase1 - look at the nodes utilization. for _, node := range nodes { nodeInfo, found := nodeNameToNodeInfo[node.Name] if !found { glog.Errorf("Node info for %s not found", node.Name) continue } utilization, err := simulator.CalculateUtilization(node, nodeInfo) if err != nil { glog.Warningf("Failed to calculate utilization for %s: %v", node.Name, err) } glog.V(4).Infof("Node %s - utilization %f", node.Name, utilization) utilizationMap[node.Name] = utilization if utilization >= context.ScaleDownUtilizationThreshold { glog.V(4).Infof("Node %s is not suitable for removal - utilization too big (%f)", node.Name, utilization) continue } currentlyUnneededNodes = append(currentlyUnneededNodes, node) } // Phase2 - check which nodes can be probably removed using fast drain. nodesToRemove, newHints, err := simulator.FindNodesToRemove(currentlyUnneededNodes, nodes, pods, nil, context.PredicateChecker, len(currentlyUnneededNodes), true, oldHints, tracker, timestamp) if err != nil { glog.Errorf("Error while simulating node drains: %v", err) return map[string]time.Time{}, oldHints, map[string]float64{} } // Update the timestamp map. now := time.Now() result := make(map[string]time.Time) for _, node := range nodesToRemove { name := node.Node.Name if val, found := unneededNodes[name]; !found { result[name] = now } else { result[name] = val } } return result, newHints, utilizationMap }
// FindUnneededNodes calculates which nodes are not needed, i.e. all pods can be scheduled somewhere else, // and updates unneededNodes map accordingly. func FindUnneededNodes(nodes []*kube_api.Node, unneededNodes map[string]time.Time, utilizationThreshold float64, pods []*kube_api.Pod, predicateChecker *simulator.PredicateChecker) map[string]time.Time { currentlyUnneededNodes := make([]*kube_api.Node, 0) nodeNameToNodeInfo := schedulercache.CreateNodeNameToInfoMap(pods) // Phase1 - look at the nodes utilization. for _, node := range nodes { nodeInfo, found := nodeNameToNodeInfo[node.Name] if !found { glog.Errorf("Node info for %s not found", node.Name) continue } utilization, err := simulator.CalculateUtilization(node, nodeInfo) if err != nil { glog.Warningf("Failed to calculate utilization for %s: %v", node.Name, err) } glog.V(4).Infof("Node %s - utilization %f", node.Name, utilization) if utilization >= utilizationThreshold { glog.V(4).Infof("Node %s is not suitable for removal - utilization to big (%f)", node.Name, utilization) continue } currentlyUnneededNodes = append(currentlyUnneededNodes, node) } // Phase2 - check which nodes can be probably removed using fast drain. nodesToRemove, err := simulator.FindNodesToRemove(currentlyUnneededNodes, nodes, pods, nil, predicateChecker, len(currentlyUnneededNodes), true) if err != nil { glog.Errorf("Error while simulating node drains: %v", err) return map[string]time.Time{} } // Update the timestamp map. now := time.Now() result := make(map[string]time.Time) for _, node := range nodesToRemove { name := node.Name if val, found := unneededNodes[name]; !found { result[name] = now } else { result[name] = val } } return result }