nagios-herald 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +11 -0
- data/CONTRIBUTING.md +28 -0
- data/Gemfile +5 -0
- data/LICENSE +21 -0
- data/README.md +94 -0
- data/Rakefile +9 -0
- data/bin/draw_stack_bars +76 -0
- data/bin/dump_nagios_env.sh +25 -0
- data/bin/get_ganglia_graph +82 -0
- data/bin/get_graph +50 -0
- data/bin/get_graphite_graph +58 -0
- data/bin/nagios-herald +6 -0
- data/bin/splunk_alert_frequency +54 -0
- data/contrib/nrpe-plugins/check_cpu_stats.sh +186 -0
- data/contrib/nrpe-plugins/check_disk.sh +34 -0
- data/contrib/nrpe-plugins/check_mem.pl +181 -0
- data/contrib/nrpe-plugins/nrpe-plugin-examples.md +11 -0
- data/docs/config.md +62 -0
- data/docs/example_alerts.md +48 -0
- data/docs/formatters.md +180 -0
- data/docs/helpers.md +12 -0
- data/docs/images/cpu_no_context.png +0 -0
- data/docs/images/cpu_with_context.png +0 -0
- data/docs/images/disk_space_no_context.png +0 -0
- data/docs/images/disk_space_with_context.png +0 -0
- data/docs/images/memory_high_no_context.png +0 -0
- data/docs/images/memory_high_with_context.png +0 -0
- data/docs/images/nagios-herald-formatter-content-example.png +0 -0
- data/docs/images/nagios-herald.png +0 -0
- data/docs/images/stack-bars.png +0 -0
- data/docs/images/vanilla-nagios.png +0 -0
- data/docs/messages.md +16 -0
- data/docs/nagios-config.md +74 -0
- data/docs/tools.md +79 -0
- data/etc/config.yml.example +14 -0
- data/etc/readme.md +2 -0
- data/lib/nagios-herald/config.rb +25 -0
- data/lib/nagios-herald/executor.rb +265 -0
- data/lib/nagios-herald/formatter_loader.rb +82 -0
- data/lib/nagios-herald/formatters/base.rb +524 -0
- data/lib/nagios-herald/formatters/check_cpu.rb +71 -0
- data/lib/nagios-herald/formatters/check_disk.rb +143 -0
- data/lib/nagios-herald/formatters/check_logstash.rb +155 -0
- data/lib/nagios-herald/formatters/check_memory.rb +42 -0
- data/lib/nagios-herald/formatters/example.rb +19 -0
- data/lib/nagios-herald/formatters.rb +1 -0
- data/lib/nagios-herald/helpers/ganglia_graph.rb +99 -0
- data/lib/nagios-herald/helpers/graphite_graph.rb +85 -0
- data/lib/nagios-herald/helpers/logstash_query.rb +125 -0
- data/lib/nagios-herald/helpers/splunk_alert_frequency.rb +170 -0
- data/lib/nagios-herald/helpers/splunk_query.rb +119 -0
- data/lib/nagios-herald/helpers/url_image.rb +76 -0
- data/lib/nagios-herald/helpers.rb +5 -0
- data/lib/nagios-herald/logging.rb +48 -0
- data/lib/nagios-herald/message_loader.rb +40 -0
- data/lib/nagios-herald/messages/base.rb +56 -0
- data/lib/nagios-herald/messages/email.rb +150 -0
- data/lib/nagios-herald/messages/irc.rb +58 -0
- data/lib/nagios-herald/messages/pager.rb +75 -0
- data/lib/nagios-herald/messages.rb +3 -0
- data/lib/nagios-herald/test_helpers/base_test_case.rb +82 -0
- data/lib/nagios-herald/util.rb +45 -0
- data/lib/nagios-herald/version.rb +3 -0
- data/lib/nagios-herald.rb +7 -0
- data/lib/stackbars/__init__.py +0 -0
- data/lib/stackbars/chart_utils.py +25 -0
- data/lib/stackbars/grouped_stackbars.py +97 -0
- data/lib/stackbars/pilfonts/Tahoma.ttf +0 -0
- data/lib/stackbars/pilfonts/aerial.ttf +0 -0
- data/lib/stackbars/pilfonts/arial_black.ttf +0 -0
- data/lib/stackbars/stackbar.py +100 -0
- data/nagios-herald.gemspec +33 -0
- data/test/env_files/check_cpu_idle.CRITICAL +199 -0
- data/test/env_files/check_cpu_iowait.WARNING +199 -0
- data/test/env_files/check_disk.CRITICAL +197 -0
- data/test/env_files/check_disk.CRITICAL_ICINGA +197 -0
- data/test/env_files/check_disk.RECOVERY +197 -0
- data/test/env_files/check_memory.CRITICAL +197 -0
- data/test/env_files/nagios_vars.EXAMPLE +197 -0
- data/test/unit/test_config.rb +31 -0
- data/test/unit/test_executor.rb +65 -0
- data/test/unit/test_formatter_base.rb +131 -0
- data/test/unit/test_formatter_check_cpu_idle_critical.rb +135 -0
- data/test/unit/test_formatter_check_memory.rb +135 -0
- data/test/unit/test_icinga_variables.rb +31 -0
- data/test/unit/test_logging.rb +35 -0
- data/test/unit/test_message_email.rb +69 -0
- data/test/unit/test_message_pager.rb +69 -0
- metadata +204 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'nagios-herald/messages/base'
|
2
|
+
# probs need socket here
|
3
|
+
|
4
|
+
module NagiosHerald
|
5
|
+
class Message
|
6
|
+
class IRC < Message
|
7
|
+
|
8
|
+
attr_accessor :text
|
9
|
+
|
10
|
+
# Public: Initializes a new Message::IRC object.
|
11
|
+
#
|
12
|
+
# recipients - A list of recipients for this message.
|
13
|
+
# options - The options hash from Executor.
|
14
|
+
# FIXME: Is that ^^ necessary now with Config.config available?
|
15
|
+
#
|
16
|
+
# Returns a new Message::IRC object.
|
17
|
+
def initialize(recipients, options = {})
|
18
|
+
@text = ""
|
19
|
+
super(recipients, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Public: Generates the text portion of the content hash.
|
23
|
+
#
|
24
|
+
# Returns the full text portion of the content hash.
|
25
|
+
def curate_text
|
26
|
+
# FIXME: Gonna need to chomp newlines
|
27
|
+
@text += self.content[:text][:host_info]
|
28
|
+
@text += self.content[:text][:state_info]
|
29
|
+
@text += self.content[:text][:alert_ack_url]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Prints the text content to the terminal.
|
33
|
+
# Useful for debugging.
|
34
|
+
#
|
35
|
+
# Returns nothing.
|
36
|
+
def print
|
37
|
+
puts @text
|
38
|
+
end
|
39
|
+
|
40
|
+
# Public: Sends the IRC message.
|
41
|
+
#
|
42
|
+
# Returns nothing.
|
43
|
+
def send
|
44
|
+
curate_text
|
45
|
+
if @no_send
|
46
|
+
self.print
|
47
|
+
return
|
48
|
+
end
|
49
|
+
|
50
|
+
# TODO: Actually make this send to an IRC server
|
51
|
+
# I expect the IRC server will be a value in the config
|
52
|
+
self.print
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'nagios-herald/messages/base'
|
2
|
+
require 'mail'
|
3
|
+
|
4
|
+
module NagiosHerald
|
5
|
+
class Message
|
6
|
+
class Pager < Message
|
7
|
+
|
8
|
+
attr_accessor :subject
|
9
|
+
attr_accessor :text
|
10
|
+
|
11
|
+
# Public: Initializes a new Message::Pager object.
|
12
|
+
#
|
13
|
+
# recipients - A list of recipients for this message.
|
14
|
+
# options - The options hash from Executor.
|
15
|
+
# FIXME: Is that ^^ necessary now with Config.config available?
|
16
|
+
#
|
17
|
+
# Returns a new Message::Pager object.
|
18
|
+
def initialize(recipients, options = {})
|
19
|
+
@replyto = options[:replyto]
|
20
|
+
@subject = ""
|
21
|
+
@text = ""
|
22
|
+
super(recipients, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: Generates the text portion of the content hash.
|
26
|
+
#
|
27
|
+
# Returns the full text portion of the content hash.
|
28
|
+
def curate_text
|
29
|
+
notification_type = get_nagios_var('NAGIOS_NOTIFICATIONTYPE')
|
30
|
+
if notification_type.eql?('ACKNOWLEDGEMENT')
|
31
|
+
@text += self.content[:text][:ack_info]
|
32
|
+
else
|
33
|
+
@text += self.content[:text][:additional_info]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Prints the subject and text content to the terminal.
|
38
|
+
# Useful for debugging.
|
39
|
+
#
|
40
|
+
# Returns nothing.
|
41
|
+
def print
|
42
|
+
puts "------------------"
|
43
|
+
puts "Subject : #{@subject}"
|
44
|
+
puts "------------------"
|
45
|
+
puts @text
|
46
|
+
end
|
47
|
+
|
48
|
+
# Public: Sends the pager message.
|
49
|
+
#
|
50
|
+
# Returns nothing.
|
51
|
+
def build_message
|
52
|
+
curate_text
|
53
|
+
if @no_send
|
54
|
+
self.print
|
55
|
+
return
|
56
|
+
end
|
57
|
+
|
58
|
+
@subject = self.content[:subject]
|
59
|
+
mail = Mail.new({
|
60
|
+
:from => @replyto,
|
61
|
+
:to => @recipients,
|
62
|
+
:subject => @subject,
|
63
|
+
:body => @text
|
64
|
+
})
|
65
|
+
end
|
66
|
+
|
67
|
+
def send
|
68
|
+
self.build_message
|
69
|
+
mail.deliver!
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
NAGIOS_VARS = {
|
5
|
+
:HOSTNAME => "test.host.com",
|
6
|
+
:LONGDATETIME => "Thu May 11 11:11:11 UTC 2011",
|
7
|
+
:NOTIFICATIONNUMBER => "1",
|
8
|
+
:NOTIFICATIONRECIPIENTS => "testuser",
|
9
|
+
|
10
|
+
:SERVICEDESC => "TestService",
|
11
|
+
|
12
|
+
:SERVICEACKAUTHOR => "service on call",
|
13
|
+
:HOSTACKAUTHOR => "host on call",
|
14
|
+
|
15
|
+
:SERVICEATTEMPT => "4",
|
16
|
+
:HOSTATTEMPT => "3",
|
17
|
+
|
18
|
+
:SERVICEACKCOMMENT => "service comment",
|
19
|
+
:HOSTACKCOMMENT => "host comment",
|
20
|
+
|
21
|
+
:SERVICEDURATION => "0d 0h 5m 11s",
|
22
|
+
:HOSTDURATION => "0d 0h 10m 11s",
|
23
|
+
|
24
|
+
:SERVICENOTES => "note for service",
|
25
|
+
:HOSTNOTES => "note for host",
|
26
|
+
|
27
|
+
:SERVICEOUTPUT => "short service output",
|
28
|
+
:HOSTOUTPUT => "short host output",
|
29
|
+
|
30
|
+
:SERVICESTATE => "S_TESTING",
|
31
|
+
:HOSTSTATE => "H_TESTING",
|
32
|
+
|
33
|
+
:SERVICENOTESURL => "service_url",
|
34
|
+
:HOSTNOTESURL => "host_url",
|
35
|
+
|
36
|
+
:LASTSERVICESTATE => "LAST_S_TESTING",
|
37
|
+
:LASTHOSTSTATE => "LAST_H_TESTING",
|
38
|
+
|
39
|
+
:LONGSERVICEOUTPUT => "long service output",
|
40
|
+
:LONGHOSTOUTPUT => "long host output",
|
41
|
+
|
42
|
+
:MAXSERVICEATTEMPTS => "4",
|
43
|
+
:MAXHOSTATTEMPTS => "3",
|
44
|
+
|
45
|
+
# The below vars are not available to nagios-herald from the env,
|
46
|
+
# but should be generated in the email.
|
47
|
+
:SERVICE_ACK_URL => "http://test/nagios/?cmd_typ=34&host=test.host.com&service=TestService",
|
48
|
+
:HOST_ACK_URL => "http://test/nagios/?cmd_typ=33&host=test.host.com"
|
49
|
+
}
|
50
|
+
module NagiosHerald
|
51
|
+
module TestHelpers
|
52
|
+
class BaseTestCase < Test::Unit::TestCase
|
53
|
+
def simple_options
|
54
|
+
options = OpenStruct.new
|
55
|
+
options.pager_mode = false
|
56
|
+
options.noemail = false
|
57
|
+
options.debug = false
|
58
|
+
options.nagiosurl = 'http://test/nagios/'
|
59
|
+
options
|
60
|
+
end
|
61
|
+
|
62
|
+
def load_env(notification_type, expected_vars)
|
63
|
+
# Load the env with the nagios var we want
|
64
|
+
all_vars = NAGIOS_VARS
|
65
|
+
all_vars[:NOTIFICATIONTYPE] = notification_type
|
66
|
+
|
67
|
+
# Load all the new values
|
68
|
+
expected_vars.each do |var|
|
69
|
+
val = all_vars.fetch(var)
|
70
|
+
ENV["NAGIOS_#{var}"] = val
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def clear_env
|
75
|
+
# Load the env with empty strings
|
76
|
+
NAGIOS_VARS.each do |k,_|
|
77
|
+
ENV["NAGIOS_#{k}"] = ""
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'nagios-herald/logging'
|
2
|
+
|
3
|
+
module NagiosHerald
|
4
|
+
module Util
|
5
|
+
include NagiosHerald::Logging
|
6
|
+
|
7
|
+
# TODO: add methods for handling HTTP(s) requests so we can control timeouts
|
8
|
+
|
9
|
+
def unescape_text(text)
|
10
|
+
return text.gsub("\\n", "\n").gsub("\\t", "\t")
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.get_script_path(script_name)
|
14
|
+
current_dir = File.dirname(__FILE__)
|
15
|
+
rel_path = File.join(current_dir, '..', '..', 'bin', script_name)
|
16
|
+
return File.expand_path(rel_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.load_helper(name)
|
20
|
+
helper_path = File.expand_path(File.join(File.dirname(__FILE__), 'helpers', name))
|
21
|
+
logger.warn "Helper '#{name}' not found" unless File.exist?(helper_path + ".rb")
|
22
|
+
begin
|
23
|
+
require helper_path
|
24
|
+
return true
|
25
|
+
rescue LoadError
|
26
|
+
logger.fatal "Exception encountered loading '#{name}' helper library!"
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_nagios_var(name)
|
32
|
+
# If we're running Icinga, change the variable prefix.
|
33
|
+
if Config.config['icinga']
|
34
|
+
name.gsub!(/^NAGIOS_/, 'ICINGA_')
|
35
|
+
end
|
36
|
+
value = ENV[name]
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.underscore_to_camel_case(name)
|
40
|
+
words = name.downcase.split('_')
|
41
|
+
return words.map! {|w| w.capitalize}.join('')
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
File without changes
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import os
|
2
|
+
from PIL import ImageFont
|
3
|
+
|
4
|
+
def get_width_for_font_size(draw, font_type, font_size, label):
|
5
|
+
font = ImageFont.truetype(font_type, font_size)
|
6
|
+
return draw.textsize(label, font=font)
|
7
|
+
|
8
|
+
def get_optimal_font_size(draw, font_type, max_width, max_height, label, min_font_size, font_size = 100):
|
9
|
+
""" Find the maximum font_size given a label to display in a max_width, using a font_type """
|
10
|
+
if font_size < min_font_size:
|
11
|
+
return None
|
12
|
+
|
13
|
+
font = ImageFont.truetype(font_type, font_size)
|
14
|
+
label_width, label_height = draw.textsize(label, font=font)
|
15
|
+
if label_width < max_width and label_height < max_height:
|
16
|
+
return font_size
|
17
|
+
else:
|
18
|
+
return get_optimal_font_size(draw, font_type, max_width, max_height, label, min_font_size, font_size -1)
|
19
|
+
|
20
|
+
def get_font(font_name):
|
21
|
+
font_path = os.path.join(os.path.dirname(__file__), 'pilfonts', font_name)
|
22
|
+
if os.path.isfile(font_path):
|
23
|
+
return font_path
|
24
|
+
else:
|
25
|
+
raise Exception("Font %s not found" % font_name)
|
@@ -0,0 +1,97 @@
|
|
1
|
+
import os
|
2
|
+
from PIL import Image, ImageDraw
|
3
|
+
from stackbar import Stackbar
|
4
|
+
from chart_utils import get_optimal_font_size, get_font, get_width_for_font_size
|
5
|
+
|
6
|
+
class GroupedStackbars:
|
7
|
+
def __init__(self, options = {}):
|
8
|
+
|
9
|
+
self.options = options
|
10
|
+
|
11
|
+
self.width = options.get('width', 500)
|
12
|
+
assert self.width > 0, "width must be > 0"
|
13
|
+
|
14
|
+
self.bar_area_ratio = options.get('bar_area_ratio', .65)
|
15
|
+
assert self.bar_area_ratio <=1, "bar_area_ration must be <= 1"
|
16
|
+
|
17
|
+
self.bar_height_ratio = options.get('bar_height_ratio', .2)
|
18
|
+
assert self.bar_height_ratio <=1, "bar_height_ratio must be <= 1"
|
19
|
+
|
20
|
+
self.font_type = options.get('font_type', get_font('arial_black.ttf'))
|
21
|
+
self.min_font_size = 15
|
22
|
+
|
23
|
+
self.img = None
|
24
|
+
self.draw = None
|
25
|
+
|
26
|
+
self.stack_bars = []
|
27
|
+
|
28
|
+
def initializeDims(self, data, optimize_bar_area_width = True):
|
29
|
+
self.bars_width = int(self.bar_area_ratio * self.width)
|
30
|
+
self.bar_height = int(self.bar_height_ratio * self.bars_width)
|
31
|
+
self.height = len(data) * self.bar_height
|
32
|
+
|
33
|
+
self.img = Image.new('RGBA', (self.width, self.height), (255, 255, 255))
|
34
|
+
self.draw = ImageDraw.Draw(self.img)
|
35
|
+
|
36
|
+
longest_label = self.get_longest_label(data)
|
37
|
+
optimal_font_size = get_optimal_font_size(
|
38
|
+
self.draw,
|
39
|
+
self.font_type,
|
40
|
+
self.width -self.bars_width,
|
41
|
+
self.bar_height,
|
42
|
+
longest_label,
|
43
|
+
self.min_font_size
|
44
|
+
)
|
45
|
+
|
46
|
+
if not optimal_font_size and optimize_bar_area_width:
|
47
|
+
# we did not find an optimal font size - shrink the bar area to find an acceptable font size
|
48
|
+
label_width, label_height = get_width_for_font_size(self.draw, self.font_type, self.min_font_size, longest_label)
|
49
|
+
new_bar_width = self.width - label_width - 3 #3 is some right border
|
50
|
+
bar_width = max(0.3 * self.width, new_bar_width)
|
51
|
+
self.bar_area_ratio = float(bar_width) / float(self.width)
|
52
|
+
self.initializeDims(data, False)
|
53
|
+
|
54
|
+
self.font_size = optimal_font_size or self.min_font_size
|
55
|
+
|
56
|
+
def get_longest_label(self, data):
|
57
|
+
# One liner for python 2.6 and above
|
58
|
+
#longest_legend = reduce(lambda x, y: x if len(x) < len(y) else y, data)[0]
|
59
|
+
longest_label = ""
|
60
|
+
for i in data:
|
61
|
+
label = i[0]
|
62
|
+
if len(label) > len(longest_label):
|
63
|
+
longest_label = label
|
64
|
+
return longest_label
|
65
|
+
|
66
|
+
def initiliazeStackBars(self, data):
|
67
|
+
self.stack_bars = []
|
68
|
+
# get the dimensions for the gauges area and for the text area
|
69
|
+
for label, value in data:
|
70
|
+
sb = Stackbar(self.draw, label, value, self.options)
|
71
|
+
self.stack_bars.append(sb)
|
72
|
+
|
73
|
+
def renderStackBars(self):
|
74
|
+
offset = 0
|
75
|
+
for i, sb in enumerate(self.stack_bars):
|
76
|
+
draw_bottom_border = i == (len(self.stack_bars) - 1)
|
77
|
+
sb.render(self.width, offset, self.bars_width, self.bar_height, self.font_size, draw_bottom_border)
|
78
|
+
offset += self.bar_height
|
79
|
+
|
80
|
+
def render(self, data):
|
81
|
+
if not data:
|
82
|
+
return
|
83
|
+
|
84
|
+
# Initialize the drawing area
|
85
|
+
self.initializeDims(data)
|
86
|
+
|
87
|
+
# Create new stack bars
|
88
|
+
self.initiliazeStackBars(data)
|
89
|
+
|
90
|
+
# render each gauge according to the optimal dims
|
91
|
+
self.renderStackBars()
|
92
|
+
|
93
|
+
return self.img
|
94
|
+
|
95
|
+
def save(self, path):
|
96
|
+
self.img.save(path)
|
97
|
+
print "image saved as %s" % path
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import os
|
2
|
+
from PIL import ImageFont
|
3
|
+
from chart_utils import get_optimal_font_size, get_font
|
4
|
+
|
5
|
+
class Stackbar:
|
6
|
+
def __init__(self, draw, legend, fill_value, options = {}):
|
7
|
+
|
8
|
+
assert fill_value <= 100, "Invalid fill value - must be <= 100"
|
9
|
+
|
10
|
+
self.legend = legend
|
11
|
+
self.fill_value = fill_value
|
12
|
+
|
13
|
+
self.border = options.get('bar_border', 1)
|
14
|
+
self.full_color = options.get('green', (255, 0, 0))
|
15
|
+
self.empty_color = options.get('red', (0, 255, 0))
|
16
|
+
self.font_type = options.get('font_type', get_font('arial_black.ttf'))
|
17
|
+
|
18
|
+
self.light_font_color = options.get('fontcolor', (255, 255, 255))
|
19
|
+
self.dark_font_color = options.get('fontcolor', (0, 0, 0))
|
20
|
+
|
21
|
+
self.draw = draw
|
22
|
+
|
23
|
+
def drawFullRectangle(self, width, bar_height, height_offset, font_size, bottom_border_offset):
|
24
|
+
|
25
|
+
|
26
|
+
rect_width = self.fill_value * width / 100.
|
27
|
+
full_top_left = (self.border, self.border + height_offset)
|
28
|
+
full_bottom_right = (rect_width, height_offset + bar_height - self.border - bottom_border_offset)
|
29
|
+
self.draw.rectangle((full_top_left, full_bottom_right), fill=self.full_color)
|
30
|
+
|
31
|
+
# Draw text
|
32
|
+
rect_label = "%i%%" % self.fill_value
|
33
|
+
optimal_font_size = get_optimal_font_size(self.draw, self.font_type, rect_width, bar_height, rect_label, 7, font_size)
|
34
|
+
if not optimal_font_size:
|
35
|
+
return
|
36
|
+
|
37
|
+
font = ImageFont.truetype(self.font_type, optimal_font_size)
|
38
|
+
label_width, label_height = self.draw.textsize(rect_label, font=font)
|
39
|
+
label_margin = (rect_width - label_width) / 2
|
40
|
+
label_offset = (bar_height - label_height) / 2
|
41
|
+
self.draw.text(
|
42
|
+
(self.border + 1 + label_margin, height_offset + self.border + 1 + label_offset),
|
43
|
+
rect_label,
|
44
|
+
font=font,
|
45
|
+
fill=self.light_font_color
|
46
|
+
)
|
47
|
+
|
48
|
+
def drawEmptyRectangle(self, width, bar_height, height_offset, font_size, bottom_border_offset):
|
49
|
+
rect_start = self.fill_value * width / 100.
|
50
|
+
rect_width = width - rect_start
|
51
|
+
|
52
|
+
empty_top_left = (rect_start + self.border + 1, self.border + height_offset)
|
53
|
+
empty_bottom_right = (width - self.border, height_offset + bar_height - self.border -bottom_border_offset)
|
54
|
+
|
55
|
+
self.draw.rectangle((empty_top_left, empty_bottom_right), fill=self.empty_color)
|
56
|
+
|
57
|
+
rect_label = "%i%%" % (100 - self.fill_value)
|
58
|
+
optimal_font_size = get_optimal_font_size(self.draw, self.font_type, rect_width, bar_height, rect_label, 7, font_size)
|
59
|
+
if not optimal_font_size:
|
60
|
+
return
|
61
|
+
|
62
|
+
font = ImageFont.truetype(self.font_type, optimal_font_size)
|
63
|
+
label_width, label_height = self.draw.textsize(rect_label, font=font)
|
64
|
+
label_margin = rect_start + (rect_width - label_width) / 2
|
65
|
+
label_offset = (bar_height - label_height) / 2
|
66
|
+
self.draw.text(
|
67
|
+
(label_margin, height_offset + self.border + 1 + label_offset),
|
68
|
+
rect_label,
|
69
|
+
font=font,
|
70
|
+
fill=self.dark_font_color
|
71
|
+
)
|
72
|
+
|
73
|
+
def drawLegend(self, width_offset, bar_height, height_offset, font_size):
|
74
|
+
font = ImageFont.truetype(self.font_type, font_size)
|
75
|
+
label_width, label_height = self.draw.textsize(self.legend, font=font)
|
76
|
+
label_margin = width_offset + 2 #some left padding
|
77
|
+
label_offset = (bar_height - label_height) / 2
|
78
|
+
self.draw.text(
|
79
|
+
(label_margin, height_offset + self.border + 1 + label_offset),
|
80
|
+
self.legend,
|
81
|
+
font=font,
|
82
|
+
fill=self.dark_font_color
|
83
|
+
)
|
84
|
+
|
85
|
+
def drawBase(self, width, bar_height, height_offset):
|
86
|
+
self.draw.rectangle((0,height_offset, width, height_offset + bar_height), fill=(0,0,0))
|
87
|
+
|
88
|
+
def render(self, width, height_offset, bars_width, bar_height, font_size, draw_bottom_border = True):
|
89
|
+
if draw_bottom_border:
|
90
|
+
bottom_border_offset = 1
|
91
|
+
else:
|
92
|
+
bottom_border_offset = 0
|
93
|
+
|
94
|
+
self.drawBase(bars_width, bar_height, height_offset)
|
95
|
+
|
96
|
+
self.drawFullRectangle(bars_width, bar_height, height_offset, font_size - 1, bottom_border_offset)
|
97
|
+
|
98
|
+
self.drawEmptyRectangle(bars_width, bar_height, height_offset, font_size - 1, bottom_border_offset)
|
99
|
+
|
100
|
+
self.drawLegend(bars_width, bar_height, height_offset, font_size)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
($LOAD_PATH << File.expand_path("../lib", __FILE__)).uniq!
|
2
|
+
require "nagios-herald/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = 'nagios-herald'
|
6
|
+
spec.summary = "A project that aims to make it easy to provide context in Nagios alerts."
|
7
|
+
spec.version = NagiosHerald::VERSION
|
8
|
+
spec.authors = ['Ryan Frantz', 'Nassim Kammah']
|
9
|
+
spec.email = ['rfrantz@etsy.com', 'nkammah@etsy.com']
|
10
|
+
spec.homepage = "https://github.com/etsy/nagios-herald"
|
11
|
+
spec.license = "MIT"
|
12
|
+
|
13
|
+
spec.files = `git ls-files`.split("\n")
|
14
|
+
spec.test_files = Dir["tests/**/*_test.rb"]
|
15
|
+
#spec.executables = ["draw_stack_bars", "dump_nagios_env.sh", "get_ganglia_graph", "get_graph", "notify-by-handler", "splunk_alert_frequency"]
|
16
|
+
spec.executables = ["nagios-herald"]
|
17
|
+
spec.required_ruby_version = '>=1.9.2'
|
18
|
+
|
19
|
+
spec.add_runtime_dependency 'app_conf', '~> 0.4', '>= 0.4.2'
|
20
|
+
spec.add_runtime_dependency 'choice', '~> 0.1', '>= 0.1.6'
|
21
|
+
spec.add_runtime_dependency 'mail', '~> 2.5', '>= 2.5.4'
|
22
|
+
spec.add_runtime_dependency 'chef', '>= 11.8.2'
|
23
|
+
spec.add_runtime_dependency 'elasticsearch', '>= 1.0.2'
|
24
|
+
|
25
|
+
spec.description = <<-DESCRIPTION_END
|
26
|
+
A project that aims to make it easy to provide context in Nagios alerts.
|
27
|
+
The project consists of a core notifier script that can be called with a formatter
|
28
|
+
to tailor the content of the message sent to an operator.
|
29
|
+
DESCRIPTION_END
|
30
|
+
|
31
|
+
spec.post_install_message = "Have fun and write your own formatters!"
|
32
|
+
|
33
|
+
end
|