city-watch 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  pkg/*
2
+ .DS_Store
@@ -1,7 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- city-watch (0.5.7)
4
+ city-watch (0.6.3)
5
+ erubis
6
+ mail
5
7
  rack
6
8
  rack-mount
7
9
  redis
@@ -13,27 +15,38 @@ PATH
13
15
  GEM
14
16
  remote: http://rubygems.org/
15
17
  specs:
16
- diff-lcs (1.1.3)
18
+ diff-lcs (1.2.1)
17
19
  drydock (0.6.9)
18
- kgio (2.7.4)
19
- rack (1.4.1)
20
+ erubis (2.7.0)
21
+ i18n (0.6.4)
22
+ kgio (2.8.0)
23
+ mail (2.5.3)
24
+ i18n (>= 0.4.0)
25
+ mime-types (~> 1.16)
26
+ treetop (~> 1.4.8)
27
+ mime-types (1.21)
28
+ polyglot (0.3.3)
29
+ rack (1.5.2)
20
30
  rack-mount (0.8.3)
21
31
  rack (>= 1.0.0)
22
32
  raindrops (0.10.0)
23
- redis (3.0.2)
24
- rspec (2.12.0)
25
- rspec-core (~> 2.12.0)
26
- rspec-expectations (~> 2.12.0)
27
- rspec-mocks (~> 2.12.0)
28
- rspec-core (2.12.1)
29
- rspec-expectations (2.12.0)
30
- diff-lcs (~> 1.1.3)
31
- rspec-mocks (2.12.0)
33
+ redis (3.0.3)
34
+ rspec (2.13.0)
35
+ rspec-core (~> 2.13.0)
36
+ rspec-expectations (~> 2.13.0)
37
+ rspec-mocks (~> 2.13.0)
38
+ rspec-core (2.13.1)
39
+ rspec-expectations (2.13.0)
40
+ diff-lcs (>= 1.1.3, < 2.0)
41
+ rspec-mocks (2.13.0)
32
42
  storable (0.8.9)
33
43
  sysinfo (0.8.0)
34
44
  drydock
35
45
  storable
36
- unicorn (4.5.0)
46
+ treetop (1.4.12)
47
+ polyglot
48
+ polyglot (>= 0.3.1)
49
+ unicorn (4.6.2)
37
50
  kgio (~> 2.6)
38
51
  rack
39
52
  raindrops (~> 0.7)
@@ -24,6 +24,8 @@ Gem::Specification.new do |s|
24
24
  s.add_dependency "rack"
25
25
  s.add_dependency "rack-mount"
26
26
  s.add_dependency "unicorn"
27
+ s.add_dependency "mail"
28
+ s.add_dependency "erubis"
27
29
  s.add_dependency "yajl-ruby"
28
30
  s.add_dependency "sysinfo"
29
31
  end
@@ -6,6 +6,8 @@ end
6
6
 
7
7
  ::Routes = CityWatch::Routes
8
8
 
9
+ require 'city_watch/util/renderer'
9
10
  require 'city_watch/watchmen'
11
+ require 'city_watch/reader'
10
12
  require 'city_watch/commander/server'
11
13
  require 'city_watch/commander/home'
@@ -1,14 +1,13 @@
1
1
  class Home
2
2
 
3
+ include Renderer
4
+
3
5
  def call(env)
4
-
5
- output = CityWatch.header << '<h1>Known servers:</h1><ul>'
6
- CityWatch.redis.smembers("#{CityWatch.config[:prefix]}::known_hosts").each do |server|
7
- output << "<li><a href=\"/" << server << "\">#{server}</a>: <pre><code>" << Yajl::Encoder.encode(Yajl::Parser.parse(CityWatch.redis.zrevrange("#{CityWatch.config[:prefix]}::#{server}::summary",0,0).first),:pretty => true, :indent => " ") << "</code></pre></li>"
8
- end
9
- output << "</ul></body></html>"
10
-
11
- [200,{"Content-Type" => "text/html"},[output]]
6
+ [200,{"Content-Type" => "text/html"},[render("home")]]
7
+ end
8
+
9
+ def servers
10
+ Reader.servers
12
11
  end
13
12
 
14
13
  Routes.add_route new, { :request_method => 'GET', :path_info => %r{^/$} }, {}, :home
@@ -0,0 +1,2 @@
1
+ require 'city_watch/reader/server'
2
+ require 'city_watch/reader/watchman'
@@ -0,0 +1,196 @@
1
+ module Reader
2
+
3
+ def self.servers
4
+ CityWatch.redis.smembers("#{CityWatch.config[:prefix]}::known_hosts").map do |server|
5
+ Reader::Host.new(server)
6
+ end
7
+ end
8
+
9
+ class Host
10
+
11
+ include Renderer
12
+
13
+ def initialize(host)
14
+ @host = host
15
+ end
16
+
17
+ def hostname
18
+ @host
19
+ end
20
+
21
+ def watchmen
22
+ CityWatch.redis.smembers(watchmen_key).map do |name|
23
+ Reader::Watchman.new(name)
24
+ end
25
+ end
26
+
27
+ def watchmen_key
28
+ key(:watchmen)
29
+ end
30
+
31
+ def data_sets
32
+ CityWatch.redis.smembers(data_sets_key)
33
+ end
34
+
35
+ def data_sets_key
36
+ @data_sets_key ||= key(:data_sets)
37
+ end
38
+
39
+ def summary
40
+ summaries.first
41
+ end
42
+
43
+ def summary_html
44
+ render "server/summary"
45
+ end
46
+
47
+ def summaries(num=1)
48
+ CityWatch.redis.zrevrange(summary_key,0,num).map {|sum| Yajl::Parser.parse(sum) }
49
+ end
50
+
51
+ def summary_key
52
+ @summary_key ||= key(:summary)
53
+ end
54
+
55
+ def current_raw
56
+ raws.first
57
+ end
58
+
59
+ def raws(num=1)
60
+ CityWatch.redis.zrevrange(raw_key,0,num)
61
+ end
62
+
63
+ def raw_key
64
+ @raw_key ||= key(:raw_stats)
65
+ end
66
+
67
+ def key(more)
68
+ "#{key_prefix}::#{more}"
69
+ end
70
+
71
+ def key_prefix
72
+ @key_prefix ||= "#{CityWatch.config[:prefix]}::#{@host}"
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
79
+ # CityWatch::earlgray.cozy.co::raw_stats
80
+ # CityWatch::earlgray.cozy.co::summary
81
+
82
+ # CityWatch::earlgray.cozy.co::DiskUsage
83
+ # CityWatch::earlgray.cozy.co::DiskUsage::summary
84
+ # CityWatch::earlgray.cozy.co::DiskUsage::data_set::root_disk_usage
85
+ # CityWatch::earlgray.cozy.co::DiskUsage::data_sets
86
+
87
+ # CityWatch::earlgray.cozy.co::Uptime
88
+ # CityWatch::earlgray.cozy.co::Uptime::summary
89
+ # CityWatch::earlgray.cozy.co::Uptime::status
90
+
91
+ # CityWatch::earlgray.cozy.co::SystemInfo
92
+ # CityWatch::earlgray.cozy.co::SystemInfo::summary
93
+
94
+ # CityWatch::earlgray.cozy.co::Nginx
95
+ # CityWatch::earlgray.cozy.co::Nginx::summary
96
+
97
+ # CityWatch::earlgray.cozy.co::CPUUsage
98
+ # CityWatch::earlgray.cozy.co::CPUUsage::summary
99
+
100
+ # CityWatch::earlgray.cozy.co::Unicorns
101
+ # CityWatch::earlgray.cozy.co::Unicorns::summary
102
+
103
+
104
+
105
+
106
+
107
+
108
+ # CityWatch::rooibos.cozy.co::raw_stats
109
+ # CityWatch::jasmine.cozy.co::CPUUsage::summary
110
+ # CityWatch::sencha.cozy.co::raw_stats
111
+ # CityWatch::pekoe.cozy.co::DiskUsage
112
+ # CityWatch::sencha.cozy.co::SystemInfo::summary
113
+ # CityWatch::sencha.cozy.co::Unicorns::summary
114
+ # CityWatch::rooibos.cozy.co::DiskUsage::flags
115
+ # CityWatch::pekoe.cozy.co::Uptime
116
+ # CityWatch::pekoe.cozy.co::DiskUsage::summary
117
+ # CityWatch::jasmine.cozy.co::CPUUsage
118
+ # CityWatch::pekoe.cozy.co::Uptime::summary
119
+ # CityWatch::pekoe.cozy.co::Nginx::summary
120
+ # CityWatch::sencha.cozy.co::Uptime::summary
121
+ # CityWatch::oolong.cozy.co::CPUUsage::summary
122
+ # CityWatch::oolong.cozy.co::Nginx::summary
123
+ # CityWatch::oolong.cozy.co::Uptime
124
+ # CityWatch::oolong.cozy.co::SystemInfo
125
+ # CityWatch::sencha.cozy.co::Uptime
126
+ # CityWatch::pekoe.cozy.co::DiskUsage::data_sets
127
+ # CityWatch::rooibos.cozy.co::DiskUsage
128
+ # CityWatch::pekoe.cozy.co::raw_stats
129
+ # CityWatch::rooibos.cozy.co::Uptime::status
130
+ # CityWatch::sencha.cozy.co::SystemInfo
131
+ # CityWatch::rooibos.cozy.co::Nginx::summary
132
+ # CityWatch::pekoe.cozy.co::CPUUsage
133
+ # CityWatch::sencha.cozy.co::CPUUsage
134
+ # CityWatch::rooibos.cozy.co::Unicorns
135
+ # CityWatch::sencha.cozy.co::DiskUsage::summary
136
+ # CityWatch::pekoe.cozy.co::CPUUsage::summary
137
+ # CityWatch::jasmine.cozy.co::DiskUsage::data_sets
138
+ # CityWatch::oolong.cozy.co::CPUUsage
139
+ # CityWatch::rooibos.cozy.co::SystemInfo::summary
140
+ # CityWatch::pekoe.cozy.co::Unicorns::summary
141
+ # CityWatch::pekoe.cozy.co::Nginx
142
+ # CityWatch::jasmine.cozy.co::Unicorns
143
+ # CityWatch::sencha.cozy.co::summary
144
+ # CityWatch::pekoe.cozy.co::Uptime::status
145
+ # CityWatch::pekoe.cozy.co::SystemInfo
146
+ # CityWatch::jasmine.cozy.co::Nginx
147
+ # CityWatch::rooibos.cozy.co::CPUUsage
148
+ # CityWatch::oolong.cozy.co::Uptime::status
149
+ # CityWatch::sencha.cozy.co::Nginx::summary
150
+ # CityWatch::pekoe.cozy.co::DiskUsage::data_set::root_disk_usage
151
+ # CityWatch::sencha.cozy.co::Unicorns
152
+ # CityWatch::known_hosts
153
+ # CityWatch::sencha.cozy.co::CPUUsage::summary
154
+ # CityWatch::jasmine.cozy.co::Uptime::summary
155
+ # CityWatch::rooibos.cozy.co::DiskUsage::data_sets
156
+ # CityWatch::jasmine.cozy.co::SystemInfo
157
+ # CityWatch::oolong.cozy.co::Nginx
158
+ # CityWatch::pekoe.cozy.co::Unicorns
159
+ # CityWatch::oolong.cozy.co::Uptime::summary
160
+ # CityWatch::sencha.cozy.co::DiskUsage
161
+ # CityWatch::oolong.cozy.co::SystemInfo::summary
162
+ # CityWatch::oolong.cozy.co::DiskUsage::data_set::root_disk_usage
163
+ # CityWatch::sencha.cozy.co::Nginx
164
+ # CityWatch::jasmine.cozy.co::Uptime
165
+ # CityWatch::rooibos.cozy.co::DiskUsage::summary
166
+ # CityWatch::DiskUsage::flag_map
167
+ # CityWatch::oolong.cozy.co::DiskUsage::data_sets
168
+ # CityWatch::rooibos.cozy.co::DiskUsage::alerts
169
+ # CityWatch::jasmine.cozy.co::Unicorns::summary
170
+ # CityWatch::sencha.cozy.co::DiskUsage::data_set::root_disk_usage
171
+ # CityWatch::jasmine.cozy.co::DiskUsage::summary
172
+ # CityWatch::sencha.cozy.co::Uptime::status
173
+ # CityWatch::oolong.cozy.co::summary
174
+ # CityWatch::jasmine.cozy.co::DiskUsage
175
+ # CityWatch::rooibos.cozy.co::summary
176
+ # CityWatch::rooibos.cozy.co::Unicorns::summary
177
+ # CityWatch::oolong.cozy.co::raw_stats
178
+ # CityWatch::rooibos.cozy.co::SystemInfo
179
+ # CityWatch::rooibos.cozy.co::Uptime
180
+ # CityWatch::rooibos.cozy.co::DiskUsage::data_set::root_disk_usage
181
+ # CityWatch::pekoe.cozy.co::SystemInfo::summary
182
+ # CityWatch::jasmine.cozy.co::SystemInfo::summary
183
+ # CityWatch::rooibos.cozy.co::Uptime::summary
184
+ # CityWatch::oolong.cozy.co::Unicorns::summary
185
+ # CityWatch::oolong.cozy.co::Unicorns
186
+ # CityWatch::jasmine.cozy.co::raw_stats
187
+ # CityWatch::jasmine.cozy.co::Nginx::summary
188
+ # CityWatch::rooibos.cozy.co::CPUUsage::summary
189
+ # CityWatch::rooibos.cozy.co::Nginx
190
+ # CityWatch::oolong.cozy.co::DiskUsage
191
+ # CityWatch::jasmine.cozy.co::DiskUsage::data_set::root_disk_usage
192
+ # CityWatch::pekoe.cozy.co::summary
193
+ # CityWatch::sencha.cozy.co::DiskUsage::data_sets
194
+ # CityWatch::jasmine.cozy.co::Uptime::status
195
+ # CityWatch::oolong.cozy.co::DiskUsage::summary
196
+ # CityWatch::jasmine.cozy.co::summary
@@ -0,0 +1,72 @@
1
+ module Reader
2
+
3
+ class Watchman
4
+
5
+ include Renderer
6
+
7
+ def initialize(host,name)
8
+ @host = host
9
+ @name = name
10
+ end
11
+
12
+ def render_from_summary(data)
13
+ @current_data = data
14
+ return render("watchmen/#{@name}/summary", "watchmen/summary")
15
+ ensure
16
+ @current_data = nil
17
+ end
18
+
19
+ def watchman_class
20
+ @klass ||= Object.const_get(@name.to_sym)
21
+ end
22
+
23
+ def sparklines
24
+ data_sets.map do |set|
25
+ [set.to_sym,sparkline(get_data_set(set).map {|(tm,val)| val })]
26
+ end
27
+ end
28
+
29
+ def sparkline_image_tags
30
+ data_sets.map do |set|
31
+ [set.to_sym, sparkline_img_tag(get_data_set(set).map {|(tm,val)| val })]
32
+ end
33
+ end
34
+
35
+ def get_data_set(data_set_name,s_time=(Time.now - (60*60*12)),e_time=Time.now)
36
+ CityWatch.redis.zrevrangebyscore(data_set_key(data_set_name), s_time.to_i, e_time.to_i, :with_scores => true).map do |(val,score)|
37
+ timestamp,value = val.split(",")
38
+ [Time.at(timestamp.to_i), value]
39
+ end
40
+ end
41
+
42
+ def data_set_key(data_set)
43
+ key("data_set::#{data_set}")
44
+ end
45
+
46
+ def data_sets
47
+ CityWatch.redis.smembers(data_sets_key)
48
+ end
49
+
50
+ def data_sets_key
51
+ @data_sets_key ||= key(:data_sets)
52
+ end
53
+
54
+ def sparkline(dat)
55
+ Base64.encode64(Spark.smooth(dat, :has_min => true, :has_max => true, :height => 14, :step => 4)).gsub("\n",'')
56
+ end
57
+
58
+ def sparkline_img_tag(dat)
59
+ "<img src=\"data:image/png;base64,#{sparkline(dat)}\"/>"
60
+ end
61
+
62
+ def key(more)
63
+ "#{key_prefix}::#{more}"
64
+ end
65
+
66
+ def key_prefix
67
+ @key_prefix ||= "#{CityWatch.config[:prefix]}::#{@host}::#{@name}"
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -19,6 +19,7 @@ module DataSets
19
19
  end
20
20
 
21
21
  def data_set_key(data_set_name,host=host)
22
+ CityWatch.redis.sadd "#{CityWatch.config[:prefix]}::#{host}::data_sets", data_set_name
22
23
  CityWatch.redis.sadd "#{CityWatch.config[:prefix]}::#{host}::#{self.name}::data_sets", data_set_name
23
24
  "#{CityWatch.config[:prefix]}::#{host}::#{self.name}::data_set::#{data_set_name}"
24
25
  end
@@ -0,0 +1,50 @@
1
+ module Renderer
2
+
3
+ require 'erubis'
4
+
5
+ def render(*tpl)
6
+ layout do
7
+ render_bare(*tpl)
8
+ end
9
+ end
10
+
11
+ def render_bare(*tpl)
12
+ Erubis::FastEruby.new(template(*tpl), filename: template_path(*tpl)).result(binding)
13
+ end
14
+
15
+ private
16
+
17
+ def layout(&block)
18
+ @layout ||= Proc.new {
19
+ file_path = "#{view_path}/layouts/default.html.erb"
20
+ Erubis::FastEruby.new(File.read(file_path), bufvar: '@output_buffer', filename: file_path)
21
+ }.call
22
+ @layout.result(binding)
23
+ end
24
+
25
+ def template_content(file_path)
26
+ return "" unless file_path
27
+ File.read(file_path)
28
+ end
29
+
30
+ def template_key(*keys)
31
+ keys.inject("") {|a,k| a += k.to_s}
32
+ end
33
+
34
+ def template_path(*tpl)
35
+ tpl.map {|t| "#{view_path}/#{t}.html.erb" }.detect{|p| File.exists?(p) }
36
+ end
37
+
38
+ def template(*tpl)
39
+ return @templates[tpl.join] if @templates && @templates[tpl.join]
40
+ file_path = template_path(*tpl)
41
+ return template_content(file_path) if CityWatch.debug?
42
+ @templates ||= {}
43
+ @templates[tpl.join] ||= template_content(file_path)
44
+ end
45
+
46
+ def view_path
47
+ File.expand_path(File.dirname(__FILE__) + "/../../../views")
48
+ end
49
+
50
+ end
@@ -0,0 +1,215 @@
1
+ # pure ruby sparklines module, generates PNG or ASCII
2
+ # contact thomas@fesch.at for questions
3
+ #
4
+ # strives to be somewhat compatible with sparklines lib by
5
+ # {Dan Nugent}[mailto:nugend@gmail.com] and {Geoffrey Grosenbach}[mailto:boss@topfunky.com]
6
+ #
7
+ # png creation based on http://www.whytheluckystiff.net/bumpspark/
8
+
9
+ class SparkCanvas
10
+ require 'zlib'
11
+
12
+ attr_accessor :color
13
+ attr_reader :width, :height
14
+
15
+ def initialize(width,height)
16
+ @canvas = []
17
+ @height = height
18
+ @width = width
19
+ height.times{ @canvas << [[0xFF,0xFF,0xFF]]*width }
20
+ @color = [0,0,0,0xFF] #RGBA
21
+ end
22
+
23
+ # alpha blends two colors, using the alpha given by c2
24
+ def blend(c1, c2)
25
+ (0..2).map{ |i| (c1[i]*(0xFF-c2[3]) + c2[i]*c2[3]) >> 8 }
26
+ end
27
+
28
+ # calculate a new alpha given a 0-0xFF intensity
29
+ def intensity(c,i)
30
+ [c[0],c[1],c[2],(c[3]*i) >> 8]
31
+ end
32
+
33
+ # calculate perceptive grayscale value
34
+ def grayscale(c)
35
+ (c[0]*0.3 + c[1]*0.59 + c[2]*0.11).to_i
36
+ end
37
+
38
+ def point(x,y,color = nil)
39
+ return if x<0 or y<0 or x>@width-1 or y>@height-1
40
+ @canvas[y][x] = blend(@canvas[y][x], color || @color)
41
+ end
42
+
43
+ def rectangle(x0, y0, x1, y1)
44
+ x0, y0, x1, y1 = x0.to_i, y0.to_i, x1.to_i, y1.to_i
45
+ x0, x1 = x1, x0 if x0 > x1
46
+ y0, y1 = y1, y0 if y0 > y1
47
+ x0.upto(x1) { |x| y0.upto(y1) { |y| point x, y } }
48
+ end
49
+
50
+ # draw an antialiased line
51
+ # google for "wu antialiasing"
52
+ def line(x0, y0, x1, y1)
53
+ # clean params
54
+ x0, y0, x1, y1 = x0.to_i, y0.to_i, x1.to_i, y1.to_i
55
+ y0, y1, x0, x1 = y1, y0, x1, x0 if y0>y1
56
+ sx = (dx = x1-x0) < 0 ? -1 : 1 ; dx *= sx ; dy = y1-y0
57
+
58
+ # special cases
59
+ x0.step(x1,sx) { |x| point x, y0 } and return if dy.zero?
60
+ y0.upto(y1) { |y| point x0, y } and return if dx.zero?
61
+ x0.step(x1,sx) { |x| point x, y0; y0 += 1 } and return if dx==dy
62
+
63
+ # main loops
64
+ point x0, y0
65
+
66
+ e_acc = 0
67
+ if dy > dx
68
+ e = (dx << 16) / dy
69
+ y0.upto(y1-1) do
70
+ e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xFFFF
71
+ x0 += sx if (e_acc <= e_acc_temp)
72
+ point x0, (y0 += 1), intensity(@color,(w=0xFF-(e_acc >> 8)))
73
+ point x0+sx, y0, intensity(@color,(0xFF-w))
74
+ end
75
+ point x1, y1
76
+ return
77
+ end
78
+
79
+ e = (dy << 16) / dx
80
+ x0.step(x1-sx,sx) do
81
+ e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xFFFF
82
+ y0 += 1 if (e_acc <= e_acc_temp)
83
+ point (x0 += sx), y0, intensity(@color,(w=0xFF-(e_acc >> 8)))
84
+ point x0, y0+1, intensity(@color,(0xFF-w))
85
+ end
86
+ point x1, y1
87
+ end
88
+
89
+ def polyline(arr)
90
+ (0...arr.size-1).each{ |i| line(arr[i][0], arr[i][1], arr[i+1][0], arr[i+1][1]) }
91
+ end
92
+
93
+ def to_png
94
+ header = [137, 80, 78, 71, 13, 10, 26, 10].pack("C*")
95
+ raw_data = @canvas.map { |row| [0] + row }.flatten.pack("C*")
96
+ ihdr_data = [@canvas.first.length,@canvas.length,8,2,0,0,0].pack("NNCCCCC")
97
+
98
+ header +
99
+ build_png_chunk("IHDR", ihdr_data) +
100
+ build_png_chunk("tRNS", ([ 0xFF ]*6).pack("C6")) +
101
+ build_png_chunk("IDAT", Zlib::Deflate.deflate(raw_data)) +
102
+ build_png_chunk("IEND", "")
103
+ end
104
+
105
+ def build_png_chunk(type,data)
106
+ to_check = type + data
107
+ [data.length].pack("N") + to_check + [Zlib.crc32(to_check)].pack("N")
108
+ end
109
+
110
+ def to_ascii
111
+ chr = %w(M O # + ; - .) << ' '
112
+ @canvas.map{ |r| r.map { |pt| chr[grayscale(pt) >> 5] }.to_s << "\n" }.to_s
113
+ end
114
+
115
+ end
116
+
117
+ module Spark
118
+ # normalize arr to contain values between 0..1 inclusive
119
+ def Spark.normalize( arr, type = :linear )
120
+ arr.map!{|v| Math.log(v) } if type == :logarithmic
121
+ adj, fac = arr.min, arr.max-arr.min
122
+ arr.map {|v| (v-adj).quo(fac) rescue 0 }
123
+ end
124
+
125
+ def Spark.process_options( options )
126
+ o = options.inject({}) do |o, (key, value)|
127
+ o[key.to_sym] = value ; o
128
+ end
129
+ [:height, :width, :step].each do |k|
130
+ o[k] = o[k].to_i if o.has_key?(k)
131
+ end
132
+ [:has_min, :has_max, :has_last].each do |k|
133
+ o[k] = (o[k] ? true : false) if o.has_key?(k)
134
+ end
135
+ o[:normalize] ||= :linear
136
+ o[:normalize] = o[:normalize].to_sym
137
+ o
138
+ end
139
+
140
+ def Spark.smooth( results, options = {} )
141
+ options = self.process_options(options)
142
+ o = {
143
+ :step => 2,
144
+ :height => 14,
145
+ :has_min => false,
146
+ :has_max => false
147
+ }.merge(options)
148
+
149
+ o[:width] ||= (results.size-1)*o[:step] + 5
150
+
151
+ c = SparkCanvas.new(o[:width], o[:height])
152
+
153
+ results = Spark.normalize(results, o[:normalize])
154
+ fac = c.height-5
155
+ i = -o[:step]
156
+ coords = results.map do |r|
157
+ [(i += o[:step])+2, c.height - 3 - r*fac ]
158
+ end
159
+
160
+ c.color = [0xB0, 0xB0, 0xB0, 0xFF]
161
+ c.polyline coords
162
+
163
+ if o[:has_min]
164
+ min_pt = coords[results.index(results.min)]
165
+ c.color = [0x80, 0x80, 0x00, 0x70]
166
+ c.rectangle(min_pt[0]-2, min_pt[1]-2, min_pt[0]+2, min_pt[1]+2)
167
+ end
168
+
169
+ if o[:has_max]
170
+ max_pt = coords[results.index(results.max)]
171
+ c.color = [0x00, 0x80, 0x00, 0x70]
172
+ c.rectangle(max_pt[0]-2, max_pt[1]-2, max_pt[0]+2, max_pt[1]+2)
173
+ end
174
+
175
+ if o[:has_last]
176
+ c.color = [0xFF, 0x00, 0x00, 0x70]
177
+ c.rectangle(coords.last[0]-2, coords.last[1]-2, coords.last[0]+2, coords.last[1]+2)
178
+ end
179
+
180
+ c
181
+ end
182
+
183
+ def Spark.discrete( results, options = {} )
184
+ options = self.process_options(options)
185
+ o = {
186
+ :height => 14,
187
+ :upper => 0.5,
188
+ :has_min => false,
189
+ :has_max => false
190
+ }.merge(options)
191
+
192
+ o[:width] ||= results.size*2-1
193
+
194
+ c = SparkCanvas.new(o[:width], o[:height])
195
+
196
+ results = Spark.normalize(results, o[:normalize])
197
+ fac = c.height-4
198
+
199
+ i = -2
200
+ results.each do |r|
201
+ p = c.height - 4 - r*fac
202
+ c.color = r < o[:upper] ? [0x66,0x66,0x66,0xFF] : [0xFF,0x00,0x00,0xFF]
203
+ c.line(i+=2, p, i, p+3)
204
+ end
205
+
206
+ c
207
+ end
208
+
209
+ # convenience method
210
+ def Spark.plot( results, options = {})
211
+ options = self.process_options(options)
212
+ options[:type] ||= 'smooth'
213
+ self.send(options[:type], results, options).to_png
214
+ end
215
+ end
@@ -16,6 +16,7 @@ module Watchman
16
16
  def process(dat,rcv,host)
17
17
  @host = host
18
18
  @rcv_time = rcv
19
+ CityWatch.redis.sadd "#{CityWatch.config[:prefix]}::#{host}::watchmen", self.name
19
20
  CityWatch.redis.zadd "#{CityWatch.config[:prefix]}::#{host}::#{self.name}", rcv_time, Yajl::Encoder.encode(dat.merge({:received_at => dat[:received_at]}))
20
21
  if dat[:summary]
21
22
  sum = dat[:summary].is_a?(Array) ? dat[:summary].inject({}) {|acc,k| acc[k.to_sym] = dat[k.to_sym]; acc} : dat[:summary]
@@ -43,7 +44,7 @@ module Watchman
43
44
 
44
45
  def options(*args)
45
46
  if args.count > 1
46
- return args.map {|k| opts[k]}
47
+ return args.map {|k| opts[k] }
47
48
  else
48
49
  return opts[args.first]
49
50
  end
@@ -0,0 +1,8 @@
1
+ class Redis
2
+
3
+ include Watchman
4
+
5
+ def self.data
6
+ end
7
+
8
+ end
@@ -0,0 +1,9 @@
1
+ class Twemproxy
2
+
3
+ include Watchman
4
+
5
+ def self.data
6
+
7
+ end
8
+
9
+ end
@@ -1,3 +1,3 @@
1
1
  module CityWatch
2
- VERSION = "0.6.2"
2
+ VERSION = "0.6.3"
3
3
  end
@@ -11,6 +11,9 @@ h1{
11
11
  }
12
12
  h4{
13
13
  font-size:105%;
14
+ margin:1em 0 .5em;
15
+ padding:0;
16
+ opacity:.4;
14
17
  }
15
18
  a{
16
19
  color:rgb(50,30,100);
@@ -27,4 +30,13 @@ ul,ul li{
27
30
  }
28
31
  ul li{
29
32
  list-style-type:none;
33
+ }
34
+ div.server{
35
+ background-color:rgba(0,0,0,.02);
36
+ padding:1em;
37
+ margin-bottom:1em;
38
+ }
39
+ pre{
40
+ padding:0;
41
+ margin:0;
30
42
  }
@@ -0,0 +1,6 @@
1
+ <h1>Known servers:</h1>
2
+ <ul>
3
+ <% servers.each do |server| %>
4
+ <li><%=server.summary_html%></li>
5
+ <% end %>
6
+ </ul>
@@ -0,0 +1,12 @@
1
+ <html>
2
+ <head>
3
+ <link rel="stylesheet" href="/stylesheets/city_watch.css">
4
+ <link rel="stylesheet" href="/stylesheets/default.css">
5
+ <script src="/javascripts/city_watch.js"></script>
6
+ <script src="/javascripts/highlight.pack.js"></script>
7
+ <script>hljs.initHighlightingOnLoad();</script>
8
+ </head>
9
+ <body>
10
+ <%= yield%>
11
+ </body>
12
+ </html>
File without changes
@@ -0,0 +1,10 @@
1
+ <%
2
+ sum = summary
3
+ update_time = sum.delete("received_at")
4
+ %>
5
+ <div class="server">
6
+ <a href="/<%=@host%>"><%=@host%></a>
7
+ <% sum.each do |watchman,dat| %>
8
+ <%= Reader::Watchman.new(@host,watchman).render_from_summary(dat) %>
9
+ <% end %>
10
+ </div>
@@ -0,0 +1,2 @@
1
+ <h4><%=@name%></h4>
2
+ <strong>Root FS usage: </strong><%=@current_data%>
@@ -0,0 +1,2 @@
1
+ <h4><%=@name%></h4>
2
+ <strong>Server IP: </strong><%=@current_data["ipaddress_internal"]%>
@@ -0,0 +1,2 @@
1
+ <h4><%=@name%></h4>
2
+ <strong>Days up: </strong><%=@current_data["uptime_days"]%>
File without changes
@@ -0,0 +1,2 @@
1
+ <h4><%=@name%></h4>
2
+ <pre><code><%=Yajl::Encoder.encode(@current_data,:pretty => true, :indent => " ")%></code></pre>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: city-watch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.6.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -91,6 +91,38 @@ dependencies:
91
91
  - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: mail
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: erubis
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
94
126
  - !ruby/object:Gem::Dependency
95
127
  name: yajl-ruby
96
128
  requirement: !ruby/object:Gem::Requirement
@@ -133,7 +165,6 @@ executables:
133
165
  extensions: []
134
166
  extra_rdoc_files: []
135
167
  files:
136
- - .DS_Store
137
168
  - .gitignore
138
169
  - Gemfile
139
170
  - Gemfile.lock
@@ -159,13 +190,18 @@ files:
159
190
  - lib/city_watch/commands/mpstat.rb
160
191
  - lib/city_watch/commands/netstat.rb
161
192
  - lib/city_watch/commands/ps.rb
193
+ - lib/city_watch/reader.rb
194
+ - lib/city_watch/reader/server.rb
195
+ - lib/city_watch/reader/watchman.rb
162
196
  - lib/city_watch/util/alerts.rb
163
197
  - lib/city_watch/util/collector.rb
164
198
  - lib/city_watch/util/datasets.rb
165
199
  - lib/city_watch/util/flags.rb
166
200
  - lib/city_watch/util/notifications.rb
201
+ - lib/city_watch/util/renderer.rb
167
202
  - lib/city_watch/util/rules.rb
168
203
  - lib/city_watch/util/run_command.rb
204
+ - lib/city_watch/util/spark_pr.rb
169
205
  - lib/city_watch/util/status.rb
170
206
  - lib/city_watch/util/watchman.rb
171
207
  - lib/city_watch/watchmen.rb
@@ -180,6 +216,7 @@ files:
180
216
  - lib/city_watch/watchmen/ruby_gc.rb
181
217
  - lib/city_watch/watchmen/server_load.rb
182
218
  - lib/city_watch/watchmen/sysinfo.rb
219
+ - lib/city_watch/watchmen/twemproxy.rb
183
220
  - lib/city_watch/watchmen/unicorns.rb
184
221
  - lib/city_watch/watchmen/uptime.rb
185
222
  - lib/version.rb
@@ -215,8 +252,16 @@ files:
215
252
  - static/stylesheets/vs.css
216
253
  - static/stylesheets/xcode.css
217
254
  - static/stylesheets/zenburn.css
255
+ - views/home.html.erb
256
+ - views/layouts/default.html.erb
257
+ - views/server/detail.html.erb
258
+ - views/server/summary.html.erb
259
+ - views/watchmen/DiskUsage/summary.html.erb
218
260
  - views/watchmen/SystemInfo/detail.html.erb
219
261
  - views/watchmen/SystemInfo/summary.html.erb
262
+ - views/watchmen/Uptime/summary.html.erb
263
+ - views/watchmen/detail.html.erb
264
+ - views/watchmen/summary.html.erb
220
265
  - watch_collector.ru
221
266
  - watch_commander.ru
222
267
  homepage: http://cozy.co
data/.DS_Store DELETED
Binary file