Generating a config file with data from other servers

Hi,
I'm not sure if I have missed the way to do this, but essentially I
want to generate parts of my config file with Ansible. This is due in
part because often I'm working late into the night and making mistakes
when half asleep.

Here is what I need to generate:

        datacenter VXNY {
                dc-node-address-port cl201.domainname.net 3000
                dc-node-address-port cl202.domainname.net 3000
                dc-node-address-port cl203.domainname.net 3000
                dc-node-address-port cl204.domainname.net 3000
                dc-node-address-port cl205.domainname.net 3000
                dc-node-address-port cl206.domainname.net 3000
                dc-node-address-port cl207.domainname.net 3000
                dc-node-address-port cl208.domainname.net 3000
                dc-int-ext-ipmap 172.30.17.162 170.6.108.162
                dc-int-ext-ipmap 172.30.17.163 170.6.108.163
                dc-int-ext-ipmap 172.30.17.164 170.6.108.164
                dc-int-ext-ipmap 172.30.17.165 170.6.108.165
                dc-int-ext-ipmap 172.30.17.166 170.6.108.166
                dc-int-ext-ipmap 172.30.17.167 170.6.108.167
                dc-int-ext-ipmap 172.30.17.14 170.6.108.168
                dc-int-ext-ipmap 172.30.17.15 170.6.108.169

the variables are the hostnames and eth0 and eth1. I understand how
to call these with discovery, but how do I tell Ansible to check 8
machines for this information?

Thanks,
James

When using a template, in Jinja2 syntax, you can iterate through the variable groups in a loop, which is a hash of all machine names in a group, keyed off the name of the group, and then with each group name access something like {{ hostvars[systemName][“ansible_some_fact”] }}.

So getting facts for other machines is pretty easy.

Reading the advanced playbooks section on accessing information about
other hosts

So I have been able to set this easily.

${ansible_eth1.ipv4.address}

But when you say group, am I access my hosts file?

is my hostvars the group in the hosts file?

is this closer?

${CL800.cl801.domain.net.ansible_eth1.ipv4.address)

Feel like I'm not getting it.

James

Here's a simple example that shows the first IP address of every
machine in the webservers group:

{% for host in groups['webservers'] %}
  HOST: {{ host }} IP: {{ hostvars[host]['ansible_all_ipv4_addresses'][0] }}
{% endfor %}

From this, the JInja2 reference documentation, and the output of

"ansible hostname -m setup" you should be able to get the facts you
want.

What is the easiest way to test this on the command line?

Is this code supposed to be in the .j2 template or in vars/somevariable.yml?

When I understand the answers to those questions, how then do I
reference the generated list? is it:
${{{ hostvars[groups['CL000']]['ansible_all_ipv4_addresses'][0] }}

CL000 is a group in the hosts file with FQDNs

ansible-playbook deploy.smart.citrusleaf.config.yml -u jmarcus -K -vvvvvv

sheepchase:tasks jmarcus$ ansible --version
ansible 0.7

Thanks,
James

What is the easiest way to test this on the command line?

Make a template, test it in a minimal playbook that writes something
to /tmp/somefile.txt

Is this code supposed to be in the .j2 template or in vars/somevariable.yml?

It's a template.

When I understand the answers to those questions, how then do I
reference the generated list? is it:
${{{ hostvars[groups['CL000']]['ansible_all_ipv4_addresses'][0] }}

I kind of answered your template problem for you already.

Here is what I have tried:

sheepchase:tasks jmarcus$ cat deploy.smart.citrusleaf.config.yml
- hosts: CL801
  user: jmarcus
  sudo: True
  gather_facts: True

  tasks:
   - name: Create the CitrusLeaf config file
     action: template
src=/Users/jmarcus/ServerConfig/IT/CitrusLeaf/Sites/CL800/templates/citrusleaf.conf.j2
dest=/etc/citrusleaf/citrusleaf.test.conf
     tags:

sheepchase:templates jmarcus$ cat citrusleaf.conf.j2
dc-node-address-port
[CL000]
{% for host in groups['CL000'] %}
  HOST: {{ host }} IP: {{ hostvars[host]['ansible_all_ipv4_addresses'][0] }}
{% endfor %}
${{{ hostvars[groups['CL000']]['ansible_all_ipv4_addresses'][0] }}}

Here is what I get:

sheepchase:tasks jmarcus$ ansible-playbook
deploy.smart.citrusleaf.config.yml -u jmarcus -K -vvvvvv
sudo password:

PLAY [CL801] *********************

GATHERING FACTS *********************
<cl801.domain.net> ESTABLISH CONNECTION FOR USER: jmarcus
<cl801.domain.net> EXEC "$SHELL" -c 'mkdir -p
$HOME/.ansible/tmp/ansible-1345815117.08-219950512120727 && chmod a+rx
$HOME/.ansible/tmp/ansible-1345815117.08-219950512120727 && echo
$HOME/.ansible/tmp/ansible-1345815117.08-219950512120727'
<cl801.domain.net> REMOTE_MODULE setup
<cl801.domain.net> PUT
/var/folders/_s/lmr53wfd0xz81v8_0x317x4h0000gn/T/tmpfV8h3w TO
/home/jmarcus/.ansible/tmp/ansible-1345815117.08-219950512120727/setup
<cl801.domain.net> EXEC "$SHELL" -c 'chmod u+x
/home/jmarcus/.ansible/tmp/ansible-1345815117.08-219950512120727/setup'
<cl801.domain.net> EXEC sudo -k && sudo -p "[sudo via ansible,
key=xriwjqswerornwtwqhlrfbwzasdfoqqmji] password: " -u root "$SHELL"
-c /home/jmarcus/.ansible/tmp/ansible-1345815117.08-219950512120727/setup
<cl801.domain.net> EXEC "$SHELL" -c 'rm -rf
/home/jmarcus/.ansible/tmp/ansible-1345815117.08-219950512120727/'
ok: [cl801.domain.net]

TASK: [Create the CitrusLeaf config file] *********************
<cl801.domain.net> ESTABLISH CONNECTION FOR USER: jmarcus
<cl801.domain.net> EXEC "$SHELL" -c 'mkdir -p
$HOME/.ansible/tmp/ansible-1345815120.45-88585705088006 && chmod a+rx
$HOME/.ansible/tmp/ansible-1345815120.45-88585705088006 && echo
$HOME/.ansible/tmp/ansible-1345815120.45-88585705088006'
<cl801.domain.net> EXEC "$SHELL" -c 'rm -rf
/home/jmarcus/.ansible/tmp/ansible-1345815120.45-88585705088006/'
fatal: [cl801.domain.net] => {'msg': "expected token ':', got '}'",
'failed': True, 'module': 'template'}
fatal: [cl801.domain.net] => {'msg': "expected token ':', got '}'",
'failed': True, 'module': 'template'}

PLAY RECAP *********************
cl801.domain.net : ok=1 changed=0 unreachable=1
failed=0

I hope this will shed some more light on the problem I'm having.
Sorry if this seems obvious to everyone else, but it doesn't to me.
Thanks,
James

${{{ hostvars[groups[‘CL000’]][‘ansible_all_ipv4_addresses’][0] }}}

This above line is basically nonsense

Print out what groups[‘CL000’] and it should become more clear. The value is a list of hostnames, not a string.

[CL000]
cl001.domain.net
cl002.domain.net
cl003.domain.net
cl004.domain.net
cl005.domain.net
cl006.domain.net
cl007.domain.net
cl008.domain.net
cl009.domain.net
cl010.domain.net

When you say print, what is in CL000 is this something I do with Jinja2?

James

So I have been working with a coworker on this issue.

I can get the hosts to print with

{% for host in groups['CL000'] %}
  HOST: {{ host }} IP: {{ hostvars[host] }}
{% endfor %}

but when we get hostvars, they are the hostvars of the servers in the
CL000 group, they are the hostvars or "setup" of the target system..

How do we get the hostvars of the servers in the group CL000?

Thanks
James

So in working through this issue we now have a play that looks like this:

- hosts: CL000
  user: jmarcus
  gather_facts: True

  tasks:
   - name: Create the CitrusLeaf config file
     action: template
src=/Users/jmarcus/ServerConfig/IT/CitrusLeaf/Sites/CL800/templates/citrusleaf.conf.j2
dest=/home/jmarcus/citrusleaf.test.conf
     tags:
        - clconfigfile
     delegate_to: cl801.domain.net

But I'm guessing this isn't the way to do things, because it presents
authentication problems.

This is the only way we have been able to get "setup" information in
citrusleaf.test.conf from the hosts in CL000.

James

There's no reason to use delegate_to here.

Step 1) run a play on all your hosts to gather facts, it doesn't even
need any tasks.

Step 2) run a play just against cl801.domain.net

