pencil 0.2.10 → 0.3.0

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.
Files changed (63) hide show
  1. data/bin/pencil +2 -6
  2. data/docs/pencil_options.md +3 -1
  3. data/lib/{config.rb → pencil/config.rb} +29 -9
  4. data/lib/pencil/config.ru +2 -0
  5. data/lib/pencil/helpers.rb +211 -0
  6. data/lib/pencil/models/base.rb +78 -0
  7. data/lib/pencil/models/dashboard.rb +124 -0
  8. data/lib/pencil/models/graph.rb +394 -0
  9. data/lib/pencil/models/host.rb +89 -0
  10. data/lib/pencil/models.rb +3 -0
  11. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_flat_30_cccccc_40x100.png +0 -0
  12. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_flat_50_5c5c5c_40x100.png +0 -0
  13. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_glass_20_333333_1x400.png +0 -0
  14. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_glass_40_000000_1x400.png +0 -0
  15. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_glass_40_ffc73d_1x400.png +0 -0
  16. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_gloss-wave_25_333333_500x100.png +0 -0
  17. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_highlight-soft_15_000000_1x100.png +0 -0
  18. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_highlight-soft_80_eeeeee_1x100.png +0 -0
  19. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-bg_inset-soft_30_ff4e0a_1x100.png +0 -0
  20. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-icons_222222_256x240.png +0 -0
  21. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-icons_4b8e0b_256x240.png +0 -0
  22. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-icons_a83300_256x240.png +0 -0
  23. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-icons_cccccc_256x240.png +0 -0
  24. data/lib/{public → pencil/public}/css/jquery_themes/pencil/images/ui-icons_ffffff_256x240.png +0 -0
  25. data/lib/{public → pencil/public}/css/jquery_themes/pencil/jquery-ui-1.8.16.custom.css +0 -0
  26. data/lib/{public → pencil/public}/css/thedoc.css +1 -0
  27. data/lib/{public → pencil/public}/favicon.ico +0 -0
  28. data/lib/{public → pencil/public}/js/cufon.js +0 -0
  29. data/lib/{public → pencil/public}/js/gotham.font.js +0 -0
  30. data/lib/{public → pencil/public}/js/jquery-1.6.2.min.js +0 -0
  31. data/lib/{public → pencil/public}/js/jquery-ui-1.8.16.custom.min.js +0 -0
  32. data/lib/{public → pencil/public}/js/pencil.js +0 -0
  33. data/lib/{public → pencil/public}/js/product-design.font.js +0 -0
  34. data/lib/pencil/public/style.css +307 -0
  35. data/lib/{rubyfixes.rb → pencil/rubyfixes.rb} +0 -0
  36. data/lib/pencil/version.rb +3 -0
  37. data/lib/{views → pencil/views}/cluster.erb +1 -1
  38. data/lib/{views → pencil/views}/dash-cluster-zoom.erb +2 -2
  39. data/lib/{views → pencil/views}/dash-cluster.erb +1 -1
  40. data/lib/{views → pencil/views}/dash-global-zoom.erb +2 -2
  41. data/lib/{views → pencil/views}/dash-global.erb +1 -1
  42. data/lib/{views → pencil/views}/global.erb +1 -1
  43. data/lib/{views → pencil/views}/host.erb +1 -1
  44. data/lib/{views → pencil/views}/layout.erb +2 -2
  45. data/lib/{views → pencil/views}/partials/cluster_selector.erb +0 -0
  46. data/lib/{views → pencil/views}/partials/cluster_switcher.erb +0 -0
  47. data/lib/{views → pencil/views}/partials/dash_switcher.erb +0 -0
  48. data/lib/{views → pencil/views}/partials/graph_switcher.erb +0 -0
  49. data/lib/{views → pencil/views}/partials/hosts_selector.erb +0 -0
  50. data/lib/{views → pencil/views}/partials/input_boxes.erb +0 -0
  51. data/lib/{views → pencil/views}/partials/shortcuts.erb +0 -0
  52. data/lib/{dash.rb → pencil.rb} +14 -17
  53. metadata +57 -58
  54. data/VERSION.rb +0 -1
  55. data/lib/config.ru +0 -2
  56. data/lib/helpers.rb +0 -209
  57. data/lib/models/base.rb +0 -73
  58. data/lib/models/dashboard.rb +0 -111
  59. data/lib/models/graph.rb +0 -367
  60. data/lib/models/host.rb +0 -87
  61. data/lib/models.rb +0 -3
  62. data/lib/namespace.rb +0 -4
  63. data/lib/public/style.css +0 -97
