File: //lib/python3.9/site-packages/ansible_collections/community/zabbix/plugins/modules/zabbix_action.py
# -*- 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()