Home Uncategorized Arithmetic Operators in Ansible

Arithmetic Operators in Ansible

by The Linux Digest Guy

Did you know that you can do mathematic calculations in Ansible? This can be very helpful in situations you need to set configurations based on calculations that can not be done beforehand. Ansible uses the Jinja2 template engine and arithmetic operations are performed by Jinja2. So you can also check the Jinja documentation if you need more information about operators.

Here are the Arithmetic operators in Ansible and Jinja2:

  • + Addition
  • – Subtraction
  • / Division
  • * Multiplication
  • // Floor division
  • % Modulo
  • ** Exponentiation

Let’s go through some simple examples of each of the operators. After that, I will show you some more practical examples.

Addition in Ansible

Adding two numbers together is done with the + operator. To get Ansible to process the expression, you must wrap it with double curly brackets: {{ expression }}. To demonstrate this, I have created a simple playbook that only prints the result:

---
- name: Run some calculations
  hosts: localhost
  tasks:
  - name: Add 2 + 2
    debug:
      msg: The result is {{ 2 + 2 }}

You can try running this if you want to see it in action. But as I imagine you can already guess, the result will be 4:

TASK [Add 2 + 2] *************************************************************************************
ok: [localhost] => {
    "msg": "The result is 4"
}

Subtraction in ansible

Subtracting two numbers is just as simple. By using the – operator, the right side argument will be subtracted from the left one. Here we will subtract 5 from 10:

  - name: Subtract: 10-5
    debug:
      msg: The result is {{ 10 - 5 }}

This will output the following result:

TASK [Subtract 10-5] *********************************************************************************
ok: [localhost] => {
    "msg": "The result is 5"
}

Division in Ansible

To divide, we use the / operator. This will divide the left argument by the right argument. Here we will divide 10 by 5:

  - name: Divide 10/5
    debug:
      msg: The result is {{ 10 / 5 }}

The answer is 2, as we can tell from the output below.

TASK [Divide 10/5] ***********************************************************************************
ok: [localhost] => {
    "msg": "The result is 2.0"
}

As you can see, the return value from a division operation will always be a floating point number. 2.0 in this case. This is not always what you want. If you want the return value to be a whole integer, you can use Jinja filters to round the number and then convert it to an integer.

You can see this in action in the task below. Here I have wrapped the calculation in parenthesis to ensure that the filter is applied to the finished calculation. Then I add the pipe character (|) and call the round filter. The int filter then converts the result to an integer.

  - name: Divide 10/5
    debug:
      msg: The result is {{ (10 / 5)|round|int }}

This time we will see the same result except without any decimal points.

TASK [Divide 10/5] ***********************************************************************************
ok: [localhost] => {
    "msg": "The result is 2"
}

Multiplication in Ansible

The multiplication operator is * (asterisk). Here we will multiply 2 by 2:

  - name: Multiply 2 * 2
    debug:
      msg: The result is {{ 2 * 2 }}

I hope you can guess the result. It is, of course, 4.

TASK [Multiply 2 * 2] ********************************************************************************
ok: [localhost] => {
    "msg": "The result is 4"
}

Floor division in Ansible

Floor division works similarly to division. The difference is that instead of returning a floating point number, it will return the highest possible whole integer. A simple example would be 5 // 2 = 2. In this example, 5 can only be divided by two twice without converting the result to a float.

