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  }