Accessing ansible host_vars with variable name - ansible

I am trying to get the host_vars using a variable and getting warning messages, is there any better way to achieve this?
My Inventory yaml is
dev_cluster:
hosts:
host-vm01:
install:
httpd: true
zookeeper: true
mysqld: true
host-vm02:
install:
httpd: true
zookeeper: true
mysql: true
host-vm03:
install:
httpd: true
mysql: false
This is my When block, the component_name is the extra variable that will be changing while calling the play.
- name: ADD HOST
add_host:
name: "{{ inventory_hostname }}"
group: component_name_group
delegate_to: localhost
changed_when: false
when:
- hostvars[inventory_hostname].install.{{ component_name }} is defined
- hostvars[inventory_hostname].install.{{ component_name }}
Below is the warning message I am getting.
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: hostvars[inventory_hostname].install.{{ component_name }} is defined
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: hostvars[inventory_hostname].install.{{ component_name }}
I tried various possibilities and ran out of options now.
The conditional check 'hostvars[inventory_hostname].install.[component_name] is defined'
The conditional check 'hostvars[inventory_hostname].install.'component_name' is defined'
If I put single quotes or double quotes erroring out. Please help
Thanks

Create the group component_name_group in the first play and use it in the second play.
For example, the playbook below
shell> cat playbook.yml
- name: "Create group component_name_group"
hosts: all
gather_facts: false
vars:
host_install: "{{ dict(ansible_play_hosts_all|
zip(ansible_play_hosts_all|
map('extract', hostvars, 'install'))) }}"
hosts_component: "{{ host_install|dict2items|json_query(_query) }}"
_query: "[?value.{{ component_name|d('none') }}].key"
tasks:
- block:
- assert:
that: hosts_component|length > 0
fail_msg: Nothing to install.
- add_host:
name: "{{ item }}"
groups: component_name_group
loop: "{{ hosts_component }}"
run_once: true
- name: "Install {{ component_name }}"
hosts: component_name_group
gather_facts: false
tasks:
- debug:
msg: "Install {{ component_name }} on {{ inventory_hostname }}"
creates the dictionary host_install
host_install:
host-vm01:
httpd: true
mysqld: true
zookeeper: true
host-vm02:
httpd: true
mysql: true
zookeeper: true
host-vm03:
httpd: true
mysql: false
and, for example, given component_name=httpd, creates the list of the hosts and adds them to the group
hosts_component:
- host-vm01
- host-vm02
- host-vm03
The second play then installs the utility
shell> ansible-playbook playbook.yml -e component_name=httpd
PLAY [Create group component_name_group] *****************************
...
PLAY [Install httpd] *************************************************
TASK [debug] *********************************************************
ok: [host-vm01] =>
msg: Install httpd on host-vm01
ok: [host-vm02] =>
msg: Install httpd on host-vm02
ok: [host-vm03] =>
msg: Install httpd on host-vm03
...

Related

ansible to perform a task based on hostname value

Hi I am trying below.
task:
- name: Perform on primary server
blockinfile:
path: '/home/conf'
marker: "#-- {mark} Adding Values --"
block: |
{{ conf }}
when: "'host01' in inventory_hostname"
- name: Perform on stdby server
blockinfile:
path: '/home/conf'
marker: "#-- {mark} Adding Values --"
block: |
{{ conf_stdby }}
when: "'host02' in inventory_hostname"
As every task is performed simultaneously on all hosts, i was expecting it would change host01 in 1st task and skip host02 and vice-versa in second task, However it changed in both hosts in both tasks and when i checked the servers both had conf_stdby.
Also there are many more tasks in my playbook which are common to both the hosts.
inventory_hostname wouldn't work as in inventory file of playbook there is ip no hostname, so is there a way i can use actual host's hostname in when condition?
Even tried this
vars:
my_conf:
host01: "{{ conf }}"
host02: "{{ conf_stdby }}"
task:
- name: Perform on primary server
blockinfile:
path: '/home/conf'
marker: "#-- {mark} Adding Values --"
block: |
{{ conf }}
when: ""{{hostvars[inventory_hostname].ansible_hostname}} in myconf"
Still both host add the same block
You're doing all right. The code should work. To test the strings are equal, usually "==" is used instead of the inclusion "in". But, you can reference the configuration data in a dictionary and simplify the code, e.g.
vars:
my_conf:
host01: "{{ conf }}"
host02: "{{ conf_stdby }}"
task:
- blockinfile:
path: /home/conf
marker: "#-- {mark} Adding Values --"
block: |
{{ my_conf[inventory_hostname] }}
when: "inventory_hostname in my_conf"
Q: "In the inventory, there is no hostname but ip."
A: Whatever you have got in the inventory put it into the dictionary. There are no restrictions on keys in the YAML dictionaries, e.g. the inventory
shell> cat hosts
10.1.0.61
10.1.0.62
and the playbook
shell> cat playbook.yml
- hosts: all
gather_facts: false
vars:
my_conf:
10.1.0.61: conf
10.1.0.62: conf_sdtdby
tasks:
- debug:
msg: "{{ my_conf[inventory_hostname] }}"
gives
ok: [10.1.0.61] =>
msg: conf
ok: [10.1.0.62] =>
msg: conf_sdtdby

