pencil 0.2.2 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
data/bin/pencil CHANGED
@@ -1,6 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  $: << File.dirname($0) + "/../lib"
3
3
 
4
+ require File.join(File.dirname(__FILE__), "..", "VERSION")
4
5
  require "dash"
5
6
 
7
+ STDOUT.puts "pencil v#{PENCIL_VERSION} on port #{Dash::App.port}, PID #{$$}"
8
+
6
9
  Rack::Handler::Mongrel.run(Dash::App, :Port => Dash::App.port)
@@ -77,6 +77,23 @@ These are options that go under the :config key in pencil configuration files.
77
77
  How many seconds before Time.now an end time is considered to still be 'now',
78
78
  for the purposes of adding meta-refresh and displaying time intervals.
79
79
 
80
+ * :use_color: [Boolean, optional, default false]
81
+
82
+ In graphite 0.9.8 and earlier coloring of targets sucks. Coloring is not done
83
+ on a per-target basis, but rather by an additional parameter
84
+ "colorList". Pencil handles colors by appending a target's color parameter
85
+ (or a suitable default) onto this list. Targets are processed in order and
86
+ consume the first color available in colorList.
87
+
88
+ This sucks because nonexistent targets occasionally produced by pencil don't
89
+ consume their associated color, which screws up the coloring of every
90
+ subsequent metric.
91
+
92
+ The latest Graphite trunk supports color as a property of a target, set with
93
+ color(target, "COLOR"). If you are using a new graphite and want to take
94
+ advantage of this more accurate coloring technique set :use_color to true,
95
+ and bask in the glory of color correctness.
96
+
80
97
  ## <a name="gopts"/> Graph-level Options
81
98
  This is a list of the supported graph-level options for pencil, which
82
99
  correspond to request(image)-level options for graphite. These options are
@@ -103,11 +120,25 @@ minor annotations:
103
120
 
104
121
  ## Target-level Options
105
122
  This is a list of the supported target-level options for pencil. These are
106
- mosly a list of transformations graphite supports, including summation and
123
+ mostly a list of transformations graphite supports, including summation and
107
124
  scaling of metrics. You can apply them to individual metrics, or lists of
108
125
  metrics. See the example configs for how this works. Also see the graphite
109
126
  composer for the effects of these options, many of which are untested.
110
127
 
128
+ A note on the divideSeries case: for aggregate graphs, pencil takes the ratio
129
+ of the sums, as opposed to the sums of the ratios, for ease of implementation
130
+ and because it makes some sense. If you're not a fan of 80k URLs then you will
131
+ agree. divideSeries should only ever be used with two targets, like this:
132
+
133
+ ? - stats.timers.mysql.innodb.threads_active_read.mean:
134
+ - stats.timers.mysql.innodb.threads_total_read.mean:
135
+ :
136
+ !omap
137
+ - :divideSeries:
138
+
139
+ (Notice the omap, as opposed to a hash, to impose an order in which options are
140
+ applied)
141
+
111
142
  ### Combinations
112
143
  These functions take an arbitrary number of targets (usually simple metrics)
113
144
  for arguments.
@@ -139,7 +170,7 @@ for arguments.
139
170
  * stdev
140
171
  * asPercent
141
172
  * diffSeries
142
- * ratio
173
+ * divideSeries (NOTE: takes exactly 1 series as divisor)
143
174
 
144
175
  ### Filters
145
176
  Most of these options take a single argument.
@@ -172,5 +203,7 @@ Most of these options take a single argument.
172
203
  * threshold
173
204
  * color
174
205
 
175
- Note: key and color are interpreted differently from the other options, which
176
- are more simply translated.
206
+ Note: key is interpreted differently from the other options, which are more
207
+ simply translated.
208
+
209
+ color's interpretation depends on whether :use_color is set.
@@ -4,12 +4,30 @@
4
4
  title: syncstorage_overview
5
5
  graphs:
6
6
  - syncstorage_qps:
7
- # hosts: ["sync*_web"]
8
7
  - syncstorage_401:
9
8
  - syncstorage_exceptions:
10
9
  - cpu_usage:
10
+ - memory_usage:
11
11
  - network_traffic:
12
12
  hosts: ["sync*_web", "wp-web*"]
