city-watch 0.5.2 → 0.5.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/Gemfile.lock +1 -1
- data/config/city_watch.yml +2 -1
- data/lib/city_watch/collector.rb +2 -0
- data/lib/city_watch/collector/receive.rb +2 -16
- data/lib/city_watch/commander.rb +1 -0
- data/lib/city_watch/commander/server.rb +20 -2
- data/lib/city_watch/util/collector.rb +17 -0
- data/lib/city_watch/util/watchman.rb +129 -0
- data/lib/city_watch/watchmen.rb +5 -6
- data/lib/city_watch/watchmen/disk_usage.rb +13 -0
- data/lib/city_watch/watchmen/unicorns.rb +1 -1
- data/lib/version.rb +1 -1
- metadata +2 -2
data/Gemfile.lock
CHANGED
data/config/city_watch.yml
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
:
|
|
1
|
+
:debug: true
|
|
2
|
+
:watch_collector: "localhost:62001"
|
data/lib/city_watch/collector.rb
CHANGED
|
@@ -1,25 +1,11 @@
|
|
|
1
|
-
class
|
|
1
|
+
class WatchCollector
|
|
2
2
|
|
|
3
3
|
def call(env)
|
|
4
4
|
|
|
5
5
|
post_data = begin Yajl::Parser.new(:symbolize_keys => true).parse(env["rack.input"].read) || {} rescue {} end
|
|
6
6
|
post_data[:received_at] = Time.now.to_s
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
CityWatch.redis.zadd "#{CityWatch.config[:prefix]}::#{post_data[:hostname]}::raw_stats", Time.now.to_i, Yajl::Encoder.encode(post_data)
|
|
10
|
-
|
|
11
|
-
summary = {}
|
|
12
|
-
|
|
13
|
-
post_data[:watchmen].each do |watchman,dat|
|
|
14
|
-
CityWatch.redis.zadd "#{CityWatch.config[:prefix]}::#{post_data[:hostname]}::#{watchman}", Time.now.to_i, Yajl::Encoder.encode(dat.merge({:received_at => post_data[:received_at]}))
|
|
15
|
-
if dat[:summary]
|
|
16
|
-
sum = dat[:summary].is_a?(Array) ? dat[:summary].inject({}) {|acc,k| acc[k.to_sym] = dat[k.to_sym]; acc} : dat[:summary]
|
|
17
|
-
CityWatch.redis.zadd "#{CityWatch.config[:prefix]}::#{post_data[:hostname]}::#{watchman}::summary", Time.now.to_i, Yajl::Encoder.encode(sum)
|
|
18
|
-
summary[watchman] = sum
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
CityWatch.redis.zadd "#{CityWatch.config[:prefix]}::#{post_data[:hostname]}::summary", Time.now.to_i, Yajl::Encoder.encode(summary.merge({:received_at => post_data[:received_at]}))
|
|
8
|
+
Collector.process(post_data)
|
|
23
9
|
|
|
24
10
|
[200,{"Content-Type" => "text/plain"},["Got it!"]]
|
|
25
11
|
end
|
data/lib/city_watch/commander.rb
CHANGED
|
@@ -5,7 +5,25 @@ class Server
|
|
|
5
5
|
parms = Rack::Request.new(env).params.merge(env["rack.routing_args"]).inject({}){|acc,(k,v)| acc[k.to_sym] = v; acc}
|
|
6
6
|
server = parms[:server]
|
|
7
7
|
|
|
8
|
-
output = CityWatch.header << '<h1>' << server << "</h1
|
|
8
|
+
output = CityWatch.header << '<h1>' << server << "</h1>"
|
|
9
|
+
Watchmen.each do |watchman|
|
|
10
|
+
flags = watchman.get_flags(server)
|
|
11
|
+
alerts = watchman.get_alerts(server,2)
|
|
12
|
+
if (flags && flags.keys.count > 0) || (alerts && alerts.count > 0)
|
|
13
|
+
output << "<h3>" << watchman.name.to_s << "</h3><ul>"
|
|
14
|
+
if flags && flags.keys.count > 0
|
|
15
|
+
output << "<li class=\"alert\"><strong>Flags:</strong> <pre><code>" << Yajl::Encoder.encode(flags,:pretty => true, :indent => " ") << "</code></pre></li>"
|
|
16
|
+
end
|
|
17
|
+
if alerts && alerts.count > 0
|
|
18
|
+
output << "<li class=\"alert\"><strong>Alerts:</strong></li>"
|
|
19
|
+
alerts.each do |alert|
|
|
20
|
+
output << "<li><pre><code>" << Yajl::Encoder.encode(Yajl::Parser.parse(alert),:pretty => true, :indent => " ") << "</code></pre></li>"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
output << "</ul>"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
output << "<ol>"
|
|
9
27
|
CityWatch.redis.zrevrange("#{CityWatch.config[:prefix]}::#{server}::raw_stats",0,10).each do |update|
|
|
10
28
|
dat = Yajl::Parser.new(:symbolize_keys => true).parse(update)
|
|
11
29
|
output << "<li><h4>" << dat[:received_at] << "</h4><ul>"
|
|
@@ -14,7 +32,7 @@ class Server
|
|
|
14
32
|
end
|
|
15
33
|
output << "</ul></li>"
|
|
16
34
|
end
|
|
17
|
-
output << "</
|
|
35
|
+
output << "</ol></body></html>"
|
|
18
36
|
|
|
19
37
|
[200,{"Content-Type" => "text/html"},[output]]
|
|
20
38
|
end
|
|
@@ -26,4 +26,21 @@ module Collector
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def self.process(data)
|
|
30
|
+
rcv_time = Time.now.to_i
|
|
31
|
+
host = data[:hostname]
|
|
32
|
+
CityWatch.redis.sadd "#{CityWatch.config[:prefix]}::known_hosts", host
|
|
33
|
+
CityWatch.redis.zadd "#{CityWatch.config[:prefix]}::#{host}::raw_stats", rcv_time, Yajl::Encoder.encode(data)
|
|
34
|
+
|
|
35
|
+
summary = {}
|
|
36
|
+
|
|
37
|
+
data[:watchmen].each do |watchman,dat|
|
|
38
|
+
if watch_obj = Watchmen.get(watchman)
|
|
39
|
+
status, summary[watchman] = watch_obj.process(dat,rcv_time,host)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
CityWatch.redis.zadd "#{CityWatch.config[:prefix]}::#{host}::summary", rcv_time, Yajl::Encoder.encode(summary.merge({:received_at => data[:received_at]}))
|
|
44
|
+
end
|
|
45
|
+
|
|
29
46
|
end
|
|
@@ -6,6 +6,135 @@ module Watchman
|
|
|
6
6
|
|
|
7
7
|
module ClassMethods
|
|
8
8
|
|
|
9
|
+
def process(dat,rcv,host)
|
|
10
|
+
@host = host
|
|
11
|
+
@rcv_time = rcv
|
|
12
|
+
CityWatch.redis.zadd "#{CityWatch.config[:prefix]}::#{host}::#{self.name}", rcv_time, Yajl::Encoder.encode(dat.merge({:received_at => dat[:received_at]}))
|
|
13
|
+
if dat[:summary]
|
|
14
|
+
sum = dat[:summary].is_a?(Array) ? dat[:summary].inject({}) {|acc,k| acc[k.to_sym] = dat[k.to_sym]; acc} : dat[:summary]
|
|
15
|
+
CityWatch.redis.zadd "#{CityWatch.config[:prefix]}::#{host}::#{self.name}::summary", rcv_time, Yajl::Encoder.encode(sum)
|
|
16
|
+
end
|
|
17
|
+
run_rules(dat)
|
|
18
|
+
return 0, sum || nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def host
|
|
22
|
+
@host || nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def rcv_time
|
|
26
|
+
@rcv_time || Time.now.to_i
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def add_rule(name,&block)
|
|
30
|
+
@rules ||= {}
|
|
31
|
+
@rules[name] = block
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def run_rules(dat)
|
|
35
|
+
@rules.map do |(name,rule)|
|
|
36
|
+
rule.call(dat)
|
|
37
|
+
end if @rules
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def send_alert(message,dat=nil)
|
|
41
|
+
CityWatch.redis.zadd "#{CityWatch.config[:prefix]}::#{host}::#{self.name}::alerts", rcv_time, Yajl::Encoder.encode({:message => message, :data => dat, :when => rcv_time})
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def alerts
|
|
45
|
+
@alerts ||= []
|
|
46
|
+
if block_given?
|
|
47
|
+
@alerts.each do |a|
|
|
48
|
+
yield a
|
|
49
|
+
end
|
|
50
|
+
else
|
|
51
|
+
@alerts
|
|
52
|
+
end
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def get_alerts(host=host,num=5)
|
|
57
|
+
CityWatch.redis.zrevrange "#{CityWatch.config[:prefix]}::#{host}::#{self.name}::alerts", 0, num - 1
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def send_alerts!
|
|
61
|
+
@alerts.map do |alert|
|
|
62
|
+
puts "Alert: #{alert.inspect}" #if CityWatch.debug?
|
|
63
|
+
end if @alerts
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def set_flag(name)
|
|
67
|
+
unless get_flag(name)
|
|
68
|
+
flag_flapped name, :on
|
|
69
|
+
end
|
|
70
|
+
CityWatch.redis.setbit "#{CityWatch.config[:prefix]}::#{host}::#{self.name}::flags", flag_position(name), 1
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def clear_flag(name)
|
|
74
|
+
if get_flag(name)
|
|
75
|
+
flag_flapped name, :off
|
|
76
|
+
end
|
|
77
|
+
CityWatch.redis.setbit "#{CityWatch.config[:prefix]}::#{host}::#{self.name}::flags", flag_position(name), 0
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def flag_flapped(name,new_val)
|
|
81
|
+
# should have some event to watch for a flag switching position
|
|
82
|
+
puts "Flag flipped: #{name} -> #{new_val}" #if CityWatch.debug?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def get_flag(name,host=host)
|
|
86
|
+
@host = host
|
|
87
|
+
CityWatch.redis.getbit("#{CityWatch.config[:prefix]}::#{host}::#{self.name}::flags", flag_position(name)) ? true : false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def get_flags(host=host)
|
|
91
|
+
@host = host
|
|
92
|
+
out = {}
|
|
93
|
+
map = flag_map
|
|
94
|
+
map.each_index do |idx|
|
|
95
|
+
out[map[idx]] = get_flag(map[idx])
|
|
96
|
+
end
|
|
97
|
+
out
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def flag_map_key
|
|
101
|
+
"#{CityWatch.config[:prefix]}::#{self.name}::flag_map"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def flag_map
|
|
105
|
+
CityWatch.redis.lrange flag_map_key, 0, -1
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def flag_position(name)
|
|
109
|
+
if (map = flag_map) && map.include?(name.to_s)
|
|
110
|
+
map.index(name.to_s)
|
|
111
|
+
else
|
|
112
|
+
new_flag(name)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def new_flag(name)
|
|
117
|
+
CityWatch.redis.rpush(flag_map_key, name) - 1
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def set_default(k,val)
|
|
121
|
+
opts[k] = val
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def opts
|
|
125
|
+
@options ||= {}
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def options(*args)
|
|
129
|
+
if args.count > 1
|
|
130
|
+
return args.map {|k| opts[k]}
|
|
131
|
+
else
|
|
132
|
+
return opts[args.first]
|
|
133
|
+
end
|
|
134
|
+
return nil
|
|
135
|
+
end
|
|
136
|
+
alias_method :option, :options
|
|
137
|
+
|
|
9
138
|
end
|
|
10
139
|
|
|
11
140
|
def self.included(base)
|
data/lib/city_watch/watchmen.rb
CHANGED
|
@@ -20,14 +20,13 @@ module Watchmen
|
|
|
20
20
|
@watchmen << cls
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def self.
|
|
24
|
-
@
|
|
25
|
-
@rules << block
|
|
23
|
+
def self.get(name)
|
|
24
|
+
@watchmen.select {|w| w.name.to_s == name.to_s }.first
|
|
26
25
|
end
|
|
27
26
|
|
|
28
|
-
def self.
|
|
29
|
-
@
|
|
30
|
-
|
|
27
|
+
def self.each
|
|
28
|
+
@watchmen.each do |w|
|
|
29
|
+
yield w
|
|
31
30
|
end
|
|
32
31
|
end
|
|
33
32
|
|
|
@@ -2,10 +2,23 @@ class DiskUsage
|
|
|
2
2
|
|
|
3
3
|
include Watchman
|
|
4
4
|
|
|
5
|
+
set_default :usage_threshold, 60
|
|
6
|
+
|
|
5
7
|
def self.data
|
|
6
8
|
dat = DF.data
|
|
7
9
|
sum = dat.select {|d| d[:mounted]=="/"}.first
|
|
8
10
|
{:partitions => dat, :summary => sum[:capacity] || sum["use%".to_sym]}
|
|
9
11
|
end
|
|
10
12
|
|
|
13
|
+
add_rule(:root_usage_high) do |data|
|
|
14
|
+
|
|
15
|
+
if (usage = data[:summary].to_i) && usage > option(:usage_threshold)
|
|
16
|
+
send_alert "Root disk usage is over #{option(:usage_threshold)}% (#{usage}%)", data[:partitions].select {|d| d[:mounted]=="/"}.first
|
|
17
|
+
set_flag :root_disk_over_quota
|
|
18
|
+
else
|
|
19
|
+
clear_flag :root_disk_over_quota
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
|
|
11
24
|
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: city-watch
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease:
|
|
5
|
-
version: 0.5.
|
|
5
|
+
version: 0.5.3
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
8
8
|
- John Bragg
|
|
@@ -10,7 +10,7 @@ autorequire:
|
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
12
|
|
|
13
|
-
date: 2013-02-
|
|
13
|
+
date: 2013-02-11 00:00:00 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: redis
|