Ansible multiple variable contains multiple values - bash

i have some ansible task below :
- name: counting object from json
shell: >
jq '.results[].stdout_lines | length' backup/{{ inventory_hostname }}_rsl.json | wc -l
register: jsondata
- name: counting object converting
shell: >
seq 0 {{ jsondata.stdout|int - 1 }} | tr '\n' ' ' | xargs | sed 's/[[:space:]]/,/g'
register: seq
- name: get interface
shell: >
cat backup/{{ inventory_hostname }}_{{ item }}_rsl_result_nows.json | cut -d, -f1
register: interface
with_items:
- "{{ seq.stdout.split(',') | list }}"
- name: get rsl value
shell: >
cat backup/{{ inventory_hostname }}_{{ item }}_rsl_result_nows.json | cut -d, -f2-
register: rslvalue
with_items:
- "{{ seq.stdout.split(',') | list }}"
- name: post to DB via curl
shell: >
curl -d "ip_address={{ inventory_hostname }}&hostname={{ varhostname.stdout }}&interface={{ interface }}&rslvalue={{ rslvalue }}" -X POST http://dev.trm.net:8088/ip_planning/rsl/postrsl -v
i want to post data with the same hostname but different interface and some other attributes.
This my curl command that i want :
curl -d "ip_address=IP_A&hostname=HOST_A&interface=IFACE_1&rslvalue=1,2,3,4,5" -X POST http://dev.trm.net:8088/ip_planning/rsl/postrsl -v
curl -d "ip_address=IP_A&hostname=HOST_A&interface=IFACE_2&rslvalue=5,4,3,2,1" -X POST http://dev.trm.net:8088/ip_planning/rsl/postrsl -v
curl -d "ip_address=IP_B&hostname=HOST_B&interface=IFACE_1&rslvalue=11,21,31,41,51" -X POST http://dev.trm.net:8088/ip_planning/rsl/postrsl -v
i'm tired a full-day, please help me. i've tried from my ansible script above but error
"msg": "The task includes an option with an undefined variable. The error was: 'item' is undefined

You have {{ item.interface.stdout }} in the last task, but with_items is not present.
Also when you use item in the shell command, use as "{{item}}"

a little improvement make me small fun...
- name: post to DB via curl
shell: >
curl -d "ip_address={{ inventory_hostname }}&hostname={{ varhostname.stdout }}&interface={{ item }}&rslvalue={{ item }}" -X POST http://dev.tr$
with_items:
- "{{ rslvalue.results | map(attribute='stdout') | list }}"
- "{{ interface.results | map(attribute='stdout') | list }}"
but "item" variable always print "rslvalue" result. how to declare "item" with multiple with_items ??

Related

Comparing the Mounts in ansible

I have a requirement in ansible to compare the mount counts before and after the maintenance, for this i am using the below tasks. We need to compare the premounts and postmounts and if there is difference in the value, it should throw a message to check the host. Please advise.
- name: Take premount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: premounts
- name: Take postmount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: postmounts
I think you are looking for the assert module. You can use this to print a message or fail as per condition.
Example:
- name: Take premount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: premounts
- name: Take postmount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: postmounts
- assert:
that:
- premounts == postmounts
fail_msg: "Premounts: {{ premounts }} and postmounts: {{ postmounts }} do not match!"
success_msg: "Mounts match."
Instead of running the df command and getting number of mounts, you can use the Ansible fact ansible_mounts to get this count also.
Example:
- set_fact:
premounts: "{{ ansible_mounts|count }}"
# some tasks in between
# Gather facts again
- setup:
- set_fact:
postmounts: "{{ ansible_mounts|count }}"
- assert:
that:
- premounts == postmounts
fail_msg: "Premounts: {{ premounts }} and postmounts: {{ postmounts }} do not match!"
success_msg: "Mounts match."
Try as below:
- name: Take premount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: premounts
- name: Take postmount counts
shell: df -h |awk ' NR>1{print $1}'|grep -v "tmpfs"|wc -l
register: postmounts
- debug:
msg: 'There are difference in the mount count, check it'
when: premounts.stdout_lines[0] | int != postmounts.stdout_lines[0] | int

