 Chromium Code Reviews
 Chromium Code Reviews Issue 2220373003:
  Allow botlist API call to respond to quarantined: and is_dead:  (Closed) 
  Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@master
    
  
    Issue 2220373003:
  Allow botlist API call to respond to quarantined: and is_dead:  (Closed) 
  Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@master| OLD | NEW | 
|---|---|
| 1 # Copyright 2015 The LUCI Authors. All rights reserved. | 1 # Copyright 2015 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 """This module defines Swarming Server endpoints handlers.""" | 5 """This module defines Swarming Server endpoints handlers.""" | 
| 6 | 6 | 
| 7 import datetime | 7 import datetime | 
| 8 import logging | 8 import logging | 
| 9 | 9 | 
| 10 from google.appengine.api import datastore_errors | 10 from google.appengine.api import datastore_errors | 
| (...skipping 590 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 601 message_conversion.task_result_to_rpc( | 601 message_conversion.task_result_to_rpc( | 
| 602 r, request.include_performance_stats) | 602 r, request.include_performance_stats) | 
| 603 for r in items | 603 for r in items | 
| 604 ], | 604 ], | 
| 605 now=now) | 605 now=now) | 
| 606 | 606 | 
| 607 | 607 | 
| 608 @swarming_api.api_class(resource_name='bots', path='bots') | 608 @swarming_api.api_class(resource_name='bots', path='bots') | 
| 609 class SwarmingBotsService(remote.Service): | 609 class SwarmingBotsService(remote.Service): | 
| 610 """Bots-related API.""" | 610 """Bots-related API.""" | 
| 611 | |
| 612 def _filter_dimensions(self, q, request): | |
| 
M-A Ruel
2016/08/09 18:05:27
maybe worth passing request.dimensions as an argum
 
kjlubick
2016/08/09 19:25:03
Done.
 | |
