Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(133)

Side by Side Diff: frontend/planner/rpc_interface.py

Issue 1595019: Merge remote branch 'origin/upstream' into tempbranch (Closed)
Patch Set: Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « frontend/planner/models.py ('k') | frontend/planner/rpc_interface_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 """\ 1 """\
2 Functions to expose over the RPC interface. 2 Functions to expose over the RPC interface.
3 """ 3 """
4 4
5 __author__ = 'jamesren@google.com (James Ren)' 5 __author__ = 'jamesren@google.com (James Ren)'
6 6
7 7
8 import os 8 import os
9 import common 9 import common
10 from django.db import models as django_models 10 from django.db import models as django_models
11 from autotest_lib.frontend import thread_local 11 from autotest_lib.frontend import thread_local
12 from autotest_lib.frontend.afe import model_logic, models as afe_models 12 from autotest_lib.frontend.afe import model_logic, models as afe_models
13 from autotest_lib.frontend.afe import rpc_utils as afe_rpc_utils 13 from autotest_lib.frontend.afe import rpc_utils as afe_rpc_utils
14 from autotest_lib.frontend.tko import models as tko_models 14 from autotest_lib.frontend.tko import models as tko_models
15 from autotest_lib.frontend.planner import models, rpc_utils 15 from autotest_lib.frontend.planner import models, rpc_utils, model_attributes
16 from autotest_lib.frontend.planner import failure_actions
16 from autotest_lib.client.common_lib import utils 17 from autotest_lib.client.common_lib import utils
17 18
18 # basic getter/setter calls 19 # basic getter/setter calls
19 # TODO: deprecate the basic calls and reimplement them in the REST framework 20 # TODO: deprecate the basic calls and reimplement them in the REST framework
20 21
21 def get_plan(id): 22 def get_plan(id):
22 return afe_rpc_utils.prepare_for_serialization( 23 return afe_rpc_utils.prepare_for_serialization(
23 models.Plan.smart_get(id).get_object_dict()) 24 models.Plan.smart_get(id).get_object_dict())
24 25
25 26
26 def modify_plan(id, **data): 27 def modify_plan(id, **data):
27 models.Plan.smart_get(id).update_object(data) 28 models.Plan.smart_get(id).update_object(data)
28 29
29 30
30 def get_test_runs(**filter_data):
31 return afe_rpc_utils.prepare_for_serialization(
32 [test_run.get_object_dict() for test_run
33 in models.TestRun.objects.filter(**filter_data)])
34
35
36 def modify_test_run(id, **data): 31 def modify_test_run(id, **data):
37 models.TestRun.objects.get(id=id).update_object(data) 32 models.TestRun.objects.get(id=id).update_object(data)
38 33
39 34
40 def modify_host(id, **data): 35 def modify_host(id, **data):
41 models.Host.objects.get(id=id).update_object(data) 36 models.Host.objects.get(id=id).update_object(data)
42 37
43 38
44 def get_test_config(id): 39 def get_test_config(id):
45 return afe_rpc_utils.prepare_rows_as_nested_dicts( 40 return afe_rpc_utils.prepare_rows_as_nested_dicts(
(...skipping 17 matching lines...) Expand all
63 @param name: the name of the plan 58 @param name: the name of the plan
64 @param hosts: a list of hostnames 59 @param hosts: a list of hostnames
65 @param host_labels: a list of host labels. The hosts under test will update 60 @param host_labels: a list of host labels. The hosts under test will update
66 to reflect changes in the label 61 to reflect changes in the label
67 @param tests: an ordered list of dictionaries: 62 @param tests: an ordered list of dictionaries:
68 alias: an alias for the test 63 alias: an alias for the test
69 control_file: the test control file 64 control_file: the test control file
70 is_server: True if is a server-side control file 65 is_server: True if is a server-side control file
71 estimated_runtime: estimated number of hours this test 66 estimated_runtime: estimated number of hours this test
72 will run 67 will run
73 @param support: the global support object 68 @param support: the global support script
74 @param label_override: label to prepend to all AFE jobs for this test plan. 69 @param label_override: label to prepend to all AFE jobs for this test plan.
75 Defaults to the plan name. 70 Defaults to the plan name.
76 """ 71 """
77 host_objects = [] 72 host_objects = []
78 label_objects = [] 73 label_objects = []
79 74
80 for host in hosts or []: 75 for host in hosts or []:
81 try: 76 try:
82 host_objects.append( 77 host_objects.append(
83 afe_models.Host.valid_objects.get(hostname=host)) 78 afe_models.Host.valid_objects.get(hostname=host))
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 next_configs: a list of dictionaries: 173 next_configs: a list of dictionaries:
179 host: ID of the host 174 host: ID of the host
180 next_test_config_id: ID of the next Planner test to run 175 next_test_config_id: ID of the next Planner test to run
181 """ 176 """
182 plan = models.Plan.smart_get(plan_id) 177 plan = models.Plan.smart_get(plan_id)
183 178
184 result = {'next_configs': []} 179 result = {'next_configs': []}
185 180
186 rpc_utils.update_hosts_table(plan) 181 rpc_utils.update_hosts_table(plan)
187 for host in models.Host.objects.filter(plan=plan): 182 for host in models.Host.objects.filter(plan=plan):
188 next_test_config_id = rpc_utils.compute_next_test_config(plan, host) 183 next_test_config = rpc_utils.compute_next_test_config(plan, host)
189 if next_test_config_id: 184 if next_test_config:
190 config = {'next_test_config_id': next_test_config_id, 185 config = {'next_test_config_id': next_test_config.id,
186 'next_test_config_alias': next_test_config.alias,
191 'host': host.host.hostname} 187 'host': host.host.hostname}
192 result['next_configs'].append(config) 188 result['next_configs'].append(config)
193 189
194 rpc_utils.check_for_completion(plan) 190 rpc_utils.check_for_completion(plan)
195 result['complete'] = plan.complete 191 result['complete'] = plan.complete
196 192
197 return result 193 return result
198 194
199 195
200 def update_test_runs(plan_id): 196 def update_test_runs(plan_id):
201 """ 197 """
202 Add all applicable TKO jobs to the Planner DB tables 198 Add all applicable TKO jobs to the Planner DB tables
203 199
204 Looks for tests in the TKO tables that were started as a part of the test 200 Looks for tests in the TKO tables that were started as a part of the test
205 plan, and add them to the Planner tables. 201 plan, and add them to the Planner tables.
206 202
207 Also updates the status of the test run if the underlying TKO test move from 203 Also updates the status of the test run if the underlying TKO test move from
208 an active status to a completed status. 204 an active status to a completed status.
209 205
210 @return a list of dictionaries: 206 @return a list of dictionaries:
211 status: the status of the new (or updated) test run 207 status: the status of the new (or updated) test run
212 tko_test_idx: the ID of the TKO test added 208 tko_test_idx: the ID of the TKO test added
213 hostname: the host added 209 hostname: the host added
214 """ 210 """
215 plan = models.Plan.objects.get(id=plan_id) 211 plan = models.Plan.smart_get(plan_id)
216 updated = [] 212 updated = []
217 213
218 for planner_job in plan.job_set.all(): 214 for planner_job in plan.job_set.all():
219 known_statuses = dict((test_run.tko_test.test_idx, test_run.status) 215 known_statuses = dict((test_run.tko_test.test_idx, test_run.status)
220 for test_run in planner_job.testrun_set.all()) 216 for test_run in planner_job.testrun_set.all())
221 tko_tests_for_job = tko_models.Test.objects.filter( 217 tko_tests_for_job = tko_models.Test.objects.filter(
222 job__afe_job_id=planner_job.afe_job.id) 218 job__afe_job_id=planner_job.afe_job.id)
223 219
224 for tko_test in tko_tests_for_job: 220 for tko_test in tko_tests_for_job:
225 status = rpc_utils.compute_test_run_status(tko_test.status.word) 221 status = rpc_utils.compute_test_run_status(tko_test.status.word)
226 needs_update = (tko_test.test_idx not in known_statuses or 222 needs_update = (tko_test.test_idx not in known_statuses or
227 status != known_statuses[tko_test.test_idx]) 223 status != known_statuses[tko_test.test_idx])
228 if needs_update: 224 if needs_update:
229 hostnames = tko_test.machine.hostname.split(',') 225 hostnames = tko_test.machine.hostname.split(',')
230 for hostname in hostnames: 226 for hostname in hostnames:
231 rpc_utils.add_test_run( 227 rpc_utils.add_test_run(
232 plan, planner_job, tko_test, hostname, status) 228 plan, planner_job, tko_test, hostname, status)
233 updated.append({'status': status, 229 updated.append({'status': status,
234 'tko_test_idx': tko_test.test_idx, 230 'tko_test_idx': tko_test.test_idx,
235 'hostname': hostname}) 231 'hostname': hostname})
236 232
237 return updated 233 return updated
234
235
236 def get_failures(plan_id):
237 """
238 Gets a list of the untriaged failures associated with this plan
239
240 @return a list of dictionaries:
241 id: the failure ID, for passing back to triage the failure
242 group: the group for the failure. Normally the same as the
243 reason, but can be different for custom queries
244 machine: the failed machine
245 blocked: True if the failure caused the machine to block
246 test_name: Concatenation of the Planner alias and the TKO test
247 name for the failed test
248 reason: test failure reason
249 seen: True if the failure is marked as "seen"
250 """
251 plan = models.Plan.smart_get(plan_id)
252 result = {}
253
254 failures = plan.testrun_set.filter(
255 finalized=True, triaged=False,
256 status=model_attributes.TestRunStatus.FAILED)
257 failures = failures.select_related('test_job__test', 'host__host',
258 'tko_test')
259 for failure in failures:
260 test_name = '%s:%s' % (
261 failure.test_job.test_config.alias, failure.tko_test.test)
262
263 group_failures = result.setdefault(failure.tko_test.reason, [])
264 failure_dict = {'id': failure.id,
265 'machine': failure.host.host.hostname,
266 'blocked': bool(failure.host.blocked),
267 'test_name': test_name,
268 'reason': failure.tko_test.reason,
269 'seen': bool(failure.seen)}
270 group_failures.append(failure_dict)
271
272 return result
273
274
275 def get_test_runs(**filter_data):
276 """
277 Gets a list of test runs that match the filter data.
278
279 Returns a list of expanded TestRun object dictionaries. Specifically, the
280 "host" and "test_job" fields are expanded. Additionally, the "test_config"
281 field of the "test_job" expansion is also expanded.
282 """
283 result = []
284 for test_run in models.TestRun.objects.filter(**filter_data):
285 test_run_dict = test_run.get_object_dict()
286 test_run_dict['host'] = test_run.host.get_object_dict()
287 test_run_dict['test_job'] = test_run.test_job.get_object_dict()
288 test_run_dict['test_job']['test_config'] = (
289 test_run.test_job.test_config.get_object_dict())
290 result.append(test_run_dict)
291 return result
292
293
294 def skip_test(test_config_id, hostname):
295 """
296 Marks a test config as "skipped" for a given host
297 """
298 config = models.TestConfig.objects.get(id=test_config_id)
299 config.skipped_hosts.add(afe_models.Host.objects.get(hostname=hostname))
300
301
302 def mark_failures_as_seen(failure_ids):
303 """
304 Marks a set of failures as 'seen'
305
306 @param failure_ids: A list of failure IDs, as returned by get_failures(), to
307 mark as seen
308 """
309 models.TestRun.objects.filter(id__in=failure_ids).update(seen=True)
310
311
312 def process_failure(failure_id, host_action, test_action, labels=(),
313 keyvals=None, bugs=(), reason=None, invalidate=False):
314 """
315 Triage a failure
316
317 @param failure_id: The failure ID, as returned by get_failures()
318 @param host_action: One of 'Block', 'Unblock', 'Reinstall'
319 @param test_action: One of 'Skip', 'Rerun'
320
321 @param labels: Test labels to apply, by name
322 @param keyvals: Dictionary of job keyvals to add (or replace)
323 @param bugs: List of bug IDs to associate with this failure
324 @param reason: An override for the test failure reason
325 @param invalidate: True if failure should be invalidated for the purposes of
326 reporting. Defaults to False.
327 """
328 if keyvals is None:
329 keyvals = {}
330
331 host_choices = failure_actions.HostAction.values
332 test_choices = failure_actions.TestAction.values
333 if host_action not in host_choices:
334 raise model_logic.ValidationError(
335 {'host_action': ('host action %s not valid; must be one of %s'
336 % (host_action, ', '.join(host_choices)))})
337 if test_action not in test_choices:
338 raise model_logic.ValidationError(
339 {'test_action': ('test action %s not valid; must be one of %s'
340 % (test_action, ', '.join(test_choices)))})
341
342 failure = models.TestRun.objects.get(id=failure_id)
343
344 rpc_utils.process_host_action(failure.host, host_action)
345 rpc_utils.process_test_action(failure.test_job, test_action)
346
347 # Add the test labels
348 for label in labels:
349 tko_test_label, _ = (
350 tko_models.TestLabel.objects.get_or_create(name=label))
351 failure.tko_test.testlabel_set.add(tko_test_label)
352
353 # Set the job keyvals
354 for key, value in keyvals.iteritems():
355 keyval, created = tko_models.JobKeyval.objects.get_or_create(
356 job=failure.tko_test.job, key=key)
357 if not created:
358 tko_models.JobKeyval.objects.create(job=failure.tko_test.job,
359 key='original_' + key,
360 value=keyval.value)
361 keyval.value = value
362 keyval.save()
363
364 # Add the bugs
365 for bug_id in bugs:
366 bug, _ = models.Bug.objects.get_or_create(external_uid=bug_id)
367 failure.bugs.add(bug)
368
369 # Set the failure reason
370 if reason is not None:
371 tko_models.TestAttribute.objects.create(test=failure.tko_test,
372 attribute='original_reason',
373 value=failure.tko_test.reason)
374 failure.tko_test.reason = reason
375 failure.tko_test.save()
376
377 # Set 'invalidated', 'seen', and 'triaged'
378 failure.invalidated = invalidate
379 failure.seen = True
380 failure.triaged = True
381 failure.save()
382
383
384 def get_static_data():
385 result = {'motd': afe_rpc_utils.get_motd(),
386 'host_actions': sorted(failure_actions.HostAction.values),
387 'test_actions': sorted(failure_actions.TestAction.values)}
388 return result
OLDNEW
« no previous file with comments | « frontend/planner/models.py ('k') | frontend/planner/rpc_interface_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698