How to compare version numbers of software using Ansible? - ansible

Hey I am trying to check the version on installed software on my VMs with Ansible.
Unfortunately I cant get the conditionals right.
Via the ansible_facts.packages I get the version of the software but I am not sure about the comparing.
- hosts:
- os_linux
vars:
softwareVersion: "4.8.0-49"
tasks:
- name: Gather the package facts
ansible.builtin.package_facts:
manager: auto
- name: Check version of SoftwareX
ansible.builtin.debug:
msg: "OLD SoftwareX version: {{ ansible_facts.packages['SoftwareX'][0]['version'] }} found"
when: "ansible_facts.packages['SoftwareX'][0]['version'] <= {{ softwareVersion }}"

I am not sure about the comparing.
when: "ansible_facts.packages['SoftwareX'][0]['version'] <= {{ softwareVersion }}"
Since according When should I use {{ }}?
when:... are always templated and you should avoid adding {{ }}.
you'll need to remove first the curly brackets from softwareVersion.
Then, since you are comparing strings which contains a version number in order, you need to address this in a different way. Means, you can't use plain a mathematical operation but instead a specific filter for.
A minimal test playbook
---
- hosts: localhost
become: false
gather_facts: false
vars:
installedVersion: "4.7.0-48"
softwareVersion: "4.8.0-49"
tasks:
- name: Show hint
debug:
msg: "OLD"
when: installedVersion is version(softwareVersion, '<=')
will result into an output of
TASK [Show hint] ******
ok: [localhost] =>
msg: OLD
Further Documentaion
Comparing versions
Note: In 2.5 version_compare was renamed to version
To compare a version number, such as checking if the ansible_facts['distribution_version'] version is greater than or equal to ‘12.04’, you can use the version test.
Similar Q&A
Compare version numbers using Jinja2
How to compare kernel (or other) version numbers in Ansible?

Related

Anisble conditionals if package version [duplicate]

For a role I'm developing I need to verify that the kernel version is greater than a particular version.
I've found the ansible_kernel fact, but is there an easy way to compare this to other versions? I thought I might manually explode the version string on the .'s and compare the numbers, but I can't even find a friendly filter to explode the version string out, so I'm at a loss.
There is a test for it:
{{ ansible_distribution_version is version('12.04', '>=') }}
{{ sample_version_var is version('1.0', operator='lt', strict=True) }}
To Print the host IP address if the kernel version is less than 3
Ansible Version : 2.0.0.2
---
- hosts: all
vars:
kernel_version: "{{ ansible_kernel }}"
tasks:
- name: 'kernel version from facts'
debug:
msg: '{{ansible_all_ipv4_addresses}} {{ansible_kernel}}'
when: ansible_kernel | version_compare('3','<')
**
In 2.5 version_compare was renamed to version
**
For ansible>=2.9 this won't work, as the test syntax is now strictly separated from filters.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html
The working solution would be:
{{ sample_version_var is version('1.0', operator='lt', strict=True) }}
...
To compare a version number, such as checking if the ansible_facts['distribution_version'] version is greater than or equal to ‘12.04’, you can use the version test.
{{ ansible_facts['distribution_version'] is version('12.04', '>=') }}
When using version in a playbook or role, don’t use {{ }} as described in the FAQ
vars:
my_version: 1.2.3
tasks:
- debug:
msg: "my_version is higher than 1.0.0"
when: my_version is version('1.0.0', '>')
Check Ansible Doc for more info
Have you thought of using shell module instead? for example:
- name: Get Kernel version
shell: uname -r | egrep '^[0-9]*\.[0-9]*' -o
register: kernel_shell_output
- debug: msg="{{ kernel_shell_output.stdout}}"
- name: Add cstate and reboot bios if kernel is 4.8
shell: echo "do what yo need to do"
when: kernel_shell_output.stdout == "4.8"

Compare two version numbers with version in ansible

