aboutsummaryrefslogtreecommitdiff
path: root/bin/mauvesend
diff options
context:
space:
mode:
Diffstat (limited to 'bin/mauvesend')
-rwxr-xr-xbin/mauvesend332
1 files changed, 332 insertions, 0 deletions
diff --git a/bin/mauvesend b/bin/mauvesend
new file mode 100755
index 0000000..7b1375f
--- /dev/null
+++ b/bin/mauvesend
@@ -0,0 +1,332 @@
+#! /usr/bin/ruby1.8
+# NAME
+# mauvesend - send alert(s) to a given alert station
+#
+# SYNOPSIS
+# mauvesend [<destination>]
+# [ --help | -h ] [ --manual | -m ] [ --version | -V ]
+# [--source | -o <source>] [--replace | -p] [--verbose | -v]
+# [--id <alertid> ... ]
+#
+# OPTIONS
+# <destination> Where the alert should go. This can be either a hostname or
+# an IP address, and optionally a port, separated by a colon.
+# The default port is 32741.
+#
+# If no destination is supplied, the value from the file
+# /etc/mauvealert/mauvesend.destination is used. If no
+# destination can be determined, an error is raised.
+#
+# If a hostname is given and no port is specified, SRV records
+# are used to determine where the alerts should go to. The SRV
+# prefix is _mauvealert._udp. If no SRV records are found, A
+# records are used instead.
+#
+# IPv6 addresses can be used, but must be enclosed in square
+# brackets, e.g. [2001:41c8::12].
+#
+# --source, -o <source> identify the source of the alert (defaults to
+# hostname, but you might want to name your monitoring
+# systems more explicitly).
+#
+# --replace, -p Send an update replacing all other alerts for this
+# source -- any previous alerts not specified in this
+# update are assumed to be cleared. If you specify this
+# option, you don't have to supply *any* alerts to raise
+# or clear (in which case all alerts from that source
+# will be cleared).
+#
+# --verbose, -v If you specify this option once, it will print the
+# transmission ID of the packet for debugging. If you
+# specify it twice, it will print the entire data
+# structure.
+#
+# --help, -h Display a short help message, and exit.
+#
+# --manual, -m Display this manual, and exit.
+#
+# --version, -V Display the version number for Mauve and exit.
+#
+# You can specify any number of alerts in an update - every time you specify
+# --id starts a new alert.
+#
+# --id, -i <alertid> Unique specified for each alert raised. This should
+# be unique on a per-source basis, i.e. for an
+# individual application or host.
+#
+# --summary, -s <summary> Text for humans describing the nature of the alert,
+# first 100 characters are only ones guaranteed to make
+# it to pagers, twitter, SMS etc.
+#
+# --detail, -d <detail> HTML fragment describing the alert in more detail,
+# no limit on length.
+#
+# --subject, -u <subject> Set the subject of the alert (i.e. the server/entity
+# that this alert concerns). If no subject is
+# specified, it is assumed to be the same as <source>,
+# detailed above.
+#
+# --raise, -r <time> Mark the alert to be (re)raised at the given time.
+# If no time is supplied, "now" is assumed. See
+# SPECIFYING TIMES below for the format of <time>.
+#
+# --clear, -c <time> Mark the alert to be cleared at the given time. If
+# no time is specified, "now" is assumed. See
+# SPECIFYING TIMES below for the format of <time>.
+#
+# SPECIFYING TIMES
+#
+# Times can be specified for an alert to be raised or cleared. This can be
+# specified as any time in the past or future. The format is + or -, followed
+# by a number, followed by a letter determining the units, one of s, m, h, d,
+# representing seconds, minutes, hours, and days, respectively. If no units are
+# specified, seconds is assumed. If no sign or unit is specified, an absolute
+# number of seconds since midnight UTC, 1st Jan 1970 is expected.
+#
+# Some example times are:
+#
+# now Immediately
+# +10m In 10 minutes time
+# -10h 10 Hours ago
+#
+# SENISBLE USAGE
+#
+# Mauve uses UDP to transmit data, which means that there is no guarrantee a
+# single packet will reach the server. Therefore:
+#
+# * The host/application should send "raise" notification regularly until the
+# alert clears, whereupon it should regularly send "cleared" notifications.
+#
+# * When setting a heartbeat-type alert, make sure that the raise time is more
+# than double the period of the "clear" notifications. For example, if the
+# host is sending a clear every 120 seconds, the raise time should be
+# greater than 240 seconds, preferably greater than 360 seconds to allow for
+# packets going missing, reducing the likelihood of false alerts.
+#
+# Try to convey salient details about the alerts in the relevant fields. A
+# typical short alert from Mauve might read
+#
+# RAISED: <subject>: <summary> -- <source>
+#
+# Make sure that the alert will be understood with just those three fields
+# displayed.
+#
+# * Keep the summary brief and salient.
+#
+# * Keep the summary constant, unless there has been a material change to the
+# nature of the alert. Mauve may re-send any messages when the subject
+# changes. If something is changing quickly, like load averages, best not
+# to put them in the summary.
+#
+# * Make sure that the subject is set correctly. Remember if no subject is
+# set, then the source of the alert is used instead.
+#
+# * Make sure that the source is correct too -- nothing worse than an alert
+# that comes in with an ambiguous origin.
+#
+# * The alert ID is used internally by Mauve to keep alerts consistent. This
+# must be unique on a per-source basis. It is OK to have many alerts with the
+# ID "heartbeat" as long as the source of the alert is different in each case.
+#
+# The raise and clear times can be specified, if needed, but generally leaving
+# them empty, i.e. setting them to "now" is sufficient. Mauve remembers when
+# an alert is first raised.
+#
+# EXAMPLES
+#
+# To raise an alert:
+#
+# mauvesend -s smtp-out-1.example.com -i mailqueue \\
+# -d "Mail queue has <b>54232</b> messages in it. That's <em>LOADS</em>" \\
+# -u "Mail queue too big on outgoing SMTP server" -r
+#
+# To clear an alert:
+#
+# mauvesend -s smtp-out-1.example.com -i mailqueue -c
+#
+# To create a "heartbeat" alert, i.e. one that says "Currently OK, but raise in the future if nothing more is heard":
+#
+# mauvesend -i heartbeat -d "No heartbeat received for 1.2.3.4. Could be down!" -s "heartbeat failed" -c -r +10m
+#
+# SEE ALSO
+#
+# mauveconsole(1), mauveserver(1)
+#
+# AUTHOR
+#
+# Patrick J Cherry <patrick@bytemark.co.uk>
+#
+
+require 'getoptlong'
+
+%w(sender mauve_time version proto).each do |r|
+ begin
+ require "mauve/#{r}"
+ rescue LoadError => ex
+ puts ex.to_s
+ end
+end
+
+NOW = Mauve::MauveTime.now
+
+def error(msg)
+ STDERR.print "*** Error: #{msg}\n"
+ STDERR.print "*** For help, type: #{$0} -h\n"
+ exit 1
+end
+
+def parse_time_spec(spec = "now")
+ #
+ # Default to now
+ #
+ spec = "now" if spec.empty?
+
+ case spec
+ when "now"
+ NOW
+
+ when /^\d+$/
+ spec.to_i
+
+ when /^(\+|-)?(\d+)([smhd])?$/
+ if $1 == "-"
+ multiplier = -1
+ else
+ multiplier = 1
+ end
+
+ multiplier *= case $3
+ when "m" then 60
+ when "h" then 3600
+ when "d" then 86400
+ else
+ 1
+ end
+
+ NOW + $2.to_i * multiplier
+
+ else
+ raise ArgumentError, "Unrecognised time format #{spec.inspect}"
+
+ end
+end
+
+begin
+ begin
+ update = Mauve::Proto::AlertUpdate.new
+ update.replace = false
+ update.alert = []
+ rescue NameError
+ #
+ # Do nothing .. When generating manpages in the build process we don't need
+ # to have Protobuf available.
+ #
+ update = nil
+ end
+ message = nil
+ verbose = 0
+ help = false
+ manual = false
+ version = false
+
+ opts = GetoptLong.new(
+ ['-h', '--help', GetoptLong::NO_ARGUMENT],
+ ['-m', '--manual', GetoptLong::NO_ARGUMENT],
+ ['-V', '--version', GetoptLong::NO_ARGUMENT],
+ ['-o', '--source', GetoptLong::OPTIONAL_ARGUMENT],
+ ['-p', '--replace', GetoptLong::NO_ARGUMENT],
+ ['-i', '--id', GetoptLong::OPTIONAL_ARGUMENT],
+ ['-s', '--summary', GetoptLong::OPTIONAL_ARGUMENT],
+ ['-u', '--subject', GetoptLong::OPTIONAL_ARGUMENT],
+ ['-c', '--clear', GetoptLong::OPTIONAL_ARGUMENT],
+ ['-r', '--raise', GetoptLong::OPTIONAL_ARGUMENT],
+ ['-d', '--detail', GetoptLong::OPTIONAL_ARGUMENT],
+ ['-v', '--verbose', GetoptLong::NO_ARGUMENT]
+ ).each do |opt,arg|
+
+ #
+ # Can catch empty arguments better if we set the GetoptLong things to
+ # "optional" rather than "required" and catch the empty arg here.
+ error "#{opt} cannot be empty" if arg.empty? and not %w(-h -m -V -p -v -c -r).include?(opt)
+
+ case opt
+ when '-h'
+ help = true
+ when '-m'
+ manual = true
+ when '-V'
+ version = true
+ when '-p'
+ error "Cannot send update -- not all libraries are available" if update.nil?
+ update.replace = true
+ when '-i'
+ error "Cannot send update -- not all libraries are available" if update.nil?
+ error "Cannot specify the same ID twice in one update -- ID #{arg}" if update.alert.any?{|a| a.id == arg}
+ message = Mauve::Proto::Alert.new
+ message.id = arg
+ update.alert << message
+ when '-o'
+ error "Cannot send update -- not all libraries are available" if update.nil?
+ error "Can only specify one source" if update.source
+ update.source = arg
+ when '-v'
+ verbose += 1
+ else
+ error "Cannot send update -- not all libraries are available" if update.nil?
+ error "Must specify --id before message" unless message
+ case opt
+ when '-s' then message.summary = arg
+ when '-u' then message.subject = arg
+ when '-d' then message.detail = arg
+ when '-c' then message.clear_time = parse_time_spec(arg).to_i
+ when '-r' then message.raise_time = parse_time_spec(arg).to_i
+ else
+ error "Unknown option #{opt}"
+ end
+ end
+ end
+
+ # CAUTION! Kwality kode.
+ #
+ if manual or help
+ # Open the file, stripping the shebang line
+ lines = File.open(__FILE__){|fh| fh.readlines}[1..-1]
+
+ found_synopsis = false
+
+ lines.each do |line|
+
+ line.chomp!
+ break if line.empty?
+
+ if help and !found_synopsis
+ found_synopsis = (line =~ /^#\s+SYNOPSIS\s*$/)
+ next
+ end
+
+ puts line[2..-1].to_s
+
+ break if help and found_synopsis and line =~ /^#\s*$/
+
+ end
+ end
+
+ puts "#{$0}: version "+Mauve::VERSION if version
+
+ exit 0 if help or version or manual
+
+ error "Cannot send update -- not all libraries are available" if update.nil?
+ error "No alerts specified" unless !update.alert.empty? || update.replace
+
+ update.transmission_id = rand(2**63)
+
+ Mauve::Sender.new(ARGV).send(update, verbose)
+
+rescue ArgumentError => ae
+ error ae.message
+
+rescue StandardError => ae
+ error ae.message
+
+end
+