spoonsix-dognotgod 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/{README → README.md} +12 -20
- data/app/models/disk.rb +28 -0
- data/app/models/file_system.rb +98 -0
- data/app/models/host.rb +130 -8
- data/app/models/load.rb +1 -1
- data/app/models/memory.rb +29 -0
- data/server.rb +57 -36
- data/views/layout.haml +4 -0
- data/views/main.haml +33 -4
- data/views/style.sass +11 -3
- metadata +6 -16
- data/bin/dognotgod-client +0 -6
- data/client.rb +0 -64
- data/views/loads_xml.haml +0 -23
data/{README → README.md}
RENAMED
@@ -1,41 +1,33 @@
|
|
1
1
|
# dog not god: performance monitoring simplified
|
2
2
|
|
3
|
-
dog not god is a performance monitoring tool for *nix based servers.
|
3
|
+
dog not god is a performance monitoring tool for *nix based servers. The focus is on simplicity and on a 100% ruby implementation.
|
4
4
|
|
5
|
-
|
5
|
+
### Installation
|
6
6
|
|
7
|
-
|
7
|
+
gem sources -a http://gems.github.com
|
8
|
+
sudo gem install spoonsix-dognotgod
|
9
|
+
sudo gem install spoonsix-dognotgod-client
|
8
10
|
|
9
11
|
### Server
|
12
|
+
The server captures performance data, and presents it via a web based interface.
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
### Installation
|
14
|
-
|
15
|
-
gem install dognotgod
|
16
|
-
dognotgod start
|
14
|
+
dognotgod start
|
17
15
|
|
18
|
-
This will kick off a thin server on port 4567. It will also create the folder ~/.dognotgod and store the
|
16
|
+
This will kick off a thin server on port 4567. It will also create the folder ~/.dognotgod and store the SQLite database at this location.
|
19
17
|
|
20
18
|
### Client
|
21
19
|
|
22
20
|
The client sits on the target machine - the machine to be monitored - captures performance data and sends it to the server. Note that this works on the machine acting as the server as well.
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
gem install dognotgod-client
|
27
|
-
|
28
|
-
Add an entry to crontab
|
29
|
-
|
30
|
-
crontab -e
|
31
|
-
*/1 * * * * ruby /path/to/gem/client.rb >> /dev/null 2>&1
|
22
|
+
crontab -e
|
23
|
+
*/1 * * * * dognotgod-client -a server-address -p port >> /dev/null 2>&1
|
32
24
|
|
33
|
-
This will run the client every minute.
|
25
|
+
This will run the client every minute. By default, the server-address is 127.0.0.1 and the port is 4567.
|
34
26
|
|
35
27
|
## License
|
36
28
|
|
37
29
|
dog not god: performance monitoring simplified
|
38
|
-
Copyright (C) 2009 Luis Correa d'Almeida
|
30
|
+
Copyright (C) 2009 spoonsix - Luis Correa d'Almeida
|
39
31
|
|
40
32
|
This program is free software; you can redistribute it and/or
|
41
33
|
modify it under the terms of the GNU General Public License
|
data/app/models/disk.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class Disk < Sequel::Model
|
2
|
+
|
3
|
+
many_to_one :filesystems
|
4
|
+
|
5
|
+
unless table_exists?
|
6
|
+
set_schema do
|
7
|
+
primary_key :id
|
8
|
+
integer :file_system_id
|
9
|
+
string :mounted_on
|
10
|
+
integer :used
|
11
|
+
integer :available
|
12
|
+
timestamp :created_at
|
13
|
+
timestamp :grain_5_min
|
14
|
+
timestamp :grain_15_min
|
15
|
+
timestamp :grain_30_min
|
16
|
+
timestamp :grain_60_min
|
17
|
+
end
|
18
|
+
create_table
|
19
|
+
end
|
20
|
+
|
21
|
+
before_create do
|
22
|
+
self.created_at = Time.now.utc unless self.created_at
|
23
|
+
self.grain_5_min = self.created_at.to_five_minute_grain_format
|
24
|
+
self.grain_15_min = self.created_at.to_fifteen_minute_grain_format
|
25
|
+
self.grain_30_min = self.created_at.to_thirty_minute_grain_format
|
26
|
+
self.grain_60_min = self.created_at.to_sixty_minute_grain_format
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
class FileSystem < Sequel::Model
|
2
|
+
|
3
|
+
many_to_one :hosts
|
4
|
+
one_to_many :disks
|
5
|
+
|
6
|
+
unless table_exists?
|
7
|
+
set_schema do
|
8
|
+
primary_key :id
|
9
|
+
integer :host_id
|
10
|
+
string :name
|
11
|
+
string :mounted_on
|
12
|
+
timestamp :created_at
|
13
|
+
end
|
14
|
+
create_table
|
15
|
+
end
|
16
|
+
|
17
|
+
def is_dev?
|
18
|
+
self.name.scan(/^\/dev/).size > 0
|
19
|
+
end
|
20
|
+
|
21
|
+
def size_in_Gb
|
22
|
+
last_entry = DB[:disks].filter(:file_system_id => self.id).reverse_order(:created_at).last
|
23
|
+
if last_entry
|
24
|
+
(last_entry[:available] + last_entry[:used]) / 1024 / 1024
|
25
|
+
else
|
26
|
+
0
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def used_in_Gb
|
31
|
+
last_entry = DB[:disks].filter(:file_system_id => self.id).reverse_order(:created_at).last
|
32
|
+
if last_entry
|
33
|
+
last_entry[:used] / 1024 / 1024
|
34
|
+
else
|
35
|
+
0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def available_in_Gb
|
40
|
+
last_entry = DB[:disks].filter(:file_system_id => self.id).reverse_order(:created_at).last
|
41
|
+
if last_entry
|
42
|
+
last_entry[:available] / 1024 / 1024
|
43
|
+
else
|
44
|
+
0
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def disks_5_min_grain(start_time, end_time)
|
49
|
+
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
|
50
|
+
|
51
|
+
time_range = (start_time..end_time)
|
52
|
+
|
53
|
+
series = []
|
54
|
+
series[0] = []
|
55
|
+
series[1] = []
|
56
|
+
series[2] = []
|
57
|
+
series[3] = []
|
58
|
+
time_range.step(300) do |five|
|
59
|
+
series[0] << five.to_minute_format
|
60
|
+
if disk.size > 0 and disk[0][:grain_5_min].to_minute_format == five.to_minute_format
|
61
|
+
disk_for_this_dim = disk.shift
|
62
|
+
series[1] << (disk_for_this_dim[:available] + disk_for_this_dim[:used])
|
63
|
+
series[2] << disk_for_this_dim[:used]
|
64
|
+
else
|
65
|
+
series[1] << -1
|
66
|
+
series[2] << -1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
series
|
71
|
+
end
|
72
|
+
|
73
|
+
def disks_15_min_grain(start_time, end_time)
|
74
|
+
disk = DB.fetch("SELECT ROUND(AVG(available)/1024/1024, 1) AS available, ROUND(AVG(used)/1024/1024, 1) AS used, grain_15_min FROM disks WHERE file_system_id = #{self.id} AND grain_15_min >= '#{start_time.to_fifteen_minute_grain_format.to_sql_format}' AND grain_15_min <= '#{end_time.to_fifteen_minute_grain_format.to_sql_format}' GROUP BY grain_15_min;").all
|
75
|
+
|
76
|
+
time_range = (start_time..end_time)
|
77
|
+
|
78
|
+
series = []
|
79
|
+
series[0] = []
|
80
|
+
series[1] = []
|
81
|
+
series[2] = []
|
82
|
+
series[3] = []
|
83
|
+
time_range.step(900) do |fifteen|
|
84
|
+
series[0] << fifteen.to_minute_format
|
85
|
+
if disk.size > 0 and disk[0][:grain_15_min].to_minute_format == fifteen.to_minute_format
|
86
|
+
disk_for_this_dim = disk.shift
|
87
|
+
series[1] << (disk_for_this_dim[:available] + disk_for_this_dim[:used])
|
88
|
+
series[2] << disk_for_this_dim[:used]
|
89
|
+
else
|
90
|
+
series[1] << -1
|
91
|
+
series[2] << -1
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
series
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
data/app/models/host.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
class Host < Sequel::Model
|
2
2
|
|
3
3
|
one_to_many :loads
|
4
|
+
one_to_many :file_systems
|
5
|
+
one_to_many :memories
|
4
6
|
|
5
7
|
unless table_exists?
|
6
8
|
set_schema do
|
@@ -11,18 +13,141 @@ class Host < Sequel::Model
|
|
11
13
|
create_table
|
12
14
|
end
|
13
15
|
|
16
|
+
####################
|
17
|
+
# Heartbeat
|
18
|
+
####################
|
19
|
+
|
20
|
+
# Timestamp of last entry in 'loads' table for this host
|
14
21
|
def last_contacted_on
|
15
22
|
self.loads.last.created_at if self.loads.last
|
16
23
|
end
|
17
24
|
|
25
|
+
# Seconds to timestamp of last entry in 'loads' table for this host
|
18
26
|
def distance_to_last_heartbeat_in_seconds
|
19
27
|
(Time.now - self.loads.last.created_at) if self.loads.last
|
20
28
|
end
|
21
29
|
|
22
|
-
def loads2(start_time, end_time)
|
23
|
-
load = DB.fetch("SELECT AVG(load_5_min) AS load_5_min, AVG(load_10_min) AS load_10_min, AVG(load_15_min) AS load_15_min, grain_5_min FROM loads WHERE host_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
|
24
|
-
end
|
25
30
|
|
31
|
+
####################
|
32
|
+
# Disk space
|
33
|
+
####################
|
34
|
+
def total_disk_space_in_Gb
|
35
|
+
@total = 0
|
36
|
+
self.file_systems.each do |fs|
|
37
|
+
@total += fs.size_in_Gb
|
38
|
+
end
|
39
|
+
|
40
|
+
@total
|
41
|
+
end
|
42
|
+
|
43
|
+
def available_disk_space_in_Gb
|
44
|
+
@available = 0
|
45
|
+
self.file_systems.each do |fs|
|
46
|
+
@available += fs.available_in_Gb
|
47
|
+
end
|
48
|
+
|
49
|
+
@available
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
####################
|
54
|
+
# Memory
|
55
|
+
####################
|
56
|
+
|
57
|
+
def total_memory_in_Mb
|
58
|
+
last_entry = DB[:memories].filter(:host_id => self.id).reverse_order(:created_at).last
|
59
|
+
if last_entry
|
60
|
+
(last_entry[:mem_available] + last_entry[:mem_used]) / 1024
|
61
|
+
else
|
62
|
+
0
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def available_memory_in_Mb
|
67
|
+
last_entry = DB[:memories].filter(:host_id => self.id).reverse_order(:created_at).last
|
68
|
+
if last_entry
|
69
|
+
last_entry[:mem_available] / 1024
|
70
|
+
else
|
71
|
+
0
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def used_memory_in_Mb
|
76
|
+
last_entry = DB[:memories].filter(:host_id => self.id).reverse_order(:created_at).last
|
77
|
+
if last_entry
|
78
|
+
last_entry[:mem_used] / 1024
|
79
|
+
else
|
80
|
+
0
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def memory_stats_15_min_grain(start_time, end_time)
|
85
|
+
data = DB.fetch("SELECT ROUND(AVG(mem_available)/1024,0) AS mem_available, ROUND(AVG(mem_used)/1024,0) AS mem_used, ROUND(AVG(swap_available)/1024,0) AS swap_available, ROUND(AVG(swap_used)/1024, 0) AS swap_used, grain_15_min FROM memories WHERE host_id = #{self.id} AND grain_15_min >= '#{start_time.to_fifteen_minute_grain_format.to_sql_format}' AND grain_15_min <= '#{end_time.to_fifteen_minute_grain_format.to_sql_format}' GROUP BY grain_15_min;").all
|
86
|
+
|
87
|
+
time_range = (start_time..end_time)
|
88
|
+
|
89
|
+
series = []
|
90
|
+
series[0] = []
|
91
|
+
series[1] = []
|
92
|
+
series[2] = []
|
93
|
+
series[3] = []
|
94
|
+
series[4] = []
|
95
|
+
|
96
|
+
time_range.step(900) do |fifteen|
|
97
|
+
series[0] << fifteen.to_minute_format
|
98
|
+
if data.size > 0 and data[0][:grain_15_min].to_minute_format == fifteen.to_minute_format
|
99
|
+
fact_for_this_dim = data.shift
|
100
|
+
series[1] << fact_for_this_dim[:mem_available]
|
101
|
+
series[2] << fact_for_this_dim[:mem_used]
|
102
|
+
series[3] << fact_for_this_dim[:swap_available]
|
103
|
+
series[4] << fact_for_this_dim[:swap_used]
|
104
|
+
else
|
105
|
+
series[1] << -1
|
106
|
+
series[2] << -1
|
107
|
+
series[3] << -1
|
108
|
+
series[4] << -1
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
series
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
####################
|
117
|
+
# Swap
|
118
|
+
####################
|
119
|
+
|
120
|
+
def total_swap_in_Mb
|
121
|
+
last_entry = DB[:memories].filter(:host_id => self.id).reverse_order(:created_at).last
|
122
|
+
if last_entry
|
123
|
+
(last_entry[:swap_available] + last_entry[:swap_used]) / 1024
|
124
|
+
else
|
125
|
+
0
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def available_swap_in_Mb
|
130
|
+
last_entry = DB[:memories].filter(:host_id => self.id).reverse_order(:created_at).last
|
131
|
+
if last_entry
|
132
|
+
last_entry[:swap_available] / 1024
|
133
|
+
else
|
134
|
+
0
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def used_swap_in_Mb
|
139
|
+
last_entry = DB[:memories].filter(:host_id => self.id).reverse_order(:created_at).last
|
140
|
+
if last_entry
|
141
|
+
last_entry[:swap_used] / 1024
|
142
|
+
else
|
143
|
+
0
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
####################
|
148
|
+
# Load stats
|
149
|
+
####################
|
150
|
+
|
26
151
|
def loads_5_min_grain(start_time, end_time)
|
27
152
|
load = DB.fetch("SELECT ROUND(AVG(load_5_min),1) AS load_5_min, ROUND(AVG(load_10_min),1) AS load_10_min, ROUND(AVG(load_15_min),1) AS load_15_min, grain_5_min FROM loads WHERE host_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
|
28
153
|
|
@@ -70,6 +195,7 @@ class Host < Sequel::Model
|
|
70
195
|
series[1] = []
|
71
196
|
series[2] = []
|
72
197
|
series[3] = []
|
198
|
+
i = 0
|
73
199
|
time_range.step(900) do |fifteen|
|
74
200
|
series[0] << fifteen.to_minute_format
|
75
201
|
if load.size > 0 and load[0][:grain_15_min].to_minute_format == fifteen.to_minute_format
|
@@ -86,7 +212,7 @@ class Host < Sequel::Model
|
|
86
212
|
|
87
213
|
series
|
88
214
|
end
|
89
|
-
|
215
|
+
|
90
216
|
def load_5_min
|
91
217
|
self.loads.last.load_5_min if self.loads.last
|
92
218
|
end
|
@@ -113,8 +239,4 @@ class Host < Sequel::Model
|
|
113
239
|
end
|
114
240
|
end
|
115
241
|
|
116
|
-
def to_google_params
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
242
|
end
|
data/app/models/load.rb
CHANGED
@@ -19,7 +19,7 @@ class Load < Sequel::Model
|
|
19
19
|
end
|
20
20
|
|
21
21
|
before_create do
|
22
|
-
self.created_at = Time.now unless self.created_at
|
22
|
+
self.created_at = Time.now.utc unless self.created_at
|
23
23
|
self.grain_5_min = self.created_at.to_five_minute_grain_format
|
24
24
|
self.grain_15_min = self.created_at.to_fifteen_minute_grain_format
|
25
25
|
self.grain_30_min = self.created_at.to_thirty_minute_grain_format
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Memory < Sequel::Model
|
2
|
+
|
3
|
+
many_to_one :hosts
|
4
|
+
|
5
|
+
unless table_exists?
|
6
|
+
set_schema do
|
7
|
+
primary_key :id
|
8
|
+
integer :host_id
|
9
|
+
integer :mem_available
|
10
|
+
integer :mem_used
|
11
|
+
integer :swap_available
|
12
|
+
integer :swap_used
|
13
|
+
timestamp :created_at
|
14
|
+
timestamp :grain_5_min
|
15
|
+
timestamp :grain_15_min
|
16
|
+
timestamp :grain_30_min
|
17
|
+
timestamp :grain_60_min
|
18
|
+
end
|
19
|
+
create_table
|
20
|
+
end
|
21
|
+
|
22
|
+
before_create do
|
23
|
+
self.created_at = Time.now.utc unless self.created_at
|
24
|
+
self.grain_5_min = self.created_at.to_five_minute_grain_format
|
25
|
+
self.grain_15_min = self.created_at.to_fifteen_minute_grain_format
|
26
|
+
self.grain_30_min = self.created_at.to_thirty_minute_grain_format
|
27
|
+
self.grain_60_min = self.created_at.to_sixty_minute_grain_format
|
28
|
+
end
|
29
|
+
end
|
data/server.rb
CHANGED
@@ -14,7 +14,10 @@ configure do
|
|
14
14
|
|
15
15
|
# require 'ostruct'
|
16
16
|
require 'host'
|
17
|
+
require 'file_system'
|
17
18
|
require 'load'
|
19
|
+
require 'disk'
|
20
|
+
require 'memory'
|
18
21
|
end
|
19
22
|
|
20
23
|
error do
|
@@ -27,64 +30,82 @@ error do
|
|
27
30
|
end
|
28
31
|
|
29
32
|
helpers do
|
30
|
-
|
31
|
-
|
33
|
+
|
34
|
+
def get_host_or_create_if_not_exist(hostname)
|
35
|
+
host = DB[:hosts].filter(:hostname => hostname).first
|
36
|
+
|
37
|
+
unless host
|
38
|
+
DB[:hosts] << {:hostname => hostname}
|
39
|
+
host = DB[:hosts].filter(:hostname => hostname).first
|
40
|
+
end
|
41
|
+
host
|
32
42
|
end
|
33
|
-
|
34
|
-
def
|
35
|
-
|
43
|
+
|
44
|
+
def get_fs_or_create_if_not_exist(host_id, fs_name, mounted_on)
|
45
|
+
fs = DB[:file_systems].filter(:name => fs_name, :mounted_on => mounted_on).first
|
46
|
+
|
47
|
+
unless fs
|
48
|
+
DB[:file_systems] << {:name => fs_name, :mounted_on => mounted_on, :host_id => host_id}
|
49
|
+
fs = DB[:file_systems].filter(:name => fs_name, :mounted_on => mounted_on).first
|
50
|
+
end
|
51
|
+
fs
|
36
52
|
end
|
53
|
+
|
37
54
|
end
|
38
|
-
|
55
|
+
|
56
|
+
get '/stylesheets/style.css' do
|
57
|
+
header 'Content-Type' => 'text/css; charset=utf-8'
|
58
|
+
sass :style
|
59
|
+
end
|
60
|
+
|
39
61
|
get "/" do
|
40
62
|
@hosts = Host.order(:hostname).all
|
41
63
|
|
42
|
-
@end_now = Time.now.to_fifteen_minute_grain_format
|
64
|
+
@end_now = Time.now.utc.to_fifteen_minute_grain_format
|
43
65
|
@start_24h_ago = @end_now - (60*60*24)
|
44
66
|
@start_6h_ago = @end_now - (60*60*6)
|
45
67
|
|
46
68
|
haml :main
|
47
69
|
end
|
48
70
|
|
49
|
-
|
50
|
-
@host = Host[params[:id]]
|
51
|
-
|
52
|
-
@end_time = Time.now.to_five_minute_grain_format # Round to zero seconds on the minute
|
53
|
-
@start_time = @end_time - (60*60*24)
|
54
|
-
@time_range = (@start_time..@end_time)
|
55
|
-
|
56
|
-
@loads = @host.loads2(@start_time, @end_time)
|
57
|
-
|
58
|
-
content_type 'application/xml', :charset => 'utf-8'
|
59
|
-
haml :loads_xml, :layout => false
|
60
|
-
end
|
71
|
+
post "/load_stats" do
|
61
72
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
73
|
+
begin
|
74
|
+
host = get_host_or_create_if_not_exist(params[:hostname])
|
75
|
+
load_stats = Load.new({:load_5_min => params[:load_5_min], :load_10_min => params[:load_10_min], :load_15_min => params[:load_15_min], :host_id => host[:id]})
|
76
|
+
load_stats.save
|
77
|
+
status(201)
|
78
|
+
rescue
|
79
|
+
status(500)
|
69
80
|
end
|
81
|
+
end
|
82
|
+
|
83
|
+
post "/mem_stats" do
|
70
84
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
85
|
+
begin
|
86
|
+
host = get_host_or_create_if_not_exist(params[:hostname])
|
87
|
+
mem_stats = Memory.new({:host_id => host[:id], :mem_available => params[:mem_available], :mem_used => params[:mem_used], :swap_available => params[:swap_available], :swap_used => params[:swap_used]})
|
88
|
+
mem_stats.save
|
75
89
|
status(201)
|
76
|
-
|
77
|
-
else
|
90
|
+
rescue
|
78
91
|
status(500)
|
79
92
|
end
|
80
93
|
end
|
81
94
|
|
82
|
-
|
83
|
-
|
84
|
-
|
95
|
+
post "/file_system_stats" do
|
96
|
+
|
97
|
+
begin
|
98
|
+
host = get_host_or_create_if_not_exist(params[:hostname])
|
99
|
+
fs = get_fs_or_create_if_not_exist(host[:id], params[:file_system_name], params[:mounted_on])
|
100
|
+
|
101
|
+
disk_stats = Disk.new({:file_system_id => fs[:id], :mounted_on => params[:mounted_on], :used => params[:used], :available => params[:available]})
|
102
|
+
disk_stats.save
|
103
|
+
status(201)
|
104
|
+
rescue
|
105
|
+
status(500)
|
106
|
+
end
|
85
107
|
end
|
86
108
|
|
87
|
-
|
88
109
|
class Time
|
89
110
|
|
90
111
|
# Rounds up to the 5 min intervals
|
data/views/layout.haml
CHANGED
@@ -3,6 +3,10 @@
|
|
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/flot/jquery.flot.pack.js"}
|
9
|
+
|
6
10
|
|
7
11
|
%body
|
8
12
|
#header
|
data/views/main.haml
CHANGED
@@ -3,17 +3,23 @@
|
|
3
3
|
%table{:id => "summary-table", :cellspacing => 0, :cellpading => 0}
|
4
4
|
%tr
|
5
5
|
%th Host
|
6
|
+
%th Last heartbeat
|
6
7
|
%th 5 min average
|
7
8
|
%th 10 min average
|
8
9
|
%th 15 min average
|
9
|
-
%th
|
10
|
+
%th Free disk space
|
11
|
+
%th Free memory
|
12
|
+
%th Free swap space
|
10
13
|
-for host in @hosts
|
11
14
|
%tr{:class => "level#{host.status}"}
|
12
15
|
%td= host.hostname
|
16
|
+
%td= "#{host.distance_to_last_heartbeat_in_seconds.to_i} seconds ago" unless host.status == 0
|
13
17
|
%td= host.load_5_min
|
14
18
|
%td= host.load_10_min
|
15
19
|
%td= host.load_15_min
|
16
|
-
%td= "#{host.
|
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
|
17
23
|
|
18
24
|
|
19
25
|
|
@@ -21,17 +27,40 @@
|
|
21
27
|
%tr
|
22
28
|
-for host in @hosts
|
23
29
|
- series_6h = host.loads_5_min_grain(@start_6h_ago, @end_now)
|
24
|
-
- params_for_6h = "cht=lc&chs=350x120&chco=FF0000,00FF00,0000FF&chm=r,DDDDDD,0,0.2,0.21&chxt=x,y&chxr=1,0,
|
30
|
+
- params_for_6h = "cht=lc&chs=350x120&chco=FF0000,00FF00,0000FF&chm=r,DDDDDD,0,0.2,0.21&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(",")}"
|
25
31
|
- series_24h = host.loads_15_min_grain(@start_24h_ago, @end_now)
|
26
|
-
- params_for_24h = "cht=lc&chs=350x120&chco=FF0000,00FF00,0000FF&chm=r,DDDDDD,0,0.2,0.21&chxt=x,y&chxr=1,0,
|
32
|
+
- params_for_24h = "cht=lc&chs=350x120&chco=FF0000,00FF00,0000FF&chm=r,DDDDDD,0,0.2,0.21&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(",")}"
|
33
|
+
- mem_series_24h = host.memory_stats_15_min_grain(@start_24h_ago, @end_now)
|
34
|
+
- mem_params_for_24h = "cht=lc&chs=350x120&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(",")}"
|
35
|
+
- swap_params_for_24h = "cht=lc&chs=350x120&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(",")}"
|
27
36
|
%td
|
28
37
|
.section
|
29
38
|
%h2=host.hostname
|
30
39
|
%p== Last heard from on #{host.last_contacted_on}
|
40
|
+
|
31
41
|
%h4 Load averages for the last 24 hours
|
32
42
|
%img{ :src => "http://chart.apis.google.com/chart?#{params_for_24h}"}
|
43
|
+
|
33
44
|
%h4 Load averages for the last 6 hours
|
34
45
|
%img{ :src => "http://chart.apis.google.com/chart?#{params_for_6h}"}
|
35
46
|
|
47
|
+
%h4 Memory
|
48
|
+
%p{:style => "float:right;"}== Total memory is #{host.total_memory_in_Mb}Mb
|
49
|
+
%br
|
50
|
+
%img{ :src => "http://chart.apis.google.com/chart?#{mem_params_for_24h}"}
|
51
|
+
|
52
|
+
%h4 Swap
|
53
|
+
%p{:style => "float:right;"}== Total memory is #{host.total_swap_in_Mb}Mb
|
54
|
+
%br
|
55
|
+
%img{ :src => "http://chart.apis.google.com/chart?#{swap_params_for_24h}"}
|
56
|
+
|
57
|
+
- host.file_systems.each do |fs|
|
58
|
+
- if fs.is_dev? and fs.size_in_Gb > 0
|
59
|
+
- series_24h = fs.disks_15_min_grain(@start_24h_ago, @end_now)
|
60
|
+
- 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(",")}"
|
61
|
+
%h4== #{fs.mounted_on} on #{fs.name}
|
62
|
+
%p{:style => "float:right;"}== Device size is #{fs.size_in_Gb}Gb
|
63
|
+
%br
|
64
|
+
%img{ :src => "http://chart.apis.google.com/chart?#{params_for_24h}"}
|
36
65
|
|
37
66
|
|
data/views/style.sass
CHANGED
@@ -4,6 +4,9 @@ body
|
|
4
4
|
:font-family Courier
|
5
5
|
:font-size 1.0em
|
6
6
|
|
7
|
+
br
|
8
|
+
:clear both
|
9
|
+
|
7
10
|
a, a:visited
|
8
11
|
:color #AAA
|
9
12
|
|
@@ -22,7 +25,7 @@ h2
|
|
22
25
|
:text-decoration underline
|
23
26
|
|
24
27
|
h4
|
25
|
-
:margin 20px 10px
|
28
|
+
:margin 20px 10px 10px 0px
|
26
29
|
:padding 5px
|
27
30
|
:background-color #EFEFEF
|
28
31
|
|
@@ -45,13 +48,13 @@ h4
|
|
45
48
|
:border-left 1px solid #DDD
|
46
49
|
|
47
50
|
|
48
|
-
#summary-table tr.level5
|
51
|
+
#summary-table tr.level4, #summary-table tr.level5
|
49
52
|
:background-color #99FF99
|
50
53
|
|
51
54
|
#summary-table tr.level3
|
52
55
|
:background-color #999933
|
53
56
|
|
54
|
-
#summary-table tr.level1
|
57
|
+
#summary-table tr.level1, #summary-table tr.level2
|
55
58
|
:background-color #DD3333
|
56
59
|
|
57
60
|
#summary-table tr.level0
|
@@ -71,9 +74,13 @@ h4
|
|
71
74
|
:padding-left 5px
|
72
75
|
:padding-right 5px
|
73
76
|
|
77
|
+
|
74
78
|
#section-table
|
75
79
|
:border 0px
|
76
80
|
|
81
|
+
#section-table td
|
82
|
+
:vertical-align top
|
83
|
+
|
77
84
|
.section
|
78
85
|
:margin-bottom 20px
|
79
86
|
:margin-left 20px
|
@@ -83,6 +90,7 @@ p
|
|
83
90
|
:font-size 0.7em
|
84
91
|
:margin 0px
|
85
92
|
:padding 0px
|
93
|
+
:margin-right 10px
|
86
94
|
|
87
95
|
#footer
|
88
96
|
:margin-top 20px
|
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.3
|
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-
|
12
|
+
date: 2009-03-01 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -62,38 +62,28 @@ dependencies:
|
|
62
62
|
- !ruby/object:Gem::Version
|
63
63
|
version: 1.2.4
|
64
64
|
version:
|
65
|
-
- !ruby/object:Gem::Dependency
|
66
|
-
name: rest-client
|
67
|
-
type: :runtime
|
68
|
-
version_requirement:
|
69
|
-
version_requirements: !ruby/object:Gem::Requirement
|
70
|
-
requirements:
|
71
|
-
- - ">="
|
72
|
-
- !ruby/object:Gem::Version
|
73
|
-
version: "0.9"
|
74
|
-
version:
|
75
65
|
description: dog not god is a performance monitoring tool.
|
76
66
|
email: luis.ca@gmail.com
|
77
67
|
executables:
|
78
68
|
- dognotgod
|
79
|
-
- dognotgod-client
|
80
69
|
extensions: []
|
81
70
|
|
82
71
|
extra_rdoc_files: []
|
83
72
|
|
84
73
|
files:
|
85
74
|
- app/models
|
75
|
+
- app/models/disk.rb
|
76
|
+
- app/models/file_system.rb
|
86
77
|
- app/models/host.rb
|
87
78
|
- app/models/load.rb
|
79
|
+
- app/models/memory.rb
|
88
80
|
- config.ru
|
89
|
-
- README
|
81
|
+
- README.md
|
90
82
|
- views/layout.haml
|
91
|
-
- views/loads_xml.haml
|
92
83
|
- views/main.haml
|
93
84
|
- views/style.sass
|
94
85
|
- config/thin.yml
|
95
86
|
- server.rb
|
96
|
-
- client.rb
|
97
87
|
has_rdoc: false
|
98
88
|
homepage: http://github.com/spoonsix/dognotgod
|
99
89
|
post_install_message:
|
data/bin/dognotgod-client
DELETED
data/client.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'optparse'
|
3
|
-
require 'restclient'
|
4
|
-
|
5
|
-
module DogNotGod
|
6
|
-
|
7
|
-
class Client
|
8
|
-
|
9
|
-
def initialize(argv)
|
10
|
-
@argv = argv
|
11
|
-
|
12
|
-
# Default options values
|
13
|
-
@options = {
|
14
|
-
:server_addr => "127.0.0.1",
|
15
|
-
:server_port => "4567",
|
16
|
-
:timeout => 5
|
17
|
-
}
|
18
|
-
|
19
|
-
parse!
|
20
|
-
|
21
|
-
@endpoint = "http://#{@options[:server_addr]}:#{@options[:server_port]}"
|
22
|
-
end
|
23
|
-
|
24
|
-
def parser
|
25
|
-
# NOTE: If you add an option here make sure the key in the +options+ hash is the
|
26
|
-
# same as the name of the command line option.
|
27
|
-
# +option+ keys are used to build the command line to launch other processes,
|
28
|
-
# see <tt>lib/thin/command.rb</tt>.
|
29
|
-
@parser ||= OptionParser.new do |opts|
|
30
|
-
opts.banner = "Usage: dognotgod-client [options]"
|
31
|
-
opts.separator ""
|
32
|
-
opts.separator "Server options:"
|
33
|
-
|
34
|
-
opts.on("-a", "--server-addr HOST", "HOST address to call (default: #{@options[:server_addr]})") { |host| @options[:server_addr] = host }
|
35
|
-
opts.on("-p", "--server-port PORT", "use PORT (default: #{@options[:server_port]})") { |port| @options[:server_port] = port.to_i }
|
36
|
-
#opts.on("-t", "--timeout SECONDS", "timeout after SECONDS (default: #{@options[:timeout]})") { |port| @options[:timeout] = port.to_i }
|
37
|
-
opts.separator ""
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# Parse the options.
|
42
|
-
def parse!
|
43
|
-
parser.parse! @argv
|
44
|
-
@arguments = @argv
|
45
|
-
end
|
46
|
-
|
47
|
-
def run!
|
48
|
-
hostname = %x[hostname].split("\n")[0]
|
49
|
-
uptime = %x[uptime]
|
50
|
-
avgs = uptime.scan(/(\d+\.\d\d)/)
|
51
|
-
|
52
|
-
begin
|
53
|
-
RestClient.post("#{@endpoint}/loads", :load_5_min => avgs[0], :load_10_min => avgs[1], :load_15_min => avgs[2], :hostname => hostname)
|
54
|
-
puts "OK"
|
55
|
-
rescue
|
56
|
-
puts "There was a problem connecting to the server on #{@endpoint}."
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
DogNotGod::Client.new(ARGV).run!
|
data/views/loads_xml.haml
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
!!! XML
|
2
|
-
%chart
|
3
|
-
%series
|
4
|
-
-i=0
|
5
|
-
-@time_range.step(300) do |five|
|
6
|
-
%value{:xid => i+=1}
|
7
|
-
=five.to_minute_format
|
8
|
-
%graphs
|
9
|
-
%graph{:gid => "1"}
|
10
|
-
-i=0
|
11
|
-
-load = Array.new(@loads)
|
12
|
-
-@time_range.step(300) do |five|
|
13
|
-
%value{:xid => i+=1}= load.shift[:load_5_min] if load.size > 0 and load[0][:grain_5_min].to_minute_format == five.to_minute_format
|
14
|
-
%graph{:gid => "2"}
|
15
|
-
-i=0
|
16
|
-
-load = Array.new(@loads)
|
17
|
-
-@time_range.step(300) do |five|
|
18
|
-
%value{:xid => i+=1}= load.shift[:load_10_min] if load.size > 0 and load[0][:grain_5_min].to_minute_format == five.to_minute_format
|
19
|
-
%graph{:gid => "3"}
|
20
|
-
-i=0
|
21
|
-
-load = Array.new(@loads)
|
22
|
-
-@time_range.step(300) do |five|
|
23
|
-
%value{:xid => i+=1}= load.shift[:load_15_min] if load.size > 0 and load[0][:grain_5_min].to_minute_format == five.to_minute_format
|