blob: e23662f3bcd1cf6e791be20ba33eda7c91342cae [file] [log] [blame]
mbligh67647152008-11-19 00:18:14 +00001# Copyright Martin J. Bligh, Google Inc 2008
2# Released under the GPL v2
3
4"""
5This class allows you to communicate with the frontend to submit jobs etc
6It is designed for writing more sophisiticated server-side control files that
7can recursively add and manage other jobs.
8
9We turn the JSON dictionaries into real objects that are more idiomatic
10
mblighc31e4022008-12-11 19:32:30 +000011For docs, see:
12 http://autotest/afe/server/noauth/rpc/
13 http://autotest/new_tko/server/noauth/rpc/
14 http://docs.djangoproject.com/en/dev/ref/models/querysets/#queryset-api
mbligh67647152008-11-19 00:18:14 +000015"""
16
mbligh1f23f362008-12-22 14:46:12 +000017import os, time, traceback, re
mbligh67647152008-11-19 00:18:14 +000018import common
19from autotest_lib.frontend.afe import rpc_client_lib
mbligh37eceaa2008-12-15 22:56:37 +000020from autotest_lib.client.common_lib import global_config
mbligh67647152008-11-19 00:18:14 +000021from autotest_lib.client.common_lib import utils
mbligh4e576612008-12-22 14:56:36 +000022try:
23 from autotest_lib.server.site_common import site_utils as server_utils
24except:
25 from autotest_lib.server import utils as server_utils
26form_ntuples_from_machines = server_utils.form_ntuples_from_machines
mbligh67647152008-11-19 00:18:14 +000027
mbligh37eceaa2008-12-15 22:56:37 +000028GLOBAL_CONFIG = global_config.global_config
29DEFAULT_SERVER = 'autotest'
30
mbligh67647152008-11-19 00:18:14 +000031
32def dump_object(header, obj):
33 """
34 Standard way to print out the frontend objects (eg job, host, acl, label)
35 in a human-readable fashion for debugging
36 """
37 result = header + '\n'
38 for key in obj.hash:
39 if key == 'afe' or key == 'hash':
40 continue
41 result += '%20s: %s\n' % (key, obj.hash[key])
42 return result
43
44
mbligh5280e3b2008-12-22 14:39:28 +000045class RpcClient(object):
mbligh67647152008-11-19 00:18:14 +000046 """
47 AFE class for communicating with the autotest frontend
48
49 All the constructors go in the afe class.
50 Manipulating methods go in the classes themselves
51 """
mblighc31e4022008-12-11 19:32:30 +000052 def __init__(self, path, user, web_server, print_log, debug):
mbligh67647152008-11-19 00:18:14 +000053 """
54 Create a cached instance of a connection to the AFE
55
56 user: username to connect as
57 web_server: AFE instance to connect to
58 print_log: pring a logging message to stdout on every operation
59 debug: print out all RPC traffic
60 """
mblighc31e4022008-12-11 19:32:30 +000061 if not user:
62 user = os.environ.get('LOGNAME')
63 if not web_server:
mbligh37eceaa2008-12-15 22:56:37 +000064 web_server = 'http://' + GLOBAL_CONFIG.get_config_value(
65 'SERVER', 'hostname', default=DEFAULT_SERVER)
mbligh67647152008-11-19 00:18:14 +000066 self.user = user
67 self.print_log = print_log
68 self.debug = debug
69 headers = {'AUTHORIZATION' : self.user}
mblighc31e4022008-12-11 19:32:30 +000070 rpc_server = web_server + path
mbligh1354c9d2008-12-22 14:56:13 +000071 if debug:
72 print 'SERVER: %s' % rpc_server
73 print 'HEADERS: %s' % headers
mbligh67647152008-11-19 00:18:14 +000074 self.proxy = rpc_client_lib.get_proxy(rpc_server, headers=headers)
75
76
77 def run(self, call, **dargs):
78 """
79 Make a RPC call to the AFE server
80 """
81 rpc_call = getattr(self.proxy, call)
82 if self.debug:
83 print 'DEBUG: %s %s' % (call, dargs)
84 return utils.strip_unicode(rpc_call(**dargs))
85
86
87 def log(self, message):
88 if self.print_log:
89 print message
90
91
mbligh5280e3b2008-12-22 14:39:28 +000092class TKO(RpcClient):
mblighc31e4022008-12-11 19:32:30 +000093 def __init__(self, user=None, web_server=None, print_log=True, debug=False):
mbligh5280e3b2008-12-22 14:39:28 +000094 super(TKO, self).__init__('/new_tko/server/noauth/rpc/', user,
mblighc31e4022008-12-11 19:32:30 +000095 web_server, print_log, debug)
96
97
98 def get_status_counts(self, job, **data):
99 entries = self.run('get_status_counts',
100 group_by=['hostname', 'test_name'],
101 job_tag__startswith='%s-' % job, **data)
mbligh5280e3b2008-12-22 14:39:28 +0000102 return [TestStatus(self, e) for e in entries['groups']]
mblighc31e4022008-12-11 19:32:30 +0000103
104
mbligh5280e3b2008-12-22 14:39:28 +0000105class AFE(RpcClient):
mblighc31e4022008-12-11 19:32:30 +0000106 def __init__(self, user=None, web_server=None, print_log=True, debug=False):
mbligh5280e3b2008-12-22 14:39:28 +0000107 super(AFE, self).__init__('/afe/server/noauth/rpc/', user, web_server,
mblighc31e4022008-12-11 19:32:30 +0000108 print_log, debug)
109
110
mbligh67647152008-11-19 00:18:14 +0000111 def host_statuses(self, live=None):
112 dead_statuses = ['Dead', 'Repair Failed']
113 statuses = self.run('get_static_data')['host_statuses']
114 if live == True:
115 return list(set(statuses) - set(['Dead', 'Repair Failed']))
116 if live == False:
117 return dead_statuses
118 else:
119 return statuses
120
121
122 def get_hosts(self, **dargs):
123 hosts = self.run('get_hosts', **dargs)
mbligh5280e3b2008-12-22 14:39:28 +0000124 return [Host(self, h) for h in hosts]
mbligh67647152008-11-19 00:18:14 +0000125
126
127 def create_host(self, hostname, **dargs):
128 id = self.run('add_host', **dargs)
129 return self.get_hosts(id=id)[0]
130
131
132 def get_labels(self, **dargs):
133 labels = self.run('get_labels', **dargs)
mbligh5280e3b2008-12-22 14:39:28 +0000134 return [Label(self, l) for l in labels]
mbligh67647152008-11-19 00:18:14 +0000135
136
137 def create_label(self, name, **dargs):
138 id = self.run('add_label', **dargs)
139 return self.get_labels(id=id)[0]
140
141
142 def get_acls(self, **dargs):
143 acls = self.run('get_acl_groups', **dargs)
mbligh5280e3b2008-12-22 14:39:28 +0000144 return [Acl(self, a) for a in acls]
mbligh67647152008-11-19 00:18:14 +0000145
146
147 def create_acl(self, name, **dargs):
148 id = self.run('add_acl_group', **dargs)
149 return self.get_acls(id=id)[0]
150
151
mbligh1354c9d2008-12-22 14:56:13 +0000152 def generate_control_file(self, tests, **dargs):
153 ret = self.run('generate_control_file', tests=tests, **dargs)
154 return ControlFile(self, ret)
155
156
mbligh67647152008-11-19 00:18:14 +0000157 def get_jobs(self, summary=False, **dargs):
158 if summary:
159 jobs_data = self.run('get_jobs_summary', **dargs)
160 else:
161 jobs_data = self.run('get_jobs', **dargs)
mbligh5280e3b2008-12-22 14:39:28 +0000162 return [Job(self, j) for j in jobs_data]
mbligh67647152008-11-19 00:18:14 +0000163
164
165 def get_host_queue_entries(self, **data):
166 entries = self.run('get_host_queue_entries', **data)
mbligh5280e3b2008-12-22 14:39:28 +0000167 return [JobStatus(self, e) for e in entries]
mbligh67647152008-11-19 00:18:14 +0000168
169
mbligh4e576612008-12-22 14:56:36 +0000170 def create_job_by_test(self, tests, hosts, kernel=None, use_container=False,
mbligh1354c9d2008-12-22 14:56:13 +0000171 **dargs):
mbligh67647152008-11-19 00:18:14 +0000172 """
173 Given a test name, fetch the appropriate control file from the server
mbligh4e576612008-12-22 14:56:36 +0000174 and submit it.
175
176 Returns a list of job objects
mbligh67647152008-11-19 00:18:14 +0000177 """
mbligh1354c9d2008-12-22 14:56:13 +0000178 control_file = self.generate_control_file(tests=tests, kernel=kernel,
179 use_container=use_container,
180 do_push_packages=True)
181 if control_file.is_server:
mbligh67647152008-11-19 00:18:14 +0000182 dargs['control_type'] = 'Server'
183 else:
184 dargs['control_type'] = 'Client'
185 dargs['dependencies'] = dargs.get('dependencies', []) + \
mbligh1354c9d2008-12-22 14:56:13 +0000186 control_file.dependencies
187 dargs['control_file'] = control_file.control_file
188 dargs['synch_count'] = control_file.synch_count
mbligh4e576612008-12-22 14:56:36 +0000189 jobs = []
190 if control_file.synch_count > 1:
191 # We don't trust the scheduler to do the groupings for us.
192 synch_count = control_file.synch_count
193 (pairs, failures) = form_ntuples_from_machines(hosts, synch_count)
194 for machines in pairs:
195 jobs.append(self.create_job(hosts=machines, **dargs))
196 else:
197 jobs.append(self.create_job(hosts=hosts, **dargs))
198 return jobs
mbligh67647152008-11-19 00:18:14 +0000199
200
201 def create_job(self, control_file, name=' ', priority='Medium',
202 control_type='Client', **dargs):
203 id = self.run('create_job', name=name, priority=priority,
204 control_file=control_file, control_type=control_type, **dargs)
205 return self.get_jobs(id=id)[0]
206
207
mbligh1f23f362008-12-22 14:46:12 +0000208 def run_test_suites(self, pairings, kernel, kernel_label, priority='Medium',
209 wait=True, poll_interval=5, email_from=None,
mbligh7b312282009-01-07 16:45:43 +0000210 email_to=None, timeout=168):
mbligh5b618382008-12-03 15:24:01 +0000211 """
212 Run a list of test suites on a particular kernel.
213
214 Poll for them to complete, and return whether they worked or not.
215
216 pairings: list of MachineTestPairing objects to invoke
217 kernel: name of the kernel to run
218 kernel_label: label of the kernel to run
219 (<kernel-version> : <config> : <date>)
220 wait: boolean - wait for the results to come back?
221 poll_interval: interval between polling for job results (in minutes)
mbligh45ffc432008-12-09 23:35:17 +0000222 email_from: send notification email upon completion from here
223 email_from: send notification email upon completion to here
mbligh5b618382008-12-03 15:24:01 +0000224 """
225 jobs = []
226 for pairing in pairings:
mbligh7b312282009-01-07 16:45:43 +0000227 new_jobs = self.invoke_test(pairing, kernel, kernel_label, priority,
228 timeout=timeout)
mbligh4e576612008-12-22 14:56:36 +0000229 for job in new_jobs:
230 job.notified = False
231 jobs += new_jobs
mbligh5280e3b2008-12-22 14:39:28 +0000232 # disabled - this is just for debugging: mbligh
233 # if email_from and email_to:
234 # subject = 'Testing started: %s : %s' % (job.name, job.id)
235 # utils.send_email(email_from, email_to, subject, subject)
mbligh5b618382008-12-03 15:24:01 +0000236 if not wait:
237 return
mbligh5280e3b2008-12-22 14:39:28 +0000238 tko = TKO()
mbligh5b618382008-12-03 15:24:01 +0000239 while True:
240 time.sleep(60 * poll_interval)
mbligh5280e3b2008-12-22 14:39:28 +0000241 result = self.poll_all_jobs(tko, jobs, email_from, email_to)
mbligh5b618382008-12-03 15:24:01 +0000242 if result is not None:
243 return result
244
245
mbligh45ffc432008-12-09 23:35:17 +0000246 def result_notify(self, job, email_from, email_to):
mbligh5b618382008-12-03 15:24:01 +0000247 """
mbligh45ffc432008-12-09 23:35:17 +0000248 Notify about the result of a job. Will always print, if email data
249 is provided, will send email for it as well.
250
251 job: job object to notify about
252 email_from: send notification email upon completion from here
253 email_from: send notification email upon completion to here
254 """
255 if job.result == True:
256 subject = 'Testing PASSED: '
257 else:
258 subject = 'Testing FAILED: '
259 subject += '%s : %s\n' % (job.name, job.id)
260 text = []
261 for platform in job.results_platform_map:
262 for status in job.results_platform_map[platform]:
263 if status == 'Total':
264 continue
265 hosts = ','.join(job.results_platform_map[platform][status])
mbligh37eceaa2008-12-15 22:56:37 +0000266 text.append('%20s %10s %s\n' % (platform, status, hosts))
267
268 tko_base_url = 'http://%s/tko' % GLOBAL_CONFIG.get_config_value(
269 'SERVER', 'hostname', default=DEFAULT_SERVER)
270
271 params = ('columns=test',
272 'rows=machine_group',
273 "condition=tag~'%s-%%25'" % job.id,
274 'title=Report')
275 query_string = '&'.join(params)
276 url = '%s/compose_query.cgi?%s' % (tko_base_url, query_string)
277 text.append('\n')
278 text.append(url)
279
280 body = '\n'.join(text)
281 print '---------------------------------------------------'
282 print 'Subject: ', subject
mbligh45ffc432008-12-09 23:35:17 +0000283 print body
mbligh37eceaa2008-12-15 22:56:37 +0000284 print '---------------------------------------------------'
mbligh45ffc432008-12-09 23:35:17 +0000285 if email_from and email_to:
mbligh37eceaa2008-12-15 22:56:37 +0000286 print 'Sending email ...'
mbligh45ffc432008-12-09 23:35:17 +0000287 utils.send_email(email_from, email_to, subject, body)
288 print
mbligh37eceaa2008-12-15 22:56:37 +0000289
mbligh45ffc432008-12-09 23:35:17 +0000290
mbligh1354c9d2008-12-22 14:56:13 +0000291 def print_job_result(self, job):
292 """
293 Print the result of a single job.
294 job: a job object
295 """
296 if job.result is None:
297 print 'PENDING',
298 elif job.result == True:
299 print 'PASSED',
300 elif job.result == False:
301 print 'FAILED',
302 print ' %s : %s' % (job.id, job.name)
303
304
mbligh5280e3b2008-12-22 14:39:28 +0000305 def poll_all_jobs(self, tko, jobs, email_from, email_to):
mbligh45ffc432008-12-09 23:35:17 +0000306 """
307 Poll all jobs in a list.
308 jobs: list of job objects to poll
309 email_from: send notification email upon completion from here
310 email_from: send notification email upon completion to here
311
312 Returns:
mbligh5b618382008-12-03 15:24:01 +0000313 a) All complete successfully (return True)
314 b) One or more has failed (return False)
315 c) Cannot tell yet (return None)
316 """
mbligh45ffc432008-12-09 23:35:17 +0000317 results = []
mbligh5b618382008-12-03 15:24:01 +0000318 for job in jobs:
mbligh5280e3b2008-12-22 14:39:28 +0000319 job.result = self.poll_job_results(tko, job, debug=False)
mbligh45ffc432008-12-09 23:35:17 +0000320 results.append(job.result)
321 if job.result is not None and not job.notified:
322 self.result_notify(job, email_from, email_to)
323 job.notified = True
324
mbligh1354c9d2008-12-22 14:56:13 +0000325 self.print_job_result(job)
mbligh45ffc432008-12-09 23:35:17 +0000326
327 if None in results:
328 return None
329 elif False in results:
330 return False
331 else:
332 return True
mbligh5b618382008-12-03 15:24:01 +0000333
334
mbligh1f23f362008-12-22 14:46:12 +0000335 def _included_platform(self, host, platforms):
336 """
337 See if host's platforms matches any of the patterns in the included
338 platforms list.
339 """
340 if not platforms:
341 return True # No filtering of platforms
342 for platform in platforms:
343 if re.search(platform, host.platform):
344 return True
345 return False
346
347
mbligh7b312282009-01-07 16:45:43 +0000348 def invoke_test(self, pairing, kernel, kernel_label, priority='Medium',
349 **dargs):
mbligh5b618382008-12-03 15:24:01 +0000350 """
351 Given a pairing of a control file to a machine label, find all machines
352 with that label, and submit that control file to them.
353
mbligh4e576612008-12-22 14:56:36 +0000354 Returns a list of job objects
mbligh5b618382008-12-03 15:24:01 +0000355 """
356 job_name = '%s : %s' % (pairing.machine_label, kernel_label)
357 hosts = self.get_hosts(multiple_labels=[pairing.machine_label])
mbligh1f23f362008-12-22 14:46:12 +0000358 platforms = pairing.platforms
359 hosts = [h for h in hosts if self._included_platform(h, platforms)]
mbligh45ffc432008-12-09 23:35:17 +0000360 host_list = [h.hostname for h in hosts if h.status != 'Repair Failed']
mbligh1f23f362008-12-22 14:46:12 +0000361 print 'HOSTS: %s' % host_list
mbligh4e576612008-12-22 14:56:36 +0000362 new_jobs = self.create_job_by_test(name=job_name,
mbligh7b312282009-01-07 16:45:43 +0000363 dependencies=[pairing.machine_label],
364 tests=[pairing.control_file],
365 priority=priority,
366 hosts=host_list,
367 kernel=kernel,
368 use_container=pairing.container,
369 **dargs)
mbligh4e576612008-12-22 14:56:36 +0000370 for new_job in new_jobs:
371 print 'Invoked test %s : %s' % (new_job.id, job_name)
372 return new_jobs
mbligh5b618382008-12-03 15:24:01 +0000373
374
mbligh5280e3b2008-12-22 14:39:28 +0000375 def _job_test_results(self, tko, job):
mbligh5b618382008-12-03 15:24:01 +0000376 """
mbligh5280e3b2008-12-22 14:39:28 +0000377 Retrieve test results for a job
mbligh5b618382008-12-03 15:24:01 +0000378 """
mbligh5280e3b2008-12-22 14:39:28 +0000379 job.test_status = {}
380 try:
381 test_statuses = tko.get_status_counts(job=job.id)
382 except Exception:
383 print "Ignoring exception on poll job; RPC interface is flaky"
384 traceback.print_exc()
385 return
386
387 for test_status in test_statuses:
388 hostname = test_status.hostname
389 if hostname not in job.test_status:
390 job.test_status[hostname] = TestResults()
391 job.test_status[hostname].add(test_status)
392
393
394 def _job_results_platform_map(self, job):
395 job.results_platform_map = {}
mbligh5b618382008-12-03 15:24:01 +0000396 try:
mbligh45ffc432008-12-09 23:35:17 +0000397 job_statuses = self.get_host_queue_entries(job=job.id)
mbligh5b618382008-12-03 15:24:01 +0000398 except Exception:
399 print "Ignoring exception on poll job; RPC interface is flaky"
400 traceback.print_exc()
401 return None
mbligh5280e3b2008-12-22 14:39:28 +0000402
mbligh5b618382008-12-03 15:24:01 +0000403 platform_map = {}
mbligh5280e3b2008-12-22 14:39:28 +0000404 job.job_status = {}
mbligh5b618382008-12-03 15:24:01 +0000405 for job_status in job_statuses:
406 hostname = job_status.host.hostname
mbligh5280e3b2008-12-22 14:39:28 +0000407 job.job_status[hostname] = job_status.status
mbligh5b618382008-12-03 15:24:01 +0000408 status = job_status.status
mbligh5280e3b2008-12-22 14:39:28 +0000409 if hostname in job.test_status and job.test_status[hostname].fail:
410 # Job status doesn't reflect failed tests, override that
411 status = 'Failed'
mbligh5b618382008-12-03 15:24:01 +0000412 platform = job_status.host.platform
413 if platform not in platform_map:
414 platform_map[platform] = {'Total' : [hostname]}
415 else:
416 platform_map[platform]['Total'].append(hostname)
417 new_host_list = platform_map[platform].get(status, []) + [hostname]
418 platform_map[platform][status] = new_host_list
mbligh45ffc432008-12-09 23:35:17 +0000419 job.results_platform_map = platform_map
mbligh5280e3b2008-12-22 14:39:28 +0000420
421
422 def poll_job_results(self, tko, job, debug=False):
423 """
424 Analyse all job results by platform, return:
mbligh5b618382008-12-03 15:24:01 +0000425
mbligh5280e3b2008-12-22 14:39:28 +0000426 False: if any platform has more than one failure
427 None: if any platform has more than one machine not yet Good.
428 True: if all platforms have at least all-but-one machines Good.
429 """
430 self._job_test_results(tko, job)
431 self._job_results_platform_map(job)
432
mbligh5b618382008-12-03 15:24:01 +0000433 good_platforms = []
434 bad_platforms = []
435 unknown_platforms = []
mbligh5280e3b2008-12-22 14:39:28 +0000436 platform_map = job.results_platform_map
mbligh5b618382008-12-03 15:24:01 +0000437 for platform in platform_map:
438 total = len(platform_map[platform]['Total'])
439 completed = len(platform_map[platform].get('Completed', []))
440 failed = len(platform_map[platform].get('Failed', []))
441 if failed > 1:
442 bad_platforms.append(platform)
443 elif completed + 1 >= total:
444 # if all or all but one are good, call the job good.
445 good_platforms.append(platform)
446 else:
447 unknown_platforms.append(platform)
448 detail = []
449 for status in platform_map[platform]:
450 if status == 'Total':
451 continue
452 detail.append('%s=%s' % (status,platform_map[platform][status]))
453 if debug:
454 print '%20s %d/%d %s' % (platform, completed, total,
455 ' '.join(detail))
456 print
457
458 if len(bad_platforms) > 0:
459 if debug:
460 print 'Result bad - platforms: ' + ' '.join(bad_platforms)
461 return False
462 if len(unknown_platforms) > 0:
463 if debug:
464 platform_list = ' '.join(unknown_platforms)
465 print 'Result unknown - platforms: ', platform_list
466 return None
467 if debug:
468 platform_list = ' '.join(good_platforms)
469 print 'Result good - all platforms passed: ', platform_list
470 return True
471
472
mbligh5280e3b2008-12-22 14:39:28 +0000473class TestResults(object):
474 """
475 Container class used to hold the results of the tests for a job
476 """
477 def __init__(self):
478 self.good = []
479 self.fail = []
480
481
482 def add(self, result):
483 if result.complete_count - result.pass_count > 0:
484 self.fail.append(result.test_name)
485 else:
486 self.good.append(result.test_name)
487
488
489class RpcObject(object):
mbligh67647152008-11-19 00:18:14 +0000490 """
491 Generic object used to construct python objects from rpc calls
492 """
493 def __init__(self, afe, hash):
494 self.afe = afe
495 self.hash = hash
496 self.__dict__.update(hash)
497
498
499 def __str__(self):
500 return dump_object(self.__repr__(), self)
501
502
mbligh1354c9d2008-12-22 14:56:13 +0000503class ControlFile(RpcObject):
504 """
505 AFE control file object
506
507 Fields: synch_count, dependencies, control_file, is_server
508 """
509 def __repr__(self):
510 return 'CONTROL FILE: %s' % self.control_file
511
512
mbligh5280e3b2008-12-22 14:39:28 +0000513class Label(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000514 """
515 AFE label object
516
517 Fields:
518 name, invalid, platform, kernel_config, id, only_if_needed
519 """
520 def __repr__(self):
521 return 'LABEL: %s' % self.name
522
523
524 def add_hosts(self, hosts):
525 return self.afe.run('label_add_hosts', self.id, hosts)
526
527
528 def remove_hosts(self, hosts):
529 return self.afe.run('label_remove_hosts', self.id, hosts)
530
531
mbligh5280e3b2008-12-22 14:39:28 +0000532class Acl(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000533 """
534 AFE acl object
535
536 Fields:
537 users, hosts, description, name, id
538 """
539 def __repr__(self):
540 return 'ACL: %s' % self.name
541
542
543 def add_hosts(self, hosts):
544 self.afe.log('Adding hosts %s to ACL %s' % (hosts, self.name))
545 return self.afe.run('acl_group_add_hosts', self.id, hosts)
546
547
548 def remove_hosts(self, hosts):
549 self.afe.log('Removing hosts %s from ACL %s' % (hosts, self.name))
550 return self.afe.run('acl_group_remove_hosts', self.id, hosts)
551
552
mbligh5280e3b2008-12-22 14:39:28 +0000553class Job(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000554 """
555 AFE job object
556
557 Fields:
558 name, control_file, control_type, synch_count, reboot_before,
559 run_verify, priority, email_list, created_on, dependencies,
560 timeout, owner, reboot_after, id
561 """
562 def __repr__(self):
563 return 'JOB: %s' % self.id
564
565
mbligh5280e3b2008-12-22 14:39:28 +0000566class JobStatus(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000567 """
568 AFE job_status object
569
570 Fields:
571 status, complete, deleted, meta_host, host, active, execution_subdir, id
572 """
573 def __init__(self, afe, hash):
574 # This should call super
575 self.afe = afe
576 self.hash = hash
577 self.__dict__.update(hash)
mbligh5280e3b2008-12-22 14:39:28 +0000578 self.job = Job(afe, self.job)
mbligh67647152008-11-19 00:18:14 +0000579 if self.host:
580 self.host = afe.get_hosts(hostname=self.host['hostname'])[0]
581
582
583 def __repr__(self):
584 return 'JOB STATUS: %s-%s' % (self.job.id, self.host.hostname)
585
586
mbligh5280e3b2008-12-22 14:39:28 +0000587class Host(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000588 """
589 AFE host object
590
591 Fields:
592 status, lock_time, locked_by, locked, hostname, invalid,
593 synch_id, labels, platform, protection, dirty, id
594 """
595 def __repr__(self):
596 return 'HOST OBJECT: %s' % self.hostname
597
598
599 def show(self):
600 labels = list(set(self.labels) - set([self.platform]))
601 print '%-6s %-7s %-7s %-16s %s' % (self.hostname, self.status,
602 self.locked, self.platform,
603 ', '.join(labels))
604
605
606 def get_acls(self):
607 return self.afe.get_acls(hosts__hostname=self.hostname)
608
609
610 def add_acl(self, acl_name):
611 self.afe.log('Adding ACL %s to host %s' % (acl_name, self.hostname))
612 return self.afe.run('acl_group_add_hosts', id=acl_name,
613 hosts=[self.hostname])
614
615
616 def remove_acl(self, acl_name):
617 self.afe.log('Removing ACL %s from host %s' % (acl_name, self.hostname))
618 return self.afe.run('acl_group_remove_hosts', id=acl_name,
619 hosts=[self.hostname])
620
621
622 def get_labels(self):
623 return self.afe.get_labels(host__hostname__in=[self.hostname])
624
625
626 def add_labels(self, labels):
627 self.afe.log('Adding labels %s to host %s' % (labels, self.hostname))
628 return self.afe.run('host_add_labels', id=self.id, labels=labels)
629
630
631 def remove_labels(self, labels):
632 self.afe.log('Removing labels %s from host %s' % (labels,self.hostname))
633 return self.afe.run('host_remove_labels', id=self.id, labels=labels)
mbligh5b618382008-12-03 15:24:01 +0000634
635
mbligh5280e3b2008-12-22 14:39:28 +0000636class TestStatus(RpcObject):
mblighc31e4022008-12-11 19:32:30 +0000637 """
638 TKO test status object
639
640 Fields:
641 test_idx, hostname, testname, id
642 complete_count, incomplete_count, group_count, pass_count
643 """
644 def __repr__(self):
645 return 'TEST STATUS: %s' % self.id
646
647
mbligh5b618382008-12-03 15:24:01 +0000648class MachineTestPairing(object):
649 """
650 Object representing the pairing of a machine label with a control file
mbligh1f23f362008-12-22 14:46:12 +0000651
652 machine_label: use machines from this label
653 control_file: use this control file (by name in the frontend)
654 platforms: list of rexeps to filter platforms by. [] => no filtering
mbligh5b618382008-12-03 15:24:01 +0000655 """
mbligh1354c9d2008-12-22 14:56:13 +0000656 def __init__(self, machine_label, control_file, platforms=[],
657 container=False):
mbligh5b618382008-12-03 15:24:01 +0000658 self.machine_label = machine_label
659 self.control_file = control_file
mbligh1f23f362008-12-22 14:46:12 +0000660 self.platforms = platforms
mbligh1354c9d2008-12-22 14:56:13 +0000661 self.container = container
662
663
664 def __repr__(self):
665 return '%s %s %s %s' % (self.machine_label, self.control_file,
666 self.platforms, self.container)