13
+ syncreg:
14
+ title: syncreg_overview
15
+ graphs:
16
+ - syncreg_qps:
17
+ - syncreg_exceptions:
18
+ - cpu_usage:
19
+ - memory_usage:
20
+ - network_traffic:
21
+ hosts: ["wp-reg*"]
22
+ syncsreg:
23
+ title: syncsreg_overview
24
+ graphs:
25
+ - syncsreg_qps:
26
+ - syncsreg_exceptions:
27
+ - cpu_usage:
28
+ - memory_usage:
29
+ - network_traffic:
30
+ hosts: ["wp-sreg*"]
13
31
  ldap_slaves:
14
32
  title: LDAP slaves
15
33
  graphs:
@@ -34,3 +52,9 @@
34
52
  - cpu_usage:
35
53
  - network_traffic:
36
54
  hosts: ["sync*_db", "wp-db*"]
55
+ sync_node_alloc:
56
+ title: sync node allocation
57
+ graphs:
58
+ - sync_node_alloc:
59
+ - sync_node_actives:
60
+ hosts: ["wp-adm01"]
data/examples/graphs.yml CHANGED
@@ -15,12 +15,6 @@
15
15
  syncstorage.request_rate.404:
16
16
  :key: 404
17
17
  :color: purple
18
- syncstorage.request_rate.500:
19
- :key: 500
20
- :color: yellow
21
- syncstorage.request_rate.502:
22
- :key: 502
23
- :color: "#cc0000"
24
18
  syncstorage.request_rate.503:
25
19
  :key: 503
26
20
  :color: red
@@ -31,7 +25,7 @@
31
25
  title: "syncstorage 401s"
32
26
  targets:
33
27
  syncstorage.request_rate.401:
34
- :key: 200
28
+ :key: 401
35
29
  :color: brown
36
30
  hosts: ["sync*_web", "test*_web", "wp-web*"]
37
31
 
@@ -43,6 +37,64 @@
43
37
  :color: red
44
38
  hosts: ["sync*_web", "test*_web", "wp-web*"]
45
39
 
40
+ syncreg_qps:
41
+ title: "syncreg qps"
42
+ targets:
43
+ syncreg.request_rate.200:
44
+ :key: 200
45
+ :color: green
46
+ syncreg.request_rate.302:
47
+ :key: 302
48
+ :color: blue
49
+ syncreg.request_rate.401:
50
+ :key: 401
51
+ :color: brown
52
+ syncreg.request_rate.404:
53
+ :key: 404
54
+ :color: purple
55
+ syncreg.request_rate.503:
56
+ :key: 503
57
+ :color: red
58
+ areaMode: stacked
59
+ hosts: ["wp-reg*"]
60
+
61
+ syncreg_exceptions:
62
+ title: "syncreg exceptions"
63
+ targets:
64
+ syncreg.log.exception:
65
+ :key: exceptions/s
66
+ :color: red
67
+ hosts: ["wp-reg*"]
68
+
69
+ syncsreg_qps:
70
+ title: "syncsreg qps"
71
+ targets:
72
+ syncsreg.request_rate.200:
73
+ :key: 200
74
+ :color: green
75
+ syncsreg.request_rate.302:
76
+ :key: 302
77
+ :color: blue
78
+ syncsreg.request_rate.401:
79
+ :key: 401
80
+ :color: brown
81
+ syncsreg.request_rate.404:
82
+ :key: 404
83
+ :color: purple
84
+ syncsreg.request_rate.503:
85
+ :key: 503
86
+ :color: red
87
+ areaMode: stacked
88
+ hosts: ["wp-sreg*"]
89
+
90
+ syncsreg_exceptions:
91
+ title: "syncsreg exceptions"
92
+ targets:
93
+ syncsreg.log.exception:
94
+ :key: exceptions/s
95
+ :color: red
96
+ hosts: ["wp-sreg*"]
97
+
46
98
  cpu_usage:
47
99
  title: "cpu usage"
48
100
  targets:
@@ -145,3 +197,27 @@
145
197
  bgcolor: "#AFFFFF"
146
198
  vtitle: tvtnolgrf
147
199
  lineWidth: 20
200
+
201
+ sync_node_alloc:
202
+ title: "node alloc rate"
203
+ targets:
204
+ sync.users.alloc_rate.scl2:
205
+ :key: scl2 alloc/s
206
+ :color: green
207
+ sync.users.alloc_rate.phx1:
208
+ :key: phx1 alloc/s
209
+ :color: blue
210
+
211
+ hosts: ["wp-adm01"]
212
+
213
+ sync_node_actives:
214
+ title: "node actives"
215
+ targets:
216
+ sync.users.active.scl2:
217
+ :key: scl2 actives
218
+ :color: green
219
+ sync.users.active.phx1:
220
+ :key: phx1 actives
221
+ :color: blue
222
+
223
+ hosts: ["wp-adm01"]
data/examples/pencil.yml CHANGED
@@ -19,3 +19,6 @@
19
19
  :date_format: "%X %x" # strftime
