diff --git a/changelogs/fragments/pr_1238.yml b/changelogs/fragments/pr_1238.yml new file mode 100644 index 000000000..dfefe200e --- /dev/null +++ b/changelogs/fragments/pr_1238.yml @@ -0,0 +1,2 @@ +minor_changes: + - zabbix_host_events_update module added diff --git a/plugins/modules/zabbix_host_events_update.py b/plugins/modules/zabbix_host_events_update.py new file mode 100644 index 000000000..ec0025467 --- /dev/null +++ b/plugins/modules/zabbix_host_events_update.py @@ -0,0 +1,244 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +--- +module: zabbix_host_events_update +short_description: update the status of event(s). +description: + - Updates the status of event(s). +author: + - "Andrew Lathrop (@aplathrop)" +requirements: + - "python >= 2.6" + +options: + params: + description: + - Parameters to update event(s) with. + - Parameters as defined at https://www.zabbix.com/documentation/current/en/manual/api/reference/event/acknowledge + - Additionally supported parameters are below + required: true + type: dict + suboptions: + action: + description: + - action to update the event with + - Overrides "action" in API docs + - Required when I(actions) is not used. + - Mutually exclusive with I(actions). + required: false + type: str + choices: + - close_problem + - close + - acknowledge_event + - acknowledge + - ack + - add_message + - message + - msg + - change_severity + - severity + - unacknowledge_event + - unacknowledge + - unack + - suppress_event + - suppress + - unsuppress_event + - unsuppress + - change_event_rank_to_cause + - convert_to_cause + - change_event_rank_to_symptom + - convert_to_symptom + actions: + description: + - actions to update the event with + - Overrides "action" in API docs + - Required when I(action) is not used. + - Mutually exclusive with I(action). + required: false + type: list + elements: str + choices: + - close_problem + - close + - acknowledge_event + - acknowledge + - ack + - add_message + - message + - msg + - change_severity + - severity + - unacknowledge_event + - unacknowledge + - unack + - suppress_event + - suppress + - unsuppress_event + - unsuppress + - change_event_rank_to_cause + - convert_to_cause + - change_event_rank_to_symptom + - convert_to_symptom + severity: + description: + - New severity for events. + - Overrides "severity" in API docs + required: False + type: str + choices: + - not_classified + - information + - warning + - average + - high + - disaster + msg: + description: + - Text of the message. + - Alias for "message" in API docs + required: False + type: str + +extends_documentation_fragment: +- community.zabbix.zabbix +''' + +EXAMPLES = r''' + +# If you want to use Username and Password to be authenticated by Zabbix Server +- name: Set credentials to access Zabbix Server API + ansible.builtin.set_fact: + ansible_user: Admin + ansible_httpapi_pass: zabbix + +# If you want to use API token to be authenticated by Zabbix Server +# https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens +- name: Set API token + ansible.builtin.set_fact: + ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 + +# Acknowledge single event +- name: ack event + community.zabbix.zabbix_host_events_update: + params: + eventids: 12345 + actions: ack + +- name: ack and close event with a message + community.zabbix.zabbix_host_events_update: + params: + eventids: [12345, 67890] + actions: ['ack', 'msg', 'close'] + msg: 'closed by user' + +''' + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase +import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils + + +class Hosteventsupdate(ZabbixBase): + ACTIONS = {'close_problem': 1, + 'close': 1, + 'acknowledge_event': 2, + 'acknowledge': 2, + 'ack': 2, + 'add_message': 4, + 'message': 4, + 'msg': 4, + 'change_severity': 8, + 'severity': 8, + 'unacknowledge_event': 16, + 'unacknowledge': 16, + 'unack': 16, + 'suppress_event': 32, + 'suppress': 32, + 'unsuppress_event': 64, + 'unsuppress': 64, + 'change_event_rank_to_cause': 128, + 'convert_to_cause': 128, + 'change_event_rank_to_symptom': 256, + 'convert_to_symptom': 256} + + SEVERITY_TYPES = {'not_classified': 0, + 'information': 1, + 'warning': 2, + 'average': 3, + 'high': 4, + 'disaster': 5} + + def get_events(self, eventids): + try: + results = self._zapi.event.get({'eventids': eventids}) + except Exception as e: + self._module.fail_json(msg="Failed to get event: %s" % e) + return results + + def update_event(self, params): + if 'severity' in params: + if params['severity'] not in self.SEVERITY_TYPES: + self._module.fail_json(msg="%s is not a valid severity type" % params['severity']) + severity = self.SEVERITY_TYPES[params['severity']] + params['severity'] = severity + if 'action' in params: + if params['action'] not in self.ACTIONS: + self._module.fail_json(msg="%s is not a valid action" % params['action']) + action_id = self.ACTIONS[params['action']] + elif 'actions' in params: + action_id = 0 + for action in params['actions']: + if action not in self.ACTIONS: + self._module.fail_json(msg="%s is not a valid action" % action) + action_id += self.ACTIONS[action] + params.pop('actions') + else: + self._module.fail_json(msg="params must contain either 'action' or 'actions'") + params['action'] = action_id + if 'msg' in params: + params['message'] = params['msg'] + params.pop('msg') + if self._module.check_mode: + self._module.exit_json(changed=True) + try: + results = self._zapi.event.acknowledge(params) + except Exception as e: + self._module.fail_json(msg="Failed to update event: %s" % e) + return results + + def check_events_changed(self, eventids, old_events): + new_events = self.get_events(eventids) + return old_events != new_events + + +def main(): + argument_spec = zabbix_utils.zabbix_common_argument_spec() + argument_spec.update( + params=dict(type='dict', required=True)) + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + + params = module.params['params'] + + hosteventsupdate = Hosteventsupdate(module) + + events = hosteventsupdate.get_events(params['eventids']) + results = hosteventsupdate.update_event(params) + changed = hosteventsupdate.check_events_changed(params['eventids'], events) + module.exit_json(changed=changed, result=results) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/test_zabbix_host_events_update/meta/main.yml b/tests/integration/targets/test_zabbix_host_events_update/meta/main.yml new file mode 100644 index 000000000..acdb704c8 --- /dev/null +++ b/tests/integration/targets/test_zabbix_host_events_update/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_zabbix diff --git a/tests/integration/targets/test_zabbix_host_events_update/tasks/main.yml b/tests/integration/targets/test_zabbix_host_events_update/tasks/main.yml new file mode 100644 index 000000000..131e87ac9 --- /dev/null +++ b/tests/integration/targets/test_zabbix_host_events_update/tasks/main.yml @@ -0,0 +1,13 @@ +--- +- name: test - do not run tests for Zabbix < 6.4 + meta: end_play + when: zabbix_version is version('6.4', '<') + +# setup stuff +- include_tasks: zabbix_setup.yml + +# zabbix_item module tests +- include_tasks: zabbix_tests.yml + +# tear down stuff set up earlier +- include_tasks: zabbix_teardown.yml diff --git a/tests/integration/targets/test_zabbix_host_events_update/tasks/zabbix_setup.yml b/tests/integration/targets/test_zabbix_host_events_update/tasks/zabbix_setup.yml new file mode 100644 index 000000000..c02a08595 --- /dev/null +++ b/tests/integration/targets/test_zabbix_host_events_update/tasks/zabbix_setup.yml @@ -0,0 +1,61 @@ +--- + +- name: Create example template + community.zabbix.zabbix_template: + template_name: ExampleTemplate + template_groups: + - Templates + +- name: Create example host + community.zabbix.zabbix_host: + host_name: ExampleHost + host_groups: + - Linux servers + - Zabbix servers + link_templates: + - ExampleTemplate + status: enabled + state: present + interfaces: + - type: 1 + main: 1 + useip: 1 + ip: 10.1.1.1 + dns: "" + port: "10050" + +- name: create ping item + community.zabbix.zabbix_item: + name: ping + template_name: ExampleTemplate + params: + type: zabbix_agent_active + key: agent.ping + value_type: numeric_unsigned + interval: 20s + state: present + +- name: create ping trigger + community.zabbix.zabbix_trigger: + name: ping + template_name: ExampleTemplate + params: + severity: warning + expression: 'nodata(/ExampleTemplate/agent.ping,1m)=1' + manual_close: True + state: present + +- name: Wait to ensure triggers are firing + ansible.builtin.wait_for: + timeout: 120 + +- name: get events for host + community.zabbix.zabbix_host_events_info: + host_identifier: ExampleHost + host_id_type: hostname + trigger_severity: warning + register: zabbix_host_events + +- name: get eventid + ansible.builtin.set_fact: + zabbix_eventid: "{{ zabbix_host_events.triggers_problem[0].last_event.eventid }}" diff --git a/tests/integration/targets/test_zabbix_host_events_update/tasks/zabbix_teardown.yml b/tests/integration/targets/test_zabbix_host_events_update/tasks/zabbix_teardown.yml new file mode 100644 index 000000000..2df4dd571 --- /dev/null +++ b/tests/integration/targets/test_zabbix_host_events_update/tasks/zabbix_teardown.yml @@ -0,0 +1,9 @@ +- name: remove example host + community.zabbix.zabbix_host: + host_name: ExampleHost + state: absent + +- name: remove example template + community.zabbix.zabbix_template: + template_name: ExampleTemplate + state: absent diff --git a/tests/integration/targets/test_zabbix_host_events_update/tasks/zabbix_tests.yml b/tests/integration/targets/test_zabbix_host_events_update/tasks/zabbix_tests.yml new file mode 100644 index 000000000..48d63b47d --- /dev/null +++ b/tests/integration/targets/test_zabbix_host_events_update/tasks/zabbix_tests.yml @@ -0,0 +1,59 @@ +--- + +- name: test - acknowledge event + community.zabbix.zabbix_host_events_update: + params: + eventids: "{{ zabbix_eventid }}" + action: ack + msg: "event acknowledged" + register: zbxevent_ack + +- name: assert that event was changed + ansible.builtin.assert: + that: zbxevent_ack is changed + +- name: get events updated status + community.zabbix.zabbix_host_events_info: + host_identifier: ExampleHost + host_id_type: hostname + trigger_severity: warning + register: zabbix_host_events_ack + +- name: assert that event was acknowledged + ansible.builtin.assert: + that: zabbix_host_events_ack.triggers_problem[0].last_event.acknowledged + +- name: test - change severity and unacknowledge + community.zabbix.zabbix_host_events_update: + params: + eventids: "{{ zabbix_eventid }}" + actions: ['severity', 'unack'] + severity: high + register: zbxevent_sev + +- name: assert that event was changed + ansible.builtin.assert: + that: zbxevent_sev is changed + +- name: get events updated status + community.zabbix.zabbix_host_events_info: + host_identifier: ExampleHost + host_id_type: hostname + trigger_severity: warning + register: zabbix_host_events_unack + +- name: assert that event was unacknowledged + ansible.builtin.assert: + that: zabbix_host_events_unack.triggers_problem[0].last_event.acknowledged == "0" + +- name: test - change severity to same + community.zabbix.zabbix_host_events_update: + params: + eventids: "{{ zabbix_eventid }}" + action: severity + severity: high + register: zbxevent_sev_existing + +- name: assert that event was not changed + ansible.builtin.assert: + that: zbxevent_sev_existing is not changed