blob: 6f92eee272e7b740536865e56397c0b435b235fa [file] [log] [blame]
Xixuan Wu835dee22017-09-07 10:47:29 -07001# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Module for executing tasks queued by suite scheduler."""
Xixuan Wu0a8d3ee2017-10-19 11:33:26 -07006# pylint: disable=g-bad-import-order
Xixuan Wu835dee22017-09-07 10:47:29 -07007
Prathmesh Prabhue8182312020-03-06 23:33:07 -08008import collections
Xixuan Wu835dee22017-09-07 10:47:29 -07009import logging
10
Xinan Lin3ba18a02019-08-13 15:44:55 -070011import buildbucket
12import constants
Xixuan Wu835dee22017-09-07 10:47:29 -070013
Xixuan Wu0a8d3ee2017-10-19 11:33:26 -070014import apiclient
Xixuan Wu835dee22017-09-07 10:47:29 -070015from google.appengine.api import taskqueue
16from google.appengine.runtime import apiproxy_errors
17
18
19SUITES_QUEUE = 'suitesQueue'
Xixuan Wu835dee22017-09-07 10:47:29 -070020
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080021Options = collections.namedtuple(
22 'Options',
23 ['batch_size', 'multirequest_size', 'per_suite_multirequest_size'])
Prathmesh Prabhue8182312020-03-06 23:33:07 -080024
25_DEFAULT_OPTIONS = Options(
Xinan Lin3b9b6f42020-03-16 16:10:37 -070026 # The maximum CTP multi-requests can be sent in this run.
27 batch_size=10,
Prathmesh Prabhue8182312020-03-06 23:33:07 -080028 multirequest_size=constants.Buildbucket.MULTIREQUEST_SIZE,
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080029 # A limit on the number of tasks included per request for specific suites.
30 per_suite_multirequest_size={
31 # crbug.com/1028732: Generates too many results to store in BuildBucket
32 # output properties.
Prathmesh Prabhu3e0bfbb2020-03-11 12:28:17 -070033 'arc-cts': 15,
34 'arc-cts-qual': 15,
35 'arc-cts-unibuild': 15,
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080036 'arc-gts': 10,
Prathmesh Prabhu3e0bfbb2020-03-11 12:28:17 -070037 'crosbolt_perf_perbuild': 15,
38 'ent-nightly': 15,
39 'faft_bios': 15,
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080040 'graphics_per-day': 10,
Prathmesh Prabhu3e0bfbb2020-03-11 12:28:17 -070041 'graphics_per-week': 15,
42 'wifi_matfunc': 10,
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080043 })
Prathmesh Prabhue8182312020-03-06 23:33:07 -080044
Xixuan Wu835dee22017-09-07 10:47:29 -070045
Prathmesh Prabhu46331482020-03-07 00:18:10 -080046def new_task_processor():
47 """Factory function to create a task processor appropriate to environment."""
Prathmesh Prabhu8f43d312020-03-07 00:25:06 -080048 # Schedule tests to PROD_BUILDER if in production project, otherwise use
49 # STAGING_BUILDER.
50 builder = constants.Buildbucket.STAGING_BUILDER
51 if (constants.environment() == constants.RunningEnv.ENV_PROD
52 and constants.application_id() == constants.AppID.PROD_APP):
53 builder = constants.Buildbucket.PROD_BUILDER
54 test_platform_client = buildbucket.TestPlatformClient(
55 constants.Buildbucket.HOST, constants.Buildbucket.PROJECT,
56 constants.Buildbucket.BUCKET, builder)
57 return TaskProcessor(
58 queue_name=SUITES_QUEUE,
59 test_platform_client=test_platform_client,
60 options=_DEFAULT_OPTIONS)
Prathmesh Prabhu46331482020-03-07 00:18:10 -080061
62
Xixuan Wu835dee22017-09-07 10:47:29 -070063class TaskProcessor(object):
64 """A class capable of executing tasks by kicking off suites.
65
Prathmesh Prabhu46331482020-03-07 00:18:10 -080066 This class fetches tasks from pullqueue, and kicks off suites
67 represented by tasks' params.
Xixuan Wu835dee22017-09-07 10:47:29 -070068 """
69
Prathmesh Prabhu8f43d312020-03-07 00:25:06 -080070 def __init__(self, queue_name, test_platform_client, options):
Xixuan Wu835dee22017-09-07 10:47:29 -070071 """Initialize a task executor for further pulling & execution.
72
73 Args:
Xinan Lin3ba18a02019-08-13 15:44:55 -070074 queue_name: The name of a pull queue.
Prathmesh Prabhu8f43d312020-03-07 00:25:06 -080075 test_platform_client: A buildbucket.TestPlatformClient object.
Prathmesh Prabhu7b961d52020-03-06 23:57:42 -080076 options: Options to configure the task processor.
Xixuan Wu835dee22017-09-07 10:47:29 -070077 """
Xixuan Wua5a29442017-10-11 11:03:02 -070078 self.queue = taskqueue.Queue(queue_name)
Prathmesh Prabhue8182312020-03-06 23:33:07 -080079 self._options = options
Prathmesh Prabhu8f43d312020-03-07 00:25:06 -080080 self.test_platform_client = test_platform_client
Xixuan Wu835dee22017-09-07 10:47:29 -070081
82 def batch_execute(self):
Craig Bergstrom58263d32018-04-26 14:11:35 -060083 """Execute tasks."""
Xinan Lin3b9b6f42020-03-16 16:10:37 -070084 sent_multireq_count = 0
85 while (sent_multireq_count < self._options.batch_size):
Xixuan Wu835dee22017-09-07 10:47:29 -070086 try:
Prathmesh Prabhue8182312020-03-06 23:33:07 -080087 tasks = self.queue.lease_tasks_by_tag(
88 3600, self._options.multirequest_size, deadline=60)
Xinan Lin9e4917d2019-11-04 10:58:47 -080089 except (taskqueue.UnknownQueueError,
90 taskqueue.TransientError,
91 apiproxy_errors.DeadlineExceededError) as e:
92 logging.exception(e)
93 raise
Xixuan Wua5a29442017-10-11 11:03:02 -070094
Xinan Lin9e4917d2019-11-04 10:58:47 -080095 if not tasks:
96 return
97
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080098 tasks = self._limit_heavy_tasks(tasks)
99 executed_tasks = []
Xinan Lin9e4917d2019-11-04 10:58:47 -0800100 try:
101 executed_tasks.extend(
Prathmesh Prabhu55c40032020-03-07 00:45:15 -0800102 self.test_platform_client.multirequest_run(tasks, _suite(tasks)))
Xinan Lin9e4917d2019-11-04 10:58:47 -0800103 except (ValueError,
104 buildbucket.BuildbucketRunError,
105 apiclient.errors.HttpError) as e:
106 logging.exception('Failed to kick off %d tasks for suite %s',
Prathmesh Prabhu55c40032020-03-07 00:45:15 -0800107 len(tasks), _suite(tasks))
Xixuan Wu835dee22017-09-07 10:47:29 -0700108 finally:
109 if executed_tasks:
Xinan Lin3b9b6f42020-03-16 16:10:37 -0700110 sent_multireq_count += 1
Xinan Lin9e4917d2019-11-04 10:58:47 -0800111 logging.info('Successfully kicking %d tasks for suite %s',
Prathmesh Prabhu55c40032020-03-07 00:45:15 -0800112 len(executed_tasks), _suite(tasks))
Xixuan Wua5a29442017-10-11 11:03:02 -0700113 self.queue.delete_tasks(executed_tasks)
114
115 def purge(self):
116 """Purge the entire tasks in the task queue."""
117 self.queue.purge()
Xixuan Wu835dee22017-09-07 10:47:29 -0700118
Prathmesh Prabhu55c40032020-03-07 00:45:15 -0800119 def _limit_heavy_tasks(self, tasks):
120 """Further limits tasks known to cause large load on cros_test_platform."""
121 if not tasks:
122 return tasks
123 limit = self._options.per_suite_multirequest_size.get(
124 _suite(tasks),
125 self._options.multirequest_size,
126 )
127 keep = tasks[:limit]
128 forget = tasks[limit:]
129 for task in forget:
130 self.queue.modify_task_lease(task, 0)
131 return keep
132
Xixuan Wu835dee22017-09-07 10:47:29 -0700133
Xinan Lin9e4917d2019-11-04 10:58:47 -0800134def push(queue_name, tag=None, **suite_kwargs):
Xixuan Wu835dee22017-09-07 10:47:29 -0700135 """Push suites to suite queue for later kickoff.
136
137 Args:
138 queue_name: the name of a pull queue.
Xinan Lin9e4917d2019-11-04 10:58:47 -0800139 tag: tag of a pull queue task.
Xixuan Wu835dee22017-09-07 10:47:29 -0700140 **suite_kwargs: the args for a suite to kick off.
141 """
142 queue = taskqueue.Queue(queue_name)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800143 queue.add(taskqueue.Task(method='PULL', tag=tag, params=suite_kwargs))
Prathmesh Prabhu55c40032020-03-07 00:45:15 -0800144
145
146def _suite(tasks):
147 return tasks[0].tag