 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 2014 The LUCI Authors. All rights reserved. | 1 # Copyright 2014 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 """Swarming bot management, e.g. list of known bots and their state. | 5 """Swarming bot management, e.g. list of known bots and their state. | 
| 6 | 6 | 
| 7 +---------+ | 7 +---------+ | 
| 8 |BotRoot | | 8 |BotRoot | | 
| 9 |id=bot_id| | 9 |id=bot_id| | 
| 10 +---------+ | 10 +---------+ | 
| (...skipping 10 matching lines...) Expand all Loading... | |
| 21 happening for the bot. | 21 happening for the bot. | 
| 22 - BotInfo is a 'dump-only' entity used for UI, it permits quickly show the | 22 - BotInfo is a 'dump-only' entity used for UI, it permits quickly show the | 
| 23 state of every bots in an single query. It is basically a cache of the last | 23 state of every bots in an single query. It is basically a cache of the last | 
| 24 BotEvent and additionally updated on poll. It doesn't need to be updated in a | 24 BotEvent and additionally updated on poll. It doesn't need to be updated in a | 
| 25 transaction. | 25 transaction. | 
| 26 - BotSettings contains bot-specific settings. It must be updated in a | 26 - BotSettings contains bot-specific settings. It must be updated in a | 
| 27 transaction and contains admin-provided settings, contrary to the other | 27 transaction and contains admin-provided settings, contrary to the other | 
| 28 entities which are generated from data provided by the bot itself. | 28 entities which are generated from data provided by the bot itself. | 
| 29 """ | 29 """ | 
| 30 | 30 | 
| 31 import datetime | |
| 31 import hashlib | 32 import hashlib | 
| 32 | 33 | 
| 33 from google.appengine.ext import ndb | 34 from google.appengine.ext import ndb | 
| 34 | 35 | 
| 36 import swarming_rpcs | |
| 35 from components import datastore_utils | 37 from components import datastore_utils | 
| 36 from components import utils | 38 from components import utils | 
| 37 from server import config | 39 from server import config | 
| 38 from server import task_pack | 40 from server import task_pack | 
| 39 | 41 | 
| 40 | 42 | 
| 
M-A Ruel
2016/08/09 20:23:13
remove one line
 
kjlubick
2016/08/09 20:44:44
Done.
 | |
| 43 | |
| 41 # Margin of randomization of BOT_REBOOT_PERIOD_SECS. Per-bot period will be in | 44 # Margin of randomization of BOT_REBOOT_PERIOD_SECS. Per-bot period will be in | 
| 42 # range [period * (1 - margin), period * (1 + margin)). | 45 # range [period * (1 - margin), period * (1 + margin)). | 
| 43 BOT_REBOOT_PERIOD_RANDOMIZATION_MARGIN = 0.2 | 46 BOT_REBOOT_PERIOD_RANDOMIZATION_MARGIN = 0.2 | 
| 44 | 47 | 
| 45 | 48 | 
| 46 ### Models. | 49 ### Models. | 
| 47 | 50 | 
| 48 # There is one BotRoot entity per bot id. Multiple bots could run on a single | 51 # There is one BotRoot entity per bot id. Multiple bots could run on a single | 
| 49 # host, for example with multiple phones connected to a host. In this case, the | 52 # host, for example with multiple phones connected to a host. In this case, the | 
| 50 # id is specific to each device acting as a bot. | 53 # id is specific to each device acting as a bot. | 
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 260 if order: | 263 if order: | 
| 261 query = query.order(BotEvent.key) | 264 query = query.order(BotEvent.key) | 
| 262 return query | 265 return query | 
| 263 | 266 | 
| 264 | 267 | 
| 265 def get_settings_key(bot_id): | 268 def get_settings_key(bot_id): | 
| 266 """Returns the BotSettings ndb.Key for a known bot.""" | 269 """Returns the BotSettings ndb.Key for a known bot.""" | 
| 267 return ndb.Key(BotSettings, 'settings', parent=get_root_key(bot_id)) | 270 return ndb.Key(BotSettings, 'settings', parent=get_root_key(bot_id)) | 
| 268 | 271 | 
| 269 | 272 | 
| 273 def filter_dimensions(q, dimensions): | |
| 274 """Filters a ndb.Query for BotInfo based on dimensions in the request.""" | |
| 275 for d in dimensions: | |
| 276 parts = d.split(':', 1) | |
| 277 if len(parts) != 2 or any(i.strip() != i or not i for i in parts): | |
| 278 raise ValueError('Invalid dimensions') | |
| 279 q = q.filter(BotInfo.dimensions_flat == d) | |
| 280 return q | |
| 281 | |
| 282 | |
| 283 def filter_availability(q, quarantined, is_dead, now): | |
| 284 """Filters a ndb.Query for BotInfo based on quarantined/is_dead.""" | |
| 285 val = swarming_rpcs.to_bool(quarantined) | |
| 
M-A Ruel
2016/08/09 20:23:13
actually that's a layering violation :/  Here only
 
kjlubick
2016/08/09 20:44:44
Done.
 | |
| 286 if val is not None: | |
| 287 q = q.filter(BotInfo.quarantined == val) | |
| 288 | |
| 289 dt = datetime.timedelta(seconds=config.settings().bot_death_timeout_secs) | |
| 290 timeout = now - dt | |
| 291 val = swarming_rpcs.to_bool(is_dead) | |
| 292 if val: | |
| 293 q = q.filter(BotInfo.last_seen_ts < timeout) | |
| 294 elif val is not None: | |
| 295 q = q.filter(BotInfo.last_seen_ts > timeout) | |
| 296 return q | |
| 297 | |
| 298 | |
| 270 def bot_event( | 299 def bot_event( | 
| 271 event_type, bot_id, external_ip, authenticated_as, dimensions, state, | 300 event_type, bot_id, external_ip, authenticated_as, dimensions, state, | 
| 272 version, quarantined, task_id, task_name, **kwargs): | 301 version, quarantined, task_id, task_name, **kwargs): | 
| 273 """Records when a bot has queried for work. | 302 """Records when a bot has queried for work. | 
| 274 | 303 | 
| 275 Arguments: | 304 Arguments: | 
| 276 - event: event type. | 305 - event: event type. | 
| 277 - bot_id: bot id. | 306 - bot_id: bot id. | 
| 278 - external_ip: IP address as seen by the HTTP handler. | 307 - external_ip: IP address as seen by the HTTP handler. | 
| 279 - authenticated_as: bot identity as seen by the HTTP handler. | 308 - authenticated_as: bot identity as seen by the HTTP handler. | 
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 368 Returns: | 397 Returns: | 
| 369 Tuple (True to restart, text message explaining the reason). | 398 Tuple (True to restart, text message explaining the reason). | 
| 370 """ | 399 """ | 
| 371 # Periodically reboot bots to workaround OS level leaks (especially on Win). | 400 # Periodically reboot bots to workaround OS level leaks (especially on Win). | 
| 372 running_time = state.get('running_time', 0) | 401 running_time = state.get('running_time', 0) | 
| 373 assert isinstance(running_time, (int, float)) | 402 assert isinstance(running_time, (int, float)) | 
| 374 period = get_bot_reboot_period(bot_id, state) | 403 period = get_bot_reboot_period(bot_id, state) | 
| 375 if period and running_time > period: | 404 if period and running_time > period: | 
| 376 return True, 'Periodic reboot: running longer than %ds' % period | 405 return True, 'Periodic reboot: running longer than %ds' % period | 
| 377 return False, '' | 406 return False, '' | 
| OLD | NEW |