aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick J Cherry <patrick@bytemark.co.uk>2014-07-03 12:01:25 +0100
committerPatrick J Cherry <patrick@bytemark.co.uk>2014-07-03 12:01:25 +0100
commit02cd939814dc8aebc80b5a23a3ba6b68f4118864 (patch)
tree3e74d4e098187fc8e8536c9cef98efa15347c3b0
parentf6ddf58350e015ee49d35d3ac4a74cce53e62102 (diff)
parent9e8ee3bec749abf11a71003efd201e1e3fb0843f (diff)
Merge
-rw-r--r--.hgtags8
-rw-r--r--debian/changelog62
-rw-r--r--debian/mauvealert-client.install2
-rw-r--r--etc/mauveserver.conf31
-rw-r--r--lib/mauve/alert.rb15
-rw-r--r--lib/mauve/alert_changed.rb2
-rw-r--r--lib/mauve/alert_group.rb16
-rw-r--r--lib/mauve/configuration_builders/person.rb15
-rw-r--r--lib/mauve/notification.rb4
-rw-r--r--lib/mauve/notifiers.rb1
-rw-r--r--lib/mauve/notifiers/debug.rb2
-rw-r--r--lib/mauve/notifiers/sms_clockwork.rb72
l---------[-rw-r--r--]lib/mauve/notifiers/templates/email_subject.txt.erb23
l---------[-rw-r--r--]lib/mauve/notifiers/templates/sms.txt.erb23
-rw-r--r--lib/mauve/notifiers/templates/xmpp.html.erb6
-rw-r--r--lib/mauve/notifiers/templates/xmpp.txt.erb6
-rw-r--r--lib/mauve/person.rb123
-rw-r--r--lib/mauve/quick_update.rb166
-rw-r--r--lib/mauve/server.rb5
-rw-r--r--lib/mauve/version.rb2
-rw-r--r--test/tc_mauve_configuration_builders_person.rb6
-rw-r--r--test/tc_mauve_person.rb124
-rw-r--r--test/th_mauve.rb9
-rw-r--r--views/_alert_actions.haml8
24 files changed, 599 insertions, 132 deletions
diff --git a/.hgtags b/.hgtags
index 74101d3..cecf13f 100644
--- a/.hgtags
+++ b/.hgtags
@@ -53,3 +53,11 @@ b245559b3b70af7c72a47bc223d27337af9938dc 3.15.3
bf551ba5805f5ec40002501a6f573f6c877ef84a 3.15.4
4eaf5c4e8b39d842470909f074be8a9393466d82 3.15.5
8f17efb79101ad42e3c835b24dbca3763d38879d 3.15.6
+fed4916f989ec76455450e46d014672826f02c27 3.15.7
+4eb1d57bab0ef97e4f10d620eb394f4c68a47dc7 3.15.8
+4c761e04d53b6367a8ade4edcf5338c38178303a 3.15.9
+63c228b13092c528b72d5117e57fec3a13def7c3 3.15.10
+73b098ffe0165adacf68689428b6116dd9f1f905 3.15.11
+33456d075c9479767ecf12e289fe8e4166910d60 3.15.12
+35b3e9e7eedd3300d11d356112c0f3819edceabc 3.15.13
+68c8c5b836f7e5a1ba49c2e04bbd25ec5823e9ee 3.15.14
diff --git a/debian/changelog b/debian/changelog
index 8c54fce..68061fd 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,9 +1,65 @@
-mauvealert (3.15.6-3) stable; urgency=medium
-
+mauvealert (3.15.14-2) stable; urgency=low
+
* Relaxed mauvealert-client ruby dependency.
-
+
-- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 03 Jul 2014 11:43:00 +0100
+mauvealert (3.15.14-1) stable; urgency=low
+
+ * Web interface now defaults to wall-clock hours.
+
+ -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 22 May 2013 11:47:52 +0100
+
+mauvealert (3.15.13-1) stable; urgency=low
+
+ * Switched off global socket reverse lookups.
+
+ -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 21 May 2013 14:34:15 +0100
+
+mauvealert (3.15.12-1) stable; urgency=low
+
+ * Removed extra database lookup when receiving alert updates.
+ * Added extra to_a when checking to see if an alert should be suppressed to
+ force the database lookup at that point.
+
+ -- Patrick J Cherry <patrick@bytemark.co.uk> Tue, 21 May 2013 12:24:13 +0100
+
+mauvealert (3.15.11-1) stable; urgency=low
+
+ * Notifications now generate just one history entry to ensure that the
+ previous changes regarding notification supression work.
+
+ -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 16 May 2013 14:13:43 +0100
+
+mauvealert (3.15.10-1ubuntu1) stable; urgency=low
+
+ * Database is now queried to see if a notification should be suppressed.
+ * Notification suppression now takes urgency into account, such that more
+ urgent alerts are not smothered by less urgent ones.
+ * Templates now include which level the alert is.
+
+ -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 16 May 2013 11:47:33 +0100
+
+mauvealert (3.15.9-1) stable; urgency=low
+
+ * Added Mauve::QuickUpdate library
+
+ -- Patrick J Cherry <patrick@bytemark.co.uk> Thu, 02 May 2013 14:23:34 +0100
+
+mauvealert (3.15.8-1) stable; urgency=low
+
+ * Added a further check to make sure the last alert of the same update type
+ was being updated by reminders.
+
+ -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 01 May 2013 15:41:10 +0100
+
+mauvealert (3.15.7-1) stable; urgency=low
+
+ * Altered alert_group to only set one reminder.
+ * Allow alert to clear raised_at/cleared_at times if none specified.
+
+ -- Patrick J Cherry <patrick@bytemark.co.uk> Wed, 01 May 2013 10:16:52 +0100
+
mauvealert (3.15.6-2) stable; urgency=low
* Added generic_http library that had been missed off.
diff --git a/debian/mauvealert-client.install b/debian/mauvealert-client.install
index 0744f97..8e6e561 100644
--- a/debian/mauvealert-client.install
+++ b/debian/mauvealert-client.install
@@ -1,3 +1,3 @@
bin/mauvesend usr/bin/
lib/mauve/sender.rb usr/lib/ruby/1.8/mauve/
-
+lib/mauve/quick_update.rb usr/lib/ruby/1.8/mauve/
diff --git a/etc/mauveserver.conf b/etc/mauveserver.conf
index 4baec0a..0a7a747 100644
--- a/etc/mauveserver.conf
+++ b/etc/mauveserver.conf
@@ -75,8 +75,8 @@ notification_method("email") {
# password "x"
# }
-# How to notify by SMS - we use aql.com, you'll need to write a module
-# to use any other provider.
+# How to notify by SMS - we use aql.com, a provider for clockworksms.com
+# is also provided. For another provider, you'll need to write a module.
#
# notification_method("sms") {
# provider "AQL"
@@ -88,6 +88,16 @@ notification_method("email") {
# # Maximum number of SMS messages to concatenate for one notification
# max_messages_per_alert 3
# }
+#
+# notification_method("sms") {
+# provider "Clockwork"
+#
+# apikey "sssseeeeeeccccccccrrrrreeeeeettttsssssss"
+# from "01234567890"
+#
+# # Maximum number of SMS messages to concatenate for one notification
+# max_messages_per_alert 3
+# }
# Simple default notification preference for root at this machine, at all
# alert levels. You probably want more people, see below for a more complete
@@ -101,16 +111,20 @@ person("root") {
#
# person("johnny") {
#
+# sms "07111222333"
+# email "johnny@example.com"
+# xmpp "johnny@example.com.jabber.org"
+#
# # Johnny wants waking up 24/7 if anything urgent happens
-# urgent { sms("07111222333") }
+# urgent { sms }
#
-# # Email him for anything that's not urgent
-# normal { email("johnny@example.com") }
+# # XMPP, or Email him for anything that's not urgent
+# normal { xmpp or email }
#
# # Anything else can just be a jabber message, which he might miss.
# # Email instead if he's unavailable/offline - but give it a try if
# # we don't know his status.
-# low { xmpp("johnny@example.com.jabber.org", :if_presence => [:available, unknown]) || email("johnny@example.com") }
+# low { xmpp("johnny@example.com.jabber.org", :if_presence => [:available, unknown]) || email }
#
# # SMS messages are expensive, if we're sending more than 5 per minute,
# # tell the user we're going to stop until it slows down.
@@ -122,7 +136,10 @@ person("root") {
# Archie is Johnny's boss
#
# person("archie") {
-# all { email("archie@example.com") }
+#
+# email "archie@example.com"
+#
+# all { email }
#
# # Don't spam Archie, if more than 3 messages per hour come in.
# suppress_notifications_after 3 => 1.hour
diff --git a/lib/mauve/alert.rb b/lib/mauve/alert.rb
index a2033a5..24b9dae 100644
--- a/lib/mauve/alert.rb
+++ b/lib/mauve/alert.rb
@@ -824,7 +824,10 @@ module Mauve
#
alert.id = Alert.remove_html(alert.id.to_s)
- alert_db = first(:alert_id => alert.id, :source => update.source) ||
+ #
+ # Load the database alert, and all its properties, since we're updating.
+ #
+ alert_db = first(:alert_id => alert.id, :source => update.source, :fields => Alert.properties) ||
new(:alert_id => alert.id, :source => update.source)
##
@@ -850,6 +853,11 @@ module Mauve
alert_db.raised_at = nil
alert_db.will_raise_at = raise_time
end
+ else
+ #
+ # If no raise time has been set, then update the database to reflect this.
+ #
+ alert_db.raised_at = alert_db.will_raise_at = nil
end
if clear_time
@@ -863,6 +871,11 @@ module Mauve
alert_db.cleared_at = nil
alert_db.will_clear_at = clear_time
end
+ else
+ #
+ # If no clear time has been set, then update the database to reflect this.
+ #
+ alert_db.cleared_at = alert_db.will_clear_at = nil
end
#
diff --git a/lib/mauve/alert_changed.rb b/lib/mauve/alert_changed.rb
index 2b9dfe5..6925085 100644
--- a/lib/mauve/alert_changed.rb
+++ b/lib/mauve/alert_changed.rb
@@ -110,7 +110,7 @@ module Mauve
#
# Push this notifitcation onto the queue.
#
- Server.notification_push([alert, Time.now])
+ Server.notification_push([alert, self.remind_at])
end
#
diff --git a/lib/mauve/alert_group.rb b/lib/mauve/alert_group.rb
index fe78ad9..fe17516 100644
--- a/lib/mauve/alert_group.rb
+++ b/lib/mauve/alert_group.rb
@@ -236,15 +236,21 @@ module Mauve
# OK got the next reminder time.
#
unless remind_at.nil?
- this_reminder = AlertChanged.new(
- :level => level.to_s,
+ #
+ # Find the last reminder, if available for the same alert, update type, and person.
+ #
+ this_reminder = AlertChanged.first_or_new(
:alert_id => alert.id,
:person => self.name,
- :at => at,
:update_type => alert.update_type,
- :remind_at => remind_at,
- :was_relevant => true)
+ :remind_at.not => nil,
+ :remind_at.gt => at
+ )
+ this_reminder.level = level.to_s
+ this_reminder.at = at
+ this_reminder.remind_at = remind_at
+ this_reminder.was_relevant = true
this_reminder.save
end
diff --git a/lib/mauve/configuration_builders/person.rb b/lib/mauve/configuration_builders/person.rb
index f171d10..9cc09ab 100644
--- a/lib/mauve/configuration_builders/person.rb
+++ b/lib/mauve/configuration_builders/person.rb
@@ -51,10 +51,15 @@ module Mauve
def suppress_notifications_after(h)
raise ArgumentError.new("notification_threshold must be specified as e.g. (10 => 1.minute)") unless h.kind_of?(Hash)
- h.each do |k,v|
- raise ArgumentError.new("notification_threshold must be specified as e.g. (10 => 1.minute)") unless k.is_a?(Integer) and v.is_a?(Integer)
-
- @result.notification_thresholds[v] = Array.new(k)
+ h.each do |number_of_alerts,in_period|
+ raise ArgumentError.new("notification_threshold must be specified as e.g. (10 => 1.minute)") unless number_of_alerts.is_a?(Integer) and in_period.is_a?(Integer)
+
+ @result.suppress_notifications_after[in_period] = number_of_alerts
+ # History.all(
+ # :limit => number_of_alerts,
+ # :order => :created_at.desc,
+ # :type => "notification",
+ # :event.like => '% succeeded')
end
end
@@ -87,7 +92,7 @@ module Mauve
#
# Add a default notification threshold
#
- person.notification_thresholds[600] = Array.new(5) if person.notification_thresholds.empty?
+ person.suppress_notifications_after[600] = 5 if person.suppress_notifications_after.empty?
#
# Add a default notify clause
diff --git a/lib/mauve/notification.rb b/lib/mauve/notification.rb
index 745660a..c164afb 100644
--- a/lib/mauve/notification.rb
+++ b/lib/mauve/notification.rb
@@ -319,9 +319,7 @@ module Mauve
#
# A bit of alert de-bouncing.
#
- if already_sent_to.include?(person.username)
- logger.info("Already sent notification of #{alert} to #{person.username}")
- else
+ unless already_sent_to.include?(person.username)
person.send_alert(level, alert)
already_sent_to << person.username
end
diff --git a/lib/mauve/notifiers.rb b/lib/mauve/notifiers.rb
index 7276091..5a7acc0 100644
--- a/lib/mauve/notifiers.rb
+++ b/lib/mauve/notifiers.rb
@@ -1,6 +1,7 @@
require 'mauve/notifiers/email'
require 'mauve/notifiers/sms_default'
require 'mauve/notifiers/sms_aql'
+require 'mauve/notifiers/sms_clockwork'
require 'mauve/notifiers/xmpp'
module Mauve
diff --git a/lib/mauve/notifiers/debug.rb b/lib/mauve/notifiers/debug.rb
index a9afc52..b3e88e2 100644
--- a/lib/mauve/notifiers/debug.rb
+++ b/lib/mauve/notifiers/debug.rb
@@ -69,7 +69,7 @@ module Mauve
deliver_to_queue << [Time.now, self.class, destination, message] if deliver_to_queue
- if @disable_normal_delivery
+ if @disable_normal_delivery
true # pretend it happened OK if we're just testing
else
send_alert_without_debug(destination, alert, all_alerts, conditions)
diff --git a/lib/mauve/notifiers/sms_clockwork.rb b/lib/mauve/notifiers/sms_clockwork.rb
new file mode 100644
index 0000000..b4bd860
--- /dev/null
+++ b/lib/mauve/notifiers/sms_clockwork.rb
@@ -0,0 +1,72 @@
+require 'mauve/notifiers/debug'
+require 'cgi'
+
+module Mauve
+ module Notifiers
+ module Sms
+
+ require 'net/https'
+
+ class Clockwork
+ GATEWAY = "https://api.clockworksms.com/http/send.aspx"
+
+ attr_writer :apikey, :from
+ attr_reader :name
+
+ def initialize(name)
+ @name = name
+ end
+
+ def send_alert(destination, alert, all_alerts, conditions = {})
+ uri = URI.parse(GATEWAY)
+
+ opts_string = {
+ :key => @apikey,
+ :to => normalize_number(destination),
+ :content => prepare_message(destination, alert, all_alerts, conditions),
+ :from => @from,
+ }.map { |k,v| "#{k}=#{CGI::escape(v.to_s)}" }.join("&")
+
+ http = Net::HTTP.new(uri.host, uri.port)
+ if uri.port == 443
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+ response, data = http.post(uri.path, opts_string, {
+ 'Content-Type' => 'application/x-www-form-urlencoded',
+ 'Content-Length' => opts_string.length.to_s
+ })
+
+ if response.kind_of?(Net::HTTPSuccess)
+ #
+ # Woo -- return true!
+ #
+ true
+ else
+ false
+ end
+ end
+
+ protected
+ def prepare_message(destination, alert, all_alerts, conditions={})
+ was_suppressed = conditions[:was_suppressed] || false
+ will_suppress = conditions[:will_suppress] || false
+
+ template_file = File.join(File.dirname(__FILE__),"templates","sms.txt.erb")
+
+ txt = if File.exists?(template_file)
+ ERB.new(File.read(template_file)).result(binding).chomp
+ else
+ logger.error("Could not find sms.txt.erb template")
+ alert.to_s
+ end
+ end
+
+ def normalize_number(n)
+ n.split("").select { |s| (?0..?9).include?(s[0]) }.join.gsub(/^0/, "44")
+ end
+ end
+ end
+ end
+end
+
diff --git a/lib/mauve/notifiers/templates/email_subject.txt.erb b/lib/mauve/notifiers/templates/email_subject.txt.erb
index 119c742..802c711 100644..120000
--- a/lib/mauve/notifiers/templates/email_subject.txt.erb
+++ b/lib/mauve/notifiers/templates/email_subject.txt.erb
@@ -1,22 +1 @@
-<%=alert.id %>: <%= alert.update_type.upcase %>: <%
-case alert.update_type
-when "cleared"
-%><%= alert.cleared_at.to_s_relative %><%
-when "acknowledged"
-%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by %> until <%= alert.will_unacknowledge_at.to_s_human %><%
-else
-%><%= alert.raised_at.to_s_relative %><%
-end
-%>: <%= alert.subject %>: <%= alert.summary %><%
-if alert.source != alert.subject
-%> -- from <%= alert.source %><%
-end
-%>. <%=WebInterface.url_for(alert)%><%
-if defined? was_suppressed and defined? will_suppress
- if was_suppressed and not will_suppress
-%> (Normal service has resumed.)<%
- elsif will_suppress and not was_suppressed
-%> (Further alerts suppressed until things calm down.)<%
- end
-end
-%>
+xmpp.txt.erb \ No newline at end of file
diff --git a/lib/mauve/notifiers/templates/sms.txt.erb b/lib/mauve/notifiers/templates/sms.txt.erb
index faec37d..802c711 100644..120000
--- a/lib/mauve/notifiers/templates/sms.txt.erb
+++ b/lib/mauve/notifiers/templates/sms.txt.erb
@@ -1,22 +1 @@
-<%= alert.update_type.upcase %>: <%
-case alert.update_type
-when "cleared"
-%><%= alert.cleared_at.to_s_relative %><%
-when "acknowledged"
-%><%= alert.acknowledged_at.to_s_relative %> by <%= alert.acknowledged_by %> until <%= alert.will_unacknowledge_at.to_s_human %><%
-else
-%><%= alert.raised_at.to_s_relative %><%
-end
-%>: <%= alert.subject %>: <%= alert.summary %><%
-if alert.source != alert.subject
-%> -- from <%= alert.source %><%
-end
-%>. <%=WebInterface.url_for(alert)%><%
-if defined? was_suppressed and defined? will_suppress
- if was_suppressed and not will_suppress
-%> (Normal service has resumed.)<%
- elsif will_suppress and not was_suppressed
-%> (Further alerts suppressed until things calm down.)<%
- end
-end
-%>
+xmpp.txt.erb \ No newline at end of file
diff --git a/lib/mauve/notifiers/templates/xmpp.html.erb b/lib/mauve/notifiers/templates/xmpp.html.erb
index 12354a2..7792bd9 100644
--- a/lib/mauve/notifiers/templates/xmpp.html.erb
+++ b/lib/mauve/notifiers/templates/xmpp.html.erb
@@ -1,5 +1,5 @@
<html xmlns="http://jabber.org/protocol/xhtml-im"><body xmlns="http://www.w3.org/1999/xhtml">
-<a href="<%=WebInterface.url_for(alert)%>"><%= alert.id%>: <%= alert.update_type.upcase %></a>: <%
+<a href="<%=WebInterface.url_for(alert)%>"><%= alert.id%>: <%= alert.update_type.upcase %></a> (<%= alert.level %>): <%
case alert.update_type
when "cleared"
%><%= alert.cleared_at.to_s_relative %><%
@@ -15,9 +15,9 @@ end
%>.<%
if defined? was_suppressed and defined? will_suppress
if was_suppressed and not will_suppress
-%><br /><em>Normal service has resumed.</em><%
+%><br /><em>Normal service for <%= alert.level %> alerts has resumed.</em><%
elsif will_suppress and not was_suppressed
-%><br /><em>Further alerts suppressed until things calm down.</em><%
+%><br /><em>Further <%= alert.level %> alerts suppressed until things calm down.</em><%
end
end
%></body></html>
diff --git a/lib/mauve/notifiers/templates/xmpp.txt.erb b/lib/mauve/notifiers/templates/xmpp.txt.erb
index f63c96c..c148f41 100644
--- a/lib/mauve/notifiers/templates/xmpp.txt.erb
+++ b/lib/mauve/notifiers/templates/xmpp.txt.erb
@@ -1,4 +1,4 @@
-<%=alert.id %>: <%= alert.update_type.upcase %>: <%
+<%=alert.id %>: <%= alert.update_type.upcase %> (<%= alert.level %>): <%
case alert.update_type
when "cleared"
%><%= alert.cleared_at.to_s_relative %><%
@@ -14,9 +14,9 @@ end
%>. <%=WebInterface.url_for(alert)%><%
if defined? was_suppressed and defined? will_suppress
if was_suppressed and not will_suppress
-%> (Normal service has resumed.)<%
+%> (Normal service for <%= alert.level %> alerts has resumed.)<%
elsif will_suppress and not was_suppressed
-%> (Further alerts suppressed until things calm down.)<%
+%> (Further <%= alert.level %> alerts suppressed until things calm down.)<%
end
end
%>
diff --git a/lib/mauve/person.rb b/lib/mauve/person.rb
index 979c0ab..1158cbf 100644
--- a/lib/mauve/person.rb
+++ b/lib/mauve/person.rb
@@ -6,13 +6,12 @@ module Mauve
class Person
attr_reader :username, :password, :urgent, :normal, :low, :email, :xmpp, :sms
- attr_reader :notification_thresholds, :last_pop3_login, :suppressed, :notifications
+ attr_reader :last_pop3_login, :suppressed, :notifications
attr_reader :notify_when_off_sick, :notify_when_on_holiday
# Set up a new Person
#
def initialize(username)
- @notification_thresholds = nil
@suppressed = false
#
# TODO fix up web login so pop3 can be used as a proxy.
@@ -107,34 +106,85 @@ module Mauve
# Works out if a notification should be suppressed. If no parameters are supplied, it will
#
+ # @param [Symbol] Level of notification that is being tested
# @param [Time] Theoretical time of notification
# @param [Time] Current time.
# @return [Boolean] If suppression is needed.
- def should_suppress?(with_notification_at = nil, now = Time.now)
+ def should_suppress?(level, with_notification_at = nil, now = Time.now)
- return self.notification_thresholds.any? do |period, previous_alert_times|
+ self.suppress_notifications_after.any? do |period, number|
#
- # This is going to work out if we would be suppressed if
- if with_notification_at.nil?
- first = previous_alert_times.first
- last = previous_alert_times.last
+ # When testing if the next alert will suppress, we know that if only
+ # one alert is needed to suppress, then this function should always
+ # return true.
+ #
+ return true if with_notification_at and number <= 1
+
+ #
+ # Here are the previous notifications set to this person in the last period.
+ #
+ previous_notifications = History.all(
+ :user => self.username, :type => "notification",
+ :created_at.gte => now - period, :created_at.lte => now,
+ :event.like => '% succeeded',
+ :order => :created_at.desc)
+
+ #
+ # Defintely not suppressed if no notifications have been found.
+ #
+ return false if previous_notifications.count == 0
+
+ #
+ # If we're suppressed already, we need to check the time of the last alert sent
+ #
+ if @suppressed
+
+ if with_notification_at.is_a?(Time)
+ latest = with_notification_at
+ else
+ latest = previous_notifications.first.created_at
+ end
+
+ #
+ # We should not suppress this alert if the last one was sent ages ago
+ #
+ if (now - latest) >= period
+ return false
+ end
+
else
- first = previous_alert_times[1]
- last = with_notification_at
+ #
+ # We do not suppress if we can't find a sufficient number of previous alerts
+ #
+ if previous_notifications.count < (with_notification_at.nil? ? number : number - 1)
+ return false
+ end
+
end
-
- (first.is_a?(Time) and (now - first) < period) or
- (last.is_a?(Time) and @suppressed and (now - last) < period)
+
+ #
+ # If we're at the lowest level, return true now.
+ #
+ return true if !AlertGroup::LEVELS.include?(level) or AlertGroup::LEVELS.index(level) == 0
+
+ #
+ # Suppress this notification if all the last N of the preceeding
+ # notifications were of a equal or higher level.
+ #
+ return previous_notifications.first(number).alerts.to_a.all? do |a|
+ AlertGroup::LEVELS.index(a.level) >= AlertGroup::LEVELS.index(level)
+ end
+
end
end
- # The notification thresholds for this user
#
- # @return [Hash]
- def notification_thresholds
- @notification_thresholds ||= { }
+ #
+ #
+ def suppress_notifications_after
+ @suppress_notifications_after ||= { }
end
-
+
# This class implements an instance_eval context to execute the blocks
# for running a notification block for each person.
#
@@ -203,10 +253,8 @@ module Mauve
#
# Log the result
- note = "#{@alert.update_type.capitalize} #{name} notification to #{@person.username} (#{destination}) " + (res ? "succeeded" : "failed" )
- logger.info note+" about #{@alert}."
- h = History.new(:alerts => [@alert], :type => "notification", :event => note, :user => @person.username)
- h.save
+ #
+ logger.info "#{@alert.update_type.capitalize} #{name} notification to #{@person.username} (#{destination}) " + (res ? "succeeded" : "failed" ) +" about #{@alert}."
return res
end
@@ -222,17 +270,17 @@ module Mauve
def send_alert(level, alert, now=Time.now)
was_suppressed = @suppressed
- @suppressed = self.should_suppress?
- will_suppress = self.should_suppress?(now)
+ @suppressed = self.should_suppress?(level)
+ will_suppress = self.should_suppress?(level, now)
logger.info "Starting to send notifications again for #{username}." if was_suppressed and not @suppressed
-
+
#
# We only suppress notifications if we were suppressed before we started,
# and are still suppressed.
#
if @suppressed or self.is_on_holiday?(now) or self.is_off_sick?(now)
- note = "#{alert.update_type.capitalize} notification to #{self.username} suppressed"
+ note = "#{alert.update_type.capitalize} #{level} notification to #{self.username} suppressed"
logger.info note + " about #{alert}."
History.create(:alerts => [alert], :type => "notification", :event => note, :user => self.username)
return true
@@ -253,24 +301,13 @@ module Mauve
:was_suppressed => was_suppressed, }
).instance_eval(&__send__(level))
end
+
+ res = [result].flatten.any?
+
+ note = "#{alert.update_type.capitalize} #{level} notification to #{self.username} " + (res ? "succeeded" : "failed" )
+ History.create(:alerts => [alert], :type => "notification", :event => note, :user => self.username)
- if [result].flatten.any?
- #
- # Remember that we've sent an alert
- #
- self.notification_thresholds.each do |period, previous_alert_times|
- #
- # Hmm.. not sure how to make this thread-safe.
- #
- self.notification_thresholds[period].push now
- self.notification_thresholds[period].shift
- end
-
-
- return true
- end
-
- return false
+ return res
end
#
diff --git a/lib/mauve/quick_update.rb b/lib/mauve/quick_update.rb
new file mode 100644
index 0000000..d5ecc8a
--- /dev/null
+++ b/lib/mauve/quick_update.rb
@@ -0,0 +1,166 @@
+# encoding: UTF-8
+require 'mauve/proto'
+require 'mauve/sender'
+
+module Mauve
+ #
+ # This class can be used in simple cases where all the program needs to do is
+ # send an update about a single alert.
+ #
+ # In its simplest form, this could be something like
+ #
+ # Mauve::QuickUpdate.new("foo").raise!
+ #
+ # sends a "raise" to the default mauve destination about alert ID "foo".
+ #
+ # It can be used to do set more details about the alert.
+ #
+ # update = Mauve::QuickUpdate.new("foo")
+ # update.summary = "Foo backups failed"
+ # update.detail = cmd_output
+ # update.raise!
+ #
+ # Another example might be a heartbeat.
+ #
+ # update = Mauve::QuickUpdate.new("heartbeat")
+ # update.summary = "Heartbeat for this.host.name not received"
+ # update.detail = "Maybe this host is down, or if not, cron has stopped running."
+ # update.raise_at = Time.now + 600
+ # update.clear_at = now
+ # update.suppress_until = Time.now + 900
+ # update.send
+ #
+ class QuickUpdate
+
+ def initialize(alert_id)
+ raise ArgumentError, "alert_id must be a String, or respond to to_s" unless alert_id.is_a?(String) or alert_id.respond_to?("to_s")
+
+ @verbose = false
+
+ @update = Mauve::Proto::AlertUpdate.new
+ @update.replace = false
+ @update.alert = []
+
+ @alert = Mauve::Proto::Alert.new
+ @alert.id = alert_id.to_s
+
+ @update << @alert
+ end
+
+ #
+ # Sets the replace flag for the whole update. Defaults to false.
+ #
+ def replace=(bool)
+ raise ArgumentError, "replace must either be true or false" unless bool.is_a?(TrueClass) or bool.is_a?(FalseClass)
+
+ @update.replace = bool
+ end
+
+ #
+ # Sets the verbose flag for the update process. Defaults to false.
+ #
+ def verbose=(bool)
+ raise ArgumentError, "verbose must either be true or false" unless bool.is_a?(TrueClass) or bool.is_a?(FalseClass)
+
+ @verbose = bool
+ end
+
+ #
+ # Sets the source of the alert. Defaults to the machine's hostname.
+ #
+ def source=(s)
+ raise ArgumentError, "source must be a String, or respond to to_s" unless s.is_a?(String) or s.respond_to?("to_s")
+
+ @update.source = s.to_s
+ end
+
+ #
+ # Sets the alert summary. Must be a string or something that can convert to a string.
+ #
+ def summary=(s)
+ raise ArgumentError, "summary must be a String, or respond to to_s" unless s.is_a?(String) or s.respond_to?("to_s")
+
+ @alert.summary = s.to_s
+ end
+
+ #
+ # Sets the alert detail. Must be a string or something that can convert to a string.
+ #
+ def detail=(s)
+ raise ArgumentError, "detail must be a String, or respond to to_s" unless s.is_a?(String) or s.respond_to?("to_s")
+
+ @alert.detail = s
+ end
+
+ #
+ # Sets the alert summary. Must be a string or something that can convert to a string.
+ #
+ def subject=(s)
+ raise ArgumentError, "subject must be a String, or respond to to_s" unless s.is_a?(String) or s.respond_to?("to_s")
+
+ @alert.subject = s
+ end
+
+ #
+ # Sets the raise time. Must be an Integer (epoch time) or a Time.
+ #
+ def raise_time=(t)
+ raise ArgumentError, "raise_time must be a Time or an Integer" unless t.is_a?(Time) or t.is_a?(Integer)
+ t = t.to_i if t.is_a?(Time)
+
+ @alert.raise_time = t
+ end
+
+ alias raise_at= raise_time=
+
+ #
+ # Sets the clear time. Must be an Integer (epoch time) or a Time.
+ #
+ def clear_time=(t)
+ clear ArgumentError, "clear_time must be a Time or an Integer" unless t.is_a?(Time) or t.is_a?(Integer)
+ t = t.to_i if t.is_a?(Time)
+
+ @alert.clear_time = t
+ end
+
+ alias clear_at= clear_time=
+
+ #
+ # Sets the time after which alerts will get sent. Must be an Integer (epoch time) or a Time.
+ #
+ def suppress_until=(t)
+ clear ArgumentError, "suppress_until must be a Time or an Integer" unless t.is_a?(Time) or t.is_a?(Integer)
+ t = t.to_i if t.is_a?(Time)
+
+ @alert.suppress_until = t
+ end
+
+ #
+ # Immediately send a raise message. The raise_time defaults to Time#now.
+ #
+ def raise!(t = Time.now)
+ self.raise_time = t
+ self.send
+ end
+
+ #
+ # Immediately send a clear message. The clear_time defaults to Time#now.
+ #
+ def clear!(t = Time.now)
+ self.clear_time = t
+ self.send
+ end
+
+ #
+ # This sends the alert. If destinations are left as nil, then the default
+ # as per Mauve::Sender are used.
+ #
+ def send(destinations = nil)
+ Mauve::Sender.new(destinations).send(@update, @verbose)
+ end
+
+ end
+
+end
+
+
diff --git a/lib/mauve/server.rb b/lib/mauve/server.rb
index bb514ce..8f8cec4 100644
--- a/lib/mauve/server.rb
+++ b/lib/mauve/server.rb
@@ -56,6 +56,11 @@ module Mauve
@bank_holidays = nil
#
+ # Turn off unwanted reverse DNS lookups across the board.
+ #
+ BasicSocket.do_not_reverse_lookup = true
+
+ #
# Set up a blank config.
#
Configuration.current = Configuration.new if Mauve::Configuration.current.nil?
diff --git a/lib/mauve/version.rb b/lib/mauve/version.rb
index 0014105..fa2d75a 100644
--- a/lib/mauve/version.rb
+++ b/lib/mauve/version.rb
@@ -5,6 +5,6 @@ module Mauve
#
# Current version
- VERSION="3.15.6"
+ VERSION="3.15.14"
end
diff --git a/test/tc_mauve_configuration_builders_person.rb b/test/tc_mauve_configuration_builders_person.rb
index 48ceafd..76841a3 100644
--- a/test/tc_mauve_configuration_builders_person.rb
+++ b/test/tc_mauve_configuration_builders_person.rb
@@ -65,9 +65,9 @@ EOF
assert_kind_of(Proc, person.normal)
assert_kind_of(Proc, person.urgent)
- assert_kind_of(Hash, person.notification_thresholds)
- assert_equal(1,person.notification_thresholds.keys.length)
- assert(person.notification_thresholds.all?{|k,v| k.is_a?(Integer) and v.is_a?(Array)})
+ assert_kind_of(Hash, person.suppress_notifications_after)
+ assert_equal(1,person.suppress_notifications_after.keys.length)
+ assert(person.suppress_notifications_after.all?{|k,v| k.is_a?(Integer) and v.is_a?(Integer)})
assert_kind_of(Array, person.notifications)
assert_equal(1, person.notifications.length)
diff --git a/test/tc_mauve_person.rb b/test/tc_mauve_person.rb
index 9e6f9ac..0d4b695 100644
--- a/test/tc_mauve_person.rb
+++ b/test/tc_mauve_person.rb
@@ -97,12 +97,10 @@ EOF
# Pop the notification off the buffer.
#
notification_buffer.pop
- assert_equal(Time.now, person.notification_thresholds[60][-1], "Notification thresholds not updated at #{Time.now}.")
else
assert_equal(0, notification_buffer.length, "Notification sent when it should not have been at #{Time.now}.")
end
-
logger_pop
end
@@ -176,7 +174,6 @@ EOF
# Pop the notification off the buffer.
#
notification_buffer.pop
- assert_equal(Time.now, person.notification_thresholds[60][-1], "Notification thresholds not updated at #{Time.now}.")
else
assert_equal(0, notification_buffer.length, "Notification sent when it should not have been at #{Time.now}.")
end
@@ -185,6 +182,127 @@ EOF
end
end
+
+ def test_send_alert_suppression_as_alerts_get_more_urgent
+ #
+ # This configuration is a bit different. We only want one alert per
+ # minute.
+ #
+ config =<<EOF
+notification_method("email") {
+ debug!
+ deliver_to_queue []
+ disable_normal_delivery!
+}
+
+person ("test") {
+ email "test@example.com"
+ all { email }
+ suppress_notifications_after( 2 => 1.minute )
+}
+
+alert_group("low") {
+ level LOW
+ includes { alert_id =~ /^low-/ }
+
+ notify("test") {
+ every 10.seconds
+ }
+}
+
+alert_group("normal") {
+ level NORMAL
+ includes { alert_id =~ /^normal-/ }
+
+ notify("test") {
+ every 10.seconds
+ }
+}
+
+alert_group("default") {
+ level URGENT
+
+ notify("test") {
+ every 10.seconds
+ }
+}
+EOF
+
+ Configuration.current = ConfigurationBuilder.parse(config)
+ notification_buffer = Configuration.current.notification_methods["email"].deliver_to_queue
+ Server.instance.setup
+
+ person = Configuration.current.people["test"]
+
+ alerts = [
+ Alert.new(
+ :alert_id => "low-test",
+ :source => "test",
+ :subject => "test"
+ ),
+ Alert.new(
+ :alert_id => "normal-test",
+ :source => "test",
+ :subject => "test"
+ ),
+ Alert.new(
+ :alert_id => "urgent-test",
+ :source => "test",
+ :subject => "test"
+ )
+ ]
+
+ #
+ # Raise the alerts
+ #
+ alerts.each{|a| a.raise!}
+ assert_equal(false, person.suppressed?, "Person suppressed before we even begin!")
+
+ assert_equal(:low, alerts[0].level)
+ assert_equal(:normal, alerts[1].level)
+ assert_equal(:urgent, alerts[2].level)
+
+ start_time = Time.now
+
+ #
+ #
+ #
+ [ [0, true, alerts.first ],
+ [1, true, alerts.first ],
+ [2, false, alerts.first ],
+ [3, false, alerts.first ],
+ [4, true, alerts[1]],
+ [5, false, alerts.first],
+ [6, true, alerts[1]],
+ [7, false, alerts[1]],
+ [8, false, alerts[1]],
+ [9, false, alerts[1]],
+ [10, true, alerts[2]],
+ [11, true, alerts[2]],
+ [12, false, alerts[2]],
+ [13, false, alerts.first]
+ ].each do |offset, notification_sent, alert|
+ #
+ # Advance in to the future!
+ #
+ Timecop.freeze(start_time + offset)
+
+ person.send_alert(alert.level, alert)
+
+ if notification_sent
+ assert_equal(1, notification_buffer.length, "#{alert.level.to_s.capitalize} notification not sent when it should have been at #{Time.now}.")
+ #
+ # Pop the notification off the buffer.
+ #
+ notification_buffer.pop
+ else
+ assert_equal(0, notification_buffer.length, "#{alert.level.to_s.capitalize} notification sent when it should not have been at #{Time.now}.")
+ end
+
+ logger_pop
+ end
+
+ end
def test_current_alerts
diff --git a/test/th_mauve.rb b/test/th_mauve.rb
index 4ec8dc5..49e714b 100644
--- a/test/th_mauve.rb
+++ b/test/th_mauve.rb
@@ -69,7 +69,14 @@ module Mauve
@logger = Log4r::Logger.new 'Mauve'
@outputter = Mauve::TestOutputter.new("test")
@outputter.formatter = Log4r::PatternFormatter.new( :pattern => "%d %l %m" )
- @outputter.level = ($debug ? Log4r::DEBUG : Log4r::WARN)
+ @outputter.level = case ENV['LOGLEVEL']
+ when "DEBUG"
+ Log4r::DEBUG
+ when "INFO"
+ Log4r::INFO
+ else
+ Log4r::WARN
+ end
@logger.outputters << @outputter
return @logger
end
diff --git a/views/_alert_actions.haml b/views/_alert_actions.haml
index 2f4eb57..db64d7f 100644
--- a/views/_alert_actions.haml
+++ b/views/_alert_actions.haml
@@ -19,10 +19,10 @@
for
%input#n_hours{ :name => 'n_hours', :type => "number", :min => 0, :max => 48, :value => 2, :style => "min-width: 6ex;"}
%select#type_hours{:name => 'type_of_hours' }
- -# Default to daytime hours
- %option{ :value => "working" } working
- %option{ :value => "daytime", :selected => "selected"} daytime
- %option{ :value => "wallclock" } wall-clock
+ -# Default to wall-clock hours.
+ %option{ :value => "working" } working
+ %option{ :value => "daytime"} daytime
+ %option{ :value => "wallclock", :selected => "selected"} wall-clock
hours
%span#ack_until_text
%input#ack_until{ :value => '', :type => :hidden, :name => 'ack_until' }