spoonsix-dognotgod 0.1.9 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +3 -3
- data/app/models/file_system.rb +30 -4
- data/app/models/host.rb +56 -17
- data/public/javascripts/jquery.simplemodal-1.2.3.min.js +8 -0
- data/server.rb +18 -10
- data/views/host.haml +43 -0
- data/views/layout.haml +4 -1
- data/views/main.haml +17 -53
- data/views/style.sass +229 -27
- metadata +7 -4
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
# dog not god:
|
1
|
+
# dog not god: server health monitoring simplified
|
2
2
|
|
3
|
-
dog not god is a
|
3
|
+
dog not god is a server health monitoring tool for *nix based servers. The focus is on simplicity and on a 100% ruby implementation.
|
4
4
|
|
5
5
|
### Installation
|
6
6
|
|
@@ -26,7 +26,7 @@ This will run the client every minute. By default, the server-address is 127.0.0
|
|
26
26
|
|
27
27
|
## License
|
28
28
|
|
29
|
-
dog not god:
|
29
|
+
dog not god: server health monitoring simplified
|
30
30
|
Copyright (C) 2009 spoonsix - Luis Correa d'Almeida
|
31
31
|
|
32
32
|
This program is free software; you can redistribute it and/or
|
data/app/models/file_system.rb
CHANGED
@@ -26,6 +26,10 @@ class FileSystem < Sequel::Model
|
|
26
26
|
available_in_Gb + used_in_Gb
|
27
27
|
end
|
28
28
|
|
29
|
+
def total_space_in_Gb
|
30
|
+
size_in_Gb
|
31
|
+
end
|
32
|
+
|
29
33
|
def used_in_Gb
|
30
34
|
if latest_disk_entry
|
31
35
|
latest_disk_entry.used / 1024 / 1024
|
@@ -34,6 +38,10 @@ class FileSystem < Sequel::Model
|
|
34
38
|
end
|
35
39
|
end
|
36
40
|
|
41
|
+
def used_space_in_Gb
|
42
|
+
used_in_Gb
|
43
|
+
end
|
44
|
+
|
37
45
|
def available_in_Gb
|
38
46
|
if latest_disk_entry
|
39
47
|
latest_disk_entry.available / 1024 / 1024
|
@@ -41,6 +49,24 @@ class FileSystem < Sequel::Model
|
|
41
49
|
0
|
42
50
|
end
|
43
51
|
end
|
52
|
+
|
53
|
+
def available_space_in_Gb
|
54
|
+
available_in_Gb
|
55
|
+
end
|
56
|
+
|
57
|
+
def available_space_in_percent
|
58
|
+
sprintf('%.2f', available_space_in_Gb.to_f / total_space_in_Gb * 100)
|
59
|
+
end
|
60
|
+
|
61
|
+
def status
|
62
|
+
if available_space_in_percent.to_i > 30
|
63
|
+
STATUS_GREEN
|
64
|
+
elsif available_space_in_percent.to_i > 10
|
65
|
+
STATUS_YELLOW
|
66
|
+
else
|
67
|
+
STATUS_RED
|
68
|
+
end
|
69
|
+
end
|
44
70
|
|
45
71
|
def disks_5_min_grain(start_time, end_time)
|
46
72
|
disk = DB.fetch("SELECT ROUND(AVG(available)/1024/1024, 1) AS available, ROUND(AVG(used)/1024/1024, 1) AS used, grain_5_min FROM disks WHERE file_system_id = #{self.id} AND grain_5_min >= '#{start_time.to_five_minute_grain_format.to_sql_format}' AND grain_5_min <= '#{end_time.to_five_minute_grain_format.to_sql_format}' GROUP BY grain_5_min;").all
|
@@ -56,8 +82,8 @@ class FileSystem < Sequel::Model
|
|
56
82
|
series[0] << five.to_minute_format
|
57
83
|
if disk.size > 0 and disk[0][:grain_5_min].to_minute_format == five.to_minute_format
|
58
84
|
disk_for_this_dim = disk.shift
|
59
|
-
series[1] << (disk_for_this_dim[:available] + disk_for_this_dim[:used])
|
60
|
-
series[2] << disk_for_this_dim[:used]
|
85
|
+
series[1] << (disk_for_this_dim[:available] + disk_for_this_dim[:used]).to_f
|
86
|
+
series[2] << disk_for_this_dim[:used].to_f
|
61
87
|
else
|
62
88
|
series[1] << -1
|
63
89
|
series[2] << -1
|
@@ -81,8 +107,8 @@ class FileSystem < Sequel::Model
|
|
81
107
|
series[0] << fifteen.to_minute_format
|
82
108
|
if disk.size > 0 and disk[0][:grain_15_min].to_minute_format == fifteen.to_minute_format
|
83
109
|
disk_for_this_dim = disk.shift
|
84
|
-
series[1] << (disk_for_this_dim[:available] + disk_for_this_dim[:used])
|
85
|
-
series[2] << disk_for_this_dim[:used]
|
110
|
+
series[1] << (disk_for_this_dim[:available] + disk_for_this_dim[:used]).to_f
|
111
|
+
series[2] << disk_for_this_dim[:used].to_f
|
86
112
|
else
|
87
113
|
series[1] << -1
|
88
114
|
series[2] << -1
|
data/app/models/host.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class Host < Sequel::Model
|
2
|
-
|
2
|
+
|
3
3
|
one_to_many :loads
|
4
4
|
|
5
5
|
one_to_many :file_systems
|
@@ -51,7 +51,7 @@ class Host < Sequel::Model
|
|
51
51
|
@total += fs.size_in_Gb
|
52
52
|
end
|
53
53
|
|
54
|
-
@total
|
54
|
+
@total_disk_space_in_Gb ||= @total
|
55
55
|
end
|
56
56
|
|
57
57
|
def available_disk_space_in_Gb
|
@@ -60,7 +60,21 @@ class Host < Sequel::Model
|
|
60
60
|
@available += fs.available_in_Gb
|
61
61
|
end
|
62
62
|
|
63
|
-
@available
|
63
|
+
@available_disk_space_in_Gb ||= @available
|
64
|
+
end
|
65
|
+
|
66
|
+
def available_disk_space_in_percent
|
67
|
+
sprintf('%.2f', available_disk_space_in_Gb.to_f / total_disk_space_in_Gb * 100)
|
68
|
+
end
|
69
|
+
|
70
|
+
def disk_status
|
71
|
+
if available_disk_space_in_percent.to_i > 30
|
72
|
+
STATUS_GREEN
|
73
|
+
elsif available_disk_space_in_percent.to_i > 10
|
74
|
+
STATUS_YELLOW
|
75
|
+
else
|
76
|
+
STATUS_RED
|
77
|
+
end
|
64
78
|
end
|
65
79
|
|
66
80
|
|
@@ -119,6 +133,19 @@ class Host < Sequel::Model
|
|
119
133
|
series
|
120
134
|
end
|
121
135
|
|
136
|
+
def available_memory_in_percent
|
137
|
+
sprintf('%.2f', available_memory_in_Mb.to_f / total_memory_in_Mb * 100)
|
138
|
+
end
|
139
|
+
|
140
|
+
def memory_status
|
141
|
+
if available_memory_in_percent.to_i > 30
|
142
|
+
STATUS_GREEN
|
143
|
+
elsif available_memory_in_percent.to_i > 10
|
144
|
+
STATUS_YELLOW
|
145
|
+
else
|
146
|
+
STATUS_RED
|
147
|
+
end
|
148
|
+
end
|
122
149
|
|
123
150
|
####################
|
124
151
|
# Swap
|
@@ -144,6 +171,20 @@ class Host < Sequel::Model
|
|
144
171
|
end
|
145
172
|
end
|
146
173
|
|
174
|
+
def available_swap_in_percent
|
175
|
+
sprintf('%.2f', available_swap_in_Mb.to_f / total_swap_in_Mb * 100)
|
176
|
+
end
|
177
|
+
|
178
|
+
def swap_status
|
179
|
+
if available_swap_in_percent.to_i > 30
|
180
|
+
STATUS_GREEN
|
181
|
+
elsif available_swap_in_percent.to_i > 10
|
182
|
+
STATUS_YELLOW
|
183
|
+
else
|
184
|
+
STATUS_RED
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
147
188
|
####################
|
148
189
|
# Load stats
|
149
190
|
####################
|
@@ -162,9 +203,9 @@ class Host < Sequel::Model
|
|
162
203
|
series[0] << five.to_minute_format
|
163
204
|
if load.size > 0 and load[0][:grain_5_min].to_minute_format == five.to_minute_format
|
164
205
|
load_for_this_dim = load.shift
|
165
|
-
series[1] << load_for_this_dim[:load_5_min]
|
166
|
-
series[2] << load_for_this_dim[:load_10_min]
|
167
|
-
series[3] << load_for_this_dim[:load_15_min]
|
206
|
+
series[1] << load_for_this_dim[:load_5_min].to_f
|
207
|
+
series[2] << load_for_this_dim[:load_10_min].to_f
|
208
|
+
series[3] << load_for_this_dim[:load_15_min].to_f
|
168
209
|
else
|
169
210
|
series[1] << -1
|
170
211
|
series[2] << -1
|
@@ -200,9 +241,9 @@ class Host < Sequel::Model
|
|
200
241
|
series[0] << fifteen.to_minute_format
|
201
242
|
if load.size > 0 and load[0][:grain_15_min].to_minute_format == fifteen.to_minute_format
|
202
243
|
load_for_this_dim = load.shift
|
203
|
-
series[1] << load_for_this_dim[:load_5_min]
|
204
|
-
series[2] << load_for_this_dim[:load_10_min]
|
205
|
-
series[3] << load_for_this_dim[:load_15_min]
|
244
|
+
series[1] << load_for_this_dim[:load_5_min].to_f
|
245
|
+
series[2] << load_for_this_dim[:load_10_min].to_f
|
246
|
+
series[3] << load_for_this_dim[:load_15_min].to_f
|
206
247
|
else
|
207
248
|
series[1] << -1
|
208
249
|
series[2] << -1
|
@@ -227,15 +268,13 @@ class Host < Sequel::Model
|
|
227
268
|
|
228
269
|
def status
|
229
270
|
if self.last_contacted_on == nil
|
230
|
-
|
231
|
-
elsif Time.now - self.last_contacted_on <
|
232
|
-
|
233
|
-
elsif Time.now - self.last_contacted_on <
|
234
|
-
|
235
|
-
elsif Time.now - self.last_contacted_on < 300
|
236
|
-
2
|
271
|
+
STATUS_RED
|
272
|
+
elsif Time.now - self.last_contacted_on < 75
|
273
|
+
STATUS_GREEN
|
274
|
+
elsif Time.now - self.last_contacted_on < 120
|
275
|
+
STATUS_YELLOW
|
237
276
|
else
|
238
|
-
|
277
|
+
STATUS_RED
|
239
278
|
end
|
240
279
|
end
|
241
280
|
|
@@ -0,0 +1,8 @@
|
|
1
|
+
/*
|
2
|
+
* SimpleModal 1.2.3 - jQuery Plugin
|
3
|
+
* http://www.ericmmartin.com/projects/simplemodal/
|
4
|
+
* Copyright (c) 2009 Eric Martin
|
5
|
+
* Dual licensed under the MIT and GPL licenses
|
6
|
+
* Revision: $Id: jquery.simplemodal.js 185 2009-02-09 21:51:12Z emartin24 $
|
7
|
+
*/
|
8
|
+
(function($){var ie6=$.browser.msie&&parseInt($.browser.version)==6&&typeof window['XMLHttpRequest']!="object",ieQuirks=null,w=[];$.modal=function(data,options){return $.modal.impl.init(data,options);};$.modal.close=function(){$.modal.impl.close();};$.fn.modal=function(options){return $.modal.impl.init(this,options);};$.modal.defaults={opacity:50,overlayId:'simplemodal-overlay',overlayCss:{},containerId:'simplemodal-container',containerCss:{},dataCss:{},zIndex:1000,close:true,closeHTML:'<a class="modalCloseImg" title="Close"></a>',closeClass:'simplemodal-close',position:null,persist:false,onOpen:null,onShow:null,onClose:null};$.modal.impl={opts:null,dialog:{},init:function(data,options){if(this.dialog.data){return false;}ieQuirks=$.browser.msie&&!$.boxModel;this.opts=$.extend({},$.modal.defaults,options);this.zIndex=this.opts.zIndex;this.occb=false;if(typeof data=='object'){data=data instanceof jQuery?data:$(data);if(data.parent().parent().size()>0){this.dialog.parentNode=data.parent();if(!this.opts.persist){this.dialog.orig=data.clone(true);}}}else if(typeof data=='string'||typeof data=='number'){data=$('<div/>').html(data);}else{alert('SimpleModal Error: Unsupported data type: '+typeof data);return false;}this.dialog.data=data.addClass('simplemodal-data').css(this.opts.dataCss);data=null;this.create();this.open();if($.isFunction(this.opts.onShow)){this.opts.onShow.apply(this,[this.dialog]);}return this;},create:function(){w=this.getDimensions();if(ie6){this.dialog.iframe=$('<iframe src="javascript:false;"/>').css($.extend(this.opts.iframeCss,{display:'none',opacity:0,position:'fixed',height:w[0],width:w[1],zIndex:this.opts.zIndex,top:0,left:0})).appendTo('body');}this.dialog.overlay=$('<div/>').attr('id',this.opts.overlayId).addClass('simplemodal-overlay').css($.extend(this.opts.overlayCss,{display:'none',opacity:this.opts.opacity/100,height:w[0],width:w[1],position:'fixed',left:0,top:0,zIndex:this.opts.zIndex+1})).appendTo('body');this.dialog.container=$('<div/>').attr('id',this.opts.containerId).addClass('simplemodal-container').css($.extend(this.opts.containerCss,{display:'none',position:'fixed',zIndex:this.opts.zIndex+2})).append(this.opts.close?$(this.opts.closeHTML).addClass(this.opts.closeClass):'').appendTo('body');this.setPosition();if(ie6||ieQuirks){this.fixIE();}this.dialog.container.append(this.dialog.data.hide());},bindEvents:function(){var self=this;$('.'+this.opts.closeClass).bind('click.simplemodal',function(e){e.preventDefault();self.close();});$(window).bind('resize.simplemodal',function(){w=self.getDimensions();self.setPosition();if(ie6||ieQuirks){self.fixIE();}else{self.dialog.iframe&&self.dialog.iframe.css({height:w[0],width:w[1]});self.dialog.overlay.css({height:w[0],width:w[1]});}});},unbindEvents:function(){$('.'+this.opts.closeClass).unbind('click.simplemodal');$(window).unbind('resize.simplemodal');},fixIE:function(){var p=this.opts.position;$.each([this.dialog.iframe||null,this.dialog.overlay,this.dialog.container],function(i,el){if(el){var bch='document.body.clientHeight',bcw='document.body.clientWidth',bsh='document.body.scrollHeight',bsl='document.body.scrollLeft',bst='document.body.scrollTop',bsw='document.body.scrollWidth',ch='document.documentElement.clientHeight',cw='document.documentElement.clientWidth',sl='document.documentElement.scrollLeft',st='document.documentElement.scrollTop',s=el[0].style;s.position='absolute';if(i<2){s.removeExpression('height');s.removeExpression('width');s.setExpression('height',''+bsh+' > '+bch+' ? '+bsh+' : '+bch+' + "px"');s.setExpression('width',''+bsw+' > '+bcw+' ? '+bsw+' : '+bcw+' + "px"');}else{var te,le;if(p&&p.constructor==Array){var top=p[0]?typeof p[0]=='number'?p[0].toString():p[0].replace(/px/,''):el.css('top').replace(/px/,'');te=top.indexOf('%')==-1?top+' + (t = '+st+' ? '+st+' : '+bst+') + "px"':parseInt(top.replace(/%/,''))+' * (('+ch+' || '+bch+') / 100) + (t = '+st+' ? '+st+' : '+bst+') + "px"';if(p[1]){var left=typeof p[1]=='number'?p[1].toString():p[1].replace(/px/,'');le=left.indexOf('%')==-1?left+' + (t = '+sl+' ? '+sl+' : '+bsl+') + "px"':parseInt(left.replace(/%/,''))+' * (('+cw+' || '+bcw+') / 100) + (t = '+sl+' ? '+sl+' : '+bsl+') + "px"';}}else{te='('+ch+' || '+bch+') / 2 - (this.offsetHeight / 2) + (t = '+st+' ? '+st+' : '+bst+') + "px"';le='('+cw+' || '+bcw+') / 2 - (this.offsetWidth / 2) + (t = '+sl+' ? '+sl+' : '+bsl+') + "px"';}s.removeExpression('top');s.removeExpression('left');s.setExpression('top',te);s.setExpression('left',le);}}});},getDimensions:function(){var el=$(window);var h=$.browser.opera&&$.browser.version>'9.5'&&$.fn.jquery<='1.2.6'?document.documentElement['clientHeight']:el.height();return[h,el.width()];},setPosition:function(){var top,left,hCenter=(w[0]/2)-((this.dialog.container.height()||this.dialog.data.height())/2),vCenter=(w[1]/2)-((this.dialog.container.width()||this.dialog.data.width())/2);if(this.opts.position&&this.opts.position.constructor==Array){top=this.opts.position[0]||hCenter;left=this.opts.position[1]||vCenter;}else{top=hCenter;left=vCenter;}this.dialog.container.css({left:left,top:top});},open:function(){this.dialog.iframe&&this.dialog.iframe.show();if($.isFunction(this.opts.onOpen)){this.opts.onOpen.apply(this,[this.dialog]);}else{this.dialog.overlay.show();this.dialog.container.show();this.dialog.data.show();}this.bindEvents();},close:function(){if(!this.dialog.data){return false;}if($.isFunction(this.opts.onClose)&&!this.occb){this.occb=true;this.opts.onClose.apply(this,[this.dialog]);}else{if(this.dialog.parentNode){if(this.opts.persist){this.dialog.data.hide().appendTo(this.dialog.parentNode);}else{this.dialog.data.remove();this.dialog.orig.appendTo(this.dialog.parentNode);}}else{this.dialog.data.remove();}this.dialog.container.remove();this.dialog.overlay.remove();this.dialog.iframe&&this.dialog.iframe.remove();this.dialog={};}this.unbindEvents();}};})(jQuery);
|
data/server.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'sinatra'
|
3
|
-
# require 'ruby-prof'
|
4
3
|
|
5
4
|
configure do
|
6
5
|
DB_DIR = "#{ENV['HOME']}/.dognotgod"
|
@@ -19,6 +18,11 @@ configure do
|
|
19
18
|
require 'load'
|
20
19
|
require 'disk'
|
21
20
|
require 'memory'
|
21
|
+
|
22
|
+
STATUS_GREEN = 1
|
23
|
+
STATUS_YELLOW = 2
|
24
|
+
STATUS_RED = 3
|
25
|
+
|
22
26
|
end
|
23
27
|
|
24
28
|
error do
|
@@ -43,11 +47,11 @@ helpers do
|
|
43
47
|
end
|
44
48
|
|
45
49
|
def get_fs_or_create_if_not_exist(host_id, fs_name, mounted_on)
|
46
|
-
fs = DB[:file_systems].filter(:name => fs_name, :mounted_on => mounted_on).first
|
50
|
+
fs = DB[:file_systems].filter(:name => fs_name, :host_id => host_id, :mounted_on => mounted_on).first
|
47
51
|
|
48
52
|
unless fs
|
49
53
|
DB[:file_systems] << {:name => fs_name, :mounted_on => mounted_on, :host_id => host_id}
|
50
|
-
fs = DB[:file_systems].filter(:name => fs_name, :mounted_on => mounted_on).first
|
54
|
+
fs = DB[:file_systems].filter(:name => fs_name, :host_id => host_id, :mounted_on => mounted_on).first
|
51
55
|
end
|
52
56
|
fs
|
53
57
|
end
|
@@ -60,21 +64,25 @@ get '/stylesheets/style.css' do
|
|
60
64
|
end
|
61
65
|
|
62
66
|
get "/" do
|
63
|
-
# result = RubyProf.profile do
|
64
|
-
|
65
67
|
@hosts = Host.order(:hostname).all
|
66
|
-
|
68
|
+
|
67
69
|
@end_now = Time.now.utc.to_fifteen_minute_grain_format
|
68
70
|
@start_24h_ago = @end_now - (60*60*24)
|
69
71
|
@start_6h_ago = @end_now - (60*60*6)
|
70
72
|
|
71
73
|
haml :main
|
72
|
-
# end
|
73
|
-
# Print a graph profile to text
|
74
|
-
# printer = RubyProf::GraphPrinter.new(result)
|
75
|
-
# printer.print(File.new("tmp/profile.log", "w"), 0)
|
76
74
|
end
|
77
75
|
|
76
|
+
get "/hosts/:id" do
|
77
|
+
@host = Host.find(:id => params[:id])
|
78
|
+
|
79
|
+
@end_now = Time.now.utc.to_fifteen_minute_grain_format
|
80
|
+
@start_24h_ago = @end_now - (60*60*24)
|
81
|
+
@start_6h_ago = @end_now - (60*60*6)
|
82
|
+
|
83
|
+
haml :host, :layout => false
|
84
|
+
end
|
85
|
+
|
78
86
|
post "/load_stats" do
|
79
87
|
|
80
88
|
begin
|
data/views/host.haml
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
- series_6h = @host.loads_15_min_grain(@start_6h_ago, @end_now)
|
2
|
+
- params_for_6h = "cht=ls&chs=300x100&chco=FF0000,00FF00,0000FF&chm=r,DDDDDD,0,0.25,0.26&chxt=x,y&chxr=1,0,4,1&chds=0,4&chl=&chd=t:#{series_6h[1].join(",")}|#{series_6h[2].join(",")}|#{series_6h[3].join(",")}"
|
3
|
+
- series_24h = @host.loads_15_min_grain(@start_24h_ago, @end_now)
|
4
|
+
- params_for_24h = "cht=lc&chs=300x100&chco=FF0000,00FF00,0000FF&chm=r,DDDDDD,0,0.25,0.26&chxt=x,y&chxr=1,0,4,1&chds=0,4&chl=&chd=t:#{series_24h[1].join(",")}|#{series_24h[2].join(",")}|#{series_24h[3].join(",")}"
|
5
|
+
- mem_series_24h = @host.memory_stats_15_min_grain(@start_24h_ago, @end_now)
|
6
|
+
- mem_params_for_24h = "cht=lc&chs=300x100&chco=FF0000,00FF00,0000FF&chm=r,DDDDDD,0,0.95,0.96&chxr=1,0,#{@host.total_memory_in_Mb * 1.05}&chxt=x,y&chl=&chds=0,#{@host.total_memory_in_Mb * 1.05}&chd=t:#{mem_series_24h[2].join(",")}|#{mem_series_24h[4].join(",")}"
|
7
|
+
- swap_params_for_24h = "cht=lc&chs=300x100&chco=FF0000,00FF00,0000FF&chm=r,DDDDDD,0,0.95,0.96&chxr=1,0,#{@host.total_swap_in_Mb * 1.05}&chxt=x,y&chl=&chds=0,#{@host.total_swap_in_Mb * 1.05}&chd=t:#{mem_series_24h[4].join(",")}"
|
8
|
+
.section
|
9
|
+
%h3=@host.hostname
|
10
|
+
%p== Last heard from on #{@host.last_contacted_on}
|
11
|
+
|
12
|
+
%table
|
13
|
+
%tr
|
14
|
+
%td{:style=> "vertical-align: top"}
|
15
|
+
%h4.load Load averages for the last 24 hours
|
16
|
+
%p{:style => "float:right;"}== 5, 10 and 15 min averages
|
17
|
+
%br
|
18
|
+
%img{ :src => "http://chart.apis.google.com/chart?#{params_for_24h}"}
|
19
|
+
%td
|
20
|
+
%h4.memory Memory & Swap
|
21
|
+
%p{:style => "float:right;"}== Total memory is #{@host.total_memory_in_Mb}Mb
|
22
|
+
%br
|
23
|
+
%img{ :src => "http://chart.apis.google.com/chart?#{mem_params_for_24h}"}
|
24
|
+
%tr
|
25
|
+
%td{:colspan => 2}
|
26
|
+
%h4.disk Disk space & File system info
|
27
|
+
%table.detail-table{:cellspacing => 0, :cellpading => 0}
|
28
|
+
%tr
|
29
|
+
%th
|
30
|
+
%th File system
|
31
|
+
%th Disk space
|
32
|
+
%th Percentage free disk space
|
33
|
+
- @host.file_systems.each do |fs|
|
34
|
+
%tr
|
35
|
+
%td=fs.mounted_on
|
36
|
+
%td=fs.name
|
37
|
+
- if fs.is_dev? and fs.size_in_Gb > 0
|
38
|
+
- series_24h = fs.disks_15_min_grain(@start_24h_ago, @end_now)
|
39
|
+
- params_for_24h = "cht=ls&chs=75x20&chco=999999&chds=0,#{series_24h[2].max.ceil}&chd=t:#{series_24h[2].join(",")}"
|
40
|
+
%td.space{:style => "padding-left: 100px; background: url(http://chart.apis.google.com/chart?#{params_for_24h}) no-repeat 5px 12px"}="#{fs.available_space_in_Gb} Gb free / #{fs.total_space_in_Gb} Gb total"
|
41
|
+
%td
|
42
|
+
%span.bar{:class => "status#{fs.status}", :style => "width: #{fs.available_space_in_percent.to_i}%"}="#{fs.available_space_in_percent}%" if fs.total_space_in_Gb > 0
|
43
|
+
|
data/views/layout.haml
CHANGED
@@ -3,12 +3,15 @@
|
|
3
3
|
%head
|
4
4
|
%title dog not god
|
5
5
|
%link{:rel=>'stylesheet', :href=>'/stylesheets/style.css', :type => "text/css"}
|
6
|
+
%script{:src => "http://www.google.com/jsapi"}
|
7
|
+
%script google.load("jquery", "1");
|
8
|
+
%script{:src => "/javascripts/jquery.simplemodal-1.2.3.min.js"}
|
6
9
|
|
7
10
|
|
8
11
|
%body
|
9
12
|
#header
|
10
13
|
#content
|
11
|
-
%h1
|
14
|
+
%h1="dog not god <br/><span style='font-size: 0.4em'>server health monitoring simplified</span>"
|
12
15
|
=yield
|
13
16
|
#footer
|
14
17
|
%p
|
data/views/main.haml
CHANGED
@@ -1,64 +1,28 @@
|
|
1
1
|
%h2 Summary
|
2
|
+
|
2
3
|
#summary-section
|
3
|
-
%table
|
4
|
+
%table.summary-table{:cellspacing => 0, :cellpading => 0}
|
4
5
|
%tr
|
5
6
|
%th Host
|
6
7
|
%th Last heartbeat
|
7
8
|
%th 5 min average
|
8
|
-
%th 10 min
|
9
|
-
%th 15 min
|
9
|
+
%th 10 min
|
10
|
+
%th 15 min
|
10
11
|
%th Free disk space
|
11
12
|
%th Free memory
|
12
13
|
%th Free swap space
|
13
|
-
-for host in @hosts
|
14
|
-
%tr{:class => "level#{host.status}"}
|
15
|
-
%td= host.hostname
|
16
|
-
%td= "#{host.distance_to_last_heartbeat_in_seconds.to_i} seconds ago" unless host.status == 0
|
17
|
-
%td= host.load_5_min
|
18
|
-
%td= host.load_10_min
|
19
|
-
%td= host.load_15_min
|
20
|
-
%td= "#{host.available_disk_space_in_Gb}Gb out of #{host.total_disk_space_in_Gb}Gb (#{sprintf('%.2f', (host.available_disk_space_in_Gb.to_f / host.total_disk_space_in_Gb) * 100)}%)" if host.total_disk_space_in_Gb > 0
|
21
|
-
%td= "#{host.available_memory_in_Mb}Mb out of #{host.total_memory_in_Mb}Mb (#{sprintf('%.2f', (host.available_memory_in_Mb.to_f / host.total_memory_in_Mb) * 100)}%)" if host.total_memory_in_Mb > 0
|
22
|
-
%td= "#{host.available_swap_in_Mb}Mb out of #{host.total_swap_in_Mb}Mb (#{sprintf('%.2f', (host.available_swap_in_Mb.to_f / host.total_swap_in_Mb) * 100)}%)" if host.total_swap_in_Mb > 0
|
23
|
-
|
24
|
-
%table{:id => "section-table"}
|
25
|
-
%tr
|
26
14
|
-for host in @hosts
|
27
15
|
- series_6h = host.loads_5_min_grain(@start_6h_ago, @end_now)
|
28
|
-
- params_for_6h = "cht=
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
%
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
%
|
41
|
-
|
42
|
-
%h4 Load averages for the last 6 hours
|
43
|
-
%img{ :src => "http://chart.apis.google.com/chart?#{params_for_6h}"}
|
44
|
-
|
45
|
-
%h4 Memory
|
46
|
-
%p{:style => "float:right;"}== Total memory is #{host.total_memory_in_Mb}Mb
|
47
|
-
%br
|
48
|
-
%img{ :src => "http://chart.apis.google.com/chart?#{mem_params_for_24h}"}
|
49
|
-
|
50
|
-
%h4 Swap
|
51
|
-
%p{:style => "float:right;"}== Total memory is #{host.total_swap_in_Mb}Mb
|
52
|
-
%br
|
53
|
-
%img{ :src => "http://chart.apis.google.com/chart?#{swap_params_for_24h}"}
|
54
|
-
|
55
|
-
- host.file_systems.each do |fs|
|
56
|
-
- if fs.is_dev? and fs.size_in_Gb > 0
|
57
|
-
- series_24h = fs.disks_15_min_grain(@start_24h_ago, @end_now)
|
58
|
-
- params_for_24h = "cht=lc&chs=350x120&chco=FF0000,00FF00,0000FF&chxr=1,0,#{fs.size_in_Gb * 1.05}&chxt=x,y&chds=0,#{fs.size_in_Gb * 1.05}&chl=&chd=t:#{series_24h[2].join(",")}"
|
59
|
-
%h4== #{fs.mounted_on} on #{fs.name}
|
60
|
-
%p{:style => "float:right;"}== Device size is #{fs.size_in_Gb}Gb
|
61
|
-
%br
|
62
|
-
%img{ :src => "http://chart.apis.google.com/chart?#{params_for_24h}"}
|
63
|
-
|
64
|
-
|
16
|
+
- params_for_6h = "cht=ls&chs=75x20&chco=999999&chds=0,#{series_6h[1].max}&chd=t:#{series_6h[1].join(",")}"
|
17
|
+
%tr.link{:onclick => "jQuery.get('/hosts/#{host.id}', {}, function(data) {jQuery.modal(data, {})})"}
|
18
|
+
%td{:class => "status#{host.status}"}= host.hostname
|
19
|
+
%td.distance-in-secs{:class => "status#{host.status}"}= "#{host.distance_to_last_heartbeat_in_seconds.to_i} seconds ago" unless host.status == 0
|
20
|
+
%td.load{:style => "background: url(http://chart.apis.google.com/chart?#{params_for_6h}) no-repeat 5px 12px"}=host.load_5_min
|
21
|
+
%td.load= host.load_10_min
|
22
|
+
%td.load= host.load_15_min
|
23
|
+
%td
|
24
|
+
%span.bar{:class => "status#{host.disk_status}", :style => "width: #{host.available_disk_space_in_percent.to_i}%"}="#{host.available_disk_space_in_percent}%" if host.total_disk_space_in_Gb > 0
|
25
|
+
%td
|
26
|
+
%span.bar{:class => "status#{host.memory_status}", :style => "width: #{host.available_memory_in_percent.to_i}%"}="#{host.available_memory_in_percent}%" if host.total_memory_in_Mb > 0
|
27
|
+
%td
|
28
|
+
%span.bar{:class => "status#{host.swap_status}", :style => "width: #{host.available_swap_in_percent.to_i}"}= "#{host.available_swap_in_percent}%" if host.total_swap_in_Mb > 0
|
data/views/style.sass
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
body
|
2
2
|
:margin 0
|
3
3
|
:padding 0
|
4
|
-
:font-family
|
4
|
+
:font-family Georgia, serif
|
5
5
|
:font-size 1.0em
|
6
6
|
|
7
7
|
br
|
@@ -10,56 +10,94 @@ br
|
|
10
10
|
a, a:visited
|
11
11
|
:color #AAA
|
12
12
|
|
13
|
+
//************************************************************
|
14
|
+
// headers
|
15
|
+
|
13
16
|
h1, h2, h3, h4, h5, h6
|
14
|
-
:font-family Georgia, serif
|
15
17
|
:font-weight normal
|
16
18
|
:margin 0px 10px 10px 0px
|
17
19
|
:white-space nowrap
|
20
|
+
:clear both
|
18
21
|
|
19
22
|
h1
|
20
23
|
:padding-bottom 10px
|
21
|
-
:margin
|
24
|
+
:margin 0px 0px 30px 0px
|
22
25
|
:border-bottom double #DDDDDD
|
23
26
|
|
24
27
|
h2
|
25
|
-
:
|
28
|
+
:background url(/images/blossom/48x48/world_globe.png) no-repeat 5px 10px
|
29
|
+
:padding 20px 10px 18px 60px
|
30
|
+
:margin 3px 0px 30px 0px
|
31
|
+
:border 1px solid #DDD
|
32
|
+
:border-bottom 1px solid #555
|
33
|
+
:background-color #EFEFEF
|
34
|
+
|
35
|
+
|
36
|
+
h3
|
37
|
+
:background url(/images/blossom/48x48/user.png) no-repeat 5px 7px
|
38
|
+
:padding 20px 10px 18px 60px
|
39
|
+
:margin 3px 0px 3px 0px
|
40
|
+
:border-top 1px solid #DDD
|
41
|
+
:border-bottom 1px solid #555
|
42
|
+
:background-color #EFEFEF
|
26
43
|
|
27
44
|
h4
|
28
|
-
:
|
29
|
-
:
|
45
|
+
:padding 10px 0px 10px 40px
|
46
|
+
:margin 2px 10px 3px 20px
|
47
|
+
:border-top 1px solid #DDD
|
48
|
+
:border-left 1px solid #DDD
|
49
|
+
:border-right 1px solid #DDD
|
50
|
+
:border-bottom 1px solid #555
|
51
|
+
|
52
|
+
h4.load
|
53
|
+
:background url(/images/blossom/32x32/process.png) no-repeat 5px 3px
|
54
|
+
:background-color #EFEFEF
|
55
|
+
|
56
|
+
h4.memory, h4.swap
|
57
|
+
:background url(/images/blossom/32x32/chart.png) no-repeat
|
30
58
|
:background-color #EFEFEF
|
59
|
+
|
60
|
+
h4.disk
|
61
|
+
:background url(/images/blossom/32x32/hard_disk.png) no-repeat
|
62
|
+
:background-color #EFEFEF
|
63
|
+
|
31
64
|
|
32
65
|
#header
|
33
66
|
:height 10px
|
34
67
|
:background-color #EFEFEF
|
68
|
+
:border-bottom 1px solid #DDD
|
35
69
|
:margin-bottom 20px
|
36
70
|
|
37
71
|
#content
|
38
72
|
:margin 0px 30px 10px 20px
|
73
|
+
|
74
|
+
#content-lightbox
|
75
|
+
:padding 10px 50px
|
76
|
+
:margin 10px auto
|
39
77
|
|
40
78
|
#summary-section
|
41
|
-
:font-size 0.
|
79
|
+
:font-size 0.8em
|
42
80
|
:margin-bottom 30px
|
43
81
|
:padding-bottom 20px
|
44
82
|
:border-bottom 1px solid #DDD
|
45
83
|
|
84
|
+
|
85
|
+
//************************************************************
|
46
86
|
// summary-table
|
47
87
|
|
48
88
|
.summary-table
|
49
89
|
:border-top 1px solid #DDD
|
50
90
|
:border-left 1px solid #DDD
|
51
|
-
|
52
|
-
.summary-table tr.level4, .summary-table tr.level5
|
53
|
-
:background-color #99FF99
|
91
|
+
:width 100%
|
54
92
|
|
55
|
-
.summary-table tr
|
56
|
-
:
|
93
|
+
.summary-table tr
|
94
|
+
:height 35px
|
57
95
|
|
58
|
-
.summary-table tr.
|
59
|
-
:
|
96
|
+
.summary-table tr.link
|
97
|
+
:cursor pointer
|
60
98
|
|
61
|
-
.summary-table tr.
|
62
|
-
:background-color #
|
99
|
+
.summary-table tr.link:hover
|
100
|
+
:background-color #FFFFEF
|
63
101
|
|
64
102
|
.summary-table th, #summary-table td
|
65
103
|
:border-bottom 1px solid #DDD
|
@@ -73,32 +111,196 @@ h4
|
|
73
111
|
.summary-table td
|
74
112
|
:padding-left 5px
|
75
113
|
:padding-right 5px
|
114
|
+
:border-bottom 1px solid #DDD
|
115
|
+
:border-right 1px solid #DDD
|
116
|
+
|
117
|
+
.summary-table td.status3
|
118
|
+
:background url(/images/coquette/24x24/red_button.png) no-repeat 8px 5px
|
119
|
+
:padding 0px 0px 0px 40px
|
120
|
+
:margin 0px 0px 0px 5px
|
121
|
+
|
122
|
+
.summary-table td.status2
|
123
|
+
:background url(/images/coquette/24x24/yellow_button.png) no-repeat 8px 5px
|
124
|
+
:padding 0px 0px 0px 40px
|
125
|
+
:margin 0px 0px 0px 5px
|
126
|
+
|
127
|
+
.summary-table td.status1
|
128
|
+
:background url(/images/coquette/24x24/green_button.png) no-repeat 8px 5px
|
129
|
+
:padding 0px 0px 0px 40px
|
130
|
+
:margin 0px 0px 0px 5px
|
131
|
+
|
132
|
+
.summary-table td.distance-in-secs, .summary-table td.load, .summary-table td.free-disk-space, .summary-table td.free-memory, .summary-table td.swap-space
|
133
|
+
:text-align right
|
134
|
+
:padding-right 5px
|
135
|
+
|
136
|
+
.summary-table td.distance-in-secs.status1
|
137
|
+
:background url(/images/coquette/16x16/green_button.png) no-repeat 10px 11px
|
138
|
+
|
139
|
+
.summary-table td.distance-in-secs.status2
|
140
|
+
:background url(/images/coquette/16x16/yellow_button.png) no-repeat 10px 11px
|
141
|
+
|
142
|
+
.summary-table td.distance-in-secs.status3
|
143
|
+
:background url(/images/coquette/16x16/red_button.png) no-repeat 10px 11px
|
144
|
+
|
145
|
+
|
146
|
+
.summary-table span.bar
|
147
|
+
:display block
|
148
|
+
:position relative
|
149
|
+
:text-align center
|
150
|
+
:color #000
|
151
|
+
:height 1.7em
|
152
|
+
:line-height 1.5em
|
153
|
+
:font-size 0.9em
|
154
|
+
|
155
|
+
.summary-table span.status1
|
156
|
+
:background-color #99DD00
|
157
|
+
|
158
|
+
.summary-table span.status2
|
159
|
+
:background-color #FFAA00
|
160
|
+
|
161
|
+
.summary-table span.status3
|
162
|
+
:background-color #CC0000
|
163
|
+
|
164
|
+
|
165
|
+
.summary-table img.sparkline
|
166
|
+
:padding-top 5px
|
167
|
+
|
168
|
+
|
169
|
+
|
170
|
+
//************************************************************
|
171
|
+
// detail-table
|
172
|
+
|
173
|
+
.detail-table
|
174
|
+
:border-top 1px solid #DDD
|
175
|
+
:border-left 1px solid #DDD
|
176
|
+
:font-size 0.7em
|
177
|
+
:margin 20px 20px 0px 20px
|
178
|
+
:width 100%
|
179
|
+
|
180
|
+
.detail-table tr
|
181
|
+
:height 25px
|
182
|
+
|
183
|
+
.detail-table th, #summary-table td
|
184
|
+
:border-bottom 1px solid #DDD
|
185
|
+
:border-right 1px solid #DDD
|
186
|
+
|
187
|
+
.detail-table th
|
188
|
+
:background-color #EFEFEF
|
189
|
+
:padding-left 15px
|
190
|
+
:padding-right 15px
|
191
|
+
|
192
|
+
.detail-table td
|
193
|
+
:padding-left 5px
|
194
|
+
:padding-right 5px
|
195
|
+
:border-bottom 1px solid #DDD
|
196
|
+
:border-right 1px solid #DDD
|
197
|
+
|
198
|
+
.detail-table td.status3
|
199
|
+
:background url(/images/coquette/24x24/red_button.png) no-repeat 8px 5px
|
200
|
+
:padding 0px 0px 0px 40px
|
201
|
+
:margin 0px 0px 0px 5px
|
202
|
+
|
203
|
+
.detail-table td.status2
|
204
|
+
:background url(/images/coquette/24x24/yellow_button.png) no-repeat 8px 5px
|
205
|
+
:padding 0px 0px 0px 40px
|
206
|
+
:margin 0px 0px 0px 5px
|
207
|
+
|
208
|
+
.detail-table td.status1
|
209
|
+
:background url(/images/coquette/24x24/green_button.png) no-repeat 8px 5px
|
210
|
+
:padding 0px 0px 0px 40px
|
211
|
+
:margin 0px 0px 0px 5px
|
212
|
+
|
213
|
+
.detail-table td.space
|
214
|
+
:text-align right
|
215
|
+
:padding-right 5px
|
216
|
+
|
217
|
+
.detail-table span.bar
|
218
|
+
:display block
|
219
|
+
:position relative
|
220
|
+
:text-align center
|
221
|
+
:color #000
|
222
|
+
:height 1.7em
|
223
|
+
:line-height 1.5em
|
224
|
+
:font-size 0.9em
|
225
|
+
|
226
|
+
.detail-table span.status1
|
227
|
+
:background-color #99DD00
|
228
|
+
|
229
|
+
.detail-table span.status2
|
230
|
+
:background-color #FFAA00
|
231
|
+
|
232
|
+
.detail-table span.status3
|
233
|
+
:background-color #CC0000
|
234
|
+
|
76
235
|
|
236
|
+
.detail-table img.sparkline
|
237
|
+
:padding-top 5px
|
238
|
+
|
239
|
+
|
240
|
+
//************************************************************
|
241
|
+
// section-table
|
77
242
|
|
78
243
|
#section-table
|
79
244
|
:border 0px
|
80
245
|
|
81
246
|
#section-table td
|
82
247
|
:vertical-align top
|
83
|
-
|
248
|
+
:padding-right 20px
|
249
|
+
|
84
250
|
.section
|
251
|
+
:padding 0px
|
252
|
+
:margin 0px
|
85
253
|
:margin-bottom 20px
|
86
|
-
:
|
87
|
-
:border-right 1px solid #DDD
|
254
|
+
:width 100%
|
88
255
|
|
89
|
-
p
|
90
|
-
:
|
91
|
-
:margin 0px
|
92
|
-
:
|
93
|
-
:
|
256
|
+
.section p
|
257
|
+
:padding 0px 15px 10px 0px
|
258
|
+
:margin 0px 0px 20px 0px
|
259
|
+
:color #333
|
260
|
+
:font-size 0.6em
|
261
|
+
:float right
|
262
|
+
|
263
|
+
.section img
|
264
|
+
:margin 10px 40px 0px 30px
|
265
|
+
|
266
|
+
|
267
|
+
//************************************************************
|
268
|
+
// footer
|
94
269
|
|
95
270
|
#footer
|
96
271
|
:margin-top 20px
|
97
272
|
:clear both
|
98
273
|
:padding 10px
|
99
|
-
:color #
|
100
|
-
:text-align left
|
101
|
-
:font-family sans-serif
|
274
|
+
:color #666
|
102
275
|
:text-align center
|
276
|
+
:background-color #EFEFEF
|
277
|
+
:border-top 1px solid #DDD
|
278
|
+
:font-size 0.8em
|
279
|
+
|
280
|
+
#footer a
|
281
|
+
:color #666
|
282
|
+
|
283
|
+
|
284
|
+
//************************************************************
|
285
|
+
// lightbox
|
286
|
+
|
287
|
+
#simplemodal-overlay
|
288
|
+
:background-color #000
|
289
|
+
:cursor wait
|
103
290
|
|
291
|
+
#simplemodal-container
|
292
|
+
:height 550px
|
293
|
+
:width 800px
|
294
|
+
:background-color #FFF
|
295
|
+
:border 5px solid #666
|
104
296
|
|
297
|
+
#simplemodal-container a.modalCloseImg
|
298
|
+
:background url(/images/blossom/32x32/delete_item.png) no-repeat
|
299
|
+
:width 32px
|
300
|
+
:height 32px
|
301
|
+
:display inline
|
302
|
+
:z-index 3200
|
303
|
+
:position absolute
|
304
|
+
:top -14px
|
305
|
+
:right -18px
|
306
|
+
:cursor pointer
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spoonsix-dognotgod
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luis Correa d'Almeida
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-03-
|
12
|
+
date: 2009-03-07 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -72,7 +72,7 @@ dependencies:
|
|
72
72
|
- !ruby/object:Gem::Version
|
73
73
|
version: "0.9"
|
74
74
|
version:
|
75
|
-
description: dog not god is a
|
75
|
+
description: dog not god is a server health monitoring tool.
|
76
76
|
email: luis.ca@gmail.com
|
77
77
|
executables:
|
78
78
|
- dognotgod
|
@@ -82,6 +82,7 @@ extensions: []
|
|
82
82
|
extra_rdoc_files: []
|
83
83
|
|
84
84
|
files:
|
85
|
+
- app/models
|
85
86
|
- app/models/disk.rb
|
86
87
|
- app/models/file_system.rb
|
87
88
|
- app/models/host.rb
|
@@ -91,6 +92,7 @@ files:
|
|
91
92
|
- lib/client_commands.rb
|
92
93
|
- config.ru
|
93
94
|
- README.md
|
95
|
+
- views/host.haml
|
94
96
|
- views/layout.haml
|
95
97
|
- views/main.haml
|
96
98
|
- views/style.sass
|
@@ -99,6 +101,7 @@ files:
|
|
99
101
|
- client.rb
|
100
102
|
- bin/dognotgod
|
101
103
|
- bin/dognotgod-client
|
104
|
+
- public/javascripts/jquery.simplemodal-1.2.3.min.js
|
102
105
|
has_rdoc: false
|
103
106
|
homepage: http://github.com/spoonsix/dognotgod
|
104
107
|
post_install_message:
|
@@ -124,6 +127,6 @@ rubyforge_project:
|
|
124
127
|
rubygems_version: 1.2.0
|
125
128
|
signing_key:
|
126
129
|
specification_version: 2
|
127
|
-
summary: dog not god is a
|
130
|
+
summary: dog not god is a server health monitoring tool.
|
128
131
|
test_files: []
|
129
132
|
|