blob: 53306679825d81dbaecea9e7a39e697558d5ee34 [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,
210 email_to=None):
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:
mbligh4e576612008-12-22 14:56:36 +0000227 new_jobs = self.invoke_test(pairing, kernel, kernel_label, priority)
228 for job in new_jobs:
229 job.notified = False
230 jobs += new_jobs
mbligh5280e3b2008-12-22 14:39:28 +0000231 # disabled - this is just for debugging: mbligh
232 # if email_from and email_to:
233 # subject = 'Testing started: %s : %s' % (job.name, job.id)
234 # utils.send_email(email_from, email_to, subject, subject)
mbligh5b618382008-12-03 15:24:01 +0000235 if not wait:
236 return
mbligh5280e3b2008-12-22 14:39:28 +0000237 tko = TKO()
mbligh5b618382008-12-03 15:24:01 +0000238 while True:
239 time.sleep(60 * poll_interval)
mbligh5280e3b2008-12-22 14:39:28 +0000240 result = self.poll_all_jobs(tko, jobs, email_from, email_to)
mbligh5b618382008-12-03 15:24:01 +0000241 if result is not None:
242 return result
243
244
mbligh45ffc432008-12-09 23:35:17 +0000245 def result_notify(self, job, email_from, email_to):
mbligh5b618382008-12-03 15:24:01 +0000246 """
mbligh45ffc432008-12-09 23:35:17 +0000247 Notify about the result of a job. Will always print, if email data
248 is provided, will send email for it as well.
249
250 job: job object to notify about
251 email_from: send notification email upon completion from here
252 email_from: send notification email upon completion to here
253 """
254 if job.result == True:
255 subject = 'Testing PASSED: '
256 else:
257 subject = 'Testing FAILED: '
258 subject += '%s : %s\n' % (job.name, job.id)
259 text = []
260 for platform in job.results_platform_map:
261 for status in job.results_platform_map[platform]:
262 if status == 'Total':
263 continue
264 hosts = ','.join(job.results_platform_map[platform][status])
mbligh37eceaa2008-12-15 22:56:37 +0000265 text.append('%20s %10s %s\n' % (platform, status, hosts))
266
267 tko_base_url = 'http://%s/tko' % GLOBAL_CONFIG.get_config_value(
268 'SERVER', 'hostname', default=DEFAULT_SERVER)
269
270 params = ('columns=test',
271 'rows=machine_group',
272 "condition=tag~'%s-%%25'" % job.id,
273 'title=Report')
274 query_string = '&'.join(params)
275 url = '%s/compose_query.cgi?%s' % (tko_base_url, query_string)
276 text.append('\n')
277 text.append(url)
278
279 body = '\n'.join(text)
280 print '---------------------------------------------------'
281 print 'Subject: ', subject
mbligh45ffc432008-12-09 23:35:17 +0000282 print body
mbligh37eceaa2008-12-15 22:56:37 +0000283 print '---------------------------------------------------'
mbligh45ffc432008-12-09 23:35:17 +0000284 if email_from and email_to:
mbligh37eceaa2008-12-15 22:56:37 +0000285 print 'Sending email ...'
mbligh45ffc432008-12-09 23:35:17 +0000286 utils.send_email(email_from, email_to, subject, body)
287 print
mbligh37eceaa2008-12-15 22:56:37 +0000288
mbligh45ffc432008-12-09 23:35:17 +0000289
mbligh1354c9d2008-12-22 14:56:13 +0000290 def print_job_result(self, job):
291 """
292 Print the result of a single job.
293 job: a job object
294 """
295 if job.result is None:
296 print 'PENDING',
297 elif job.result == True:
298 print 'PASSED',
299 elif job.result == False:
300 print 'FAILED',
301 print ' %s : %s' % (job.id, job.name)
302
303
mbligh5280e3b2008-12-22 14:39:28 +0000304 def poll_all_jobs(self, tko, jobs, email_from, email_to):
mbligh45ffc432008-12-09 23:35:17 +0000305 """
306 Poll all jobs in a list.
307 jobs: list of job objects to poll
308 email_from: send notification email upon completion from here
309 email_from: send notification email upon completion to here
310
311 Returns:
mbligh5b618382008-12-03 15:24:01 +0000312 a) All complete successfully (return True)
313 b) One or more has failed (return False)
314 c) Cannot tell yet (return None)
315 """
mbligh45ffc432008-12-09 23:35:17 +0000316 results = []
mbligh5b618382008-12-03 15:24:01 +0000317 for job in jobs:
mbligh5280e3b2008-12-22 14:39:28 +0000318 job.result = self.poll_job_results(tko, job, debug=False)
mbligh45ffc432008-12-09 23:35:17 +0000319 results.append(job.result)
320 if job.result is not None and not job.notified:
321 self.result_notify(job, email_from, email_to)
322 job.notified = True
323
mbligh1354c9d2008-12-22 14:56:13 +0000324 self.print_job_result(job)
mbligh45ffc432008-12-09 23:35:17 +0000325
326 if None in results:
327 return None
328 elif False in results:
329 return False
330 else:
331 return True
mbligh5b618382008-12-03 15:24:01 +0000332
333
mbligh1f23f362008-12-22 14:46:12 +0000334 def _included_platform(self, host, platforms):
335 """
336 See if host's platforms matches any of the patterns in the included
337 platforms list.
338 """
339 if not platforms:
340 return True # No filtering of platforms
341 for platform in platforms:
342 if re.search(platform, host.platform):
343 return True
344 return False
345
346
mbligh5b618382008-12-03 15:24:01 +0000347 def invoke_test(self, pairing, kernel, kernel_label, priority='Medium'):
348 """
349 Given a pairing of a control file to a machine label, find all machines
350 with that label, and submit that control file to them.
351
mbligh4e576612008-12-22 14:56:36 +0000352 Returns a list of job objects
mbligh5b618382008-12-03 15:24:01 +0000353 """
354 job_name = '%s : %s' % (pairing.machine_label, kernel_label)
355 hosts = self.get_hosts(multiple_labels=[pairing.machine_label])
mbligh1f23f362008-12-22 14:46:12 +0000356 platforms = pairing.platforms
357 hosts = [h for h in hosts if self._included_platform(h, platforms)]
mbligh45ffc432008-12-09 23:35:17 +0000358 host_list = [h.hostname for h in hosts if h.status != 'Repair Failed']
mbligh1f23f362008-12-22 14:46:12 +0000359 print 'HOSTS: %s' % host_list
mbligh4e576612008-12-22 14:56:36 +0000360 new_jobs = self.create_job_by_test(name=job_name,
mbligh5b618382008-12-03 15:24:01 +0000361 dependencies=[pairing.machine_label],
362 tests=[pairing.control_file],
363 priority=priority,
364 hosts=host_list,
mbligh1354c9d2008-12-22 14:56:13 +0000365 kernel=kernel,
366 use_container=pairing.container)
mbligh4e576612008-12-22 14:56:36 +0000367 for new_job in new_jobs:
368 print 'Invoked test %s : %s' % (new_job.id, job_name)
369 return new_jobs
mbligh5b618382008-12-03 15:24:01 +0000370
371
mbligh5280e3b2008-12-22 14:39:28 +0000372 def _job_test_results(self, tko, job):
mbligh5b618382008-12-03 15:24:01 +0000373 """
mbligh5280e3b2008-12-22 14:39:28 +0000374 Retrieve test results for a job
mbligh5b618382008-12-03 15:24:01 +0000375 """
mbligh5280e3b2008-12-22 14:39:28 +0000376 job.test_status = {}
377 try:
378 test_statuses = tko.get_status_counts(job=job.id)
379 except Exception:
380 print "Ignoring exception on poll job; RPC interface is flaky"
381 traceback.print_exc()
382 return
383
384 for test_status in test_statuses:
385 hostname = test_status.hostname
386 if hostname not in job.test_status:
387 job.test_status[hostname] = TestResults()
388 job.test_status[hostname].add(test_status)
389
390
391 def _job_results_platform_map(self, job):
392 job.results_platform_map = {}
mbligh5b618382008-12-03 15:24:01 +0000393 try:
mbligh45ffc432008-12-09 23:35:17 +0000394 job_statuses = self.get_host_queue_entries(job=job.id)
mbligh5b618382008-12-03 15:24:01 +0000395 except Exception:
396 print "Ignoring exception on poll job; RPC interface is flaky"
397 traceback.print_exc()
398 return None
mbligh5280e3b2008-12-22 14:39:28 +0000399
mbligh5b618382008-12-03 15:24:01 +0000400 platform_map = {}
mbligh5280e3b2008-12-22 14:39:28 +0000401 job.job_status = {}
mbligh5b618382008-12-03 15:24:01 +0000402 for job_status in job_statuses:
403 hostname = job_status.host.hostname
mbligh5280e3b2008-12-22 14:39:28 +0000404 job.job_status[hostname] = job_status.status
mbligh5b618382008-12-03 15:24:01 +0000405 status = job_status.status
mbligh5280e3b2008-12-22 14:39:28 +0000406 if hostname in job.test_status and job.test_status[hostname].fail:
407 # Job status doesn't reflect failed tests, override that
408 status = 'Failed'
mbligh5b618382008-12-03 15:24:01 +0000409 platform = job_status.host.platform
410 if platform not in platform_map:
411 platform_map[platform] = {'Total' : [hostname]}
412 else:
413 platform_map[platform]['Total'].append(hostname)
414 new_host_list = platform_map[platform].get(status, []) + [hostname]
415 platform_map[platform][status] = new_host_list
mbligh45ffc432008-12-09 23:35:17 +0000416 job.results_platform_map = platform_map
mbligh5280e3b2008-12-22 14:39:28 +0000417
418
419 def poll_job_results(self, tko, job, debug=False):
420 """
421 Analyse all job results by platform, return:
mbligh5b618382008-12-03 15:24:01 +0000422
mbligh5280e3b2008-12-22 14:39:28 +0000423 False: if any platform has more than one failure
424 None: if any platform has more than one machine not yet Good.
425 True: if all platforms have at least all-but-one machines Good.
426 """
427 self._job_test_results(tko, job)
428 self._job_results_platform_map(job)
429
mbligh5b618382008-12-03 15:24:01 +0000430 good_platforms = []
431 bad_platforms = []
432 unknown_platforms = []
mbligh5280e3b2008-12-22 14:39:28 +0000433 platform_map = job.results_platform_map
mbligh5b618382008-12-03 15:24:01 +0000434 for platform in platform_map:
435 total = len(platform_map[platform]['Total'])
436 completed = len(platform_map[platform].get('Completed', []))
437 failed = len(platform_map[platform].get('Failed', []))
438 if failed > 1:
439 bad_platforms.append(platform)
440 elif completed + 1 >= total:
441 # if all or all but one are good, call the job good.
442 good_platforms.append(platform)
443 else:
444 unknown_platforms.append(platform)
445 detail = []
446 for status in platform_map[platform]:
447 if status == 'Total':
448 continue
449 detail.append('%s=%s' % (status,platform_map[platform][status]))
450 if debug:
451 print '%20s %d/%d %s' % (platform, completed, total,
452 ' '.join(detail))
453 print
454
455 if len(bad_platforms) > 0:
456 if debug:
457 print 'Result bad - platforms: ' + ' '.join(bad_platforms)
458 return False
459 if len(unknown_platforms) > 0:
460 if debug:
461 platform_list = ' '.join(unknown_platforms)
462 print 'Result unknown - platforms: ', platform_list
463 return None
464 if debug:
465 platform_list = ' '.join(good_platforms)
466 print 'Result good - all platforms passed: ', platform_list
467 return True
468
469
mbligh5280e3b2008-12-22 14:39:28 +0000470class TestResults(object):
471 """
472 Container class used to hold the results of the tests for a job
473 """
474 def __init__(self):
475 self.good = []
476 self.fail = []
477
478
479 def add(self, result):
480 if result.complete_count - result.pass_count > 0:
481 self.fail.append(result.test_name)
482 else:
483 self.good.append(result.test_name)
484
485
486class RpcObject(object):
mbligh67647152008-11-19 00:18:14 +0000487 """
488 Generic object used to construct python objects from rpc calls
489 """
490 def __init__(self, afe, hash):
491 self.afe = afe
492 self.hash = hash
493 self.__dict__.update(hash)
494
495
496 def __str__(self):
497 return dump_object(self.__repr__(), self)
498
499
mbligh1354c9d2008-12-22 14:56:13 +0000500class ControlFile(RpcObject):
501 """
502 AFE control file object
503
504 Fields: synch_count, dependencies, control_file, is_server
505 """
506 def __repr__(self):
507 return 'CONTROL FILE: %s' % self.control_file
508
509
mbligh5280e3b2008-12-22 14:39:28 +0000510class Label(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000511 """
512 AFE label object
513
514 Fields:
515 name, invalid, platform, kernel_config, id, only_if_needed
516 """
517 def __repr__(self):
518 return 'LABEL: %s' % self.name
519
520
521 def add_hosts(self, hosts):
522 return self.afe.run('label_add_hosts', self.id, hosts)
523
524
525 def remove_hosts(self, hosts):
526 return self.afe.run('label_remove_hosts', self.id, hosts)
527
528
mbligh5280e3b2008-12-22 14:39:28 +0000529class Acl(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000530 """
531 AFE acl object
532
533 Fields:
534 users, hosts, description, name, id
535 """
536 def __repr__(self):
537 return 'ACL: %s' % self.name
538
539
540 def add_hosts(self, hosts):
541 self.afe.log('Adding hosts %s to ACL %s' % (hosts, self.name))
542 return self.afe.run('acl_group_add_hosts', self.id, hosts)
543
544
545 def remove_hosts(self, hosts):
546 self.afe.log('Removing hosts %s from ACL %s' % (hosts, self.name))
547 return self.afe.run('acl_group_remove_hosts', self.id, hosts)
548
549
mbligh5280e3b2008-12-22 14:39:28 +0000550class Job(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000551 """
552 AFE job object
553
554 Fields:
555 name, control_file, control_type, synch_count, reboot_before,
556 run_verify, priority, email_list, created_on, dependencies,
557 timeout, owner, reboot_after, id
558 """
559 def __repr__(self):
560 return 'JOB: %s' % self.id
561
562
mbligh5280e3b2008-12-22 14:39:28 +0000563class JobStatus(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000564 """
565 AFE job_status object
566
567 Fields:
568 status, complete, deleted, meta_host, host, active, execution_subdir, id
569 """
570 def __init__(self, afe, hash):
571 # This should call super
572 self.afe = afe
573 self.hash = hash
574 self.__dict__.update(hash)
mbligh5280e3b2008-12-22 14:39:28 +0000575 self.job = Job(afe, self.job)
mbligh67647152008-11-19 00:18:14 +0000576 if self.host:
577 self.host = afe.get_hosts(hostname=self.host['hostname'])[0]
578
579
580 def __repr__(self):
581 return 'JOB STATUS: %s-%s' % (self.job.id, self.host.hostname)
582
583
mbligh5280e3b2008-12-22 14:39:28 +0000584class Host(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000585 """
586 AFE host object
587
588 Fields:
589 status, lock_time, locked_by, locked, hostname, invalid,
590 synch_id, labels, platform, protection, dirty, id
591 """
592 def __repr__(self):
593 return 'HOST OBJECT: %s' % self.hostname
594
595
596 def show(self):
597 labels = list(set(self.labels) - set([self.platform]))
598 print '%-6s %-7s %-7s %-16s %s' % (self.hostname, self.status,
599 self.locked, self.platform,
600 ', '.join(labels))
601
602
603 def get_acls(self):
604 return self.afe.get_acls(hosts__hostname=self.hostname)
605
606
607 def add_acl(self, acl_name):
608 self.afe.log('Adding ACL %s to host %s' % (acl_name, self.hostname))
609 return self.afe.run('acl_group_add_hosts', id=acl_name,
610 hosts=[self.hostname])
611
612
613 def remove_acl(self, acl_name):
614 self.afe.log('Removing ACL %s from host %s' % (acl_name, self.hostname))
615 return self.afe.run('acl_group_remove_hosts', id=acl_name,
616 hosts=[self.hostname])
617
618
619 def get_labels(self):
620 return self.afe.get_labels(host__hostname__in=[self.hostname])
621
622
623 def add_labels(self, labels):
624 self.afe.log('Adding labels %s to host %s' % (labels, self.hostname))
625 return self.afe.run('host_add_labels', id=self.id, labels=labels)
626
627
628 def remove_labels(self, labels):
629 self.afe.log('Removing labels %s from host %s' % (labels,self.hostname))
630 return self.afe.run('host_remove_labels', id=self.id, labels=labels)
mbligh5b618382008-12-03 15:24:01 +0000631
632
mbligh5280e3b2008-12-22 14:39:28 +0000633class TestStatus(RpcObject):
mblighc31e4022008-12-11 19:32:30 +0000634 """
635 TKO test status object
636
637 Fields:
638 test_idx, hostname, testname, id
639 complete_count, incomplete_count, group_count, pass_count
640 """
641 def __repr__(self):
642 return 'TEST STATUS: %s' % self.id
643
644
mbligh5b618382008-12-03 15:24:01 +0000645class MachineTestPairing(object):
646 """
647 Object representing the pairing of a machine label with a control file
mbligh1f23f362008-12-22 14:46:12 +0000648
649 machine_label: use machines from this label
650 control_file: use this control file (by name in the frontend)
651 platforms: list of rexeps to filter platforms by. [] => no filtering
mbligh5b618382008-12-03 15:24:01 +0000652 """
mbligh1354c9d2008-12-22 14:56:13 +0000653 def __init__(self, machine_label, control_file, platforms=[],
654 container=False):
mbligh5b618382008-12-03 15:24:01 +0000655 self.machine_label = machine_label
656 self.control_file = control_file
mbligh1f23f362008-12-22 14:46:12 +0000657 self.platforms = platforms
mbligh1354c9d2008-12-22 14:56:13 +0000658 self.container = container
659
660
661 def __repr__(self):
662 return '%s %s %s %s' % (self.machine_label, self.control_file,
663 self.platforms, self.container)