Ansible - Advanced shell command execution format

20,891

Solution 1

  1. 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
    
  2. 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.

  3. 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}}.

  4. 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.

  5. You can iterate over these results using result.stdout_lines:

    - name: iterate results
      local_action: debug msg={{item}}
      with_items: result.stdout_lines
    

Solution 2

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 :)

Share:
20,891
Khan
Author by

Khan

Updated on July 09, 2022

Comments

  • Khan
    Khan almost 2 years

    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