data/lib/models/graph.rb DELETED
@@ -1,367 +0,0 @@
1
- require "models/base"
2
- require "uri"
3
-
4
- module Dash::Models
5
- class Graph < Base
6
- def initialize(name, params={})
7
- super
8
-
9
- @params["hosts"] ||= ["*"]
10
- @params["title"] ||= name
11
-
12
- if not @params["targets"]
13
- raise ArgumentError, "graph #{name} needs a 'targets' map"
14
- end
15
- end
16
-
17
- # fixme parameters in general
18
- def width(opts={})
19
- opts["width"] || @params[:url_opts][:width]
20
- end
21
-
22
- # translate STR into graphite-speak for applying FUNC to STR
23
- # graphite functions take zero or one argument
24
- # pass passes STR through, instead of raising an error if FUNC isn't
25
- # recognized
26
- def translate(func, str, arg=nil, pass=false)
27
- # puts "calling translate"
28
- # puts "func => #{func}"
29
- # puts "str => #{str}"
30
- # puts "arg => #{arg}"
31
- # procs and lambdas don't support default arguments in 1.8, so I have to
32
- # do this
33
- z = lambda { |*body| "#{func}(#{body[0]||str})" }
34
- y = "#{str}, #{arg}"
35
- x = lambda { z.call(y) }
36
-
37
- return \
38
- case func.to_s
39
- # comb
40
- when "sumSeries", "averageSeries", "minSeries", "maxSeries", "group"
41
- z.call
42
- # transform
43
- when "scale", "offset"
44
- # perhaps .to_f
45
- x.call
46
- when "derivative", "integral"
47
- z.call
48
- when "nonNegativeDerivative"
49
- z.call("#{str}#{', ' + arg if arg}")
50
- when "log", "timeShift", "summarize", "hitcount",
51
- # calculate
52
- "movingAverage", "stdev", "asPercent"
53
- x.call
54
- when "diffSeries", "divideSeries"
55
- z.call
56
- # filters
57
- when "mostDeviant"
58
- z.call("#{arg}, #{str}")
59
- when "highestCurrent", "lowestCurrent", "nPercentile", "currentAbove",
60
- "currentBelow", "highestAverage", "lowestAverage", "averageAbove",
61
- "averageBelow", "maximumAbove", "maximumBelow"
62
- x.call
63
- when "sortByMaxima", "minimalist"
64
- z.call
65
- when "limit", "exclude"
66
- x.call
67
- when "key", "alias"
68
- "alias(#{str}, \"#{arg}\")"
69
- when "cumulative", "drawAsInfinite"
70
- z.call
71
- when "lineWidth"
72
- x.call
73
- when "dashed", "keepLastValue"
74
- z.call
75
- when "substr", "threshold"
76
- x.call
77
- when "color"
78
- @params[:use_color] ? "color(#{str}, \"#{arg}\")" : str
79
- else
80
- raise "BAD FUNC #{func}" unless pass
81
- str
82
- end
83
- end
84
-
85
- # inner means we're dealing with a complex key; @params will be applied
86
- def handle_metric(name, opts, inner=false)
87
- ret = name.dup
88
- if inner
89
- @params.each do |k, v|
90
- ret = translate(k, ret, v, true)
91
- end
92
- end
93
- (opts||{}).each do |k, v|
94
- #puts "#{k} => #{v}"
95
- ret = translate(k, ret, v)
96
- end
97
- ret
98
- end
99
-
100
- def render_url(hosts, clusters, opts={})
101
- opts = {
102
- :sum => nil,
103
- :title => @params["title"],
104
- }.merge(opts)
105
-
106
- if ! [:global, :cluster, nil].member?(opts[:sum])
107
- raise ArgumentError, "render graph #{name}: invalid :sum - #{opts[:sum]}"
108
- end
109
-
110
- sym_hash = {}
111
- (opts[:dynamic_url_opts]||[]).each do |k,v|
112
- sym_hash[k.to_sym] = v
113
- end
114
-
115
- # fixme key checking may be necessary
116
- url_opts = {
117
- :title => opts[:title],
118
- }.merge(@params[:url_opts]).merge(sym_hash)
119
-
120
- url_opts[:from] = url_opts.delete(:stime) || ""
121
- url_opts[:until] = url_opts.delete(:etime) || ""
122
- url_opts.delete(:start)
123
- url_opts.delete(:duration)
124
-
125
- graphite_opts = [ "vtitle", "yMin", "yMax", "lineWidth", "areaMode",
126
- "template", "lineMode", "bgcolor", "graphOnly", "hideAxes", "hideGrid",
127
- "hideLegend", "fgcolor", "fontSize", "fontName", "fontItalic",
128
- "fontBold" ]
129
-
130
- @params.each do |k, v|
131
- if graphite_opts.member?(k)
132
- url_opts[k.to_sym] = v
133
- end
134
- end
135
-
136
- target = []
137
- colors = []
138
- #FIXME code duplication
139
- if opts[:sum] == :global
140
- @params["targets"].each do |stat_name, opts|
141
- z = Marshal.load(Marshal.dump(opts))
142
- z[:key] ||= stat_name
143
- #######################
144
- if stat_name.instance_of?(Array)
145
- metric = stat_name.map do |m|
146
- mm = compose_metric(m.keys.first,
147
- "{#{clusters.to_a.join(',')}}",
148
- "{#{hosts.to_a.join(',')}}")
149
-
150
- if z.keys.member?(:divideSeries)
151
- handle_metric(translate(:sumSeries, mm),
152
- m[m.keys.first], true)
153
- else
154
- handle_metric(mm, m[m.keys.first], true)
155
- end
156
- end.join(",")
157
- else
158
- metric = compose_metric(stat_name,
159
- "{#{clusters.to_a.join(',')}}",
160
- "{#{hosts.to_a.join(',')}}")
161
- metric = handle_metric(metric, {}, true)
162
- end
163
- #######################
164
- z[:key] = "global #{z[:key]}"
165
- # target << handle_metric(translate(:sumSeries, metric), z)
166
-
167
- if z.keys.member?(:divideSeries) # special case
168
- # apply divideSeries, sumSeries then other options
169
- res = translate(:divideSeries, metric)
170
- res = translate(:sumSeries, res)
171
- z.delete(:divideSeries)
172
- h = YAML::Omap.new
173
- z.each { |k,v| h[k] = v unless k == :divideSeries }
174
- target << handle_metric(res, h)
175
- else
176
- target << handle_metric(translate(:sumSeries, metric), z)
177
- end
178
- if !@params[:use_color] ||
179
- (!z[:color] && @params[:use_color])
180
- colors << next_color(colors, z[:color])
181
- end
182
- end # @params["targets"].each
183
- elsif opts[:sum] == :cluster # one line per cluster/metric
184
- clusters.each do |cluster|
185
- @params["targets"].each do |stat_name, opts|
186
- z = Marshal.load(Marshal.dump(opts))
187
- metrics = []
188
- #######################
189
- h = "{#{hosts.to_a.join(',')}}"
190
- if stat_name.instance_of?(Array)
191
- metrics << stat_name.map do |m|
192
- mm = compose_metric(m.keys.first, cluster, h)
193
- # note: we take the ratio of the sums in this case, instead of
194
- # the sums of the ratios
195
- if z.keys.member?(:divideSeries)
196
- # divideSeries is picky about the number of series given as
197
- # arguments, so sum them in this case
198
- handle_metric(translate(:sumSeries, mm),
199
- m[m.keys.first], true)
200
- else
201
- handle_metric(mm, m[m.keys.first], true)
202
- end
203
- end.join(",")
204
- else
205
- metrics << handle_metric(compose_metric(stat_name,
206
- cluster, h), {}, true)
207
- end
208
- #######################
209
- z[:key] = "#{cluster} #{z[:key]}"
210
-
211
- if z.keys.member?(:divideSeries) # special case
212
- # apply divideSeries, sumSeries then other options
213
- res = translate(:divideSeries, metrics.join(','))
214
- res = translate(:sumSeries, res)
215
- z.delete(:divideSeries)
216
- h = YAML::Omap.new
217
- z.each { |k,v| h[k] = v unless k == :divideSeries }
218
- target << handle_metric(res, h)
219
- else
220
- target << handle_metric(translate(:sumSeries,
221
- metrics.join(',')), z)
222
- end
223
-
224
- if !@params[:use_color] || (!z[:color] && @params[:use_color])
225
- colors << next_color(colors, z[:color])
226
- end
227
- end # metrics.each
228
- end # clusters.each
229
- else # one line per {metric,host,colo}
230
- @params["targets"].each do |stat_name, opts|
231
- clusters.each do |cluster|
232
- hosts.each do |host|
233
- label = "#{host} #{opts[:key]}"
234
- #################
235
- if stat_name.instance_of?(Array)
236
- metric = stat_name.map do |m|
237
- mm = compose_metric(m.keys.first, cluster, host)
238
- handle_metric(mm, m[m.keys.first], true)
239
- end.join(",")
240
- else
241
- metric = handle_metric(compose_metric(stat_name, cluster, host), {}, true)
242
- end
243
- #################
244
-
245
- if label =~ /\*/
246
- # for this particular type of graph, don't display a legend,
247
- # and color with abandon
248
- url_opts[:hideLegend] = true
249
- z = Marshal.load(Marshal.dump(opts))
250
- z.delete(:color)
251
- # fixme proper labeling... maybe
252
- # With wildcards let graphite construct the legend (or not).
253
- # Since we're handling wildcards we don't know how many
254
- # hosts will match, so just put in the default color list.
255
- # technically we do know, so this can be fixed
256
- z.delete(:key)
257
- target << handle_metric(metric, z)
258
- colors.concat(@params[:default_colors]) if colors.empty?
259
- else
260
- z = Marshal.load(Marshal.dump(opts))
261
- z[:key] = "#{host}/#{cluster} #{z[:key]}"
262
- target << handle_metric(metric, z)
263
- if !@params[:use_color] ||
264
- (!opts[:color] && @params[:use_color])
265
- colors << next_color(colors, opts[:color])
266
- end
267
- end
268
- end
269
- end
270
- end # @params["targets"].each
271
- end # if opts[:sum]
272
-
273
- url_opts[:target] = target
274
- url_opts[:colorList] = colors.join(",")
275
-
276
- url = URI.join(@params[:graphite_url], "/render/?").to_s
277
- url_parts = []
278
- url_opts.each do |k, v|
279
- [v].flatten.each do |v|
280
- url_parts << "#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}"
281
- end
282
- end
283
- url += url_parts.join("&")
284
- return url
285
- end
286
-
287
- # return an array of all metrics matching the specifications in
288
- # @params["targets"]
289
- # metrics are arrays of fields (once delimited by periods)
290
- def expand
291
- url = URI.join(@params[:graphite_url], "/metrics/expand/?query=").to_s
292
- metrics = []
293
-
294
- @params["targets"].each do |metric|
295
- if metric.first.instance_of?(Array)
296
- metric.first.each do |m|
297
- composed = compose_metric(m.first.first, "*", "*")
298
- query = open("#{url}#{composed}").read
299
- metrics << JSON.parse(query)["results"]
300
- end
301
- else
302
- composed = compose_metric(metric.first.first, "*", "*")
303
- query = open("#{url}#{composed}").read
304
- metrics << JSON.parse(query)["results"]
305
- end
306
- end
307
-
308
- return metrics.flatten.map { |x| x.split(".") }
309
- end
310
-
311
- def hosts_clusters
312
- metrics = expand
313
- clusters = Set.new
314
-
315
- # find the indicies of the clusters and hosts
316
- f = @params[:metric_format].dup.split("%m")
317
- first = f.first.split(".")
318
- last = f.last.split(".")
319
- ci = hi = nil
320
- first.each_with_index do |v, i|
321
- ci = i if v.match("%c")
322
- hi = i if v.match("%h")
323
- end
324
- unless ci && hi
325
- last.reverse.each_with_index do |v, i|
326
- ci = -1 -i if v.match("%c")
327
- hi = -1 -i if v.match("%h")
328
- end
329
- end
330
- hosts = metrics.map do |m|
331
- Host.new(m[hi], m[ci], @params)
332
- end.uniq
333
-
334
- # filter by what matches the graph definition
335
- hosts = hosts.select { |h| h.multi_match(@params["hosts"]) }
336
- hosts.each { |h| clusters << h.cluster }
337
-
338
- return hosts, clusters
339
- end
340
-
341
- private
342
- def next_color(colors, preferred_color=nil)
343
- default_colors = @params[:default_colors].clone
344
-
345
- if preferred_color and !colors.member?(preferred_color)
346
- return preferred_color
347
- end
348
-
349
- if preferred_color and ! default_colors.member?(preferred_color)
350
- default_colors << preferred_color
351
- end
352
-
353
- weights = Hash.new(0)
354
- colors.each do |c|
355
- weights[c] += 1
356
- end
357
-
358
- i = 0
359
- loop do
360
- default_colors.each do |c|
361
- return c if weights[c] == i
362
- end
363
- i += 1
364
- end
365
- end
366
- end # Dash::Models::Graph
367
- end # Dash::Models
data/lib/models/host.rb DELETED
@@ -1,87 +0,0 @@
1
- require "models/base"
2
- require "models/graph"
3
-
4
- module Dash::Models
5
- class Host < Base
6
- attr_accessor :graphs
7
- attr_reader :cluster
8
-
9
- def initialize(name, cluster, params={})
10
- super(name, params)
11
- @cluster = cluster
12
- # hack for the case where colo{1,2}.host1 both exist
13
- @@objects[self.class.to_s].delete(name)
14
- @@objects[self.class.to_s]["#{cluster}#{name}"] = self
15
-
16
- @graphs = []
17
- Graph.each do |graph_name, graph|
18
- graph["hosts"].each do |h|
19
- if match(h)
20
- @graphs << graph
21
- break
22
- end
23
- end # graph["hosts"].each
24
- end # Graph.each
25
- end
26
-
27
- def self.find(name)
28
- return @@objects[self.name][key] rescue []
29
- end
30
-
31
- def key
32
- "#{@cluster}#{@name}"
33
- end
34
-
35
- def eql?(other)
36
- key == other.key
37
- end
38
-
39
- def ==(other)
40
- key == other.key
41
- end
42
-
43
- def <=>(other)
44
- if @params[:host_sort] == "builtin"
45
- return key <=> other.key
46
- elsif @params[:host_sort] == "numeric"
47
- regex = /\d+/
48
- match = @name.match(regex)
49
- match2 = other.name.match(regex)
50
- if match.pre_match != match2.pre_match
51
- return match.pre_match <=> match2.pre_match
52
- else
53
- return match[0].to_i <=> match2[0].to_i
54
- end
55
- else
56
- # http://www.bofh.org.uk/2007/12/16/comprehensible-sorting-in-ruby
57
- sensible = lambda do |k|
58
- k.to_s.split(
59
- /((?:(?:^|\s)[-+])?(?:\.\d+|\d+(?:\.\d+?(?:[eE]\d+)?(?:$|(?![eE\.])))?))/ms
60
- ).map { |v| Float(v) rescue v.downcase }
61
- end
62
- return sensible.call(self) <=> sensible.call(other)
63
- end
64
- end
65
-
66
- def hash
67
- key.hash
68
- end
69
-
70
- def self.find_by_name_and_cluster(name, cluster)
71
- Host.each do |host_name, host|
72
- next unless host.name == name
73
- return host if host.cluster == cluster
74
- end
75
- return nil
76
- end
77
-
78
- def self.find_by_cluster(cluster)
79
- ret = []
80
- Host.each do |name, host|
81
- ret << host if host.cluster == cluster
82
- end
83
- return ret
84
- end
85
-
86
- end # Dash::Models::Host
87
- end # Dash::Models
data/lib/models.rb DELETED
@@ -1,3 +0,0 @@
1
- require "models/dashboard"
2
- require "models/graph"
3
- require "models/host"
data/lib/namespace.rb DELETED
@@ -1,4 +0,0 @@
1
- module Dash
2
- module Models
3
- end
4
- end
data/lib/public/style.css DELETED
@@ -1,97 +0,0 @@
1
- h2 {
2
- color: #ADFF2F;
3
- }
4
-
5
- h3 {
6
- color: #32cd32;
7
- }
8
-
9
- .graphtitle {
10
- text-shadow: 1px 1px 1px #333333;
11
- }
12
-
13
- .dashlinks {
14
- color: green;
15
- font-size: small;
16
- }
17
-
18
- a {
19
- color: #15317E;
20
- }
21
-
22
- a:visited {
23
- color: #15317E;
24
- }
25
-
26
- .graphsection {
27
- border: dotted 2px #333333;
28
- width: 500;
29
- margin-left: 5px;
30
- margin-top: 10px;
31
- }
32
-
33
- div#wrap {
34
- width: 100%;
35
- margin-top: 10px;
36
- margin-bottom: 10px;
37
- margin-left: auto;
38
- margin-right: auto;
39
- padding: 0px;
40
- }
41
-
42
- div#header {
43
- padding: 15px;
44
- margin: 0px;
45
- margin-left: 23%;
46
- }
47
-
48
- div#nav {
49
- width: 20%;
50
- padding: 10px;
51
- margin-top: 1px;
52
- float: left;
53
- color: green;
54
- }
55
-
56
- div#main {
57
- margin-left: 23%;
58
- margin-top: 1px;
59
- padding: 10px;
60
- color: #4CBB17;
61
- }
62
-
63
- div#footer {
64
- padding: 15px;
65
- margin: 0px;
66
- border-top: thin solid #808080;
67
- }
68
-
69
- table {
70
- color: green;
71
- }
72
-
73
- input {
74
- border: solid 1px #15317E;
75
- color: green;
76
- background-color: #1f1f1f;
77
- font-size: small;
78
- }
79
-
80
- .invisible {
81
- display: none;
82
- }
83
-
84
- select {
85
- border: solid 1px #15317E;
86
- color: green;
87
- background-color: #1f1f1f;
88
- }
89
-
90
- .select2 {
91
- color: #4CBB17;
92
- font-size: large;
93
- }
94
-
95
- .error {
96
- color: red;
97
- }