20
20
  :metric_format: "%m.%c.%h" #%m metric, %c cluster, %h host
21
21
  :now_threshold: 300 # Time.at(Time.now - now_threshold) considered Time.now
22
+
23
+ # set this if you have a new graphite that supports color(series, "color")
24
+ # :use_color: true
data/lib/dash.rb CHANGED
@@ -15,6 +15,7 @@ require "yaml"
15
15
  require "chronic"
16
16
  require "chronic_duration"
17
17
  require "optparse"
18
+ require "rubyfixes"
18
19
 
19
20
  # fixme style.css isn't actually cached, you need to set something up with
20
21
  # rack to cache static files
@@ -32,6 +33,7 @@ module Dash
32
33
  use Rack::Session::Cookie, :expire_after => 126227700 # 4 years
33
34
  set :root, File.dirname(__FILE__)
34
35
  set :static, true
36
+ set :logging, true
35
37
 
36
38
  def initialize(settings={})
37
39
  super
@@ -73,7 +75,11 @@ module Dash
73
75
 
74
76
  get %r[^/(dash/?)?$] do
75
77
  @no_graphs = true
76
- redirect '/dash/global'
78
+ if settings.config.clusters.size == 1
79
+ redirect "/dash/#{settings.config.clusters.first}"
80
+ else
81
+ redirect '/dash/global'
82
+ end
77
83
  end
78
84
 
79
85
  get '/dash/:cluster/:dashboard/:zoom/?' do
data/lib/helpers.rb CHANGED
@@ -188,7 +188,7 @@ module Dash::Helpers
188
188
 
189
189
  def permalink
190
190
  return "" unless @stime && @duration
191
- format = "%x %X" # chronic understands this
191
+ format = "%F %T" # chronic REALLY understands this
192
192
  url = request.path + "?"
193
193
  url << "&start=#{@stime.strftime(format)}"
194
194
  url << "&duration=#{ChronicDuration.output(@duration)}"
@@ -15,7 +15,7 @@ module Dash::Models
15
15
  @graph_opts = {}
16
16
  params["graphs"].each do |name|
17
17
  # graphs map to option hashes
18
- if name.instance_of?(Hash)
18
+ if name.instance_of?(Hash) # could be YAML::Omap
19
19
  g = Graph.find(name.keys.first) # should only be one key
20
20
  @graph_opts[g] = name[name.keys.first]||{}
21
21
  else
data/lib/models/graph.rb CHANGED
@@ -51,7 +51,7 @@ module Dash::Models
51
51
  # calculate
52
52
  "movingAverage", "stdev", "asPercent"
53
53
  x.call
54
- when "diffSeries", "ratio"
54
+ when "diffSeries", "divideSeries"
55
55
  z.call
56
56
  # filters
57
57
  when "mostDeviant"
@@ -75,7 +75,7 @@ module Dash::Models
75
75
  when "substr", "threshold"
76
76
  x.call
77
77
  when "color"
78
- str #color is handled elsewhere
78
+ @params[:use_color] ? "color(#{str}, \"#{arg}\")" : str
79
79
  else
80
80
  raise "BAD FUNC #{func}" unless pass
81
81
  str
@@ -83,7 +83,6 @@ module Dash::Models
83
83
  end
84
84
 
85
85
  # inner means we're dealing with a complex key; @params will be applied
86
- # later on
87
86
  def handle_metric(name, opts, inner=false)
88
87
  ret = name.dup
89
88
  if inner
@@ -136,18 +135,24 @@ module Dash::Models
136
135
 
137
136
  target = []
138
137
  colors = []
139
- #fixme code duplication
138
+ #FIXME code duplication
140
139
  if opts[:sum] == :global
141
140
  @params["targets"].each do |stat_name, opts|
142
- z = opts.dup
143
- # opts['key'] ||= stat_name
141
+ z = Marshal.load(Marshal.dump(opts))
144
142
  z[:key] ||= stat_name
145
143
  #######################
146
144
  if stat_name.instance_of?(Array)
147
145
  metric = stat_name.map do |m|
148
- mm = compose_metric(m.keys.first, clusters.to_a.join(","),
149
- hosts.to_a.join(","))
150
- handle_metric(mm, m[m.keys.first], true)
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
151
156
  end.join(",")
152
157
  else
153
158
  metric = "#{stat_name}.{#{clusters.to_a.join(',')}}" +
@@ -156,30 +161,68 @@ module Dash::Models
156
161
  end
157
162
  #######################
158
163
  z[:key] = "global #{z[:key]}"
159
- target << handle_metric("sumSeries(#{metric})", z)
160
- colors << next_color(colors, z[:color])
164
+ # target << handle_metric(translate(:sumSeries, metric), z)
165
+
166
+ if z.keys.member?(:divideSeries) # special case
167
+ # apply divideSeries, sumSeries then other options
168
+ res = translate(:divideSeries, metric)
169
+ res = translate(:sumSeries, res)
170
+ z.delete(:divideSeries)
171
+ h = YAML::Omap.new
172
+ z.each { |k,v| h[k] = v unless k == :divideSeries }
173
+ target << handle_metric(res, h)
174
+ else
175
+ target << handle_metric(translate(:sumSeries, metric), z)
176
+ end
177
+ if !@params[:use_color] ||
178
+ (!z[:color] && @params[:use_color])
179
+ colors << next_color(colors, z[:color])
180
+ end
161
181
  end # @params["targets"].each
162
182
  elsif opts[:sum] == :cluster # one line per cluster/metric
163
183
  clusters.each do |cluster|
164
184
  @params["targets"].each do |stat_name, opts|
165
- z = opts.dup
185
+ z = Marshal.load(Marshal.dump(opts))
166
186
  metrics = []
167
- hosts.each do |host|
168
- #######################
169
- if stat_name.instance_of?(Array)
170
- metrics << stat_name.map do |m|
171
- mm = compose_metric(m.keys.first, cluster, host)
187
+ #######################
188
+ h = "{#{hosts.to_a.join(',')}}"
189
+ if stat_name.instance_of?(Array)
190
+ metrics << stat_name.map do |m|
191
+ mm = compose_metric(m.keys.first, cluster, h)
192
+ # note: we take the ratio of the sums in this case, instead of
193
+ # the sums of the ratios
194
+ if z.keys.member?(:divideSeries)
195
+ # divideSeries is picky about the number of series given as
196
+ # arguments, so sum them in this case
197
+ handle_metric(translate(:sumSeries, mm),
198
+ m[m.keys.first], true)
199
+ else
172
200
  handle_metric(mm, m[m.keys.first], true)
173
- end.join(",")
174
- else
175
- metrics << handle_metric(compose_metric(stat_name, cluster, host), {}, true)
176
- end
177
- #######################
178
- end # hosts.each
179
-
201
+ end
202
+ end.join(",")
203
+ else
204
+ metrics << handle_metric(compose_metric(stat_name,
205
+ cluster, h), {}, true)
206
+ end
207
+ #######################
180
208
  z[:key] = "#{cluster} #{z[:key]}"
181
- target << handle_metric("sumSeries(#{metrics.join(',')})", z)
182
- colors << next_color(colors, z[:color])
209
+
210
+ if z.keys.member?(:divideSeries) # special case
211
+ # apply divideSeries, sumSeries then other options
212
+ res = translate(:divideSeries, metrics.join(','))
213
+ res = translate(:sumSeries, res)
214
+ z.delete(:divideSeries)
215
+ h = YAML::Omap.new
216
+ z.each { |k,v| h[k] = v unless k == :divideSeries }
217
+ target << handle_metric(res, h)
218
+ else
219
+ target << handle_metric(translate(:sumSeries,
220
+ metrics.join(',')), z)
221
+ end
222
+
223
+ if !@params[:use_color] || (!z[:color] && @params[:use_color])
224
+ colors << next_color(colors, z[:color])
225
+ end
183
226
  end # metrics.each
184
227
  end # clusters.each
185
228
  else # one line per {metric,host,colo}
@@ -199,9 +242,11 @@ module Dash::Models
199
242
  #################
200
243
 
201
244
  if label =~ /\*/
202
- # for this particular type of graph, don't display a legend
245
+ # for this particular type of graph, don't display a legend,
246
+ # and color with abandon
203
247
  url_opts[:hideLegend] = true
204
- z = opts.dup
248
+ z = Marshal.load(Marshal.dump(opts))
249
+ z.delete(:color)
205
250
  # fixme proper labeling... maybe
206
251
  # With wildcards let graphite construct the legend (or not).
