This ansible module serves as an anti-sucking tool to help developers maintain their mental integrity when forced to work with ansible.
Enables the developer to avoid the most crazy driving ansible feature: using jinja2 substitution to construct meaningful data structures from facts and back to facts.
Ansible seemes nice, but only for simple stuff like provisioning. When you have to maintain complex setups, you inevitably have to deal with ansible facts, the inventory and transformation of those into tool specific configurations and data structures.
Finally you force yourself registering a lot of intermediate variables, looping over those and using a lot of jinja filters. Your transformation becomes a distributed incomprehensible nightmare.
If you return a little later and try to make a sense out of it, you find yourself cursing and your head gets smashed by the pure insanity under which you subjugated yourself. Ansible is a monster eating the soul of every brave developer - if you ever had a chance to review the ansible code, you know you are fighting the devil 1.
Basically this module executes a python code block via exec
. You can provide locals
to it. If you want to register the result, use set_result(**kwargs)
if you want to create some facts use set_facts(**kwargs)
def run_module():
module_args = {
"code": {"type": "str", "required": True},
"locals": {"type": "dict", "required": False, "default": {}},
}
module = AnsibleModule(argument_spec=module_args, supports_check_mode=True)
result = {}
facts = {}
_globals = {
"set_result": result.update,
"set_facts": facts.update,
# copy locals, since ansible tries to clean it up afterwards, but fails
# for e.g. imported modules
**module.params["locals"],
}
code_object = compile(module.params["code"], "<string>", "exec")
# imported names settle in locals, so we unite locals and globals, to have
# them available
exec(code_object, _globals, _globals)
result["ansible_facts"] = facts
module.exit_json(msg="Code executed", **result)
Clone the project and add the project path to ANSIBLE_LIBRARY
or define library
accordingly in ansible.cfg
(see docs).
- name: Get database IPs
run_once: yes
delegate_to: localhost
py:
locals:
names: "{{ groups['database'] }}"
code: |
import socket
ips = [socket.gethostbyname(name) for name in names]
set_facts(database_ips=ips)
- debug:
var: database_ips
1: Der Pfad zur Hölle ist gepflastert mit guten Absichten.