Getting Ansible info into your playbook
Early this week a co-worker asked if it was possible to access the Ansible command-line in a playbook. It seems that is not the case, in a “normal”, clean Ansible environment.
But in the meantime I was creating a playbook that served multiple purposes, stopping and starting services. These playbooks are completely the same, except for the start
and stop
keywords. Of course I could have solved that with a variabele, either hardcoded or as an extra variable on the commandline. But, where is the fun in that
So the idea arose to let the playbook depend on its name and if it is called start
, start all services and if it is called stop
just stop them. Something along the line of $0
in shell or sys.argv[0]
in Python.
But this idea turned out to exactly the same idea as my co-worker had. They are very related, but it is just not in Ansible.
But, it is open source, so just fix it
I started looking into an action plugin and after a lot of trail, error and Ansible source code reading I have fixed it.
The Ansible source-code contains a helper module called context
, that parses the command-line and consumes all options. But luckily, all that’s left are the playbook names and these are in context.CLIARGS['args']
. So if I take these I’m done. But when I’m doing this, I can also fix the co-workers problem, if I can access the ansible-playbook
parameters. And that turns out to be even simpler, just get sys.argv
in Python.
The result of all this craft is this Python script, an action plugin.
#!/usr/bin/python
# Make coding more python3-ish, this is required for contributions to Ansible
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import sys
import os
# Get Ansible context parser
from ansible import context
# ADT base class for our Ansible Action Plugin
from ansible.plugins.action import ActionBase
# Load the display handler to send logging to CLI or relevant display mechanism
try:
from __main__ import display
except ImportError:
from ansible.utils.display import Display
display = Display()
# Get all Ansible commandline arguments and place these in the
# `facts` dictionairy as `ansible_facts['argv']`
class ActionModule(ActionBase):
# No file transfer needed
TRANSFERS_FILES = False
def run(self, tmp=None, task_vars=None):
'''Run action plugin'''
# All checks (file, exists, etc) are already done
# by the Ansible context
playbooks = list(map(os.path.abspath, list(context.CLIARGS['args'])))
# Create the result JSON blob
result = {
'changed': False,
'failed': False,
'skipped': False,
'msg': '',
'ansible_facts': {
'argv' : sys.argv,
'playbooks': playbooks,
}
}
return result
This results in two extra Ansible facts, called argv
and playbooks
, that can be used in your playbooks like this:
- name: lets go
hosts: localhost
become: false
connection: local
tasks:
- name: get commandline arguments
get_argv:
- debug:
msg:
- "{{ ansible_facts['argv'] | default('Nope' ) }}"
- "{{ ansible_facts['playbooks'] | default('Nope' ) }}"
To use the action plugin, create a directory called action_plugins
in your Ansible directory, or set the action_plugins
path in the ansible.cfg
file and place the get_argv
script in this directory.
Enjoy!