Ansible to consolidate service status on multiple servers

Below playbook works where inventory groups has multiple servers and to check the status of each service and display its status on console.
Problem is, I wanted to consolidate the status of each service on every server and send in an email, but it seems array (uistatus_app, uistatus_status, uistatus_print) gets re-initiated for each server run.
Is there any way where I can store the status in the global variable of each run and display?
for example:
server1: nginx is active
server1: PHP is active
server2: nginx is active
server2: PHP is unknown
Thanks.
- name: checking services
shell: 'systemctl is-active {{item}}'
with_items:
- nginx
- php74-php-fpm
failed_when: false
register: uistatus
when: 'inventory_hostname in groups[''stg-ui]'
- name: get ui status in arrray
set_fact:
uistatus_app: '{{uistatus_app}}+[ ''{{item.item}}'']'
uistatus_status: '{{uistatus_app}}+[ ''{{item.stdout}}'']'
with_items: '{{ uistatus.results }}'
when: 'inventory_hostname in groups[''stg-ui]'
- name: consolidate
set_fact:
uistatus_print: '{{uistatus_print}}+{{inventory_hostname}}: {{item[0]}} is item[1]}}]'
loop: '{{ query(''together'',uistatus_app, uistatus.status }}'
when: 'inventory_hostname in groups[''stg-ui]'
Set the variable with the list of the statuses in each host, e.g.
- hosts: all
tasks:
- name: checking services
command: "echo {{ item }} is active"
loop:
- nginx
- php74-php-fpm
failed_when: False
register: uistatus
when: inventory_hostname in groups.stg_ui
- set_fact:
my_services: "{{ uistatus.results|default([])|
map(attribute='stdout')|
list }}"
- debug:
var: my_services
gives
ok: [server1] =>
my_services:
- nginx is active
- php74-php-fpm is active
ok: [server2] =>
my_services:
- nginx is active
- php74-php-fpm is active
Then run_once, extract the lists and create the dictionary all_services, e.g.
- set_fact:
all_services: "{{ dict(ansible_play_hosts|zip(stats)) }}"
vars:
stats: "{{ ansible_play_hosts|
map('extract', hostvars, 'my_services')|
list }}"
run_once: true
- debug:
var: all_services
run_once: true
gives
ok: [server1] =>
all_services:
server1:
- nginx is active
- php74-php-fpm is active
server2:
- nginx is active
- php74-php-fpm is active
The dictionary is available to all hosts in the playbook.

Ansible search sublists for value

