| # coding: utf-8 |
| # Copyright 2013 The LUCI Authors. All rights reserved. |
| # Use of this source code is governed under the Apache License, Version 2.0 |
| # that can be found in the LICENSE file. |
| |
| """This file is meant to be overriden by the server's specific copy. |
| |
| You can upload a new version via /restricted/upload/bot_config. |
| |
| There's 3 types of functions in this file: |
| - get_*() to return properties to describe this bot. |
| - on_*() as hooks based on events happening on the bot. |
| - setup_*() to setup global state on the host. |
| |
| This file shouldn't import from other scripts in this directory except |
| os_utilities which is guaranteed to be usable as an API. It's fine to import |
| from stdlib. |
| |
| Set the environment variable SWARMING_LOAD_TEST=1 to disable the use of |
| server-provided bot_config.py. This permits safe load testing. |
| |
| This file contains unicode to confirm UTF-8 encoded file is well supported. |
| Here's a pile of poo: 💩 |
| """ |
| |
| import os |
| import time |
| |
| from api import os_utilities |
| from api import platforms |
| |
| # Unused argument 'bot' - pylint: disable=W0613 |
| |
| |
| def get_dimensions(bot): |
| # pylint: disable=line-too-long |
| """Returns dict with the bot's dimensions. |
| |
| The dimensions are what are used to select the bot that can run each task. |
| |
| The bot id will be automatically selected based on the hostname with |
| os_utilities.get_dimensions(). If you want something more special, specify it |
| in your bot_config.py and override the item 'id'. |
| |
| The dimensions returned here will be joined with server defined dimensions |
| (extracted from bots.cfg config file based on the bot id). Server defined |
| dimensions override the ones provided by the bot. See bot.Bot.dimensions for |
| more information. |
| |
| See https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md. |
| |
| Arguments: |
| - bot: bot.Bot instance or None. See ../api/bot.py. |
| """ |
| dimensions = os_utilities.get_dimensions() |
| |
| # The bot base directory is formatted like <HOME>/bots/<Pool>/<Id> |
| id = dimensions['id'][0] |
| index = os.path.basename(bot.base_dir) |
| pool_name = os.path.basename(os.path.dirname(bot.base_dir)) |
| |
| dimensions['pool'] = [pool_name] |
| dimensions['id'] = ['%s_%s' % (id, index)] |
| |
| return dimensions |
| |
| |
| def get_state(bot): |
| # pylint: disable=line-too-long |
| """Returns dict with a state of the bot reported to the server with each poll. |
| |
| It is only for dynamic state that changes while bot is running for information |
| for the sysadmins. |
| |
| The server can not use this state for immediate scheduling purposes (use |
| 'dimensions' for that), but it can use it for maintenance and bookkeeping |
| tasks. |
| |
| See https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md. |
| |
| Arguments: |
| - bot: bot.Bot instance or None. See ../api/bot.py. |
| """ |
| return os_utilities.get_state() |
| |
| |
| def get_authentication_headers(bot): |
| """Returns authentication headers and their expiration time. |
| |
| The returned headers will be passed with each HTTP request to the Swarming |
| server (and only Swarming server). The bot will use the returned headers until |
| they are close to expiration (usually 6 min, see AUTH_HEADERS_EXPIRATION_SEC |
| in remote_client.py), and then it'll attempt to refresh them by calling |
| get_authentication_headers again. |
| |
| Can be used to implement per-bot authentication. If no headers are returned, |
| the server will use only IP whitelist for bot authentication. |
| |
| On GCE will use OAuth token of the default GCE service account. It should have |
| "User info" API scope enabled (this can be set when starting an instance). The |
| server should be configured (via bots.cfg) to trust this account (see |
| 'require_service_account' in bots.proto). |
| |
| May be called by different threads, but never concurrently. |
| |
| Arguments: |
| - bot: bot.Bot instance. See ../api/bot.py. |
| |
| Returns: |
| Tuple (dict with headers or None, unix timestamp of when they expire). |
| """ |
| if platforms.is_gce(): |
| tok = platforms.gce.oauth2_access_token() |
| return {'Authorization': 'Bearer %s' % tok}, time.time() + 5*60 |
| return (None, None) |
| |
| |
| ### Hooks |
| |
| |
| def on_bot_shutdown(bot): |
| """Hook function called when the bot shuts down, usually rebooting. |
| |
| It's a good time to do other kinds of cleanup. |
| |
| Arguments: |
| - bot: bot.Bot instance. See ../api/bot.py. |
| """ |
| pass |
| |
| |
| def on_bot_startup(bot): |
| """Hook function called when the bot starts, before handshake with the server. |
| |
| Here the bot may initialize and examine its environment, pick initial state |
| and dimensions to send to the server during the handshake. |
| |
| Arguments: |
| - bot: bot.Bot instance. See ../api/bot.py. |
| """ |
| pass |
| |
| |
| def on_handshake(bot): |
| """Hook function called when the bot starts, after handshake with the server. |
| |
| Here the bot already knows server enforced dimensions (defined in server side |
| bots.cfg file). |
| |
| This is called right before starting to poll for tasks. It's a good time to |
| do some final initialization or cleanup that may depend on server provided |
| configuration. |
| |
| Arguments: |
| - bot: bot.Bot instance. See ../api/bot.py. |
| """ |
| pass |
| |
| |
| def on_before_task(bot, bot_file=None): |
| """Hook function called before running a task. |
| |
| It shouldn't do much, since it can't cancel the task so it shouldn't do |
| anything too fancy. |
| |
| Arguments: |
| - bot: bot.Bot instance. See ../api/bot.py. |
| - bot_file: Path to file to write information about the state of the bot. |
| This file can be used to pass certain info about the bot |
| to tasks, such as which connected android devices to run on. See |
| https://github.com/luci/luci-py/tree/master/appengine/swarming/doc/Magic-Values.md#run_isolated |
| TODO(bpastene): Remove default value None. |
| """ |
| pass |
| |
| |
| def on_after_task(bot, failure, internal_failure, dimensions, summary): |
| """Hook function called after running a task. |
| |
| It is an excellent place to do post-task cleanup of temporary files. |
| |
| The default implementation restarts after a task failure or an internal |
| failure. |
| |
| Arguments: |
| - bot: bot.Bot instance. See ../api/bot.py. |
| - failure: bool, True if the task failed. |
| - internal_failure: bool, True if an internal failure happened. |
| - dimensions: dict, Dimensions requested as part of the task. |
| - summary: dict, Summary of the task execution. |
| """ |
| # Example code: |
| #if failure: |
| # bot.restart('Task failure') |
| #elif internal_failure: |
| # bot.restart('Internal failure') |
| |
| |
| def on_bot_idle(bot, since_last_action): |
| """Hook function called once when the bot has been idle; when it has no |
| command to execute. |
| |
| This is an excellent place to put device in 'cool down' mode or any |
| "pre-warming" kind of stuff that could take several seconds to do, that would |
| not be appropriate to do in on_after_task(). It could be worth waiting for |
| `since_last_action` to be several seconds before doing a more lengthy |
| operation. |
| |
| This function is called repeatedly until an action is taken (a task, updating, |
| etc). |
| |
| This is a good place to do "auto reboot" for hardware based bots that are |
| rebooted periodically. |
| |
| Arguments: |
| - bot: bot.Bot instance. See ../api/bot.py. |
| - since_last_action: time in second since last action; e.g. amount of time the |
| bot has been idle. |
| """ |
| pass |
| |
| |
| ### Setup |
| |
| |
| def setup_bot(bot): |
| """Does one time initialization for this bot. |
| |
| Returns True if it's fine to start the bot right away. Otherwise, the calling |
| script should exit. |
| |
| This is an excellent place to drop a README file in the bot directory, to give |
| more information about the purpose of this bot. |
| |
| Example: making this script starts automatically on user login via |
| os_utilities.set_auto_startup_win() or os_utilities.set_auto_startup_osx(). |
| """ |
| with open(os.path.join(bot.base_dir, 'README'), 'wb') as f: |
| f.write( |
| """This directory contains a Swarming bot. |
| |
| Swarming source code is hosted at https://github.com/luci/luci-py. |
| |
| The bot was generated from the server %s. To get the bot's attributes, run: |
| |
| python swarming_bot.zip attributes |
| """ % bot.server) |
| return True |