Ansible loops and conditionals - loops

Ansible docs states that:
Combining when with with_items (see Loops), be aware that the when statement is processed separately for each item.
However when I try to skip one item in task, it doesn't work that way:
value_var: [1, 5]
- name: register variable
command: echo "4"
register: var
- name: conditional check
command: nevermind
when: var.stdout > item
By my understanding, that I would get changed on first item within conditional check task, and skipped on second item. But I get:
changed: [guest] => (item=5)
changed: [guest] => (item=1)
What am I doing wrong?

It has nothing to do with loops. You are comparing a string (the result of echo command) with an integer.
You should first cast the value:
when: var.stdout|int > item

Related

Access list sub-elements with wildcard in Ansible?

I want to loop through the sub-elements of a list in Ansible.
I have a var file like this:
config:
machine1:
services:
sub-element11:
- value
ports:
sub-element21:
- value
- value
sub-element22:
- value
- value
machine2:
.
.
.
And I want to loop through the sub-elements while not giving the name of the element that comes before it:
- debug
var: item
with_items: "{{ config.machine1.* | list }}"
where item would have the value: sub-element11, sub-element21, sub-element22
I can use 2 loops as I know I'll always have "services" and "ports" as a key-word. But I don't want to clone everything that comes after.
Maybe I can use nested loops with set facts but is seems tedious.
Thank you for your help :)

Run a command with different args everytime in Ansible playbook

I am trying to run a command in Ansible so as to find the neighbors in my network:
- name: Get neighbors
junos_rpc:
rpc: "get-lldp-interface-neighbors"
output: 'xml'
args:
interface_device: A
register: net_topology
So my problem comes when in this task I need to loop over a list and give another arg for the interface_device and register the result also in another variable 'net_topology' every time.
- name: Get neighbors
junos_rpc:
rpc: "get-lldp-interface-neighbors"
output: 'xml'
args:
interface_device: "{{ item }}"
loop:
- A
- B
- C
register: net_topology
Once you modify your task like this, it will play three times: once for each element in my example loop. The variable item will get the value of the current element in the list.
You do not need to change your register variable: it will automatically be modified as explained in the ansible 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
So you can inspect all your results in a subsequent task by looping over net_topology.results which contains the list of individual results.
Actually i did something similar with the above, but i just passed my list with a different way:
- name: building network topology
junos_rpc:
rpc: "get-lldp-interface-neighbors"
output: 'xml'
args:
interface_device: "{{item}}"
loop:
"{{my_list}}"
register: net_topology
And this is actually the same as doing this also:
- name: building network topology
junos_rpc:
rpc: "get-lldp-interface-neighbors"
output: 'xml'
args:
interface_device: "{{item}}"
with_items:
"{{my_list}}"
register: net_topology
I must say that my initial mistake was the identation of the loop, because it was placed inside the junos_rpc and by doing this I could not get any result !!!

Using any to check existence of element in array inside hash

I am trying to check if an element (array) of a hash contains a specific item by using any. Because my arrays can get very large it seemed any was the most efficient way as it returns true as soon as the item is found. The problem is that CLI returns:
Type of arg 1 to List::Util::any must be block or sub {} (not array
dereference) at ...
The line is (changed to a fictitious example) reproduced below. I am trying to see if id of item2 is inside field of item1 in the fictitious example below.
unless(any(#{$hash{$item1}{field}}) eq $hash{$item2}{id}) {
# Do magic.
}
What am I doing wrong? As any is part of List::Util, I have loaded that module at the top.
use List::Util qw(any);
You need to import the function:
use List::Util qw(any);
UPDATE: As noted, the 1st arg to any should be a block of code. In this case, compare the hash value to $_, which is assigned to each value in your array until the condition is true.
unless(any { $_ eq $hash{$item2}{id} } #{$hash{$item1}{field}}) {

Ansible - Power of 2 loop on a variable

I need:
vars_prompt:
- name: loopvar
prompt: Enter the loop variable
private: False
default: 16
- hosts: epcs
serial:1
gather_facts: no
tasks:
- name: Do some mathematics divide multiply
#insert logic here
register: my_content # save logic in register
with_sequence: count={{loopvar}} #I need a loop sequence here
when: inventory_hostname == "vm1"
ignore_errors: yes
A simple loop in c++ would be sth like:
int x=0;
for (int i=1; i<loopvar; i+=pow(2,x)) //pow is a math function with pow(2,x)= 2^x
{
cout<<hi;
x++;
}
One more thing, how can I store the results of each iteration in a register, so that I have it available, when the playbook runs serially the 2nd, 3rd or 4th time etc.
Update:
Jinja2 allows the following:
Raise the left operand to the power of the right operand.
{{ 2**3 }} would return 8.
Now keeping this new information in mind, can we do a power of 2 loop?
The type of loop you want is not going to be possible in Ansible. The tool is limited to looping over a list, dictionary, or a sequence but the counter cannot be modified. See the ansible docs on loops.
For something like this you may want to look into writing a custom module.
One more thing, how can I store the results of each iteration in a register, so that I have it available, when the playbook runs serially the 2nd, 3rd or 4th time etc.
See the section in the Ansible docs on how to use register in a loop.
If you register a var named my_content you can access the individual results in my_content.results.

can't convert Enumerator into Array

While working on one application I am getting this error:
can't convert Enumerator into Array
Here is my code, mr_collection is MongoID query.
mr_collection = self.where(query).map_reduce(map, reduce).finalize(finalize).out({:replace => 'mr_results'})
paginator = WillPaginate::Collection.new(page, limit, collection_count)
collection = mr_collection.find(
:sort => sort,
:limit => limit,
:skip => skip
)
paginator.replace(collection)
While getting mr_collection, if I inspect the result mr_collection gives me:
[
{"_id"=>1.0, "value"=>{"s"=>4.2, "p"=>14.95, "pml"=>0.01993}},
{"_id"=>2.0, "value"=>{"s"=>3.7, "p"=>12.9, "pml"=>0.0172}},
{"_id"=>3.0, "value"=>{"s"=>4.2, "p"=>12.9, "pml"=>0.0172}},
{"_id"=>4.0, "value"=>{"s"=>4.0, "p"=>11.95, "pml"=>0.01593}},
{"_id"=>300.0, "value"=>{"s"=>0.0, "p"=>8.95, "pml"=>0.01193}},
]
While getting collection, if I inspect the result collection gives me:
#<Enumerator: []:find({:sort=>[["value.s", :desc], ["value.pml", :asc]], :limit=>10, :skip=>0})>
I am getting error on the line paginator.replace(collection). I'm using Ruby 1.9.3 & Rails 3.2.6.
collection is an Enumerator which obviously can't convert into an Array, which is what replace expects.
Here are the comments from the rubydocs:
Enumerable#find(ifnone = nil) { |e| ... }
Passes each entry in enum to block. Returns the first for which block
is not false. If no object matches, calls ifnone and returns its
result when it is specified, or returns nil otherwise.
If no block is given, an enumerator is returned instead.
Therefore you have two options:
If you want all elements, yield from the Enumerator to an Array.
If you only want the first match, supply a block that determines what the match is.
Hope this helps.
(Moral of the story: always read the docs!)
I have no idea about mongoid having never used it.
But a search has brought to fore an awfully similar question -
Mongoid 3 - access map_reduce results
Unfortunately my environent is not set to test the magic of
collection = mr_collection.send(:documents).sort(sort).limit(limit).skip(skip).to_a
Have you had a look at this link? Hopefully it'll help solve your issue!

Resources