I am trying to extract the major distro version (which ansible_facts
holds as a string) and store it as an integer for later <
or >
comparison to an integer. When I do this:
- set_fact:
distromajor: "{{ ansible_facts['distribution_major_version'] | int }}"
I find distromajor
holds "7"
instead of 7
.
So later comparisons fail. In fact, the only way I can get it to work is to compare like this:
(distromajor|int >=6) and (distromajor|int <= 8)
Is this expected behaviour?
Why can I not save the distro major version as an int?
The closest SO question does not explain why a later integer comparison fails without reconverting the distromajor
variable to integer at time of comparison.
Q: "Is this expected behavior?"
A: Yes. This is the expected behavior, in Ansible.
Q: "Why can I not save the distro major version as an int?"
A: Ansible decided you can't (todo: reference to source code needed). In YAML, there are three basic primitives:
As you can see, the scalars are both strings and numbers. But, for some reason unknown to me, Ansible decided that any "{{ scalar }}"
expression can return only string or boolean. For example,
- set_fact:
distromajor: "{{ ansible_facts['distribution_major_version']|int }}"
- debug:
var: distromajor
- debug:
msg: "{{ distromajor|type_debug }}"
gives a string despite the explicit conversion to integer, as you've already found out
distromajor: '20'
msg: AnsibleUnsafeText
Note: In the above example, scalar represents a scalar result of the Jinja expression. If there is no expression Ansible doesn't call Jinja. As a result, this will preserve the evaluated variable's type. For example,
- set_fact:
v2: "{{ v1 }}"
vars:
v1: 123
- debug:
msg: |
v2: {{ v2 }}
v2 is {{ v2 | type_debug }}
gives
msg: |-
v2: 123
v2 is int
However, if there is any expression, even an arithmetic one, Ansible calls Jinja to evaluate the expression and the result will be a string
- set_fact:
v3: "{{ v1 + 1 }}"
vars:
v1: 123
- debug:
msg: |
v3: {{ v3 }}
v3 is {{ v3 | type_debug }}
gives
msg: |-
v3: 124
v3 is str
Update.
If you want to keep a variable as integer put it into a dictionary. For example,
my_dict_yaml: |
distromajor: {{ ansible_distribution_major_version }}
my_dict: "{{ my_dict_yaml|from_yaml }}"
gives
my_dict:
distromajor: 20
my_dict.distromajor|type_debug: int
Example of a complete playbook for testing
- hosts: localhost
vars:
my_dict_yaml: |
distromajor: {{ ansible_distribution_major_version }}
my_dict: "{{ my_dict_yaml|from_yaml }}"
tasks:
- setup:
gather_subset: distribution_major_version
- debug:
var: ansible_distribution_major_version
- debug:
var: ansible_distribution_major_version|type_debug
- debug:
var: my_dict
- debug:
var: my_dict.distromajor|type_debug