Any time you are using delegate_to without a variable you are doing it
wrong. You could quite literally be executing that 200 times if you
had 200 hosts.

Just do 2 plays.

So I have done this, to gather facts, normally I do this in the play.

ansible all -m setup

I may have missed it in the documentation, does this get stored somewhere?

James

I dont' need this last question answered

Thanks for the help.

I had lots of help from someone at my office too and now I'm getting some where:

sheepchase:tasks jmarcus$ cat deploy.smart.citrusleaf.config.yml
- hosts: CL000
  user: jmarcus
  gather_facts: True

- hosts: CL801
  user: jmarcus
  gather_facts: False

  tasks:
   - name: Create the CitrusLeaf config file
     action: template
src=/Users/jmarcus/ServerConfig/IT/CitrusLeaf/Sites/CL800/templates/citrusleaf.conf.j2
dest=/home/jmarcus/citrusleaf.test.conf
     tags:
        - clconfigfile

sheepchase:templates jmarcus$ cat citrusleaf.conf.j2
{% for host in groups['CL000'] %}
    dc-node-address-port {{
hostvars[host]['ansible_eth0']['ipv4']['address'] }} 3000 {% endfor %}
{% for host in groups['CL000'] %}
    dc-int-ext-ipmap {{
hostvars[host]['ansible_eth1']['ipv4']['address'] }} {{
hostvars[host]['ansible_eth0']['ipv4']['address'] }} {% endfor %}

