1

I have an Ansible role that manipulates some Zabbix components. This role has this set of tasks to check the Zabbix Agent type (1 or 2) and version.

- name: Check if Zabbix Agent 2 is installed
  ansible.builtin.shell:
    cmd: which zabbix_agent2
  register: zabbix_agent2_installed
  ignore_errors: yes

- name: Check if Zabbix Agent D is installed
  ansible.builtin.shell:
    cmd: which zabbix_agentd
  when: not zabbix_agent2_installed.stdout
  register: zabbix_agentd_installed
  ignore_errors: yes

- name: Stop if there is no Agent installed
  ansible.builtin.fail:
    msg: |
      Zabbix Agent is not installed.
  when: not zabbix_agent2_installed.stdout and not zabbix_agentd_installed.stdout and zabbix_component == "agent"

- name: Fetch Zabbix Agent 2 version
  ansible.builtin.shell:
    cmd: zabbix_agent2 -V | grep "zabbix_agent" | cut -d' ' -f3
  when: zabbix_agent2_installed.stdout
  register: zabbix_agent_ver_old
  failed_when: zabbix_agent_ver_old.stderr

- name: Fetch Zabbix Agent D version
  ansible.builtin.shell:
    cmd: zabbix_agentd -V | grep "zabbix_agent" | cut -d' ' -f4
  when: not zabbix_agentd_installed.skipped
  register: zabbix_agent_ver_old
  failed_when: zabbix_agent_ver_old.stderr

When I run the playbook, the variable "zabbix_agentd_ver_old" is registered twice in the last two tasks, with the same name. Even if the last task is skipped, the variable is registered as skipped.

TASK [zabbix_ansible : Check if Zabbix Agent 2 is installed] *********************************************************************
changed: [zbxproxy]

TASK [zabbix_ansible : Check if Zabbix Agent D is installed] *********************************************************************
skipping: [zbxproxy]

TASK [zabbix_ansible : Stop if there is no Agent installed] **********************************************************************
skipping: [zbxproxy]

TASK [zabbix_ansible : Fetch Zabbix Agent 2 version] *****************************************************************************
changed: [zbxproxy]

TASK [zabbix_ansible : Fetch Zabbix Agent D version] *******************************************************************************
skipping: [zbxproxy]

TASK [zabbix_ansible : ansible.builtin.debug] ************************************************************************************
ok: [zbxproxy] =>
  zabbix_agent_ver_old:
    changed: false
    skip_reason: Conditional result was False
    skipped: true

I want to avoid changing the variable name, so I need the variable to be registered in either one of the last two tasks, not both.

How can I skip the last task altogether, including the registration, if its condition is not met?

2 Answers 2

0

This is a typical case for custom facts. Create dynamic facts about Zabbix by adding executable script zabbix.fact to /etc/ansible/facts.d on the remote hosts.

The below script tests the existence of the executable files in the list zabbix_agent_list and returns JSON dictionary ansible_custom_zabix_agent with the comma-separated items that exist

shell> cat zabbix.fact
#!/bin/sh

zabbix_agent_list="/usr/local/bin/zabbix_agentd \
                   /usr/local/bin/zabbix_agent2"

zabbix_agent=""
for a in $zabbix_agent_list; do
    if [ -x $a ]; then
        zabbix_agent="$zabbix_agent $a"
    fi
done

zabbix_agent=`echo $zabbix_agent | tr ' ' ','`
echo '{"'ansible_custom_zabix_agent'"': '"'$zabbix_agent'"}'

Distribute this script to the remote hosts you want to provide this custom fact

- hosts: all

  tasks:

    - file:
        state: directory
        dest: /etc/ansible/facts.d

    - copy:
        src: zabbix.fact
        dest: /etc/ansible/facts.d
        mode: '0755'

Test it. Given the remote hosts test_11 and test_13

shell> ssh admin@test_11 ls -1 /usr/local/bin/zabbix_agent2
/usr/local/bin/zabbix_agent2
shell> ssh admin@test_13 ls -1 /usr/local/bin/zabbix_agentd
/usr/local/bin/zabbix_agentd

