I’m including is quite complex, has lots of set_facts and always works with couple of network hosts defined (HYKNET01,HYKNET02). I’m noticing quite strange behaviour with set_fact modules inside network_port_channel.yml. Its hard to explain, but seems when using combination of include_tasks and loop, Ansible processes the facts from other iterations, while current iteration doesn’t have that fact. I don’t understand if this somekind a bug or something. I saw some people has problems with set_fact and include_tasks, but didn’t saw exact my case. Maybe someone has some odd behaviour like i’m trying to describe ? When running the mentioned tasks separately - without looping with the same arguments everything works good.
I did find a solution for that - meta: clear_facts, but that still doesn’t clear all facts which are generated by previous.
I think i’ve tried everything, and i don’t see a way to make this loop work without preserving any set_facts from previous iterations. I think the workaround for that would be to use some kind of separate CI/CD tool to run standalone playbook ansible_various/port_channel_2ints.yml at main · edvinaskairys/ansible_various · GitHub separately everytime…
First, facts are HOST scoped and not the same thing as vars which are play object scoped (play/role/block/task), so they will be available for the host they were defined in or via hostvars['hostdefinedin'], include_tasks in a loop will not restrict playbook vars scopes too much, it is mostly the same as including the file N times (once per loop item) in succession and a block scoping the ‘loop var’. You also have to take into account that you are running 2 loops, one per host and in each per host loop the include loop and ansible does parallelization by default, you don’t always have the results of another host’s task unless you can guarantee the other host ran first (see serial keyword).
thanks, whats my options here ? I’ve tried to unset the facts at the top of the playbook, but seems it’s not possible to completely delete the facts, i can convert them to null but on even it’s null - it hits the when statement.
ofcourse i can edit my when statements, but the code gets even more dirty…
You should not need to ‘unset’ facts that were not set for the current host, but Ansible allows for a variable in the ‘current’ context to come from both scopes, so it might not be an issue with facts but other variables set with the same name. so depending on how you define and/or reference a variable you might get one or the other.
Thanks, so do i understand correctly that i should write a playbook where i wouldn’t need to use include_task with loop like here. But honestly i don’t know if that’s possible. I’m thinking of trying to use Anbile AWX webhook, if that will not help, then maybe some CI/CD tool
No, that is not what I’m trying to say, the issue you are describing is not how facts should work and I suspect you are masking the facts with other types of variables. This should not be a problem with include_tasks itself nor set_fact.
I take it you want facts defined in network_port_channel.yml to be local to that file. If that file were a role it would be a special case of a problem I attempted to solve very recently (see my post on the topic), so I have a suggestion.
While you can refactor to a role and use my collection, I wouldn’t bet on it being production ready. Instead, since I believe you are in control of all the code, you can set a dictionary as a fact at the start of each iteration, and set any local facts on that dictionary fact.
--- # network_port_channel.yml
- name: reset the fact to serve as a local namespace
ansible.builtin.set_fact:
local: {}
- name: set `my_fact` key on `local`
ansible.builtin.set_fact:
local: "{{ local | combine({ 'my_fact': my_value }) }}"
- name: use the local fact
ansible.builtin.debug:
var: local.my_fact
- ...
My suggestion at the bottom of my previous reaction should allow resetting your facts on each iteration. (No knowledge of internals necessary, local is just a name, you could call it anything.)
sorry, i deleted the reply it was meant to this topic:
Thank you, Pieter
Looks nice, but for me the solution you provided, looks too complex, because i’m not that into Ansible internals to understand fully what that does:)
so i’m trying to stick to the easiest workaround i think, is try to set all important facts to null, and then will try to modify the when clauses.
You mean i can just add this:
--- # network_port_channel.yml
- name: reset the fact to serve as a local namespace
ansible.builtin.set_fact:
local: {}
- name: set `my_fact` key on `local`
ansible.builtin.set_fact:
local: "{{ local | combine({ 'my_fact': my_value }) }}"
- name: use the local fact
ansible.builtin.debug:
var: local.my_fact
at the end of the playbook and it will reset the facts ?
The problem i see, that if i will set those facts, then i will need accordingly tune when statements to be able to access the facts, which will take more effort than to just clear the set_facts and then add some when statements which will compare the lenght of the set_facts…