Ansible - Advanced shell command execution format - bash

I have 3 variables named IPOctet, ServerIPRange and epcrange.
If I perform the following operation in my terminal, it works perfectly
IPOctet=$(echo "$ServerIPRange/$epcrange+$IPOctet" | bc)
How to I do something similar in a ansible inside a task, for e.g
---
- hosts: localhost
gather_facts: False
vars_prompt:
- name: epcrange
prompt: Enter the number of EPCs that you want to configure
private: False
default: "1"
- name: serverrange
prompt: Enter the number of Clients that you want to configure
private: False
default: "1"
- name: ServerIPRange
prompt: Enter the ServerIP range
private: False
default: '128'
- name: LastIPOctet
prompt: Enter The last Octet of the IP you just entered
private: False
default: '10'
pre_tasks:
- name: Set some facts
set_fact:
ServerIP1: "{{ServerIP}}"
ServerIPRange1: "{{ServerIPRange}}"
IPOctet: "{{LastIPOctet}}"
- name: local action math
local_action: shell {{IPOctet}}=$(echo "${{ServerIPRange}}/${{epcrange}}+${{IPOctet}}" | bc) # Proper Syntax?
with_sequence: start=1 end=4
register: result
ignore_errors: yes
What is the proper syntax for this command? Maybe using shell echo "......." . I just need to save the contents of this command into the IPOctet variable and IPOctet will change with each loop iteration and the results should be stored in my result register
P.S: how can I access the individual items in the array separately?
Edit: Is anything like this possible, currently it just does the calculation once and stores it 4 times in the register...
- name: bashless math
set_fact:
IPOctet: "{{ (ServerIPRange|int/epcrange|int)+IPOctet|int }}"
register: IPOctet
with_sequence: "start=1 end={{stop}} "
register: my_ip_octet

Your terminal expression reassigns the IPOctet shell variable, so it gives a different result each time it is executed. This is fine, but difficult to reproduce in Ansible:
$ IPOctet=10 ServerIPRange=128 epcrange=1
$ IPOctet=$(echo "$ServerIPRange/$epcrange+$IPOctet" | bc); echo $IPOctet
138
$ IPOctet=$(echo "$ServerIPRange/$epcrange+$IPOctet" | bc); echo $IPOctet
266
The syntax: "shell {{IPOctet}}=$(echo ..." does NOT assign to the Ansible variable.
The shell attempts to execute a command like "10=138", which is not found.
When register is used within a loop, the target variable is not set until the loop completes - so your expression always sees the original value for {{IPOctet}}.
A solution is to run the whole loop as a single shell command:
- name: local action math2
local_action: shell IPOctet={{IPOctet}}; for i in 1 2 3 4; do IPOctet=$(expr {{ServerIPRange}} / {{epcrange}} + $IPOctet); echo $IPOctet; done
register: result
NOTE: I've used the expr command rather than bc, but the results are the same.
You can iterate over these results using result.stdout_lines:
- name: iterate results
local_action: debug msg={{item}}
with_items: result.stdout_lines

Firstly your Jinja template is incorrect, every single variable needs to be surrounded with a pair of brackets. You can not use multiple variables within single pair of brackets. For example,
{{ ServerIPRange }}
Secondly, set_fact is used only to set a fact value. You can not run shell commands using set_fact. You should use shell module instead.
- name: local action math
local_action: shell {{ IPOctet }}=$(echo {{ ServerIPRange|int }}/{{ epcrange|int }}+{{ IPOctet|int }}" | bc)
with_sequence: start=1 end=4
register: result
ignore_errors: yes
Ansible will do the calculation 4 times and store it in a list as 4 different elements. You can check what all is stored inside this list and can even access it by looping over it.
- debug: msg={{ result }}
Hope this helps :)

Related

Does Ansible have an else condition

So far I can see using a when in ansible to determin whether to run a task but do I have to define 2 tasks to run the alternative option..
example - if I want to run the following task then run the debug task, i need to run two tasks or existStatus will not have been defined for the debug statement. Can I not use some sort of if else statement rather than include 2 separate tasks?
- name: Print user does not exist status
shell: echo 'user does not exist'
when: kafka_configs_result.stdout_lines[1] == '1'
register: existStatus
- name: Print user does not exist status
shell: echo 'user already exists so could not be created'
when: kafka_configs_result.stdout_lines[1] == '0'
register: existStatus
- debug: msg="{{ existStatus.stdout_lines }}"
You can do this in one single task without having to go through an unneeded shell execution. A simple way is to use a test and the ternary filter
I added the var section simply for readability. You can make a long one liner if you wish.
- debug:
vars:
exists_test: "{{ kafka_configs_result.stdout_lines[1] == '1' }}"
msg_exists: "user already exists so could not be created"
msg_notexists: "user does not exist"
msg: "{{ exists_test | ternary(msg_notexists, msg_exists) }}"
You can write something like this to utilize if-loop
- set_fact: build="{% if '<something>' in <something> %}<VALUE>{% else %}<VALUE>{% endif %}"

Use awk with ansible to run command

I have a playbook below:
- hosts: localhost
vars:
folderpath:
folder1/des
folder2/sdf
tasks:
- name: Create a symlink
shell: "echo {{folderpath}} | awk -F'/' '{system(\"mkdir \" $1$2 );}'"
register: result
#- debug:
# msg: "{{ result.stdout }}"
with_items:
- " {{folderpath}} "
However when I run the playbook I get 2 folders made. The first one is :
1- folder1des (as expected)
2- folder2 (this should ideally be folder2sdf )
I have tried many combination and still it doesnt want to work. What do I need to have it work properly.
I do not have ansible environment at the moment. But following should work:
- hosts: localhost
tasks:
- name: Create a symlink
shell: "echo {{item}} | awk -F'/' '{system(\"mkdir \" $1$2 );}'"
register: result
#- debug:
# msg: "{{ result.stdout }}"
with_items:
- folder1/des
- folder2/sdf
Reference: Ansible Loops Example
Explanation:
You were adding a single list object to the with_items. so in your with_items it finds only one object (which is of type list) to iterate over. Hence it runs only once. So now what I have done is I have passed a list of items to with_items that way it can iterate over the multiple items present in with_items.
Hope this helps!
Maybe
- hosts: localhost
vars:
folderpath:
folder1/des
folder2/sdf
tasks:
- name: Create a symlink
file:
state : link
path : "{{ item | regex_replace('[0-9]/','_') }}"
src : "{{ item }}"
with_items: " {{ folderpath }} "
Nothing in your given code creates symlinks. Is that really what you meant to do?

What does conditional "when: var | d()" mean in Ansible 2.5

I am unable to source from Ansible documents a clear meaning of a conditional such as when: var | d(). Is someone able give a clear explanation?
E.g. Below works whether inputing extra-var value from cli or defaulting to local ENV variable value:
vars:
my_var: "{{ e_var | default(ansible_env.USER | default(False,true)) }}"
tasks:
- name: Conditional check
debug:
msg: "{{ my_var }}"
when: my_var | d()
But this fails:
vars:
my_var: "{{ e_var | default(ansible_env.USER | default(false,true)) }}"
tasks:
- name: Conditional check
debug:
msg: "{{ my_var }}"
when: my_var
What is when: my_var | d() exactly doing? How how does it interplay with the | default(false,true) part in the variable declaration?
d is an alias to the default filter. It is a Jinja2 filter, so head for the Jinja2 docs. They work the same:
default(value, default_value=u'', boolean=False)
[ ]
Aliases: d
Regarding the problem you are facing, it is because Ansible processes a condition consisting of only a variable name differently from a more complex expression (which is passed directly to Jinja2/Python) (the actual code starts here):
If the my_var variable has a value of user01, the conditional will try to find a value of user01 variable and fail because it doesn't exist.
If you just add a logical conjunction (which in common sense is redundant), Ansible will process the whole expression differently and it will work:
when: my_var and true
In your case using another default filter in the expression is also redundant, but it prevents Ansible from trying to resolve a "nested" variable value.

Ansible : evaluate variable inside a variable with register

I have a task where i need to evaluate a variable attribute , where the name is already a vaiable.
here is the scenario :
i'm executing a shell command ( docker ps ) and i'm registering the output in a variable , where the name is already dynamic:
- name : Display running containers for {{apiType}}
shell: docker ps
register: docker_containers_{{apiType}}
when:
- '"containers" in type'
no i want to display the content of that ouput and not only the string itself , so i need to do something like this:
- name: Display running containers for {{apiType}}
debug:
msg: {{docker_containers_{{apiType}}.stdout}}
when:
- '"containers" in type'
of course , this : {{docker_containers_{{apiType}}.stdout}}
is syntaxically refused
i ve tried this : {{docker_containers_[apiType].stdout}}
but it fails.
Suggestions?
This is a FAQ. You can build a string and use that to index the hostvars for your current host:
- name: Display running containers for {{apiType}}
debug:
msg: "{{ hostvars[inventory_hostname]['docker_containers_' + apiType].stdout}}"
when:
- '"containers" in type'
...this assumes that your docker_containers_... variable is a host fact, rather than, say, something set via group_vars or a vars stanza in your playbook.
Here's a runnable example:
- hosts: localhost
gather_facts: false
vars:
apiType: foo
tasks:
- set_fact:
docker_containers_foo:
stdout: "this is foo"
- set_fact:
docker_containers_bar:
stdout: "this is bar"
- name: Display running containers for {{apiType}}
debug:
msg: "{{ hostvars[inventory_hostname]['docker_containers_' + apiType].stdout}}"

ansible read (more than one) values from a line in file

i am trying to read variables from a file into an array of something, but not into a pre defined variable.
The goal is a playbook, that searches for lines with item.0 and NOT item.1 and deletes them, later the playbook makes sure, that a line with item.0 item.1 is present, thats why i need this splitted.
For example i have a file with lines like this:
Parameter Value
Parameter Value
Parameter Value
to use this in a loop until EOF.
example part ot the playbook:
- name: lineinfile loop
lineinfile:
path: /myfile
regexp '^{{somethinglike item.0}}.(?!{{something like item.1}})'
state absent
...
Does anybody know a solution for the lookup and the loop?
Best regards
This should help you.
Input file
cat filein
Parameter1,Value1
Parameter2,Value2
Parameter3,Value3
Playbook:
---
- hosts: test
tasks:
- name: reg
shell: cat filein
register: myitems
- name: deb
debug: var=myitems.stdout_lines
- name: echo to file
shell: "echo {{ item.split(',')[1] }} - {{ item.split(',')[0] }} >> fileout"
with_items: "{{ myitems.stdout_lines }}"
Output file:
cat fileout
Value1 - Parameter1
Value2 - Parameter2
Value3 - Parameter3
This is not state of the art but should work.
The solutuion:
There is a ansible role in ansible galaxy, it provides exactly what i need:
https://github.com/mkouhei/ansible-role-includecsv

Resources