I am writing a playbook that only should run when a new package version of a software is available.
Both version numbers are custom facts I set with "set_fact"
Ansible let's you compare version numbers, so I tried it like this:
- name: compare versions
debug:
msg: "The version {{ new_version }} is newer than the old version {{ old_version }}"
when: "{{ new_version is version('{{ old_version }}', '>', strict=True }}"
Ansible throws an error that the version number is invalid. When I set "old_version" to a fixed version number it works as expected.
Is it possible to compare two facts with "version"?
I already tried different approaches with double-quotes etc which leads to syntax errors in ansible.
Any ideas?
Thanks
It could be use of Jinja delimiters {{ in a conditional. In a when: condition you don't need these delimiters to interpolate.
Example below works:
vars:
new_version: 14
old_version: 12
tasks:
- debug:
msg: 'The version {{ new_version }} is newer than the old version {{ old_version }}'
when: "new_version is version(old_version, '>')"

How to show all the host names of hosts within a specific OS in Ansible

Is there any command in Ansible to collect the hostnames of hosts with OS Debain?
The file hosts contains no groups!
So a simple command to see the hostnames of hosts containing Debain.
The setup module, which is implicitly called in any playbook via the gather_facts mechanism, contains some output that you could use.
Look for example for the ansible_distribution fact, which should contain Debian on the hosts you are looking for.
If all you want is that list once, you could invoke the module directly, using the ansible command and grep:
ansible all -m setup -a 'filter=ansible_distribution' | grep Debian
If you want to use that information dynamically in a playbook, you could use this pattern:
---
- hosts: all
tasks:
- name: Do something on Debian
debug:
msg: I'm a Debian host
when: ansible_distribution == 'Debian'
I would add that if you want to target distribution and minimum version (often the case), you could do that with a dictionary. I often use this snippet:
---
- hosts: localhost
vars:
minimum_required_version:
CentOS: 7
Debian: 11
Fedora: 31
RHEL7: 7
Ubuntu: 21.10
- name: set boolean fact has_minimum_required_version
set_fact:
has_minimum_required_version: "{{ ansible_distribution_version is version(minimum_required_version[ansible_distribution], '>=') | bool }}"
tasks:
- name: run task when host has minimum version required
debug:
msg: "{{ ansible_distribution }} {{ ansible_distribution_version }} >= {{ minimum_required_version[ansible_distribution] }}"
when: has_minimum_required_version
You could also dynamically include a playbook fragment that targets specific distributions or versions like so:
- name: Include task list in play for specific distros
include_tasks: "{{ ansible_distribution }}.yaml"
Which assumes you'd have Debian.yaml, Ubuntu.yaml, etc. defined for each distro you want to support.

How to compare kernel (or other) version numbers in Ansible

For a role I'm developing I need to verify that the kernel version is greater than a particular version.
I've found the ansible_kernel fact, but is there an easy way to compare this to other versions? I thought I might manually explode the version string on the .'s and compare the numbers, but I can't even find a friendly filter to explode the version string out, so I'm at a loss.
There is a test for it:
{{ ansible_distribution_version is version('12.04', '>=') }}
{{ sample_version_var is version('1.0', operator='lt', strict=True) }}
To Print the host IP address if the kernel version is less than 3
Ansible Version : 2.0.0.2
---
- hosts: all
vars:
kernel_version: "{{ ansible_kernel }}"
tasks:
- name: 'kernel version from facts'
debug:
msg: '{{ansible_all_ipv4_addresses}} {{ansible_kernel}}'
when: ansible_kernel | version_compare('3','<')
**
In 2.5 version_compare was renamed to version
**
For ansible>=2.9 this won't work, as the test syntax is now strictly separated from filters.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html
The working solution would be:
{{ sample_version_var is version('1.0', operator='lt', strict=True) }}
...
To compare a version number, such as checking if the ansible_facts['distribution_version'] version is greater than or equal to ‘12.04’, you can use the version test.
{{ ansible_facts['distribution_version'] is version('12.04', '>=') }}
When using version in a playbook or role, don’t use {{ }} as described in the FAQ
vars:
my_version: 1.2.3
tasks:
- debug:
msg: "my_version is higher than 1.0.0"
when: my_version is version('1.0.0', '>')
Check Ansible Doc for more info
Have you thought of using shell module instead? for example:
- name: Get Kernel version
shell: uname -r | egrep '^[0-9]*\.[0-9]*' -o
register: kernel_shell_output
- debug: msg="{{ kernel_shell_output.stdout}}"
- name: Add cstate and reboot bios if kernel is 4.8
shell: echo "do what yo need to do"
when: kernel_shell_output.stdout == "4.8"

Ansible: variable interpolation in task name

I cannot get this seemingly simple example to work in Ansible 1.8.3. The variable interpolation does not kick in the task name. All examples I have seen seem to suggest this should work. Given that the variable is defined in the vars section I expected the task name to print the value of the variable. Why doesn't this work?
Even the example from the Ansible documentation seems to not print the variable value.
---
- hosts: 127.0.0.1
gather_facts: no
vars:
vhost: "foo"
tasks:
- name: create a virtual host file for {{ vhost }}
debug: msg="{{ vhost }}"
This results in the following output:
PLAY [127.0.0.1]
**************************************************************
TASK: [create a virtual host file for {{ vhost }}]
****************************
ok: [127.0.0.1] => {
"msg": "foo"
}
PLAY RECAP
********************************************************************
127.0.0.1 : ok=1 changed=0 unreachable=0 failed=0
Update
This works with 1.7.2 but does not work with 1.8.3. So either this is a bug or a feature.
Variables are not resolved inside the name. Only inside the actual tasks/conditions etc. the placeholders will be resolved. I guess this is by design. Imagine you have a with_items loop and use the {{ item }}in the name. The tasks name will only be printed once, but the {{ item }} would change in every iteration.
I see the examples, even the one in the doc you linked to, use variables in the name. But that doesn't mean the result would be like you expected it. The docs are community managed. It might be someone just put that line there w/o testing it - or maybe it used to work like that in a previous version of Ansible and the docs have not been updated then. (I'm only using Ansible since about one year). But even though it doesn't work like we wish it would, I'm still using variables in my name's, just to indicate that the task is based on dynamic parameters. Might be the examples have been written with the same intention.
An interesting observation I recently made (Ansible 1.9.4) is, default values are written out in the task name.
- name: create a virtual host file for {{ vhost | default("foo") }}
When executed, Ansible would show the task title as:
TASK: [create a virtual host file for foo]
This way you can avoid ugly task names in the output.
Explanation
Whether the variable gets interpolated depends on where it has been declared.
Imagine You have two hosts: A and B.
If variable foo has only per-host values, when Ansible runs the play, it cannot decide which value to use.
On the other hand, if it has a global value (global in a sense of host invariance), there is no confusion which value to use.
Source: https://github.com/ansible/ansible/issues/3103#issuecomment-18835432
Hands on playbook
ansible_user is an inventory variable
greeting is an invariant variable
- name: Test variable substitution in names
hosts: localhost
connection: local
vars:
greeting: Hello
tasks:
- name: Sorry {{ ansible_user }}
debug:
msg: this won't work
- name: You say '{{ greeting }}'
debug:
var: ansible_user
I experienced the same problem today in one of my Ansible roles and I noticed something interesting.
When I use the set_fact module before I use the vars in the task name, they actually get translated to their correct values.
In this example I wanted to set the password for a remote user:
Notice that I use the vars test_user and user_password that I set as facts before.
- name: Prepare to set user password
set_fact:
user_password: "{{ linux_pass }}"
user_salt: "s0m3s4lt"
test_user: "{{ ansible_user }}"
- name: "Changing password for user {{ test_user }} to {{ user_password }}"
user:
name: "{{ ansible_user }}"
password: "{{ user_password | password_hash('sha512', user_salt) }}"
state: present
shell: /bin/bash
update_password: always
This gives me the following output:
TASK [install : Changing password for user linux to LiNuXuSeRPaSs#]
So this solved my problem.
It might be ugly, but you can somewhat workaround with something like this:
- name: create a virtual host file
debug:
msg: "Some command result"
loop: "{{ [ vhost ] }}"
or
- name: create a virtual host file
debug:
msg: "Some command result"
loop_control:
label: "{{ vhost }}"
loop: [1]
I wouldn't do this in general, but it shows how you can use items or label to give information outside of the command result. While it might not
Source: https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html

Resources