In my previous blogs about Ansible, I covered a few use cases:
- Simple playbooks
- Use Ansible like a Programming Language
- REST API calls
- Gather logs and runtime information
- Infrastructure as Code with YaK: on-premise, create a component, and two blogs regarding JBoss EAP (here and here).
All these still require a manual trigger to run. With Ansible Event Driven (AED), we can be pro-active by running playbooks automatically based on events.
Installation
To install, it is just a few commands:
- Install pip3 and a JDK:
sudo apt install python3-pip openjdk-17-jdk maven
- Set variables (or add them in ~/.bash_profile to make them permanent):
export PATH=$PATH:$HOME/.local/bin
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PIP_NO_BINARY=jpy
export PATH=/home/vagrant/.local/bin:$PATH
- lnstall required Python packages:
pip install wheel ansible-rulebook ansible ansible-runner
- Finally, install eda collection:
ansible-galaxy collection install community.general ansible.eda
Now, we are ready to run our first rulebook.
Rulebook
AED introduce a new tool as well as a new type of file: The rulebook which is associated with ansible-rulebook
command. The rulebook is a yaml file, so we are in a known environment.
This file consists of three sections:
- Sources: It defined the event used as input.
- Rules: A filter on event to determine when to apply actions.
- Actions: Playbooks, modules or tasks.
Here is simple example from the official GitHub repository with a webhook:
---
- name: Listen for events on a webhook
hosts: all
## Define our source for events
sources:
- ansible.eda.webhook:
host: 0.0.0.0
port: 5000
## Define the conditions we are looking for
rules:
- name: Say Hello
condition: event.payload.message == "Ansible is super cool"
## Define the action we should take should the condition be met
action:
run_playbook:
name: say-what.yml
Create a simple inventory (for testing purpose only):
all:
hosts:
localhost:
ansible_connection: local
As well as the playbook (say-what.yml) we will run:
- hosts: localhost
connection: local
tasks:
- debug:
msg: "Thank you, my friend!"
To run it (with verbosity so we can see something):
ansible-rulebook --rulebook webhook.yml -i inventory.yml --verbose
Output:
2023-02-03 16:15:30,048 - ansible_rulebook.app - INFO - Starting sources
2023-02-03 16:15:30,048 - ansible_rulebook.app - INFO - Starting rules
2023-02-03 16:15:30,048 - ansible_rulebook.engine - INFO - run_ruleset
2023-02-03 16:15:30,791 - ansible_rulebook.engine - INFO - ruleset define: {"name": "Listen for events on a webhook", "hosts": ["all"], "sources": [{"EventSource": {"name": "ansible.eda.webhook", "source_name": "ansible.eda.webhook", "source_args": {"host": "0.0.0.0", "port": 5000}, "source_filters": []}}], "rules": [{"Rule": {"name": "Say Hello", "condition": {"AllCondition": [{"EqualsExpression": {"lhs": {"Event": "payload.message"}, "rhs": {"String": "Ansible is super cool"}}}]}, "action": {"Action": {"action": "run_playbook", "action_args": {"name": "say-what.yml"}}}, "enabled": true}}]}
2023-02-03 16:15:30,793 - ansible_rulebook.engine - INFO - load source
2023-02-03 16:15:31,294 - ansible_rulebook.engine - INFO - load source filters
2023-02-03 16:15:31,296 - ansible_rulebook.engine - INFO - Calling main in ansible.eda.webhook
2023-02-03 16:15:31,298 - ansible_rulebook.engine - INFO - Start a playbook action runner for Listen for events on a webhook
2023-02-03 16:15:31,299 - ansible_rulebook.engine - INFO - Waiting for actions on events from Listen for events on a webhook
2023-02-03 16:15:31,302 - ansible_rulebook.engine - INFO - Waiting for events from Listen for events on a webhook
Then, with a simple curl command, we will trigger the web hook:
curl -H 'Content-Type: application/json' -d '{"message": "Ansible is super cool"}' 127.0.0.1:5000/endpoint
In the output of the running command, we will see some new logs as well as something familiar to any ansible developer: The playbook running.
2023-02-03 16:24:53,960 - aiohttp.access - INFO - 127.0.0.1 [03/Feb/2023:16:24:53 +0000] "POST /endpoint HTTP/1.1" 200 158 "-" "curl/7.74.0"
2023-02-03 16:24:53 962 [main] INFO org.drools.ansible.rulebook.integration.api.rulesengine.RegisterOnlyAgendaFilter - Activation of effective rule "Say Hello" with facts: [Event DROOLS_PROTOTYPE with values = {meta.headers.Accept=*/*, meta.headers.Host=127.0.0.1:5000, payload={message=Ansible is super cool}, payload.message=Ansible is super cool, meta={headers={Accept=*/*, User-Agent=curl/7.74.0, Host=127.0.0.1:5000, Content-Length=36, Content-Type=application/json}, endpoint=endpoint}, meta.headers.Content-Type=application/json, meta.headers.User-Agent=curl/7.74.0, meta.endpoint=endpoint, meta.headers.Content-Length=36, meta.headers={Accept=*/*, User-Agent=curl/7.74.0, Host=127.0.0.1:5000, Content-Length=36, Content-Type=application/json}}]
2023-02-03 16:24:53,964 - ansible_rulebook.rule_generator - INFO - calling Say Hello
2023-02-03 16:24:53,964 - ansible_rulebook.engine - INFO - call_action run_playbook
2023-02-03 16:24:53,965 - ansible_rulebook.engine - INFO - substitute_variables [{'name': 'say-what.yml'}] [{'event': {'payload': {'message': 'Ansible is super cool'}, 'meta': {'headers': {'Accept': '*/*', 'User-Agent': 'curl/7.74.0', 'Host': '127.0.0.1:5000', 'Content-Length': '36', 'Content-Type': 'application/json'}, 'endpoint': 'endpoint'}}, 'fact': {'payload': {'message': 'Ansible is super cool'}, 'meta': {'headers': {'Accept': '*/*', 'User-Agent': 'curl/7.74.0', 'Host': '127.0.0.1:5000', 'Content-Length': '36', 'Content-Type': 'application/json'}, 'endpoint': 'endpoint'}}}]
2023-02-03 16:24:53,966 - ansible_rulebook.engine - INFO - action args: {'name': 'say-what.yml'}
2023-02-03 16:24:53,967 - ansible_rulebook.engine - INFO - Queue playbook/module/job template {'say-what.yml'} for running later
2023-02-03 16:24:53,967 - ansible_rulebook.builtin - INFO - running Ansible playbook: say-what.yml
2023-02-03 16:24:53,970 - ansible_rulebook.builtin - INFO - ruleset: Listen for events on a webhook, rule: Say Hello
2023-02-03 16:24:53,971 - ansible_rulebook.builtin - INFO - Calling Ansible runner
PLAY [localhost] ***************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "Thank you, my friend!"
}
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Conclusion
We confirmed that our Ansible Event Driven installation is working. On my next blog, I will integrate it with Apache Kafka, the well know event store and stream-processing platform.