I have a well functioning Nagios Core system set up. I wanted to make our alerts both easier to read pertinent information quickly as well as provide additional details from custom host and service variables. While I have touched on Perl in the past this is my first real Perl script. The larger portion of CSS and HTML structure com from fm4dd.com. The email logic was taken from the Mail::Sendmail documentation FAQ.
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use Mail::Sendmail;
use MIME::QuotedPrint;
use HTML::Entities;
my $notificationType = '';
my $hostAlias = '';
my $hostAddress = '';
my $serviceDescription = '';
my $serviceState = '';
my $eventDateTime = '';
my $serviceOutput = '';
my $verbose = 0;
my $companyName = 'My Company';
my %ancillaryValues = ();
##############################
# Handle command line options
##############################
GetOptions(
'notificationType=s' => \$notificationType,
'hostAlias=s' => \$hostAlias,
'hostAddress=s' => \$hostAddress,
'serviceDescription=s' => \$serviceDescription,
'serviceState=s' => \$serviceState,
'eventDateTime=s' => \$eventDateTime,
'serviceOutput=s' => \$serviceOutput,
'optional=s' => \%ancillaryValues,
'verbose' => \$verbose
);
##############################
# CSS for mail
##############################
(my $style = qq(
!<style type="text/css">
! body {text-align: center; font-family: Verdana, sans-serif; font-size: 10pt;}
! img.logo {float: left; margin: 10px 10px 10px; vertical-align: middle}
! img.link {float: right; margin: 0px 1px; vertical-align: middle}
! table.fixed {width: 600px; text-align:center; margin-left: auto; margin-right: auto; border: 1px solid black; table-layout:fixed;}
! th {white-space: nowrap; width: 180px;}
! table.fixed td {word-wrap:break-word;}
! th.even {background-color: #D9D9D9;}
! td.even {background-color: #F2F2F2;}
! th.odd {background-color: #F2F2F2;}
! td.odd {background-color: #FFFFFF;}
! th,td {font-family: Verdana, sans-serif; font-size: 10pt; text-align:left;}
! th.customer {width: 600px; background-color: #004488; color: #ffffff;}
! td.problem {background-color: #FF8080;}
! td.recovery {background-color: #80FF80;}
! td.acknowledgement {background-color: #FFFF80;}
! td.downtimestart {background-color: #80FFFF;}
! td.downtimeend {background-color: #80FF80;}
! td.downtimecancelled {background-color: #FFFF80;}
! td.flappingstart {background-color: #FF8080;}
! td.flappingstop {background-color: #80FF80;}
! td.flappingdisabled {background-color: #FFFF80;}
! td.test {background-color: #80FFFF;}
! td.critical {background-color: #FFAA60;}
! td.warning {background-color: #FFFF80;}
! td.ok {background-color: #80FF80;}
! td.unknown {background-color: #80FFFF;}
! th.header{text-align: center; font-family: Verdana, sans-serif; font-size: 12pt;}
! p.foot {width: 602px; background-color: #004488; color: #ffffff; margin-left: auto; margin-right: auto;}
!</style>
)) =~ s/^[^\S\n]+[!\n]?//mg;
##############################
# Function and Subroutines
##############################
sub buildHTMLContent {
# Function will build the html content to be used in the email.
# First create a table with all of the common service information.
# A second optional table will be created if supplemental information is provided.
# CSS and other wrapper HTML tags are added after.
my $ancillaryHTMLTable = '';
# Build the main table with service information
(my $serviceHTMLTable = qq(
!<table class=\"fixed\">
! <tr><th colspan=\"2\" class=\"header\">Nagios Monitoring System Notification</th></tr>
! <tr><th colspan=\"2\" class=\"customer\">$companyName</th></tr>
! <tr><th class=\"even\">Notification Type:</th><td class=\"$notificationType\">$notificationType</td></tr>
! <tr><th class=\"odd\">Host:</th><td class=\"odd\">$hostAlias</td></tr>
! <tr><th class=\"even\">Check:</th><td class=\"even\">$serviceDescription</td></tr>
! <tr><th class=\"odd\">State:</th><td class=\"$serviceState\">$serviceState</td></tr>
! <tr><th class=\"even\">Address:</th><td class=\"even\">$hostAddress</td></tr>
! <tr><th class=\"odd\">Date/Time:</th><td class=\"odd">$eventDateTime</td></tr>
! <tr><th class=\"even\">Additional Info:</th><td class=\"even\">$serviceOutput</td></tr>
!</table>
))=~ s/^[^\S\n]+[!\n]?//mg;
# Build the optional second table from %ancillaryValues
# Check if there are any values to be processed.
if((keys %ancillaryValues) > 0){
my $rows = '';
my $parity = 'even'; # This will get toggled as we build the rows. Used for colour formatting.
# Build each row. If the data appears to be a hyperlink, format it as such.
foreach(keys %ancillaryValues) {
# Check if the data starts with a link.
if($ancillaryValues{$_} =~ /^https?://///){
# This is a link.
$rows .= "<tr><th class=\"$parity\">$_:</th><td class=\"$parity\"><a href=\"$ancillaryValues{$_}\">$ancillaryValues{$_}</a></td></tr>"
} else {
# This is not a link.
$rows .= "<tr><th class=\"$parity\">$_:</th><td class=\"$parity\">$ancillaryValues{$_}</td></tr>"
}
# Switch parity
$parity = $parity eq 'even' ? 'odd' : :'even';
}
# Insert the rows into the table
($ancillaryHTMLTable = qq(
!<table class="fixed">
! <tr><th colspan=\"2\" class=\"header\">Ancillary Infomation</th></tr>
! $rows
!</table>
!))=~ s/^[^\S\n]+[!\n]?//mg;
}
(my $html = qq(
!<html>\n<head>\n$style\n<head>\n<body>\n
!$serviceHTMLTable
!<br>
!$ancillaryHTMLTable
!</body>\n</html>
)) =~ s/^[^\S\n]+[!\n]?//mg;
return $html;
}
sub sendHTMLMail {
# This function will send an html formatted mail
# of the service data
my $boundary = "====" . time() . "====";
(my $messageText = qq(
!***** Nagios *****
!
!Notification Type: $notificationType
!
!Service: $serviceDescription
!Host: $hostAlias
!Address: $hostAddress
!State: $serviceState
!
!Date/Time: $eventDateTime
!
!Additional Info:
!
!${\uc $serviceState}: $serviceOutput
!)) =~ s/^[^\S\n]+[!\n]?//mg;
my $plain = encode_qp $messageText;
my %mail = (
To => 'myemail@address.com',
From => 'script@company.com',
Subject => "Testing HTML Message",
'Content-Type' => "multipart/alternative; boundary=\"$boundary\""
);
$boundary = '--'.$boundary;
($mail{body} = qq(
!$boundary
!Content-Type: text/plain; charset="iso-8859-1"
!Content-Transfer-Encoding: quoted-printable
!
!$plain
!
!$boundary
!Content-Type: text/html; charset="iso-8859-1"
!Content-Transfer-Encoding: quoted-printable
!
!${\encode_qp(buildHTMLContent)}
!$boundary--
)) =~ s/^[^\S\n]+[!\n]?//mg;
sendmail(%mail) or die $Mail::Sendmail::error;
print "Mail was sent succesfully: \n", $Mail::Sendmail::log;
print "\n";
};
##############################
# Print optional verbose information
##############################
if($verbose == 1){
print "Time time is:" . time() . "\n";
print "The service is: '$serviceDescription'\n";
# Check to see if we have any extra values passed
if((keys %ancillaryValues) > 0){
foreach(keys %ancillaryValues) {
print "Optional Value: $_ / $ancillaryValues{$_}\n";
}
}
}
sendHTMLMail;
I tried to minimize reliance on modules so while suggesting some that would have reduced the code it good to know I am hoping not to add any more.
Sample call:
perl nagios_send_html_service_mail.pl --serviceDescription "CPU Usage" --hostAlias yrp_7f_server
--hostAddress 10.10.13.33 --notificationType PROBLEM --eventDateTime `date '+%d/%m/%Y'`
--serviceOutput "5m: average load 100% critical, 1m: average load 100% critical, 30s: average load 100% critical"
--serviceState Critical --opt This=That --verbose --opt 'Document Manager'='http://kanimage02:81/asset.aspx?AssetID=6456' --option CodeReview=Awesome
In a production environment you would build a Nagios command string using macros that would populate those parameters with real information on execution. I have used dummy data for this example.
Example email generated.
HTML of the above table
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"><style type="text/css">
body {text-align: center; font-family: Verdana, sans-serif; font-size: 10pt;}
img.logo {float: left; margin: 10px 10px 10px; vertical-align: middle}
img.link {float: right; margin: 0px 1px; vertical-align: middle}
table.fixed {width: 600px; text-align:center; margin-left: auto; margin-right: auto; border: 1px solid black; table-layout:fixed;}
th {white-space: nowrap; width: 180px;}
table.fixed td {word-wrap:break-word;}
th.even {background-color: #D9D9D9;}
td.even {background-color: #F2F2F2;}
th.odd {background-color: #F2F2F2;}
td.odd {background-color: #FFFFFF;}
th,td {font-family: Verdana, sans-serif; font-size: 10pt; text-align:left;}
th.customer {width: 600px; background-color: #004488; color: #ffffff;}
td.problem {background-color: #FF8080;}
td.recovery {background-color: #80FF80;}
td.acknowledgement {background-color: #FFFF80;}
td.downtimestart {background-color: #80FFFF;}
td.downtimeend {background-color: #80FF80;}
td.downtimecancelled {background-color: #FFFF80;}
td.flappingstart {background-color: #FF8080;}
td.flappingstop {background-color: #80FF80;}
td.flappingdisabled {background-color: #FFFF80;}
td.test {background-color: #80FFFF;}
td.critical {background-color: #FFAA60;}
td.warning {background-color: #FFFF80;}
td.ok {background-color: #80FF80;}
td.unknown {background-color: #80FFFF;}
th.header{text-align: center; font-family: Verdana, sans-serif; font-size: 12pt;}
p.foot {width: 602px; background-color: #004488; color: #ffffff; margin-left: auto; margin-right: auto;}
</style>
<head>
<body>
<table class="fixed">
<tr><th colspan="2" class="header">Nagios Monitoring System Notification</th></tr>
<tr><th colspan="2" class="customer">My Company</th></tr>
<tr><th class="even">Notification Type:</th><td class="PROBLEM">PROBLEM</td></tr>
<tr><th class="odd">Host:</th><td class="odd">yrp_7f_server</td></tr>
<tr><th class="even">Check:</th><td class="even">CPU Usage</td></tr>
<tr><th class="odd">State:</th><td class="Critical">Critical</td></tr>
<tr><th class="even">Address:</th><td class="even">10.10.13.33</td></tr>
<tr><th class="odd">Date/Time:</th><td class="odd">01/12/2016</td></tr>
<tr><th class="even">Additional Info:</th><td class="even">5m: average load 100% critical, 1m: average load 100% critical, 30s: average load 100% critical</td></tr>
</table>
<br>
<table class="fixed">
<tr><th colspan="2" class="header">Ancillary Infomation</th></tr>
<tr><th class="even">CodeReview:</th><td class="even">Awesome</td></tr><tr><th class="odd">Document Manager:</th><td class="odd"><a href="http://kanimage02:81/asset.aspx?AssetID=6456">http://kanimage02:81/asset.aspx?AssetID=6456</a></td></tr><tr><th class="even">This:</th><td class="even">That</td></tr>
</table>
</body>
</html>
My Perl build details
This is perl 5, version 16, subversion 3 (v5.16.3) built for x86_64-linux-thread-multi
I intend to make a host email script from this as well. It will be mostly the same except the main table will be displaying different variables.