Created
May 21, 2014 03:16
-
-
Save b-boyd/7ddd1a163dca537b340a to your computer and use it in GitHub Desktop.
mailer-handler.rb - TCP Mailer handler for Sensu
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/opt/sensu/embedded/bin/ruby | |
# A TCP Mailer handler for Sensu, based on https://github.com/sensu/sensu-community-plugins/blob/master/handlers/notification/mailer.rb. | |
# | |
# This expects the email address to be defined with an 'mail_to' attribute in the check, | |
# so different checks can send to different addresses. | |
# | |
# Requires sensu-generichandler.rb (https://gist.github.com/b-boyd/d52abc1c3baf6ea9e33d) | |
# and expects it in "../lib/". This mailer-handler.rb can be copied to | |
# /opt/sensu/handler/bin/. | |
# | |
# Example handler configuration, mailer.json: | |
# { | |
# "mailer": { | |
# "smtp_domain": "mydomain.com", | |
# "mail_from": "sensu@mydomain.com", | |
# "smtp_port": 25, | |
# "smtp_address": "smtp.mydomain.com" | |
# }, | |
# "handlers": { | |
# "mailer": { | |
# "severities": [ | |
# "ok", | |
# "warning", | |
# "critical", | |
# "unknown" | |
# ], | |
# "type": "tcp", | |
# "socket": { | |
# "port": 2035, | |
# "host": "127.0.0.1" | |
# } | |
# } | |
# } | |
# } | |
# | |
# Don't forget an init script (https://gist.github.com/b-boyd/821433dd903fd45d998e) | |
# and to rotate/purge the /var/log/sensu/sensu-mailer-handler.log file. | |
# | |
# Add a sensu check to ensure the process is running and/or the port is listening | |
# and use the remediator handler to automatically restart. | |
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') | |
require 'rubygems' | |
require 'sensu/cli' | |
require 'sensu/base' | |
require 'sensu-generichandler' | |
gem 'mail', '~> 2.5.4' | |
require 'mail' | |
require 'timeout' | |
class MailerServer | |
$STOP_SIGNALS = %w[INT TERM] | |
def self.run(options={}) | |
server = self.new(options) | |
EM.run do | |
server.start | |
server.trap_signals | |
end | |
end | |
def initialize(options={}) | |
base = Sensu::Base.new(options) | |
base.setup_process | |
@logger = base.logger | |
@settings = base.settings | |
end | |
def start | |
begin | |
host = @settings['handlers']['mailer']['socket']['host'] | |
rescue | |
@logger.error("@settings['handlers']['mailer']['socket']['host'] not defined") | |
return | |
end | |
begin | |
port = @settings['handlers']['mailer']['socket']['port'].to_i | |
rescue | |
@logger.error("@settings['handlers']['mailer']['socket']['port'] not defined") | |
return | |
end | |
mailer_client = MailerClient.new | |
EM.start_server(host, port, MailerReceiver) do |conn| | |
conn.handler = mailer_client | |
conn.logger = @logger | |
end | |
end | |
def trap_signals | |
@signals = Array.new | |
$STOP_SIGNALS.each do |signal| | |
Signal.trap(signal) do | |
@signals << signal | |
end | |
end | |
EM::PeriodicTimer.new(1) do | |
signal = @signals.shift | |
if $STOP_SIGNALS.include?(signal) | |
@logger.warn('received signal', { | |
:signal => signal | |
}) | |
EM.stop | |
end | |
end | |
end | |
end | |
# Receive data over a socket that should go to Mail | |
class MailerReceiver < EM::Connection | |
attr_accessor :handler | |
attr_accessor :logger | |
def receive_data(data) | |
begin | |
handler.handle(data, logger) | |
rescue Exception => e | |
logger.info(e.message) | |
end | |
close_connection | |
end | |
end | |
class MailerClient < Sensu::GenericHandler | |
@event | |
@logger | |
def handle(data, logger) | |
@logger = logger | |
if ! is_json?(data) | |
@logger.error("Invalid JSON data - #{data}") | |
return | |
end | |
@event = JSON.parse(data) | |
self.filter | |
client = @event['client']['name'] | |
output = @event['check']['output'] | |
send_email(client, output) | |
end | |
def send_email(client, output) | |
# If a 'mail_to' isn't defined for this check | |
if ! @event['check']['mail_to'] | |
@logger.info("No mail_to defined") | |
return | |
end | |
mail_to = @event['check']['mail_to'] | |
mail_from = settings['mailer']['mail_from'] | |
smtp_address = settings['mailer']['smtp_address'] || 'localhost' | |
smtp_port = settings['mailer']['smtp_port'] || '25' | |
smtp_domain = settings['mailer']['smtp_domain'] || 'localhost.localdomain' | |
smtp_username = settings['mailer']['smtp_username'] || nil | |
smtp_password = settings['mailer']['smtp_password'] || nil | |
smtp_authentication = settings['mailer']['smtp_authentication'] || :plain | |
if @event['check']['status'] == 0 | |
status = "RESOLVED" | |
elsif @event['check']['status'] == 1 | |
status = "WARNING" | |
elsif @event['check']['status'] == 2 | |
status = "CRITICAL" | |
else | |
status = "UNKNOWN" | |
end | |
subject = "#{status} - " + @event['check']['name'] + " on #{client}" | |
text_body = "#{output}\n\n" | |
text_body += <<-BODY | |
Host: #{@event['client']['name']} | |
Check Name: #{@event['check']['name']} | |
Timestamp: #{Time.at(@event['check']['issued'])} | |
Command: #{@event['check']['command']} | |
Occurrences: #{@event['occurrences']} | |
BODY | |
html_body = "<b>#{output}</b><br><br>" | |
html_body += <<-BODY | |
Host: #{@event['client']['name']}<br> | |
Timestamp: #{Time.at(@event['check']['issued'])}<br> | |
Check Name: #{@event['check']['name']}<br> | |
Command: #{@event['check']['command']}<br> | |
Occurrences: #{@event['occurrences']}<br> | |
BODY | |
Mail.defaults do | |
delivery_options = { | |
:address => smtp_address, | |
:port => smtp_port, | |
:domain => smtp_domain, | |
:openssl_verify_mode => 'none', | |
:enable_starttls_auto => true | |
} | |
unless smtp_username.nil? | |
auth_options = { | |
:user_name => smtp_username, | |
:password => smtp_password, | |
:authentication => smtp_authentication | |
} | |
delivery_options.merge! auth_options | |
end | |
delivery_method :smtp, delivery_options | |
end | |
begin | |
timeout 10 do | |
Mail.deliver do | |
to mail_to | |
from mail_from | |
subject subject | |
text_part do | |
body text_body | |
end | |
html_part do | |
content_type 'text/html; charset=UTF-8' | |
body html_body | |
end | |
end | |
@logger.info('mail -- sent alert for ' + short_name + ' to ' + mail_to.to_s) | |
end | |
rescue Timeout::Error | |
@logger.error('mail -- timed out while attempting to ' + @event['action'] + ' an incident -- ' + short_name) | |
end | |
end | |
def short_name | |
@event['client']['name'] + '/' + @event['check']['name'] | |
end | |
def bail(msg) | |
raise msg + ': ' + @event['client']['name'] + '/' + @event['check']['name'] | |
end | |
def is_json?(str) | |
begin | |
!!JSON.parse(str) | |
rescue | |
false | |
end | |
end | |
end | |
begin | |
options = Sensu::CLI.read | |
MailerServer.run(options) | |
rescue exit | |
rescue Exception => e | |
@logger.error("#{e}. #{e.backtrace}") | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment