Created
November 27, 2024 10:52
-
-
Save cohan/a192f2895a4490df9028ad0569a8a5b4 to your computer and use it in GitHub Desktop.
To fix Zabbix->Slack 'invalid_arguments'
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
zabbix_export: | |
version: '7.0' | |
media_types: | |
- | |
name: Slack | |
type: WEBHOOK | |
parameters: | |
- | |
name: alert_message | |
value: '{ALERT.MESSAGE}' | |
- | |
name: alert_subject | |
value: '{ALERT.SUBJECT}' | |
- | |
name: bot_token | |
value: '<PLACE YOUR KEY HERE>' | |
- | |
name: channel | |
value: '{ALERT.SENDTO}' | |
- | |
name: discovery_host_dns | |
value: '{DISCOVERY.DEVICE.DNS}' | |
- | |
name: discovery_host_ip | |
value: '{DISCOVERY.DEVICE.IPADDRESS}' | |
- | |
name: event_date | |
value: '{EVENT.DATE}' | |
- | |
name: event_id | |
value: '{EVENT.ID}' | |
- | |
name: event_nseverity | |
value: '{EVENT.NSEVERITY}' | |
- | |
name: event_opdata | |
value: '{EVENT.OPDATA}' | |
- | |
name: event_recovery_date | |
value: '{EVENT.RECOVERY.DATE}' | |
- | |
name: event_recovery_time | |
value: '{EVENT.RECOVERY.TIME}' | |
- | |
name: event_severity | |
value: '{EVENT.SEVERITY}' | |
- | |
name: event_source | |
value: '{EVENT.SOURCE}' | |
- | |
name: event_tags | |
value: '{EVENT.TAGSJSON}' | |
- | |
name: event_time | |
value: '{EVENT.TIME}' | |
- | |
name: event_update_date | |
value: '{EVENT.UPDATE.DATE}' | |
- | |
name: event_update_status | |
value: '{EVENT.UPDATE.STATUS}' | |
- | |
name: event_update_time | |
value: '{EVENT.UPDATE.TIME}' | |
- | |
name: event_value | |
value: '{EVENT.VALUE}' | |
- | |
name: host_conn | |
value: '{HOST.CONN}' | |
- | |
name: host_name | |
value: '{HOST.NAME}' | |
- | |
name: slack_mode | |
value: alarm | |
- | |
name: trigger_description | |
value: '{TRIGGER.DESCRIPTION}' | |
- | |
name: trigger_id | |
value: '{TRIGGER.ID}' | |
- | |
name: zabbix_url | |
value: '{$ZABBIX.URL}' | |
script: | | |
var SEVERITY_COLORS = [ | |
'#97AAB3', '#7499FF', '#FFC859', | |
'#FFA059', '#E97659', '#E45959' | |
]; | |
var RESOLVE_COLOR = '#009900'; | |
var SLACK_MODE_HANDLERS = { | |
alarm: handlerAlarm, | |
event: handlerEvent | |
}; | |
if (!String.prototype.format) { | |
String.prototype.format = function() { | |
var args = arguments; | |
return this.replace(/{(\d+)}/g, function(match, number) { | |
return number in args | |
? args[number] | |
: match | |
; | |
}); | |
}; | |
} | |
function isEventProblem(params) { | |
return params.event_value == 1 | |
&& params.event_update_status == 0 | |
; | |
} | |
function isEventUpdate(params) { | |
return params.event_value == 1 | |
&& params.event_update_status == 1 | |
; | |
} | |
function isEventResolve(params) { | |
return params.event_value == 0; | |
} | |
function getPermalink(channelId, messageTimestamp) { | |
var req = new HttpRequest(); | |
if (typeof params.HTTPProxy === 'string' && params.HTTPProxy.trim() !== '') { | |
req.setProxy(params.HTTPProxy); | |
} | |
req.addHeader('Content-Type: application/x-www-form-urlencoded; charset=utf-8'); | |
req.addHeader('Authorization: Bearer ' + params.bot_token); | |
var query = '{0}?channel={1}&message_ts={2}'.format( | |
Slack.getPermalink, | |
encodeURIComponent(channelId), | |
encodeURIComponent(messageTimestamp)), | |
resp = JSON.parse(req.get(query)); | |
if (req.getStatus() != 200 || !resp.ok || resp.ok === 'false') { | |
throw 'message was created, but getting message link was failed with reason "' + resp.error + '"'; | |
} | |
return resp.permalink; | |
} | |
function createProblemURL(zabbix_url, triggerid, eventid, event_source) { | |
var problem_url = ''; | |
if (event_source === '0') { | |
problem_url = '{0}/tr_events.php?triggerid={1}&eventid={2}' | |
.format( | |
zabbix_url, | |
triggerid, | |
eventid | |
); | |
} | |
else { | |
problem_url = zabbix_url; | |
} | |
return problem_url; | |
} | |
function handlerAlarm(params) { | |
var fields = { | |
channel: params.channel, | |
}; | |
if (isEventProblem(params)) { | |
fields.attachments = [ | |
createMessage( | |
SEVERITY_COLORS[params.event_nseverity] || 0, | |
params.event_date, | |
params.event_time, | |
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source) | |
) | |
]; | |
var resp = JSON.parse(req.post(Slack.postMessage, JSON.stringify(fields))); | |
if (req.getStatus() != 200 || !resp.ok || resp.ok === 'false') { | |
throw resp.error; | |
} | |
result.tags = { | |
['__message_ts_' + params.channel]: resp.ts, | |
['__channel_id_' + params.channel]: resp.channel, | |
['__message_link_' + params.channel]: getPermalink(resp.channel, resp.ts), | |
}; | |
} | |
else if (isEventUpdate(params)) { | |
try { | |
var channel_event_tags = JSON.parse(params.event_tags); | |
} catch (error) { | |
throw 'Cannot process event tags: ' + error; | |
} | |
if (Array.isArray(channel_event_tags)) { | |
for (i in channel_event_tags) { | |
if (channel_event_tags[i].tag.includes('__message_ts_' + params.channel)) { | |
fields.thread_ts = channel_event_tags[i].value; | |
break; | |
} | |
} | |
} | |
fields.attachments = [ | |
createMessage( | |
SEVERITY_COLORS[params.event_nseverity] || 0, | |
params.event_update_date, | |
params.event_update_time, | |
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source), | |
true | |
) | |
]; | |
resp = JSON.parse(req.post(Slack.postMessage, JSON.stringify(fields))); | |
if (req.getStatus() != 200 || !resp.ok || resp.ok === 'false') { | |
throw resp.error; | |
} | |
} | |
else if (isEventResolve(params)) { | |
fields.text = ''; | |
try { | |
var channel_event_tags = JSON.parse(params.event_tags); | |
} catch (error) { | |
throw 'Cannot process event tags: ' + error; | |
} | |
if (Array.isArray(channel_event_tags)) { | |
for (i in channel_event_tags) { | |
if (channel_event_tags[i].tag.includes('__channel_id_' + params.channel)) { | |
fields.channel = channel_event_tags[i].value; | |
continue; | |
} | |
if (channel_event_tags[i].tag.includes('__message_ts_' + params.channel)) { | |
fields.ts = channel_event_tags[i].value; | |
} | |
} | |
} | |
fields.attachments = [ | |
createMessage( | |
RESOLVE_COLOR, | |
params.event_date, | |
params.event_time, | |
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source) | |
) | |
]; | |
resp = JSON.parse(req.post(Slack.chatUpdate, JSON.stringify(fields))); | |
if (req.getStatus() != 200 || !resp.ok || resp.ok === 'false') { | |
throw resp.error; | |
} | |
} | |
} | |
function handlerEvent(params) { | |
var fields = { | |
channel: params.channel, | |
}; | |
if (isEventProblem(params)) { | |
fields.attachments = [ | |
createMessage( | |
SEVERITY_COLORS[params.event_nseverity] || 0, | |
params.event_date, | |
params.event_time, | |
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source) | |
) | |
]; | |
var resp = JSON.parse(req.post(Slack.postMessage, JSON.stringify(fields))); | |
if (req.getStatus() != 200 || !resp.ok || resp.ok === 'false') { | |
throw resp.error; | |
} | |
result.tags = { | |
['__message_link_' + params.channel]: getPermalink(resp.channel, resp.ts) | |
} | |
} | |
else if (isEventUpdate(params)) { | |
fields.attachments = [ | |
createMessage( | |
SEVERITY_COLORS[params.event_nseverity] || 0, | |
params.event_update_date, | |
params.event_update_time, | |
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source), | |
false | |
) | |
]; | |
resp = JSON.parse(req.post(Slack.postMessage, JSON.stringify(fields))); | |
if (req.getStatus() != 200 || !resp.ok || resp.ok === 'false') { | |
throw resp.error; | |
} | |
} | |
else if (isEventResolve(params)) { | |
fields.attachments = [ | |
createMessage( | |
RESOLVE_COLOR, | |
params.event_recovery_date, | |
params.event_recovery_time, | |
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source) | |
) | |
]; | |
resp = JSON.parse(req.post(Slack.postMessage, JSON.stringify(fields))); | |
if (req.getStatus() != 200 || !resp.ok || resp.ok === 'false') { | |
throw resp.error; | |
} | |
} | |
} | |
function createMessage( | |
event_severity_color, | |
event_date, | |
event_time, | |
problem_url, | |
isShort, | |
messageText | |
) { | |
var message = { | |
fallback: params.alert_subject, | |
title: params.alert_subject, | |
color: event_severity_color, | |
title_link: problem_url, | |
pretext: messageText || '', | |
fields: [ | |
{ | |
title: 'Host', | |
value: '{0} [{1}]'.format(params.host_name, params.host_conn), | |
short: true | |
}, | |
{ | |
title: 'Event time', | |
value: '{0} {1}'.format(event_date, event_time), | |
short: true | |
} | |
], | |
}; | |
if (params.event_source === '0') { | |
message.fields.push( | |
{ | |
title: 'Severity', | |
value: params.event_severity, | |
short: true | |
}, | |
{ | |
title: 'Opdata', | |
value: params.event_opdata, | |
short: true | |
} | |
); | |
} | |
if (!isShort && params.event_source === '0') { | |
message['actions'] = [ | |
{ | |
type: 'button', | |
text: 'Open in Zabbix', | |
url: problem_url | |
} | |
]; | |
message.fields.push( | |
{ | |
title: 'Event tags', | |
value: JSON.parse(params.event_tags).filter(function (e) { return !e.tag.includes('__') }).map(function (e) { return e.tag + ': ' + e.value }).join('\n') || 'None', | |
short: true | |
}, | |
{ | |
title: 'Trigger description', | |
value: params.trigger_description, | |
short: true | |
} | |
); | |
} | |
if (params.event_source !== '0' || params.event_update_status === '1') { | |
message.fields.push( | |
{ | |
title: 'Details', | |
value: params.alert_message, | |
short: false | |
} | |
); | |
} | |
return message; | |
} | |
function validateParams(params) { | |
if (typeof params.bot_token !== 'string' || params.bot_token.trim() === '') { | |
throw 'Field "bot_token" cannot be empty'; | |
} | |
if (typeof params.channel !== 'string' || params.channel.trim() === '') { | |
throw 'Field "channel" cannot be empty'; | |
} | |
if (isNaN(params.event_id)) { | |
throw 'Field "event_id" is not a number'; | |
} | |
if ([0, 1, 2, 3].indexOf(parseInt(params.event_source)) === -1) { | |
throw 'Incorrect "event_source" parameter given: "' + params.event_source + '".\nMust be 0-3.'; | |
} | |
if (params.event_source !== '0') { | |
params.event_nseverity = '0'; | |
params.event_severity = 'Not classified'; | |
params.event_update_status = '0'; | |
params.slack_mode = 'event'; | |
} | |
if (params.event_source === '1' || params.event_source === '2') { | |
params.event_value = '1'; | |
} | |
if (params.event_source === '1') { | |
params.host_name = params.discovery_host_dns; | |
params.host_ip = params.discovery_host_ip; | |
} | |
if (!~[0, 1, 2, 3, 4, 5].indexOf(parseInt(params.event_nseverity))) { | |
throw 'Incorrect "event_nseverity" parameter given: ' + params.event_nseverity + '\nMust be 0-5.'; | |
} | |
if (typeof params.event_severity !== 'string' || params.event_severity.trim() === '') { | |
throw 'Field "event_severity" cannot be empty'; | |
} | |
if (params.event_update_status !== '0' && params.event_update_status !== '1') { | |
throw 'Incorrect "event_update_status" parameter given: ' + params.event_update_status + '\nMust be 0 or 1.'; | |
} | |
if (params.event_value !== '0' && params.event_value !== '1') { | |
throw 'Incorrect "event_value" parameter given: ' + params.event_value + '\nMust be 0 or 1.'; | |
} | |
if (typeof params.host_conn !== 'string' || params.host_conn.trim() === '') { | |
throw 'Field "host_conn" cannot be empty'; | |
} | |
if (typeof params.host_name !== 'string' || params.host_name.trim() === '') { | |
throw 'Field "host_name" cannot be empty'; | |
} | |
if (!~['alarm', 'event'].indexOf(params.slack_mode)) { | |
throw 'Incorrect "slack_mode" parameter given: ' + params.slack_mode + '\nMust be "alarm" or "event".'; | |
} | |
if (isNaN(params.trigger_id) && params.event_source === '0') { | |
throw 'field "trigger_id" is not a number'; | |
} | |
if (typeof params.zabbix_url !== 'string' || params.zabbix_url.trim() === '') { | |
throw 'Field "zabbix_url" cannot be empty'; | |
} | |
if (!/^(http|https):\/\/.+/.test(params.zabbix_url)) { | |
throw 'Field "zabbix_url" must contain a schema'; | |
} | |
} | |
try { | |
var params = JSON.parse(value); | |
validateParams(params); | |
var req = new HttpRequest(), | |
result = {tags: {}}; | |
if (typeof params.HTTPProxy === 'string' && params.HTTPProxy.trim() !== '') { | |
req.setProxy(params.HTTPProxy); | |
} | |
req.addHeader('Content-Type: application/json; charset=utf-8'); | |
req.addHeader('Authorization: Bearer ' + params.bot_token); | |
var slack_endpoint = 'https://slack.com/api/'; | |
var Slack = { | |
postMessage: slack_endpoint + 'chat.postMessage', | |
getPermalink: slack_endpoint + 'chat.getPermalink', | |
chatUpdate: slack_endpoint + 'chat.update' | |
}; | |
params.slack_mode = params.slack_mode.toLowerCase(); | |
params.slack_mode = params.slack_mode in SLACK_MODE_HANDLERS | |
? params.slack_mode | |
: 'alarm'; | |
SLACK_MODE_HANDLERS[params.slack_mode](params); | |
if (params.event_source === '0') { | |
return JSON.stringify(result); | |
} | |
else { | |
return 'OK'; | |
} | |
} | |
catch (error) { | |
Zabbix.log(4, '[ Slack Webhook ] Slack notification failed : ' + error); | |
throw 'Slack notification failed : ' + error; | |
} | |
process_tags: 'YES' | |
message_templates: | |
- | |
event_source: TRIGGERS | |
operation_mode: PROBLEM | |
subject: 'Problem: {EVENT.NAME}' | |
message: | | |
Problem started at {EVENT.TIME} on {EVENT.DATE} | |
Problem name: {EVENT.NAME} | |
Host: {HOST.NAME} | |
Severity: {EVENT.SEVERITY} | |
Operational data: {EVENT.OPDATA} | |
Original problem ID: {EVENT.ID} | |
{TRIGGER.URL} | |
- | |
event_source: TRIGGERS | |
operation_mode: RECOVERY | |
subject: 'Resolved in {EVENT.DURATION}: {EVENT.NAME}' | |
message: | | |
Problem has been resolved at {EVENT.RECOVERY.TIME} on {EVENT.RECOVERY.DATE} | |
Problem name: {EVENT.NAME} | |
Problem duration: {EVENT.DURATION} | |
Host: {HOST.NAME} | |
Severity: {EVENT.SEVERITY} | |
Original problem ID: {EVENT.ID} | |
{TRIGGER.URL} | |
- | |
event_source: TRIGGERS | |
operation_mode: UPDATE | |
subject: 'Updated problem in {EVENT.AGE}: {EVENT.NAME}' | |
message: | | |
{USER.FULLNAME} {EVENT.UPDATE.ACTION} problem at {EVENT.UPDATE.DATE} {EVENT.UPDATE.TIME}. | |
{EVENT.UPDATE.MESSAGE} | |
Current problem status is {EVENT.STATUS}, age is {EVENT.AGE}, acknowledged: {EVENT.ACK.STATUS}. | |
- | |
event_source: DISCOVERY | |
operation_mode: PROBLEM | |
subject: 'Discovery: {DISCOVERY.DEVICE.STATUS} {DISCOVERY.DEVICE.IPADDRESS}' | |
message: | | |
Discovery rule: {DISCOVERY.RULE.NAME} | |
Device IP: {DISCOVERY.DEVICE.IPADDRESS} | |
Device DNS: {DISCOVERY.DEVICE.DNS} | |
Device status: {DISCOVERY.DEVICE.STATUS} | |
Device uptime: {DISCOVERY.DEVICE.UPTIME} | |
Device service name: {DISCOVERY.SERVICE.NAME} | |
Device service port: {DISCOVERY.SERVICE.PORT} | |
Device service status: {DISCOVERY.SERVICE.STATUS} | |
Device service uptime: {DISCOVERY.SERVICE.UPTIME} | |
- | |
event_source: AUTOREGISTRATION | |
operation_mode: PROBLEM | |
subject: 'Autoregistration: {HOST.HOST}' | |
message: | | |
Host name: {HOST.HOST} | |
Host IP: {HOST.IP} | |
Agent port: {HOST.PORT} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment