Server IP : 85.214.239.14 / Your IP : 13.59.1.58 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /lib/python3/dist-packages/ansible_collections/community/zabbix/plugins/modules/ |
Upload File : |
#!/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 = ''' --- module: zabbix_action short_description: Create/Delete/Update Zabbix actions description: - This module allows you to create, modify and delete Zabbix actions. author: - Ruben Tsirunyan (@rubentsirunyan) - Ruben Harutyunov (@K-DOT) requirements: - "python >= 2.6" options: name: description: - Name of the action required: true event_source: description: - Type of events that the action will handle. - Required when C(state=present). required: false choices: ['trigger', 'discovery', 'auto_registration', 'internal'] state: description: - State of the action. - On C(present), it will create an action if it does not exist or update the action if the associated data is different. - On C(absent), it will remove the action if it exists. choices: ['present', 'absent'] default: 'present' status: description: - Status of the action. choices: ['enabled', 'disabled'] default: 'enabled' pause_in_maintenance: description: - Whether to pause escalation during maintenance periods or not. - Can be used when I(event_source=trigger). type: 'bool' default: true esc_period: description: - Default operation step duration. Must be greater than 60 seconds. - Accepts only seconds in int for <= Zabbix 3.2 - Accepts seconds, time unit with suffix and user macro since => Zabbix 3.4 - Required when C(state=present). required: false conditions: type: list elements: dict description: - List of conditions to use for filtering results. - For more information about suboptions of this option please check out Zabbix API documentation U(https://www.zabbix.com/documentation/5.0/manual/api/reference/action/object#action_filter_condition) suboptions: type: description: - Type (label) of the condition. - C(application) is available only with <= Zabbix 5.2. - 'Possible values when I(event_source=trigger):' - ' - C(host_group)' - ' - C(host)' - ' - C(trigger)' - ' - C(trigger_name)' - ' - C(trigger_severity)' - ' - C(time_period)' - ' - C(host_template)' - ' - C(application)' - ' - C(maintenance_status) known in Zabbix 4.0 and above as "Problem is suppressed"' - ' - C(event_tag)' - ' - C(event_tag_value)' - 'Possible values when I(event_source=discovery):' - ' - C(host_IP)' - ' - C(discovered_service_type)' - ' - C(discovered_service_port)' - ' - C(discovery_status)' - ' - C(uptime_or_downtime_duration)' - ' - C(received_value)' - ' - C(discovery_rule)' - ' - C(discovery_check)' - ' - C(proxy)' - ' - C(discovery_object)' - 'Possible values when I(event_source=auto_registration):' - ' - C(proxy)' - ' - C(host_name)' - ' - C(host_metadata)' - 'Possible values when I(event_source=internal):' - ' - C(host_group)' - ' - C(host)' - ' - C(host_template)' - ' - C(application)' - ' - C(event_type)' value: description: - Value to compare with. - 'When I(type=discovery_status), the choices are:' - ' - C(up)' - ' - C(down)' - ' - C(discovered)' - ' - C(lost)' - 'When I(type=discovery_object), the choices are:' - ' - C(host)' - ' - C(service)' - 'When I(type=event_type), the choices are:' - ' - C(item in not supported state)' - ' - C(item in normal state)' - ' - C(LLD rule in not supported state)' - ' - C(LLD rule in normal state)' - ' - C(trigger in unknown state)' - ' - C(trigger in normal state)' - 'When I(type=trigger_severity), the choices are (case-insensitive):' - ' - C(not classified)' - ' - C(information)' - ' - C(warning)' - ' - C(average)' - ' - C(high)' - ' - C(disaster)' - Irrespective of user-visible names being changed in Zabbix. Defaults to C(not classified) if omitted. - Besides the above options, this is usually either the name of the object or a string to compare with. value2: description: - Secondary value to compare with. - Required for trigger actions when condition I(type=event_tag_value). operator: description: - Condition operator. - When I(type) is set to C(time_period), the choices are C(in), C(not in). - C(matches), C(does not match), C(Yes) and C(No) condition operators work only with >= Zabbix 4.0 - When I(type) is set to C(maintenance_status), the choices are C(Yes) and C(No) for Zabbix >= 6.0 choices: - C(equals) or C(=) - C(does not equal) or C(<>) - C(contains) or C(like) - C(does not contain) or C(not like) - C(in) - C(is greater than or equals) or C(>=) - C(is less than or equals) or C(<=) - C(not in) - C(matches) - C(does not match) - C(Yes) - C(No) formulaid: description: - Arbitrary unique ID that is used to reference the condition from a custom expression. - Can only contain upper-case letters. - Required for custom expression filters and ignored otherwise. eval_type: description: - Filter condition evaluation method. - Defaults to C(andor) if conditions are less then 2 or if I(formula) is not specified. - Defaults to C(custom_expression) when formula is specified. choices: - 'andor' - 'and' - 'or' - 'custom_expression' formula: description: - User-defined expression to be used for evaluating conditions with a custom expression. - The expression must contain IDs that reference each condition by its formulaid. - The IDs used in the expression must exactly match the ones defined in the I(conditions). No condition can remain unused or omitted. - Required when I(eval_type=custom_expression). - Use sequential IDs that start at "A". If non-sequential IDs are used, Zabbix re-indexes them. This makes each module run notice the difference in IDs and update the action. default_message: description: - Problem message default text. - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - Works only with < Zabbix 5.0 default_subject: description: - Problem message default subject. - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - Works only with < Zabbix 5.0 recovery_default_message: description: - Recovery message text. - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - Works only with >= Zabbix 3.2 and < Zabbix 5.0 recovery_default_subject: description: - Recovery message subject. - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - Works only with >= Zabbix 3.2 and < Zabbix 5.0 acknowledge_default_message: description: - Update operation (known as "Acknowledge operation" before Zabbix 4.0) message text. - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - Works only with >= Zabbix 3.4 and < Zabbix 5.0 acknowledge_default_subject: description: - Update operation (known as "Acknowledge operation" before Zabbix 4.0) message subject. - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - Works only with >= Zabbix 3.4 and < Zabbix 5.0 operations: type: list description: - List of action operations suboptions: type: description: - Type of operation. - 'Valid choices when setting type for I(recovery_operations) and I(acknowledge_operations):' - ' - C(send_message)' - ' - C(remote_command)' - ' - C(notify_all_involved)' - Choice C(notify_all_involved) only supported in I(recovery_operations) and I(acknowledge_operations). choices: - send_message - remote_command - add_host - remove_host - add_to_host_group - remove_from_host_group - link_to_template - unlink_from_template - enable_host - disable_host - set_host_inventory_mode - notify_all_involved esc_period: description: - Duration of an escalation step in seconds. - Must be greater than 60 seconds. - Accepts only seconds in int for <= Zabbix 3.2 - Accepts seconds, time unit with suffix and user macro since => Zabbix 3.4 - If set to 0 or 0s, the default action escalation period will be used. default: 0s esc_step_from: description: - Step to start escalation from. default: 1 esc_step_to: description: - Step to end escalation at. - Specify 0 for infinitely. default: 1 send_to_groups: type: list description: - User groups to send messages to. send_to_users: type: list description: - Users (usernames or aliases) to send messages to. message: description: - Operation message text. - Will check the 'default message' and use the text from I(default_message) if this and I(default_subject) are not specified subject: description: - Operation message subject. - Will check the 'default message' and use the text from I(default_subject) if this and I(default_subject) are not specified media_type: description: - Media type that will be used to send the message. - Can be used with I(type=send_message) or I(type=notify_all_involved) inside I(acknowledge_operations). - Set to C(all) for all media types default: 'all' operation_condition: type: 'str' description: - The action operation condition object defines a condition that must be met to perform the current operation. choices: - acknowledged - not_acknowledged host_groups: type: list description: - List of host groups host should be added to. - Required when I(type=add_to_host_group) or I(type=remove_from_host_group). templates: type: list description: - List of templates host should be linked to. - Required when I(type=link_to_template) or I(type=unlink_from_template). inventory: description: - Host inventory mode. - Required when I(type=set_host_inventory_mode). choices: - manual - automatic command_type: description: - Type of operation command. - Required when I(type=remote_command). choices: - custom_script - ipmi - ssh - telnet - global_script command: description: - Command to run. - Required when I(type=remote_command) and I(command_type!=global_script). execute_on: description: - Target on which the custom script operation command will be executed. - Required when I(type=remote_command) and I(command_type=custom_script). choices: - agent - server - proxy run_on_groups: description: - Host groups to run remote commands on. - Required when I(type=remote_command) and I(run_on_hosts) is not set. run_on_hosts: description: - Hosts to run remote commands on. - Required when I(type=remote_command) and I(run_on_groups) is not set. - If set to 0 the command will be run on the current host. ssh_auth_type: description: - Authentication method used for SSH commands. - Required when I(type=remote_command) and I(command_type=ssh). choices: - password - public_key ssh_privatekey_file: description: - Name of the private key file used for SSH commands with public key authentication. - Required when I(ssh_auth_type=public_key). - Can be used when I(type=remote_command). ssh_publickey_file: description: - Name of the public key file used for SSH commands with public key authentication. - Required when I(ssh_auth_type=public_key). - Can be used when I(type=remote_command). username: description: - User name used for authentication. - Required when I(ssh_auth_type in [public_key, password]) or I(command_type=telnet). - Can be used when I(type=remote_command). password: description: - Password used for authentication. - Required when I(ssh_auth_type=password) or I(command_type=telnet). - Can be used when I(type=remote_command). port: description: - Port number used for authentication. - Can be used when I(command_type in [ssh, telnet]) and I(type=remote_command). script_name: description: - The name of script used for global script commands. - Required when I(command_type=global_script). - Can be used when I(type=remote_command). recovery_operations: type: list description: - List of recovery operations. - C(Suboptions) are the same as for I(operations). - Works only with >= Zabbix 3.2 acknowledge_operations: type: list description: - List of acknowledge operations. - Action acknowledge operations are known as update operations since Zabbix 4.0. - C(Suboptions) are the same as for I(operations). - Works only with >= Zabbix 3.4 aliases: [ update_operations ] pause_symptoms: type: bool description: - Whether to pause escalation if event is a symptom event. - I(supported) if C(event_source) is set to C(trigger) - Works only with >= Zabbix 6.4 default: true notes: - Only Zabbix >= 3.0 is supported. extends_documentation_fragment: - community.zabbix.zabbix ''' EXAMPLES = ''' # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API 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 set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 # Trigger action with only one condition - name: Deploy trigger action # set task level variables as we change ansible_connection plugin here vars: ansible_network_os: community.zabbix.zabbix ansible_connection: httpapi ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_action: name: "Send alerts to Admin" event_source: 'trigger' state: present status: enabled esc_period: 60 conditions: - type: 'trigger_severity' operator: '>=' value: 'Information' operations: - type: send_message subject: "Something bad is happening" message: "Come on, guys do something" media_type: 'Email' send_to_users: - 'Admin' # Trigger action with multiple conditions and operations - name: Deploy trigger action # set task level variables as we change ansible_connection plugin here vars: ansible_network_os: community.zabbix.zabbix ansible_connection: httpapi ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_action: name: "Send alerts to Admin" event_source: 'trigger' state: present status: enabled esc_period: 1m conditions: - type: 'trigger_name' operator: 'like' value: 'Zabbix agent is unreachable' formulaid: A - type: 'trigger_severity' operator: '>=' value: 'disaster' formulaid: B formula: A or B operations: - type: send_message media_type: 'Email' send_to_users: - 'Admin' - type: remote_command command: 'systemctl restart zabbix-agent' command_type: custom_script execute_on: server run_on_hosts: - 0 # Trigger action with recovery and acknowledge operations - name: Deploy trigger action # set task level variables as we change ansible_connection plugin here vars: ansible_network_os: community.zabbix.zabbix ansible_connection: httpapi ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_action: name: "Send alerts to Admin" event_source: 'trigger' state: present status: enabled esc_period: 1h conditions: - type: 'trigger_severity' operator: '>=' value: 'Information' operations: - type: send_message subject: "Something bad is happening" message: "Come on, guys do something" media_type: 'Email' send_to_users: - 'Admin' recovery_operations: - type: send_message subject: "Host is down" message: "Come on, guys do something" media_type: 'Email' send_to_users: - 'Admin' acknowledge_operations: - type: send_message media_type: 'Email' send_to_users: - 'Admin' ''' RETURN = ''' msg: description: The result of the operation returned: success type: str sample: 'Action Deleted: Register webservers, ID: 0001' ''' from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase from ansible.module_utils.compat.version import LooseVersion import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils class Zapi(ZabbixBase): def __init__(self, module, zbx=None): super(Zapi, self).__init__(module, zbx) self._zapi_wrapper = self def check_if_action_exists(self, name): """Check if action exists. Args: name: Name of the action. Returns: The return value. True for success, False otherwise. """ try: _params = { "selectOperations": "extend", "selectRecoveryOperations": "extend", "selectAcknowledgeOperations": "extend", "selectFilter": "extend", 'filter': {'name': [name]} } if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0'): _params['selectUpdateOperations'] = _params.pop('selectAcknowledgeOperations', 'extend') _action = self._zapi.action.get(_params) if len(_action) > 0 and LooseVersion(self._zbx_api_version) < LooseVersion('6.0'): _action[0]['recovery_operations'] = _action[0].pop('recoveryOperations', []) _action[0]['acknowledge_operations'] = _action[0].pop('acknowledgeOperations', []) return _action except Exception as e: self._module.fail_json(msg="Failed to check if action '%s' exists: %s" % (name, e)) def get_action_by_name(self, name): """Get action by name Args: name: Name of the action. Returns: dict: Zabbix action """ try: action_list = self._zapi.action.get({ 'output': 'extend', 'filter': {'name': [name]} }) if len(action_list) < 1: self._module.fail_json(msg="Action not found: %s" % name) else: return action_list[0] except Exception as e: self._module.fail_json(msg="Failed to get ID of '%s': %s" % (name, e)) def get_host_by_host_name(self, host_name): """Get host by host name Args: host_name: host name. Returns: host matching host name """ try: host_list = self._zapi.host.get({ 'output': 'extend', 'selectInventory': 'extend', 'filter': {'host': [host_name]} }) if len(host_list) < 1: self._module.fail_json(msg="Host not found: %s" % host_name) else: return host_list[0] except Exception as e: self._module.fail_json(msg="Failed to get host '%s': %s" % (host_name, e)) def get_hostgroup_by_hostgroup_name(self, hostgroup_name): """Get host group by host group name Args: hostgroup_name: host group name. Returns: host group matching host group name """ try: hostgroup_list = self._zapi.hostgroup.get({ 'output': 'extend', 'filter': {'name': [hostgroup_name]} }) if len(hostgroup_list) < 1: self._module.fail_json(msg="Host group not found: %s" % hostgroup_name) else: return hostgroup_list[0] except Exception as e: self._module.fail_json(msg="Failed to get host group '%s': %s" % (hostgroup_name, e)) def get_template_by_template_name(self, template_name): """Get template by template name Args: template_name: template name. Returns: template matching template name """ try: template_list = self._zapi.template.get({ 'output': 'extend', 'filter': {'host': [template_name]} }) if len(template_list) < 1: self._module.fail_json(msg="Template not found: %s" % template_name) else: return template_list[0] except Exception as e: self._module.fail_json(msg="Failed to get template '%s': %s" % (template_name, e)) def get_trigger_by_trigger_name(self, trigger_name): """Get trigger by trigger name Args: trigger_name: trigger name. Returns: trigger matching trigger name """ try: trigger_list = self._zapi.trigger.get({ 'output': 'extend', 'filter': {'description': [trigger_name]} }) if len(trigger_list) < 1: self._module.fail_json(msg="Trigger not found: %s" % trigger_name) else: return trigger_list[0] except Exception as e: self._module.fail_json(msg="Failed to get trigger '%s': %s" % (trigger_name, e)) def get_discovery_rule_by_discovery_rule_name(self, discovery_rule_name): """Get discovery rule by discovery rule name Args: discovery_rule_name: discovery rule name. Returns: discovery rule matching discovery rule name """ try: discovery_rule_list = self._zapi.drule.get({ 'output': 'extend', 'filter': {'name': [discovery_rule_name]} }) if len(discovery_rule_list) < 1: self._module.fail_json(msg="Discovery rule not found: %s" % discovery_rule_name) else: return discovery_rule_list[0] except Exception as e: self._module.fail_json(msg="Failed to get discovery rule '%s': %s" % (discovery_rule_name, e)) def get_discovery_check_by_discovery_check_name(self, discovery_check_name): """Get discovery check by discovery check name Args: discovery_check_name: discovery check name. Returns: discovery check matching discovery check name """ try: discovery_rule_name, dcheck_type = discovery_check_name.split(': ') dcheck_type_to_number = { 'SSH': '0', 'LDAP': '1', 'SMTP': '2', 'FTP': '3', 'HTTP': '4', 'POP': '5', 'NNTP': '6', 'IMAP': '7', 'TCP': '8', 'Zabbix agent': '9', 'SNMPv1 agent': '10', 'SNMPv2 agent': '11', 'ICMP ping': '12', 'SNMPv3 agent': '13', 'HTTPS': '14', 'Telnet': '15' } if dcheck_type not in dcheck_type_to_number: self._module.fail_json(msg="Discovery check type: %s does not exist" % dcheck_type) discovery_rule_list = self._zapi.drule.get({ 'output': ['dchecks'], 'filter': {'name': [discovery_rule_name]}, 'selectDChecks': 'extend' }) if len(discovery_rule_list) < 1: self._module.fail_json(msg="Discovery check not found: %s" % discovery_check_name) for dcheck in discovery_rule_list[0]['dchecks']: if dcheck_type_to_number[dcheck_type] == dcheck['type']: return dcheck self._module.fail_json(msg="Discovery check not found: %s" % discovery_check_name) except Exception as e: self._module.fail_json(msg="Failed to get discovery check '%s': %s" % (discovery_check_name, e)) def get_proxy_by_proxy_name(self, proxy_name): """Get proxy by proxy name Args: proxy_name: proxy name. Returns: proxy matching proxy name """ try: proxy_list = self._zapi.proxy.get({ 'output': 'extend', 'filter': {'host': [proxy_name]} }) if len(proxy_list) < 1: self._module.fail_json(msg="Proxy not found: %s" % proxy_name) else: return proxy_list[0] except Exception as e: self._module.fail_json(msg="Failed to get proxy '%s': %s" % (proxy_name, e)) def get_mediatype_by_mediatype_name(self, mediatype_name): """Get mediatype by mediatype name Args: mediatype_name: mediatype name Returns: mediatype matching mediatype name """ if LooseVersion(self._zbx_api_version) >= LooseVersion('4.4'): filter = {'name': [mediatype_name]} else: filter = {'description': [mediatype_name]} try: if str(mediatype_name).lower() == 'all': return '0' mediatype_list = self._zapi.mediatype.get({ 'output': 'extend', 'filter': filter }) if len(mediatype_list) < 1: self._module.fail_json(msg="Media type not found: %s" % mediatype_name) else: return mediatype_list[0]['mediatypeid'] except Exception as e: self._module.fail_json(msg="Failed to get mediatype '%s': %s" % (mediatype_name, e)) def get_user_by_user_name(self, user_name): """Get user by user name Args: user_name: user name Returns: user matching user name """ try: if LooseVersion(self._zbx_api_version) >= LooseVersion('5.4'): filter = {'username': [user_name]} else: filter = {'alias': [user_name]} user_list = self._zapi.user.get({ 'output': 'extend', 'filter': filter, }) if len(user_list) < 1: self._module.fail_json(msg="User not found: %s" % user_name) else: return user_list[0] except Exception as e: self._module.fail_json(msg="Failed to get user '%s': %s" % (user_name, e)) def get_usergroup_by_usergroup_name(self, usergroup_name): """Get usergroup by usergroup name Args: usergroup_name: usergroup name Returns: usergroup matching usergroup name """ try: usergroup_list = self._zapi.usergroup.get({ 'output': 'extend', 'filter': {'name': [usergroup_name]} }) if len(usergroup_list) < 1: self._module.fail_json(msg="User group not found: %s" % usergroup_name) else: return usergroup_list[0] except Exception as e: self._module.fail_json(msg="Failed to get user group '%s': %s" % (usergroup_name, e)) # get script by script name def get_script_by_script_name(self, script_name): """Get script by script name Args: script_name: script name Returns: script matching script name """ try: if script_name is None: return {} script_list = self._zapi.script.get({ 'output': 'extend', 'filter': {'name': [script_name]} }) if len(script_list) < 1: self._module.fail_json(msg="Script not found: %s" % script_name) else: return script_list[0] except Exception as e: self._module.fail_json(msg="Failed to get script '%s': %s" % (script_name, e)) class Action(Zapi): def __init__(self, module, zbx=None): super(Action, self).__init__(module, zbx) self.existing_data = None def _construct_parameters(self, **kwargs): """Construct parameters. Args: **kwargs: Arbitrary keyword parameters. Returns: dict: dictionary of specified parameters """ _params = { 'name': kwargs['name'], 'eventsource': zabbix_utils.helper_to_numeric_value([ 'trigger', 'discovery', 'auto_registration', 'internal'], kwargs['event_source']), 'esc_period': kwargs.get('esc_period'), 'filter': kwargs['conditions'], 'def_longdata': kwargs['default_message'], 'def_shortdata': kwargs['default_subject'], 'r_longdata': kwargs['recovery_default_message'], 'r_shortdata': kwargs['recovery_default_subject'], 'ack_longdata': kwargs['acknowledge_default_message'], 'ack_shortdata': kwargs['acknowledge_default_subject'], 'operations': kwargs['operations'], 'recovery_operations': kwargs.get('recovery_operations'), 'acknowledge_operations': kwargs.get('acknowledge_operations'), 'status': zabbix_utils.helper_to_numeric_value([ 'enabled', 'disabled'], kwargs['status']) } if kwargs['event_source'] == 'trigger': if LooseVersion(self._zbx_api_version) >= LooseVersion('4.0'): _params['pause_suppressed'] = '1' if kwargs['pause_in_maintenance'] else '0' else: _params['maintenance_mode'] = '1' if kwargs['pause_in_maintenance'] else '0' if LooseVersion(self._zbx_api_version) >= LooseVersion('6.4'): _params['pause_symptoms'] = '1' if kwargs['pause_symptoms'] else '0' if LooseVersion(self._zbx_api_version) >= LooseVersion('5.0'): # remove some fields regarding # https://www.zabbix.com/documentation/5.0/manual/api/reference/action/object _params.pop('def_longdata', None) _params.pop('def_shortdata', None) _params.pop('r_longdata', None) _params.pop('r_shortdata', None) if (LooseVersion(self._zbx_api_version) < LooseVersion('3.4') or LooseVersion(self._zbx_api_version) >= LooseVersion('5.0')): _params.pop('ack_longdata', None) _params.pop('ack_shortdata', None) if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0'): _params['update_operations'] = kwargs.get('update_operations') if 'update_operations' in _params and not isinstance(_params.get('update_operations', None), type(None)): _params.pop('acknowledge_operations', None) elif isinstance(_params.get('acknowledge_operations', None), list): _params['update_operations'] = _params.pop('acknowledge_operations', []) else: _params['update_operations'] = [] _params.pop('acknowledge_operations', None) if 'esc_period' in _params and isinstance(_params.get('esc_period', None), type(None)): _params.pop('esc_period') if 'recovery_operations' in _params: if isinstance(_params.get('recovery_operations', None), type(None)) or len(_params.get('recovery_operations', [])) == 0: _params.pop('recovery_operations') if 'update_operations' in _params: if isinstance(_params.get('update_operations', None), type(None)) or len(_params.get('update_operations', [])) == 0: _params.pop('update_operations') if _params['eventsource'] not in [0, 3]: _params.pop('esc_period') return _params def check_difference(self, **kwargs): """Check difference between action and user specified parameters. Args: **kwargs: Arbitrary keyword parameters. Returns: dict: dictionary of differences """ existing_action = zabbix_utils.helper_convert_unicode_to_str(self.check_if_action_exists(kwargs['name'])[0]) parameters = zabbix_utils.helper_convert_unicode_to_str(self._construct_parameters(**kwargs)) change_parameters = {} _diff = zabbix_utils.helper_cleanup_data(zabbix_utils.helper_compare_dictionaries(parameters, existing_action, change_parameters)) return _diff def update_action(self, **kwargs): """Update action. Args: **kwargs: Arbitrary keyword parameters. Returns: action: updated action """ try: if self._module.check_mode: self._module.exit_json(msg="Action would be updated if check mode was not specified: %s" % kwargs, changed=True) kwargs['actionid'] = kwargs.pop('action_id') return self._zapi.action.update(kwargs) except Exception as e: self._module.fail_json(msg="Failed to update action '%s': %s" % (kwargs['actionid'], e)) def add_action(self, **kwargs): """Add action. Args: **kwargs: Arbitrary keyword parameters. Returns: action: added action """ try: if self._module.check_mode: self._module.exit_json(msg="Action would be added if check mode was not specified", changed=True) parameters = self._construct_parameters(**kwargs) action_list = self._zapi.action.create(parameters) return action_list['actionids'][0] except Exception as e: self._module.fail_json(msg="Failed to create action '%s': %s" % (kwargs['name'], e)) def delete_action(self, action_id): """Delete action. Args: action_id: Action id Returns: action: deleted action """ try: if self._module.check_mode: self._module.exit_json(msg="Action would be deleted if check mode was not specified", changed=True) return self._zapi.action.delete([action_id]) except Exception as e: self._module.fail_json(msg="Failed to delete action '%s': %s" % (action_id, e)) class Operations(Zapi): def _construct_operationtype(self, operation): """Construct operation type. Args: operation: operation to construct Returns: str: constructed operation """ try: return zabbix_utils.helper_to_numeric_value([ "send_message", "remote_command", "add_host", "remove_host", "add_to_host_group", "remove_from_host_group", "link_to_template", "unlink_from_template", "enable_host", "disable_host", "set_host_inventory_mode"], operation['type'] ) except Exception: self._module.fail_json(msg="Unsupported value '%s' for operation type." % operation['type']) def _construct_opmessage(self, operation): """Construct operation message. Args: operation: operation to construct the message Returns: dict: constructed operation message """ try: return { 'default_msg': '0' if operation.get('message') is not None or operation.get('subject') is not None else '1', 'mediatypeid': self._zapi_wrapper.get_mediatype_by_mediatype_name( operation.get('media_type') ) if operation.get('media_type') is not None else '0', 'message': operation.get('message'), 'subject': operation.get('subject'), } except Exception as e: self._module.fail_json(msg="Failed to construct operation message. The error was: %s" % e) def _construct_opmessage_usr(self, operation): """Construct operation message user. Args: operation: operation to construct the message user Returns: list: constructed operation message user or None if operation not found """ if operation.get('send_to_users') is None: return None return [{ 'userid': self._zapi_wrapper.get_user_by_user_name(_user)['userid'] } for _user in operation.get('send_to_users')] def _construct_opmessage_grp(self, operation): """Construct operation message group. Args: operation: operation to construct the message group Returns: list: constructed operation message group or None if operation not found """ if operation.get('send_to_groups') is None: return None return [{ 'usrgrpid': self._zapi_wrapper.get_usergroup_by_usergroup_name(_group)['usrgrpid'] } for _group in operation.get('send_to_groups')] def _construct_opcommand(self, operation): """Construct operation command. Args: operation: operation to construct command Returns: list: constructed operation command """ try: if LooseVersion(self._zbx_api_version) < LooseVersion('6.0'): opcommand = { 'type': zabbix_utils.helper_to_numeric_value([ 'custom_script', 'ipmi', 'ssh', 'telnet', 'global_script'], operation.get('command_type', 'custom_script')), 'command': operation.get('command'), 'execute_on': zabbix_utils.helper_to_numeric_value([ 'agent', 'server', 'proxy'], operation.get('execute_on', 'server')), 'scriptid': self._zapi_wrapper.get_script_by_script_name( operation.get('script_name') ).get('scriptid'), 'authtype': zabbix_utils.helper_to_numeric_value([ 'password', 'public_key' ], operation.get('ssh_auth_type')), 'privatekey': operation.get('ssh_privatekey_file'), 'publickey': operation.get('ssh_publickey_file'), 'username': operation.get('username'), 'password': operation.get('password'), 'port': operation.get('port') } else: # In 6.0 opcommand is an opbject with just one key 'scriptid' opcommand = { 'scriptid': self._zapi_wrapper.get_script_by_script_name( operation.get('script_name') ).get('scriptid') } return opcommand except Exception as e: self._module.fail_json(msg="Failed to construct operation command. The error was: %s" % e) def _construct_opcommand_hst(self, operation): """Construct operation command host. Args: operation: operation to construct command host Returns: list: constructed operation command host """ if operation.get('run_on_hosts') is None: return None return [{ 'hostid': self._zapi_wrapper.get_host_by_host_name(_host)['hostid'] } if str(_host) != '0' else {'hostid': '0'} for _host in operation.get('run_on_hosts')] def _construct_opcommand_grp(self, operation): """Construct operation command group. Args: operation: operation to construct command group Returns: list: constructed operation command group """ if operation.get('run_on_groups') is None: return None return [{ 'groupid': self._zapi_wrapper.get_hostgroup_by_hostgroup_name(_group)['groupid'] } for _group in operation.get('run_on_groups')] def _construct_opgroup(self, operation): """Construct operation group. Args: operation: operation to construct group Returns: list: constructed operation group """ return [{ 'groupid': self._zapi_wrapper.get_hostgroup_by_hostgroup_name(_group)['groupid'] } for _group in operation.get('host_groups', [])] def _construct_optemplate(self, operation): """Construct operation template. Args: operation: operation to construct template Returns: list: constructed operation template """ return [{ 'templateid': self._zapi_wrapper.get_template_by_template_name(_template)['templateid'] } for _template in operation.get('templates', [])] def _construct_opinventory(self, operation): """Construct operation inventory. Args: operation: operation to construct inventory Returns: dict: constructed operation inventory """ return { 'inventory_mode': zabbix_utils.helper_to_numeric_value([ 'manual', 'automatic' ], operation.get('inventory')) } def _construct_opconditions(self, operation): """Construct operation conditions. Args: operation: operation to construct the conditions Returns: list: constructed operation conditions """ _opcond = operation.get('operation_condition') if _opcond is not None: if _opcond == 'acknowledged': _value = '1' elif _opcond == 'not_acknowledged': _value = '0' return [{ 'conditiontype': '14', 'operator': '0', 'value': _value }] return [] def construct_the_data(self, operations, event_source): """Construct the operation data using helper methods. Args: operation: operation to construct Returns: list: constructed operation data """ constructed_data = [] for op in operations: operation_type = self._construct_operationtype(op) constructed_operation = { 'operationtype': operation_type, 'esc_period': op.get('esc_period'), 'esc_step_from': op.get('esc_step_from'), 'esc_step_to': op.get('esc_step_to') } # Send Message type if constructed_operation['operationtype'] == 0: constructed_operation['opmessage'] = self._construct_opmessage(op) constructed_operation['opmessage_usr'] = self._construct_opmessage_usr(op) constructed_operation['opmessage_grp'] = self._construct_opmessage_grp(op) if event_source == 'trigger': # opconditions valid only for 'trigger' action constructed_operation['opconditions'] = self._construct_opconditions(op) # Send Command type if constructed_operation['operationtype'] == 1: constructed_operation['opcommand'] = self._construct_opcommand(op) constructed_operation['opcommand_hst'] = self._construct_opcommand_hst(op) constructed_operation['opcommand_grp'] = self._construct_opcommand_grp(op) if event_source == 'trigger': # opconditions valid only for 'trigger' action constructed_operation['opconditions'] = self._construct_opconditions(op) # Add to/Remove from host group if constructed_operation['operationtype'] in (4, 5): constructed_operation['opgroup'] = self._construct_opgroup(op) # Link/Unlink template if constructed_operation['operationtype'] in (6, 7): constructed_operation['optemplate'] = self._construct_optemplate(op) # Set inventory mode if constructed_operation['operationtype'] == 10: constructed_operation['opinventory'] = self._construct_opinventory(op) # Remove escalation params when for event sources where they are not applicable if event_source in ['trigger', 'internal']: if isinstance(constructed_operation.get('esc_period'), type(None)): constructed_operation['esc_period'] = 0 else: constructed_operation.pop('esc_period') constructed_operation.pop('esc_step_from') constructed_operation.pop('esc_step_to') constructed_data.append(constructed_operation) return zabbix_utils.helper_cleanup_data(constructed_data) class RecoveryOperations(Operations): """ Restructures the user defined recovery operations data to fit the Zabbix API requirements """ def _construct_operationtype(self, operation): """Construct operation type. Args: operation: operation to construct type Returns: str: constructed operation type """ try: return zabbix_utils.helper_to_numeric_value([ "send_message", "remote_command", None, None, None, None, None, None, None, None, None, "notify_all_involved"], operation['type'] ) except Exception: self._module.fail_json(msg="Unsupported value '%s' for recovery operation type." % operation['type']) def construct_the_data(self, operations): """Construct the recovery operations data using helper methods. Args: operation: operation to construct Returns: list: constructed recovery operations data """ constructed_data = [] for op in operations: operation_type = self._construct_operationtype(op) constructed_operation = { 'operationtype': operation_type, } # Send Message type if constructed_operation['operationtype'] == 0: constructed_operation['opmessage'] = self._construct_opmessage(op) constructed_operation['opmessage_usr'] = self._construct_opmessage_usr(op) constructed_operation['opmessage_grp'] = self._construct_opmessage_grp(op) if constructed_operation['operationtype'] == 11: constructed_operation['opmessage'] = self._construct_opmessage(op) if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0'): constructed_operation['opmessage'].pop('mediatypeid') # Send Command type if constructed_operation['operationtype'] == 1: constructed_operation['opcommand'] = self._construct_opcommand(op) constructed_operation['opcommand_hst'] = self._construct_opcommand_hst(op) constructed_operation['opcommand_grp'] = self._construct_opcommand_grp(op) constructed_data.append(constructed_operation) return zabbix_utils.helper_cleanup_data(constructed_data) class AcknowledgeOperations(Operations): """ Restructures the user defined acknowledge operations data to fit the Zabbix API requirements """ def _construct_operationtype(self, operation): """Construct operation type. Args: operation: operation to construct type Returns: str: constructed operation type """ try: return zabbix_utils.helper_to_numeric_value([ "send_message", "remote_command", None, None, None, None, None, None, None, None, None, None, "notify_all_involved"], operation['type'] ) except Exception: self._module.fail_json(msg="Unsupported value '%s' for acknowledge operation type." % operation['type']) def construct_the_data(self, operations): """Construct the acknowledge operations data using helper methods. Args: operation: operation to construct Returns: list: constructed acknowledge operations data """ constructed_data = [] for op in operations: operation_type = self._construct_operationtype(op) constructed_operation = { 'operationtype': operation_type, } # Send Message type if constructed_operation['operationtype'] == 0: constructed_operation['opmessage'] = self._construct_opmessage(op) constructed_operation['opmessage_usr'] = self._construct_opmessage_usr(op) constructed_operation['opmessage_grp'] = self._construct_opmessage_grp(op) if constructed_operation['operationtype'] == 12: constructed_operation['opmessage'] = self._construct_opmessage(op) if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0'): constructed_operation['opmessage'].pop('mediatypeid') # Send Command type if constructed_operation['operationtype'] == 1: constructed_operation['opcommand'] = self._construct_opcommand(op) constructed_operation['opcommand_hst'] = self._construct_opcommand_hst(op) constructed_operation['opcommand_grp'] = self._construct_opcommand_grp(op) constructed_data.append(constructed_operation) return zabbix_utils.helper_cleanup_data(constructed_data) class Filter(Zapi): def _construct_evaltype(self, _eval_type, _formula, _conditions): """Construct the eval type Args: _formula: zabbix condition evaluation formula _conditions: list of conditions to check Returns: dict: constructed acknowledge operations data """ if len(_conditions) <= 1: return { 'evaltype': '0', 'formula': None } if _eval_type == 'andor': return { 'evaltype': '0', 'formula': None } if _eval_type == 'and': return { 'evaltype': '1', 'formula': None } if _eval_type == 'or': return { 'evaltype': '2', 'formula': None } if _eval_type == 'custom_expression': if _formula is not None: return { 'evaltype': '3', 'formula': _formula } else: self._module.fail_json(msg="'formula' is required when 'eval_type' is set to 'custom_expression'") if _formula is not None: return { 'evaltype': '3', 'formula': _formula } return { 'evaltype': '0', 'formula': None } def _construct_conditiontype(self, _condition): """Construct the condition type Args: _condition: condition to check Returns: str: constructed condition type data """ # application is disabled is disabled for condition type since 5.4 version. if (LooseVersion(self._zbx_api_version) >= LooseVersion('5.4') and _condition['type'] == 'application'): self._module.fail_json(msg="'%s' is disabled for condition type since 5.4 version." % _condition['type']) try: return zabbix_utils.helper_to_numeric_value([ "host_group", "host", "trigger", "trigger_name", "trigger_severity", "trigger_value", "time_period", "host_ip", "discovered_service_type", "discovered_service_port", "discovery_status", "uptime_or_downtime_duration", "received_value", "host_template", None, "application", "maintenance_status", None, "discovery_rule", "discovery_check", "proxy", "discovery_object", "host_name", "event_type", "host_metadata", "event_tag", "event_tag_value"], _condition['type'] ) except Exception: self._module.fail_json(msg="Unsupported value '%s' for condition type." % _condition['type']) def _construct_operator(self, _condition): """Construct operator Args: _condition: condition to construct Returns: str: constructed operator """ try: return zabbix_utils.helper_to_numeric_value([ ["equals", "="], ["does not equal", "<>"], ["contains", "like"], ["does not contain", "not like"], "in", ["is greater than or equals", ">="], ["is less than or equals", "<="], "not in", "matches", "does not match", "Yes", "No"], _condition['operator'] ) except Exception: self._module.fail_json(msg="Unsupported value '%s' for operator." % _condition['operator']) def _construct_value(self, conditiontype, value): """Construct operator Args: conditiontype: type of condition to construct value: value to construct Returns: str: constructed value """ try: # Host group if conditiontype == 0: return self._zapi_wrapper.get_hostgroup_by_hostgroup_name(value)['groupid'] # Host if conditiontype == 1: return self._zapi_wrapper.get_host_by_host_name(value)['hostid'] # Trigger if conditiontype == 2: return self._zapi_wrapper.get_trigger_by_trigger_name(value)['triggerid'] # Trigger name: return as is # Trigger severity if conditiontype == 4: return zabbix_utils.helper_to_numeric_value([ "not classified", "information", "warning", "average", "high", "disaster"], value or "not classified" ) # Trigger value if conditiontype == 5: return zabbix_utils.helper_to_numeric_value([ "ok", "problem"], value or "ok" ) # Time period: return as is # Host IP: return as is # Discovered service type if conditiontype == 8: return zabbix_utils.helper_to_numeric_value([ "SSH", "LDAP", "SMTP", "FTP", "HTTP", "POP", "NNTP", "IMAP", "TCP", "Zabbix agent", "SNMPv1 agent", "SNMPv2 agent", "ICMP ping", "SNMPv3 agent", "HTTPS", "Telnet"], value ) # Discovered service port: return as is # Discovery status if conditiontype == 10: return zabbix_utils.helper_to_numeric_value([ "up", "down", "discovered", "lost"], value ) if conditiontype == 13: return self._zapi_wrapper.get_template_by_template_name(value)['templateid'] if LooseVersion(self._zapi_wrapper._zbx_api_version) >= LooseVersion('6.0'): # maintenance_status if conditiontype == 16: return zabbix_utils.helper_to_numeric_value([ "Yes", "No"], value ) if conditiontype == 18: return self._zapi_wrapper.get_discovery_rule_by_discovery_rule_name(value)['druleid'] if conditiontype == 19: return self._zapi_wrapper.get_discovery_check_by_discovery_check_name(value)['dcheckid'] if conditiontype == 20: return self._zapi_wrapper.get_proxy_by_proxy_name(value)['proxyid'] if conditiontype == 21: return zabbix_utils.helper_to_numeric_value([ "pchldrfor0", "host", "service"], value ) if conditiontype == 23: return zabbix_utils.helper_to_numeric_value([ "item in not supported state", "item in normal state", "LLD rule in not supported state", "LLD rule in normal state", "trigger in unknown state", "trigger in normal state"], value ) return value except Exception: self._module.fail_json( msg="""Unsupported value '%s' for specified condition type. Check out Zabbix API documentation for supported values for condition type '%s' at https://www.zabbix.com/documentation/3.4/manual/api/reference/action/object#action_filter_condition""" % (value, conditiontype) ) def construct_the_data(self, _eval_type, _formula, _conditions): """Construct the user defined filter conditions to fit the Zabbix API requirements operations data using helper methods. Args: _formula: zabbix condition evaluation formula _conditions: conditions to construct Returns: dict: user defined filter conditions """ if _conditions is None: return None constructed_data = {} constructed_data['conditions'] = [] for cond in _conditions: condition_type = self._construct_conditiontype(cond) constructed_data['conditions'].append({ "conditiontype": condition_type, "value": self._construct_value(condition_type, cond.get("value")), "value2": cond.get("value2"), "formulaid": cond.get("formulaid"), "operator": self._construct_operator(cond) }) _constructed_evaltype = self._construct_evaltype( _eval_type, _formula, constructed_data['conditions'] ) constructed_data['evaltype'] = _constructed_evaltype['evaltype'] constructed_data['formula'] = _constructed_evaltype['formula'] return zabbix_utils.helper_cleanup_data(constructed_data) def main(): """Main ansible module function """ argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( esc_period=dict(type='str', required=False), name=dict(type='str', required=True), event_source=dict(type='str', required=False, choices=['trigger', 'discovery', 'auto_registration', 'internal']), state=dict(type='str', required=False, default='present', choices=['present', 'absent']), status=dict(type='str', required=False, default='enabled', choices=['enabled', 'disabled']), pause_in_maintenance=dict(type='bool', required=False, default=True), default_message=dict(type='str', required=False, default=''), default_subject=dict(type='str', required=False, default=''), recovery_default_message=dict(type='str', required=False, default=''), recovery_default_subject=dict(type='str', required=False, default=''), acknowledge_default_message=dict(type='str', required=False, default=''), acknowledge_default_subject=dict(type='str', required=False, default=''), conditions=dict( type='list', required=False, default=[], elements='dict', options=dict( formulaid=dict(type='str', required=False), operator=dict(type='str', required=True), type=dict(type='str', required=True), value=dict(type='str', required=False), value2=dict(type='str', required=False) ), required_if=[ ['type', 'event_tag_value', ['value2']], ] ), formula=dict(type='str', required=False, default=None), eval_type=dict(type='str', required=False, default=None, choices=['andor', 'and', 'or', 'custom_expression']), operations=dict( type='list', required=False, default=[], elements='dict', options=dict( type=dict( type='str', required=True, choices=[ 'send_message', 'remote_command', 'add_host', 'remove_host', 'add_to_host_group', 'remove_from_host_group', 'link_to_template', 'unlink_from_template', 'enable_host', 'disable_host', 'set_host_inventory_mode', ] ), esc_period=dict(type='str', required=False), esc_step_from=dict(type='int', required=False, default=1), esc_step_to=dict(type='int', required=False, default=1), operation_condition=dict( type='str', required=False, default=None, choices=['acknowledged', 'not_acknowledged'] ), # when type is remote_command command_type=dict( type='str', required=False, choices=[ 'custom_script', 'ipmi', 'ssh', 'telnet', 'global_script' ] ), command=dict(type='str', required=False), execute_on=dict( type='str', required=False, choices=['agent', 'server', 'proxy'] ), password=dict(type='str', required=False, no_log=True), port=dict(type='int', required=False), run_on_groups=dict(type='list', required=False), run_on_hosts=dict(type='list', required=False), script_name=dict(type='str', required=False), ssh_auth_type=dict(type='str', required=False, choices=['password', 'public_key']), ssh_privatekey_file=dict(type='str', required=False), ssh_publickey_file=dict(type='str', required=False), username=dict(type='str', required=False), # when type is send_message media_type=dict(type='str', required=False), subject=dict(type='str', required=False), message=dict(type='str', required=False), send_to_groups=dict(type='list', required=False), send_to_users=dict(type='list', required=False), # when type is add_to_host_group or remove_from_host_group host_groups=dict(type='list', required=False), # when type is set_host_inventory_mode inventory=dict(type='str', required=False, choices=['manual', 'automatic']), # when type is link_to_template or unlink_from_template templates=dict(type='list', required=False) ), required_if=[ ['type', 'remote_command', ['command_type']], ['type', 'remote_command', ['run_on_groups', 'run_on_hosts'], True], ['command_type', 'custom_script', ['command', 'execute_on']], ['command_type', 'ipmi', ['command']], ['command_type', 'ssh', ['command', 'ssh_auth_type']], ['ssh_auth_type', 'password', ['username', 'password']], ['ssh_auth_type', 'public_key', ['username', 'ssh_privatekey_file', 'ssh_publickey_file']], ['command_type', 'telnet', ['command', 'username', 'password']], ['command_type', 'global_script', ['script_name']], ['type', 'add_to_host_group', ['host_groups']], ['type', 'remove_from_host_group', ['host_groups']], ['type', 'link_to_template', ['templates']], ['type', 'unlink_from_template', ['templates']], ['type', 'set_host_inventory_mode', ['inventory']], ['type', 'send_message', ['send_to_users', 'send_to_groups'], True] ] ), recovery_operations=dict( type='list', required=False, default=[], elements='dict', options=dict( type=dict( type='str', required=True, choices=[ 'send_message', 'remote_command', 'notify_all_involved' ] ), # when type is remote_command command_type=dict( type='str', required=False, choices=[ 'custom_script', 'ipmi', 'ssh', 'telnet', 'global_script' ] ), command=dict(type='str', required=False), execute_on=dict( type='str', required=False, choices=['agent', 'server', 'proxy'] ), password=dict(type='str', required=False, no_log=True), port=dict(type='int', required=False), run_on_groups=dict(type='list', required=False), run_on_hosts=dict(type='list', required=False), script_name=dict(type='str', required=False), ssh_auth_type=dict(type='str', required=False, choices=['password', 'public_key']), ssh_privatekey_file=dict(type='str', required=False), ssh_publickey_file=dict(type='str', required=False), username=dict(type='str', required=False), # when type is send_message media_type=dict(type='str', required=False), subject=dict(type='str', required=False), message=dict(type='str', required=False), send_to_groups=dict(type='list', required=False), send_to_users=dict(type='list', required=False), ), required_if=[ ['type', 'remote_command', ['command_type']], ['type', 'remote_command', [ 'run_on_groups', 'run_on_hosts' ], True], ['command_type', 'custom_script', [ 'command', 'execute_on' ]], ['command_type', 'ipmi', ['command']], ['command_type', 'ssh', ['command', 'ssh_auth_type']], ['ssh_auth_type', 'password', ['username', 'password']], ['ssh_auth_type', 'public_key', ['username', 'ssh_privatekey_file', 'ssh_publickey_file']], ['command_type', 'telnet', ['command', 'username', 'password']], ['command_type', 'global_script', ['script_name']], ['type', 'send_message', ['send_to_users', 'send_to_groups'], True] ] ), acknowledge_operations=dict( type='list', required=False, default=[], elements='dict', aliases=['update_operations'], options=dict( type=dict( type='str', required=True, choices=[ 'send_message', 'remote_command', 'notify_all_involved' ] ), # when type is remote_command command_type=dict( type='str', required=False, choices=[ 'custom_script', 'ipmi', 'ssh', 'telnet', 'global_script' ] ), command=dict(type='str', required=False), execute_on=dict( type='str', required=False, choices=['agent', 'server', 'proxy'] ), password=dict(type='str', required=False, no_log=True), port=dict(type='int', required=False), run_on_groups=dict(type='list', required=False), run_on_hosts=dict(type='list', required=False), script_name=dict(type='str', required=False), ssh_auth_type=dict(type='str', required=False, choices=['password', 'public_key']), ssh_privatekey_file=dict(type='str', required=False), ssh_publickey_file=dict(type='str', required=False), username=dict(type='str', required=False), # when type is send_message media_type=dict(type='str', required=False), subject=dict(type='str', required=False), message=dict(type='str', required=False), send_to_groups=dict(type='list', required=False), send_to_users=dict(type='list', required=False), ), required_if=[ ['type', 'remote_command', ['command_type']], ['type', 'remote_command', [ 'run_on_groups', 'run_on_hosts' ], True], ['command_type', 'custom_script', [ 'command', 'execute_on' ]], ['command_type', 'ipmi', ['command']], ['command_type', 'ssh', ['command', 'ssh_auth_type']], ['ssh_auth_type', 'password', ['username', 'password']], ['ssh_auth_type', 'public_key', ['username', 'ssh_privatekey_file', 'ssh_publickey_file']], ['command_type', 'telnet', ['command', 'username', 'password']], ['command_type', 'global_script', ['script_name']], ['type', 'send_message', ['send_to_users', 'send_to_groups'], True] ] ), pause_symptoms=dict(type='bool', required=False, default=True) )) module = AnsibleModule( argument_spec=argument_spec, required_if=[ ['state', 'present', [ 'event_source' ]] ], supports_check_mode=True ) zabbix_utils.require_creds_params(module) for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: if p in module.params and not module.params[p] is None: module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) name = module.params['name'] esc_period = module.params['esc_period'] event_source = module.params['event_source'] state = module.params['state'] status = module.params['status'] pause_in_maintenance = module.params['pause_in_maintenance'] default_message = module.params['default_message'] default_subject = module.params['default_subject'] recovery_default_message = module.params['recovery_default_message'] recovery_default_subject = module.params['recovery_default_subject'] acknowledge_default_message = module.params['acknowledge_default_message'] acknowledge_default_subject = module.params['acknowledge_default_subject'] conditions = module.params['conditions'] formula = module.params['formula'] eval_type = module.params['eval_type'] operations = module.params['operations'] recovery_operations = module.params['recovery_operations'] acknowledge_operations = module.params['acknowledge_operations'] pause_symptoms = module.params['pause_symptoms'] zapi_wrapper = Zapi(module) action = Action(module) action_exists = zapi_wrapper.check_if_action_exists(name) ops = Operations(module, zapi_wrapper) recovery_ops = RecoveryOperations(module, zapi_wrapper) acknowledge_ops = AcknowledgeOperations(module, zapi_wrapper) fltr = Filter(module, zapi_wrapper) if action_exists: action_id = zapi_wrapper.get_action_by_name(name)['actionid'] if state == "absent": result = action.delete_action(action_id) module.exit_json(changed=True, msg="Action Deleted: %s, ID: %s" % (name, result)) else: kwargs = dict( action_id=action_id, name=name, event_source=event_source, esc_period=esc_period, status=status, pause_in_maintenance=pause_in_maintenance, default_message=default_message, default_subject=default_subject, recovery_default_message=recovery_default_message, recovery_default_subject=recovery_default_subject, acknowledge_default_message=acknowledge_default_message, acknowledge_default_subject=acknowledge_default_subject, operations=ops.construct_the_data(operations, event_source), recovery_operations=recovery_ops.construct_the_data(recovery_operations), conditions=fltr.construct_the_data(eval_type, formula, conditions) ) if LooseVersion(zapi_wrapper._zbx_api_version) >= LooseVersion('6.4'): kwargs['pause_symptoms'] = pause_symptoms if LooseVersion(zapi_wrapper._zbx_api_version) >= LooseVersion('6.0'): kwargs[argument_spec['acknowledge_operations']['aliases'][0]] = acknowledge_ops.construct_the_data(acknowledge_operations) else: kwargs['acknowledge_operations'] = acknowledge_ops.construct_the_data(acknowledge_operations) difference = action.check_difference(**kwargs) if difference == {}: module.exit_json(changed=False, msg="Action is up to date: %s" % (name)) else: result = action.update_action( action_id=action_id, **difference ) module.exit_json(changed=True, msg="Action Updated: %s, ID: %s" % (name, result)) else: if state == "absent": module.exit_json(changed=False) else: kwargs = dict( name=name, event_source=event_source, esc_period=esc_period, status=status, pause_in_maintenance=pause_in_maintenance, default_message=default_message, default_subject=default_subject, recovery_default_message=recovery_default_message, recovery_default_subject=recovery_default_subject, acknowledge_default_message=acknowledge_default_message, acknowledge_default_subject=acknowledge_default_subject, operations=ops.construct_the_data(operations, event_source), recovery_operations=recovery_ops.construct_the_data(recovery_operations), conditions=fltr.construct_the_data(eval_type, formula, conditions) ) if LooseVersion(zapi_wrapper._zbx_api_version) >= LooseVersion('6.0'): kwargs[argument_spec['acknowledge_operations']['aliases'][0]] = acknowledge_ops.construct_the_data(acknowledge_operations) else: kwargs['acknowledge_operations'] = acknowledge_ops.construct_the_data(acknowledge_operations) if LooseVersion(zapi_wrapper._zbx_api_version) >= LooseVersion('6.4'): kwargs['pause_symptoms'] = pause_symptoms action_id = action.add_action(**kwargs) module.exit_json(changed=True, msg="Action created: %s, ID: %s" % (name, action_id)) if __name__ == '__main__': main()