1 # Copyright 2017 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 """Extra functions for frontend.afe.models.Job objects. 6 7 Most of these exist in tightly coupled forms in legacy Autotest code 8 (e.g., part of methods with completely unrelated names on Task objects 9 under multiple layers of abstract classes). These are defined here to 10 sanely reuse without having to commit to a long refactor of legacy code 11 that is getting deleted soon. 12 13 It's not really a good idea to define these on the Job class either; 14 they are specialized and the Job class already suffers from method 15 bloat. 16 """ 17 18 from __future__ import absolute_import 19 from __future__ import division 20 from __future__ import print_function 21 22 import os 23 import time 24 import urllib 25 26 from lucifer import autotest 27 from lucifer import results 28 29 30 def is_hostless(job): 31 """Return True if the job is hostless. 32 33 @param job: frontend.afe.models.Job instance 34 """ 35 return not hostnames(job) 36 37 38 def hostnames(job): 39 """Return a list of hostnames for a job. 40 41 @param job: frontend.afe.models.Job instance 42 """ 43 hqes = job.hostqueueentry_set.all().prefetch_related('host') 44 return [hqe.host.hostname for hqe in hqes if hqe.host is not None] 45 46 47 def is_aborted(job): 48 """Return if the job is aborted. 49 50 (This means the job is marked for abortion; the job can still be 51 running.) 52 53 @param job: frontend.afe.models.Job instance 54 """ 55 return job.hostqueueentry_set.filter(aborted=True).exists() 56 57 58 def is_server_job(job): 59 """Return whether the job is a server job. 60 61 @param job: frontend.afe.models.Job instance 62 """ 63 return not is_client_job(job) 64 65 66 def is_client_job(job): 67 """Return whether the job is a client job. 68 69 If the job is not a client job, it is a server job. 70 71 (In theory a job can be neither. I have no idea what you should do 72 in that case.) 73 74 @param job: frontend.afe.models.Job instance 75 """ 76 CONTROL_TYPE = autotest.load('client.common_lib.control_data').CONTROL_TYPE 77 return CONTROL_TYPE.get_value(job.control_type) == CONTROL_TYPE.CLIENT 78 79 80 def needs_ssp(job): 81 """Return whether the job needs SSP. 82 83 This also looks up the config for jobs that do not have a value 84 specified. 85 86 @param job: frontend.afe.models.Job instance 87 """ 88 return (_ssp_enabled() 89 and is_server_job(job) 90 # None is the same as True. 91 and job.require_ssp != False) 92 93 94 def _ssp_enabled(): 95 """Return whether SSP is enabled in the config.""" 96 global_config = autotest.load('client.common_lib.global_config') 97 return global_config.global_config.get_config_value( 98 'AUTOSERV', 'enable_ssp_container', type=bool, 99 default=True) 100 101 102 def control_file_path(workdir): 103 """Path to control file for a job. 104 105 This makes more sense in the old Autotest drone world. The 106 scheduler has to copy the control file to the drone. It goes to a 107 temporary path `drone_tmp/attach.N`. 108 109 The drone is then able to run `autoserv <args> drone_tmp/attach.N`. 110 111 But in the Lucifer world, we are already running on the drone, so we 112 don't need to rendezvous with a temporary directory through 113 drone_manager first. 114 115 So pick an arbitrary filename to plop into the workdir. autoserv 116 will later copy this back to the standard control/control.srv. 117 """ 118 return os.path.join(workdir, 'lucifer', 'control_attach') 119 120 121 def prepare_control_file(job, workdir): 122 """Prepare control file for a job.""" 123 with open(control_file_path(workdir), 'w') as f: 124 f.write(job.control_file) 125 126 127 def prepare_keyvals_files(job, workdir): 128 """Prepare Autotest keyvals files for a job.""" 129 keyvals = job.keyval_dict() 130 keyvals['job_queued'] = \ 131 int(time.mktime(job.created_on.timetuple())) 132 results.write_keyvals(workdir, keyvals) 133 if is_hostless(job): 134 return 135 for hqe in job.hostqueueentry_set.all().prefetch_related('host'): 136 results.write_host_keyvals( 137 workdir, hqe.host.hostname, _host_keyvals(hqe.host)) 138 139 140 def write_aborted_keyvals_and_status(job, workdir): 141 """Write the keyvals and status for an aborted job.""" 142 aborted_by = 'autotest_system' 143 aborted_on = int(time.time()) 144 for hqe in job.hostqueueentry_set.all(): 145 if not hasattr(hqe, 'abortedhostqueueentry'): 146 continue 147 ahqe = hqe.abortedhostqueueentry 148 aborted_by = ahqe.aborted_by 149 aborted_on = int(time.mktime(ahqe.aborted_on.timetuple())) 150 break 151 results.write_keyvals(workdir, { 152 'aborted_by': aborted_by, 153 'aborted_on': aborted_on, 154 }) 155 results.write_status_comment( 156 workdir, 'Job aborted by %s on %s' % (aborted_by, aborted_on)) 157 158 159 def _host_keyvals(host): 160 """Return keyvals dict for a host. 161 162 @param host: frontend.afe.models.Host instance 163 """ 164 labels = list(_host_labels(host)) 165 platform = None 166 for label in labels: 167 if label.platform: 168 platform = label.name 169 return { 170 'platform': platform, 171 'labels': ','.join(urllib.quote(label.name) for label in labels), 172 } 173 174 175 def _host_labels(host): 176 """Return an iterable of labels for a host. 177 178 @param host: frontend.afe.models.Host instance 179 """ 180 if autotest.load('scheduler.scheduler_models').RESPECT_STATIC_LABELS: 181 return _host_labels_with_static(host) 182 else: 183 return host.labels.all() 184 185 186 def _host_labels_with_static(host): 187 """Return a generator of labels for a host, respecting static labels. 188 189 @param host: frontend.afe.models.Host instance 190 """ 191 models = autotest.load('frontend.afe.models') 192 replaced_label_ids = frozenset(models.ReplacedLabel.objects.all() 193 .values_list('label_id', flat=True)) 194 shadowed_labels = set() 195 for label in host.labels.all(): 196 if label.id in replaced_label_ids: 197 shadowed_labels.add(label.name) 198 else: 199 yield label 200 for label in host.static_labels.all(): 201 if label.name in shadowed_labels: 202 yield label 203