From 29a10962a986dd74b4731703f75f0442f5b05bfe Mon Sep 17 00:00:00 2001 From: Adar Nimrod <nimrod@shore.co.il> Date: Sun, 26 Apr 2015 00:13:34 +0300 Subject: [PATCH] - Add new entry (Ansible module in Python). --- content/ansible-python.rst | 70 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 content/ansible-python.rst diff --git a/content/ansible-python.rst b/content/ansible-python.rst new file mode 100644 index 0000000..445250e --- /dev/null +++ b/content/ansible-python.rst @@ -0,0 +1,70 @@ +Using Ansible as a Python module +================================ + +:date: 2015-01-01 +:summary: Using Ansible as a Python module when playbooks are not enough. + +At my current employer we have several servers in production with various +providers, some of them with multiple ip addresses. When configuring the +firewall to allow traffic from other servers I reached for Ansible. The +obvious solution was to use a nested loop, something like this: :: + + - name: Allow other servers + ufw: rule=allow from_ip={{ item[1] }} + with_nested: + - all_hosts + - {{ item.ansible_all_ipv4_addresses }} + +However, this syntax is invalid (and other variations I tried). Using 'include' +with 'with_items' is deprecated and I didn't manage to get it to work with +registering variables as well. What I had left was programaticaly generating +a playbook, but investigating further I found that Ansible can be imported as +a Python module. + +Incorperating Ansible in Python +------------------------------- + +To retrieve all of the ip addresses I'd ran the setup module to gather the +information :: + + from ansible.runner import Runner + struct = Runner (module_name='setup', pattern='all_hosts').run() + +Now we have a complex data structure that is the output of Ansible's fact +gathering module. Running it in the interpeter and examining the structure is +not hard at all and that is how I managed to write the following code to extract +a list of all of our server's ip addresses. :: + + ipaddresses = [] + for host in struct['contacted']: + for ip in struct['contacted'][host]['ansible_facts']['ansible_all_ipv4_addresses']: + ipaddresses.append (ip) + +Putting that information to good use +------------------------------------ + +Now that we have a list of the ip addresses, we can start running Ansible +commands right from with Python (just like we did) or build a playbook by +outputing a YAML file. I chose the latter. :: + + from yaml import safe_dump + doc = {'all_ipv4': ipaddresses} + print (safe_dump (doc), file='vars.yml') + +This will create a vars.yml file with the all_ipv4 variable already defined +there to be imported to any playbook and run. For example: :: + + --- + - hosts: all_hosts + vars_files: + - vars.yml + tasks: + - name: Allow other servers + ufw: rule=allow from_ip={{ item }} + with_items: all_ipv4 + +With this much little code we were able to query all of our hosts, extract the +needed information and output it back to Ansible for further use. I see this as +a product of the good decisions the Ansible developers choose early on (YAML, +Python, SSH). As always, for any feedback you may have, `email me <mailto: +nimrod@shore.co.il>`_. -- GitLab