[jmarcus@cl801 ~]$ cat citrusleaf.test.conf

    dc-node-address-port 143.48.118.70 3000
    dc-node-address-port 143.48.118.72 3000
    dc-node-address-port 143.48.118.74 3000
    dc-node-address-port 143.48.118.76 3000
    dc-node-address-port 143.48.118.78 3000
    dc-node-address-port 143.48.118.80 3000
    dc-node-address-port 143.48.118.82 3000
    dc-node-address-port 143.48.118.84 3000
    dc-node-address-port 143.48.118.86 3000
    dc-node-address-port 143.48.118.88 3000

    dc-int-ext-ipmap 10.1.0.170 143.48.118.70
    dc-int-ext-ipmap 10.1.0.172 143.48.118.72
    dc-int-ext-ipmap 10.1.0.174 143.48.118.74
    dc-int-ext-ipmap 10.1.0.176 143.48.118.76
    dc-int-ext-ipmap 10.1.0.178 143.48.118.78
    dc-int-ext-ipmap 10.1.0.180 143.48.118.80
    dc-int-ext-ipmap 10.1.0.82 143.48.118.82
    dc-int-ext-ipmap 10.1.0.84 143.48.118.84
    dc-int-ext-ipmap 10.1.0.86 143.48.118.86
    dc-int-ext-ipmap 10.1.0.88 143.48.118.88

- hosts: CL000
  user: jmarcus
  gather_facts: True

- hosts: CL300
  user: jmarcus
  gather_facts: True

- hosts: CL801
  user: jmarcus
  gather_facts: False

  tasks:
   - name: Create the CitrusLeaf config file
     action: template
src=/Users/jmarcus/ServerConfig/IT/CitrusLeaf/Sites/CL800/templates/citrusleaf.conf.j2
dest=/home/jmarcus/citrusleaf.test.conf
     tags:
        - clconfigfile

citrusleaf.conf.j2

{% for host in groups['CL000'] %}
                dc-node-address-port {{
hostvars[host]['ansible_eth0']['ipv4']['address'] }} 3000 {% endfor %}
{% for host in groups['CL000'] %}
                dc-int-ext-ipmap {{
hostvars[host]['ansible_eth1']['ipv4']['address'] }} {{
hostvars[host]['ansible_eth0']['ipv4']['address'] }} {% endfor %}
        }

        datacenter AM {
{% for host in groups['CL300'] %}
                dc-node-address-port {{
hostvars[host]['ansible_eth0']['ipv4']['address'] }} 3000 {% endfor %}

But I'm getting this error when running the play:

TASK: [Create the CitrusLeaf config file] *********************
fatal: [cl801.domain.net] => {'msg': "'dict object' has no attribute
'ansible_eth0'", 'failed': True}
fatal: [cl801.domain.net] => {'msg': "'dict object' has no attribute
'ansible_eth0'", 'failed': True}

Does this have to do with how I setup the playbook?

James

James,

A few questions are ok, but I need you do some investigation of your
own too, else I'm going to start charging you :slight_smile:

Think about why that host might not have that attribute, print out the
dictionaries (don't paste them back here, but read them and try to
understand them), and so on.

You can easily also test what facts a host supplies by:

ansible hostname -m setup

You clearly got the interface information for most of the hosts, but
perhaps this one doesn't have an eth0, and you were trying it on other
hosts before.

There are other facts to choose from, like the ipv4 address list, that
may be more appropriate.

--Michael

okay thanks