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

Side by Side Diff: tools/android/loading/cloud/frontend/clovis_frontend.py

Issue 2053363002: tools/android/loading Add link to dashboard and a way to cancel tasks. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@frontendTimeout
Patch Set: Created 4 years, 6 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 | « no previous file | tools/android/loading/cloud/frontend/templates/base.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2016 The Chromium Authors. All rights reserved. 1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import logging 5 import logging
6 import datetime 6 import datetime
7 import math 7 import math
8 import os 8 import os
9 import sys 9 import sys
10 import time 10 import time
(...skipping 20 matching lines...) Expand all
31 clovis_logger = logging.getLogger('clovis_frontend') 31 clovis_logger = logging.getLogger('clovis_frontend')
32 clovis_logger.setLevel(logging.DEBUG) 32 clovis_logger.setLevel(logging.DEBUG)
33 project_name = app_identity.get_application_id() 33 project_name = app_identity.get_application_id()
34 instance_helper = common.google_instance_helper.GoogleInstanceHelper( 34 instance_helper = common.google_instance_helper.GoogleInstanceHelper(
35 credentials=GoogleCredentials.get_application_default(), 35 credentials=GoogleCredentials.get_application_default(),
36 project=project_name, 36 project=project_name,
37 logger=clovis_logger) 37 logger=clovis_logger)
38 app = flask.Flask(__name__) 38 app = flask.Flask(__name__)
39 39
40 40
41 def Render(message, memory_logs=None): 41 def RenderJobCreationPage(message, memory_logs=None):
42 """Renders the log.html template. 42 """Renders the log.html template.
43 43
44 Args: 44 Args:
45 message (str): Main content of the page. 45 message (str): Main content of the page.
46 memory_logs (MemoryLogs): Optional logs. 46 memory_logs (MemoryLogs): Optional logs.
47 """ 47 """
48 log = None 48 log = None
49 if memory_logs: 49 if memory_logs:
50 log = memory_logs.Flush().split('\n') 50 log = memory_logs.Flush().split('\n')
51 return flask.render_template('log.html', body=message, log=log) 51 return flask.render_template('log.html', body=message, log=log,
52 title='Job Creation Status')
52 53
53 54
54 def PollWorkers(tag, start_time, timeout_hours, email_address, task_url): 55 def PollWorkers(tag, start_time, timeout_hours, email_address, task_url):
55 """Checks if there are workers associated with tag, by polling the instance 56 """Checks if there are workers associated with tag, by polling the instance
56 group. When all workers are finished, the instance group and the instance 57 group. When all workers are finished, the instance group and the instance
57 template are destroyed. 58 template are destroyed.
58 After some timeout delay, the instance group is destroyed even if there are 59 After some timeout delay, the instance group is destroyed even if there are
59 still workers associated to it, which has the effect of killing all these 60 still workers associated to it, which has the effect of killing all these
60 workers. 61 workers.
61 62
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 def StartFromJsonString(http_body_str): 317 def StartFromJsonString(http_body_str):
317 """Main function handling a JSON task posted by the user.""" 318 """Main function handling a JSON task posted by the user."""
318 # Set up logging. 319 # Set up logging.
319 memory_logs = MemoryLogs(clovis_logger) 320 memory_logs = MemoryLogs(clovis_logger)
320 memory_logs.Start() 321 memory_logs.Start()
321 322
322 # Load the task from JSON. 323 # Load the task from JSON.
323 task = ClovisTask.FromJsonString(http_body_str) 324 task = ClovisTask.FromJsonString(http_body_str)
324 if not task: 325 if not task:
325 clovis_logger.error('Invalid JSON task.') 326 clovis_logger.error('Invalid JSON task.')
326 return Render('Invalid JSON task:\n' + http_body_str, memory_logs) 327 return RenderJobCreationPage(
328 'Invalid JSON task:\n' + http_body_str, memory_logs)
327 329
328 task_tag = task.BackendParams()['tag'] 330 task_tag = task.BackendParams()['tag']
329 clovis_logger.info('Start processing %s task with tag %s.' % (task.Action(), 331 clovis_logger.info('Start processing %s task with tag %s.' % (task.Action(),
330 task_tag)) 332 task_tag))
331 user_email = email_helper.GetUserEmail() 333 user_email = email_helper.GetUserEmail()
332 334
333 # Write the job to the datastore. 335 # Write the job to the datastore.
334 frontend_job = FrontendJob.CreateForTag(task_tag) 336 frontend_job = FrontendJob.CreateForTag(task_tag)
335 frontend_job.email = user_email 337 frontend_job.email = user_email
336 frontend_job.status = 'not_started' 338 frontend_job.status = 'not_started'
337 frontend_job.clovis_task = task.ToJsonString() 339 frontend_job.clovis_task = task.ToJsonString()
338 frontend_job.put() 340 frontend_job.put()
339 341
340 # Process the job on the queue, to avoid timeout issues. 342 # Process the job on the queue, to avoid timeout issues.
341 deferred.defer(SpawnTasksOnBackgroundQueue, task_tag) 343 deferred.defer(SpawnTasksOnBackgroundQueue, task_tag)
342 344
343 return Render( 345 return RenderJobCreationPage(
344 flask.Markup( 346 flask.Markup(
345 '<a href="%s">See progress.</a>' % FrontendJob.GetJobURL(task_tag)), 347 '<a href="%s">See progress.</a>' % FrontendJob.GetJobURL(task_tag)),
346 memory_logs) 348 memory_logs)
347 349
348 350
349 def SpawnTasksOnBackgroundQueue(task_tag): 351 def SpawnTasksOnBackgroundQueue(task_tag):
350 """Spawns Clovis tasks associated with task_tag from the backgound queue. 352 """Spawns Clovis tasks associated with task_tag from the backgound queue.
351 353
352 This function is mostly a wrapper around SpawnTasks() that catches exceptions. 354 This function is mostly a wrapper around SpawnTasks() that catches exceptions.
353 It is assumed that a FrontendJob for task_tag exists. 355 It is assumed that a FrontendJob for task_tag exists.
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
429 sequential_duration_s / target_parallel_duration_s) 431 sequential_duration_s / target_parallel_duration_s)
430 432
431 # Check the instance quotas. 433 # Check the instance quotas.
432 clovis_logger.info( 434 clovis_logger.info(
433 'Requesting %i instances.' % task.BackendParams()['instance_count']) 435 'Requesting %i instances.' % task.BackendParams()['instance_count'])
434 frontend_job.status = 'checking_instance_quotas' 436 frontend_job.status = 'checking_instance_quotas'
435 max_instances = instance_helper.GetAvailableInstanceCount() 437 max_instances = instance_helper.GetAvailableInstanceCount()
436 if max_instances == -1: 438 if max_instances == -1:
437 frontend_job.status = 'instance_count_error' 439 frontend_job.status = 'instance_count_error'
438 return 440 return
439 elif task.BackendParams()['instance_count'] == 0: 441 elif max_instances == 0 and task.BackendParams()['instance_count'] > 0:
440 frontend_job.status = 'no_instance_available_error' 442 frontend_job.status = 'no_instance_available_error'
441 return 443 return
442 elif max_instances < task.BackendParams()['instance_count']: 444 elif max_instances < task.BackendParams()['instance_count']:
443 clovis_logger.warning( 445 clovis_logger.warning(
444 'Instance count limited by quota: %i available / %i requested.' % ( 446 'Instance count limited by quota: %i available / %i requested.' % (
445 max_instances, task.BackendParams()['instance_count'])) 447 max_instances, task.BackendParams()['instance_count']))
446 task.BackendParams()['instance_count'] = max_instances 448 task.BackendParams()['instance_count'] = max_instances
447 449
448 # Compute the timeout if there is none specified. 450 # Compute the timeout if there is none specified.
449 expected_duration_s = sequential_duration_s / ( 451 expected_duration_s = sequential_duration_s / (
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
514 def Root(): 516 def Root():
515 """Home page: show the new task form.""" 517 """Home page: show the new task form."""
516 return flask.render_template('form.html') 518 return flask.render_template('form.html')
517 519
518 520
519 @app.route('/form_sent', methods=['POST']) 521 @app.route('/form_sent', methods=['POST'])
520 def StartFromForm(): 522 def StartFromForm():
521 """HTML form endpoint.""" 523 """HTML form endpoint."""
522 data_stream = flask.request.files.get('json_task') 524 data_stream = flask.request.files.get('json_task')
523 if not data_stream: 525 if not data_stream:
524 return Render('Failed, no content.') 526 return RenderJobCreationPage('Failed, no content.')
525 http_body_str = data_stream.read() 527 http_body_str = data_stream.read()
526 return StartFromJsonString(http_body_str) 528 return StartFromJsonString(http_body_str)
527 529
528 530
531 @app.route('/kill_job')
532 def KillJob():
533 tag = flask.request.args.get('tag')
534 page_title = 'Kill Job'
535 if not tag:
536 return flask.render_template('log.html', body='Failed: Invalid tag.',
537 title=page_title)
538
539 frontend_job = FrontendJob.GetFromTag(tag)
540
541 if not frontend_job:
542 return flask.render_template('log.html', body='Job not found.',
543 title=page_title)
544 Finalize(tag, frontend_job.email, 'CANCELED', frontend_job.task_url)
545
546 body = 'Killed job %s.' % tag
547 return flask.render_template('log.html', body=body, title=page_title)
548
549
529 @app.route('/list_jobs') 550 @app.route('/list_jobs')
530 def ShowTaskList(): 551 def ShowJobList():
531 """Shows a list of all active jobs.""" 552 """Shows a list of all active jobs."""
532 tags = FrontendJob.ListJobs() 553 tags = FrontendJob.ListJobs()
554 page_title = 'Active Jobs'
533 555
534 if not tags: 556 if not tags:
535 Render('No active jobs.') 557 return flask.render_template('log.html', body='No active job.',
558 title=page_title)
536 559
537 html = flask.Markup('Active tasks:<ul>') 560 html = ''
538 for tag in tags: 561 for tag in tags:
539 html += flask.Markup( 562 html += flask.Markup(
540 '<li><a href="%s">%s</a></li>') % (FrontendJob.GetJobURL(tag), tag) 563 '<li><a href="%s">%s</a></li>') % (FrontendJob.GetJobURL(tag), tag)
541 html += flask.Markup('</ul>') 564 html += flask.Markup('</ul>')
542 return Render(html) 565 return flask.render_template('log.html', body=html, title=page_title)
543 566
544 567
545 @app.route(FrontendJob.SHOW_JOB_URL) 568 @app.route(FrontendJob.SHOW_JOB_URL)
546 def ShowTask(): 569 def ShowJob():
547 """Shows basic information abour a job.""" 570 """Shows basic information abour a job."""
548 tag = flask.request.args.get('tag') 571 tag = flask.request.args.get('tag')
572 page_title = 'Job Information'
549 if not tag: 573 if not tag:
550 return Render('Invalid task tag.') 574 return flask.render_template('log.html', body='Invalid tag.',
575 title=page_title)
551 576
552 frontend_job = FrontendJob.GetFromTag(tag) 577 frontend_job = FrontendJob.GetFromTag(tag)
553 578
554 if not frontend_job: 579 if not frontend_job:
555 return Render('Task not found.') 580 return flask.render_template('log.html', body='Job not found.',
556 581 title=page_title)
557 message = flask.Markup('Task details:' + frontend_job.RenderAsHtml())
558 582
559 log = None 583 log = None
560 if frontend_job.log: 584 if frontend_job.log:
561 log = frontend_job.log.split('\n') 585 log = frontend_job.log.split('\n')
562 586
563 return flask.render_template('log.html', body=message, log=log) 587 body = flask.Markup(frontend_job.RenderAsHtml())
588 body += flask.Markup('<a href="/kill_job?tag=%s">Kill</a>' % tag)
589 return flask.render_template('log.html', log=log, title=page_title,
590 body=body)
564 591
565 592
566 @app.errorhandler(404) 593 @app.errorhandler(404)
567 def PageNotFound(e): # pylint: disable=unused-argument 594 def PageNotFound(e): # pylint: disable=unused-argument
568 """Return a custom 404 error.""" 595 """Return a custom 404 error."""
569 return 'Sorry, Nothing at this URL.', 404 596 return 'Sorry, Nothing at this URL.', 404
570 597
571 598
572 @app.errorhandler(500) 599 @app.errorhandler(500)
573 def ApplicationError(e): 600 def ApplicationError(e):
574 """Return a custom 500 error.""" 601 """Return a custom 500 error."""
575 return 'Sorry, unexpected error: {}'.format(e), 499 602 return 'Sorry, unexpected error: {}'.format(e), 499
OLDNEW
« no previous file with comments | « no previous file | tools/android/loading/cloud/frontend/templates/base.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698