| OLD | NEW |
| 1 # Copyright 2013 The LUCI Authors. All rights reserved. | 1 # Copyright 2013 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 """Main entry point for Swarming service. | 5 """Main entry point for Swarming service. |
| 6 | 6 |
| 7 This file contains the URL handlers for all the Swarming service URLs, | 7 This file contains the URL handlers for all the Swarming service URLs, |
| 8 implemented using the webapp2 framework. | 8 implemented using the webapp2 framework. |
| 9 """ | 9 """ |
| 10 | 10 |
| (...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 version = bot_code.get_bot_version(self.request.host_url) | 238 version = bot_code.get_bot_version(self.request.host_url) |
| 239 bots, cursor, more = fetch_future.get_result() | 239 bots, cursor, more = fetch_future.get_result() |
| 240 # Prefetch the tasks. We don't actually use the value here, it'll be | 240 # Prefetch the tasks. We don't actually use the value here, it'll be |
| 241 # implicitly used by ndb local's cache when refetched by the html template. | 241 # implicitly used by ndb local's cache when refetched by the html template. |
| 242 tasks = filter(None, (b.task for b in bots)) | 242 tasks = filter(None, (b.task for b in bots)) |
| 243 ndb.get_multi(tasks) | 243 ndb.get_multi(tasks) |
| 244 num_bots_busy = num_bots_busy_future.get_result() | 244 num_bots_busy = num_bots_busy_future.get_result() |
| 245 num_bots_dead = num_bots_dead_future.get_result() | 245 num_bots_dead = num_bots_dead_future.get_result() |
| 246 num_bots_quarantined = num_bots_quarantined_future.get_result() | 246 num_bots_quarantined = num_bots_quarantined_future.get_result() |
| 247 num_bots_total = num_bots_total_future.get_result() | 247 num_bots_total = num_bots_total_future.get_result() |
| 248 try_link = '/newui/botlist?l=%d' % limit | 248 try_link = '/botlist?l=%d' % limit |
| 249 if dimensions: | 249 if dimensions: |
| 250 try_link += '&f=' + '&f='.join(dimensions) | 250 try_link += '&f=' + '&f='.join(dimensions) |
| 251 params = { | 251 params = { |
| 252 'bots': bots, | 252 'bots': bots, |
| 253 'current_version': version, | 253 'current_version': version, |
| 254 'cursor': cursor.urlsafe() if cursor and more else '', | 254 'cursor': cursor.urlsafe() if cursor and more else '', |
| 255 'dimensions': '\n'.join(dimensions), | 255 'dimensions': '\n'.join(dimensions), |
| 256 'is_admin': acl.is_admin(), | 256 'is_admin': acl.is_admin(), |
| 257 'is_privileged_user': acl.is_privileged_user(), | 257 'is_privileged_user': acl.is_privileged_user(), |
| 258 'limit': limit, | 258 'limit': limit, |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 'bot_id': bot_id, | 340 'bot_id': bot_id, |
| 341 'current_version': bot_code.get_bot_version(self.request.host_url), | 341 'current_version': bot_code.get_bot_version(self.request.host_url), |
| 342 'cursor': cursor.urlsafe() if cursor and more else None, | 342 'cursor': cursor.urlsafe() if cursor and more else None, |
| 343 'events': events, | 343 'events': events, |
| 344 'idle_time': idle_time, | 344 'idle_time': idle_time, |
| 345 'is_admin': acl.is_admin(), | 345 'is_admin': acl.is_admin(), |
| 346 'limit': limit, | 346 'limit': limit, |
| 347 'now': now, | 347 'now': now, |
| 348 'run_results': run_results, | 348 'run_results': run_results, |
| 349 'run_time': run_time, | 349 'run_time': run_time, |
| 350 'try_link': '/newui/bot?id=%s' % bot_id, | 350 'try_link': '/bot?id=%s' % bot_id, |
| 351 'xsrf_token': self.generate_xsrf_token(), | 351 'xsrf_token': self.generate_xsrf_token(), |
| 352 } | 352 } |
| 353 self.response.write( | 353 self.response.write( |
| 354 template.render('swarming/restricted_bot.html', params)) | 354 template.render('swarming/restricted_bot.html', params)) |
| 355 | 355 |
| 356 | 356 |
| 357 class BotDeleteHandler(auth.AuthenticatingHandler): | 357 class BotDeleteHandler(auth.AuthenticatingHandler): |
| 358 """Deletes a known bot. | 358 """Deletes a known bot. |
| 359 | 359 |
| 360 This only deletes the BotInfo, not BotRoot, BotEvent's nor BotSettings. | 360 This only deletes the BotInfo, not BotRoot, BotEvent's nor BotSettings. |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 519 # In theory, t.duration_as_seen_by_server should always be set when | 519 # In theory, t.duration_as_seen_by_server should always be set when |
| 520 # t.deduped_from is set but there has some broken entities in the datastore. | 520 # t.deduped_from is set but there has some broken entities in the datastore. |
| 521 total_saved = safe_sum( | 521 total_saved = safe_sum( |
| 522 t.duration_as_seen_by_server for t in tasks | 522 t.duration_as_seen_by_server for t in tasks |
| 523 if t.deduped_from and t.duration_as_seen_by_server) | 523 if t.deduped_from and t.duration_as_seen_by_server) |
| 524 duration_sum = safe_sum(durations) | 524 duration_sum = safe_sum(durations) |
| 525 total_saved_percent = ( | 525 total_saved_percent = ( |
| 526 (100. * total_saved.total_seconds() / duration_sum.total_seconds()) | 526 (100. * total_saved.total_seconds() / duration_sum.total_seconds()) |
| 527 if duration_sum else 0.) | 527 if duration_sum else 0.) |
| 528 | 528 |
| 529 try_link = '/newui/tasklist?l=%d' % limit | 529 try_link = '/tasklist?l=%d' % limit |
| 530 if task_tags: | 530 if task_tags: |
| 531 try_link += '&f=' + '&f='.join(task_tags) | 531 try_link += '&f=' + '&f='.join(task_tags) |
| 532 params = { | 532 params = { |
| 533 'cursor': cursor_str, | 533 'cursor': cursor_str, |
| 534 'duration_average': avg(durations), | 534 'duration_average': avg(durations), |
| 535 'duration_median': median(durations), | 535 'duration_median': median(durations), |
| 536 'duration_sum': duration_sum, | 536 'duration_sum': duration_sum, |
| 537 'has_pending': any(t.is_pending for t in tasks), | 537 'has_pending': any(t.is_pending for t in tasks), |
| 538 'has_running': any(t.is_running for t in tasks), | 538 'has_running': any(t.is_running for t in tasks), |
| 539 'is_admin': acl.is_admin(), | 539 'is_admin': acl.is_admin(), |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 709 'is_gae_admin': users.is_current_user_admin(), | 709 'is_gae_admin': users.is_current_user_admin(), |
| 710 'is_privileged_user': acl.is_privileged_user(), | 710 'is_privileged_user': acl.is_privileged_user(), |
| 711 'following_task': following_task, | 711 'following_task': following_task, |
| 712 'full_appid': os.environ['APPLICATION_ID'], | 712 'full_appid': os.environ['APPLICATION_ID'], |
| 713 'host_url': self.request.host_url, | 713 'host_url': self.request.host_url, |
| 714 'is_running': result.state == task_result.State.RUNNING, | 714 'is_running': result.state == task_result.State.RUNNING, |
| 715 'parent_task': parent_task, | 715 'parent_task': parent_task, |
| 716 'previous_task': previous_task, | 716 'previous_task': previous_task, |
| 717 'request': request, | 717 'request': request, |
| 718 'task': result, | 718 'task': result, |
| 719 'try_link': '/newui/task?id=%s' % task_id, | 719 'try_link': '/task?id=%s' % task_id, |
| 720 'xsrf_token': self.generate_xsrf_token(), | 720 'xsrf_token': self.generate_xsrf_token(), |
| 721 } | 721 } |
| 722 self.response.write(template.render('swarming/user_task.html', params)) | 722 self.response.write(template.render('swarming/user_task.html', params)) |
| 723 | 723 |
| 724 | 724 |
| 725 class TaskCancelHandler(BaseTaskHandler): | 725 class TaskCancelHandler(BaseTaskHandler): |
| 726 """Cancel a task.""" | 726 """Cancel a task.""" |
| 727 | 727 |
| 728 @auth.require(acl.is_user) | 728 @auth.require(acl.is_user) |
| 729 def post(self, task_id): | 729 def post(self, task_id): |
| (...skipping 22 matching lines...) Expand all Loading... |
| 752 # original one, but with new parameters. | 752 # original one, but with new parameters. |
| 753 new_request = task_request.new_request_clone( | 753 new_request = task_request.new_request_clone( |
| 754 original_request, allow_high_priority=acl.is_admin()) | 754 original_request, allow_high_priority=acl.is_admin()) |
| 755 result_summary = task_scheduler.schedule_request(new_request) | 755 result_summary = task_scheduler.schedule_request(new_request) |
| 756 self.redirect('/user/task/%s' % result_summary.task_id) | 756 self.redirect('/user/task/%s' % result_summary.task_id) |
| 757 | 757 |
| 758 | 758 |
| 759 ### Public pages. | 759 ### Public pages. |
| 760 | 760 |
| 761 | 761 |
| 762 class RootHandler(auth.AuthenticatingHandler): | 762 class OldUIHandler(auth.AuthenticatingHandler): |
| 763 @auth.public | 763 @auth.public |
| 764 def get(self): | 764 def get(self): |
| 765 params = { | 765 params = { |
| 766 'host_url': self.request.host_url, | 766 'host_url': self.request.host_url, |
| 767 'is_admin': acl.is_admin(), | 767 'is_admin': acl.is_admin(), |
| 768 'is_privileged_user': acl.is_privileged_user(), | 768 'is_privileged_user': acl.is_privileged_user(), |
| 769 'is_user': acl.is_user(), | 769 'is_user': acl.is_user(), |
| 770 'is_bootstrapper': acl.is_bootstrapper(), | 770 'is_bootstrapper': acl.is_bootstrapper(), |
| 771 'bootstrap_token': '...', | 771 'bootstrap_token': '...', |
| 772 'mapreduce_jobs': [], | 772 'mapreduce_jobs': [], |
| 773 'user_type': acl.get_user_type(), | 773 'user_type': acl.get_user_type(), |
| 774 'xsrf_token': '', | 774 'xsrf_token': '', |
| 775 } | 775 } |
| 776 if acl.is_admin(): | 776 if acl.is_admin(): |
| 777 params['mapreduce_jobs'] = [ | 777 params['mapreduce_jobs'] = [ |
| 778 {'id': job_id, 'name': job_def['job_name']} | 778 {'id': job_id, 'name': job_def['job_name']} |
| 779 for job_id, job_def in mapreduce_jobs.MAPREDUCE_JOBS.iteritems() | 779 for job_id, job_def in mapreduce_jobs.MAPREDUCE_JOBS.iteritems() |
| 780 ] | 780 ] |
| 781 params['xsrf_token'] = self.generate_xsrf_token() | 781 params['xsrf_token'] = self.generate_xsrf_token() |
| 782 if acl.is_bootstrapper(): | 782 if acl.is_bootstrapper(): |
| 783 params['bootstrap_token'] = bot_code.generate_bootstrap_token() | 783 params['bootstrap_token'] = bot_code.generate_bootstrap_token() |
| 784 self.response.write(template.render('swarming/root.html', params)) | 784 self.response.write(template.render('swarming/root.html', params)) |
| 785 | 785 |
| 786 | 786 |
| 787 class UIHandler(auth.AuthenticatingHandler): | 787 class UIHandler(auth.AuthenticatingHandler): |
| 788 @auth.public | 788 @auth.public |
| 789 def get(self, page): | 789 def get(self, page): |
| 790 if not page: | 790 if not page: |
| 791 page = "swarming" | 791 page = 'swarming' |
| 792 | 792 |
| 793 params = { | 793 params = { |
| 794 'client_id': config.settings().ui_client_id, | 794 'client_id': config.settings().ui_client_id, |
| 795 } | 795 } |
| 796 self.response.write(template.render( | 796 try: |
| 797 self.response.write(template.render( |
| 797 'swarming/public_%s_index.html' % page, params)) | 798 'swarming/public_%s_index.html' % page, params)) |
| 799 except template.TemplateNotFound: |
| 800 self.abort(404, 'Page not found.') |
| 798 | 801 |
| 799 | 802 |
| 800 class WarmupHandler(webapp2.RequestHandler): | 803 class WarmupHandler(webapp2.RequestHandler): |
| 801 def get(self): | 804 def get(self): |
| 802 auth.warmup() | 805 auth.warmup() |
| 803 bot_code.get_swarming_bot_zip(self.request.host_url) | 806 bot_code.get_swarming_bot_zip(self.request.host_url) |
| 804 utils.get_module_version_list(None, None) | 807 utils.get_module_version_list(None, None) |
| 805 self.response.headers['Content-Type'] = 'text/plain; charset=utf-8' | 808 self.response.headers['Content-Type'] = 'text/plain; charset=utf-8' |
| 806 self.response.write('ok') | 809 self.response.write('ok') |
| 807 | 810 |
| 808 | 811 |
| 809 class EmailHandler(webapp2.RequestHandler): | 812 class EmailHandler(webapp2.RequestHandler): |
| 810 """Blackhole any email sent.""" | 813 """Blackhole any email sent.""" |
| 811 def post(self, to): | 814 def post(self, to): |
| 812 pass | 815 pass |
| 813 | 816 |
| 814 | 817 |
| 815 def create_application(debug): | 818 def create_application(debug): |
| 816 template.bootstrap() | 819 template.bootstrap() |
| 817 utils.set_task_queue_module('default') | 820 utils.set_task_queue_module('default') |
| 818 | 821 |
| 819 routes = [ | 822 routes = [ |
| 820 # Frontend pages. They return HTML. | 823 # Frontend pages. They return HTML. |
| 821 # Public pages. | 824 # Public pages. |
| 822 ('/', RootHandler), | 825 ('/oldui', OldUIHandler), |
| 823 ('/stats', stats_gviz.StatsSummaryHandler), | 826 ('/stats', stats_gviz.StatsSummaryHandler), |
| 824 ('/newui/<page:[a-z]*>', UIHandler), | 827 ('/<page:(bot|botlist|task|tasklist|)>', UIHandler), |
| 825 | 828 |
| 826 # User pages. | 829 # User pages. |
| 827 ('/user/tasks', TasksHandler), | 830 ('/user/tasks', TasksHandler), |
| 828 ('/user/task/<task_id:[0-9a-fA-F]+>', TaskHandler), | 831 ('/user/task/<task_id:[0-9a-fA-F]+>', TaskHandler), |
| 829 ('/user/task/<task_id:[0-9a-fA-F]+>/cancel', TaskCancelHandler), | 832 ('/user/task/<task_id:[0-9a-fA-F]+>/cancel', TaskCancelHandler), |
| 830 ('/user/task/<task_id:[0-9a-fA-F]+>/retry', TaskRetryHandler), | 833 ('/user/task/<task_id:[0-9a-fA-F]+>/retry', TaskRetryHandler), |
| 831 | 834 |
| 832 # Privileged user pages. | 835 # Privileged user pages. |
| 833 ('/restricted/bots', BotsListHandler), | 836 ('/restricted/bots', BotsListHandler), |
| 834 ('/restricted/bot/<bot_id:[^/]+>', BotHandler), | 837 ('/restricted/bot/<bot_id:[^/]+>', BotHandler), |
| (...skipping 21 matching lines...) Expand all Loading... |
| 856 | 859 |
| 857 # If running on a local dev server, allow bots to connect without prior | 860 # If running on a local dev server, allow bots to connect without prior |
| 858 # groups configuration. Useful when running smoke test. | 861 # groups configuration. Useful when running smoke test. |
| 859 if utils.is_local_dev_server(): | 862 if utils.is_local_dev_server(): |
| 860 acl.bootstrap_dev_server_acls() | 863 acl.bootstrap_dev_server_acls() |
| 861 | 864 |
| 862 routes.extend(handlers_backend.get_routes()) | 865 routes.extend(handlers_backend.get_routes()) |
| 863 routes.extend(handlers_bot.get_routes()) | 866 routes.extend(handlers_bot.get_routes()) |
| 864 routes.extend(handlers_endpoints.get_routes()) | 867 routes.extend(handlers_endpoints.get_routes()) |
| 865 return webapp2.WSGIApplication(routes, debug=debug) | 868 return webapp2.WSGIApplication(routes, debug=debug) |
| OLD | NEW |