volcano.sh/volcano@v1.9.0/pkg/scheduler/plugins/sla/sla.go (about) 1 /* 2 Copyright 2021 The Volcano 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 sla 18 19 import ( 20 "time" 21 22 "k8s.io/klog/v2" 23 24 "volcano.sh/volcano/pkg/scheduler/api" 25 "volcano.sh/volcano/pkg/scheduler/framework" 26 "volcano.sh/volcano/pkg/scheduler/plugins/util" 27 ) 28 29 const ( 30 // PluginName indicates name of volcano scheduler plugin 31 PluginName = "sla" 32 // JobWaitingTime is maximum waiting time that a job could stay Pending in service level agreement 33 // when job waits longer than waiting time, it should be inqueue at once, and cluster should reserve resources for it 34 // Valid time units are “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h” 35 JobWaitingTime = "sla-waiting-time" 36 ) 37 38 type slaPlugin struct { 39 // Arguments given for sla plugin 40 pluginArguments framework.Arguments 41 jobWaitingTime *time.Duration 42 } 43 44 // New function returns sla plugin object 45 func New(arguments framework.Arguments) framework.Plugin { 46 return &slaPlugin{ 47 pluginArguments: arguments, 48 jobWaitingTime: nil, 49 } 50 } 51 52 func (sp *slaPlugin) Name() string { 53 return PluginName 54 } 55 56 // readJobWaitingTime read job waiting time from jobInfo or sla plugin arguments 57 // Valid time units are “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h” 58 func (sp *slaPlugin) readJobWaitingTime(jwt *time.Duration) *time.Duration { 59 // read individual jobInfo waiting time from jobInfos 60 if jwt == nil { 61 // if no individual settings, read global jobInfo waiting time from sla plugin arguments 62 return sp.jobWaitingTime 63 } 64 return jwt 65 } 66 67 /* 68 User should give global job waiting time settings via sla plugin arguments: 69 actions: "enqueue, allocate, backfill" 70 tiers: 71 - plugins: 72 - name: sla 73 arguments: 74 sla-waiting-time: 1h2m3s4ms5µs6ns 75 76 Meanwhile, use can give individual job waiting time settings for one job via job annotations: 77 apiVersion: batch.volcano.sh/v1alpha1 78 kind: Job 79 metadata: 80 81 annotations: 82 sla-waiting-time: 1h2m3s4ms5us6ns 83 */ 84 func (sp *slaPlugin) OnSessionOpen(ssn *framework.Session) { 85 klog.V(4).Infof("Enter sla plugin ...") 86 defer klog.V(4).Infof("Leaving sla plugin.") 87 88 // read in sla waiting time for global cluster from sla plugin arguments 89 // if not set, job waiting time still can set in job yaml separately, otherwise job have no sla limits 90 if _, exist := sp.pluginArguments[JobWaitingTime]; exist { 91 waitTime, ok := sp.pluginArguments[JobWaitingTime].(string) 92 if !ok { 93 waitTime = "" 94 } 95 jwt, err := time.ParseDuration(waitTime) 96 if err != nil { 97 klog.Errorf("Error occurs in parsing global job waiting time in sla plugin, err: %s.", err.Error()) 98 } 99 100 if jwt <= 0 { 101 klog.Warningf("Invalid global waiting time setting: %s in sla plugin.", jwt.String()) 102 } else { 103 sp.jobWaitingTime = &jwt 104 klog.V(4).Infof("Global job waiting time is %s.", sp.jobWaitingTime.String()) 105 } 106 } 107 108 jobOrderFn := func(l, r interface{}) int { 109 lv := l.(*api.JobInfo) 110 rv := r.(*api.JobInfo) 111 112 var lJobWaitingTime = sp.readJobWaitingTime(lv.WaitingTime) 113 var rJobWaitingTime = sp.readJobWaitingTime(rv.WaitingTime) 114 115 if lJobWaitingTime == nil { 116 if rJobWaitingTime == nil { 117 return 0 118 } 119 return 1 120 } 121 if rJobWaitingTime == nil { 122 return -1 123 } 124 125 lCreationTimestamp := lv.CreationTimestamp 126 rCreationTimestamp := rv.CreationTimestamp 127 if lCreationTimestamp.Add(*lJobWaitingTime).Before(rCreationTimestamp.Add(*rJobWaitingTime)) { 128 return -1 129 } else if lCreationTimestamp.Add(*lJobWaitingTime).After(rCreationTimestamp.Add(*rJobWaitingTime)) { 130 return 1 131 } 132 return 0 133 } 134 ssn.AddJobOrderFn(sp.Name(), jobOrderFn) 135 136 permitableFn := func(obj interface{}) int { 137 jobInfo := obj.(*api.JobInfo) 138 var jwt = sp.readJobWaitingTime(jobInfo.WaitingTime) 139 140 if jwt == nil { 141 return util.Abstain 142 } 143 144 if time.Since(jobInfo.CreationTimestamp.Time) < *jwt { 145 return util.Abstain 146 } 147 148 return util.Permit 149 } 150 // if job waiting time is over, turn job to be inqueue in enqueue action 151 ssn.AddJobEnqueueableFn(sp.Name(), permitableFn) 152 // if job waiting time is over, turn job to be pipelined in allocate action 153 ssn.AddJobPipelinedFn(sp.Name(), permitableFn) 154 } 155 156 func (sp *slaPlugin) OnSessionClose(ssn *framework.Session) {}