spoonsix-dognotgod 0.1.2 → 0.1.3
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.
- 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
|