11

I have a playbook that runs different roles on different hosts. Is it possible to pass a variable from one role running on one host to another role on another host running within the same playbook run? Or any workaround ?

playbook
   host1
     role1
       here I get some variables: var1 var2 ...etc
   host2
     role2
       here I need to use var1 var2 ... etc from the above host/role

The task in role1 that sets teh variable db looks like this:

- shell: cd /ACE/conf && grep ^db.url local1.properties | awk -F/ '{print $4}' | awk -F? '{print $1}'
  register: db

UPDATE: On the first host the values are dynamic, it's like a configuration file that is always updated. After I store the values in variables on host1 with the role1 I then move to the host2, run the role2 and do stuff with those values from variables stored by host1.

I tried with hostvars:

{{ hostvars.LBL.db.stdout }}
{{ hostvars['LBL']['db'] }}
{{ hostvars['LBL']['db']['stdout'] }}

and I get error:

in get_variables raise Exception("host not found: %s" % hostname) Exception: host not found: LBL

LBL exists in hosts as on it I run the first role

I set a variable on one host and I want that variable to be available to the other host. All this within a single playbook. Can it be done ?

hostvars is not working using it like this:

---   
- name: test hostvars host1
  hosts: LBL
  tasks:
    - command: "ls /bin"
        register: ls_out

- name: test hostvars host2
  hosts: LM
  tasks:
    - debug:
        var: "{{ hostvars['LBL']['ls_out']['stdout'] }}"

error:

fatal: [10.104.148.138] => host not found: LBL

/etc/ansible/hosts

[root@NS1 ansible]# cat /etc/ansible/hosts
[LBL]
10.104.148.136
[LM]
10.104.148.138
tlo
  • 1,571
  • 1
  • 25
  • 38
ady8531
  • 689
  • 5
  • 13
  • 24
  • Can you post the portion of playbook that sets teh variables var1/2? `hostvars.LBL.*` should've worked. If the var1/2 are defined in inventory file then post that too. – Kashyap Mar 31 '15 at 15:34
  • This is the first role `- name: bla bla shell: cd /ACE/conf && grep ^db.url local1.properties | awk -F/ '{print $4}' | awk -F? '{print $1}' register: db` If I do and echo {{ do.stdout }} in the same role it works great. I don't use an inventory. In the second role if I do `shell: echo "{{ hostvars['LBL']['db']['stdout'] }}"` it gives the error – ady8531 Mar 31 '15 at 15:39
  • "hostvars is not working using it like this:" that's not possible. Those two tasks work just fine for me and print `ls_out` as expected. Posted wrong code or something...? Please elborate on "not working" – Kashyap Mar 31 '15 at 16:40
  • I added the output of that single playbook at the question above. Ansible version is 1.9.0.1 ... same output for 1.8.4 – ady8531 Mar 31 '15 at 17:25

5 Answers5

17

This topic is complicated, and there are two different answers depending on what you want.

Access a variable defined in one role inside other for the same host

Example:

---
- hosts: host1
- roles:
    - role1
    - role2

Goal: You want to access some variable from role1 inside role2.

Use set_fact module.

Inside role1:

name: save precious value
set_fact: 
  pantsu: shiroi

Inside role2:

name: Nozoki...
debug: msg="Color is {{ pantsu }}"

Access to static variable for one host (or group) to other

Example:

[group_foo]
host1
host2
[group_bar]
host3
host4

group_vars/group_foo

important_value=bla-bla-ba

Goal: You want to use it in playbook for group2.

This is much trickier to do.

Inside group_vars/group_bar

other_var: '{{hostvars[groups["group_foo"][0]].important_value}}'

You can use other indexes besides '0'.

George Shuklin
  • 6,952
  • 10
  • 39
  • 80
  • The first example si a basic variable to value allocation, it does not apply here because I use two different hosts. On the first host the values are dynamic it's like a configuration file that is always updated. After I store the values in variables on that first host with the first role I then move to the second host, run the second role and do stuff with those values from variables stored by host one. I set a variable on one host and I want that variable to be available to the other host. All this within a single playbook. Basically both hosts are in the same group. – ady8531 Mar 31 '15 at 14:22
  • you can set_fact in one playbook and than access it though `hostvars: {{ hostvars['serv1']['saved_fact'] }}` – George Shuklin Mar 31 '15 at 16:12
  • `--- - name: test hostvars host1 hosts: LBL tasks: - shell: echo hostname register: ls_out - set_fact: ls_down='{{ ls_out.stdout }}' - name: test hostvars host2 hosts: LM tasks: - shell: echo {{ hostvars['LBL']['ls_down'] }}` error: `fatal: [10.104.148.138] => host not found: LBL` – ady8531 Mar 31 '15 at 16:31
  • @ady8531, you using set_fact wrong. It not takes foo=bar notation, it require a list of dicts. See examples here: http://docs.ansible.com/set_fact_module.html. Here working example for the code above: https://gist.github.com/amarao/5ebe4d9c2fd04dfaf287 – George Shuklin Apr 01 '15 at 22:14
  • 2
    Thanks for a clear suggestion as to how to peek at shiroi pantsu. :P – Taro Sato Oct 12 '17 at 21:43
6

The problem is in your inventory.

This message:

fatal: [10.104.148.138] => host not found: LBL

is because LBL is a group and not a host. Group LBL has one host in it: 10.104.148.136

Do one of the following:

1. Change your inventory (/etc/ansible/hosts) to:

LBL ansible_ssh_host=10.104.148.136
LM ansible_ssh_host=10.104.148.138

2. or if you really know what you're doing and LBL is a group and you wanna keep it that way then access the variable with:

{{ hostvars['10.104.148.136']['db']['stdout'] }}

