i have an inventory file containing 200 servers and thier respective variables as shown in a sample below:
[myhost1.mrsh.com]
myhost1.mrsh.com ORACLE_HOME=/u/orahome12/middleware/12c_db1 ansible_user=wladmin
[myhost2.mrsh.com]
myhost2.mrsh.com ORACLE_HOME=/u/orahome12/middleware/12c_db1 ansible_user=wladmin
..........
........
i ask the user to enter any hostname which is passed to hostnames variable as below:
ansible-playbook /web/playbooks/automation/applycpupatch/applycpupatch.yml -i /web/playbooks/automation/applycpupatch/applycpupatch.hosts -f 5 -e action=status -e hostnames='myhost1
myhost2' -e patch_file='p33286132_122130_Generic.zip'
if myhost1 is present in the applycpupatch.hosts file i then wish to create a dynamic inventory using add_host having only myhost1 and its variables like ORACLE_HOME
Below is my code:
- name: "Play 1 - Set Destination details"
hosts: all
tasks:
- add_host:
name: "{{ item | upper }}"
groups: dest_nodes
ansible_user: "{{ hostvars[item + '*'].ansible_user }}"
ORACLE_HOME: "{{ hostvars[item + '*'].ORACLE_HOME }}"
when: inventory_hostname | regex_search(item)"
with_items: "{{ hostnames.split() }}"
Unfortunately, i get the error as below:
TASK [add_host] ****************************************************************
Saturday 20 November 2021 19:05:38 -0600 (0:00:00.059) 0:00:23.532 *****
[0;31mfatal: [myhost222.mrsh.com]: FAILED! => {"msg": "The conditional check 'inventory_hostname | regex_search(item)\"' failed. The error was: template error while templating string: unexpected char '\"' at 45. String: {% if inventory_hostname | regex_search(item)\" %} True {% else %} False {% endif %}\n\nThe error appears to be in '/web/playbooks/automation/applycpupatch/applycpupatch.yml': line 36, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - add_host:\n ^ here\n"}[0m
I also tried the below but it fails with the error.
ORACLE_HOME: "{{ hostvars['all'][item + '*'].ORACLE_HOME }}"
Thus my dynamic inventory constructed runtime dest_nodes in this example should have ONLY the below.
myhost1.mrsh.com ORACLE_HOME=/u/orahome12/middleware/12c_db1 ansible_user=wladmin
myhost2.mrsh.com ORACLE_HOME=/u/orahome12/middleware/12c_db1 ansible_user=wladmin
i dont understand very well what do you want, but you have lot of errors to fix in your playbook:
1- launch your playbook with -e hostnames='myhost1,myhost2'
2- fix your playbook: you have to test the result of your regex_search, use the variable inventory_hostname and use split(','):
a sample
- name: "Play 1 - Set Destination details"
hosts: all
tasks:
- debug:
msg: "{{ item }} - {{ hostvars[inventory_hostname].ORACLE_HOME }}"
when: (inventory_hostname | regex_search(item)) != ''
with_items: "{{ hostnames.split(',') }}"
Related
An answer on StackOverflow suggests using - debug: var=vars or - debug: var=hostvars to print out all the variables used by an Ansible playbook.
Using var=hostvars did not print out all of the variables. But I did get all of the variables printed out when I added the following lines to the top of the main.yml file of the role executed by my playbook:
- name: print all variables
debug:
var=vars
The problem is that the values of the variables printed out are not fully evaluated if they are dependent on the values of other variables. For example, here is a portion of what gets printed out:
"env": "dev",
"rpm_repo": "project-subproject-rpm-{{env}}",
"index_prefix": "project{{ ('') if (env=='prod') else ('_' + env) }}",
"our_server": "{{ ('0.0.0.0') if (env=='dev') else ('192.168.100.200:9997') }}",
How can I get Ansible to print out the variables fully evaluated like this?
"env": "dev",
"rpm_repo": "project-subproject-rpm-dev",
"index_prefix": "project_dev",
"our_server": "0.0.0.0",
EDIT:
After incorporating the tasks section in the answer into my playbook file and removing the roles section, my playbook file looks like the following (where install-vars.yml contains some variable definitions):
- hosts: all
become: true
vars_files:
- install-vars.yml
tasks:
- debug:
msg: |-
{% for k in _my_vars %}
{{ k }}: {{ lookup('vars', k) }}
{% endfor %}
vars:
_special_vars:
- ansible_dependent_role_names
- ansible_play_batch
- ansible_play_hosts
- ansible_play_hosts_all
- ansible_play_name
- ansible_play_role_names
- ansible_role_names
- environment
- hostvars
- play_hosts
- role_names
_hostvars: "{{ hostvars[inventory_hostname].keys() }}"
_my_vars: "{{ vars.keys()|
difference(_hostvars)|
difference(_special_vars)|
reject('match', '^_.*$')|
list|
sort }}"
When I try to run the playbook, I get this failure:
shell> ansible-playbook playbook.yml
SSH password:
SUDO password[defaults to SSH password]:
PLAY [all] *********************************************************************
TASK [setup] *******************************************************************
ok: [192.168.100.111]
TASK [debug] *******************************************************************
fatal: [192.168.100.111]: FAILED! => {"failed": true, "msg": "lookup plugin (vars) not found"}
to retry, use: --limit #/usr/local/project-directory/installer-1.0.0.0/playbook.retry
PLAY RECAP *********************************************************************
192.168.100.111 : ok=1 changed=0 unreachable=0 failed=1
The minimal playbook below
shell> cat pb.yml
- hosts: localhost
gather_facts: false
vars:
test_var1: A
test_var2: "{{ test_var1 }}"
tasks:
- debug:
var: vars
reproduces the problem you described. For example,
shell> ansible-playbook pb.yml | grep test_var
test_var1: A
test_var2: '{{ test_var1 }}'
Q: How can I print out the actual values of all the variables used by an Ansible playbook?
A: You can get the actual values of the variables when you evaluate them. For example,
shell> cat pb.yml
- hosts: localhost
gather_facts: false
vars:
test_var1: A
test_var2: "{{ test_var1 }}"
tasks:
- debug:
msg: |-
{% for k in _my_vars %}
{{ k }}: {{ lookup('vars', k) }}
{% endfor %}
vars:
_special_vars:
- ansible_dependent_role_names
- ansible_play_batch
- ansible_play_hosts
- ansible_play_hosts_all
- ansible_play_name
- ansible_play_role_names
- ansible_role_names
- environment
- hostvars
- play_hosts
- role_names
_hostvars: "{{ hostvars[inventory_hostname].keys() }}"
_my_vars: "{{ vars.keys()|
difference(_hostvars)|
difference(_special_vars)|
reject('match', '^_.*$')|
list|
sort }}"
gives the evaluated playbook's vars
msg: |-
test_var1: A
test_var2: A
Looking for an answer to the same question, I found the following solution from this link:
- name: Display all variables/facts known for a host
debug:
var: hostvars[inventory_hostname]
tags: debug_info
I'm just a beginner and write some small playbook.
For now I get some little error.
- name: Facts
ios_command:
commands: show interface status
register: ios_comm_result
In this playbook, for beginner I get the list of interfaces and then in next task I get list with first position including column name.
- name: get a list of ports to modify
set_fact:
newlist: "{{ (newlist | default([])) + [item.split()[0]] }}"
with_items: "{{ ios_comm_result.stdout_lines }}"
Output:
'Port'
'fa0/1'
'gi0/2'
....
etc
How can I delete first line? Or where can I read about this?
Based on your requirement and the given comments about Accessing list elements with Slice notation ([1:]) and Jinja Filters (| reject()) I've setup an short test
---
- hosts: localhost
become: false
gather_facts: false
vars:
STDOUT_LINES: ['Port', 'fa0/1', 'gi0/2', 'etc/3']
tasks:
# Slice notation
- name: Show 'stdout_lines' from the 2nd line to the end
debug:
msg: "{{ item }}"
loop: "{{ STDOUT_LINES[1:] }}"
# Jinja2 filter
- name: Show 'stdout_lines' and drop any lines which match
debug:
msg: "{{ item }}"
loop: "{{ STDOUT_LINES | reject('match', '^Port') | list }}"
# Conditionals
- name: Show 'stdout_lines' but not the first one
debug:
msg: "{{ item }}"
when: ansible_loop.index != 1
loop: "{{ STDOUT_LINES }}"
loop_control:
extended: true
label: "{{ ansible_loop.index0 }}"
with some value added about Extended loop variables and using conditionals in loops.
All three options show the expected result.
My tasks:
- name: Init a new swarm with default parameters
docker_swarm:
state: present
advertise_addr: "{{ manager_ip }}:2377"
register: rezult
when: "ansible_default_ipv4.address == '{{ manager_ip }}'"
- debug:
msg: '{{ rezult.swarm_facts.JoinTokens.Worker }}'
when: "ansible_default_ipv4.address == '{{ manager_ip }}'"
Now this works fine, but if I want to run it on another server (!=) with:
- debug:
msg: '{{ rezult.swarm_facts.JoinTokens.Worker }}'
when: "ansible_default_ipv4.address != '{{ manager_ip }}'"
I get:
FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'swarm_facts'\n\nThe error appears to be in '/home/ansible/docker-ansible/docker.yml': line 42, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - debug:\n ^ here\n"}
I do not get it. It is like ansible forgets the value of registered variable called "rezult" when running on another server even though I registered it and it should have a fixed value.
Yes, variables are always host specific, including ones declared by set_fact:; if you want that variable to appear on all your hosts, you'll have to have a special "propagation" action that reaches "over" to the one host that does have the value, and assign it to all the hosts:
- set_fact:
hello: I am a manager
when: ansible_default_ipv4.address == '{{ manager_ip }}'
- set_fact:
hello: '{{ hostvars[manager_host].hello }}'
when: hello is not defined
vars:
# you may have access to some less convoluted logic than this
# to know which inventory_hostname ran that "set_fact:"
manager_host: '{{ hostvars | dict2items
| selectattr("value.ansible_default_ipv4.address", "eq", manager_ip)
| map(attribute="key") | first }}'
I have included the below variable file in my playbook:
more vars/was_vars.yaml
::::::::::::::
10.9.12.112: "/was/IBM/WebSphere"
10.8.10.28: "/was/IBM/profiles"
10.7.120.129: "/app/tmp"
Here is my playbook:
- name: Configure nodes
hosts: dest_nodes
user: "{{ USER }}"
tasks:
- name: Construct File Path on "{{ inventory_hostname }}".
command: "touch {{ BASEPATH }}/{{ ( item | splitext)[1] }}/del.tmp"
when: "{{ Layer == 'APP' }}"
file: path="{{ "{{ inventory_hostname }}" }}/{{ App_List }}/{{ Rel_Path }}/del.tmp state=directory mode=u=rwx,g=rw,o=r"
when: "{{ Layer == 'WAS' }}"
"{{ inventory_hostname }}" gets substituted with "10.9.12.112" which should then get further substituted to "/was/IBM/WebSphere" as stated in the included vars("was_vars.yaml") file.
I'm getting the below Syntax error with my current code:
ERROR! conflicting action statements: command, file
The error appears to be in '/app/Ansible/deploy.yml': line 133, column 4, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
tasks:
- name: Construct File Path on "{{ inventory_hostname }}".
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
I'm on the latest version on ansible.
Can you please suggest?
1) The dash is missing in front of the file module.
2) Nested expansion is not possible (wrong: "{{ "{{ inventory_hostname }}" }}").
3) when: condition is expanded by default.
4) The whole string shall be quoted when the expansion of a variable is included.
5) The index of the expansion {{ ( item | splitext)[1] }} wont work.
6) It's not clear where do the variable item and the filter splitext come from.
The correct syntax might be
tasks:
- name: "Construct File Path on {{ inventory_hostname }}."
command: "touch {{ BASEPATH }}/{{ item|splitext|first }}/del.tmp"
when: Layer == 'APP'
- file:
path: "{{ inventory_hostname }}/{{ App_List }}/{{ Rel_Path }}/del.tmp"
state: directory
mode: "u=rwx,g=rw,o=r"
when: Layer == 'WAS'
(not tested)
Thank you for the inputs but what we are actually looking for is how to get the expansion to work...
I.e the inventory_hostname would be substituted with host ip say 10.9.12.112 ... But how do we code to get "/was/IBM/WebSphere" substituted matching the IP from the included variable file?
Can you try as :
"{{vars[inventory_hostname]}}"
or if you want one more level use as :
"{{vars[vars[inventory_hostname]]}}"
I am using Ansible to read in a YAML file containing a list of dictionaries. I then need to iterate through this list and call a different role depending on data within each list object. I have a solution that is working, but it seems so kludgy to me, that I wanted to find out if there was a better way.
I have defined a YAML file structure to provide the input to my playbook and I read that file into a variable with the include_vars module. I then use with_items to loop through the array one time for each role that I need to support (currently 6, but it may increase) and use a when clause to only include_role when the data in the object is correct for that role.
Code
Sample Input file:
---
objects:
- type: type1
name: obj_type1
- type: type2
name: obj_type2
- type: type3
name: obj_type3
Sample Playbook:
---
- hosts: cf-host
gather_facts: False
vars:
objects_file: ''
tasks:
- name: Read objects_file
include_vars:
file: "{{ objects_file }}"
- name: Handle type1
include_role:
name: type1_role
vars:
- type1_role_name: "{{ item.name }}"
with_items: "{{ objects }}"
when: item.type == "type1"
- name: Handle type2
include_role:
name: type2_role
vars:
- type2_role_name: "{{ item.name }}"
with_items: "{{ objects }}"
when: item.type == "type2"
- name: Handle type3
include_role:
name: type3_role
vars:
- type3_role_name: "{{ item.name }}"
with_items: "{{ objects }}"
when: item.type == "type3"
Sample Role:
---
- name: Print name
debug:
msg: "Type 1 - Name is {{ type1_role_name }}"
The other roles are the same, but have msg set to "Type X - Name is {{ typeX_role_name }}" instead.
This leads to a skipped task for every item in the list when it does not match the type corresponding to the role. It also means that I have to loop through the same list multiple times (as many as types that I need to support). As mentioned, I already need to support 6 different types and that number may grow.
This solution seems like it would scale very poorly and would have terrible performance as the list and supported types become larger and larger. Is there a better way to do what I need to do?
Better Solution
Using this answer: https://stackoverflow.com/a/53859851/9744341 as a base, I was able to come up with this as a better solution that I am happy with. Here it is in case anyone else has the same needs:
Sample Playbook:
---
- hosts: cf-host
gather_facts: False
vars:
objects_file: ''
role_name_lookup:
type1:
role_name: type1_role
tasks_from: type1_tasks
type2:
role_name: type2_role
tasks_from: type2_tasks
type3:
role_name: type3_role
tasks_from: main
tasks:
- name: Read objects_file
include_vars:
file: "{{ objects_file }}"
- name: Call roles to create infra
include_role:
name: "{{ role_name_lookup[item.type].role_name }}"
tasks_from: "{{ role_name_lookup[item.type].tasks_from }}"
vars:
inputs: "{{ item }}"
with_items: "{{ objects }}"
Sample Role:
---
- name: Print name
debug:
msg: "Type 1 - Name is {{ inputs.name }}"
The other roles are the same, but have msg set to Type X - Name is {{ inputs.name }} instead.
Would this code fit your purpose?
- name: Include roles
include_role:
name: "{{ item.type }}_role"
with_items: "{{ objects }}"
- name: Print name
debug:
msg: "{{ item.type }} - Name is {{ role_name }}"