diff options
author | Andrew Biba <andrew.biba@zabbix.com> | 2022-04-06 15:35:34 +0300 |
---|---|---|
committer | Andrew Biba <andrew.biba@zabbix.com> | 2022-04-06 15:35:34 +0300 |
commit | 393d1d39b9bdf44ce4cdf64cb374b783bda2ccf2 (patch) | |
tree | 419830a9989f35e0ba65fd13d6f623e923b66162 /templates | |
parent | ccfbdc1a9137fe6208c1d3e445afdf11335f1a52 (diff) |
.........T [ZBXNEXT-7505] added new GLPi media type
Diffstat (limited to 'templates')
-rw-r--r-- | templates/media/glpi/README.md | 67 | ||||
-rw-r--r-- | templates/media/glpi/images/1.png | bin | 0 -> 11449 bytes | |||
-rw-r--r-- | templates/media/glpi/images/1.thumb.png | bin | 0 -> 22093 bytes | |||
-rw-r--r-- | templates/media/glpi/images/2.png | bin | 0 -> 5910 bytes | |||
-rw-r--r-- | templates/media/glpi/images/2.thumb.png | bin | 0 -> 12109 bytes | |||
-rw-r--r-- | templates/media/glpi/images/3.png | bin | 0 -> 37864 bytes | |||
-rw-r--r-- | templates/media/glpi/images/3.thumb.png | bin | 0 -> 51848 bytes | |||
-rw-r--r-- | templates/media/glpi/media_glpi.yaml | 375 |
8 files changed, 442 insertions, 0 deletions
diff --git a/templates/media/glpi/README.md b/templates/media/glpi/README.md new file mode 100644 index 00000000000..43a061cb42d --- /dev/null +++ b/templates/media/glpi/README.md @@ -0,0 +1,67 @@ + +# GLPi webhook + +## About webhook + +This webhook creates problems in GLPi Assistance section. Created problems have the next severity mapping: + +|Severity In zabbix|Urgency in GLPi| +|-|-| +0 - Not classified| Medium (default)| +1 - Information| Very low| +2 - Warning| Low| +3 - Average| Medium| +4 - High| High| +5 - Disaster| Very High| + +On Update action in zabbix, webhook updates created problem's title, severity and creates followup with update comment. + +On resolve action, webhook updates created problem title and creates followup with resolve information. + +Created problems have "New" status, and resolved - "Solved" status. + +Due to the specifics of the webhook, the number of retries is set to 1 by default. We recommend that you do not change this setting, because in case of a transaction error, additional duplicate objects (problems, followups) may be created during the retry. + +## Installation guide + +This guide describes how to integrate your Zabbix installation with GLPi problems using the Zabbix webhook feature. This guide provides instructions on setting up a media type, a user and an action in Zabbix. +<br/><br/> +## In GLPi + +1\. Create or use existing user in GLPi with permission to create problems and followups. +[![](images/1.thumb.png?raw=true)](images/1.png) +[![](images/2.thumb.png?raw=true)](images/2.png) + +2\. Please create an **API token**. For that you should go into user profile and set tick in "Regenerate" field against "API token" and hit save. +[![](images/3.thumb.png?raw=true)](images/3.png) + + +3\. Copy the **API token** of your new integration to use it in Zabbix. +<br/><br/> +## In Zabbix + +The configuration consists of a _media type_ in Zabbix, which will invoke the webhook to send alerts to GLPi problems through the GLPi Rest API. + + +1\. [Import](https://www.zabbix.com/documentation/6.2/manual/web_interface/frontend_sections/administration/mediatypes) the GLPi media type from file [media_glpi.yaml](media_glpi.yaml). + +2\. Change in the imported media the values of the variable *glpi_token* and *glpi_url*. + + +For more information about the Zabbix Webhook configuration, please see the [documentation](https://www.zabbix.com/documentation/6.2/manual/config/notifications/media/webhook). + +3\. Create a **Zabbix user** and add **Media** with the **GLPi** media type. +Though a "Send to" field is not used in GLPi webhook, it cannot be empty. To comply with frontend requirements, you can put any symbol there. +Make sure this user has access to all hosts for which you would like problem notifications to be converted into GLPi problems. + +4\. Set up a global macro {$ZABBIX.URL} with URL of current zabbix. Please notice that HTTPS will be used by default if HTTP/HTTPS schema is not present in the URL. + +For more information, please see [Zabbix](https://www.zabbix.com/documentation/6.2/manual/config/notifications) and [GLPi](https://glpi-project.org/DOC/EN/) documentation. +<br/><br/> + +## Tested on +GLPI 9.5.7 +<br/><br/> +## Supported Versions + +Zabbix 6.2 diff --git a/templates/media/glpi/images/1.png b/templates/media/glpi/images/1.png Binary files differnew file mode 100644 index 00000000000..608a8295e3b --- /dev/null +++ b/templates/media/glpi/images/1.png diff --git a/templates/media/glpi/images/1.thumb.png b/templates/media/glpi/images/1.thumb.png Binary files differnew file mode 100644 index 00000000000..886b48a7a8c --- /dev/null +++ b/templates/media/glpi/images/1.thumb.png diff --git a/templates/media/glpi/images/2.png b/templates/media/glpi/images/2.png Binary files differnew file mode 100644 index 00000000000..7f0371e40d3 --- /dev/null +++ b/templates/media/glpi/images/2.png diff --git a/templates/media/glpi/images/2.thumb.png b/templates/media/glpi/images/2.thumb.png Binary files differnew file mode 100644 index 00000000000..b8cf38d3d88 --- /dev/null +++ b/templates/media/glpi/images/2.thumb.png diff --git a/templates/media/glpi/images/3.png b/templates/media/glpi/images/3.png Binary files differnew file mode 100644 index 00000000000..f0375146973 --- /dev/null +++ b/templates/media/glpi/images/3.png diff --git a/templates/media/glpi/images/3.thumb.png b/templates/media/glpi/images/3.thumb.png Binary files differnew file mode 100644 index 00000000000..95d96a9838a --- /dev/null +++ b/templates/media/glpi/images/3.thumb.png diff --git a/templates/media/glpi/media_glpi.yaml b/templates/media/glpi/media_glpi.yaml new file mode 100644 index 00000000000..b52a435f983 --- /dev/null +++ b/templates/media/glpi/media_glpi.yaml @@ -0,0 +1,375 @@ +zabbix_export: + version: '6.2' + date: '2022-03-24T18:54:55Z' + media_types: + - + name: GLPi + type: WEBHOOK + parameters: + - + name: alert_message + value: '{ALERT.MESSAGE}' + - + name: alert_subject + value: '{ALERT.SUBJECT}' + - + name: event_id + value: '{EVENT.ID}' + - + name: event_nseverity + value: '{EVENT.NSEVERITY}' + - + name: event_recovery_value + value: '{EVENT.RECOVERY.VALUE}' + - + name: event_source + value: '{EVENT.SOURCE}' + - + name: event_update_status + value: '{EVENT.UPDATE.STATUS}' + - + name: event_value + value: '{EVENT.VALUE}' + - + name: glpi_problem_id + value: '{EVENT.TAGS.__zbx_glpi_problem_id}' + - + name: glpi_token + value: '<PLACE GLPI TOKEN>' + - + name: glpi_url + value: '<PLACE GLPI URL>' + - + name: trigger_id + value: '{TRIGGER.ID}' + - + name: zabbix_url + value: '{$ZABBIX.URL}' + attempts: '1' + script: | + var GLPi = { + params: {}, + + setParams: function (params) { + if (typeof params !== 'object') { + return; + } + GLPi.params = params; + }, + + setProxy: function (HTTPProxy) { + GLPi.HTTPProxy = HTTPProxy; + }, + + urlCheckFormat: function (url) { + if (typeof url === 'string' && !url.endsWith('/')) { + url += '/'; + } + + if (url.indexOf('http://') === -1 && url.indexOf('https://') === -1) { + url = 'https://' + url; + } + + return url; + }, + + getAuthToken: function (url, token) { + var response, + request = new HttpRequest(); + + request.addHeader('Content-Type: application/json'); + request.addHeader('Authorization: user_token ' + token); + + response = request.get(url + "apirest.php/initSession"); + + if (response !== null) { + try { + response = JSON.parse(response); + } + catch (error) { + Zabbix.log(4, '[ GLPi Webhook ] Failed to receive authentication token from GLPi.'); + response = null; + } + } + + if (Array.isArray(response)) { + if (response[1]) { + throw 'Error received from GLPi: ' + response[1]; + } else { + throw 'Failed to receive authentication token from GLPi.'; + } + } + + if (typeof response !== 'object' || !response.session_token) { + throw 'Failed to process response received from getting GLPi authentication token. Check debug log for more information.'; + } + + return response.session_token; + }, + + getProblemUrl: function (zabbix_url, triggerid, eventid, event_source) { + var problem_url = zabbix_url; + + if (event_source === '0') { + problem_url += 'tr_events.php?triggerid=' + triggerid + '&eventid=' + eventid; + } + + return problem_url; + }, + + request: function (method, url, data) { + if (typeof GLPi.params !== 'object' || typeof GLPi.params.authToken === 'undefined' || GLPi.params.authToken === '') { + throw 'Required GLPi param authToken is not set.'; + } + + var response, + request = new HttpRequest(); + + request.addHeader('Content-Type: application/json'); + request.addHeader('Session-Token:' + GLPi.params.authToken); + + if (typeof GLPi.HTTPProxy !== 'undefined' && GLPi.HTTPProxy !== '') { + request.setProxy(GLPi.HTTPProxy); + } + + if (typeof data !== 'undefined') { + data = JSON.stringify(data); + } + + Zabbix.log(4, '[ GLPi Webhook ] Sending request: ' + url + ((typeof data === 'string') + ? ('\n' + data) + : '')); + + switch (method) { + case 'post': + response = request.post(url, data); + break; + + case 'put': + response = request.put(url, data); + break; + + default: + throw 'Unsupported HTTP request method: ' + method; + } + + Zabbix.log(4, '[ GLPi Webhook ] Received response with status code ' + + request.getStatus() + '\n' + response); + + if (response !== null) { + try { + response = JSON.parse(response); + } + catch (error) { + Zabbix.log(4, '[ GLPi Webhook ] Failed to parse response received from GLPi'); + response = null; + } + } + + if (typeof response !== 'object' || typeof response === 'undefined' || response === null) { + throw 'Failed to process response received from GLPi. Check debug log for more information.'; + } + + if (request.getStatus() < 200 || request.getStatus() >= 300) { + var message = 'Request failed with status code ' + request.getStatus(); + + if (response.message) { + message += ': ' + response.message; + } + + throw message + ' Check debug log for more information.'; + } + + return response; + } + }; + + try { + var params = JSON.parse(value), + glpi = {}, + url = '', + data = {}, + comment_data, + result = { tags: {} }, + required_params = [ + 'alert_subject', 'alert_message', 'event_source', 'event_value', + 'event_update_status', 'event_recovery_value', + 'event_id', 'trigger_id', 'zabbix_url', + 'glpi_token', 'glpi_url' + ], + method = 'post', + process_tags = true, + response; + + Object.keys(params) + .forEach(function (key) { + if (key.startsWith('glpi_')) { + glpi[key.substring(5)] = params[key]; + } + else if (required_params.indexOf(key) !== -1 && params[key] === '') { + throw 'Parameter "' + key + '" can\'t be empty.'; + } + }); + + if ([0, 1, 2, 3].indexOf(parseInt(params.event_source)) === -1) { + throw 'Incorrect "event_source" parameter given: ' + params.event_source + '\nMust be 0-3.'; + } + + // Check {EVENT.VALUE} for trigger-based and internal events. + if (params.event_value !== '0' && params.event_value !== '1' + && (params.event_source === '0' || params.event_source === '3')) { + throw 'Incorrect "event_value" parameter given: ' + params.event_value + '\nMust be 0 or 1.'; + } + + // Check {EVENT.UPDATE.STATUS} only for trigger-based events. + if (params.event_update_status !== '0' && params.event_update_status !== '1' && params.event_source === '0') { + throw 'Incorrect "event_update_status" parameter given: ' + params.event_update_status + '\nMust be 0 or 1.'; + } + + if (params.event_source !== '0' && params.event_recovery_value === '0') { + throw 'Recovery operations are supported only for trigger-based actions.'; + } + + if (typeof params.zabbix_url !== 'string' || params.zabbix_url.trim() === '' || params.zabbix_url === '{$ZABBIX.URL}') { + throw 'Field "zabbix_url" cannot be empty.'; + } + + // Check for backslash in the end of url and schema. + glpi.url = GLPi.urlCheckFormat(glpi.url); + params.zabbix_url = GLPi.urlCheckFormat(params.zabbix_url); + + glpi.authToken = GLPi.getAuthToken(glpi.url, glpi.token); + GLPi.setParams(glpi); + + data = { + 'input': { + 'name': params.alert_subject, + 'content': params.alert_message + '\n<a href=' + GLPi.getProblemUrl(params.zabbix_url, params.trigger_id, params.event_id, params.event_source) + '>Link to problem in Zabbix</a>', + 'status': 1, // Set status "New" + 'urgency': params.event_nseverity + } + }; + + // In case of resolve + if (params.event_source === '0' && params.event_value === '0') { + process_tags = false; + dataFollowup = { + 'input': { + 'items_id': glpi.problem_id, + 'itemtype': 'Problem', + 'content': params.alert_message + '\n<a href=' + GLPi.getProblemUrl(params.zabbix_url, params.trigger_id, params.event_id, params.event_source) + '>Link to problem in Zabbix</a>' + } + }; + dataProblem = { + 'id': glpi.problem_id, + 'input': { + 'name': params.alert_subject, + 'status': 5, // Set status "Solved" + 'urgency': params.event_nseverity + } + }; + + GLPi.request('put', glpi.url + 'apirest.php/Problem/' + glpi.problem_id, dataProblem); + GLPi.request('post', glpi.url + 'apirest.php/Problem/' + glpi.problem_id + '/ITILFollowup', dataFollowup); + } + + // In case of update + else if (params.event_source === '0' && params.event_update_status === '1') { + process_tags = false; + dataFollowup = { + 'input': { + 'items_id': glpi.problem_id, + 'itemtype': 'Problem', + 'content': params.alert_message + '\n<a href=' + GLPi.getProblemUrl(params.zabbix_url, params.trigger_id, params.event_id, params.event_source) + '>Link to problem in Zabbix</a>' + } + }; + dataProblem = { + 'id': glpi.problem_id, + 'input': { + 'name': params.alert_subject, + 'urgency': params.event_nseverity + } + }; + + GLPi.request('put', glpi.url + 'apirest.php/Problem/' + glpi.problem_id, dataProblem); + GLPi.request('post', glpi.url + 'apirest.php/Problem/' + glpi.problem_id + '/ITILFollowup', dataFollowup); + } + + // In case of problem + else { + response = GLPi.request('post', glpi.url + 'apirest.php/Problem/', data); + } + + if (process_tags) { + result.tags.__zbx_glpi_problem_id = response.id; + result.tags.__zbx_glpi_link = glpi.url + 'front/problem.form.php?id=' + response.id; + } + + Zabbix.log(4, '[ GLPi Webhook ] Result: ' + JSON.stringify(result)); + return JSON.stringify(result); + } + catch (error) { + Zabbix.log(4, '[ GLPi Webhook ] ERROR: ' + error); + throw 'Sending failed: ' + error; + } + process_tags: 'YES' + show_event_menu: 'YES' + event_menu_url: '{EVENT.TAGS.__zbx_glpi_link}' + event_menu_name: 'GLPi: Problem {EVENT.TAGS.__zbx_glpi_problem_id}' + message_templates: + - + event_source: TRIGGERS + operation_mode: PROBLEM + subject: '[{EVENT.STATUS}] {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: '[{EVENT.STATUS}] {EVENT.NAME}' + message: | + Problem has been resolved in {EVENT.DURATION} at {EVENT.RECOVERY.TIME} on {EVENT.RECOVERY.DATE} + Problem name: {EVENT.NAME} + Host: {HOST.NAME} + Severity: {EVENT.SEVERITY} + Original problem ID: {EVENT.ID} + {TRIGGER.URL} + - + event_source: TRIGGERS + operation_mode: UPDATE + subject: '[{EVENT.STATUS}] {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}, 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} |