Again LBL is a group not a host. More info.

Evan Kroske
  • 4,506
  • 12
  • 40
  • 59
Kashyap
  • 15,354
  • 13
  • 64
  • 103
  • Actually I modified the hosts file and added: `[ow.hosts] LBL LM` and it's working. thanks a lot! – ady8531 Mar 31 '15 at 19:27
  • is it possible to use something similar to this: `with_items: "{{ hostvars['LBL']['db_*'] }}"` for a loop ? Example: on LBL host I store some variables named: db_ace, db_abc etc. How can I call them within a loop from another host? hostvars.LBL.db_* ? – ady8531 Apr 01 '15 at 08:39
  • @ady8531 nothing from top of my head.. check if there is some Jinja2 filter to filter the a list.. If there is then you can filter `hostvars['LBL'].keys()`, assign it to some other variable using `set_fact` or in `vars:` section.. – Kashyap Apr 01 '15 at 16:15
2

Here's my solution. My task was to sync data between two servers and I wanted to pass in the server names like this: ansible-playbook sync.yaml -e "source=host1 destination=host2"

Here's the main playbook:

---

- name: get_sync_facts
  hosts: "{{ source }}"
  roles:
    - set_sync_facts

- name: sync
  hosts: "{{ destination }}" 
  roles:
    - get_sync_facts
    - sync

Here's the set_sync_facts role:

---

- set_fact: src_media_dir='/some/dir/'
- set_fact: src_user='myuser'
- set_fact: src_host='1.1.1.1'
- set_fact: src_port=12345
- set_fact: src_db_user='dbuser'
- set_fact: src_db_password='something'
- set_fact: src_db_name='some_db'

(I actually derived some of these from tasks and others from host vars but you get the point)

And here's the get_sync_facts role:

---

- set_fact: src_media_dir={{ hostvars[source]['src_media_dir'] }}
- set_fact: src_user={{ hostvars[source]['src_user'] }}
- set_fact: src_host={{ hostvars[source]['src_host'] }}
- set_fact: src_port={{ hostvars[source]['src_port'] }}
- set_fact: src_db_user={{ hostvars[source]['src_db_user'] }}
- set_fact: src_db_password={{ hostvars[source]['src_db_password'] }}
- set_fact: src_db_name={{ hostvars[source]['src_db_name'] }}

You could do without this and just reference hostvars directly in your plays but this seemed easier to maintain as it corresponds directly with the set_sync_facts role.

Andy Baker
  • 21,158
  • 12
  • 58
  • 71
  • I basically have exactly the same problem to solve, however, your solution doesn't seem to be working for me. I get: `fatal: [1.2.3.4]: FAILED! => {"failed": true, "msg": "'ansible.vars.hostvars.HostVars object' has no attribute u'source'"}` where source is my var (e.g. staging). In my first play I finish with a `set_fact: blah={{somvar}}` and in the second play I start by doing `set_fact: blah={{hostvars[source][blah]}}`. What am I doing wrong? – seeafish Jul 09 '16 at 13:03
  • Ah, I made the mistake of literally passing `staging` to the `hostvars`. Dumped out the whole `hostvars` structure and saw it's organised (obviously) by actual hostname. Gives me something to work with, however, I might have to change my inventory grouping as I cannot guarantee that `staging[0]` for example will always be a relevant web host (could be a db node). How is your inventory set up if you don't mind me asking? – seeafish Jul 09 '16 at 13:35
  • @seeafish My inventory is wholly dynamic. I call a python script that returns all my hosts and then I do something like: ansible-playbook -i dynamic_inventory/get_hosts playbook.yaml -e "group=${DESTINATION} ${SOURCE}" – Andy Baker Jul 12 '16 at 12:52
  • Thanks. I ended up restructuring my static inventory to separate environments and role and select with :children where needed. Then I am doing two extra vars `-e source=[source_group]` and `-e target=[target_group]` then setting hosts in the playbook as `{{source}}-web` for example. – seeafish Jul 13 '16 at 11:54
1

it is quite old topic, but maybe it will be helpfull to somebody. I used sed to get hostname based on "target" specified in extra parameters.

To use this "target" group must contain only 1 hostname.

My inventory hosts:

[ansible_local]
localhost

[machine1]
machine1.domain.tld

upgrade_packages.yml

---
- hosts: '{{ target }}'
  sudo: yes
  tasks:
  - name: check for Debian system
    shell: /bin/false
    when: ansible_pkg_mgr != "apt"

  - name: full-upgrade all packages
    apt: update_cache=yes upgrade=full
    register: upgrade_result

- hosts: ansible_local
  tasks:
  - name: find out host from target
    shell: /bin/sed -n -e '/^\[{{ target }}\]$/,/^\[.*\]$/ { /^\[/d; /^$/d; p; }' {{ inventory_file }}
    register: target_inventory

  - name: Display all facts from target machine (change when to true if needed)
    debug: var=hostvars[target_inventory.stdout]
    when: false

  - name: Display upgrade result on ansible_local node
    debug: var=hostvars[target_inventory.stdout].upgrade_result.msg

Invoke with: ansible-playbook ./upgrade-packages.yml -e "target=machine1" -v

Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358
Quoing
  • 11
  • 2
0

I had a similar setup where there are three hosts h1,h2,h3 and on each of them I wanted to define a fact { "important_fact": "foo" } (and bar, and baz, respectively)

To obtain a list containing all the different important_facts on all hosts, you can do this like so:

- set_fact:
    important_facts_list: "{{ hostvars | json('*.important_fact') }}"
  run_once: yes
  delegate_to: h1

important_facts_list will contain [ 'foo', 'bar', 'baz' ], and you can now iterate over them using with_items.