Index: tools/android/loading/cloud/frontend/clovis_frontend.py |
diff --git a/tools/android/loading/cloud/frontend/clovis_frontend.py b/tools/android/loading/cloud/frontend/clovis_frontend.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8eeee35f157298928a86d12173c059ee89d82591 |
--- /dev/null |
+++ b/tools/android/loading/cloud/frontend/clovis_frontend.py |
@@ -0,0 +1,109 @@ |
+# Copyright 2016 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import flask |
+from google.appengine.api import taskqueue |
+import json |
+import os |
+import sys |
+import uuid |
+ |
+from common.clovis_task import ClovisTask |
+ |
+ |
+app = flask.Flask(__name__) |
+ |
+ |
+def StartFromJson(http_body_str): |
+ """Creates a new batch of tasks from its JSON representation.""" |
+ task = ClovisTask.FromJsonDict(http_body_str) |
+ if not task: |
+ return 'Invalid JSON task:\n%s\n' % http_body_str |
+ |
+ task_tag = task.TaskqueueTag() |
+ if not task_tag: |
+ task_tag = uuid.uuid1() |
+ |
+ sub_tasks = [] |
+ if task.Action() == 'trace': |
+ sub_tasks = SplitTraceTask(task) |
+ else: |
+ return 'Unsupported action: %s\n' % task.Action() |
+ |
+ return EnqueueTasks(sub_tasks, task_tag) |
+ |
+ |
+def SplitTraceTask(task): |
+ """Split a tracing task with potentially many URLs into several tracing tasks |
+ with few URLs. |
+ """ |
+ params = task.Params() |
+ urls = params['urls'] |
+ |
+ # Split the task in smaller tasks with fewer URLs each. |
+ urls_per_task = 1 |
+ sub_tasks = [] |
+ for i in range(0, len(urls), urls_per_task): |
+ sub_task_params = params.copy() |
+ sub_task_params['urls'] = [url for url in urls[i:i+urls_per_task]] |
+ sub_tasks.append(ClovisTask(task.Action(), sub_task_params, |
+ task.TaskqueueTag())) |
+ return sub_tasks |
+ |
+ |
+def EnqueueTasks(tasks, task_tag): |
+ """Enqueues a list of tasks in the Google Cloud task queue, for consumption by |
+ Google Compute Engine. |
+ """ |
+ q = taskqueue.Queue('clovis-queue') |
+ retry_options = taskqueue.TaskRetryOptions(task_retry_limit=3) |
+ # Add tasks to the queue by groups. |
+ # TODO(droger): This support to thousands of tasks, but maybe not millions. |
+ # Defer the enqueuing if it times out. |
+ # is too large. |
+ group_size = 100 |
+ callbacks = [] |
+ try: |
+ for i in range(0, len(tasks), group_size): |
+ group = tasks[i:i+group_size] |
+ taskqueue_tasks = [ |
+ taskqueue.Task(payload=task.ToJsonDict(), method='PULL', tag=task_tag, |
+ retry_options=retry_options) |
+ for task in group] |
+ rpc = taskqueue.create_rpc() |
+ q.add_async(task=taskqueue_tasks, rpc=rpc) |
+ callbacks.append(rpc) |
+ for callback in callbacks: |
+ callback.get_result() |
+ except Exception as e: |
+ return 'Exception:' + type(e).__name__ + ' ' + str(e.args) + '\n' |
+ return 'pushed %i tasks with tag: %s\n' % (len(tasks), task_tag) |
+ |
+ |
+@app.route('/') |
+def Root(): |
+ """Home page: redirect to the static form.""" |
+ return flask.redirect('/static/form.html') |
+ |
+ |
+@app.route('/form_sent', methods=['POST']) |
+def StartFromForm(): |
+ """HTML form endpoint""" |
+ data_stream = flask.request.files.get('json_task') |
+ if not data_stream: |
+ return 'failed' |
+ http_body_str = data_stream.read() |
+ return StartFromJson(http_body_str) |
+ |
+ |
+@app.errorhandler(404) |
+def PageNotFound(e): # pylint: disable=unused-argument |
+ """Return a custom 404 error.""" |
+ return 'Sorry, Nothing at this URL.', 404 |
+ |
+ |
+@app.errorhandler(500) |
+def ApplicationError(e): |
+ """Return a custom 500 error.""" |
+ return 'Sorry, unexpected error: {}'.format(e), 499 |