Email Notification Plugin - really only SMTP tested and confirmed

working.
This commit is contained in:
Nicholas Padilla 2012-03-29 09:29:14 -06:00
parent 6c1c5e3c62
commit 8f48d1ed42
4 changed files with 352 additions and 2 deletions

View file

@ -11,6 +11,7 @@ gem "onstomp" # for stomp protocol, Apache 2.0 License
gem "json" # Ruby license
#gem "awesome_print" # MIT License
gem "jruby-openssl", :platforms => :jruby # For enabling SSL support, CPL/GPL 2.0
gem "mail" #outputs/email, # License: MIT License
gem "minitest" # License: Ruby
gem "rack" # License: MIT

View file

@ -33,6 +33,7 @@ GEM
rubyzip
http_parser.rb (0.5.3)
http_parser.rb (0.5.3-java)
i18n (0.6.0)
jls-grok (0.10.6)
cabin (~> 0.4.0)
jruby-elasticsearch (0.0.11)
@ -46,12 +47,18 @@ GEM
addressable (~> 2.2.6)
ffi (~> 1.0.9)
spoon (~> 0.0.1)
mail (2.4.4)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.18)
minitest (2.11.4)
mongo (1.6.1)
bson (~> 1.6.1)
mtrc (0.0.4)
netrc (0.7.1)
onstomp (1.0.6)
polyglot (0.3.3)
rack (1.4.1)
rack-protection (1.2.0)
rack
@ -70,8 +77,10 @@ GEM
tilt (~> 1.3, >= 1.3.3)
spoon (0.0.1)
statsd-ruby (0.3.0)
stomp (1.2.1)
tilt (1.3.3)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
trollop (1.16.2)
uuidtools (2.1.2)
xmpp4r (0.5)
@ -96,14 +105,15 @@ DEPENDENCIES
jruby-elasticsearch (= 0.0.11)
jruby-openssl
json
mail
minitest
mongo
onstomp
rack
redis
riemann-client (= 0.0.6)
sass
sinatra
statsd-ruby (= 0.3.0)
stomp
uuidtools
xmpp4r (= 0.5)

338
lib/logstash/outputs/email.rb Executable file
View file

@ -0,0 +1,338 @@
require "logstash/outputs/base"
require "logstash/namespace"
# https://github.com/mikel/mail
# supports equal(default), not equal(!), greater than(>), less than(<), greater than or equal(>=), less than or equal(<=), contains(*), does not contain(!*)
# you must provide a matchName - which is the key. Then provide your query values - again in key value pairs, separated by a ',' in the value spot.
# You can say this :
# [ "response errors", "response,501,,or,response,301" ]
# I hate making requirements like this but this is the format that is the most flexible for making fine selections over data.
# NOTE: In the above example we are using just an equality test - so the two values must be exact for matches to be made. You must provide an AND/OR block
# between conditions so we know how to deal with them. Please see below for an example where you wanted an AND instead of the OR default - this would require both to be valid.
# [ "response errors", "response,501,,and,response,301" ]
# as you can see you can just seperate the Operator logic with a blank key and the operator of your liking - AND/OR
# IMPORTANT : you MUST provide a "matchName". This is so I can easily be able to provide a label of sorts for the alert.
# In addition, we break after we find the first valid match.
#
# email {
# tags => [ "sometag" ]
# match => [ "response errors", "response,501,,or,response,301",
# "multiple response errors", "response,501,,and,response,301" ]
# to => "main.contact@domain.com"
# from => "alert.account@domain.com" # default: logstash.alert@nowhere.com
# cc => "" # provide additional recipients
# options => [ "smtpIporHost", "smtp.gmail.com",
# "port", "587",
# "domain", "yourDomain", # optional
# "userName", "yourSMTPUsername",
# "password", "PASS",
# "starttls", "true",
# "authenticationType", "plain",
# "debug", "true" # optional
# ]
# via => "smtp" # or pop or sendmail
# subject => "Found '%{matchName}' Alert on %{@source_host}"
# body => "Here is the event line %{@message}"
# htmlbody => "<h2>%{matchName}</h2><br/><br/><h3>Full Event</h3><br/><br/><div align='center'>%{@message}</div>"
# }
class LogStash::Outputs::Email < LogStash::Outputs::Base
config_name "email"
plugin_status "beta"
# the registered fields that we want to monitor
# A hash of matches of field => value
config :match, :validate => :hash, :required => true
# the To address setting - fully qualified email address to send to
config :to, :validate => :string, :required => true
# The From setting for email - fully qualified email address for the From:
config :from, :validate => :string, :default => "logstash.alert@nowhere.com"
# cc - send to others
config :cc, :validate => :string, :default => ""
# how to send email: either smtp or sendmail - default to 'smtp'
config :via, :validate => :string, :default => "smtp"
# the options to use:
# smtp: address, port, enable_starttls_auto, user_name, password, authentication(bool), domain
# sendmail: location, arguments
# If you do not specify anything, you will get the following equivalent code set in
# every new mail object:
#
# Mail.defaults do
# delivery_method :smtp, { :address => "localhost",
# :port => 25,
# :domain => 'localhost.localdomain',
# :user_name => nil,
# :password => nil,
# :authentication => nil,(plain, login and cram_md5)
# :enable_starttls_auto => true }
#
# retriever_method :pop3, { :address => "localhost",
# :port => 995,
# :user_name => nil,
# :password => nil,
# :enable_ssl => true }
# end
#
# Mail.delivery_method.new #=> Mail::SMTP instance
# Mail.retriever_method.new #=> Mail::POP3 instance
#
# Each mail object inherits the default set in Mail.delivery_method, however, on
# a per email basis, you can override the method:
#
# mail.delivery_method :sendmail
#
# Or you can override the method and pass in settings:
#
# mail.delivery_method :sendmail, { :address => 'some.host' }
#
# You can also just modify the settings:
#
# mail.delivery_settings = { :address => 'some.host' }
#
# The passed in hash is just merged against the defaults with +merge!+ and the result
# assigned the mail object. So the above example will change only the :address value
# of the global smtp_settings to be 'some.host', keeping all other values
config :options, :validate => :hash, :default => {}
# subject for email
config :subject, :validate => :string, :default => ""
# body for email - just plain text
config :body, :validate => :string, :default => ""
# body for email - can contain html markup
config :htmlbody, :validate => :string, :default => ""
# attachments - has of name of file and file location
config :attachments, :validate => :array, :default => []
# contenttype : for multipart messages, set the content type and/or charset of the html part
config :contenttype, :validate => :string, :default => "text/html; charset=UTF-8"
public
def register
require "mail"
if @via == "smtp"
debug = @options.include?("debug")
if !debug
debug = false
else
debug = @options.fetch("debug")
end
smtpIporHost = @options.include?("smtpIporHost")
if !smtpIporHost
smtpIporHost = "localhost"
else
smtpIporHost = @options.fetch("smtpIporHost")
end
domain = @options.include?("domain")
if !domain
domain = "localhost"
else
domain = @options.fetch("domain")
end
port = @options.include?("port")
if !port
port = 25
else
port = @options.fetch("port")
end
tls = @options.include?("starttls")
if !tls
tls = false
else
tls = @options.fetch("starttls")
end
pass = @options.include?("password")
if !pass
pass = ""
else
pass = @options.fetch("password")
end
userName = @options.include?("userName")
if !userName
userName = ""
else
userName = @options.fetch("userName")
end
authenticationType = @options.include?("authenticationType")
if !authenticationType
authenticationType = "plain"
else
authenticationType = @options.fetch("authenticationType")
end
Mail.defaults do
delivery_method :smtp , { :address => smtpIporHost,
:port => port,
:domain => domain,
:user_name => userName,
:password => pass,
:authentication => authenticationType,
:enable_starttls_auto => tls,
:debug => debug }
end
else
Mail.defaults do
delivery_method :@via, @options
end
end # @via tests
@logger.debug("Email Output Registered!", :config => @config)
end # def register
public
def receive(event)
return unless output?(event)
@logger.debug("Event being tested for Email", :tags => @tags, :event => event)
# Set Intersection - returns a new array with the items that are the same between the two
if !@tags.empty? && (event.tags & @tags).size == 0
# Skip events that have no tags in common with what we were configured
@logger.debug("No Tags match for Email Output!")
return
end
@logger.debug("Match data for Email - ", :match => @match)
successful = false
matchName = ""
operator = ""
@match.each do |name, query|
if successful
break
else
matchName = name
end
# now loop over the csv query
queryArray = query.split(',')
index = 1
while index < queryArray.length
field = queryArray.at(index -1)
value = queryArray.at(index)
index = index + 2
if field == ""
if value.downcase == "and"
operator = "and"
elsif value.downcase == "or"
operator = "or"
else
operator = "or"
@logger.error("Operator Provided Is Not Found, Currently We Only Support AND/OR Values! - defaulting to OR")
end
else
hasField = event.fields.has_key?(field)
@logger.debug("Does Event Contain Field - ", :hasField => hasField)
isValid = false
# if we have maching field and value is wildcard - we have a success
if hasField
if value == "*"
isValid = true
else
# we get an array so we need to loop over the values and find if we have a match
eventFieldValues = event.fields.fetch(field)
@logger.debug("Event Field Values - ", :eventFieldValues => eventFieldValues)
eventFieldValues.each do |eventFieldValue|
isValid = validateValue(eventFieldValue, value)
if isValid # no need to iterate any further
@logger.debug("VALID CONDITION FOUND - ", :eventFieldValue => eventFieldValue, :value => value)
break
end
end # end eventFieldValues.each do
end # end value == "*"
end # end hasField
# if we have an AND operator and we have a successful == false break
if operator == "and" && !isValid
successful = false
elsif operator == "or" && (isValid || successful)
successful = true
else
successful = isValid
end
end
end
end # @match.each do
@logger.debug("Email Did we match any alerts for event : ", :successful => successful)
if successful
# first add our custom field - matchName - so we can use it in the sprintf function
event["matchName"] = matchName
@logger.debug("Sending mail with these settings : ", :via => @via, :options => @options, :from => @from, :to => @to, :cc => @cc, :subject => @subject, :body => @body, :content_type => @contenttype, :htmlbody => @htmlbody, :attachments => @attachments, :to => to, :to => to)
formatedSubject = event.sprintf(@subject)
formattedBody = event.sprintf(@body)
formattedHtmlBody = event.sprintf(@htmlbody)
# we have a match(s) - send email
mail = Mail.new
mail.from = event.sprintf(@from)
mail.to = event.sprintf(@to)
mail.cc = event.sprintf(@cc)
mail.subject = formatedSubject
if @htmlbody.empty?
mail.body = formattedBody
else
mail.text_part = Mail::Part.new do
content_type "text/plain; charset=UTF-8"
body formattedBody
end
mail.html_part = Mail::Part.new do
content_type "text/html; charset=UTF-8"
body formattedHtmlBody
end
end
@attachments.each do |fileLocation|
mail.add_file(fileLocation)
end # end @attachments.each
mail.deliver!
end # end if successful
end # def receive
private
def validateValue(eventFieldValue, value)
valid = false
# order of this if-else is important - please don't change it
if value.start_with?(">=")# greater than or equal
value.gsub!(">=","")
if eventFieldValue.to_i >= value.to_i
valid = true
end
elsif value.start_with?("<=")# less than or equal
value.gsub!("<=","")
if eventFieldValue.to_i <= value.to_i
valid = true
end
elsif value.start_with?(">")# greater than
value.gsub!(">","")
if eventFieldValue.to_i > value.to_i
valid = true
end
elsif value.start_with?("<")# less than
value.gsub!("<","")
if eventFieldValue.to_i < value.to_i
valid = true
end
elsif value.start_with?("*")# contains
value.gsub!("*","")
if eventFieldValue.include?(value)
valid = true
end
elsif value.start_with?("!*")# does not contain
value.gsub!("!*","")
if !eventFieldValue.include?(value)
valid = true
end
elsif value.start_with?("!")# not equal
value.gsub!("!","")
if eventFieldValue != value
valid = true
end
else # default equal
if eventFieldValue == value
valid = true
end
end
return valid
end # end validateValue()
end # class LogStash::Outputs::Email

View file

@ -47,6 +47,7 @@ Gem::Specification.new do |spec|
spec.add_dependency "stomp" # for stomp protocol, Apache 2.0 License
spec.add_dependency "uuidtools" # for naming amqp queues, License ???
spec.add_dependency "xmpp4r", "~> 0.5" # outputs/xmpp, # License: As-Is
spec.add_dependency "mail" #outputs/email, # License: MIT License
spec.add_dependency("ffi-rzmq")