blob: fbb172e593f84bba6da8bb208952cef6fadf7758 [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):
mbligh54459c72009-01-21 19:26:44 +0000128 id = self.run('add_host', hostname=hostname, **dargs)
mbligh67647152008-11-19 00:18:14 +0000129 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):
mbligh54459c72009-01-21 19:26:44 +0000138 id = self.run('add_label', name=name, **dargs)
mbligh67647152008-11-19 00:18:14 +0000139 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):
mbligh54459c72009-01-21 19:26:44 +0000148 id = self.run('add_acl_group', name=name, **dargs)
mbligh67647152008-11-19 00:18:14 +0000149 return self.get_acls(id=id)[0]
150
151
mbligh54459c72009-01-21 19:26:44 +0000152 def get_users(self, **dargs):
153 users = self.run('get_users', **dargs)
154 return [User(self, u) for u in users]
155
156
mbligh1354c9d2008-12-22 14:56:13 +0000157 def generate_control_file(self, tests, **dargs):
158 ret = self.run('generate_control_file', tests=tests, **dargs)
159 return ControlFile(self, ret)
160
161
mbligh67647152008-11-19 00:18:14 +0000162 def get_jobs(self, summary=False, **dargs):
163 if summary:
164 jobs_data = self.run('get_jobs_summary', **dargs)
165 else:
166 jobs_data = self.run('get_jobs', **dargs)
mbligh5280e3b2008-12-22 14:39:28 +0000167 return [Job(self, j) for j in jobs_data]
mbligh67647152008-11-19 00:18:14 +0000168
169
170 def get_host_queue_entries(self, **data):
171 entries = self.run('get_host_queue_entries', **data)
mbligh5280e3b2008-12-22 14:39:28 +0000172 return [JobStatus(self, e) for e in entries]
mbligh67647152008-11-19 00:18:14 +0000173
174
mbligh4e576612008-12-22 14:56:36 +0000175 def create_job_by_test(self, tests, hosts, kernel=None, use_container=False,
mbligh1354c9d2008-12-22 14:56:13 +0000176 **dargs):
mbligh67647152008-11-19 00:18:14 +0000177 """
178 Given a test name, fetch the appropriate control file from the server
mbligh4e576612008-12-22 14:56:36 +0000179 and submit it.
180
181 Returns a list of job objects
mbligh67647152008-11-19 00:18:14 +0000182 """
mbligh1354c9d2008-12-22 14:56:13 +0000183 control_file = self.generate_control_file(tests=tests, kernel=kernel,
184 use_container=use_container,
185 do_push_packages=True)
186 if control_file.is_server:
mbligh67647152008-11-19 00:18:14 +0000187 dargs['control_type'] = 'Server'
188 else:
189 dargs['control_type'] = 'Client'
190 dargs['dependencies'] = dargs.get('dependencies', []) + \
mbligh1354c9d2008-12-22 14:56:13 +0000191 control_file.dependencies
192 dargs['control_file'] = control_file.control_file
193 dargs['synch_count'] = control_file.synch_count
mbligh4e576612008-12-22 14:56:36 +0000194 jobs = []
195 if control_file.synch_count > 1:
196 # We don't trust the scheduler to do the groupings for us.
197 synch_count = control_file.synch_count
198 (pairs, failures) = form_ntuples_from_machines(hosts, synch_count)
199 for machines in pairs:
200 jobs.append(self.create_job(hosts=machines, **dargs))
201 else:
202 jobs.append(self.create_job(hosts=hosts, **dargs))
203 return jobs
mbligh67647152008-11-19 00:18:14 +0000204
205
206 def create_job(self, control_file, name=' ', priority='Medium',
207 control_type='Client', **dargs):
208 id = self.run('create_job', name=name, priority=priority,
209 control_file=control_file, control_type=control_type, **dargs)
210 return self.get_jobs(id=id)[0]
211
212
mbligh1f23f362008-12-22 14:46:12 +0000213 def run_test_suites(self, pairings, kernel, kernel_label, priority='Medium',
214 wait=True, poll_interval=5, email_from=None,
mbligh7b312282009-01-07 16:45:43 +0000215 email_to=None, timeout=168):
mbligh5b618382008-12-03 15:24:01 +0000216 """
217 Run a list of test suites on a particular kernel.
218
219 Poll for them to complete, and return whether they worked or not.
220
221 pairings: list of MachineTestPairing objects to invoke
222 kernel: name of the kernel to run
223 kernel_label: label of the kernel to run
224 (<kernel-version> : <config> : <date>)
225 wait: boolean - wait for the results to come back?
226 poll_interval: interval between polling for job results (in minutes)
mbligh45ffc432008-12-09 23:35:17 +0000227 email_from: send notification email upon completion from here
228 email_from: send notification email upon completion to here
mbligh5b618382008-12-03 15:24:01 +0000229 """
230 jobs = []
231 for pairing in pairings:
mbligh7b312282009-01-07 16:45:43 +0000232 new_jobs = self.invoke_test(pairing, kernel, kernel_label, priority,
233 timeout=timeout)
mbligh4e576612008-12-22 14:56:36 +0000234 for job in new_jobs:
235 job.notified = False
236 jobs += new_jobs
mbligh5280e3b2008-12-22 14:39:28 +0000237 # disabled - this is just for debugging: mbligh
238 # if email_from and email_to:
239 # subject = 'Testing started: %s : %s' % (job.name, job.id)
240 # utils.send_email(email_from, email_to, subject, subject)
mbligh5b618382008-12-03 15:24:01 +0000241 if not wait:
242 return
mbligh5280e3b2008-12-22 14:39:28 +0000243 tko = TKO()
mbligh5b618382008-12-03 15:24:01 +0000244 while True:
245 time.sleep(60 * poll_interval)
mbligh5280e3b2008-12-22 14:39:28 +0000246 result = self.poll_all_jobs(tko, jobs, email_from, email_to)
mbligh5b618382008-12-03 15:24:01 +0000247 if result is not None:
248 return result
249
250
mbligh45ffc432008-12-09 23:35:17 +0000251 def result_notify(self, job, email_from, email_to):
mbligh5b618382008-12-03 15:24:01 +0000252 """
mbligh45ffc432008-12-09 23:35:17 +0000253 Notify about the result of a job. Will always print, if email data
254 is provided, will send email for it as well.
255
256 job: job object to notify about
257 email_from: send notification email upon completion from here
258 email_from: send notification email upon completion to here
259 """
260 if job.result == True:
261 subject = 'Testing PASSED: '
262 else:
263 subject = 'Testing FAILED: '
264 subject += '%s : %s\n' % (job.name, job.id)
265 text = []
266 for platform in job.results_platform_map:
267 for status in job.results_platform_map[platform]:
268 if status == 'Total':
269 continue
270 hosts = ','.join(job.results_platform_map[platform][status])
mbligh37eceaa2008-12-15 22:56:37 +0000271 text.append('%20s %10s %s\n' % (platform, status, hosts))
272
273 tko_base_url = 'http://%s/tko' % GLOBAL_CONFIG.get_config_value(
274 'SERVER', 'hostname', default=DEFAULT_SERVER)
275
276 params = ('columns=test',
277 'rows=machine_group',
278 "condition=tag~'%s-%%25'" % job.id,
279 'title=Report')
280 query_string = '&'.join(params)
281 url = '%s/compose_query.cgi?%s' % (tko_base_url, query_string)
282 text.append('\n')
283 text.append(url)
284
285 body = '\n'.join(text)
286 print '---------------------------------------------------'
287 print 'Subject: ', subject
mbligh45ffc432008-12-09 23:35:17 +0000288 print body
mbligh37eceaa2008-12-15 22:56:37 +0000289 print '---------------------------------------------------'
mbligh45ffc432008-12-09 23:35:17 +0000290 if email_from and email_to:
mbligh37eceaa2008-12-15 22:56:37 +0000291 print 'Sending email ...'
mbligh45ffc432008-12-09 23:35:17 +0000292 utils.send_email(email_from, email_to, subject, body)
293 print
mbligh37eceaa2008-12-15 22:56:37 +0000294
mbligh45ffc432008-12-09 23:35:17 +0000295
mbligh1354c9d2008-12-22 14:56:13 +0000296 def print_job_result(self, job):
297 """
298 Print the result of a single job.
299 job: a job object
300 """
301 if job.result is None:
302 print 'PENDING',
303 elif job.result == True:
304 print 'PASSED',
305 elif job.result == False:
306 print 'FAILED',
307 print ' %s : %s' % (job.id, job.name)
308
309
mbligh5280e3b2008-12-22 14:39:28 +0000310 def poll_all_jobs(self, tko, jobs, email_from, email_to):
mbligh45ffc432008-12-09 23:35:17 +0000311 """
312 Poll all jobs in a list.
313 jobs: list of job objects to poll
314 email_from: send notification email upon completion from here
315 email_from: send notification email upon completion to here
316
317 Returns:
mbligh5b618382008-12-03 15:24:01 +0000318 a) All complete successfully (return True)
319 b) One or more has failed (return False)
320 c) Cannot tell yet (return None)
321 """
mbligh45ffc432008-12-09 23:35:17 +0000322 results = []
mbligh5b618382008-12-03 15:24:01 +0000323 for job in jobs:
mbligh5280e3b2008-12-22 14:39:28 +0000324 job.result = self.poll_job_results(tko, job, debug=False)
mbligh45ffc432008-12-09 23:35:17 +0000325 results.append(job.result)
326 if job.result is not None and not job.notified:
327 self.result_notify(job, email_from, email_to)
328 job.notified = True
329
mbligh1354c9d2008-12-22 14:56:13 +0000330 self.print_job_result(job)
mbligh45ffc432008-12-09 23:35:17 +0000331
332 if None in results:
333 return None
334 elif False in results:
335 return False
336 else:
337 return True
mbligh5b618382008-12-03 15:24:01 +0000338
339
mbligh1f23f362008-12-22 14:46:12 +0000340 def _included_platform(self, host, platforms):
341 """
342 See if host's platforms matches any of the patterns in the included
343 platforms list.
344 """
345 if not platforms:
346 return True # No filtering of platforms
347 for platform in platforms:
348 if re.search(platform, host.platform):
349 return True
350 return False
351
352
mbligh7b312282009-01-07 16:45:43 +0000353 def invoke_test(self, pairing, kernel, kernel_label, priority='Medium',
354 **dargs):
mbligh5b618382008-12-03 15:24:01 +0000355 """
356 Given a pairing of a control file to a machine label, find all machines
357 with that label, and submit that control file to them.
358
mbligh4e576612008-12-22 14:56:36 +0000359 Returns a list of job objects
mbligh5b618382008-12-03 15:24:01 +0000360 """
361 job_name = '%s : %s' % (pairing.machine_label, kernel_label)
362 hosts = self.get_hosts(multiple_labels=[pairing.machine_label])
mbligh1f23f362008-12-22 14:46:12 +0000363 platforms = pairing.platforms
364 hosts = [h for h in hosts if self._included_platform(h, platforms)]
mbligh45ffc432008-12-09 23:35:17 +0000365 host_list = [h.hostname for h in hosts if h.status != 'Repair Failed']
mbligh1f23f362008-12-22 14:46:12 +0000366 print 'HOSTS: %s' % host_list
mbligh4e576612008-12-22 14:56:36 +0000367 new_jobs = self.create_job_by_test(name=job_name,
mbligh7b312282009-01-07 16:45:43 +0000368 dependencies=[pairing.machine_label],
369 tests=[pairing.control_file],
370 priority=priority,
371 hosts=host_list,
372 kernel=kernel,
373 use_container=pairing.container,
374 **dargs)
mbligh4e576612008-12-22 14:56:36 +0000375 for new_job in new_jobs:
376 print 'Invoked test %s : %s' % (new_job.id, job_name)
377 return new_jobs
mbligh5b618382008-12-03 15:24:01 +0000378
379
mbligh5280e3b2008-12-22 14:39:28 +0000380 def _job_test_results(self, tko, job):
mbligh5b618382008-12-03 15:24:01 +0000381 """
mbligh5280e3b2008-12-22 14:39:28 +0000382 Retrieve test results for a job
mbligh5b618382008-12-03 15:24:01 +0000383 """
mbligh5280e3b2008-12-22 14:39:28 +0000384 job.test_status = {}
385 try:
386 test_statuses = tko.get_status_counts(job=job.id)
387 except Exception:
388 print "Ignoring exception on poll job; RPC interface is flaky"
389 traceback.print_exc()
390 return
391
392 for test_status in test_statuses:
mbligh7479a182009-01-07 16:46:24 +0000393 # SERVER_JOB is buggy, and often gives false failures. Ignore it.
394 if test_status.test_name == 'SERVER_JOB':
395 continue
mbligh5280e3b2008-12-22 14:39:28 +0000396 hostname = test_status.hostname
397 if hostname not in job.test_status:
398 job.test_status[hostname] = TestResults()
399 job.test_status[hostname].add(test_status)
400
401
402 def _job_results_platform_map(self, job):
403 job.results_platform_map = {}
mbligh5b618382008-12-03 15:24:01 +0000404 try:
mbligh45ffc432008-12-09 23:35:17 +0000405 job_statuses = self.get_host_queue_entries(job=job.id)
mbligh5b618382008-12-03 15:24:01 +0000406 except Exception:
407 print "Ignoring exception on poll job; RPC interface is flaky"
408 traceback.print_exc()
409 return None
mbligh5280e3b2008-12-22 14:39:28 +0000410
mbligh5b618382008-12-03 15:24:01 +0000411 platform_map = {}
mbligh5280e3b2008-12-22 14:39:28 +0000412 job.job_status = {}
mbligh5b618382008-12-03 15:24:01 +0000413 for job_status in job_statuses:
414 hostname = job_status.host.hostname
mbligh5280e3b2008-12-22 14:39:28 +0000415 job.job_status[hostname] = job_status.status
mbligh5b618382008-12-03 15:24:01 +0000416 status = job_status.status
mbligh5280e3b2008-12-22 14:39:28 +0000417 if hostname in job.test_status and job.test_status[hostname].fail:
418 # Job status doesn't reflect failed tests, override that
419 status = 'Failed'
mbligh5b618382008-12-03 15:24:01 +0000420 platform = job_status.host.platform
421 if platform not in platform_map:
422 platform_map[platform] = {'Total' : [hostname]}
423 else:
424 platform_map[platform]['Total'].append(hostname)
425 new_host_list = platform_map[platform].get(status, []) + [hostname]
426 platform_map[platform][status] = new_host_list
mbligh45ffc432008-12-09 23:35:17 +0000427 job.results_platform_map = platform_map
mbligh5280e3b2008-12-22 14:39:28 +0000428
429
430 def poll_job_results(self, tko, job, debug=False):
431 """
432 Analyse all job results by platform, return:
mbligh5b618382008-12-03 15:24:01 +0000433
mbligh5280e3b2008-12-22 14:39:28 +0000434 False: if any platform has more than one failure
435 None: if any platform has more than one machine not yet Good.
436 True: if all platforms have at least all-but-one machines Good.
437 """
438 self._job_test_results(tko, job)
439 self._job_results_platform_map(job)
440
mbligh5b618382008-12-03 15:24:01 +0000441 good_platforms = []
442 bad_platforms = []
443 unknown_platforms = []
mbligh5280e3b2008-12-22 14:39:28 +0000444 platform_map = job.results_platform_map
mbligh5b618382008-12-03 15:24:01 +0000445 for platform in platform_map:
446 total = len(platform_map[platform]['Total'])
447 completed = len(platform_map[platform].get('Completed', []))
448 failed = len(platform_map[platform].get('Failed', []))
449 if failed > 1:
450 bad_platforms.append(platform)
mbligh7479a182009-01-07 16:46:24 +0000451 elif (completed > 1) and (completed + 1 >= total):
mbligh5b618382008-12-03 15:24:01 +0000452 # if all or all but one are good, call the job good.
453 good_platforms.append(platform)
454 else:
455 unknown_platforms.append(platform)
456 detail = []
457 for status in platform_map[platform]:
458 if status == 'Total':
459 continue
460 detail.append('%s=%s' % (status,platform_map[platform][status]))
461 if debug:
462 print '%20s %d/%d %s' % (platform, completed, total,
463 ' '.join(detail))
464 print
465
466 if len(bad_platforms) > 0:
467 if debug:
468 print 'Result bad - platforms: ' + ' '.join(bad_platforms)
469 return False
470 if len(unknown_platforms) > 0:
471 if debug:
472 platform_list = ' '.join(unknown_platforms)
473 print 'Result unknown - platforms: ', platform_list
474 return None
475 if debug:
476 platform_list = ' '.join(good_platforms)
477 print 'Result good - all platforms passed: ', platform_list
478 return True
479
480
mbligh5280e3b2008-12-22 14:39:28 +0000481class TestResults(object):
482 """
483 Container class used to hold the results of the tests for a job
484 """
485 def __init__(self):
486 self.good = []
487 self.fail = []
488
489
490 def add(self, result):
491 if result.complete_count - result.pass_count > 0:
492 self.fail.append(result.test_name)
493 else:
494 self.good.append(result.test_name)
495
496
497class RpcObject(object):
mbligh67647152008-11-19 00:18:14 +0000498 """
499 Generic object used to construct python objects from rpc calls
500 """
501 def __init__(self, afe, hash):
502 self.afe = afe
503 self.hash = hash
504 self.__dict__.update(hash)
505
506
507 def __str__(self):
508 return dump_object(self.__repr__(), self)
509
510
mbligh1354c9d2008-12-22 14:56:13 +0000511class ControlFile(RpcObject):
512 """
513 AFE control file object
514
515 Fields: synch_count, dependencies, control_file, is_server
516 """
517 def __repr__(self):
518 return 'CONTROL FILE: %s' % self.control_file
519
520
mbligh5280e3b2008-12-22 14:39:28 +0000521class Label(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000522 """
523 AFE label object
524
525 Fields:
526 name, invalid, platform, kernel_config, id, only_if_needed
527 """
528 def __repr__(self):
529 return 'LABEL: %s' % self.name
530
531
532 def add_hosts(self, hosts):
533 return self.afe.run('label_add_hosts', self.id, hosts)
534
535
536 def remove_hosts(self, hosts):
537 return self.afe.run('label_remove_hosts', self.id, hosts)
538
539
mbligh5280e3b2008-12-22 14:39:28 +0000540class Acl(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000541 """
542 AFE acl object
543
544 Fields:
545 users, hosts, description, name, id
546 """
547 def __repr__(self):
548 return 'ACL: %s' % self.name
549
550
551 def add_hosts(self, hosts):
552 self.afe.log('Adding hosts %s to ACL %s' % (hosts, self.name))
553 return self.afe.run('acl_group_add_hosts', self.id, hosts)
554
555
556 def remove_hosts(self, hosts):
557 self.afe.log('Removing hosts %s from ACL %s' % (hosts, self.name))
558 return self.afe.run('acl_group_remove_hosts', self.id, hosts)
559
560
mbligh54459c72009-01-21 19:26:44 +0000561 def add_users(self, users):
562 self.afe.log('Adding users %s to ACL %s' % (users, self.name))
563 return self.afe.run('acl_group_add_users', id=self.name, users=users)
564
565
mbligh5280e3b2008-12-22 14:39:28 +0000566class Job(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000567 """
568 AFE job object
569
570 Fields:
571 name, control_file, control_type, synch_count, reboot_before,
572 run_verify, priority, email_list, created_on, dependencies,
573 timeout, owner, reboot_after, id
574 """
575 def __repr__(self):
576 return 'JOB: %s' % self.id
577
578
mbligh5280e3b2008-12-22 14:39:28 +0000579class JobStatus(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000580 """
581 AFE job_status object
582
583 Fields:
584 status, complete, deleted, meta_host, host, active, execution_subdir, id
585 """
586 def __init__(self, afe, hash):
587 # This should call super
588 self.afe = afe
589 self.hash = hash
590 self.__dict__.update(hash)
mbligh5280e3b2008-12-22 14:39:28 +0000591 self.job = Job(afe, self.job)
mbligh67647152008-11-19 00:18:14 +0000592 if self.host:
593 self.host = afe.get_hosts(hostname=self.host['hostname'])[0]
594
595
596 def __repr__(self):
597 return 'JOB STATUS: %s-%s' % (self.job.id, self.host.hostname)
598
599
mbligh5280e3b2008-12-22 14:39:28 +0000600class Host(RpcObject):
mbligh67647152008-11-19 00:18:14 +0000601 """
602 AFE host object
603
604 Fields:
605 status, lock_time, locked_by, locked, hostname, invalid,
606 synch_id, labels, platform, protection, dirty, id
607 """
608 def __repr__(self):
609 return 'HOST OBJECT: %s' % self.hostname
610
611
612 def show(self):
613 labels = list(set(self.labels) - set([self.platform]))
614 print '%-6s %-7s %-7s %-16s %s' % (self.hostname, self.status,
615 self.locked, self.platform,
616 ', '.join(labels))
617
618
mbligh54459c72009-01-21 19:26:44 +0000619 def delete(self):
620 return self.afe.run('delete_host', id=self.id)
621
622
mbligh6463c4b2009-01-30 00:33:37 +0000623 def modify(self, **dargs):
624 return self.afe.run('modify_host', id=self.id, **dargs)
625
626
mbligh67647152008-11-19 00:18:14 +0000627 def get_acls(self):
628 return self.afe.get_acls(hosts__hostname=self.hostname)
629
630
631 def add_acl(self, acl_name):
632 self.afe.log('Adding ACL %s to host %s' % (acl_name, self.hostname))
633 return self.afe.run('acl_group_add_hosts', id=acl_name,
634 hosts=[self.hostname])
635
636
637 def remove_acl(self, acl_name):
638 self.afe.log('Removing ACL %s from host %s' % (acl_name, self.hostname))
639 return self.afe.run('acl_group_remove_hosts', id=acl_name,
640 hosts=[self.hostname])
641
642
643 def get_labels(self):
644 return self.afe.get_labels(host__hostname__in=[self.hostname])
645
646
647 def add_labels(self, labels):
648 self.afe.log('Adding labels %s to host %s' % (labels, self.hostname))
649 return self.afe.run('host_add_labels', id=self.id, labels=labels)
650
651
652 def remove_labels(self, labels):
653 self.afe.log('Removing labels %s from host %s' % (labels,self.hostname))
654 return self.afe.run('host_remove_labels', id=self.id, labels=labels)
mbligh5b618382008-12-03 15:24:01 +0000655
656
mbligh54459c72009-01-21 19:26:44 +0000657class User(RpcObject):
658 def __repr__(self):
659 return 'USER: %s' % self.login
660
661
mbligh5280e3b2008-12-22 14:39:28 +0000662class TestStatus(RpcObject):
mblighc31e4022008-12-11 19:32:30 +0000663 """
664 TKO test status object
665
666 Fields:
667 test_idx, hostname, testname, id
668 complete_count, incomplete_count, group_count, pass_count
669 """
670 def __repr__(self):
671 return 'TEST STATUS: %s' % self.id
672
673
mbligh5b618382008-12-03 15:24:01 +0000674class MachineTestPairing(object):
675 """
676 Object representing the pairing of a machine label with a control file
mbligh1f23f362008-12-22 14:46:12 +0000677
678 machine_label: use machines from this label
679 control_file: use this control file (by name in the frontend)
680 platforms: list of rexeps to filter platforms by. [] => no filtering
mbligh5b618382008-12-03 15:24:01 +0000681 """
mbligh1354c9d2008-12-22 14:56:13 +0000682 def __init__(self, machine_label, control_file, platforms=[],
683 container=False):
mbligh5b618382008-12-03 15:24:01 +0000684 self.machine_label = machine_label
685 self.control_file = control_file
mbligh1f23f362008-12-22 14:46:12 +0000686 self.platforms = platforms
mbligh1354c9d2008-12-22 14:56:13 +0000687 self.container = container
688
689
690 def __repr__(self):
691 return '%s %s %s %s' % (self.machine_label, self.control_file,
692 self.platforms, self.container)