Version numbers in Ansible code
There are two useful Jinja filters that could help dealing with version numbers in Ansible code. One is version_compare
, which allows to compare version numbers. The other is regex_replace
, which can be used to get a major.minor version number from a longer version number.
Version comparison
It is possible to compare a simple version to an integer or decimal, to decide, whether to, for example, run a task in Ansible. That, however, becomes a bit harder, when using version numbers, that are more complex and are not really numbers, e.g. 3.2.1
. One can use the split
function - split the version by dots and compare the results, but it's not that clean:
when: "version.stdout.split('.')[0] >= '2' and version.stdout.split('.')[1] > '7'"
Fortunately, there is a version_compare
jinja filter, that can be used in Ansible to compare even more complex versions:
- hosts: "localhost"
gather_facts: false
vars:
version: "2.7.11"
tasks:
- name: check if version is within acceptable range
assert:
that:
- version | version_compare('2.7', '>=', strict=True)
- version | version_compare('4.0', '<', strict=True)
The second parameter of version_compare
(relational operator) can be noted as above, or using letter references (le
, gt
...) as well.
The documentation on the strict
parameter is very brief, but the testing seems to show that it indeed uses stricter version notation rules on both sides of comparison: it requires at least one dot in the version number, and does not allow more than 2 dots. Setting version
variable in the code above to 3
or 3.0.0.0
would not work, while 3.0
or 3.0.1
would be fine with the strict
parameter present.
Version shortening
We'll use Python as an example, which has a version numbering scheme, that always has 2 dots, e.g. 2.7.11
or 3.5.1
. In some cases, we must use Python executables, that are suffixed by a shortened version number, e.g. python2.7
. This means that we somehow need to reliably turn 2.7.11
into 2.7
.
Another jinja filter, regex_replace
, seemed the most efficient and reliable way to do just that. This regexp seemed to do the trick:
version | regex_replace('^([0-9])\\.([0-9]*).*', '\\1.\\2')
There is a caveat when using Ansible < 2.x:
Prior to ansible 2.0, if “regex_replace” filter was used with variables inside YAML arguments (as opposed to simpler ‘key=value’ arguments), then you needed to escape backreferences (e.g. \1) with 4 backslashes (
\\\\
) instead of 2 (\\
).
So, for Ansible versions < 2.x, the code would look like this:
version_var | regex_replace('^([0-9])\\.([0-9]*).*', '\\\\1.\\\\2')