Discover a hostname in one play and use it to run the next play?

I’m doing some cloud scripting that involves some work on one host, then using the results to take action on another host. The hostname of the second host is a dynamic result of that first bit, and won’t be in my inventory. I’d like to do as much of it as possible in ansible, and add_host seems to be working. But I’m having trouble using a variable or fact from one play as the hosts value for the next play.

Here’s a test case that I’ve been working on: I’ve made it as simple as possible.

test.yml

  • hosts: 127.0.0.1
    tasks:

  • set_fact: hostname_test=‘fubar’

  • add_host: name=‘{{ hostname_test }}’
    register: resp

  • debug: msg=‘{{ resp }}’

  • hosts: 127.0.0.1
    tasks:

  • debug: msg=‘resp={{ resp|default(“None”) }}’

  • debug: msg=‘hostname_test={{ hostname_test|default(“None”) }}’

end

I’d like that second play to start with - hosts: hostname_test. But that results in “skipping: no hosts matched”. At first I thought that meant add_host wasn’t working. But if I hardcode hosts: fubar for the second play then ansible tries to connect as expected. However I need to determine the second hostname as part of the first play. Running the code as above suggests that neither set_fact nor register persists from the first play to the next: both seem to be undefined.

Is it possible to use a hostname determined in one play as the hosts value for the next play? If so, what am I doing wrong? Is there a better way to persist new information from one play to the next?

Is it significant that ansible reports the first play under 127.0.0.1, as in the yml, but the second play shifts to localhost?

Thanks in advance for any help.

$ ansible --version
ansible 1.8.4
configured module search path = None
$ ansible-playbook -v test.yml

PLAY [127.0.0.1] **************************************************************

GATHERING FACTS ***************************************************************
ok: [127.0.0.1]

TASK: [set_fact hostname_test=‘fubar’] ****************************************
ok: [127.0.0.1] => {“ansible_facts”: {“hostname_test”: “fubar”}}

TASK: [add_host name=‘{{ hostname_test }}’] ***********************************
ok: [127.0.0.1] => {“new_host”: “fubar”}

TASK: [debug msg=‘{{ resp }}’] ************************************************
ok: [127.0.0.1] => {
“msg”: “{‘invocation’: {‘module_name’: u’add_host’, ‘module_args’: u"name=‘fubar’"}, ‘new_host’: u’fubar’}”
}

PLAY [127.0.0.1] **************************************************************

GATHERING FACTS ***************************************************************
ok: [localhost]

TASK: [debug msg=‘resp=None’] *************************************************
ok: [localhost] => {
“msg”: “resp=None”
}

TASK: [debug msg=‘hostname_test=None’] ****************************************
ok: [localhost] => {
“msg”: “hostname_test=None”
}

PLAY RECAP ********************************************************************
127.0.0.1 : ok=4 changed=0 unreachable=0 failed=0
localhost : ok=3 changed=0 unreachable=0 failed=0

Play's hosts are rendered pretty early, I think you can only use --extra-vars in there. But you can put a group name in the second play, and add that dynamic host to that group.

Michael Blakeley <michael.blakeley@gmail.com> napisał:

Thanks, and I can probably do something with that. My inventory is ec2.py, so I’ll have to look into how to supplement it with at least one extra group. I believe that’s possible using an inventory directory, or else I’ll write a wrapper script.

Along with that I think I’m still going to need to pass some new information from one play to the next, so I’d like to understand why the test.yml at the start of this thread isn’t working as expected. Both plays run as “hosts: 127.0.0.1”, but the second play doesn’t seem to have access to variables or facts set by the first play. The docs at http://docs.ansible.com/playbooks_variables.html say that “Registered variables are valid on the host the remainder of the playbook run, which is the same as the lifetime of facts in Ansible. Effectively registered variables are just like facts.” Then http://docs.ansible.com/set_fact_module.html says that “Variables are set on a host-by-host basis just like facts discovered by the setup module. These variables will survive between plays.”

In my test case neither set_fact nor register seems to be working as described. Information gathered in the first play isn’t available to tasks in the second play. I suspect ansible’s getting confused about the hosts. Both plays have “hosts: 127.0.0.1”, but the output shows 127.0.01 for the first play and localhost for the second. For this test my global inventory file is the only one in scope, and it has this entry:

localhost ansible_connection=local ansible_python_interpreter=/usr/local/bin/python

If I change test.yml to target “hosts: localhost” for both plays, then my test.yml works. Using hostvars on 127.0.0.1 also works, but feels like it shouldn’t be necessary:

test.yml

  • hosts: 127.0.0.1
    tasks:

  • set_fact: hostname_test=‘fubar’

  • add_host: name=‘{{ hostname_test }}’
    register: resp

  • debug: msg=‘{{ resp }}’

  • hosts: 127.0.0.1
    tasks:

  • debug: msg=‘resp={{ resp|default(“None”) }}’

  • debug: msg=‘hostname_test={{ hostname_test|default(“None”) }}’

  • debug: msg=‘resp={{ hostvars[“127.0.0.1”][“resp”]|default(“None”) }}’

  • debug: msg=‘hostname_test={{ hostvars[“127.0.0.1”][“hostname_test”]|default(“None”) }}’

end

Partial output:

TASK: [debug msg=‘resp=None’] *************************************************
ok: [localhost] => {
“msg”: “resp=None”
}

TASK: [debug msg=‘hostname_test=None’] ****************************************
ok: [localhost] => {
“msg”: “hostname_test=None”
}

TASK: [debug msg=‘resp={{ hostvars[“127.0.0.1”][“resp”]|default(“None”) }}’] ***
ok: [localhost] => {
“msg”: “resp={‘invocation’: {‘module_name’: u’add_host’, ‘module_args’: u"name=‘fubar’"}, ‘new_host’: u’fubar’}”
}

TASK: [debug msg=‘hostname_test={{ hostvars[“127.0.0.1”][“hostname_test”]|default(“None”) }}’] ***
ok: [localhost] => {
“msg”: “hostname_test=fubar”
}

The first two debugs output None while the second two output the expected values. Apparently ansible (1.8.4) is smart enough to map 127.0.0.1 to localhost, but does so in a way that breaks variable scope across plays. Bug?

– Mike

In case the variable scope behavior is a bug, I’ve filed https://github.com/ansible/ansible/issues/10424

I’ve more or less solved the original problem now. There’s a sample playbook at http://stackoverflow.com/a/28977513/908390 that shows the tricks I had to use. I’d welcome any suggestions to make it cleaner.