How to call ansible-playbook from custom python code

Hello all,
I’d like to ask you about preferred method how to call playbook from custom python code.

We have our own Python based orchestration tool where we can call (not only) ansible-playbooks (we are using direct shell command call, not ansible api) with some settings (playbook path, extra variables, ansible settings, etc).

Everything is working, but we are getting playbook/tasks statistics from text output starting RECAP line - I know, not very nice method, but for this older custom project that was only one way how to do it.

Now we’d like to change it and that’s the reason to ask you about best way how to run playbook from Python code more ‘programmatically’.

I know, that using Ansible Python API is not recommended method because it can changed in future. Therefore I was playing with ansible-runner project but there are several limitations for us:

  • ansible_runner.interface.run()
    • cons:
      • can’t correctly send ‘ctrl+c’ to ansible playbook
      • it’s using strict directory hierarchy
      • there are some issues with vault
    • pros:
      • we can get stats as dictionary (success, changed, errors,…)
  • ansible_runner.interface.run_command()
    • cons:
      • no stats (only out (str), err (str) and rc (int)
    • pros:
      • simple
      • this can be helpful for us, but we preferred stats as dictionary (similar to run())

My question is:

  • Can we use Ansible Python API directly?
  • I know that ansible-runner project is recommended way, but there are some limitations for us
  • Is there any other method how to call ansible playbook and get run results?

Thank you for the recommendations how to solve this question(s)

To preface, I have never heard of the Ansible Python API, nor have I ever needed to use ansible-runner

I have a Web App that has some buttons that, when pressed, kick off some ansible playbooks - for example, restarting services. I believe I am doing this in the way you describe as your current setup:

  • In Python, I connect through SSH to the Ansible control server, and simply kick off the ansible playbook a call might be cd /opt/ansible && ansible-playbook playbooks/restart-services.yml -e '{"hosts_used":"server1"}'
  • I then read STDOUT and just use the “failed=0” or “failed=1” feedback that is received to determine if it ran successfully

Obviously this is not an advanced error management setup - but in my setup, currently neither is us running it manually on the Ansible server. Either way, if there are errors, then further investigation would be needed.

I have thoughts of looking into things like Ansible ARA to enhance error management, but have yet to find the time - and I don’t know how much that helps specifically calling from Python.

  • Yes. It’s “unsupported” in the sense that if you update ansible-core and the update breaks your code, then it’s your responsibility to support fixing what broke. Don’t expect support from RedHat or the Ansible Core/Community teams. That said, if you have a project idea that would benefit from using Ansible’s API directly, go for it.
  • Ansible-runner is definitely a recommended approach since there’s at least some support for ansible-runner and instructions on how to use it. You might be limited by what’s there, but you can always submit an issue/feature request to the github repository.
  • AWX is the first thing I would think of (and it uses ansible-runners for you!). Even with your custom python project, you can hook into the AWX API to run jobs and get results back. Whether you need success/fail stats or the full stdout of the job, there’s an API for retrieving that. AWX even supports webhooks to stream a job’s status, and then your python project could retrieve the results after the job’s webhook notifies its job completed.

That said, AWX itself may take a lot of time and investment to get going, but you might find a number of its other features to be useful in the long run.

Just as an FYI, one of the reasons why it is unsupported is that Ansible heavily relies on the fork syscall for its workers. When you try and run Ansible in your own process that means Ansible will fork all the existing state of your profess into the worker which can have unexpected consequences. Running Ansible in a dedicated process is the only way to avoid this and is what ansible-runner, and you can do through the subprocess module.

1 Like

May be for embedding calls to ansible plybooks from a python script one should look into callback plugins. The idea would be to customize the output of the embedded playbook execution that would run ansible-playbook in subprosses.

I haven’t tried this yet myself but encountered own usecases where embedding annsible functionality into existing scripts should be done better in terms of reacting to success or failure of the plays.

https://docs.ansible.com/ansible/latest/plugins/callback.html