How to use condition while selecting hosts in Ansible - ansible

Below is my playlist. My doubt is, How do I use the IP which get from {{ hostvars['localhost']['srv'] }} and {{ hostvars['localhost']['srv1'] }
to the hosts with condition.
I wish to use like if telnet to server is successful then in next hosts, it should use {{ hostvars['localhost']['srv'] }} and if server1 telnet failed then hosts should get {{ hostvars['localhost']['srv1'] } and so on.
I tried to use OR in hosts but it is not working.
Is there any way to get this working?
- hosts: localhost
tasks:
- name: Telnet to server1
shell: 'telnet 10.2.0.150 8080'
register: pass
ignore_errors: yes
- set_fact:
ip={{pass.cmd| regex_findall('[0-9./]+') | list}}
register: ip_result
- set_fact:
srv={{ip_result.ansible_facts.ip[0]}}
register: srv
- debug: msg={{srv}}
- name: Telnet to server2
shell: "telnet 10.2.0.187 8080"
register: pass1
ignore_errors: yes
when: "'Connection refused' in pass.stderr or 'Connection timed out' in pass.stderr"
- set_fact:
ip1={{pass1.cmd| regex_findall('[0-9./]+') | list}}
register: ip1_result
when: "'Connection refused' in pass.stderr or 'Connection timed out' in pass.stderr"
- set_fact:
srv1={{ip1_result.ansible_facts.ip[0]}}
register: srv1
when: "'Connection refused' in pass.stderr or 'Connection timed out' in pass.stderr"
- debug: msg={{srv1}}
when: "'Connection refused' in pass.stderr or 'Connection timed out' in pass.stderr"
- hosts: "{{ hostvars['localhost']['srv'] }}" or "{{ hostvars['localhost']['srv1'] }}"
tasks:

I solve it by using jinja query.
- hosts: "{{ hostvars['localhost']['srv'] if 'Connection closed' in hostvars['localhost']['pass']['stderr'] else hostvars['localhost']['srv1']}}"
Tested it by stopping first server, then it took second servers and executed task to that server.

You can't change an inventory on-fly (you can't add or remove hosts from playbooks). But if all your hosts already are in an inventory, you can use group_by module to create groups inside plays.
https://docs.ansible.com/ansible/2.6/modules/group_by_module.html

Related

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.

How do I access values of Ansible Facts when using a loop?

This works when I don't use a loop:
---
- hosts: localhost
tasks:
- name: Get a cert from gRPC port
get_certificate:
host: "labrouter.abc.com"
port: 50051
delegate_to: localhost
run_once: true
ignore_errors: yes
register: cert
- name: Get Cert Data
debug:
msg: "Not_After: {{ cert.not_after }}"
Output:
TASK [Get Cert Data] ****************************************
ok: [localhost] => {
"msg": "Not After: 20221210143235Z"
}
However, when I try to loop through my hosts like this:
---
- hosts: localhost
tasks:
- name: Get a cert from gRPC port
get_certificate:
host: "{{ item }}"
port: 50051
delegate_to: localhost
run_once: true
ignore_errors: yes
register: cert
loop: "{{ groups['lab'] }}"
- name: Get Cert Data
debug:
msg: "Not After: {{ cert.not_after }}"
I get this error:
The error was: 'dict object' has no attribute 'not_after'
My inventory file contains a single group called "lab" with one device "labrouter.abc.com" associated to it.
I want to be able to add more devices to the "lab" group and run this playbook so I can get the "not_after" value for each device. I can't figure out how to access the "not_after" value for each item in the loop.
Quoting the documentation:
When you use register with a loop, the data structure placed in the variable will contain a results attribute that is a list of all responses from the module. This differs from the data structure returned when using register without a loop
The easiest way to fix your actual problem is to loop over the results:
- name: Get Cert Data
debug:
msg: "Not After: {{ item.not_after }}"
loop: "{{ cert.results }}"
Although this is a direct answer to your question, if the final goal is to take actions on each target in the lab group, see #Jack's answer and drop your loop.
Stop looping, and have Ansible do its implicit looping over the hosts:
---
- hosts: lab
tasks:
- name: Get a cert from gRPC port
get_certificate:
host: "{{ ansible_host }}"
port: 50051
delegate_to: localhost
ignore_errors: yes
register: cert
- name: Get Cert Data
debug:
msg: "Not After: {{ cert.not_after }}"
(As a general "best practices" thing, if you are looping over hosts, there's probably a better way to do it by setting the hosts value for the play to those hosts, and letting Ansible do its implicit looping.)

saving variables from playbook run to ansible host local file

I'm sort of trying to build an inventory file from an ansible playbook run.
I'm trying to list out all the kvm hosts and the guests running on them, by running both service libvirtd status and if successful, virsh list --all, and to store the values in a file on the ansible host.
Ive tried a few different playbook structures but none have been successful in writing the file (using local_action wrote the ansible_hostname from just one host).
Please can someone guide me on what I'm doing wrong?
This is what I'm running:
- name: Determine KVM hosts
hosts: all
become: yes
#gather_facts: false
tasks:
- name: Check if libvirtd service exists
shell: "service libvirtd status"
register: libvirtd_status
failed_when: not(libvirtd_status.rc == 0)
ignore_errors: true
- name: List KVM guests
shell: "virsh list --all"
register: list_vms
when: libvirtd_status.rc == 0
ignore_errors: true
- name: Write hostname to file
lineinfile:
path: /tmp/libvirtd_hosts
line: "{{ ansible_hostname }} kvm guests: "
create: true
#local_action: copy content="{{ item.value }}" dest="/tmp/libvirtd_hosts"
with_items:
- variable: ansible_hostname
value: "{{ ansible_hostname }}"
- variable: list_vms
value: "{{ list_vms }}"
when: libvirtd_status.rc == 0 or list_vms.rc == 0
Was able to cobble something that's mostly working:
- name: Check if libvirtd service exists
shell: "service libvirtd status"
register: libvirtd_status
failed_when: libvirtd_status.rc not in [0, 1]
- name: List KVM guests
#shell: "virsh list --all"
virt:
command: list_vms
register: all_vms
when: libvirtd_status.rc == 0
---
- name: List all KVM hosts
hosts: production, admin_hosts, kvm_hosts
become: yes
tasks:
- name: create file
file:
dest: /tmp/libvirtd_hosts
state: touch
delegate_to: localhost
- name: Copy VMs list
include_tasks: run_libvirtd_commands.yaml
- name: saving cumulative result
lineinfile:
line: '{{ ansible_hostname }} has {{ all_vms }}'
dest: /tmp/libvirtd_hosts
insertafter: EOF
delegate_to: localhost
when: groups["list_vms"] is defined and (groups["list_vms"] | length > 0)
Now if only I could clean up the output to filter out false positives (machines that don't have libvirtd status, and have an empty/no list of VMs, because the above doesn't really work.
But at least there is output from all the KVM hosts!

how to run multiple items using variables

I am trying to call port. But it is taking as array as two values instead of one.
Example:
cmd: nc -z msgq0003.svc.ops.wd5.wd ['2181', '5674']
Instead of:
nc -z servername 2181
This is my ansible palybook:
become: true
gather_facts: false
name: "Check all port numbers are accessible from current host"
hosts: ops-mgmt-aod,ops-mgmt-ots
vars:
server: "{{ groups['mgmt-server'] }}"
port: "{{ groups['mgmt-port'] }}"
tasks:
- name: network connection for zookeeper
shell: "nc -z {{ item }} {{ port }}"
with_items:
- "{{ server }}"
register: network_reachable_zoo
- debug: msg={{network_reachable_zoo}}

Trying to get IPs from file and use it as Inventory in Ansible

I get list of IP address in test.text file from which I am trying to get the IP in loop
and then try to get in group or variable and use it as hosts (dynamic_groups)
Below is my playlist
---
- name: provision stack
hosts: localhost
connection: local
gather_facts: no
serial: 1
tasks:
- name: Get Instance IP Addresses From File
shell: cat /home/user/test.text
register: serverlist
- debug: msg={{ serverlist.stdout_lines }}
- name: Add Instance IP Addresses to temporary inventory groups
add_host:
groups: dynamic_groups
hostname: "{{item}}"
with_items: serverlist.stdout_lines
- hosts: dynamic_groups
become: yes
become_user: root
become_method: sudo
gather_facts: True
serial: 1
vars:
ansible_connection: "{{ connection_type }}"
ansible_ssh_user: "{{ ssh_user_name }}"
ansible_ssh_private_key_file: "{{ ssh_private_key_file }}"
tasks:
.....
.....
After running above playbbok I am getting below error
TASK [debug] *****************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"192.168.1.10",
"192.168.1.11",
"192.168.1.50"
]
}
TASK [Add Instance IP Addresses to temporary inventory groups] ***************************************************************************************************************************************************************************
changed: [localhost] => (item=serverlist.stdout_lines)
PLAY [dynamic_groups] *********************************************************************************************************************************************************************************************************************
TASK [Some Command] **********************************************************************************************************************************************************************************************************************
fatal: [serverlist.stdout_lines]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname serverlist.stdout_lines: Name or service not known", "unreachable": true}
What Am I missing here?
Below is correct way to use variable
- name: Add Instance IP Addresses to temporary inventory groups
add_host:
groups: working_hosts
hostname: "{{item}}"
with_items: "{{ serverlist.stdout_lines }}"
It should solve your problem.
As reported in fatal error message "Failed to connect to the host via ssh: ssh: Could not resolve hostname serverlist.stdout_lines", it is trying to connect to "serverlist.stdout_lines", not to a valid IP.
This is caused by an error when passing variable to with_items. In your task:
with_items: serverlist.stdout_lines
it is passing serverlist.stdout_lines string and not its value.
With_items requires variable definition using "{{ ... }}" (https://docs.ansible.com/ansible/2.7/user_guide/playbooks_loops.html#with-items).
This is the correct way for your task:
- name: Add Instance IP Addresses to temporary inventory groups
add_host:
groups: dynamic_groups
hostname: "{{item}}"
with_items: "{{ serverlist.stdout_lines }}"
You can simply use ansible-playbook -i inventory_file_name playbook.yaml for this. inventory_file is the file containing your groups and ips.

Resources