Ansible - Select attribute by subsequent looping over a registered dictionary

28 views Asked by At

In my Ansible script i would like to verify whether I have certain packages and repos available using the yum module.

Afterwards I want to filter my the results of that task to get installed packages and repos above a certain version using selectattr within a loop to manipulate my data

But when I filter my data, one query works and one doesnt. I dont see the difference.

Version: Ansible 2.9.25

Detailed Description starts here:

I get my first dict like this:

    - name: 'checks A | check (available) base packages'
      yum:
        list: '{{ item }}'
      with_items: '{{ base_packages_to_install }}'
      register: yum_base_packages_result

and

- name: 'checks B | Verify mongodb repos'
  block:
    - name: 'repolist_checks B | check (available) mongodb repos'
      yum:
        list: mongodb
      register: mongodb_repos_result

Afterwards I filter those lists to get what I am looking for.

This works just fine, I get my intended dataset (installed packages)

    - set_fact:
        yum_list_installed: []

    - name: 'checks A | create list of installed packages'
      set_fact:
        yum_list_installed: '{{ yum_list_installed + item.results | selectattr("yumstate", "match", "installed") | list }}'
      with_items: '{{ yum_base_packages_result.results }}'

However, this doesnt work:

    - set_fact:
        mongodb_repos_version: []

    - name: 'checks B | check list for version lower/greater than ...'
      set_fact:
        mongo_list_version: '{{ mongodb_repos_version+ item.results | selectattr("version", "version", required_version, ">" ) | list }}'
      with_items: '{{ mongodb_repos_result.results }}'

The error messsage says:

    "msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'results'\n\nThe error appears to be in 'path/to/task.yml': line, column, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n    - name: 'checks B | check list for version lower/greater than ...'\n      ^ here\n"

When I run a type debug like this

    - name: 'checks B | Output TYPE mongodb_repos_result'
      debug:
        msg: '{{ mongodb_repos_result| type_debug }}'
        verbosity: 0


    - name: 'checks B | Output TYPE mongodb_repos_result.results'
      debug:
        msg: '{{ mongodb_repos_result.results | type_debug }}'
        verbosity: 0

My problem:

I get dict for the first, and list for the second, which is what I expected. I dont see the difference between check A and check B aside from the initial yum query, where one loops over a list and one asks for mongodb_repos. But this shouldnt make the difference since the data structure that is yielded, is the same (since i get multiple mongodb_repos)

Maybe useful to you, maybe not:

Data structure mongodb_repos_result looks like this:

    "mongodb_repos_result": {
        "changed": false, 
        "failed": false, 
        "results": [
        {
            "arch": "foo", 
            "envra": "foo", 
            "epoch": "foo", 
            "name": "mongodb-repo-foo", 
            "release": "foo", 
            "repo": "repo-name-1", 
            "version": "4.2.0", 
            "yumstate": "available"
        },          
        {
            "arch": "foo", 
            "envra": "foo", 
            "epoch": "foo", 
            "name": "mongodb-repo-foo", 
            "release": "foo", 
            "repo": "repo-name-2", 
            "version": "6.0.0", 
            "yumstate": "available"
        }
]
}

And the data structure mongodb_repos_result.results looks of course like this:

    "mongodb_repos_result.results": [
        {
            "arch": "foo", 
            "envra": "foo", 
            "epoch": "foo", 
            "name": "mongodb-repo-foo", 
            "release": "foo", 
            "repo": "repo-name-1", 
            "version": "4.2.0", 
            "yumstate": "available"
        },          
        {
            "arch": "foo", 
            "envra": "foo", 
            "epoch": "foo", 
            "name": "mongodb-repo-foo", 
            "release": "foo", 
            "repo": "repo-name-2", 
            "version": "6.0.0", 
            "yumstate": "available"
        }
]
1

There are 1 answers

2
larsks On

Your first task is using a loop, which means that register behaves differently than it does when used in a non-looping task. Specifically, when used in a loop your registered variable will have a key results containing a list of results from each task execution. That gets you:

{
  "yum_base_packages_result": {
    "results": [
      {
        "results": [...]
      },
      {
        "results": [...]
      },
      {
        "results": [...]
      }
    ]
  }
}

When used in a non-looping task, the results are directly accessible in the registered variable:

{
"mongodb_repos_results": {
    "results": [...]
  }
}

One solution is to rewrite the mongodb task to use a loop:

- name: 'repolist_checks B | check (available) mongodb repos'
  yum:
    list: "{{ item }}"
  with_items:
  - mongodb
  register: mongodb_repos_result

Now both variables will have the same structure.