ansible playbook access first loop variable in second loop

I have list of applications paths in a file APP_DIR, need to loop through the paths and run start command.
- name: start
command: "{{item[1]}}"
with_nested:
- "{{ lookup('file', 'APP_DIR').splitlines() }}"
- [ "chdir={{item[0]}} ./start",
"ps -aef | grep httpd | grep -v grep"]
ERROR: FAILED! => {"msg": "'item' is undefined"}.
Thanks in Advance for help.
You can't call item variable in a nested list, you can use your command with a simple with_item and a multiline like this :
- name: start
command: |
./start
ps -aef | grep httpd | grep -v grep
args:
chdir: "{{ item[0] }}"
with_items: "{{ lookup('file', 'APP_DIR').splitlines() }}"

How to use register in this context? (Ansible)

Thanks for your time reading this (probably dumb question)...
I have the following playbook:
- hosts: all
gather_facts: true
vars:
ansible_python_interpreter: /usr/bin/python
tasks:
- name: Get shard status.
shell:
cmd: |
mongo node1:27020 --eval "sh.status()" | grep shards -A 4 | awk -F ':|,|/' '{ print $2 " ", " ", $5,$7,$9}' | sed -e '1 i Shard Nodes' | column -t
when: ansible_fqdn == 'node1'
register: shards_status
- name: Get replica status from SHARD1.
shell:
cmd: |
mongo --eval 'printjson(rs.status())' | grep -E "stateStr|name" | awk -F ':' '{ print $2 }' | sed 's/"//g'| sed 's/,//g'| xargs -n2 | sed -e '1 i Server Status' | column -t
when: ansible_fqdn == 'node1'
register: shard1
- name: Get replica status from SHARD2.
shell:
cmd: |
mongo --eval 'printjson(rs.status())' | grep -E "stateStr|name" | awk -F ':' '{ print $2 }' | sed 's/"//g'| sed 's/,//g'| xargs -n2 | sed -e '1 i Server Status' | column -t
when: ansible_fqdn == 'node2'
register: shard2
- name: Get replica status from SHARD3.
shell:
cmd: |
mongo --eval 'printjson(rs.status())' | grep -E "stateStr|name" | awk -F ':' '{ print $2 }' | sed 's/"//g'| sed 's/,//g'| xargs -n2 | sed -e '1 i Server Status' | column -t
when: ansible_fqdn == 'node3'
register: shard3
- name: Get replica status from SHARD4.
shell:
cmd: |
mongo --eval 'printjson(rs.status())' | grep -E "stateStr|name" | awk -F ':' '{ print $2 }' | sed 's/"//g'| sed 's/,//g'| xargs -n2 | sed -e '1 i Server Status' | column -t
when: ansible_fqdn == 'node4'
register: shard4
- name: Append logs.
lineinfile:
dest: /tmp/status.txt
line: "{{ item }}"
insertafter: EOF
with_items:
- "{{ shards_status.stdout }}"
- "{{ shard1.stdout }}"
- "{{ shard2.stdout }}"
- "{{ shard3.stdout }}"
- "{{ shard4.stdout }}"
delegate_to: localhost
I want to have in one file (/tmp/status.txt) the result of the above vars (shard1,2,3,4). The issue is, if I run the playbook, I got the following message:
TASK [Append logs.] ****************************************************************************************************************************************************************************************
fatal: [node1]: FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'stdout'"}
fatal: [node2]: FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'stdout'"}
fatal: [node3]: FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'stdout'"}
fatal: [node4]: FAILED! => {"failed": true, "msg": "'dict object' has no attribute 'stdout'"}
Any idea of how I can achieve this?
I have tried accessing into the dic, running only that task from one node and multiple workarounds.. but no luck
I believe this failure is because each time you are guarding your tasks with when: ansible_fqdn == 'node1'.
So when ansible is executing this playbook in node1, its executing all node1 tasks and then when it reaches the Append logs. task it tries to dump the stdout from node2. But since we are running in node1, node2 dictionary is empty, hence 'dict object' has no attribute 'stdout'.
One solution would be to give it a default value, for example
with_items:
- "{{ shards_status.stdout | default("") }}"
- "{{ shard1.stdout | default("") }}"
- "{{ shard2.stdout | default("") }}"
- "{{ shard3.stdout | default("") }}"
- "{{ shard4.stdout | default("") }}"
default value for dictionary in jinja2 (ansible)
But the proper ansible solution would be to change your playbook and way of thinking.
With an inventory like this
[mongo]
shard1 get_shard_status=true
shard2
shard3
shard4
your playbook could look something like this
- hosts: mongo
vars:
get_shard_status: false
tasks:
- name: get shard status
shell: mongo node1:27020 --eval "sh.status()" | grep shards -A 4 | awk -F ':|,|/' '{ print $2 " ", " ", $5,$7,$9}' | sed -e '1 i Shard Nodes' | column -t
register: shard_status
when: get_shard_status
- name: get the replica status
shell: mongo --eval 'printjson(rs.status())' | grep -E "stateStr|name" | awk -F ':' '{ print $2 }' | sed 's/"//g'| sed 's/,//g'| xargs -n2 | sed -e '1 i Server Status' | column -t
register: replicas
- name: dump status to localhost
lineinfile:
line: "{{ shard_status + '\n' | default('') }}{{ replicas.stdout }}"
state: present
become: false
dest: "/tmp/foobar"
delegate_to: localhost
With this way, if you had another mongo node, you would only modify your inventory and not your playbook and you also dont have to guard the replicas.stdout with a | default("") as you are always executing that in your nodes.

Ansible: no filter named 'selectattr'

I'm taking a foray into the world of Ansible. Right now, I have a task where I am trying to say:
If any of a list of files don't exist, then run this task
To accomplish this, I have this piece of code right here.
- name: Check if certificate already exists.
stat:
path: /etc/letsencrypt/live/{{ item }}/cert.pem
with_items: "{{ cert_domains }}"
register: letsencrypt_cert
- name: Create the certs
raw: certbot --nginx --noninteractive -d "{{ cert_domains.0 }}" -d "{{ cert_domains.1 }}"
register: certbot_create
changed_when: certbot_create.stdout
when:
- "{{ letsencrypt_cert.results|selectattr('stat','false') | length > 0 }}"
However, when I run my process it greets me with the following error:
"msg": "The conditional check '{{
letsencrypt_cert.results|selectattr('stat','false') | length > 1 }}' failed.
The error was: template error while templating string: no filter named
'selectattr'. String: {{ letsencrypt_cert.results|selectattr('stat','false')
| length > 0 }}\
In something like LINQ, i would do something like this:
list.where( n => n.equals(false)).Count() > 0
What could be going wrong?
For the sake of thoroughness, here are my versions:
ansible 2.4.2.0
python version = 2.6.9
Name: Jinja2 Version: 2.7.2
Any help on this is greatly appreciated.
I had installed Jinja2 using 'pip install jinja2'. What I needed to do was
'sudo yum install python-jinja2'
to get things working.
Try this:
- name: Create the certs
raw: certbot --nginx --noninteractive -d "{{ cert_domains.0 }}" -d "{{ cert_domains.1 }}"
register: certbot_create
changed_when: certbot_create.stdout
when:
- letsencrypt_cert.results | selectattr('stat', 'defined') | map(attribute='stat') | selectattr('exists', 'equalto', false) | list | length > 0

complex loop in ansible with_together

How do I skip empty item in list if I use with_together?
see the code below:
- name: get data_files list
shell: ls -l data_files | awk -F " " {'print $9'}
register: csv_file_list
- debug: var=csv_file_list
- name: get table name list
shell: ls -l data_files/ | awk -F " " {'print $9'} | sed -e "s/.csv//g" | sed -e "s/-/./g"
register: table_list
- debug: var=table_list
- name: copy table from csv to demo db
shell: psql -U postgres -d demo -c "\copy {{ item.1 }} from /home/ubuntu/data_files/{{ item.0 }} DELIMITER ',' CSV HEADER"
with_together:
- csv_file_list.stdout_lines
- table_list.stdout_lines
when: {{ item.1 }} != ''
Test if item.1 is not none.
when: item.1 != None

Resources