A webhook triggers an AWX job and I want to run the deployment on a certain host depending on the service, since they run on different servers. I need to know which server uses that service to set is as a var so it can be used as a host in the following play.
My variable inside vars.yaml looks like this:
staging_hosts:
server1: ['service1', 'service2', 'service3']
server2: ['service4', 'service5', 'service6']
server3: ['service7', 'service8', 'service9']
Playbook:
- name: write deployment hosts
hosts: localhost
vars:
deployment_hosts: absent
vars_files:
- ./group_vars/vars.yaml
tasks:
- set_fact:
modified_repos: (small regex filter to find modified repository)
- set_fact:
deployment_hosts: "{{ item }}"
when: '{{ modified_repos }} in {{ item }}'
with_list:
- "{{ staging_hosts }}"
- name: connect to Cluster
hosts: "{{ hostvars['localhost']['deployment_hosts'] }}"
What can I do against this warning and error?
[WARNING]: conditional statements should not include jinja2 templating
delimiters such as {{ }} or {% %}. Found: {{ modified_repos }} in {{ item }}
fatal: [localhost]: FAILED! => {"msg": "The conditional check '{{ modified_repos }} in {{ item }}' failed. True {% else %} False {% endif %}): unhashable type: 'list'
Oh I forgot to mention. It is important, that deployment_hosts could also contain two hosts if modified repos include for example service1 and service4.
Q: "deployment_hosts could also contain two hosts if modified repos include for example service1 and service4."
A: Use intersect filter. For example, the playbook
- hosts: localhost
vars:
staging_hosts:
server1: ['service1', 'service2', 'service3']
server2: ['service4', 'service5', 'service6']
server3: ['service7', 'service8', 'service9']
modified_repos: ['service1', 'service4']
tasks:
- set_fact:
deployment_hosts: "{{ deployment_hosts|default([]) + [item.key] }}"
loop: "{{ staging_hosts|dict2items }}"
when: modified_repos|intersect(item.value)|length > 0
- debug:
var: deployment_hosts
gives
deployment_hosts:
- server1
- server2

How do I test in a var matches against a list of substrings in ansible?

I am fairly new to ansible and I am trying to determine how to test is a variable passed to my playbook matches against a list of substrings.
I have tried something like the following. Looping through my list of badcmds and then testing whether it is in the variable passed.
vars:
badcmds:
- clear
- no
tasks:
- name: validate input
debug:
msg: " {{ item }}"
when: item in my_command
with_items: "{{ badcmds }}"
I am getting the following error:
"msg": "The conditional check 'item in my_command' failed.
The error was: Unexpected templating type error occurred on
({% if item in my_command %} True {% else %} False {% endif %}):
coercing to Unicode: need string or buffer, bool found
Many thanks.
one problem with your playbook is that - no is automatically translated to boolean false. you should use "no" to make Ansible consider the variable as a string. without quotes:
---
- hosts: localhost
connection: local
gather_facts: false
vars:
badcmds:
- clear
- no
my_command: clear
tasks:
- name: print variable
debug:
msg: "{{ item }}"
with_items:
- "{{ badcmds }}"
output:
TASK [print variable] ***********************************************************************************************************************************************************************************************
ok: [localhost] => (item=None) => {
"msg": "clear"
}
ok: [localhost] => (item=None) => {
"msg": false
}
I guess you should enclose no in quotes, because this behavior was not your intention.
to make a loop and check if the variable matches any item from the badcmds list, you can use:
---
- hosts: localhost
connection: local
gather_facts: false
vars:
badcmds:
- "clear"
- "no"
tasks:
- name: validate input
debug:
msg: "{{ item }}"
when: item == my_command
with_items:
- "{{ badcmds }}"
hope it helps

iteration using with_items and register

Looking for help with a problem I've been struggling with for a few hours. I want to iterate over a list, run a command, register the output for each command and then iterate with debug over each unique registers {{ someregister }}.stdout
For example, the following code will spit out "msg": "1" and "msg": "2"
---
- hosts: localhost
gather_facts: false
vars:
numbers:
- name: "first"
int: "1"
- name: "second"
int: "2"
tasks:
- name: Register output
command: "/bin/echo {{ item.int }}"
register: result
with_items: "{{ numbers }}"
- debug: msg={{ item.stdout }}
with_items: "{{ result.results }}"
If however, I try and capture the output of a command in a register variable that is named using with_list, I am having trouble accessing the list or the elements within it. For example, altering the code slightly to:
---
- hosts: localhost
gather_facts: false
vars:
numbers:
- name: "first"
int: "1"
- name: "second"
int: "2"
tasks:
- name: Register output
command: "/bin/echo {{ item.int }}"
register: "{{ item.name }}"
with_items: "{{ numbers }}"
- debug: var={{ item.name.stdout }}
with_items: "{{ numbers }}"
Gives me:
TASK [debug]
> ******************************************************************* fatal: [localhost]: FAILED! => {"failed": true, "msg": "'unicode
> object' has no attribute 'stdout'"}
Is it not possible to dynamically name the register the output of a command which can then be called later on in the play? I would like each iteration of the command and its subsequent register name to be accessed uniquely, e.g, given the last example I would expect there to be variables registered called "first" and "second" but there aren't.
Taking away the with_items from the debug stanza, and just explicitly defining the var or message using first.stdout returns "undefined".
Ansible version is 2.0.2.0 on Centos 7_2.
Thanks in advance.
OK so I found a post on stackoverflow that helped me better understand what is going on here and how to access the elements in result.results.
The resultant code I ended up with was:
---
- hosts: localhost
gather_facts: false
vars:
numbers:
- name: "first"
int: "1"
- name: "second"
int: "2"
tasks:
- name: Register output
command: "/bin/echo {{ item.int }}"
register: echo_out
with_items: "{{ numbers }}"
- debug: msg="item.item={{item.item.name}}, item.stdout={{item.stdout}}"
with_items: "{{ echo_out.results }}"
Which gave me the desired result:
"msg": "item.item=first, item.stdout=1"
"msg": "item.item=second, item.stdout=2"
I am not sure if I understand the question correctly, but maybe this can help:
- debug: msg="{{ item.stdout }}"
with_items: echo_out.results
Please note that Ansible will print each item and the msg both - so you need to look carefully for a line that looks like "msg": "2".

Resources