k8s.io/kubernetes@v1.29.3/pkg/scheduler/internal/cache/snapshot.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cache
    18  
    19  import (
    20  	"fmt"
    21  
    22  	v1 "k8s.io/api/core/v1"
    23  	"k8s.io/apimachinery/pkg/util/sets"
    24  	"k8s.io/kubernetes/pkg/scheduler/framework"
    25  )
    26  
    27  // Snapshot is a snapshot of cache NodeInfo and NodeTree order. The scheduler takes a
    28  // snapshot at the beginning of each scheduling cycle and uses it for its operations in that cycle.
    29  type Snapshot struct {
    30  	// nodeInfoMap a map of node name to a snapshot of its NodeInfo.
    31  	nodeInfoMap map[string]*framework.NodeInfo
    32  	// nodeInfoList is the list of nodes as ordered in the cache's nodeTree.
    33  	nodeInfoList []*framework.NodeInfo
    34  	// havePodsWithAffinityNodeInfoList is the list of nodes with at least one pod declaring affinity terms.
    35  	havePodsWithAffinityNodeInfoList []*framework.NodeInfo
    36  	// havePodsWithRequiredAntiAffinityNodeInfoList is the list of nodes with at least one pod declaring
    37  	// required anti-affinity terms.
    38  	havePodsWithRequiredAntiAffinityNodeInfoList []*framework.NodeInfo
    39  	// usedPVCSet contains a set of PVC names that have one or more scheduled pods using them,
    40  	// keyed in the format "namespace/name".
    41  	usedPVCSet sets.Set[string]
    42  	generation int64
    43  }
    44  
    45  var _ framework.SharedLister = &Snapshot{}
    46  
    47  // NewEmptySnapshot initializes a Snapshot struct and returns it.
    48  func NewEmptySnapshot() *Snapshot {
    49  	return &Snapshot{
    50  		nodeInfoMap: make(map[string]*framework.NodeInfo),
    51  		usedPVCSet:  sets.New[string](),
    52  	}
    53  }
    54  
    55  // NewSnapshot initializes a Snapshot struct and returns it.
    56  func NewSnapshot(pods []*v1.Pod, nodes []*v1.Node) *Snapshot {
    57  	nodeInfoMap := createNodeInfoMap(pods, nodes)
    58  	nodeInfoList := make([]*framework.NodeInfo, 0, len(nodeInfoMap))
    59  	havePodsWithAffinityNodeInfoList := make([]*framework.NodeInfo, 0, len(nodeInfoMap))
    60  	havePodsWithRequiredAntiAffinityNodeInfoList := make([]*framework.NodeInfo, 0, len(nodeInfoMap))
    61  	for _, v := range nodeInfoMap {
    62  		nodeInfoList = append(nodeInfoList, v)
    63  		if len(v.PodsWithAffinity) > 0 {
    64  			havePodsWithAffinityNodeInfoList = append(havePodsWithAffinityNodeInfoList, v)
    65  		}
    66  		if len(v.PodsWithRequiredAntiAffinity) > 0 {
    67  			havePodsWithRequiredAntiAffinityNodeInfoList = append(havePodsWithRequiredAntiAffinityNodeInfoList, v)
    68  		}
    69  	}
    70  
    71  	s := NewEmptySnapshot()
    72  	s.nodeInfoMap = nodeInfoMap
    73  	s.nodeInfoList = nodeInfoList
    74  	s.havePodsWithAffinityNodeInfoList = havePodsWithAffinityNodeInfoList
    75  	s.havePodsWithRequiredAntiAffinityNodeInfoList = havePodsWithRequiredAntiAffinityNodeInfoList
    76  	s.usedPVCSet = createUsedPVCSet(pods)
    77  
    78  	return s
    79  }
    80  
    81  // createNodeInfoMap obtains a list of pods and pivots that list into a map
    82  // where the keys are node names and the values are the aggregated information
    83  // for that node.
    84  func createNodeInfoMap(pods []*v1.Pod, nodes []*v1.Node) map[string]*framework.NodeInfo {
    85  	nodeNameToInfo := make(map[string]*framework.NodeInfo)
    86  	for _, pod := range pods {
    87  		nodeName := pod.Spec.NodeName
    88  		if _, ok := nodeNameToInfo[nodeName]; !ok {
    89  			nodeNameToInfo[nodeName] = framework.NewNodeInfo()
    90  		}
    91  		nodeNameToInfo[nodeName].AddPod(pod)
    92  	}
    93  	imageExistenceMap := createImageExistenceMap(nodes)
    94  
    95  	for _, node := range nodes {
    96  		if _, ok := nodeNameToInfo[node.Name]; !ok {
    97  			nodeNameToInfo[node.Name] = framework.NewNodeInfo()
    98  		}
    99  		nodeInfo := nodeNameToInfo[node.Name]
   100  		nodeInfo.SetNode(node)
   101  		nodeInfo.ImageStates = getNodeImageStates(node, imageExistenceMap)
   102  	}
   103  	return nodeNameToInfo
   104  }
   105  
   106  func createUsedPVCSet(pods []*v1.Pod) sets.Set[string] {
   107  	usedPVCSet := sets.New[string]()
   108  	for _, pod := range pods {
   109  		if pod.Spec.NodeName == "" {
   110  			continue
   111  		}
   112  
   113  		for _, v := range pod.Spec.Volumes {
   114  			if v.PersistentVolumeClaim == nil {
   115  				continue
   116  			}
   117  
   118  			key := framework.GetNamespacedName(pod.Namespace, v.PersistentVolumeClaim.ClaimName)
   119  			usedPVCSet.Insert(key)
   120  		}
   121  	}
   122  	return usedPVCSet
   123  }
   124  
   125  // getNodeImageStates returns the given node's image states based on the given imageExistence map.
   126  func getNodeImageStates(node *v1.Node, imageExistenceMap map[string]sets.Set[string]) map[string]*framework.ImageStateSummary {
   127  	imageStates := make(map[string]*framework.ImageStateSummary)
   128  
   129  	for _, image := range node.Status.Images {
   130  		for _, name := range image.Names {
   131  			imageStates[name] = &framework.ImageStateSummary{
   132  				Size:     image.SizeBytes,
   133  				NumNodes: imageExistenceMap[name].Len(),
   134  			}
   135  		}
   136  	}
   137  	return imageStates
   138  }
   139  
   140  // createImageExistenceMap returns a map recording on which nodes the images exist, keyed by the images' names.
   141  func createImageExistenceMap(nodes []*v1.Node) map[string]sets.Set[string] {
   142  	imageExistenceMap := make(map[string]sets.Set[string])
   143  	for _, node := range nodes {
   144  		for _, image := range node.Status.Images {
   145  			for _, name := range image.Names {
   146  				if _, ok := imageExistenceMap[name]; !ok {
   147  					imageExistenceMap[name] = sets.New(node.Name)
   148  				} else {
   149  					imageExistenceMap[name].Insert(node.Name)
   150  				}
   151  			}
   152  		}
   153  	}
   154  	return imageExistenceMap
   155  }
   156  
   157  // NodeInfos returns a NodeInfoLister.
   158  func (s *Snapshot) NodeInfos() framework.NodeInfoLister {
   159  	return s
   160  }
   161  
   162  // StorageInfos returns a StorageInfoLister.
   163  func (s *Snapshot) StorageInfos() framework.StorageInfoLister {
   164  	return s
   165  }
   166  
   167  // NumNodes returns the number of nodes in the snapshot.
   168  func (s *Snapshot) NumNodes() int {
   169  	return len(s.nodeInfoList)
   170  }
   171  
   172  // List returns the list of nodes in the snapshot.
   173  func (s *Snapshot) List() ([]*framework.NodeInfo, error) {
   174  	return s.nodeInfoList, nil
   175  }
   176  
   177  // HavePodsWithAffinityList returns the list of nodes with at least one pod with inter-pod affinity
   178  func (s *Snapshot) HavePodsWithAffinityList() ([]*framework.NodeInfo, error) {
   179  	return s.havePodsWithAffinityNodeInfoList, nil
   180  }
   181  
   182  // HavePodsWithRequiredAntiAffinityList returns the list of nodes with at least one pod with
   183  // required inter-pod anti-affinity
   184  func (s *Snapshot) HavePodsWithRequiredAntiAffinityList() ([]*framework.NodeInfo, error) {
   185  	return s.havePodsWithRequiredAntiAffinityNodeInfoList, nil
   186  }
   187  
   188  // Get returns the NodeInfo of the given node name.
   189  func (s *Snapshot) Get(nodeName string) (*framework.NodeInfo, error) {
   190  	if v, ok := s.nodeInfoMap[nodeName]; ok && v.Node() != nil {
   191  		return v, nil
   192  	}
   193  	return nil, fmt.Errorf("nodeinfo not found for node name %q", nodeName)
   194  }
   195  
   196  func (s *Snapshot) IsPVCUsedByPods(key string) bool {
   197  	return s.usedPVCSet.Has(key)
   198  }