Determining distribution specific facts/paths/etc

I’ve found myself writing a lot of tasks like:

  • name: “RedHat: Set facts”
    set_fact: path_lib_security=“/usr/lib64/security”
    when: ansible_os_family == ‘RedHat’ or ansible_os_family == ‘SHMZ’

  • name: “Debian: Set facts”
    set_fact: path_lib_security=“/lib/x86_64-linux-gnu/security”
    when: ansible_os_family == ‘Debian’

  • name: “EL8+: Set facts”
    set_fact: pam_kwallet=“pam_kwallet5.so”
    when: ansible_distribution_major_version|int >= 8

  • name: “Not EL8+: Set facts”
    set_fact: pam_kwallet=“pam_kwallet.so”
    when: ansible_distribution_major_version|int < 8
    tags: always

  • name: “Check if pam_kwallet is installed”
    stat:
    path: “{{ path_lib_security }}/{{ pam_kwallet }}”
    register: kwallet

Lately, I’ve started to think that this would be better served by a lookup plugin, e.g. (this is very crude and a different set of facts):

python 3 headers, required if submitting to Ansible

from future import (absolute_import, division, print_function)
metaclass = type

DOCUMENTATION = “”"
lookup: distro_facts
author: Orion Poplawski <orion@nwra.com
version_added: “0.1”
short_description: Lookup distribution dependent facts
description:

  • Return distribution dependent facts
    options:
    _terms:
    description: Fact name
    required: True
    notes:
  • Need to have run setup
    “”"
    from ansible.errors import AnsibleError, AnsibleParserError
    from ansible.module_utils._text import to_native, to_text
    from ansible.plugins.lookup import LookupBase
    from ansible.utils.display import Display

display = Display()

FACTS = {
‘polkit_user’: {
‘RedHat’: ‘polkitd’,
‘Debian’: ‘root’,
},
‘polkit_rules_dir’: {
‘RedHat’: ‘/etc/polkit-1/rules.d’,
‘Debian’: ‘/etc/polkit-1/localauthority/20-org.d’,
},
}

class LookupModule(LookupBase):

def run(self, terms, variables=None, **kwargs):

lookups in general are expected to both take a list as input and output a list

this is done so they work with the looping construct ‘with_’.

ret =
for term in terms:

ret.append(FACTS[term][variables[‘ansible_facts’][‘os_family’]])

return ret

and thus:

  • name: “Configure polkit admins”
    copy:
    src: 05-libvirt-admin.rules
    dest: “{{ lookup(‘distro_facts’, ‘polkit_rules_dir’) }}/”
    owner: “{{ lookup(‘distro_facts’, ‘polkit_user’) }}”
    mode: 0600

I’m curious to get other people’s perspective on this. Perhaps there are already implementations of this out there? Would anyone like to collaborate on this? Is there a community collection where this should live? Other better approaches?

Thanks,
Orion

I've found myself writing a lot of tasks like:

- name:"RedHat: Set facts"
set_fact:path_lib_security="/usr/lib64/security"
when:ansible_os_family == 'RedHat'or ansible_os_family == 'SHMZ'

- name:"Debian: Set facts"
set_fact:path_lib_security="/lib/x86_64-linux-gnu/security"
when:ansible_os_family == 'Debian'

- name:"EL8+: Set facts"
set_fact:pam_kwallet="pam_kwallet5.so"
when:ansible_distribution_major_version|int >= 8

- name:"Not EL8+: Set facts"
set_fact:pam_kwallet="pam_kwallet.so"
when:ansible_distribution_major_version|int < 8
tags:always

- name:"Check if pam_kwallet is installed"
stat:
path:"{{ path_lib_security }}/{{ pam_kwallet }}"
register:kwallet

Lately, I've started to think that this would be better served by a lookup plugin, e.g. (this is very crude and a different set of facts):

I don't think the lookup plugin makes it any easier or user friendly. The common approach is to put the OS dependent variables in separate files and use

include_vars, for example:

- name: Include variables depending on OS details
include_vars: "{{ item }}"
with_first_found:
- files:
- "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml"
- "{{ ansible_distribution }}.yml"
- "{{ ansible_os_family }}.yml"
- "defaults.yml"

The disadvantage of this approach that you need to repeat the values from Debian.yml in Debian-11.yml.

Regards

          Racke

yeah, I do that as well. But I don't like having to repeat that in multiple
roles.

Lookup filters run on the Ansible controller, not the remote host.

Splitting the data across multiple files doesn’t improve readability either.

This is somewhat easier to read. At least you don’t have to discern if or how multiple tasks are related, because this is only one task.