volcano.sh/volcano@v1.9.0/pkg/scheduler/framework/util.go (about) 1 /* 2 Copyright 2018 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 framework 18 19 import ( 20 v1 "k8s.io/api/core/v1" 21 "k8s.io/apimachinery/pkg/api/errors" 22 "k8s.io/apimachinery/pkg/labels" 23 "k8s.io/klog/v2" 24 k8sframework "k8s.io/kubernetes/pkg/scheduler/framework" 25 26 "volcano.sh/volcano/pkg/scheduler/api" 27 ) 28 29 // PodFilter is a function to filter a pod. If pod passed return true else return false. 30 type PodFilter func(*v1.Pod) bool 31 32 // PodsLister interface represents anything that can list pods for a scheduler. 33 type PodsLister interface { 34 // Returns the list of pods. 35 List(labels.Selector) ([]*v1.Pod, error) 36 // This is similar to "List()", but the returned slice does not 37 // contain pods that don't pass `podFilter`. 38 FilteredList(podFilter PodFilter, selector labels.Selector) ([]*v1.Pod, error) 39 } 40 41 // PodLister is used in predicate and nodeorder plugin 42 type PodLister struct { 43 Session *Session 44 45 CachedPods map[api.TaskID]*v1.Pod 46 Tasks map[api.TaskID]*api.TaskInfo 47 TaskWithAffinity map[api.TaskID]*api.TaskInfo 48 } 49 50 // PodAffinityLister is used to list pod with affinity 51 type PodAffinityLister struct { 52 pl *PodLister 53 } 54 55 // HaveAffinity checks pod have affinity or not 56 func HaveAffinity(pod *v1.Pod) bool { 57 affinity := pod.Spec.Affinity 58 return affinity != nil && 59 (affinity.NodeAffinity != nil || 60 affinity.PodAffinity != nil || 61 affinity.PodAntiAffinity != nil) 62 } 63 64 // NewPodLister returns a PodLister generate from ssn 65 func NewPodLister(ssn *Session) *PodLister { 66 pl := &PodLister{ 67 Session: ssn, 68 69 CachedPods: make(map[api.TaskID]*v1.Pod), 70 Tasks: make(map[api.TaskID]*api.TaskInfo), 71 TaskWithAffinity: make(map[api.TaskID]*api.TaskInfo), 72 } 73 74 for _, job := range pl.Session.Jobs { 75 for status, tasks := range job.TaskStatusIndex { 76 if !api.AllocatedStatus(status) { 77 continue 78 } 79 80 for _, task := range tasks { 81 pl.Tasks[task.UID] = task 82 83 pod := pl.copyTaskPod(task) 84 pl.CachedPods[task.UID] = pod 85 86 if HaveAffinity(task.Pod) { 87 pl.TaskWithAffinity[task.UID] = task 88 } 89 } 90 } 91 } 92 93 return pl 94 } 95 96 // NewPodListerFromNode returns a PodLister generate from ssn 97 func NewPodListerFromNode(ssn *Session) *PodLister { 98 pl := &PodLister{ 99 Session: ssn, 100 CachedPods: make(map[api.TaskID]*v1.Pod), 101 Tasks: make(map[api.TaskID]*api.TaskInfo), 102 TaskWithAffinity: make(map[api.TaskID]*api.TaskInfo), 103 } 104 105 for _, node := range pl.Session.Nodes { 106 for _, task := range node.Tasks { 107 if !api.AllocatedStatus(task.Status) && task.Status != api.Releasing { 108 continue 109 } 110 111 pl.Tasks[task.UID] = task 112 pod := pl.copyTaskPod(task) 113 pl.CachedPods[task.UID] = pod 114 if HaveAffinity(task.Pod) { 115 pl.TaskWithAffinity[task.UID] = task 116 } 117 } 118 } 119 120 return pl 121 } 122 123 func (pl *PodLister) copyTaskPod(task *api.TaskInfo) *v1.Pod { 124 pod := task.Pod.DeepCopy() 125 pod.Spec.NodeName = task.NodeName 126 return pod 127 } 128 129 // GetPod will get pod with proper nodeName, from cache or DeepCopy 130 // keeping this function read only to avoid concurrent panic of map 131 func (pl *PodLister) GetPod(task *api.TaskInfo) *v1.Pod { 132 if task.NodeName == task.Pod.Spec.NodeName { 133 return task.Pod 134 } 135 136 pod, found := pl.CachedPods[task.UID] 137 if !found { 138 // we could not write the copied pod back into cache for read only 139 pod = pl.copyTaskPod(task) 140 klog.Warningf("DeepCopy for pod %s/%s at PodLister.GetPod is unexpected", pod.Namespace, pod.Name) 141 } 142 return pod 143 } 144 145 // UpdateTask will update the pod nodeName in cache using nodeName 146 // NOT thread safe, please ensure UpdateTask is the only called function of PodLister at the same time. 147 func (pl *PodLister) UpdateTask(task *api.TaskInfo, nodeName string) *v1.Pod { 148 pod, found := pl.CachedPods[task.UID] 149 if !found { 150 pod = pl.copyTaskPod(task) 151 pl.CachedPods[task.UID] = pod 152 } 153 pod.Spec.NodeName = nodeName 154 155 if !api.AllocatedStatus(task.Status) { 156 delete(pl.Tasks, task.UID) 157 if HaveAffinity(task.Pod) { 158 delete(pl.TaskWithAffinity, task.UID) 159 } 160 } else { 161 pl.Tasks[task.UID] = task 162 if HaveAffinity(task.Pod) { 163 pl.TaskWithAffinity[task.UID] = task 164 } 165 } 166 167 return pod 168 } 169 170 // List method is used to list all the pods 171 func (pl *PodLister) List(selector labels.Selector) ([]*v1.Pod, error) { 172 var pods []*v1.Pod 173 for _, task := range pl.Tasks { 174 pod := pl.GetPod(task) 175 if selector.Matches(labels.Set(pod.Labels)) { 176 pods = append(pods, pod) 177 } 178 } 179 180 return pods, nil 181 } 182 183 // FilteredList is used to list all the pods under filter condition 184 func (pl *PodLister) filteredListWithTaskSet(taskSet map[api.TaskID]*api.TaskInfo, podFilter PodFilter, selector labels.Selector) ([]*v1.Pod, error) { 185 var pods []*v1.Pod 186 for _, task := range taskSet { 187 pod := pl.GetPod(task) 188 if podFilter(pod) && selector.Matches(labels.Set(pod.Labels)) { 189 pods = append(pods, pod) 190 } 191 } 192 193 return pods, nil 194 } 195 196 // FilteredList is used to list all the pods under filter condition 197 func (pl *PodLister) FilteredList(podFilter PodFilter, selector labels.Selector) ([]*v1.Pod, error) { 198 return pl.filteredListWithTaskSet(pl.Tasks, podFilter, selector) 199 } 200 201 // AffinityFilteredList is used to list all the pods with affinity under filter condition 202 func (pl *PodLister) AffinityFilteredList(podFilter PodFilter, selector labels.Selector) ([]*v1.Pod, error) { 203 return pl.filteredListWithTaskSet(pl.TaskWithAffinity, podFilter, selector) 204 } 205 206 // AffinityLister generate a PodAffinityLister following current PodLister 207 func (pl *PodLister) AffinityLister() *PodAffinityLister { 208 pal := &PodAffinityLister{ 209 pl: pl, 210 } 211 return pal 212 } 213 214 // List method is used to list all the pods 215 func (pal *PodAffinityLister) List(selector labels.Selector) ([]*v1.Pod, error) { 216 return pal.pl.List(selector) 217 } 218 219 // FilteredList is used to list all the pods with affinity under filter condition 220 func (pal *PodAffinityLister) FilteredList(podFilter PodFilter, selector labels.Selector) ([]*v1.Pod, error) { 221 return pal.pl.AffinityFilteredList(podFilter, selector) 222 } 223 224 // GenerateNodeMapAndSlice returns the nodeMap and nodeSlice generated from ssn 225 func GenerateNodeMapAndSlice(nodes map[string]*api.NodeInfo) map[string]*k8sframework.NodeInfo { 226 nodeMap := make(map[string]*k8sframework.NodeInfo) 227 for _, node := range nodes { 228 nodeInfo := k8sframework.NewNodeInfo(node.Pods()...) 229 nodeInfo.SetNode(node.Node) 230 nodeMap[node.Name] = nodeInfo 231 // add imagestate into nodeinfo 232 nodeMap[node.Name].ImageStates = node.CloneImageSummary() 233 } 234 return nodeMap 235 } 236 237 // CachedNodeInfo is used in nodeorder and predicate plugin 238 type CachedNodeInfo struct { 239 Session *Session 240 } 241 242 // GetNodeInfo is used to get info of a particular node 243 func (c *CachedNodeInfo) GetNodeInfo(name string) (*v1.Node, error) { 244 node, found := c.Session.Nodes[name] 245 if !found { 246 return nil, errors.NewNotFound(v1.Resource("node"), name) 247 } 248 249 return node.Node, nil 250 } 251 252 // NodeLister is used in nodeorder plugin 253 type NodeLister struct { 254 Session *Session 255 } 256 257 // List is used to list all the nodes 258 func (nl *NodeLister) List() ([]*v1.Node, error) { 259 var nodes []*v1.Node 260 for _, node := range nl.Session.Nodes { 261 nodes = append(nodes, node.Node) 262 } 263 return nodes, nil 264 } 265 266 // ConvertPredicateStatus return predicate status from k8sframework status 267 func ConvertPredicateStatus(status *k8sframework.Status) *api.Status { 268 internalStatus := &api.Status{} 269 if status.Code() == k8sframework.Success { 270 internalStatus.Code = api.Success 271 return internalStatus 272 } else if status.Code() == k8sframework.Unschedulable { 273 internalStatus.Code = api.Unschedulable 274 internalStatus.Reason = status.Message() 275 return internalStatus 276 } else if status.Code() == k8sframework.UnschedulableAndUnresolvable { 277 internalStatus.Code = api.UnschedulableAndUnresolvable 278 internalStatus.Reason = status.Message() 279 return internalStatus 280 } else { 281 internalStatus.Code = api.Error 282 internalStatus.Reason = status.Message() 283 return internalStatus 284 } 285 }