The below play

- hosts: all

  vars:

    zabbix_agent: "{{ ansible_facts.ansible_local.zabbix.ansible_custom_zabix_agent|
                      default('')|
                      split(',')|
                      first }}"
  tasks:

    - setup:
        gather_subset: local
    - debug:
        var: ansible_facts.ansible_local.zabbix.ansible_custom_zabix_agent
    - debug:
        var: zabbix_agent

gives (abridged)

ok: [test_11] => 
  zabbix_agent: /usr/local/bin/zabbix_agent2
ok: [test_13] => 
  zabbix_agent: /usr/local/bin/zabbix_agentd

Notes:

  • By default the facts will be gathered per play. In this case, you don't have to run the task setup.

  • If you disable the gathering of the facts, or if you want to refresh the facts run the task setup

  • Speed up the fact gathering by limiting to the local facts. Set gather_subset: local

  • The variable ansible_custom_zabix_agent will be appended to the dictionary ansible_facts.ansible_local.zabbix.ansible_custom_zabix_agent

    • ansible_local: local facts (gather_subset: local)

    • zabbix: facts collected by zabbix.fact

    • ansible_custom_zabix_agent: comma-separated list of agents

  • The variable ansible_custom_zabix_agent, in the above play, might be a list of comma-separated items if more items from the list zabbix_agent_list exist. In this case, split the string into a list and take the first item.

  • You can add other functionality to the script and return other facts.

  • Use can use any other language, for example, Python, to write the scripts.


Example of Python script

shell> cat /etc/ansible/facts.d/apache.fact
#!/usr/bin/env python3

import shutil
import subprocess
import yaml
import json


def main():

    name = 'httpd'

    path = shutil.which(name)
    if path:
        result = subprocess.run([name, '-v'], capture_output=True, text=True)
        version = yaml.safe_load(result.stdout)
    else:
        version = None

    ansible_custom_facts = {
        'path': path,
        'version': version
    }

    print(json.dumps(ansible_custom_facts))


if __name__ == '__main__':
    main()

gives for example,

  ansible_facts.ansible_local:
    apache:
      path: /usr/local/sbin/httpd
      version:
        Server built: unknown
        Server version: Apache/2.4.46 (FreeBSD)
1

Multiple tasks branching on slight variations is annoying to write in Ansible, and runs slowly due to task overhead. Sometimes this can be collapsed into one script with variables. Here's my quick and dirty attempt with a few lines of shell. Disclaimer: I have not tested it with zabbix, although the general concepts are not difficult.

  - name: Fetch Zabbix Agent version
    register: zabbix_agent_ver_result
    shell:
      # Note use of YAML multiple line to embed script  http://yaml-multiline.info/
      # Although much longer should probably go in a separate file with and a script task
      cmd: |
        WHICHZAGENT=$(which zabbix_agent2 || which zabbix_agentd );
        case $WHICHZAGENT in
          *agent2 )
               VERFIELD=3
              ;;
          *agentd )
               VERFIELD=4
              ;;
        esac;
        ${WHICHZAGENT}  --version  | grep 'zabbix_agent' | cut -d' ' -f ${VERFIELD};


  - name: zabbix agent ver short name
    set_fact:
      zabbix_agent_ver: "{{ zabbix_agent_ver_result.stdout }}"

  - debug:
      var: zabbix_agent_ver

register: will overwrite a (host scoped) variable that exists of the same name. Its not that skipped tasks don't return anything, they return a skipped status. This is confusing, I recommend all registered task variable names be unique.

Branching flow is not nearly as nice to write in Ansible tasks as it is in many programming languages.


See also: community.zabbix collection has a community.zabbix.zabbix_agent role with a different way of doing things. Install agent package from software repos, then template config file where most of the possible values can come from variables. Very common patterns for Ansible.

Personally I prefer the style of installing software packages that should be there. It can fix missing agents, and you don't have to figure out what is on the host.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .