Ansible - Advanced shell command execution syntax

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 Cleints that you want to configure
      private: False
      defualt: "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 sepearately?

As an aside, you can do math in Jinja2-

- name: bashless math
  set_fact:
    IPOctet: "{{ 4*(ServerIPRange/epcrange)+IPOctet }}"

will do something approaching what you want.

If you want to get the actual shell-out working, something like this
is going to be enlightening:

  - name: local action math
    local_action: shell echo "{{ServerIPRange}}/{{epcrange}}+{{IPOctet}}" | bc
    with_sequence: start=1 end=4
    register: result
  - debug: var=result

"debug: var=result" will dump a pretty-printed JSON structure, so
you'll know what's available, and what you can use. You should try it
with & without the with_sequence.

Thank you for your quick reply:
But this works, but not in a loop!

  • name: bashless math
    set_fact:
    IPOctet: “{{ (ServerIPRange|int/epcrange|int)+IPOctet|int }}”
    with_sequence: start=1 end=4
    register: IPOctet

this wont work either:

  • name: bashless math
    set_fact:
    IPOctet: “{{ (ServerIPRange|int/epcrange|int)+IPOctet|int }}”
    register: IPOctet
    with_sequence: start=1 end=4
    register: my_ip_octet

I need to save the value in IPOctet repetitively

Again this will save the same value 4 times!

  • name: local action math
    local_action: shell echo “{{ServerIPRange}}/{{epcrange}}+{{IPOctet}}” | bc
    with_sequence: start=1 end=4
    register: result
  • debug: var=resu

There’s a bugfix PR for that which I need to test.

You can of course still do this as a shell command too, right now.

What problem are you having at the shell?

The shell command only does the calculation once and stores the same value in each loop iteration. I want it to run the command by using the last calculated value, which is supposedly not happening for me. Can the jinj2 sum or replace filters be of any help ?

I’m not understanding what “run the command using the last calculated value” means, unfortunately.

Well I just want a loop, that does the following:

IPOctet=$(echo “$ServerIPRange/$epcrange+$IPOctet” | bc)

as you can see IPOctet is added to $ServerIPRange/$epcrange and the result is SAVED in IPOctet
In each loop IPOctet is is say incremented by “x” and the result is saved in IPOctet again

What happens:
The loop runs the number of times but saves the same value in each loop iteration.
eg:
IPOctet=“5”
ServerIPRange=“16”
epcrange=“2”
IPOctet= 5 + 16/2 = 13

In the next iteration the result should be
IPOctet= 13 + 8 = 21 //But what happens is that IPOctet stays constant at “13”

In the third iteration the result should be
IPOctet= 21 + 8 = 29 //IPOctet is doesn’t change, reamins 13 and will remain 13 no matter how many iterations of the loop are run

I hope I have got my point across. Now any suggestions on the solution ?

Thank you for your quick reply:
But this works, but not in a loop!

    - name: bashless math
      set_fact:
        IPOctet: "{{ (ServerIPRange|int/epcrange|int)+IPOctet|int }}"
      with_sequence: start=1 end=4
      register: IPOctet

You may have missed a crucial bit of what I wrote- your algorithm
doesn't actually require any iteration. It decomposes into

IPrange/epcrange + (IPrange/epcrange + (IPrange/epcrange +
(IPrange/epcrange + LastIPOctet) ) )

which is the same thing as

4 * (IPrange/epcrange) + LastIPOctet

I posted that code piece of code for explanation purposes only, loop iteration i.e. the stop value is not fixed and will vary each time depending on user input (it is taken from the user at run time using vars_prompt)

Please can you tell me how to do x=x+y with x=10(will change with each loop iteration) and y=5 (remains constant), inside a loop that runs ten any number of time

Isn’t what you are doing mathematically equivalent to:

z = x + (y * iterations)

In which case, why do you need to store each result? Why not just run the task where you want to use this value using with_sequence, and use this formula directly?

Such as this contrived example:

  • debug: msg=“{{ IPOctet + (ServerIPRange//epcrange) * item|int }}”
    with_sequence: count=“{{ iterations }}”

I posted that code piece of code for explanation purposes only, loop iteration i.e. the stop value is not fixed and will vary each time depending on user input (it is taken from the user at run time using vars_prompt)

Please can you tell me how to do x=x+y with x=10(will change with each loop iteration) and y=5 (remains constant), inside a loop that runs ten any number of time

That decomposes the same way!

In any case, the yaml/jinja2 layer is generally the wrong layer to do logic in if your logic is going to get complicated. If you write a module, you can do anything python can do.

Nope, I can’t use them like that, my playbook is very complex, with some tasks running on some hosts serially