jsonbashansibleansible-factsjson-query

Grabbing all *el7* packages from Ansible Package facts


We are doing an In-Place Upgrade from RHEL 7 to RHEL 8 in my organization and we've come to find out that not all RHEL 7 packages are removed after an OS Upgrade. I've been asked to report on all RHEL 7 packages leftover on the device. To start I've looked into Ansible Package facts which has a variable that looks something like:

'ansible_facts.packages:{
zlib: [
    {
        "arch": "x86_64",
        "epoch": null,
        "release": "11.el7",
        "source": "rpm",
        "version": "1.2.7"
    },
    {
        "arch": "x86_64",
        "epoch": null,
        "release": "10.el7",
        "source": "rpm",
        "version": "1.2.6"
    },
    {
        "arch": "x86_64",
        "epoch": null,
        "release": "10.el8",
        "source": "rpm",
        "version": "1.2.7"
    }
]
}
    

This occurs for a handful of packages, what I would like to do is to run a json_query against the entire dictionary of package_facts and determine what "el7" packages live on the host. That can tell me what I want to remove. Are there any suggestions on the json_query to run in order to find all packages that have an 'el7' in the release key?

Here's what I've tried so far:

vars:
  example_query1: "[?contains(release, 'el7')]"
  example_query2: "[?release == '*el7*']"
tasks:
  new_package_list: "{{ ansible_facts.packages | json_query(example_query) | list }}"

Solution

  • A quick (and dirty) variant with Ansible and Jinja2 filter techniques is as follows:

    - name: Grab all packages with el7 releases
      set_fact:
        el7_packages: "{{ el7_packages | default({}) | combine(new_item | items2dict) }}"
      with_dict: "{{ ansible_facts.packages }}"
      loop_control:
        label: "{{ item.key }}"
      vars:
        el7_releases: "{{ item.value | map(attribute='release') | select('search', 'el7') }}"
        new_item:
          - key: "{{ item.key }}"
            value: "{{ el7_releases }}"
      when: el7_releases | list | length
    
    - name: Print found el7 packages
      debug:
        var: el7_packages
    
    1. you iterate over each entry of the dict
      with_dict: "{{ ansible_facts.packages }}"

    2. you reduce the list of dicts to the release value and then filter the list by el7
      el7_releases: "{{ item.value | map(attribute='release') | select('search', 'el7') }}"

    3. only if a release with el7 exists, the package with its el7 releases is saved in the target dict el7_packages

      • Condition to ensure the list is not empty (means el7 release exists)
        when: el7_releases | list | length
      • Add new_item to target dict
        set_fact with el7_packages: "{{ el7_packages | default({}) | combine(new_item | items2dict) }}"
    4. the result is a dict, the keys are the package names, the values are lists of release strings containing el7

    Notes:

    Result:

    TASK [Grab all packages with el7 releases] *********************
    ok: [localhost] => (item=zlib)
    ok: [localhost] => (item=other_pkg)
    skipping: [localhost] => (item=an_other_tool)
    
    TASK [Print found el7 packages] ********************************
    ok: [localhost] => {
        "el7_packages": {
            "other_pkg": [
                "11.el7"
            ],
            "zlib": [
                "11.el7",
                "10.el7"
            ]
        }
    }
    

    Edit:

    To get the whole dict as a result, instead of just the release string, only a minimal change of the variable assignment for el7_releases is required:

    el7_releases: "{{ item.value | selectattr('release', 'search', 'el7') }}"

    Code:

    - name: Grab all packages with el7 releases
      set_fact:
        el7_packages: "{{ el7_packages | default({}) | combine(new_item | items2dict) }}"
      with_dict: "{{ ansible_facts.packages }}"
      loop_control:
        label: "{{ item.key }}"
      vars:
        el7_releases: "{{ item.value | selectattr('release', 'search', 'el7') }}"
        new_item:
          - key: "{{ item.key }}"
            value: "{{ el7_releases }}"
      when: el7_releases | list | length
    
    - name: Print found el7 packages
      debug:
        var: el7_packages
    

    Reslut:

    TASK [Grab all packages with el7 releases] ****************************
    ok: [localhost] => (item=zlib)
    ok: [localhost] => (item=other_pkg)
    skipping: [localhost] => (item=an_other_tool)
    
    TASK [Print found el7 packages] ***************************************
    ok: [localhost] => {
        "el7_packages": {
            "other_pkg": [
                {
                    "arch": "x86_64",
                    "epoch": null,
                    "release": "11.el7",
                    "source": "rpm",
                    "version": "1.2.7"
                }
            ],
            "zlib": [
                {
                    "arch": "x86_64",
                    "epoch": null,
                    "release": "11.el7",
                    "source": "rpm",
                    "version": "1.2.7"
                },
                {
                    "arch": "x86_64",
                    "epoch": null,
                    "release": "10.el7",
                    "source": "rpm",
                    "version": "1.2.6"
                }
            ]
        }
    }
    

    example input data:

    packages:
      zlib:
        - arch: "x86_64"
          epoch: null
          release: "11.el7"
          source: "rpm"
          version: "1.2.7"
    
        - arch: "x86_64"
          epoch: null
          release: "10.el7"
          source: "rpm"
          version: "1.2.6"
    
        - arch: "x86_64"
          epoch: null
          release: "10.el8"
          source: "rpm"
          version: "1.2.7"
    
      other_pkg:
        - arch: "x86_64"
          epoch: null
          release: "11.el7"
          source: "rpm"
          version: "1.2.7"
    
        - arch: "x86_64"
          epoch: null
          release: "10.el8"
          source: "rpm"
          version: "1.2.7"
    
      an_other_tool:
        - arch: "x86_64"
          epoch: null
          release: "10.el8"
          source: "rpm"
          version: "1.2.7"