mirror of
https://github.com/moudsen/mailGraph
synced 2025-10-28 16:17:39 +01:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb96ecaf24 | ||
|
|
71df37e612 | ||
|
|
e861d0c7d0 | ||
|
|
73ce4adb9c | ||
|
|
e262f21dc8 | ||
|
|
7f6d5ccd03 | ||
|
|
162bf1b932 | ||
|
|
6ff6a2c55d | ||
|
|
7ee5324aeb | ||
|
|
88d8c018f5 | ||
|
|
b7935b0a47 | ||
|
|
d624fa694d | ||
|
|
7c4d8a7ff8 | ||
|
|
1a82a1ac85 | ||
|
|
5797538519 | ||
|
|
0c7d577975 | ||
|
|
497e7c3fe9 | ||
|
|
fd48cb604e | ||
|
|
f2075d161d | ||
|
|
2970f10be1 | ||
|
|
e3913d9813 | ||
|
|
c0e31d37be | ||
|
|
42bea0df98 | ||
|
|
cb8d2cb546 | ||
|
|
2808544170 | ||
|
|
995fdf0b68 | ||
|
|
db81193e17 | ||
|
|
1fb10ed089 | ||
|
|
7e1c5e5d98 | ||
|
|
4f4c41f991 | ||
|
|
15512cf35a | ||
|
|
2ba2946f30 | ||
|
|
ff0c686ae0 |
49
CODE_OF_CONDUCT.md
Normal file
49
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at abuse@puzzl.nl. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq
|
||||
162
README.md
162
README.md
@@ -1,161 +1,7 @@
|
||||
# mailGraph
|
||||
## mailGraph (v1.22)
|
||||
Zabbix Media module and scripts for sending e-mail alerts with graphs.
|
||||
|
||||
[](images/Example-mail-message.png)
|
||||
**Please use the Wiki for information on how to install, configure and use MailGraph in Zabbix.**
|
||||
|
||||
# WORK IN PROGRESS
|
||||
Although still under development (consider the current release "beta"), I need feedback and interaction with other users of Zabbix that are looking for the functionality I've developed hence I'm releasing my code to the world.
|
||||
|
||||
**List of item to-do**
|
||||
1. Resolve macro information.
|
||||
1. Passing dynamic parameters via the Webhook for template/output usage.
|
||||
1. Finding "beta testers" to assist me in further enhancing the use cases.
|
||||
|
||||
# Zabbix enhancements
|
||||
https://support.zabbix.com/browse/ZBXNEXT-6534
|
||||
Main ticket asking how to get this Media type onboarded in the Media type section of the manual and the associated Zabbix GitHub directory.
|
||||
|
||||
https://support.zabbix.com/browse/ZBXNEXT-6535
|
||||
One of the major items that needs to be tackled is to resolve Macros that are in names, descriptions, etc. As this does work for the Trigger and TriggerPrototype I've examined the Zabbix source code and from here I would say it's quite "easy" to add the "expandXXX" flags to other types as well (found many existing functions to perform this).
|
||||
I'm adding this to my testlab setup for Item and ItemPrototype and will share the outcome. If it is succesfull I will also arrange for other types (like Host) eventually giving back the additional code lines to Zabbix for incorporation into a release.
|
||||
|
||||
# Installation pre-requisites
|
||||
The suggested installation path of this script is on the same host where Zabbix lives but outside the actual Zabbix directory, although it is possible to run the script entirely somewhere else (the code is webhook based, picking up information from Zabbix is via the front-end login and API).
|
||||
|
||||
# I'm assuming
|
||||
- you are familiar with "composer"
|
||||
- you know how to configure a webserver/virtual host
|
||||
- that you have CURL and PHP installed
|
||||
|
||||
# Prepare the installation
|
||||
- Pick a directory within a (virtual) host of your webserver
|
||||
- Create the directories "config", "images", "log", "templates" and "tmp" inside this directory
|
||||
- Copy .htaccess to the main directory _(if not using Apache make sure your webserver denies access to /config!)_
|
||||
- Copy mailGraph.php to the main directory
|
||||
- Install SwiftMailer: `composer require swiftmailer/swiftmailer`
|
||||
- Install TWIG: `composer require twig/twig`
|
||||
- Copy config/config.php to your /config directory
|
||||
- Copy config/config.json.template to your /config/ directory and rename to "config.json"
|
||||
- Copy templates/html.template and templates/plain.template to your templates directory
|
||||
- Copy mailGraph.xml to a location where you can upload the Media Type to Zabbix
|
||||
|
||||
# Configuration
|
||||
- Goto your /config directory
|
||||
- Two ways to configure the config.json file: 1) with config.php or 2) with your favorite text editor (note that you must have knowledge of JSON format to use this option)'
|
||||
- List the available configuration options with "php config.php config.json list"
|
||||
- Change any option with "php config.php config.json replace 'key_name' 'new_value'" (note the usage of the single quotes from the command-line!)
|
||||
|
||||
**"script_baseurl"** should point to the URL of your directory (ie. "https://mydomain.com/mailgraph/"). Note the ending '/'!
|
||||
|
||||
**"zabbix_user"** must be a Zabbix SuperAdmin user you create to login to Zabbix (this is for grabbing the images via the regular Zabbix routines).
|
||||
|
||||
**"zabbix_user_api"** must also be a Zabbix SuperAdmin user your create to login to the Zabbix API (this is for grabbing the information of the event via the Zabbix API).
|
||||
|
||||
**"mail_from"** must be a valid e-mail address which represents the 'from' address in the mails that are sent (ie. "zabbix.mailgraph.noreply@domain.com").
|
||||
|
||||
# Load the Media Type "MailGraph" into Zabbix
|
||||
- Login to your Zabbix instance
|
||||
- Goto "Administration" => "Media yypes"
|
||||
- Import the "mailGraph.xml" file
|
||||
- Edit the new media type
|
||||
- Configure some of the macros associated
|
||||
|
||||
"baseURL" must contain your Zabbix URL (ie. "https://mydomain.com/zabbix/"). Note the ending '/'!
|
||||
|
||||
You can set your custom "graphWidth" and "GraphHeight" to your convenience.
|
||||
|
||||
You can switch the graph legend on/off with "showLegend" (0=off,1=on).
|
||||
|
||||
You can change the "subject" of the e-mail that is sent (note that the markup can be a combination of Zabbix MACRO or TWIG notation!).
|
||||
|
||||
"URL" is the url to the mailGraph script (ie. "https://mydomain.com/mailGraph.php").
|
||||
|
||||
# Actions configuration
|
||||
At this point the Media type is ready for configuration under "actions" as per the regular way of Zabbix alert processing. Please refer to the manual how to configure.
|
||||
|
||||
# Template adjustments
|
||||
I've picked TWIG as the template processor, where the following macros are available for your convenience. Feel free to adjust the html.template and plain.template files as you see fit for your situation!
|
||||
|
||||
Values available:
|
||||
|
||||
{{ baseURL }} - base url of the Zabbix system (use for references to API and login)
|
||||
|
||||
{{ TRIGGER_ID }} - id of the applicable trigger
|
||||
|
||||
{{ TRIGGER_DESCRIPTION }} - raw trigger description (note: macros are not parsed!)
|
||||
|
||||
{{ TRIGGER_COMMENTS }} - comments of the trigger
|
||||
|
||||
{{ TRIGGER_URL }} - url of the trigger form
|
||||
|
||||
{{ ITEM_ID }} - id of the associated item to the trigger
|
||||
|
||||
{{ ITEM_KEY }} - key of the item
|
||||
|
||||
{{ ITEM_NAME }} - item name
|
||||
|
||||
{{ ITEM_DESCRIPTION }} - description of the item
|
||||
|
||||
{{ ITEM_LASTVALUE }} - last value of the item
|
||||
|
||||
{{ ITEM_PREVIOUSVALUE }} - the value of the before LASTVALUE
|
||||
|
||||
{{ ITEM_URL }} - url of the item form
|
||||
|
||||
{{ HOST_ID }} - id of the associated host to the item
|
||||
|
||||
{{ HOST_NAME }} - name of the host
|
||||
|
||||
{{ HOST_ERROR }} - last error state of the applicable host
|
||||
|
||||
{{ HOST_DESCRIPTION }} - description of the host
|
||||
|
||||
{{ HOST_URL }} - url of the host form
|
||||
|
||||
{{ EVENT_ID }} - id of the associated event
|
||||
|
||||
{{ EVENT_NAME }} - name of the event (note: macros are parsed!)
|
||||
|
||||
{{ EVENT_OPDATA }} - associated operational data of the vent
|
||||
|
||||
{{ EVENT_VALUE }} - event state (0=Recovered, 1=Triggered/Active)
|
||||
|
||||
{{ EVENT_SEVERITY }} - severity of the event
|
||||
|
||||
{{ EVENT_STATUS }} - status of the event
|
||||
|
||||
{{ EVENT_URL }} - url of the event details
|
||||
|
||||
{{ GRAPH_ID }} - id of the (first) associated graph that contains the item
|
||||
|
||||
{{ GRAPH_NAME }} - name of this graph
|
||||
|
||||
{{ GRAPH_URL }} - URL to this graph (assuming script produces to an accessible location)
|
||||
|
||||
{{ GRAPH_CID }} - IMG embed string (<img src="{{ GRAPH_CID }}" />)
|
||||
|
||||
{{ LOG_HTML }} - script log in HTML format
|
||||
|
||||
{{ LOG_PLAIN }} - script log in PLAIN text format
|
||||
|
||||
# Troubleshooting
|
||||
In general if something goes wrong (no output), use the following sequence to identify where the error has occured (and raise an issue in this repository so I can take a look at it):
|
||||
- Goto Zabbix => Reports => Action Log and search for events with Status "Failed"
|
||||
- Note the itemId, triggerId and eventId from this event for testing the Media Type manually
|
||||
- If the popup message says "Syntax Error" something went wrong with the processing during the script. In this case you have to investigate a bit more what is happening.
|
||||
|
||||
The easiest way to test what is happening is to now goto Administration => Media types and hit the "Test" at the right hand side for MailGraph.
|
||||
- Replace relevant macros with information (eventId, triggerId, itemId, recipient, baseUrl and URL) and hit "Test"
|
||||
- The last line in the result will tell you what the problem is (most likely an access or connectivity issue)
|
||||
- Fix accordingly and retry
|
||||
|
||||
To facilitate troubleshooting, you can (at code level):
|
||||
- switch on $cDebugMail to receive processing logs as attachment of an e-mail message
|
||||
- store logs in the /log directory when $cDebug is switched on
|
||||
|
||||
In case of an issue that happens before an e-mail is sent, you can also perform a CLI based test:
|
||||
- php mailGraph.php test
|
||||
|
||||
Note that you have to set the configuration items starting with "cli" in config.json with actual values from a previous message to make this work!
|
||||
|
||||
Last resort is to raise an issue in this repository and I will try to assist as soon as possible to fix it.
|
||||
## Example message
|
||||
[](images/Example-mail-message-v122.png)
|
||||
|
||||
@@ -6,11 +6,23 @@
|
||||
"cli_eventValue": 0,
|
||||
"cli_duration": 0,
|
||||
"cli_recipient": "recipient@domain.com",
|
||||
"cli_subject": "\"{{ HOST_NAME }}\": ({{ EVENT_SEVERITY }}) {{ EVENT_NAME }}"
|
||||
"cli_subject": "[TEST] {{ HOST_NAME|raw }}: ({{ EVENT_SEVERITY }}) {{ EVENT_NAME|raw }}",
|
||||
"cli_baseURL": "https:\/\/domain.com\/zabbix\/",
|
||||
"cli_period": "30m",
|
||||
"cli_period_header": "Last 30 minutes",
|
||||
"cli_debug": 1,
|
||||
"zabbix_user": "alogicalusername",
|
||||
"zabbix_pwd": "astrongpassword",
|
||||
"zabbix_user_pwd": "astrongpassword",
|
||||
"zabbix_api_user": "alogicalusername",
|
||||
"zabbix_api_pwd": "astrongpassword",
|
||||
"mail_from": "sender@domain.com"
|
||||
"mail_from": "sender@domain.com",
|
||||
"subject": "{{ HOST_NAME|raw }}: ({{ EVENT_SEVERITY }}) {{ EVENT_NAME|raw }}",
|
||||
"smtp_server": "localhost",
|
||||
"smtp_port": 25,
|
||||
"smtp_transport": "none",
|
||||
"smtp_strict": "yes",
|
||||
"graph_match": "any",
|
||||
"period": "20m",
|
||||
"period_header": "Last 20 minutes",
|
||||
"debug": 0
|
||||
}
|
||||
|
||||
28
config/config.json.template.multigraph
Normal file
28
config/config.json.template.multigraph
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"script_baseurl": "https:\/\/domain.com\/",
|
||||
"cli_itemId": 0,
|
||||
"cli_triggerId": 0,
|
||||
"cli_eventId": 0,
|
||||
"cli_eventValue": 0,
|
||||
"cli_duration": 0,
|
||||
"cli_recipient": "recipient@domain.com",
|
||||
"cli_subject": "[TEST] {{ HOST_NAME|raw }}: ({{ EVENT_SEVERITY }}) {{ EVENT_NAME|raw }}",
|
||||
"cli_baseURL": "https:\/\/domain.com\/zabbix\/",
|
||||
"cli_periods": "10m,4h,2d,7d",
|
||||
"cli_periods_headers": "Last 10 minutes,Last 4 hours,Last 2 days,Last 7 days",
|
||||
"cli_debug": 1,
|
||||
"zabbix_user": "alogicalusername",
|
||||
"zabbix_user_pwd": "astrongpassword",
|
||||
"zabbix_api_user": "alogicalusername",
|
||||
"zabbix_api_pwd": "astrongpassword",
|
||||
"mail_from": "sender@domain.com",
|
||||
"subject": "{{ HOST_NAME|raw }}: ({{ EVENT_SEVERITY }}) {{ EVENT_NAME|raw }}",
|
||||
"smtp_server": "localhost",
|
||||
"smtp_port": 25,
|
||||
"smtp_transport": "none",
|
||||
"smtp_strict": "yes",
|
||||
"graph_match": "any",
|
||||
"periods": "10m,4h,2d,7d",
|
||||
"periods_headers": "Last 10 minutes,Last 4 hours,Last 2 days,Last 7 days",
|
||||
"debug": 0
|
||||
}
|
||||
BIN
images/Example-mail-message-v122.png
Executable file
BIN
images/Example-mail-message-v122.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 67 KiB |
60
javascript/zabbix.mailGraph.js
Normal file
60
javascript/zabbix.mailGraph.js
Normal file
@@ -0,0 +1,60 @@
|
||||
try {
|
||||
// Pickup parameters
|
||||
params = JSON.parse(value),
|
||||
req = new CurlHttpRequest(),
|
||||
fields = {},
|
||||
resp = '',
|
||||
result = { tags: {} };
|
||||
|
||||
// Set HTTP proxy if required
|
||||
if (typeof params.HTTPProxy === 'string' && params.HTTPProxy.trim() !== '') { req.setProxy(params.HTTPProxy); }
|
||||
|
||||
// Declare output type
|
||||
req.AddHeader('Content-Type: application/json');
|
||||
|
||||
// Must have fields
|
||||
fields.itemId = params.itemId;
|
||||
fields.triggerId = params.triggerId;
|
||||
fields.eventId = params.eventId;
|
||||
fields.eventValue = params.eventValue;
|
||||
fields.recipient = params.recipient;
|
||||
fields.baseURL = params.baseURL;
|
||||
fields.duration = params.duration;
|
||||
|
||||
// Optional fields
|
||||
if (typeof params.graphWidth === 'string') { fields.graphWidth = params.graphWidth; }
|
||||
if (typeof params.graphHeight === 'string') { fields.graphHeight = params.graphHeight; }
|
||||
if (typeof params.subject === 'string') { fields.subject = params.subject; }
|
||||
if (typeof params.showLegend === 'string') { fields.showLegend = params.showLegend; }
|
||||
if (typeof params.periods === 'string') { fields.periods = params.periods; }
|
||||
if (typeof params.periods_headers === 'string') { fields.periods_headers = params.periods_headers; }
|
||||
if (typeof params.debug === 'string') { fields.debug = params.debug; }
|
||||
|
||||
// Add generic fields
|
||||
Object.keys(params).forEach(function(key) {
|
||||
if (key.substring(0, 4) == 'info') {
|
||||
fields[key] = params[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Post information to the processing script
|
||||
Zabbix.Log(4, '[MailGraph Webhook] Sending request: ' + params.URL + '?' + JSON.stringify(fields));
|
||||
var resp = req.Post(params.URL,JSON.stringify(fields));
|
||||
Zabbix.Log(4, '[Mailgraph Webhook] Receiving response:' + resp);
|
||||
|
||||
// If there was an error, report it
|
||||
if (req.Status() != 200) { throw JSON.parse(resp).errors[0]; }
|
||||
|
||||
// We expect the message id back from the processing script
|
||||
resp = JSON.parse(resp);
|
||||
result.tags.__message_id = resp.messageId;
|
||||
|
||||
// Pass the result back to Zabbix
|
||||
return JSON.stringify(result);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
// In case something went wrong in the processing, pass the error back to Zabbix
|
||||
Zabbix.Log(127, 'MailGraph notification failed : '+error);
|
||||
throw 'MailGraph notification failed : '+error;
|
||||
}
|
||||
491
mailGraph.php
491
mailGraph.php
@@ -11,7 +11,23 @@
|
||||
// 1.00 2021/02/26 - Mark Oudsen - MVP version, ready for distribution
|
||||
// 1.01 2021/02/27 - Mark Oudsen - Enhanced search for associated graphs to an item // bug fixes
|
||||
// 1.10 2021/02/27 - Mark Oudsen - Moved all configuration outside code
|
||||
// 1.11 2021/02/28 - Mark Oudsen - Bug fixes
|
||||
// 1.11 2021/02/28 - Mark Oudsen - Bugfixes
|
||||
// 1.12 2021/03/01 - Mark Oudsen - Bugfixes
|
||||
// Adding mail server configuration via config.json
|
||||
// 1.13 2021/03/01 - Mark Oudsen - Added smtp options to encrypt none,ssl,tls
|
||||
// 1.14 2021/03/01 - Mark Oudsen - Added smtp strict certificates yes|no via config.json
|
||||
// 1.15 2021/03/01 - Mark Oudsen - Revised relevant graph locator; allowing other item graphs if current
|
||||
// item does not have a graph associated
|
||||
// 1.16 2021/03/02 - Mark Oudsen - Found issue with graph.get not returning graphs to requested item ids
|
||||
// Workaround programmed (fetch host graphs, search for certain itemids)
|
||||
// 1.17 2021/03/02 - Mark Oudsen - Added ability to specify period of time displayed in the graph
|
||||
// 1.18 2021/03/04 - Mark Oudsen - Added ability to specify Tags per trigger
|
||||
// Shorten long "lastvalue" or "prevvalue"
|
||||
// 1.19 2021/03/05 - Mark Oudsen - Added ability to pass Zabbix 'infoXXX' parameters for TWIG template
|
||||
// 1.20 2021/03/07 - Mark Oudsen - Production level version - leaving BETA from here on ...
|
||||
// 1.21 2021/03/09 - Mark Oudsen - Reverted graph.get code back to original code as it was not a bug but
|
||||
// a wrongly typed requested (should be ARRAY, not comma separated)!
|
||||
// 1.22 2021/03/10 - Mark Oudsen - Added ability to embed multiple periods (1-4) of the same graph
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// (C) M.J.Oudsen, mark.oudsen@puzzl.nl
|
||||
@@ -20,16 +36,13 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// !!! NOTE: configure the script before usage !!!
|
||||
// Sections are marked [CONFIGURE] across the script
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// MAIN SEQUENCE
|
||||
// -------------
|
||||
// 1) Fetch trigger, item, host, graph, event information via Zabbix API via CURL
|
||||
// 2) Fetch Graph associated to the item/trigger (if any) via Zabbix URL login via CURL
|
||||
// 2) Fetch Graph(s) associated to the item/trigger (if any) via Zabbix URL login via CURL
|
||||
// 3) Build and send mail message from template using Swift/TWIG
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -37,23 +50,23 @@
|
||||
|
||||
// CONSTANTS
|
||||
|
||||
$cVersion = 'v1.11';
|
||||
$cVersion = 'v1.22';
|
||||
$cCRLF = chr(10).chr(13);
|
||||
$maskDateTime = 'Y-m-d H:i:s';
|
||||
|
||||
// DEBUG SETTINGS
|
||||
// -- Should be FALSE for production level use
|
||||
|
||||
$cDebug = TRUE; // Comprehensive debug logging including log storage
|
||||
$cDebugMail = TRUE; // Include log in the mail message? (attachments)
|
||||
$showLog = FALSE; // Display the log - !! switch to TRUE when performing CLI debugging only !!!
|
||||
$cDebug = FALSE; // Extended debug logging mode
|
||||
$cDebugMail = FALSE; // If TRUE, includes log in the mail message (html and plain text attachments)
|
||||
$showLog = FALSE; // Display the log - !!! only use in combination with CLI mode !!!
|
||||
|
||||
// INCLUDE REQUIRED LIBRARIES (Composer)
|
||||
// (configure at same location as the script is running or load in your own central library)
|
||||
// -- swiftmailer/swiftmailer https://swiftmailer.symfony.com/docs/introduction.html
|
||||
// -- twig/twig https://twig.symfony.com/doc/3.x/templates.html
|
||||
|
||||
// [CONFIGURE] Change only required if you decide to use a local central library, otherwise leave as is
|
||||
// Change only required if you decide to use a local/central library, otherwise leave as is
|
||||
include(getcwd().'/vendor/autoload.php');
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -85,7 +98,8 @@
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
|
||||
curl_setopt($ch, CURLOPT_POST, TRUE);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS,
|
||||
json_encode($data,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
|
||||
// Execute Curl
|
||||
$data = curl_exec($ch);
|
||||
@@ -98,11 +112,11 @@
|
||||
$data .= 'Requested page = '.$url.$cCRLF;
|
||||
$data .= 'Error = '.curl_error($ch).$cCRLF;
|
||||
}
|
||||
else
|
||||
{
|
||||
else
|
||||
{
|
||||
_log('> Received '.strlen($data).' bytes');
|
||||
$data = json_decode($data,TRUE);
|
||||
}
|
||||
$data = json_decode($data,TRUE);
|
||||
}
|
||||
|
||||
// Close Curl
|
||||
curl_close($ch);
|
||||
@@ -117,7 +131,7 @@
|
||||
// --- Store with unique name
|
||||
// --- Pass filename back to caller
|
||||
|
||||
function GraphImageById ($graphid, $width = 400, $height = 100, $showLegend = 0)
|
||||
function GraphImageById ($graphid, $width = 400, $height = 100, $graphType = 0, $showLegend = 0, $period = '48h')
|
||||
{
|
||||
global $z_server;
|
||||
global $z_user;
|
||||
@@ -134,14 +148,16 @@
|
||||
// Relative web calls
|
||||
$z_url_index = $z_server ."index.php";
|
||||
$z_url_graph = $z_server ."chart2.php";
|
||||
$z_url_fetch = $z_url_graph ."?graphid=" .$graphid ."&width=" .$width ."&height=" .$height ."&legend=".$showLegend."&profileIdx=web.charts.filter";
|
||||
$z_url_fetch = $z_url_graph ."?graphid=" .$graphid ."&width=" .$width ."&height=" .$height .
|
||||
"&graphtype=".$graphType."&legend=".$showLegend."&profileIdx=web.graphs.filter".
|
||||
"&from=now-".$period."&to=now";
|
||||
|
||||
// Prepare POST login
|
||||
$z_login_data = array('name' => $z_user, 'password' => $z_pass, 'enter' => "Sign in");
|
||||
|
||||
// Cookie and image names
|
||||
$filename_cookie = $z_tmp_cookies ."zabbix_cookie_" .$graphid . "." .$thisTime. ".txt";
|
||||
$filename = "zabbix_graph_" .$graphid . "." . $thisTime . ".png";
|
||||
$filename = "zabbix_graph_" .$graphid . "." . $thisTime . "-" . $period . ".png";
|
||||
$image_name = $z_images_path . $filename;
|
||||
|
||||
// Configure CURL
|
||||
@@ -294,13 +310,21 @@
|
||||
// Initialize ///////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// --- CONFIG DATA ---
|
||||
|
||||
// [CONFIGURE] Change only when you want to place your config file somewhere else ...
|
||||
$config = readConfig(getcwd().'/config/config.json');
|
||||
|
||||
_log('# Configuration taken from config.json'.$cCRLF.json_encode($config,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
|
||||
// --- POST DATA ---
|
||||
|
||||
// Read POST data
|
||||
$problemJSON = file_get_contents('php://input');
|
||||
$problemData = json_decode($problemJSON,TRUE);
|
||||
|
||||
// --- CLI DATA ---
|
||||
|
||||
// Facilitate CLI based testing
|
||||
if (isset($argc))
|
||||
{
|
||||
@@ -309,6 +333,8 @@
|
||||
_log('# Invoked from CLI');
|
||||
|
||||
// Assumes that config.json file has the correct information
|
||||
|
||||
// MANDATORY
|
||||
$problemData['itemId'] = $config['cli_itemId'];
|
||||
$problemData['triggerId'] = $config['cli_triggerId'];
|
||||
$problemData['eventId'] = $config['cli_eventId'];
|
||||
@@ -317,12 +343,24 @@
|
||||
$problemData['baseURL'] = $config['cli_baseURL'];
|
||||
$problemData['duration'] = $config['cli_duration'];
|
||||
$problemData['subject'] = $config['cli_subject'];
|
||||
$problemData['period'] = $config['cli_period'];
|
||||
|
||||
// OPTIONAL
|
||||
if (isset($config['cli_period_header'])) { $problemData['period_header'] = $config['cli_period_header']; }
|
||||
if (isset($config['cli_periods'])) { $problemData['periods'] = $config['cli_periods']; }
|
||||
if (isset($config['cli_periods_headers'])) { $problemData['periods_headers'] = $config['cli_periods_headers']; }
|
||||
if (isset($config['cli_debug'])) { $problemData['debug'] = $config['cli_debug']; }
|
||||
|
||||
// Switch on CLI log display
|
||||
$showLog = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
_log('# Data passed to MailGraph main routine and used for processing'.$cCRLF.json_encode($problemData,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
|
||||
// --- CHECK AND SET P_ VARIABLES ---
|
||||
// FROM POST OR CLI DATA
|
||||
|
||||
if (!isset($problemData['itemId'])) { echo "Missing ITEM ID?\n"; die; }
|
||||
$p_itemId = intval($problemData['itemId']);
|
||||
|
||||
@@ -344,7 +382,7 @@
|
||||
if (!isset($problemData['baseURL'])) { echo "Missing URL?\n"; die; }
|
||||
$p_URL = $problemData['baseURL'];
|
||||
|
||||
$p_subject = '{{ EVENT_SEVERITY }}: {{ EVENT_NAME }}';
|
||||
$p_subject = '{{ EVENT_SEVERITY }}: {{ EVENT_NAME|raw }}';
|
||||
if (isset($problemData['subject'])) { $p_subject = $problemData['subject']; }
|
||||
|
||||
$p_graphWidth = 450;
|
||||
@@ -356,9 +394,35 @@
|
||||
$p_showLegend = 0;
|
||||
if (isset($problemData['showLegend'])) { $p_showLegend = intval($problemData['showLegend']); }
|
||||
|
||||
_log('# Data passed from Zabbix'.$cCRLF.json_encode($problemData,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
$p_period = '48h';
|
||||
if (isset($problemData['period'])) { $p_period = $problemData['period']; }
|
||||
|
||||
// --- CONFIGURATION ---
|
||||
// DYNAMIC VARIABLES FROM ZABBIX
|
||||
|
||||
foreach($problemData as $aKey=>$aValue)
|
||||
{
|
||||
if (substr($aKey,0,4)=='info') { $mailData[$aKey] = $aValue; }
|
||||
}
|
||||
|
||||
// FROM CONFIG DATA
|
||||
|
||||
$p_smtp_server = 'localhost';
|
||||
if (isset($config['smtp_server'])) { $p_smtp_server = $config['smtp_server']; }
|
||||
|
||||
$p_smtp_port = 25;
|
||||
if (isset($config['smtp_port'])) { $p_smtp_port = $config['smtp_port']; }
|
||||
|
||||
$p_smtp_transport = 'none';
|
||||
if ((isset($config['smtp_transport'])) && ($config['smtp_transport']=='tls')) { $p_smtp_transport = 'tls'; }
|
||||
if ((isset($config['smtp_transport'])) && ($config['smtp_transport']=='ssl')) { $p_smtp_transport = 'ssl'; }
|
||||
|
||||
$p_smtp_strict = 'yes';
|
||||
if ((isset($config['smtp_strict'])) && ($config['smtp_strict']=='no')) { $p_smtp_strict = 'no'; }
|
||||
|
||||
$p_graph_match = 'any';
|
||||
if ((isset($config['graph_match'])) && ($config['graph_match']=='exact')) { $p_graph_match = 'exact'; }
|
||||
|
||||
// --- GLOBAL CONFIGURATION ---
|
||||
|
||||
// Script related settings
|
||||
$z_url = $config['script_baseurl']; // Script URL location (for relative paths to images, templates, log, tmp)
|
||||
@@ -366,10 +430,10 @@
|
||||
|
||||
// Absolute path where to store the generated images - note: script does not take care of clearing out old images!
|
||||
$z_path = getcwd().'/'; // Absolute base path on the filesystem for this url
|
||||
$z_images_path = $z_path.'/images/';
|
||||
$z_template_path = $z_path.'/templates/';
|
||||
$z_tmp_cookies = $z_path.'//tmp/';
|
||||
$z_log_path = $z_path.'/log/';
|
||||
$z_images_path = $z_path.'images/';
|
||||
$z_template_path = $z_path.'templates/';
|
||||
$z_tmp_cookies = $z_path.'tmp/';
|
||||
$z_log_path = $z_path.'log/';
|
||||
|
||||
// Zabbix user (requires Super Admin access rights to access image generator script)
|
||||
$z_user = $config['zabbix_user'];
|
||||
@@ -387,38 +451,15 @@
|
||||
$z_server = $p_URL; // Zabbix server URL from config
|
||||
$z_url_api = $z_server ."api_jsonrpc.php"; // Zabbix API URL
|
||||
|
||||
// Check accessibility of paths and files
|
||||
//TODO: Check write access?
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Check accessibility of paths and template files //////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if (!file_exists($z_images_path))
|
||||
{
|
||||
echo 'Image path inaccessible?'.$cCRLF;
|
||||
die;
|
||||
}
|
||||
|
||||
if (!file_exists($z_tmp_cookies))
|
||||
{
|
||||
echo 'Cookies temporary path inaccessible?'.$cCRLF;
|
||||
die;
|
||||
}
|
||||
|
||||
if (!file_exists($z_log_path))
|
||||
{
|
||||
echo 'Log path inaccessible?'.$cCRLF;
|
||||
die;
|
||||
}
|
||||
|
||||
if (!file_exists($z_template_path.'html.template'))
|
||||
{
|
||||
echo 'HTML template missing?'.$cCRLF;
|
||||
die;
|
||||
}
|
||||
|
||||
if (!file_exists($z_template_path.'plain.template'))
|
||||
{
|
||||
echo 'PLAIN template missing?'.$cCRLF;
|
||||
die;
|
||||
}
|
||||
if (!is_writable($z_images_path)) { echo 'Image path inaccessible?'.$cCRLF; die; }
|
||||
if (!is_writable($z_tmp_cookies)) { echo 'Cookies temporary path inaccessible?'.$cCRLF; die; }
|
||||
if (!is_writable($z_log_path)) { echo 'Log path inaccessible?'.$cCRLF; die; }
|
||||
if (!file_exists($z_template_path.'html.template')) { echo 'HTML template missing?'.$cCRLF; die; }
|
||||
if (!file_exists($z_template_path.'plain.template')) { echo 'PLAIN template missing?'.$cCRLF; die; }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Fetch information via API ////////////////////////////////////////////////////////////////////////////
|
||||
@@ -457,7 +498,8 @@
|
||||
'method'=>'trigger.get',
|
||||
'params'=>array('triggerids'=>$p_triggerId,
|
||||
'output'=>'extend',
|
||||
'selectFunctions'=>'extend'),
|
||||
'selectFunctions'=>'extend',
|
||||
'selectTags'=>'extend'),
|
||||
'expandComment'=>1,
|
||||
'expandDescription'=>1,
|
||||
'expandExpression'=>1,
|
||||
@@ -467,10 +509,67 @@
|
||||
$thisTrigger = postJSON($z_url_api,$request);
|
||||
_log('> Trigger data'.$cCRLF.json_encode($thisTrigger,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
|
||||
if (!isset($thisTrigger['result'][0])) { echo '! No response data received?'.$cCRLF; die; }
|
||||
|
||||
$mailData['TRIGGER_ID'] = $thisTrigger['result'][0]['triggerid'];
|
||||
$mailData['TRIGGER_DESCRIPTION'] = $thisTrigger['result'][0]['description'];
|
||||
$mailData['TRIGGER_COMMENTS'] = $thisTrigger['result'][0]['comments'];
|
||||
|
||||
// --- Custom settings?
|
||||
|
||||
$forceGraph = 0;
|
||||
|
||||
foreach($thisTrigger['result'][0]['tags'] as $aTag)
|
||||
{
|
||||
switch ($aTag['tag'])
|
||||
{
|
||||
case 'mailGraph.period':
|
||||
$problemData['period'] = $aTag['value'];
|
||||
_log('+ Graph display period override = '.$problemData['period']);
|
||||
break;
|
||||
|
||||
case 'mailGraph.period_header':
|
||||
$problemData['period_header'] = $aTag['value'];
|
||||
_log('+ Graph display period header override = '.$problemData['period_header']);
|
||||
break;
|
||||
|
||||
case 'mailGraph.periods':
|
||||
$problemData['periods'] = $aTag['value'];
|
||||
_log('+ Graph display periods override = '.$problemData['periods']);
|
||||
break;
|
||||
|
||||
case 'mailGraph.periods_headers':
|
||||
$problemData['periods_headers'] = $aTag['value'];
|
||||
_log('+ Graph display periods headers override = '.$problemData['periods_headers']);
|
||||
break;
|
||||
|
||||
case 'mailGraph.graph':
|
||||
$forceGraph = intval($aTag['value']);
|
||||
_log('+ Graph ID to display = '.$forceGraph);
|
||||
break;
|
||||
|
||||
case 'mailGraph.showLegend':
|
||||
$p_showLegend = intval($aTag['value']);
|
||||
_log('+ Graph display legend override = '.$p_showLegend);
|
||||
break;
|
||||
|
||||
case 'mailGraph.graphWidth':
|
||||
$p_graphWidth = intval($aTag['value']);
|
||||
_log('+ Graph height override = '.$$p_graphWidth);
|
||||
break;
|
||||
|
||||
case 'mailGraph.graphHeight':
|
||||
$p_graphHeight = intval($aTag['value']);
|
||||
_log('+ Graph height override = '.$$p_graphHeight);
|
||||
break;
|
||||
|
||||
case 'mailGraph.debug':
|
||||
$problemData['debug'] = 1;
|
||||
_log('+ Mail debug log enabled');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// --- GET ITEM INFO ---
|
||||
|
||||
_log('# Retrieve ITEM information');
|
||||
@@ -487,6 +586,8 @@
|
||||
$thisItem = postJSON($z_url_api,$request);
|
||||
_log('> Item data'.$cCRLF.json_encode($thisItem,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
|
||||
if (!isset($thisItem['result'][0])) { echo '! No response data received?'.$cCRLF; die; }
|
||||
|
||||
$mailData['ITEM_ID'] = $thisItem['result'][0]['itemid'];
|
||||
$mailData['ITEM_KEY'] = $thisItem['result'][0]['key_'];
|
||||
$mailData['ITEM_NAME'] = $thisItem['result'][0]['name'];
|
||||
@@ -498,6 +599,10 @@
|
||||
if (substr($mailData['ITEM_LASTVALUE'],0,5)=='<?xml') { $mailData['ITEM_LASTVALUE'] = '[record]'; }
|
||||
if (substr($mailData['ITEM_PREVIOUSVALUE'],0,5)=='<?xml') { $mailData['ITEM_PREVIOUSTVALUE'] = '[record]'; }
|
||||
|
||||
// Catch long elements
|
||||
if (strlen($mailData['ITEM_LASTVALUE'])>50) { $mailData['ITEM_LASTVALUE'] = substr($mailData['ITEM_LASTVALUE'],0,50).' ...'; }
|
||||
if (strlen($mailData['ITEM_PREVIOUSVALUE'])>50) { $mailData['ITEM_PREVIOUSVALUE'] = substr($mailData['ITEM_PREVIOUSVALUE'],0,50).' ...'; }
|
||||
|
||||
// --- GET HOST INFO ---
|
||||
|
||||
_log('# Retrieve HOST information');
|
||||
@@ -514,6 +619,8 @@
|
||||
$thisHost = postJSON($z_url_api,$request);
|
||||
_log('> Host data'.$cCRLF.json_encode($thisHost,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
|
||||
if (!isset($thisHost['result'][0])) { echo '! No response data received?'.$cCRLF; die; }
|
||||
|
||||
$mailData['HOST_ID'] = $thisHost['result'][0]['hostid'];
|
||||
$mailData['HOST_NAME'] = $thisHost['result'][0]['name'];
|
||||
$mailData['HOST_ERROR'] = $thisHost['result'][0]['error'];
|
||||
@@ -531,64 +638,104 @@
|
||||
$thisMacros = postJSON($z_url_api,$request);
|
||||
_log('> Host macro data'.$cCRLF.json_encode($thisMacros,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
|
||||
// --- GET GRAPHS ASSOCIATED WITH THIS ITEM ---
|
||||
// --- GET GRAPHS ASSOCIATED WITH THIS HOST ---
|
||||
|
||||
_log('# Retrieve associated graphs to this item');
|
||||
_log('# Retrieve associated graphs to this HOST and the TRIGGER ITEMS');
|
||||
|
||||
$searchItems = array();
|
||||
|
||||
foreach($thisTrigger['result'][0]['functions'] as $aFunction)
|
||||
{
|
||||
$searchItems[] = $aFunction['itemid'];
|
||||
}
|
||||
|
||||
$keyName = $thisItem['result'][0]['key_'];
|
||||
$hostId = $thisItem['result'][0]['hostid'];
|
||||
|
||||
// Look for graphs across all functions inside the item
|
||||
$itemIds = array();
|
||||
|
||||
foreach($thisTrigger['result'][0]['functions'] as $aFunction)
|
||||
{
|
||||
$itemIds[] = $aFunction['itemid'];
|
||||
}
|
||||
|
||||
$request = array('jsonrpc'=>'2.0',
|
||||
'method'=>'graph.get',
|
||||
'params'=>array('itemids'=>implode(',',$itemIds),
|
||||
'hostids'=>$hostId,
|
||||
'params'=>array('hostids'=>$hostId,
|
||||
'itemids'=>$searchItems,
|
||||
'expandName'=>1,
|
||||
'selectGraphItems'=>'extend',
|
||||
'output'=>'extend'),
|
||||
'auth'=>$token,
|
||||
'id'=>nextRequestID());
|
||||
|
||||
$thisGraph = postJSON($z_url_api,$request);
|
||||
_log('> Graph data'.$cCRLF.json_encode($thisGraph,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
$thisGraphs = postJSON($z_url_api,$request);
|
||||
_log('> Graphs data'.$cCRLF.json_encode($thisGraphs,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
|
||||
// --- FIND ASSOCIATED GRAPH ITEMS ---
|
||||
|
||||
_log('# Retreiving associated graph items for the identified graphs');
|
||||
|
||||
$matchedGraphs = array();
|
||||
|
||||
foreach($thisGraph['result'] as $aGraph)
|
||||
if ($forceGraph>0)
|
||||
{
|
||||
_log('# Retrieving FORCED graph information');
|
||||
|
||||
// --- GET GRAPH ASSOCIATED WITH FORCEGRAPH ---
|
||||
|
||||
$request = array('jsonrpc'=>'2.0',
|
||||
'method'=>'graphitem.get',
|
||||
'params'=>array('graphids'=>$aGraph['graphid'],
|
||||
'method'=>'graph.get',
|
||||
'params'=>array('graphids'=>$forceGraph,
|
||||
'expandName'=>1,
|
||||
'output'=>'extend'),
|
||||
'auth'=>$token,
|
||||
'id'=>nextRequestID());
|
||||
|
||||
$thisGraphItems[$aGraph['graphid']] = postJSON($z_url_api,$request);
|
||||
$forceGraphInfo = postJSON($z_url_api,$request);
|
||||
_log('> Forced graph data'.$cCRLF.json_encode($forceGraphInfo,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
|
||||
foreach($thisGraphItems[$aGraph['graphid']]['result'] as $graphItem)
|
||||
if (!isset($forceGraphInfo['result'][0]))
|
||||
{
|
||||
if ($graphItem['itemid']==$itemId)
|
||||
_log('! No data received for graph #'.$forceGraph.'; discarding forced graph information');
|
||||
$forceGraph = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// --- FIND MATCHING GRAPH ITEMS WITH OUR TRIGGER ITEMS ---
|
||||
|
||||
_log('# Matching retreived graph information with our trigger items');
|
||||
|
||||
// Look for graphs across all functions inside the item
|
||||
|
||||
$itemIds = array();
|
||||
|
||||
foreach($thisTrigger['result'][0]['functions'] as $aFunction)
|
||||
{
|
||||
$didFind = FALSE;
|
||||
|
||||
foreach($itemIds as $anItem)
|
||||
{
|
||||
if ($anItem==$aFunction['itemid']) { $didFind = TRUE; break; }
|
||||
}
|
||||
|
||||
if (!$didFind) { $itemIds[] = $aFunction['itemid']; }
|
||||
}
|
||||
|
||||
$matchedGraphs = array();
|
||||
$otherGraphs = array();
|
||||
|
||||
foreach($thisGraphs['result'] as $aGraph)
|
||||
{
|
||||
foreach($aGraph['gitems'] as $aGraphItem)
|
||||
{
|
||||
foreach($itemIds as $anItemId)
|
||||
{
|
||||
$matchedGraphs[] = $aGraph;
|
||||
_log('+ Graph item ### MATCH ###'.$cCRLF.json_encode($aGraph,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
}
|
||||
else
|
||||
{
|
||||
_log('- Graph item (nomatch)'.$cCRLF.json_encode($aGraph,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
if ($aGraphItem['itemid']==$anItemId)
|
||||
{
|
||||
if ($anItemId=$itemId)
|
||||
{
|
||||
_log('+ Graph #'.$aGraphItem['graphid'].' full match found (item #'.$aGraphItem['itemid'].')');
|
||||
$matchedGraphs[] = $aGraph;
|
||||
}
|
||||
else
|
||||
{
|
||||
$otherGraphs[] = $aGraph;
|
||||
_log('~ Graph #'.$aGraphItem['graphid'].' partial match found (item #'.$aGraphItem['itemid'].')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_log('> Graphs found (matching) = '.sizeof($matchedGraphs));
|
||||
_log('> Graphs found (matching/partial) = '.sizeof($matchedGraphs).' / '.sizeof($otherGraphs));
|
||||
|
||||
// --- READ EVENT INFORMATION ---
|
||||
|
||||
@@ -604,6 +751,8 @@
|
||||
$thisEvent = postJSON($z_url_api,$request);
|
||||
_log('> Event data'.$cCRLF.json_encode($thisEvent,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK));
|
||||
|
||||
if (!isset($thisEvent['result'][0])) { echo '! No response data received?'.$cCRLF; die; }
|
||||
|
||||
$mailData['EVENT_ID'] = $thisEvent['result'][0]['eventid'];
|
||||
$mailData['EVENT_NAME'] = $thisEvent['result'][0]['name'];
|
||||
$mailData['EVENT_OPDATA'] = $thisEvent['result'][0]['opdata'];
|
||||
@@ -628,21 +777,97 @@
|
||||
// Fetch Graph //////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
$graphFile = '';
|
||||
// Determine number of periods for this graph
|
||||
|
||||
$p_periods = array();
|
||||
$p_periods_headers = array();
|
||||
|
||||
if (isset($problemData['periods']))
|
||||
{
|
||||
// Multiple periods mode selected
|
||||
|
||||
_log('# Multi period graph mode selected');
|
||||
|
||||
$p_periods = explode(',',$problemData['periods']);
|
||||
|
||||
// If invalid, replace with single graph item
|
||||
if (sizeof($p_periods)==0) { $p_periods[] = $p_period; }
|
||||
|
||||
// --- Determine headers
|
||||
|
||||
if (isset($problemData['periods_headers'])) { $p_periods_headers = explode(',',$problemData['periods_headers']); }
|
||||
|
||||
// If no headers specified, simply copy the period information
|
||||
if (sizeof($p_periods_headers)==0) { $p_periods_headers = $p_periods; }
|
||||
}
|
||||
else
|
||||
{
|
||||
// Single period mode selected
|
||||
|
||||
$p_periods[] = $p_period;
|
||||
|
||||
if (isset($problemData['period_header']))
|
||||
{
|
||||
$p_periods_headers[] = $problemData['period_header'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$p_periods_headers[] = $p_period;
|
||||
}
|
||||
}
|
||||
|
||||
while (sizeof($p_periods)>4) { array_pop($p_periods); }
|
||||
while (sizeof($p_periods_headers)>4) { array_pop($p_periods_headers); }
|
||||
|
||||
$graphFiles = array();
|
||||
$graphURL = '';
|
||||
|
||||
if (sizeof($matchedGraphs)>0)
|
||||
// If we have any matching graph, make the embedding information available
|
||||
|
||||
if ((sizeof($matchedGraphs) + sizeof($otherGraphs) + $forceGraph)>0)
|
||||
{
|
||||
// TODO: if multiple graphs, pick the first one or the one that is TAGGED with a mailGraph tag/value
|
||||
if ($forceGraph>0)
|
||||
{
|
||||
$theGraph = $forceGraphInfo;
|
||||
$theType = 'Forced';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sizeof($matchedGraphs)>0)
|
||||
{
|
||||
$theGraph = $matchedGraphs[0];
|
||||
$theType = 'Matched';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sizeof($otherGraphs)>0)
|
||||
{
|
||||
$theGraph = $otherGraphs[0];
|
||||
$theType = 'Other';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_log('# Adding graph #'.$matchedGraphs[0]['graphid']);
|
||||
$graphFile = GraphImageById($matchedGraphs[0]['graphid'],$p_graphWidth,$p_graphHeight,$p_showLegend);
|
||||
$mailData['GRAPH_ID'] = $theGraph['graphid'];
|
||||
$mailData['GRAPH_NAME'] = $theGraph['name'];
|
||||
$mailData['GRAPH_MATCH'] = $theType;
|
||||
|
||||
_log('> Filename = '.$graphFile);
|
||||
_log('# Adding '.strtoupper($theType).' graph #'.$mailData['GRAPH_ID']);
|
||||
|
||||
$mailData['GRAPH_ID'] = $matchedGraphs[0]['graphid'];
|
||||
$mailData['GRAPH_NAME'] = $matchedGraphs[0]['name'];
|
||||
$mailData['GRAPH_URL'] = $z_url_image . $graphFile;
|
||||
foreach($p_periods as $aKey=>$aPeriod)
|
||||
{
|
||||
$graphFile = GraphImageById($mailData['GRAPH_ID'],
|
||||
$p_graphWidth,$p_graphHeight,
|
||||
$theGraph['graphtype'],
|
||||
$p_showLegend,$aPeriod);
|
||||
|
||||
$graphFiles[] = $graphFile;
|
||||
|
||||
$mailData['GRAPH_URL'.($aKey+1)] = $z_url_image . $graphFile;
|
||||
$mailData['GRAPH_HEADER'.($aKey+1)] = $p_periods_headers[$aKey];
|
||||
}
|
||||
|
||||
$mailData['GRAPH_ZABBIXLINK'] = $z_server.'graphs.php?form=update&graphid='.$mailData['GRAPH_ID'];
|
||||
}
|
||||
|
||||
// Prepare HTML LOG content
|
||||
@@ -663,10 +888,10 @@
|
||||
|
||||
// Prepare others
|
||||
|
||||
$mailData['TRIGGER_URL'] = $z_server.'/triggers.php?form=update&triggerid='.$mailData['TRIGGER_ID'];
|
||||
$mailData['ITEM_URL'] = $z_server.'/items.php?form=update&hostid='.$mailData['HOST_ID'].'&itemid='.$mailData['ITEM_ID'];
|
||||
$mailData['HOST_URL'] = $z_server.'/zabbix/hosts.php?form=update&hostid='.$mailData['HOST_ID'];
|
||||
$mailData['EVENTDETAILS_URL'] = $z_server.'/tr_events.php?triggerid='.$mailData['TRIGGER_ID'].'&eventid='.$mailData['EVENT_ID'];
|
||||
$mailData['TRIGGER_URL'] = $z_server.'triggers.php?form=update&triggerid='.$mailData['TRIGGER_ID'];
|
||||
$mailData['ITEM_URL'] = $z_server.'items.php?form=update&hostid='.$mailData['HOST_ID'].'&itemid='.$mailData['ITEM_ID'];
|
||||
$mailData['HOST_URL'] = $z_server.'hosts.php?form=update&hostid='.$mailData['HOST_ID'];
|
||||
$mailData['EVENTDETAILS_URL'] = $z_server.'tr_events.php?triggerid='.$mailData['TRIGGER_ID'].'&eventid='.$mailData['EVENT_ID'];
|
||||
|
||||
$mailData['EVENT_DURATION'] = $p_duration;
|
||||
|
||||
@@ -676,7 +901,40 @@
|
||||
|
||||
_log('# Setting up mailer');
|
||||
|
||||
$transport = (new Swift_SmtpTransport('mail.puzzl.nl', 25));
|
||||
// Do we need TLS or SSL?
|
||||
|
||||
if (($p_smtp_transport=='tls') || ($p_smtp_transport=='ssl'))
|
||||
{
|
||||
$transport = (new Swift_SmtpTransport($p_smtp_server, $p_smtp_port, $p_smtp_transport));
|
||||
|
||||
if ($p_smtp_strict=='no')
|
||||
{
|
||||
if ($transport instanceof \Swift_Transport_EsmtpTransport)
|
||||
{
|
||||
$transport->setStreamOptions([
|
||||
'ssl' => ['allow_self_signed' => true,
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false]
|
||||
]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($transport instanceof \Swift_Transport_EsmtpTransport)
|
||||
{
|
||||
$transport->setStreamOptions([
|
||||
'ssl' => ['allow_self_signed' => false,
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$transport = (new Swift_SmtpTransport($p_smtp_server, $p_smtp_port));
|
||||
}
|
||||
|
||||
$mailer = new Swift_Mailer($transport);
|
||||
|
||||
$message = (new Swift_Message());
|
||||
@@ -706,11 +964,17 @@
|
||||
|
||||
$twig = new \Twig\Environment($loader);
|
||||
|
||||
if ($graphFile!='')
|
||||
if (sizeof($graphFiles)>0)
|
||||
{
|
||||
// Embed the image
|
||||
$mailData['GRAPH_CID'] = $message->embed(Swift_Image::fromPath($z_images_path.$graphFile));
|
||||
_log('> Embedded graph image '.$z_images_path.$graphFile);
|
||||
// Embed the image(s)
|
||||
|
||||
$graphReference = 1;
|
||||
|
||||
foreach($graphFiles as $graphFile)
|
||||
{
|
||||
$mailData['GRAPH_CID'.$graphReference++] = $message->embed(Swift_Image::fromPath($z_images_path.$graphFile));
|
||||
_log('> Embedded graph image '.$z_images_path.$graphFile);
|
||||
}
|
||||
}
|
||||
|
||||
$bodyHTML = $twig->render('html', $mailData);
|
||||
@@ -725,7 +989,7 @@
|
||||
->setBody($bodyHTML, 'text/html')
|
||||
->addPart($bodyPlain, 'text/plain');
|
||||
|
||||
if ($cDebugMail)
|
||||
if (($cDebugMail) || (isset($problemData['debug'])))
|
||||
{
|
||||
_log('# Attaching logs to mail message');
|
||||
|
||||
@@ -740,14 +1004,14 @@
|
||||
|
||||
$result = $mailer->send($message);
|
||||
|
||||
// Return TAG information
|
||||
// Return Event TAG information for Zabbix
|
||||
|
||||
$response = array('messageId.mailGraph'=>$messageId);
|
||||
echo json_encode($response).$cCRLF;
|
||||
|
||||
// Store log?
|
||||
|
||||
if ($cDebug)
|
||||
if (($cDebug) || (isset($problemData['debug'])))
|
||||
{
|
||||
unset($mailData['LOG_HTML']);
|
||||
unset($mailData['LOG_PLAIN']);
|
||||
@@ -755,6 +1019,9 @@
|
||||
$content = implode(chr(10),$logging).$cCRLF.$cCRLF.'=== MAILDATA ==='.$cCRLF.$cCRLF.json_encode($mailData,JSON_PRETTY_PRINT|JSON_NUMERIC_CHECK);
|
||||
$content = str_replace(chr(13),'',$content);
|
||||
|
||||
file_put_contents($z_log_path.'log.'.$p_eventId.'.'.date('YmdHis').'.dump',$content);
|
||||
$logName = 'log.'.$p_eventId.'.'.date('YmdHis').'.dump';
|
||||
|
||||
file_put_contents($z_log_path.$logName,$content);
|
||||
_log('= Log stored to '.$z_log_path.$logName);
|
||||
}
|
||||
?>
|
||||
|
||||
133
mailGraph.xml
133
mailGraph.xml
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<zabbix_export>
|
||||
<version>5.0</version>
|
||||
<date>2021-02-28T20:27:03Z</date>
|
||||
<date>2021-03-10T18:50:24Z</date>
|
||||
<media_types>
|
||||
<media_type>
|
||||
<name>MailGraph</name>
|
||||
@@ -9,7 +9,7 @@
|
||||
<parameters>
|
||||
<parameter>
|
||||
<name>baseURL</name>
|
||||
<value>https://domain.com/zabbix/</value>
|
||||
<value>https://mydomain.com/zabbix/</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>duration</name>
|
||||
@@ -39,6 +39,14 @@
|
||||
<name>itemId</name>
|
||||
<value>{ITEM.ID}</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>periods</name>
|
||||
<value>10m,4h,1d,7d</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>periods_headers</name>
|
||||
<value>Last 10 minutes,Last 4 hours,Last day,Last 7 days</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>recipient</name>
|
||||
<value>{ALERT.SENDTO}</value>
|
||||
@@ -49,7 +57,7 @@
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>subject</name>
|
||||
<value>{{ HOST_NAME }}: ({{ EVENT_SEVERITY }}) {{ EVENT_NAME }}</value>
|
||||
<value>{{ HOST_NAME|raw }}: ({{ EVENT_SEVERITY }}) {{ EVENT_NAME|raw }}</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>triggerId</name>
|
||||
@@ -57,58 +65,68 @@
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>URL</name>
|
||||
<value>https://domain.com/mailGraph.php</value>
|
||||
<value>https://mydomain.com/mailGraph.php</value>
|
||||
</parameter>
|
||||
</parameters>
|
||||
<script>try {
|
||||
// Pickup parameters
|
||||
params = JSON.parse(value),
|
||||
req = new CurlHttpRequest(),
|
||||
fields = {},
|
||||
resp = '',
|
||||
result = { tags: {} };
|
||||
// Pickup parameters
|
||||
params = JSON.parse(value),
|
||||
req = new CurlHttpRequest(),
|
||||
fields = {},
|
||||
resp = '',
|
||||
result = { tags: {} };
|
||||
|
||||
// Set HTTP proxy if required
|
||||
if (typeof params.HTTPProxy === 'string' && params.HTTPProxy.trim() !== '') { req.setProxy(params.HTTPProxy); }
|
||||
// Set HTTP proxy if required
|
||||
if (typeof params.HTTPProxy === 'string' && params.HTTPProxy.trim() !== '') { req.setProxy(params.HTTPProxy); }
|
||||
|
||||
// Declare output type
|
||||
req.AddHeader('Content-Type: application/json');
|
||||
// Declare output type
|
||||
req.AddHeader('Content-Type: application/json');
|
||||
|
||||
// Must have fields
|
||||
fields.itemId = params.itemId;
|
||||
fields.triggerId = params.triggerId;
|
||||
fields.eventId = params.eventId;
|
||||
fields.eventValue = params.eventValue;
|
||||
fields.recipient = params.recipient;
|
||||
fields.baseURL = params.baseURL;
|
||||
fields.duration = params.duration;
|
||||
// Must have fields
|
||||
fields.itemId = params.itemId;
|
||||
fields.triggerId = params.triggerId;
|
||||
fields.eventId = params.eventId;
|
||||
fields.eventValue = params.eventValue;
|
||||
fields.recipient = params.recipient;
|
||||
fields.baseURL = params.baseURL;
|
||||
fields.duration = params.duration;
|
||||
|
||||
// Optional fields
|
||||
if (typeof params.graphWidth === 'string') { fields.graphWidth = params.graphWidth; }
|
||||
if (typeof params.graphHeight === 'string') { fields.graphHeight = params.graphHeight; }
|
||||
if (typeof params.subject === 'string') { fields.subject = params.subject; }
|
||||
if (typeof params.showLegend === 'string') { fields.showLegend = params.showLegend; }
|
||||
// Optional fields
|
||||
if (typeof params.graphWidth === 'string') { fields.graphWidth = params.graphWidth; }
|
||||
if (typeof params.graphHeight === 'string') { fields.graphHeight = params.graphHeight; }
|
||||
if (typeof params.subject === 'string') { fields.subject = params.subject; }
|
||||
if (typeof params.showLegend === 'string') { fields.showLegend = params.showLegend; }
|
||||
if (typeof params.periods === 'string') { fields.periods = params.periods; }
|
||||
if (typeof params.periods_headers === 'string') { fields.periods_headers = params.periods_headers; }
|
||||
if (typeof params.debug === 'string') { fields.debug = params.debug; }
|
||||
|
||||
// Post information to the processing script
|
||||
Zabbix.Log(4, '[MailGraph Webhook] Sending request: ' + params.URL + '?' + JSON.stringify(fields));
|
||||
var resp = req.Post(params.URL,JSON.stringify(fields));
|
||||
Zabbix.Log(4, '[Mailgraph Webhook] Receiving response:' + resp);
|
||||
// Add generic fields
|
||||
Object.keys(params).forEach(function(key) {
|
||||
if (key.substring(0, 4) == 'info') {
|
||||
fields[key] = params[key];
|
||||
}
|
||||
});
|
||||
|
||||
// If there was an error, report it
|
||||
if (req.Status() != 200) { throw JSON.parse(resp).errors[0]; }
|
||||
// Post information to the processing script
|
||||
Zabbix.Log(4, '[MailGraph Webhook] Sending request: ' + params.URL + '?' + JSON.stringify(fields));
|
||||
var resp = req.Post(params.URL,JSON.stringify(fields));
|
||||
Zabbix.Log(4, '[Mailgraph Webhook] Receiving response:' + resp);
|
||||
|
||||
// We expect the message id back from the processing script
|
||||
resp = JSON.parse(resp);
|
||||
result.tags.__message_id = resp.messageId;
|
||||
// If there was an error, report it
|
||||
if (req.Status() != 200) { throw JSON.parse(resp).errors[0]; }
|
||||
|
||||
// Pass the result back to Zabbix
|
||||
return JSON.stringify(result);
|
||||
// We expect the message id back from the processing script
|
||||
resp = JSON.parse(resp);
|
||||
result.tags.__message_id = resp.messageId;
|
||||
|
||||
// Pass the result back to Zabbix
|
||||
return JSON.stringify(result);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
// In case something went wrong in the processing, pass the error back to Zabbix
|
||||
Zabbix.Log(127, 'MailGraph notification failed : '+error);
|
||||
throw 'MailGraph notification failed : '+error;
|
||||
// In case something went wrong in the processing, pass the error back to Zabbix
|
||||
Zabbix.Log(127, 'MailGraph notification failed : '+error);
|
||||
throw 'MailGraph notification failed : '+error;
|
||||
}</script>
|
||||
<process_tags>YES</process_tags>
|
||||
<description>The "URL" must point to the location of the processing script. If a proxy is required, define "HTTPProxy" for the proxy address.
|
||||
@@ -120,37 +138,7 @@ Customization:
|
||||
|
||||
The html.template and plain.template files can be adjusted (TWIG format).
|
||||
|
||||
Values available:
|
||||
{{ baseURL }} - base url of the Zabbix system (use for references to API and login)
|
||||
{{ TRIGGER_ID }} - id of the applicable trigger
|
||||
{{ TRIGGER_DESCRIPTION }} - raw trigger description (note: macros are not parsed!)
|
||||
{{ TRIGGER_COMMENTS }} - comments of the trigger
|
||||
{{ TRIGGER_URL }} - url of the trigger form
|
||||
{{ ITEM_ID }} - id of the associated item to the trigger
|
||||
{{ ITEM_KEY }} - key of the item
|
||||
{{ ITEM_NAME }} - item name
|
||||
{{ ITEM_DESCRIPTION }} - description of the item
|
||||
{{ ITEM_LASTVALUE }} - last value of the item
|
||||
{{ ITEM_PREVIOUSVALUE }} - the value of the before LASTVALUE
|
||||
{{ ITEM_URL }} - url of the item form
|
||||
{{ HOST_ID }} - id of the associated host to the item
|
||||
{{ HOST_NAME }} - name of the host
|
||||
{{ HOST_ERROR }} - last error state of the applicable host
|
||||
{{ HOST_DESCRIPTION }} - description of the host
|
||||
{{ HOST_URL }} - url of the host form
|
||||
{{ EVENT_ID }} - id of the associated event
|
||||
{{ EVENT_NAME }} - name of the event (note: macros are parsed!)
|
||||
{{ EVENT_OPDATA }} - associated operational data of the vent
|
||||
{{ EVENT_VALUE }} - event state (0=Recovered, 1=Triggered/Active)
|
||||
{{ EVENT_SEVERITY }} - severity of the event
|
||||
{{ EVENT_STATUS }} - status of the event
|
||||
{{ EVENT_URL }} - url of the event details
|
||||
{{ GRAPH_ID }} - id of the (first) associated graph that contains the item
|
||||
{{ GRAPH_NAME }} - name of this graph
|
||||
{{ GRAPH_URL }} - URL to this graph (assuming script produces to an accessible location)
|
||||
{{ GRAPH_CID }} - IMG embed string (<img src="{{ GRAPH_CID }}" />)
|
||||
{{ LOG_HTML }} - script log in HTML format
|
||||
{{ LOG_PLAIN }} - script log in PLAIN text format</description>
|
||||
More details are available at https://github.com/moudsen/mailGraph</description>
|
||||
<message_templates>
|
||||
<message_template>
|
||||
<event_source>TRIGGERS</event_source>
|
||||
@@ -201,4 +189,3 @@ itemId: {ITEM.ID]</message>
|
||||
</media_type>
|
||||
</media_types>
|
||||
</zabbix_export>
|
||||
|
||||
|
||||
@@ -1,74 +1,175 @@
|
||||
<html lang="en"><head><meta http-equiv=Content-Type content="text/html; charset=UTF-8">
|
||||
<style>
|
||||
.Resolved {
|
||||
background-color:#86cc89;
|
||||
border:1px solid #57bd5b;
|
||||
font-family:Tahoma,Geneva,Arial,sans-serif;font-size:14px;
|
||||
}
|
||||
.Information {
|
||||
background-color:#7499ff;
|
||||
border:1px solid #4673f0;
|
||||
font-family:Tahoma,Geneva,Arial,sans-serif;font-size:14px;
|
||||
}
|
||||
.Warning {
|
||||
background-color:#FFC859;
|
||||
border:1px solid #E69F10;
|
||||
font-family:Tahoma,Geneva,Arial,sans-serif;font-size:14px;
|
||||
}
|
||||
.Average {
|
||||
background-color:#FFA059;
|
||||
border:1px solid #e66e15;
|
||||
font-family:Tahoma,Geneva,Arial,sans-serif;font-size:14px;
|
||||
}
|
||||
.High {
|
||||
background-color:#E97659;
|
||||
border:1px solid #E45959;
|
||||
font-family:Tahoma,Geneva,Arial,sans-serif;font-size:14px;
|
||||
}
|
||||
.Disaster {
|
||||
background-color:#E45959;
|
||||
border:1px solid #DE1E09;
|
||||
font-family:Tahoma,Geneva,Arial,sans-serif;font-size:14px;
|
||||
}
|
||||
body {
|
||||
font-family: Arial,sans-serif;
|
||||
}
|
||||
.Resolved {
|
||||
background-color:#86cc89;
|
||||
border:1px solid #57bd5b;
|
||||
}
|
||||
.Information {
|
||||
background-color:#7499ff;
|
||||
border:1px solid #4673f0;
|
||||
}
|
||||
.Warning {
|
||||
background-color:#FFC859;
|
||||
border:1px solid #E69F10;
|
||||
}
|
||||
.Average {
|
||||
background-color:#FFA059;
|
||||
border:1px solid #e66e15;
|
||||
}
|
||||
.High {
|
||||
background-color:#E97659;
|
||||
border:1px solid #E45959;
|
||||
}
|
||||
.Disaster {
|
||||
background-color:#E45959;
|
||||
border:1px solid #DE1E09;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #010059;
|
||||
}
|
||||
a:link {
|
||||
text-decoration: none;
|
||||
color: #010059;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration: none;
|
||||
color: #010059;
|
||||
}
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
color: #010059;
|
||||
}
|
||||
hr {
|
||||
border: none;
|
||||
height: 1px;
|
||||
color: #333; /* old IE */
|
||||
background-color: #333;
|
||||
}
|
||||
.MsgBody {
|
||||
width: 100%;
|
||||
}
|
||||
#maintable {
|
||||
width: 800px;
|
||||
}
|
||||
@media screen and (max-width: 800px;) {
|
||||
#maintable {
|
||||
width: 100%;
|
||||
max-width: 790px;
|
||||
}
|
||||
#mainimage {
|
||||
-ms-interpolation-mode: bicubic;
|
||||
width: 100%;
|
||||
max-width: 790px;
|
||||
max-height: 450px;
|
||||
}
|
||||
}
|
||||
.links {
|
||||
font-size: 9px;
|
||||
}
|
||||
.header {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.graphHeader {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.content {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table style="margin: 10px;border-spacing:0 15px;border-collapse: separate;">
|
||||
<tr>
|
||||
<td class="{{ EVENT_SEVERITY }}" style="border-radius:10px;padding: 10px 36px 10px 36px; ">
|
||||
<table class="{{ EVENT_SEVERITY }}" style="border:0; text-align:left;" cellpadding="0" cellspacing="0">
|
||||
<tr><td>
|
||||
<p><b>EVENT INFORMATION</b></p>
|
||||
Description: <b>{{ EVENT_NAME }}</b><br/>
|
||||
Host: <b>{{ HOST_NAME }}</b>
|
||||
<table style="margin: 5px;border-spacing:0 10px;border-collapse: separate;" align=center>
|
||||
<tr>
|
||||
<td class="{{ EVENT_SEVERITY }}" style="border-radius:10px;padding:5px 18px 5px 18px; ">
|
||||
<table class="{{ EVENT_SEVERITY }}" style="border:0; text-align:left;" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<p><span class="header">EVENT INFORMATION</span></p>
|
||||
<span class="content">
|
||||
Description: <b>{{ EVENT_NAME }}</b><br/>
|
||||
Host: <b>{{ HOST_NAME }}</b>
|
||||
{% if HOST_ERROR|length > 0 %}
|
||||
({{ HOST_ERROR }})
|
||||
({{ HOST_ERROR }})
|
||||
{% endif %}
|
||||
<br/>
|
||||
Operational data: <b>{{ EVENT_OPDATA }}</b><br/>
|
||||
Status: <b>{{ EVENT_STATUS }}</b><br/>
|
||||
Severity: <b>{{ EVENT_SEVERITY }}</b><br/>
|
||||
<br/>
|
||||
{% if EVENT_OPDATE|length > 0 %}
|
||||
Operational data: <b>{{ EVENT_OPDATA }}</b><br/>
|
||||
{% endif %}
|
||||
Status: <b>{{ EVENT_STATUS }}</b><br/>
|
||||
Severity: <b>{{ EVENT_SEVERITY }}</b><br/>
|
||||
{% if EVENT_SEVERITY == "Resolved" %}
|
||||
Duration: <b>{{ EVENT_DURATION }}</b><br/>
|
||||
Duration: <b>{{ EVENT_DURATION }}</b><br/>
|
||||
{% endif %}
|
||||
Item: <b>{{ ITEM_NAME }}</b><br/>
|
||||
Previous/Last: {{ ITEM_PREVIOUSVALUE }} ==> {{ ITEM_LASTVALUE }}
|
||||
<p><a href="{{ EVENTDETAILS_URL }}">Event Details</a><br/></p>
|
||||
</td></tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% if GRAPH_CID|length > 0 %}
|
||||
<tr>
|
||||
<td align="center"><img src="{{ GRAPH_CID }}" /></td>
|
||||
</tr>
|
||||
Item: <b>{{ ITEM_NAME }}</b><br/>
|
||||
Previous/Last: {{ ITEM_PREVIOUSVALUE }} ==> {{ ITEM_LASTVALUE }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% if GRAPH_CID1|length > 0 %}
|
||||
<tr>
|
||||
<div align=justify>
|
||||
<td align="center">
|
||||
<span class="graphHeader">{{ GRAPH_HEADER1 }}</span><br/>
|
||||
<img id="mainimage" border=0 style="width: 100%; max-width: 790px" alt="Zabbix Graph" src="{{ GRAPH_CID1 }}" />
|
||||
</td>
|
||||
</div>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
<p style="font-size:10px">
|
||||
Event ID: <a href="{{ EVENTDETAILS_URL }}">{{ EVENT_ID }}</a> //
|
||||
Trigger ID: <a href="{{ TRIGGER_URL }}">{{ TRIGGER_ID }}</a> //
|
||||
Item ID: <a href="{{ ITEM_URL }}">{{ ITEM_ID }}</a> //
|
||||
Host ID: <a href="{{ HOST_URL }}">{{ HOST_ID }}</a>
|
||||
</p>
|
||||
{% if GRAPH_CID2|length > 0 %}
|
||||
<tr>
|
||||
<div align=justify>
|
||||
<td align="center">
|
||||
<hr/>
|
||||
<span class="graphHeader">{{ GRAPH_HEADER2 }}</span><br/>
|
||||
<img id="mainimage" border=0 style="width: 100%; max-width: 790px" alt="Zabbix Graph" src="{{ GRAPH_CID2 }}" />
|
||||
</td>
|
||||
</div>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if GRAPH_CID3|length > 0 %}
|
||||
<tr>
|
||||
<div align=justify>
|
||||
<td align="center">
|
||||
<hr/>
|
||||
<span class="graphHeader">{{ GRAPH_HEADER3 }}</span><br/>
|
||||
<img id="mainimage" border=0 style="width: 100%; max-width: 790px" alt="Zabbix Graph" src="{{ GRAPH_CID3 }}" />
|
||||
</td>
|
||||
</div>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if GRAPH_CID4|length > 0 %}
|
||||
<tr>
|
||||
<div align=justify>
|
||||
<td align="center">
|
||||
<hr/>
|
||||
<span class="graphHeader">{{ GRAPH_HEADER4 }}</span><br/>
|
||||
<img id="mainimage" border=0 style="width: 100%; max-width: 790px" alt="Zabbix Graph" src="{{ GRAPH_CID4 }}" />
|
||||
</td>
|
||||
</div>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td class="links">
|
||||
<div align=center>
|
||||
Event ID: <a href="{{ EVENTDETAILS_URL }}">{{ EVENT_ID }}</a> //
|
||||
Trigger ID: <a href="{{ TRIGGER_URL }}">{{ TRIGGER_ID }}</a> //
|
||||
Item ID: <a href="{{ ITEM_URL }}">{{ ITEM_ID }}</a> //
|
||||
Host ID: <a href="{{ HOST_URL }}">{{ HOST_ID }}</a>
|
||||
{% if GRAPH_ZABBIXLINK|length > 0 %}
|
||||
// Graph ID: <a href="{{ GRAPH_ZABBIXLINK }}">{{ GRAPH_ID }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -3,10 +3,10 @@ EVENT INFORMATION #{{ EVENT_ID }}
|
||||
Severity: {{ EVENT_SEVERITY }}
|
||||
Description: {{ EVENT_NAME }}
|
||||
|
||||
{% if GRAPH_CID|length > 0 %}
|
||||
GRAPH #{{ GRAPH_ID }}
|
||||
{{ GRAPH_URL }}
|
||||
This graph will remain available for approx. the next 2 weeks only; use HTML reader after this period to view the graph.
|
||||
{% if GRAPH_CID1|length > 0 %}
|
||||
GRAPH #{{ GRAPH_ID1 }}
|
||||
{{ GRAPH_URL1 }}
|
||||
This graph will remain available for approx. the next 2 weeks only; use HTML reader after this period to view the graph that is embedded in the attached HTML version.
|
||||
{% endif %}
|
||||
|
||||
TRIGGER #{{ TRIGGER_ID }}
|
||||
|
||||
Reference in New Issue
Block a user