loopsansiblejinja2zabbixzabbix-api

Ansible: Create sublist from list using "startswith" comparison


I have system (Zabbix) that uses crude group/subgroup definition based on "/" delimiter which are defined in plain list.

For example: "Grp(1)" "Grp(1)/Subgrp(A)" "Grp(1)/Subgrp(B)" "Grp(2)" "Grp(2)/Subgrp(X)"

This defines two groups, Grp(1) with two subgroups (A and B) and Grp(2) with one subgroup (X)

If I logically assign user to "Grp(1)" it is expected that user also automatically have right to "Grp(1)/Subgrp(A)" and "Grp(1)/Subgrp(B)"

Example vars file looks like:

---
groups_vars:
  - "Grp(1)"
  - "Grp(1)/Subgrp(A)"
  - "Grp(1)/Subgrp(B)"
  - "Grp(2)"
  - "Grp(2)/Subgrp(X)"

The vars are used in ansible galaxy module community.zabbix, there is simplified usage for Grp(1):

- name: Ensure user groups are created and right to itself and subgroups are assigned
  community.zabbix.zabbix_usergroup:
    name: Grp(1)
    rights:
      - {host_group: ["Grp(1)","Grp(1)/Subgrp(A)","Grp(1)/Subgrp(B)"], permission: "read-write" }

I have tried to achieve "set_fact" transformation of input vars into format more suitable for ansible loop:

--- 
groups_vars: 
  - 
    name: Grp(1)
    rights: 
      host_group: 
        - Grp(1)
        - Grp(1)/Subgrp(A)
        - Grp(1)/Subgrp(B)
      permission: read-write
  - 
    name: Grp(1)/Subgrp(A)
    rights: 
      host_group: 
        - Grp(1)/Subgrp(A)
      permission: read-write
  - 
    name: Grp(1)/Subgrp(B)
    rights: 
      host_group: 
        - Grp(1)/Subgrp(B)
      permission: read-write
  - 
    name: Grp(2)
    rights: 
      host_group: 
        - Grp(2)
        - Grp(1)/Subgrp(X)
      permission: read-write
  - 
    name: Grp(2)/Subgrp(X)
    rights: 
      host_group: 
        - Grp(2)/Subgrp(X)
      permission: read-write

But I failed to define the transformation. The select('match', ) function that I try to use for filtering is regex based but itself can contain regex directives (name "Grp(1)" contains parenthesis that are regex directives) and I cannot fing any "startswith" method for finding subgroups. My idea was, that for each group from original group_vars defined above I find all items, that begins with the group name (so for "Grp(2)" I will find "Grp(2)" and "Grp(2)/Subgrp(X)", for "Grp(2)/Subgrp(X)" I will find only "Grp(2)/Subgrp(X)" itself)

Please any ideas how to solve the problem? Maybe my approach is complete wrong, if there is any more elegant solution, please help.


Solution

  • I finally found working approach for the problem.

    I created simple Python script that generates data usable for simple plain ansible loop.

    The inner loop is emulated by generation of complex structure (array of dictionaries)

    There is source YML file:

    ---
    groups_vars:
      - "z_JC(015)"
      - "z_JC(015)/Pisek(022)"
      - "z_HK(055)"
    

    There is trasformed YML file, the inner loop is emulated by array under "rights:"

    usergroups_vars:
    - group: z_JC(015)
      rights:
      - host_group: z_JC(015)
        permission: read-write
      - host_group: z_JC(015)/Pisek(022)
        permission: read-write
    - group: z_JC(015)/Pisek(022)
      rights:
      - host_group: z_JC(015)/Pisek(022)
        permission: read-write
    - group: z_HK(055)
      rights:
      - host_group: z_HK(055)
        permission: read-write
    

    The playbook works simply with trasformed YML file usin simple plain loop:

    - name: Ensure z_ prefixed Zabbix UserGroups are present and linked to eponymous HostGroups and subhostgroups
          community.zabbix.zabbix_usergroup:
            server_url: "{{ static_hostvars.server_url }}"
            login_user: "{{ static_hostvars.login_user }}"
            login_password: "{{ static_hostvars.login_password }}"
            state: "present"
            name: "{{ item.group }}"
            rights: "{{ item.rights }}"
          loop: "{{ usergroups_vars }}"
    

    There is example of the Python transformation script (using pyyaml library):

    import yaml
    
    # Press the green button in the gutter to run the script.
    if __name__ == '__main__':
    
        # Load and parse host group var yaml file
        hostgroups = None
        with open('groups_vars.yaml') as f:
            hostgroups = yaml.load(f, Loader=yaml.FullLoader)
    
        # Create eponymous usergroups for hostgroups with prefix 'z_'
        usergroups = []
        for hostgroup in hostgroups.get('groups_vars'):
            if hostgroup.startswith('z_'):
                usergroups.append(hostgroup)
    
    
        # Find subgroups ut of list og groups delimited by '/'
        # (E.g array ['grp1','grp1/subgrp1'] defined one group 'grp1' and one subgroup 'subgrp1')
        usergrpsubgrps = []
        for onegrp in usergroups:
            # Find subgroups (including the group itself)
            subgrps = []
            for onesubgroup in usergroups:
                if onesubgroup.startswith(onegrp):
                    subgrps.append({'host_group': onesubgroup, 'permission': 'read-write'})
    
            usergrpsubgrps.append({'group': onegrp, 'rights': subgrps})
        out_yaml = yaml.dump({'usergroups_vars' : usergrpsubgrps})
        print(out_yaml)
    
        # Write output yaml to the output ansible vars file
        out_file = open('usergroups_vars.yaml','w')
        out_file.write(out_yaml)
        out_file.close()