207
252
  # Since we're handling wildcards we don't know how many
@@ -211,10 +256,13 @@ module Dash::Models
211
256
  target << handle_metric(metric, z)
212
257
  colors.concat(@params[:default_colors]) if colors.empty?
213
258
  else
214
- z = opts.dup
215
- z[:key] = "#{host}/#{cluster} #{opts[:key]}"
259
+ z = Marshal.load(Marshal.dump(opts))
260
+ z[:key] = "#{host}/#{cluster} #{z[:key]}"
216
261
  target << handle_metric(metric, z)
217
- colors << next_color(colors, opts[:color])
262
+ if !@params[:use_color] ||
263
+ (!opts[:color] && @params[:use_color])
264
+ colors << next_color(colors, opts[:color])
265
+ end
218
266
  end
219
267
  end
220
268
  end
@@ -259,13 +307,27 @@ module Dash::Models
259
307
  return metrics.flatten.map { |x| x.split(".") }
260
308
  end
261
309
 
310
+ # FIXMEEEE needs to be fixed for different metric formats
262
311
  def hosts_clusters
263
312
  metrics = expand
264
313
  clusters = Set.new
265
314
 
266
- # field -1 is the host name, and -2 is its cluster
267
- hosts = metrics.map do |x|
268
- Host.new(x[-1], @params.merge({ "cluster" => x[-2] }))
315
+ f = @params[:metric_format].dup.split("%m")
316
+ first = f.first.split(".")
317
+ last = f.last.split(".")
318
+ ci = hi = nil
319
+ first.each_with_index do |v, i|
320
+ ci = i if v.match("%c")
321
+ hi = i if v.match("%h")
322
+ end
323
+ unless ci && hi
324
+ last.reverse.each_with_index do |v, i|
325
+ ci = -1 -i if v.match("%c")
326
+ hi = -1 -i if v.match("%h")
327
+ end
328
+ end
329
+ hosts = metrics.map do |m|
330
+ Host.new(m[hi], @params.merge({ "cluster" => m[ci] }))
269
331
  end.uniq
270
332
 
271
333
  # filter by what matches the graph definition
@@ -287,7 +349,7 @@ module Dash::Models
287
349
  default_colors << preferred_color
288
350
  end
289
351
 
290
- weights = Hash.new { |h, k| h[k] = 0 }
352
+ weights = Hash.new(0)
291
353
  colors.each do |c|
292
354
  weights[c] += 1
293
355
  end
data/lib/rubyfixes.rb ADDED
@@ -0,0 +1,8 @@
1
+ class YAML::Omap
2
+ def keys
3
+ self.map {|k,v| k}
4
+ end
5
+ def values
6
+ self.map {|k,v| v}
7
+ end
8
+ end
@@ -16,8 +16,11 @@
16
16
  end %>
17
17
  <br>
18
18
  <% end %>
19
+
20
+ <% if (hosts.to_set - seen_hosts).size > 0 %>
19
21
  <h3>Other Hosts (not associated with a dashboard)</h3>
20
22
  <%= (hosts.to_set - seen_hosts).sort.collect do |h|
21
23
  href = append_query_string("/host/#{@cluster}/#{h}")
22
24
  "<li><a href=\"#{href}\">#{h}</a></li>"
23
25
  end %>
26
+ <% end %>
@@ -15,14 +15,6 @@
15
15
  </div>
16
16
  </div>
17
17
 
18
- <div class="graphsection" style="width:<%= @zoom.width(merge_opts) %>;">
19
- <a name="by_host">
20
- <span class="graphtitle"><%= @zoom.name %> / <%= @cluster %> :: by host</span>
21
- <div class="graph">
22
- <img src="<%= @dash.render_cluster_graph(@zoom, @cluster, :zoom => true, :dynamic_url_opts => merge_opts) %>">
23
- </div>
24
- </div>
25
-
26
18
  <% hosts.each do |host| %>
27
19
  <div class="graphsection" style="width:<%= @zoom.width(merge_opts) %>;">
28
20
  <a name="<%= host %>">
data/lib/views/layout.erb CHANGED
@@ -3,7 +3,7 @@
3
3
  <%= css_url %>
4
4
  <link href="/favicon.ico" rel="icon" type="text/x-icon">
5
5
  <link href="/favicon.ico" rel="shortcut icon" type="text/x-icon">
6
- <%= refresh %>
6
+ <%= refresh unless @no_graphs %>
7
7
  </head>
8
8
  <body bgcolor="black">
