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
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
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.
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
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
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".