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 }