Let’s look at two examples in action. In these examples, we try to divide 11 by 5 and 18 by 5.

  - name: Floor division 11 // 5
    debug:
      msg: The result is {{ 11 // 5 }}

  - name: Floor division 18 // 5
    debug:
      msg: The result is {{ 18 // 5 }}

If we had used division for these two problems, we would have gotten 2.2 and 3.6, respectively. But, floor division will only return the highest whole integer possible. In our case, that would be 2 and 3:

TASK [Floor division 11 // 5] ************************************************************************
ok: [localhost] => {
    "msg": "The result is 2"
}

TASK [Floor division 18 // 5] ************************************************************************
ok: [localhost] => {
    "msg": "The result is 3"
}

Modulo in Ansible

The modulo operator is %. A modulo operation gives you the remainder after a division. As an example, calculating 7 / 3 will not result in a whole number, but 6 / 3 will. 7 % 3 will give you the remainder of 1.

Let’s try a couple of examples:

  - name: Modulo operation 11 % 5
    debug:
      msg: The result is {{ 11 % 5 }}
  - name: Modulo operation 19 % 5
    debug:
      msg: The result is {{ 19 % 5 }}

In the first example of 11 % 5, you can only divide 11 by five twice to get a whole number. This would be equal to 10 / 5. So you have a remainder of 11-10=1

In the second example of 19 % 5, you can dive 19 by 5 three times to get a whole number. This would be equal to 15 / 5. Now you have a remainder of 19 – 15 = 4.

TASK [Modulo operation 11 % 5] *************************************************
ok: [localhost] => {
    "msg": "The result is 1"
}

TASK [Modulo operation 19 % 5] *************************************************
ok: [localhost] => {
    "msg": "The result is 4"
}

Exponentation in Ansible

The last operator we should know is the exponent operator **. Also known as the “power of” operator. In mathematics, exponentiation is usually written as Xn . The operation of 5 to the power of 2 would be 52. This is not easy to write in a text editor when programming, so programming languages use simpler operators like ** for this purpose. 5 to the power of 2 can be written as 5**2 in Ansible.

Let’s try a couple of examples:

  - name: Exponentation 5 ** 2
    debug:
      msg: The result is {{ 5 ** 2 }}
  - name: Exponentation 2 ** 3
    debug:
      msg: The result is {{ 2 ** 3 }}

The first example of 52 is written in Ansible as 5 ** 2. This is the equivalent of writing 5*5. This should result in 25.

The second example of 23 is written in Ansible as 2 ** 3. This is the equivalent of writing 2 * 2 * 2. This should result in 8.

Here is the output of these two operations:

TASK [Exponentation 5 ** 2] ****************************************************
ok: [localhost] => {
    "msg": "The result is 25"
}

TASK [Exponentation 2 ** 3] ****************************************************
ok: [localhost] => {
    "msg": "The result is 8"
}

Practical examples of arithmetic in Ansible

Now we have gone over simple examples of doing math in Ansible. But, of course, Ansible is not just a simple calculator. Now we should try to put our newfound knowledge to good use and do something useful.

Count VCPU cores and multiply them to get thread count

Let’s imagine we have a fictional service we want to run two threads of for every VCPU our host has. Our fictional service has a configuration file that has an argument for how many threads it should run. We start by defining a template for our service and place it in the templates directory (templates/fictional.conf.j2)

cores: {{ cores }}
threads: {{ threads }}

We can now create our playbook. To get the VCPU count, I have added “gather_facts: true” to the playbook. This way, we will be able to get the VCPUs for the processor_vcpus fact.

---
- name: Configure fictional service
  hosts: myhost
  remote_user: root
  gather_facts: true
  tasks:
  - name: How many vcpus do we have?
    debug:
      var: ansible_facts.processor_vcpus
  - name: Calculate the number of threads
    set_fact:
      cores: '{{ ansible_facts.processor_vcpus }}'
      threads: '{{ ansible_facts.processor_vcpus * 2 }}'
  - name: Write the configuration file
    template:
      src: fictional.conf.j2
      dest: /etc/fictional/fictional.conf

The first task will print the number of VCPUs, so we can verify that it is correct. The next task will set the variables for us. Finally, we use the built-in template module to save our configuration file to the host.

Here is the output from our playbook.

PLAY [Configure fictional service] *******************************************************************

TASK [Gathering Facts] *******************************************************************************
ok: [myhost]

TASK [How many vcpus do we have?] ********************************************************************
ok: [myhost] => {
    "ansible_facts.processor_vcpus": "12"
}

TASK [Calculate the number of threads] ***************************************************************
ok: [myhost]

TASK [Write the configuration file] ******************************************************************
changed: [myhost]

PLAY RECAP *******************************************************************************************
myhost                     : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

The output from the playbook run tells us that we have 12 VCPUs. That should mean that our thread count should be set to 12*2=24. Let’s check the contents of /etc/fictional/fictional.conf:

:~# cat /etc/fictional/fictional.conf 
cores: 12
threads: 24

Yes! The file contains exactly what we expected.

If we want to simplify our playbook, we can also do the calculations in the template file itself. This way, we can skip the set_fact task and access the processor_vcpu fact directly.

Here is our new template:

cores: {{ ansible_facts.processor_vcpus }}
threads: {{ ansible_facts.processor_vcpus * 2 }}

Suppose we want one thread for every two VCPUs our host has. Obviously, we would need to do division instead of multiplication.

Here I have defined a new template where I divide the number of VCPUs by 2 with the division operator, /. I have also added one small change. At the end of the calculation, I added a filter to ensure we only get a whole integer. The calculation is also wrapped in parentheses to ensure Ansible knows we want the filter to apply to the result, not just the last integer.

cores: {{ ansible_facts.processor_vcpus }}
threads: {{ (ansible_facts.processor_vcpus / 2)|int }}

As expected, our threads variable now contains 6:

:~# cat /etc/fictional/fictional.conf 
cores: 12
threads: 6

Doing calculations within conditional arguments

Let’s say we only want to configure our service when a host has more than 1024MB of memory for every VCPU. This is a totally fictional scenario. But it is not unreasonable to think a similar scenario could come up in the real world.

To do this, we would add the “when” statement to our task. We also need to calculate how many MB of memory we have for every VCPU by dividing the amount of available memory by our VCPU count.

We can retrieve the amount of memory the host has with the fact “memtotal_mb”. So the calculation should be memtotal_mb / processor_vcpus. After the calculation, we add >= 1024 to see if the result is greater than or equal to 1024.

Here is the task with the “when” statement added.

  - name: Write the configuration file
    template:
      src: fictional.conf.j2
      dest: /etc/fictional/fictional.conf
    when: (ansible_facts.memtotal_mb / ansible_facts.processor_vcpus) > 1024

Now we have tried all kinds of calculations and even tried to apply our new knowledge to some semi-practical examples. I hope you have found this tutorial helpful. Now it is up to you to find ways to use these examples in your own playbooks. The beauty of ansible is that there are endless ways you can use arithmetic operators in playbooks. You can customize all kinds of configurations with calculations made on the fly. Good luck!

1

Related Posts

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More

Privacy & Cookies Policy