• 11
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 191

Backtrace:

File: /home/prodcxja/public_html/questions/application/views/question.php
Line: 191
Function: _error_handler

File: /home/prodcxja/public_html/questions/application/controllers/Questions.php
Line: 433
Function: view

File: /home/prodcxja/public_html/questions/index.php
Line: 315
Function: require_once

name Punditsdkoslkdosdkoskdo

Running ansible-playbook using Python API

How can I run a playbook in python script? What is the equivalent of the following using ansible module in python:

ansible -i hosts dbservers -m setup
ansible-playbook -i hosts -vvvv -k site.yml

I was looking at their documenation in http://docs.ansible.com/developing_api.html but they have very limited examples.

Deprecation Notice: This post doesn't work as of ansible 2. The API was changed.

This covered in the Ansible documentation under "Python API."

For example, ansible -i hosts dbservers -m setup is implemented via:

import ansible.runner

runner = ansible.runner.Runner(
   module_name='setup',
   module_args='',
   pattern='dbservers',
)
dbservers_get_facts = runner.run()

There are a bunch of non-documented parameters in the __init__ method of Runner (from ansible.runner). There's too many to list inline, but I've included some of the parameters in this post as a guess to what you're specifically looking for.

class Runner(object):
    ''' core API interface to ansible '''

    # see bin/ansible for how this is used...

    def __init__(self,
        host_list=C.DEFAULT_HOST_LIST,      # ex: /etc/ansible/hosts, legacy usage
        module_path=None,                   # ex: /usr/share/ansible
        module_name=C.DEFAULT_MODULE_NAME,  # ex: copy
        module_args=C.DEFAULT_MODULE_ARGS,  # ex: "src=/tmp/a dest=/tmp/b"
        ...
        pattern=C.DEFAULT_PATTERN,          # which hosts?  ex: 'all', 'acme.example.org'
        remote_user=C.DEFAULT_REMOTE_USER,  # ex: 'username'
        remote_pass=C.DEFAULT_REMOTE_PASS,  # ex: 'password123' or None if using key
        remote_port=None,                   # if SSH on different ports
        private_key_file=C.DEFAULT_PRIVATE_KEY_FILE, # if not using keys/passwords
        sudo_pass=C.DEFAULT_SUDO_PASS,      # ex: 'password123' or None
        ...
        sudo=False,                         # whether to run sudo or not
        sudo_user=C.DEFAULT_SUDO_USER,      # ex: 'root'
        module_vars=None,                   # a playbooks internals thing
        play_vars=None,                     #
        play_file_vars=None,                #
        role_vars=None,                     #
        role_params=None,                   #
        default_vars=None,                  #
        extra_vars=None,                    # extra vars specified with he playbook(s)
        is_playbook=False,                  # running from playbook or not?
        inventory=None,                     # reference to Inventory object
        ...
        su=False,                           # Are we running our command via su?
        su_user=None,                       # User to su to when running command, ex: 'root'
        su_pass=C.DEFAULT_SU_PASS,
        vault_pass=None,
        ...
        ):

For instance, the above command that specifies a sudo user and pass would be:

runner = ansible.runner.Runner(
   module_name='setup',
   module_args='',
   pattern='dbservers',
   remote_user='some_user'
   remote_pass='some_pass_or_python_expression_that_returns_a_string'
)

For playbooks, look into playbook.PlayBook, which takes a similar set of initializers:

class PlayBook(object):
    '''
    runs an ansible playbook, given as a datastructure or YAML filename.
    ...
    '''

    # *****************************************************

    def __init__(self,
        playbook         = None,
        host_list        = C.DEFAULT_HOST_LIST,
        module_path      = None,
        .... 

and can be executed with the .run() method. e.g.:

from ansible.playbook import PlayBook
pb = PlayBook(playbook='/path/to/book.yml, --other initializers--)
pb.run()

more robust usage can be found in the ansible-playbook file.

As far as I know, translating playbooks to Python modules is a bit more involved, but the documentation listed above should get you covered and you can reuse the YAML parser built into Ansible to convert playbooks to variables.

  • 37
Reply Report
    • This doesn't seem to answer how to run a playbook, but instead an individual task. There is an ansible.playbook.Playbook class with a run method as well which I think the OP is asking about - I know it's what I'm trying to find :D
      • 1
    • ansible.playbook.Playbook calls to runner for execution (see is_playbook in ansible.runner), but good point. updating answer now.
    • @fideloper happy to help. my typical path in extending Ansible is to just hop around in the source or to just kick off pdb and follow it -- Ansible's code base is pretty straightforward.

I have answered the question here Posting this here cause posting links is discouraged in the community. Hope it helps.

The documentation is surprisingly lacking and you'll have to get started here

That being said, here is a quick script I hacked together that manages to run a playbook.

#!/usr/bin/env python

import os
import sys
from collections import namedtuple

from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import Inventory
from ansible.executor.playbook_executor import PlaybookExecutor

loader = DataLoader()

inventory = Inventory(loader=loader, sources='/home/slotlocker/hosts2')
variable_manager = VariableManager(loader=loader, inventory=inventory)
playbook_path = '/home/slotlocker/ls.yml'

if not os.path.exists(playbook_path):
    print '[INFO] The playbook does not exist'
    sys.exit()

Options = namedtuple('Options', ['listtags', 'listtasks', 'listhosts', 'syntax', 'connection','module_path', 'forks', 'remote_user', 'private_key_file', 'ssh_common_args', 'ssh_extra_args', 'sftp_extra_args', 'scp_extra_args', 'become', 'become_method', 'become_user', 'verbosity', 'check','diff'])
options = Options(listtags=False, listtasks=False, listhosts=False, syntax=False, connection='ssh', module_path=None, forks=100, remote_user='slotlocker', private_key_file=None, ssh_common_args=None, ssh_extra_args=None, sftp_extra_args=None, scp_extra_args=None, become=True, become_method='sudo', become_user='root', verbosity=None, check=False, diff=False)

variable_manager.extra_vars = {'hosts': 'mywebserver'} # This can accomodate various other command line arguments.`

passwords = {}

pbex = PlaybookExecutor(playbooks=[playbook_path], inventory=inventory, variable_manager=variable_manager, loader=loader, options=options, passwords=passwords)

results = pbex.run()
  • 11
Reply Report
      • 1
    • Hi thank you for your answer, helped me a lot, for everyone using paramiko_ssh please note, that you have to have ** ssh_common_args='' and ssh_extra_args='' not None **, otherwise you will get an exception: TypeError: sequence item: expected string, NoneType found cost me a bit of time and surfing the code to get that straight.
    • For Ansible 2.6 (2.6.8) there is API change, you should change Inventory to InventoryManager so.... from ansible.inventory.manager import InventoryManager and inventory = InventoryManager(loader=loader ...

Just a quick code update that works on 2.8.3,

from ansible import context
from ansible.cli import CLI
from ansible.module_utils.common.collections import ImmutableDict
from ansible.executor.playbook_executor import PlaybookExecutor
from ansible.parsing.dataloader import DataLoader
from ansible.inventory.manager import InventoryManager
from ansible.vars.manager import VariableManager

loader = DataLoader()

context.CLIARGS = ImmutableDict(tags={}, listtags=False, listtasks=False, listhosts=False, syntax=False, connection='ssh',
                    module_path=None, forks=100, remote_user='xxx', private_key_file=None,
                    ssh_common_args=None, ssh_extra_args=None, sftp_extra_args=None, scp_extra_args=None, become=True,
                    become_method='sudo', become_user='root', verbosity=True, check=False, start_at_task=None)

inventory = InventoryManager(loader=loader, sources=('/xxx/inventory_file',))

variable_manager = VariableManager(loader=loader, inventory=inventory, version_info=CLI.version_info(gitinfo=False))

pbex = PlaybookExecutor(playbooks=['/xxx/playbook.yml'], inventory=inventory, variable_manager=variable_manager, loader=loader, passwords={})

results = pbex.run()
  • 10
Reply Report

You're looking at something that isn't officially supported or recommended therefore little documentation to be had.

That said, if you really want to pursue that course I would start with cracking open the ansible-playbook script in bin and reverse engineering what you want to do.

  • -13
Reply Report
      • 2
    • tristan: I'm not sure what you're looking at, because at the moment, there is zero official documentation on running playbooks via the Python API.
      • 2
    • @Dolph Click on the link in my comment -- the ansible.runner is the entry point for running playbooks (see comment of "The Python API is very powerful, and is how the ansible CLI and ansible-playbook are implemented."). And yes, the documentation reads better for people that have already worked in the Ansible codebase, but that doesn't make this answer correct.

Trending Tags