| 613 """Filters a ndb.Query for BotInfo based on dimensions in the request.""" | |
| 614 for d in request.dimensions: | |
| 615 parts = d.split(':', 1) | |
| 616 if len(parts) != 2 or any(i.strip() != i or not i for i in parts): | |
| 617 raise endpoints.BadRequestException('Invalid dimensions') | |
| 618 q = q.filter(bot_management.BotInfo.dimensions_flat == d) | |
| 619 return q | |
| 620 | |
| 621 def _filter_availability(self, q, request, now): | |
| 
M-A Ruel
2016/08/09 18:05:27
same here
def _filter_availability(self, q, is_bus
 
kjlubick
2016/08/09 19:25:03
Done.
 | |
| 622 """Filters a ndb.Query for BotInfo based on quarantined/is_dead.""" | |
| 623 val = swarming_rpcs.to_bool(request.quarantined) | |
| 624 if val is not None: | |
| 625 q = q.filter(bot_management.BotInfo.quarantined == val) | |
| 626 | |
| 627 dt = datetime.timedelta(seconds=config.settings().bot_death_timeout_secs) | |
| 628 timeout = now - dt | |
| 629 if request.is_dead == swarming_rpcs.ThreeStateBool.TRUE: | |
| 630 q = q.filter(bot_management.BotInfo.last_seen_ts < timeout) | |
| 631 elif request.is_dead == swarming_rpcs.ThreeStateBool.FALSE: | |
| 632 q = q.filter(bot_management.BotInfo.last_seen_ts > timeout) | |
| 633 return q | |
| 634 | |
| 611 @gae_ts_mon.instrument_endpoint() | 635 @gae_ts_mon.instrument_endpoint() | 
| 612 @auth.endpoints_method( | 636 @auth.endpoints_method( | 
| 613 swarming_rpcs.BotsRequest, swarming_rpcs.BotList, | 637 swarming_rpcs.BotsRequest, swarming_rpcs.BotList, | 
| 614 http_method='GET') | 638 http_method='GET') | 
| 615 @auth.require(acl.is_privileged_user) | 639 @auth.require(acl.is_privileged_user) | 
| 616 def list(self, request): | 640 def list(self, request): | 
| 617 """Provides list of known bots. | 641 """Provides list of known bots. | 
| 618 | 642 | 
| 619 Deleted bots will not be listed. | 643 Deleted bots will not be listed. | 
| 620 """ | 644 """ | 
| 621 logging.info('%s', request) | 645 logging.info('%s', request) | 
| 622 now = utils.utcnow() | 646 now = utils.utcnow() | 
| 623 q = bot_management.BotInfo.query().order(bot_management.BotInfo.key) | 647 q = bot_management.BotInfo.query() | 
| 624 for d in request.dimensions: | 648 q = self._filter_dimensions(q, request) | 
| 625 if not ':' in d: | 649 q = self._filter_availability(q, request, now) | 
| 626 raise endpoints.BadRequestException('Invalid dimensions') | 650 | 
| 627 parts = d.split(':', 1) | |
| 628 if len(parts) != 2 or any(i.strip() != i or not i for i in parts): | |
| 629 raise endpoints.BadRequestException('Invalid dimensions') | |
| 630 q = q.filter(bot_management.BotInfo.dimensions_flat == d) | |
| 631 bots, cursor = datastore_utils.fetch_page(q, request.limit, request.cursor) | 651 bots, cursor = datastore_utils.fetch_page(q, request.limit, request.cursor) | 
| 632 return swarming_rpcs.BotList( | 652 return swarming_rpcs.BotList( | 
| 633 cursor=cursor, | 653 cursor=cursor, | 
| 634 death_timeout=config.settings().bot_death_timeout_secs, | 654 death_timeout=config.settings().bot_death_timeout_secs, | 
| 635 items=[message_conversion.bot_info_to_rpc(bot, now) for bot in bots], | 655 items=[message_conversion.bot_info_to_rpc(bot, now) for bot in bots], | 
| 636 now=now) | 656 now=now) | 
| 637 | 657 | 
| 638 @gae_ts_mon.instrument_endpoint() | 658 @gae_ts_mon.instrument_endpoint() | 
| 639 @auth.endpoints_method( | 659 @auth.endpoints_method( | 
| 640 swarming_rpcs.BotsRequest, swarming_rpcs.BotsCount, | 660 swarming_rpcs.BotsRequest, swarming_rpcs.BotsCount, | 
| 641 http_method='GET') | 661 http_method='GET') | 
| 642 @auth.require(acl.is_privileged_user) | 662 @auth.require(acl.is_privileged_user) | 
| 643 def count(self, request): | 663 def count(self, request): | 
| 644 """Counts number of bots with given dimensions.""" | 664 """Counts number of bots with given dimensions.""" | 
| 645 logging.info('%s', request) | 665 logging.info('%s', request) | 
| 646 now = utils.utcnow() | 666 now = utils.utcnow() | 
| 647 q = bot_management.BotInfo.query() | 667 q = bot_management.BotInfo.query() | 
| 
M-A Ruel
2016/08/09 18:05:26
you can probably inline lines 667 and 668, and sam
 
kjlubick
2016/08/09 19:25:03
Cannot, it is too long
 | |
| 648 for d in request.dimensions: | 668 q = self._filter_dimensions(q, request) | 
| 649 parts = d.split(':', 1) | 669 | 
| 650 if len(parts) != 2 or any(i.strip() != i or not i for i in parts): | 670 quarantined = swarming_rpcs.to_bool(request.quarantined) | 
| 651 raise endpoints.BadRequestException('Invalid dimensions: %s' % d) | 671 is_dead = swarming_rpcs.to_bool(request.is_dead) | 
| 652 q = q.filter(bot_management.BotInfo.dimensions_flat == d) | |
| 653 f_count = q.count_async() | |
| 654 dt = datetime.timedelta(seconds=config.settings().bot_death_timeout_secs) | 672 dt = datetime.timedelta(seconds=config.settings().bot_death_timeout_secs) | 
| 655 timeout = now - dt | 673 timeout = now - dt | 
| 656 f_dead = q.filter( | 674 | 
| 657 bot_management.BotInfo.last_seen_ts < timeout).count_async() | 675 if quarantined is None and is_dead is None: | 
| 658 f_quarantined = q.filter( | 676 f_count = q.count_async() | 
| 659 bot_management.BotInfo.quarantined == True).count_async() | 677 f_dead = q.filter( | 
| 
M-A Ruel
2016/08/09 18:05:26
you can do:
f_dead = self._filter_availability(q,
 
kjlubick
2016/08/09 19:25:03
Done.
 | |
| 660 f_busy = q.filter(bot_management.BotInfo.is_busy == True).count_async() | 678 bot_management.BotInfo.last_seen_ts < timeout).count_async() | 
| 661 return swarming_rpcs.BotsCount( | 679 f_quarantined = q.filter( | 
| 
M-A Ruel
2016/08/09 18:05:27
same here
 
kjlubick
2016/08/09 19:25:03
Done.
 | |
| 662 count=f_count.get_result(), | 680 bot_management.BotInfo.quarantined == True).count_async() | 
| 663 quarantined=f_quarantined.get_result(), | 681 f_busy = q.filter(bot_management.BotInfo.is_busy == True).count_async() | 
| 664 dead=f_dead.get_result(), | 682 return swarming_rpcs.BotsCount( | 
| 665 busy=f_busy.get_result(), | 683 count=f_count.get_result(), | 
| 666 now=now) | 684 quarantined=f_quarantined.get_result(), | 
| 685 dead=f_dead.get_result(), | |
| 686 busy=f_busy.get_result(), | |
| 687 now=now) | |
| 688 elif is_dead is not None: | |
| 689 if is_dead: | |
| 690 q = q.filter(bot_management.BotInfo.last_seen_ts < timeout) | |
| 691 else: | |
| 692 q = q.filter(bot_management.BotInfo.last_seen_ts > timeout) | |
| 693 count = q.count() | |
| 694 # Dead bots, by definition, are not quarantined, nor are they busy. | |
| 695 return swarming_rpcs.BotsCount( | |
| 696 count=count, | |
| 697 quarantined=0, | |
| 698 dead=count, | |
| 699 busy=0, | |
| 700 now=now) | |
| 701 else: | |
| 702 # Quarantined bots, by definition, are not dead, nor are they busy. | |
| 703 q = q.filter(bot_management.BotInfo.quarantined == quarantined) | |
| 704 count = q.count() | |
| 705 return swarming_rpcs.BotsCount( | |
| 706 count=count, | |
| 707 quarantined=count, | |
| 708 dead=0, | |
| 709 busy=0, | |
| 710 now=now) | |
| 711 | |
| 667 | 712 | 
| 668 @gae_ts_mon.instrument_endpoint() | 713 @gae_ts_mon.instrument_endpoint() | 
| 669 @auth.endpoints_method( | 714 @auth.endpoints_method( | 
| 670 message_types.VoidMessage, swarming_rpcs.BotsDimensions, | 715 message_types.VoidMessage, swarming_rpcs.BotsDimensions, | 
| 671 http_method='GET') | 716 http_method='GET') | 
| 672 @auth.require(acl.is_privileged_user) | 717 @auth.require(acl.is_privileged_user) | 
| 673 def dimensions(self, _request): | 718 def dimensions(self, _request): | 
| 674 """Returns the cached set of dimensions currently in use in the fleet.""" | 719 """Returns the cached set of dimensions currently in use in the fleet.""" | 
| 675 dims = bot_management.DimensionAggregation.KEY.get() | 720 dims = bot_management.DimensionAggregation.KEY.get() | 
| 676 fd = [ | 721 fd = [ | 
| 677 swarming_rpcs.StringListPair(key=d.dimension, value=d.values) | 722 swarming_rpcs.StringListPair(key=d.dimension, value=d.values) | 
| 678 for d in dims.dimensions | 723 for d in dims.dimensions | 
| 679 ] | 724 ] | 
| 680 return swarming_rpcs.BotsDimensions(bots_dimensions=fd, ts=dims.ts) | 725 return swarming_rpcs.BotsDimensions(bots_dimensions=fd, ts=dims.ts) | 
| 681 | 726 | 
| 682 | 727 | 
| 683 def get_routes(): | 728 def get_routes(): | 
| 684 return ( | 729 return ( | 
| 685 endpoints_webapp2.api_routes(SwarmingServerService) + | 730 endpoints_webapp2.api_routes(SwarmingServerService) + | 
| 686 endpoints_webapp2.api_routes(SwarmingTaskService) + | 731 endpoints_webapp2.api_routes(SwarmingTaskService) + | 
| 687 endpoints_webapp2.api_routes(SwarmingTasksService) + | 732 endpoints_webapp2.api_routes(SwarmingTasksService) + | 
| 688 endpoints_webapp2.api_routes(SwarmingBotService) + | 733 endpoints_webapp2.api_routes(SwarmingBotService) + | 
| 689 endpoints_webapp2.api_routes(SwarmingBotsService) + | 734 endpoints_webapp2.api_routes(SwarmingBotsService) + | 
| 690 # components.config endpoints for validation and configuring of luci-config | 735 # components.config endpoints for validation and configuring of luci-config | 
| 691 # service URL. | 736 # service URL. | 
| 692 endpoints_webapp2.api_routes(config.ConfigApi)) | 737 endpoints_webapp2.api_routes(config.ConfigApi)) | 
| OLD | NEW |