9
9
 
@@ -51,5 +51,7 @@
51
51
  </div>
52
52
 
53
53
  </body>
54
- <div id="footer" style="color:green;">generated <%= @request_time %></div>
54
+ <div id="footer" style="color:green;">
55
+ generated <%= @request_time %> by <a href="https://github.com/petef/pencil">pencil</a> v<%= PENCIL_VERSION %>
56
+ </div>
55
57
  </html>
@@ -7,6 +7,7 @@ clusters:
7
7
  <%= if @cluster == "global"
8
8
  " (global)"
9
9
  else
10
+ # this sub works because the cluster should be first
10
11
  " <a href=\"#{append_query_string(request.path.sub(@cluster, 'global'))}\">(global)</a>"
11
12
  end %>
12
13
  <br>
@@ -1,6 +1,7 @@
1
1
  <span class="graphtitle">
2
2
  dashboards:
3
3
  <%= @dashboards.sort.collect do |d|
4
+ # this works unless you have a cluster with the same name as a dashboard
4
5
  sub = append_query_string(request.path.sub(@dash.name, d.name))
5
6
  @dash == d ? "#{d}" : "<a href=\"#{sub}\">#{d}</a>"
6
7
  end.join(" ") %>
@@ -1,7 +1,10 @@
1
1
  <span class="graphtitle">
2
2
  graphs:
3
3
  <%= @dash.graphs.map { |g| g.name }.collect do |g|
4
- sub = append_query_string(request.path.sub(@zoom.name, g))
4
+ reversed = request.path.split("/").reverse
5
+ i = reversed.index(@zoom.name)
6
+ reversed[i] = g
7
+ sub = append_query_string(reversed.reverse.join('/'))
5
8
  @zoom.name == g ? "#{g}" : "<a href=\"#{sub}\">#{g}</a>"
6
9
  end.join(" ") %>
7
10
  </span>
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pencil
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 2
10
- version: 0.2.2
9
+ - 6
10
+ version: 0.2.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Pete Fritchman
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-07-12 00:00:00 -07:00
19
+ date: 2011-08-02 00:00:00 -07:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -114,33 +114,34 @@ extensions: []
114
114
  extra_rdoc_files: []
115
115
 
116
116
  files:
117
+ - lib/dash.rb
117
118
  - lib/models.rb
118
- - lib/public/style.css
119
- - lib/public/favicon.ico
119
+ - lib/helpers.rb
120
+ - lib/namespace.rb
120
121
  - lib/config.rb
121
- - lib/models/host.rb
122
- - lib/models/graph.rb
123
- - lib/models/dashboard.rb
124
- - lib/models/base.rb
122
+ - lib/views/dash-cluster.erb
125
123
  - lib/views/cluster.erb
126
- - lib/views/global.erb
127
- - lib/views/dash-cluster-zoom.erb
128
- - lib/views/dash-global-zoom.erb
129
- - lib/views/dash-global.erb
124
+ - lib/views/layout.erb
130
125
  - lib/views/host.erb
131
- - lib/views/partials/graph_switcher.erb
132
- - lib/views/partials/hosts_selector.erb
126
+ - lib/views/dash-global.erb
127
+ - lib/views/dash-global-zoom.erb
128
+ - lib/views/global.erb
133
129
  - lib/views/partials/input_boxes.erb
130
+ - lib/views/partials/graph_switcher.erb
134
131
  - lib/views/partials/cluster_selector.erb
135
- - lib/views/partials/cluster_switcher.erb
136
132
  - lib/views/partials/dash_switcher.erb
137
- - lib/views/partials/cookies_form.erb
133
+ - lib/views/partials/hosts_selector.erb
138
134
  - lib/views/partials/refresh_button.erb
139
- - lib/views/layout.erb
140
- - lib/views/dash-cluster.erb
141
- - lib/namespace.rb
142
- - lib/helpers.rb
143
- - lib/dash.rb
135
+ - lib/views/partials/cookies_form.erb
136
+ - lib/views/partials/cluster_switcher.erb
137
+ - lib/views/dash-cluster-zoom.erb
138
+ - lib/rubyfixes.rb
139
+ - lib/public/favicon.ico
140
+ - lib/public/style.css
141
+ - lib/models/graph.rb
142
+ - lib/models/dashboard.rb
143
+ - lib/models/base.rb
144
+ - lib/models/host.rb
144
145
  - lib/config.ru
145
146
  - docs/pencil_options.md
146
147
  - examples/pencil.yml