Skip to content

Ansible-playbook is not accepting JSON args properly #19

@nmaludy

Description

@nmaludy

Problem

When running the ansible.playbook action and passing in "structure" data for extra_vars, Ansible is not interpreting them properly.

    ansible_playbook_linux:
      action: ansible.playbook
      input:
        playbook: "{{ _.playbook }}"
        inventory_file: "{{ _.server }},"
        extra_vars:
          - wait_timeout: 2
            wait_sleep: 2
            wait_connect_timeout: 2
            ansible_user: "user"
            ansible_ssh_pass: "xxx"
            ansible_ssh_extra_args: "-o StrictHostKeyChecking=no"

Looking at the output of ps -aef | grep ansible i see the following:

root     12711  2520  0 10:02 pts/0    00:00:00 sudo -E -- bash -c /opt/stackstorm/packs/ansible/actions/ansible_playbook.py --extra_vars='[{u'"'"'wait_sleep'"'"': 2, u'"'"'ansible_ssh_pass'"'"': u'"'"'xxx'"'"', u'"'"'ansible_user'"'"': u'"'"'user'"'"', u'"'"'wait_timeout'"'"': 2, u'"'"'wait_connect_timeout'"'"': 2}]' --inventory_file=host.domain.tld, -vvv /opt/encore/ansible/playbooks/wait_for_connection.yaml
root     12712 12711  0 10:02 pts/0    00:00:00 python /opt/stackstorm/packs/ansible/actions/ansible_playbook.py --extra_vars=[{u'wait_sleep': 2, u'ansible_ssh_pass': u'xxx', u'ansible_user': u'user', u'wait_timeout': 2, u'wait_connect_timeout': 2}] --inventory_file=host.domain.tld, -vvv /opt/encore/ansible/playbooks/wait_for_connection.yaml
root     12713 12712 12 10:02 pts/0    00:00:00 /opt/stackstorm/virtualenvs/ansible/bin/python /opt/stackstorm/virtualenvs/ansible/bin/ansible-playbook --inventory-file=host.domain.tld, -vvv /opt/encore/ansible/playbooks/wait_for_connection.yaml --extra-vars='{"wait_timeout": 2, "wait_connect_timeout": 2, "wait_sleep": 2, "ansible_user": "user", "ansible_ssh_pass": "xxx"}'
root     12726 12713  2 10:02 pts/0    00:00:00 /opt/stackstorm/virtualenvs/ansible/bin/python /opt/stackstorm/virtualenvs/ansible/bin/ansible-playbook --inventory-file=host.domain.tld, -vvv /opt/encore/ansible/playbooks/wait_for_connection.yaml --extra-vars='{"wait_timeout": 2, "wait_connect_timeout": 2, "wait_sleep": 2, "ansible_user": "user", "ansible_ssh_pass": "xxx"}'

This ansible command times out after the default 10 minutes for the playbook.

System Details

$ ansible --version
ansible 2.3.2.0
  config file = 
  configured module search path = Default w/o overrides
  python version = 2.7.5 (default, Nov  6 2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)]

$ st2 --version
st2 2.4.0

$ st2 pack get ansible
+-------------+--------------------------------------------------+
| Property    | Value                                            |
+-------------+--------------------------------------------------+
| name        | ansible                                          |
| version     | 0.5.2                                            |
| author      | StackStorm, Inc.                                 |
| email       | [email protected]                              |
| keywords    | [                                                |
|             |     "ansible",                                   |
|             |     "cfg management",                            |
|             |     "configuration management"                   |
|             | ]                                                |
| description | st2 content pack containing ansible integrations |
+-------------+--------------------------------------------------+

Debugging

Manually running the following command executes just, ansible logs in immediately and no timeouts.

 /opt/stackstorm/virtualenvs/ansible/bin/python /opt/stackstorm/virtualenvs/ansible/bin/ansible-playbook --inventory-file=host.domain.tld, -vvv /opt/encore/ansible/playbooks/wait_for_connection.yaml --extra-vars='{"wait_timeout": 2, "wait_connect_timeout": 2, "wait_sleep": 2, "ansible_user": "user", "ansible_ssh_pass": "xxx"}'

However, running the following does NOT work (waits forever until a timeout).

sudo -E -- bash -c /opt/stackstorm/packs/ansible/actions/ansible_playbook.py --extra_vars='[{u'"'"'wait_sleep'"'"': 2, u'"'"'ansible_ssh_pass'"'"': u'"'"'xxx'"'"', u'"'"'ansible_user'"'"': u'"'"'user'"'"', u'"'"'wait_timeout'"'"': 2, u'"'"'wait_connect_timeout'"'"': 2}]' --inventory_file=host.domain.tld, -vvv /opt/encore/ansible/playbooks/wait_for_connection.yaml

I next added a print statement to my playbook

  pre_tasks:
    - name: Display all variables/facts
      debug:
        msg: "{{ vars | to_nice_json(indent=4) }}"

Now i compared the outputs from the first command to the second command.

Output from the first command that invokes ansible_playbook.py:


TASK [Display all variables/facts] ************************************************************************************
task path: /opt/encore/ansible/playbooks/wait_for_connection.yaml:13
ok: [host.domain.tld] => {
    "msg": {
        "_raw_params": "'{\"wait_timeout\": 2, \"wait_connect_timeout\": 2, \"wait_sleep\": 2, \"ansible_user\": \"user\", \"ansible_ssh_pass\": \"xxx\"}'",                                                                             
        "ansible_check_mode": false, 
        "ansible_play_batch": [
            "host.domain.tld"
        ], 
        "ansible_play_hosts": [
            "host.domain.tld"
        ],
....
 
        "wait_connect_timeout": 5, 
        "wait_sleep": 1, 
        "wait_timeout": 600
    }
}

Output from the second command that invoking ansible directly:


TASK [Display all variables/facts] ************************************************************************************
ok: [host.domain.tld] => {
    "msg": {
        "ansible_check_mode": false, 
        "ansible_play_batch": [
            "host.domain.tld"
        ], 
        "ansible_play_hosts": [
            "host.domain.tld"
        ], 
        "ansible_play_hosts_all": [
            "host.domain.tld"
        ], 
        "ansible_playbook_python": "/opt/stackstorm/virtualenvs/ansible/bin/python", 
        "ansible_ssh_pass": "xxx", 
        "ansible_user": "user", 
....
        "wait_connect_timeout": 2, 
        "wait_sleep": 2, 
        "wait_timeout": 2
    }
}

As you can see, when invoking ansible_playbook.py the variables are are not merged properly into the vars dict, causing the connection to fail repeatedly until the timeout of 10 minutes (default) occurs.

Conversely, when we invoke it from shell, the --extra-vars is merged properly with the vars dict and the connection works as expected.

Proposed Solution

The ansible-playbook is where the command is being executed here https://github.com/StackStorm-Exchange/stackstorm-ansible/blob/master/actions/lib/ansible_base.py#L105 .

If we change that line to use a shell, and convert the command from a list to a string (see below), then everything works as expected.

exit_code = subprocess.call(' '.join(self.cmd), env=os.environ.copy(), shell=True)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions