5

I'd like to write a Python program that uses the facts that Ansible gives me with ansible HOST -m setup.

When I call this, I get a response which makes it only almost pure JSON:

$ ansible localhost -m setup
localhost | success >> {
    // actual data
}

Is there some way to get this JSON response directly without parsing the shell output (which might not be too stable)? Could I even use Ansible directly in a Python 3 program?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Martin Ueding
  • 8,245
  • 6
  • 46
  • 92

2 Answers2

3

version stable-2.2, stable-2.3, and 2.4+

The latest ansible releases for 2.2, 2.3, and 2.4 all support ANSIBLE_STDOUT_CALLBACK variable. To use it, you need to add an ansible.cfg file that looks like:

[defaults]
bin_ansible_callbacks = True
callback_plugins = ~/.ansible/callback_plugins

You can place it wherever you're using ansible. Then, you need to create the callback_plugins directory, if you haven't already. Finally, you need to add a custom json parser to the directory. I copied the json parser that is bundled with ansible to the callback_plugins directory, then edited a single line in it to make it work.

I found the json.py file by first executing ansible --version

$ ansible --version
ansible 2.4.0.0
    config file = /Users/artburkart/Code/ansible.cfg
    configured module search path = [u'/Users/artburkart/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
    ansible python module location = /usr/local/lib/python2.7/site-packages/ansible
    executable location = /usr/local/bin/ansible
    python version = 2.7.13 (default, Jul 18 2017, 09:17:00) [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)]

Then using the "ansible python module location" to find the json.py.

cp /usr/local/lib/python2.7/site-packages/ansible/plugins/callback/json.py ~/.ansible/callback_plugins/

Finally I edited the v2_runner_on_ok function in the json.py file to look like this (courtesy of armab on GitHub):

def v2_runner_on_ok(self, result, **kwargs):
    host = result._host
    self.results[-1]['tasks'][-1]['hosts'][host.name] = result._result
    print(json.dumps({host.name: result._result}, indent=4))

Once that was all set up, the command is very simple:

ANSIBLE_STDOUT_CALLBACK=json ansible all -i localhost, -c local -m setup | jq

If you always want to parse JSON output, you can add the following line to the end of the ansible.cfg file I described above.

stdout_callback = json

That way, you don't need to include the environment variable anymore.

versions <= latest 2.2 stable

When querying against instances, I use the following command:

ansible all --inventory 127.0.0.1, --connection local --module-name setup | sed '1 s/^.*|.*=>.*$/{/g'

If you pipe the output into jq, as leucos suggested, it happily parses the semi-valid JSON. For example:

ansible all -i hosts -m setup | sed '1 s/^.*|.*=>.*$/{/g' | jq -r '.ansible_facts.ansible_distribution'
CentOS
Ubuntu
artburkart
  • 1,830
  • 1
  • 20
  • 28
  • This is what ANSIBLE_STDOUT_CALLBACK for. Just set it to `json`. – Konstantin Suvorov Sep 10 '16 at 08:51
  • @KonstantinSuvorov - You are aware the `ANSIBLE_STDOUT_CALLBACK` flag is only available in `ansible-playbook`, right? Just so the facts are clear: https://github.com/ansible/ansible/pull/15050 – artburkart Sep 11 '16 at 18:24
  • Sorry for my brief comment. Yes, i'm aware of this. If you want to use json with `ansible` cli, you can copy json.py as ./library/minimal.py. See my [other](http://stackoverflow.com/a/38667680/2795592) answer – Konstantin Suvorov Sep 11 '16 at 19:28
  • Since this is a question about `ansible` and not `ansible-playbook`, I think I'll leave my answer as-is. When https://github.com/ansible/ansible/pull/15050 is closed, I'll update my answer to reflect the new functionality. – artburkart Sep 12 '16 at 12:12
  • It appears there will be no JSON output supported in ansible any time in the near future. https://github.com/ansible/ansible/pull/15050#issuecomment-317078659 – artburkart Jul 24 '17 at 16:33
  • Nice, I'll try your solution out when ansible 2.4 is released. – artburkart Jul 26 '17 at 14:00
1

If Python2 is OK for you, you can use the Ansible API directly. You can find detailled instructions here: http://docs.ansible.com/developing_api.html It's really easy.

And alternate, shell centric way is to use jq. There is a quick intro here: http://xmodulo.com/how-to-parse-json-string-via-command-line-on-linux.html

leucos
  • 17,661
  • 1
  • 44
  • 34
  • Downvoted. This is a joke of an answer. If the asker found it "really easy" they wouldn't be here. Also, have you looked at the Ansible API?? It may be easy to you, having used it a while, but it is poorly documented and comes with a fairly steep learning curve. And there is no mention of accessing gathered facts on the page you referenced. – C. Taylor May 22 '18 at 21:09