In Ch.9 LabEx.1 "system-software" we use the module ansible.builtin.package_facts to get the local Ansible facts about installed packages. We access those through ansible_facts, which is a variable which apparently is a list of lists. The exercise specifies to print it with:
%<---
- name: Print fact again
ansible.builtin.debug:
var: ansible_facts['packages'][custom_pkg]
when: custom_pkg in ansible_facts['packages']
--->%
Here custom_pkg is a variable that we set ourselves in the playbook (as "simple-agent") hence it is not a literal string and we have to use the unquoted name in the reference within ansible_facts.
The debug statement prints:
%<---
TASK [Print fact again] ********************************************************
ok: [servera.lab.example.com] => {
"ansible_facts['packages'][custom_pkg]": [
{
"arch": "x86_64",
"epoch": null,
"name": "simple-agent",
"release": "1.el9",
"source": "rpm",
"version": "1.0"
}
]
}
--->%
So this is a list item that is a dictionary.
We are only interested in the "version", so we should be able to make debug print only the version.
I tried:
%<---
- name: Print fact again
ansible.builtin.debug:
var: ansible_facts['packages'][custom_pkg]['version']
when: custom_pkg in ansible_facts['packages']
--->%
But that gives an error:
TASK [Print fact again] ********************************************************
ok: [servera.lab.example.com] => {
"ansible_facts['packages'][custom_pkg]['version']": "VARIABLE IS NOT DEFINED!"
}
Which makes some sense since 'version' is a dictionary variable and not a list item so the [ ] list syntax should not be used.
I do know this article about lists and dictionaries but it does not appear to clarify how to handle complex mixed variables like ansible_facts:
How to work with lists and dictionaries in Ansible | Enable Sysadmin (redhat.com)
So my question is:
what is the proper syntax to address a dictionary variable within a list?
Right, thanx, that works.
BTW, [0]['version'] also works, which is an alternative notation for [0].version .
But why?
custom_pkg refers to a single dictionary, as is shown by the first debug statement. So why do we need to explicitly specify entry [0] in the list?
Also I find it odd that if the inline syntax for lists is x: [a, b] and for dictionaries it is { x: a, y: b},
then when we expand them we have to use for both the same syntax with like ['x']['y'] or x.y .
@TPeters Yes both notation works :
Even though it may seem custom_pkg a single dictionary , it actually is a list containing one dictionary :
Refer ansible_mounts for your reference : list of multiple dictionary items
@TPeters -
You are correct in that the custom_pkg is referring to the single dictionary, however, the data type returned is a list of dictionaries. Unfortunately, the example in the book, you are using a "when" statement to essentially loop through the values and only display the relevant information you are looking for (in this case custom_pkg).
The package returned is from a list of packages and contains a dictionary full of additional information. @Chetan_Tiwary_ gave a great example with the Ansible Mounts and facts, but I also have an example that can be shared which hopefully provides more context.
Unfortunately, it can get confusing when working with Ansible as everything is lists or dictionaries or lists of dictionaries and figuring out how to access some of the values that are returned can be difficult. The playbook linked above shows me grabbing the package information and setting it as a variable at the end. What is dumped to the screen is a waste (in most cases when there is a large number of packages) but it shows how things iterate through the loop and how you are actually getting a list of dictionaries back.
If you look at the module documentation it does return a dictionary (https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_facts_module.html) with a key called packages. But if you look closely at the description, it states "Maps the package name to a non-empty list of dicts with package information." So even though you only have the single "element" that you are wanting to get, the [0] is needed.
If you look at the playbook I have linked and observe the output, you can see where a list of dictionaries is spit out to the screen when using the debug module and only dumping what is in the variable. The use of the "when" statement isolates it to a single dictionary item out of the list.
Going down further in the playbook, you can see me setting a fact and looping over that value. In order to do that with the debug and msg I need to process it as a list. The last task in the playbook, I am checking to see when my_package is matching at the current package in the loop and printing it. Again, unfortunately, when using the debug module you can see each item iterate through the list and be skipped until it matches. That is good for understanding, but takes a while if you have a lot of packages on your system, so you need to be able to scroll in the window to truly look at the output.
Hope this helps ...
Very useful resources in your repo @Travis , thank you for sharing it !
Travis, thank you for going through the effort to show how this is working. It will be useful in my future work.
@Trevor_Chandler - Another interesting topic for your classes.
@TPeters -
You're welcome. Actually thank you for the question. I went ahead and created one more item here for this post and I will cross-post to your other variables question.
https://github.com/tmichett/AnsiblePlaybooks/tree/master/Vars
Here is a simple playbook to show some variables and how to get values out of it as well as determine what type of variable it is. Sometimes I have a hard time reading JSON, so this is very useful on determining whether Ansible is seeing a list or a dictionary based on the variable passed.
Red Hat
Learning Community
A collaborative learning environment, enabling open source skill development.