Developing Strategy Plugins
Relevant source files
The following files were used as context for generating this wiki page:- lib/ansible/plugins/strategy/linear.py
- lib/ansible/plugins/strategy/free.py
- lib/ansible/plugins/strategy/debug.py
- lib/ansible/cli/doc.py
- test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py
- test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py
- test/integration/targets/ansible-galaxy-collection-cli/files/expected.txt
- AGENTS.md
Strategy plugins in Ansible determine the execution flow of a playbook. They control how tasks are executed across a set of hosts, managing parallelism, task ordering, and failure handling. While Ansible comes with several built-in strategies, such as the default linear strategy, developers can create custom strategies to implement complex execution logic tailored to specific needs.
Developing a custom strategy plugin involves creating a Python class that inherits from a base strategy class and implements a series of methods to manage the playbook run. This guide covers the architecture, implementation, documentation, and testing of strategy plugins, based on the existing implementations and validation tools within the Ansible codebase.
Core Architecture
Strategy plugins are a fundamental part of Ansible's execution engine. When a playbook is run, the chosen strategy plugin receives the playbook's iterator and a set of hosts, and it is responsible for orchestrating the execution of tasks on those hosts.
Key Implementations
Ansible includes several core strategy plugins that serve as excellent references:
| Strategy | Description | Source File |
|---|---|---|
linear | The default strategy. It runs tasks on a batch of hosts (defined by serial) and waits for them to complete before moving to the next batch. | lib/ansible/plugins/strategy/linear.py |
free | Executes tasks on all hosts in parallel without waiting for others to complete. This is useful for operations that are not interdependent. | lib/ansible/plugins/strategy/free.py |
debug | A specialized strategy used for debugging tasks. It provides an interactive debugging session when a task is executed. | lib/ansible/plugins/strategy/debug.py |
Sources: lib/ansible/plugins/strategy/linear.py, lib/ansible/plugins/strategy/free.py, lib/ansible/plugins/strategy/debug.py
Class Structure
All strategy plugins inherit from a common base class, which is inferred from the source files to be StrategyBase. The following diagram illustrates the inheritance structure of the core strategy plugins.
This diagram shows the relationship between the base strategy class and its concrete implementations.
Sources: lib/ansible/plugins/strategy/linear.py, lib/ansible/plugins/strategy/free.py, lib/ansible/plugins/strategy/debug.py
The Strategy Plugin API
Developing a strategy plugin requires overriding methods from the StrategyBase class. The most important method is run, which serves as the main entry point for the strategy's execution logic.
Core Execution Flow
The run method is the heart of a strategy plugin. It receives the iterator for the playbook and the list of hosts to run against. The plugin's logic then dictates the order and manner of task execution.
The following diagrams contrast the high-level execution flow of the linear and free strategies.
Linear Strategy Flow
The linear strategy processes hosts in batches, ensuring one batch completes before the next one starts.
Sources: lib/ansible/plugins/strategy/linear.py
Free Strategy Flow
The free strategy runs tasks on all hosts concurrently without waiting, maximizing parallelism.
Sources: lib/ansible/plugins/strategy/free.py
Key Overridable Methods
While run is the main entry point, several other methods are crucial for controlling the execution.
| Method | Description |
|---|---|
run(iterator, play_context) | The main entry point for the strategy. It orchestrates the entire play. |
_get_next_task_lockstep(hosts, iterator) | Used by the linear strategy to determine the next task for a group of hosts. |
_process_pending_results(iterator, max_passes) | Processes results from completed tasks. |
cleanup() | Called at the end of the strategy execution to clean up any resources. |
_get_hosts_left(iterator) | Returns a list of hosts that still have tasks to run. |
Sources: lib/ansible/plugins/strategy/linear.py, lib/ansible/plugins/strategy/free.py, lib/ansible/plugins/strategy/debug.py
Documenting Your Plugin
Proper documentation is essential for custom plugins and is enforced by Ansible's validation tools. Strategy plugins, like other plugins, must contain a DOCUMENTATION string in YAML format.
Sources: lib/ansible/cli/doc.py:110-112, test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:840-843
Documentation Structure
A strategy plugin file must include a DOCUMENTATION variable containing YAML-formatted metadata.
# Example from lib/ansible/plugins/strategy/debug.py
DOCUMENTATION = r'''
name: debug
short_description: The debug strategy
description:
- This is the debug strategy.
author:
- Ansible Core Team
version_added: "2.1"
'''
Sources: lib/ansible/plugins/strategy/debug.py:22-30
The ansible-doc tool and validate-modules sanity test parse this block to generate documentation and validate its contents.
Sources: lib/ansible/cli/doc.py:732-747, test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:840-1048
The DOCUMENTATION Schema
The DOCUMENTATION block is validated against a schema. The key fields are summarized below.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The name of the plugin. Must match the filename. |
short_description | string | Yes | A brief, one-line description of the plugin. |
description | string or list | Yes | A more detailed explanation of the plugin's purpose and functionality. |
version_added | string | Yes | The Ansible version in which the plugin was added. |
author | string or list | Yes | The author(s) of the plugin. |
options | dict | No | A dictionary of configuration options for the plugin. |
deprecated | dict | No | Information regarding the deprecation of the plugin. |
Sources: test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/schema.py:1139-1175
Semantic Markup
The description, notes, and other free-form text fields support semantic markup to create links and format text. This markup is processed by ansible-doc.
| Markup | Example | Renders As |
|---|---|---|
| Module | M(ansible.builtin.ping) | [ansible.builtin.ping] |
| Plugin | P(ansible.builtin.file#cache) | [ansible.builtin.file] |
| URL | U(https://ansible.com) | https://ansible.com |
| Link | L(Ansible,https://ansible.com) | Ansible <https://ansible.com> |
| Bold | B(text) | text |
| Italic | I(text) | text |
Sources: lib/ansible/cli/doc.py:313-324
Validation and Testing
Ansible provides a robust testing framework, ansible-test, to ensure plugin quality.
Sanity Testing
Sanity tests are static analysis checks that enforce coding standards and documentation requirements. The validate-modules test is particularly relevant for plugin developers.
# Run all sanity tests
ansible-test sanity -v --docker default
# Run only the validate-modules test
ansible-test sanity -v --docker default --test validate-modules
Sources: AGENTS.md:31-32
The validate-modules test performs numerous checks, including:
- GPLv3 License: Ensures the file has a valid GPLv3 header.
- Documentation Schema: Validates the
DOCUMENTATIONblock against the official schema. - Import Order: Verifies that imports appear after the documentation block.
- Python Syntax: Checks for syntax errors.
- Forbidden Calls: Flags direct usage of
sys.exit()orsubprocess.Popen.
Integration Testing
Integration tests are crucial for verifying the runtime behavior of a strategy plugin. These tests execute playbooks using the custom strategy to ensure it functions as expected.
# Run a specific integration test target
ansible-test integration -v --docker ubuntu2204 <your_test_target>
New plugins should be accompanied by integration tests that cover their core functionality.
Sources: AGENTS.md:52-55, 138-139
Packaging in a Collection
Modern Ansible content, including strategy plugins, should be packaged within an Ansible Collection. This simplifies distribution and versioning.
A strategy plugin file (e.g., my_strategy.py) should be placed in the plugins/strategy/ directory within the collection structure.
The following diagram shows a simplified Ansible Collection directory structure.
New plugins should generally be developed in collections rather than being submitted to ansible-core.
Sources: AGENTS.md:204-205
Conclusion
Developing a custom Ansible strategy plugin allows for fine-grained control over playbook execution. By inheriting from StrategyBase, implementing the run method, and adhering to Ansible's documentation and testing standards, developers can create powerful, reusable execution logic. Packaging the plugin within a collection ensures it is easily shareable and manageable within the broader Ansible ecosystem.